Ricerca per raggruppamento
Una ricerca di raggruppamento consente a Milvus di raggruppare i risultati della ricerca in base ai valori di un campo specifico per aggregare i dati a un livello superiore. Ad esempio, è possibile utilizzare una ricerca RNA di base per trovare libri simili a quello in questione, ma è possibile utilizzare una ricerca di raggruppamento per trovare le categorie di libri che possono riguardare gli argomenti trattati in quel libro. Questo argomento descrive come utilizzare la ricerca per raggruppamento e le considerazioni principali.
Panoramica
Quando le entità nei risultati della ricerca condividono lo stesso valore in un campo scalare, ciò indica che sono simili in un particolare attributo, il che può avere un impatto negativo sui risultati della ricerca.
Si supponga che una collezione contenga più documenti (indicati da docId). Per conservare il maggior numero possibile di informazioni semantiche durante la conversione dei documenti in vettori, ogni documento viene suddiviso in paragrafi (o chunks) più piccoli e gestibili e memorizzato come entità separate. Anche se il documento è diviso in sezioni più piccole, gli utenti sono spesso interessati a identificare i documenti più rilevanti per le loro esigenze.
Ricerca ANN
Quando si esegue una ricerca approssimativa dei vicini (ANN) su una raccolta di questo tipo, i risultati della ricerca possono includere diversi paragrafi dello stesso documento, facendo potenzialmente trascurare altri documenti, che potrebbero non essere in linea con il caso d'uso previsto.
Raggruppamento della ricerca
Per migliorare la varietà dei risultati della ricerca, è possibile aggiungere il parametro group_by_field
nella richiesta di ricerca per attivare la ricerca per gruppi. Come mostrato nel diagramma, è possibile impostare group_by_field
su docId
. Quando riceve questa richiesta, Milvus.
Esegue una ricerca ANN basata sul vettore di query fornito per trovare tutte le entità più simili alla query.
Raggruppa i risultati della ricerca in base a
group_by_field
, ad esempiodocId
.Restituisce i primi risultati per ogni gruppo, come definito dal parametro
limit
, con l'entità più simile di ogni gruppo.
Per impostazione predefinita, la ricerca per gruppi restituisce solo un'entità per gruppo. Se si desidera aumentare il numero di risultati da restituire per gruppo, è possibile controllarlo con i parametri group_size
e strict_group_size
.
Esecuzione della ricerca per gruppi
Questa sezione fornisce un esempio di codice per dimostrare l'uso della ricerca per gruppi. L'esempio seguente presuppone che l'insieme includa i campi id
, vector
, chunk
e docId
.
[
{"id": 0, "vector": [0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592], "chunk": "pink_8682", "docId": 1},
{"id": 1, "vector": [0.19886812562848388, 0.06023560599112088, 0.6976963061752597, 0.2614474506242501, 0.838729485096104], "chunk": "red_7025", "docId": 5},
{"id": 2, "vector": [0.43742130801983836, -0.5597502546264526, 0.6457887650909682, 0.7894058910881185, 0.20785793220625592], "chunk": "orange_6781", "docId": 2},
{"id": 3, "vector": [0.3172005263489739, 0.9719044792798428, -0.36981146090600725, -0.4860894583077995, 0.95791889146345], "chunk": "pink_9298", "docId": 3},
{"id": 4, "vector": [0.4452349528804562, -0.8757026943054742, 0.8220779437047674, 0.46406290649483184, 0.30337481143159106], "chunk": "red_4794", "docId": 3},
{"id": 5, "vector": [0.985825131989184, -0.8144651566660419, 0.6299267002202009, 0.1206906911183383, -0.1446277761879955], "chunk": "yellow_4222", "docId": 4},
{"id": 6, "vector": [0.8371977790571115, -0.015764369584852833, -0.31062937026679327, -0.562666951622192, -0.8984947637863987], "chunk": "red_9392", "docId": 1},
{"id": 7, "vector": [-0.33445148015177995, -0.2567135004164067, 0.8987539745369246, 0.9402995886420709, 0.5378064918413052], "chunk": "grey_8510", "docId": 2},
{"id": 8, "vector": [0.39524717779832685, 0.4000257286739164, -0.5890507376891594, -0.8650502298996872, -0.6140360785406336], "chunk": "white_9381", "docId": 5},
{"id": 9, "vector": [0.5718280481994695, 0.24070317428066512, -0.3737913482606834, -0.06726932177492717, -0.6980531615588608], "chunk": "purple_4976", "docId": 3},
]
Nella richiesta di ricerca, impostare sia group_by_field
che output_fields
su docId
. Milvus raggrupperà i risultati in base al campo specificato e restituirà l'entità più simile di ogni gruppo, includendo il valore di docId
per ogni entità restituita.
from pymilvus import MilvusClient
client = MilvusClient(
uri="http://localhost:19530",
token="root:Milvus"
)
query_vectors = [
[0.14529211512077012, 0.9147257273453546, 0.7965055218724449, 0.7009258593102812, 0.5605206522382088]]
# Group search results
res = client.search(
collection_name="group_search_collection",
data=query_vectors,
limit=3,
group_by_field="docId",
output_fields=["docId"]
)
# Retrieve the values in the `docId` column
doc_ids = [result['entity']['docId'] for result in res[0]]
import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.service.vector.request.SearchReq
import io.milvus.v2.service.vector.request.data.FloatVec;
import io.milvus.v2.service.vector.response.SearchResp
MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder()
.uri("http://localhost:19530")
.token("root:Milvus")
.build());
FloatVec queryVector = new FloatVec(new float[]{0.14529211512077012f, 0.9147257273453546f, 0.7965055218724449f, 0.7009258593102812f, 0.5605206522382088f});
SearchReq searchReq = SearchReq.builder()
.collectionName("group_search_collection")
.data(Collections.singletonList(queryVector))
.topK(3)
.groupByFieldName("docId")
.outputFields(Collections.singletonList("docId"))
.build();
SearchResp searchResp = client.search(searchReq);
List<List<SearchResp.SearchResult>> searchResults = searchResp.getSearchResults();
for (List<SearchResp.SearchResult> results : searchResults) {
System.out.println("TopK results:");
for (SearchResp.SearchResult result : results) {
System.out.println(result);
}
}
// Output
// TopK results:
// SearchResp.SearchResult(entity={docId=5}, score=0.74767184, id=1)
// SearchResp.SearchResult(entity={docId=2}, score=0.6254269, id=7)
// SearchResp.SearchResult(entity={docId=3}, score=0.3611898, id=3)
// nolint
func ExampleClient_Search_grouping() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
milvusAddr := "127.0.0.1:19530"
token := "root:Milvus"
cli, err := client.New(ctx, &client.ClientConfig{
Address: milvusAddr,
APIKey: token,
})
if err != nil {
log.Fatal("failed to connect to milvus server: ", err.Error())
}
defer cli.Close(ctx)
queryVector := []float32{0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592}
resultSets, err := cli.Search(ctx, client.NewSearchOption(
"my_collection", // collectionName
3, // limit
[]entity.Vector{entity.FloatVector(queryVector)},
).WithGroupByField("docId"))
if err != nil {
log.Fatal("failed to perform basic ANN search collection: ", err.Error())
}
for _, resultSet := range resultSets {
log.Println("IDs: ", resultSet.IDs)
log.Println("Scores: ", resultSet.Scores)
}
// Output:
// IDs:
// Scores:
}
import { MilvusClient, DataType } from "@zilliz/milvus2-sdk-node";
const address = "http://localhost:19530";
const token = "root:Milvus";
const client = new MilvusClient({address, token});
var query_vector = [0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592]
res = await client.search({
collection_name: "my_collection",
data: [query_vector],
limit: 3,
// highlight-start
group_by_field: "docId"
// highlight-end
})
// Retrieve the values in the `docId` column
var docIds = res.results.map(result => result.entity.docId)
export CLUSTER_ENDPOINT="http://localhost:19530"
export TOKEN="root:Milvus"
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/search" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-d '{
"collectionName": "group_search_collection",
"data": [
[0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592]
],
"annsField": "vector",
"limit": 3,
"groupingField": "docId",
"outputFields": ["docId"]
}'
Nella richiesta qui sopra, limit=3
indica che il sistema restituirà i risultati della ricerca da tre gruppi, con ogni gruppo contenente la singola entità più simile al vettore della query.
Configurare la dimensione dei gruppi
Per impostazione predefinita, la ricerca per gruppi restituisce solo un'entità per gruppo. Se si desidera ottenere più risultati per gruppo, regolare i parametri group_size
e strict_group_size
.
# Group search results
res = client.search(
collection_name="group_search_collection",
data=query_vectors, # Query vector
limit=5, # Top K results to return
group_by_field="docId", # Group by docId
group_size=2, # Return 2 entities per group
strict_group_size=True, # Ensure each group has 2 entities
output_fields=["docId"]
)
FloatVec queryVector = new FloatVec(new float[]{0.14529211512077012f, 0.9147257273453546f, 0.7965055218724449f, 0.7009258593102812f, 0.5605206522382088f});
SearchReq searchReq = SearchReq.builder()
.collectionName("group_search_collection")
.data(Collections.singletonList(queryVector))
.topK(5)
.groupByFieldName("docId")
.groupSize(2)
.strictGroupSize(true)
.outputFields(Collections.singletonList("docId"))
.build();
SearchResp searchResp = client.search(searchReq);
List<List<SearchResp.SearchResult>> searchResults = searchResp.getSearchResults();
for (List<SearchResp.SearchResult> results : searchResults) {
System.out.println("TopK results:");
for (SearchResp.SearchResult result : results) {
System.out.println(result);
}
}
// Output
// TopK results:
// SearchResp.SearchResult(entity={docId=5}, score=0.74767184, id=1)
// SearchResp.SearchResult(entity={docId=5}, score=-0.49148706, id=8)
// SearchResp.SearchResult(entity={docId=2}, score=0.6254269, id=7)
// SearchResp.SearchResult(entity={docId=2}, score=0.38515577, id=2)
// SearchResp.SearchResult(entity={docId=3}, score=0.3611898, id=3)
// SearchResp.SearchResult(entity={docId=3}, score=0.19556211, id=4)
import { MilvusClient, DataType } from "@zilliz/milvus2-sdk-node";
const address = "http://localhost:19530";
const token = "root:Milvus";
const client = new MilvusClient({address, token});
var query_vector = [0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592]
res = await client.search({
collection_name: "my_collection",
data: [query_vector],
limit: 3,
group_by_field: "docId",
// highlight-start
group_size: 2,
strict_group_size: true
// highlight-end
})
// Retrieve the values in the `docId` column
var docIds = res.results.map(result => result.entity.docId)
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/search" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-d '{
"collectionName": "group_search_collection",
"data": [
[0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592]
],
"annsField": "vector",
"limit": 5,
"groupingField": "docId",
"groupSize":2,
"strictGroupSize":true,
"outputFields": ["docId"]
}'
Nell'esempio precedente.
group_size
: Specifica il numero desiderato di entità da restituire per gruppo. Per esempio, impostandogroup_size=2
si intende che ogni gruppo (o ognidocId
) dovrebbe idealmente restituire due dei paragrafi (o pezzi) più simili. Segroup_size
non è impostato, il sistema restituisce per default un risultato per gruppo.strict_group_size
: Questo parametro booleano controlla se il sistema deve applicare rigorosamente il conteggio impostato dagroup_size
. Sestrict_group_size=True
, il sistema cercherà di includere il numero esatto di entità specificato dagroup_size
in ogni gruppo (ad esempio, due paragrafi), a meno che non ci siano abbastanza dati in quel gruppo. Per impostazione predefinita (strict_group_size=False
), il sistema dà la priorità al raggiungimento del numero di gruppi specificato dal parametrolimit
, piuttosto che assicurarsi che ogni gruppo contenga le entità digroup_size
. Questo approccio è generalmente più efficiente nei casi in cui la distribuzione dei dati non è uniforme.
Per ulteriori dettagli sui parametri, fare riferimento a search().
Considerazioni
Numero di gruppi: Il parametro
limit
controlla il numero di gruppi da cui vengono restituiti i risultati della ricerca, piuttosto che il numero specifico di entità all'interno di ciascun gruppo. L'impostazione di unlimit
appropriato aiuta a controllare la diversità della ricerca e le prestazioni della query. Ridurrelimit
può ridurre i costi di calcolo se i dati sono distribuiti densamente o se le prestazioni sono un problema.Entità per gruppo: Il parametro
group_size
controlla il numero di entità restituite per gruppo. La regolazione digroup_size
in base al caso d'uso può aumentare la ricchezza dei risultati della ricerca. Tuttavia, se i dati sono distribuiti in modo non uniforme, alcuni gruppi potrebbero restituire un numero di entità inferiore a quello specificato dagroup_size
, in particolare in scenari di dati limitati.Dimensione rigida del gruppo: Quando si utilizza
strict_group_size=True
, il sistema cercherà di restituire il numero di entità specificato (group_size
) per ogni gruppo, a meno che non ci siano abbastanza dati in quel gruppo. Questa impostazione garantisce un conteggio coerente delle entità per gruppo, ma può comportare un calo delle prestazioni in caso di distribuzione non uniforme dei dati o di risorse limitate. Se non è richiesto un numero rigido di entità, l'impostazione district_group_size=False
può migliorare la velocità delle query.