Pesquisa de agrupamento
Uma pesquisa de agrupamento permite ao Milvus agrupar os resultados da pesquisa pelos valores num campo específico para agregar dados a um nível superior. Por exemplo, pode utilizar uma pesquisa ANN básica para encontrar livros semelhantes ao livro em questão, mas pode utilizar uma pesquisa de agrupamento para encontrar as categorias de livros que podem envolver os tópicos discutidos nesse livro. Este tópico descreve como usar a Pesquisa de agrupamento juntamente com as principais considerações.
Visão geral
Quando as entidades nos resultados da pesquisa compartilham o mesmo valor em um campo escalar, isso indica que elas são semelhantes em um atributo específico, o que pode afetar negativamente os resultados da pesquisa.
Suponha que uma coleção armazena vários documentos (denotados por docId). Para reter o máximo de informação semântica possível ao converter documentos em vectores, cada documento é dividido em parágrafos mais pequenos e geríveis (ou pedaços) e armazenado como entidades separadas. Apesar de o documento estar dividido em secções mais pequenas, os utilizadores continuam muitas vezes interessados em identificar quais os documentos mais relevantes para as suas necessidades.
Pesquisa ANN
Ao efetuar uma pesquisa ANN (Approximate Nearest Neighbor) numa coleção deste tipo, os resultados da pesquisa podem incluir vários parágrafos do mesmo documento, fazendo com que outros documentos sejam potencialmente ignorados, o que pode não estar de acordo com o caso de utilização pretendido.
Pesquisa de agrupamento
Para melhorar a diversidade dos resultados da pesquisa, pode adicionar o parâmetro group_by_field
no pedido de pesquisa para ativar a Pesquisa de agrupamento. Como mostrado no diagrama, pode definir group_by_field
para docId
. Ao receber este pedido, o Milvus irá.
Efetuar uma pesquisa ANN com base no vetor de consulta fornecido para encontrar todas as entidades mais semelhantes à consulta.
Agrupa os resultados da pesquisa pelo endereço especificado
group_by_field
, comodocId
.Retorna os principais resultados para cada grupo, conforme definido pelo parâmetro
limit
, com a entidade mais semelhante de cada grupo.
Por padrão, a Pesquisa de agrupamento retorna apenas uma entidade por grupo. Se você quiser aumentar o número de resultados a serem retornados por grupo, poderá controlar isso com os parâmetros group_size
e strict_group_size
.
Executar a pesquisa de agrupamento
Esta secção fornece um código de exemplo para demonstrar a utilização da Pesquisa de agrupamento. O exemplo a seguir supõe que a coleção inclui campos para 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},
]
Na solicitação de pesquisa, defina group_by_field
e output_fields
como docId
. O Milvus agrupará os resultados pelo campo especificado e devolverá a entidade mais semelhante de cada grupo, incluindo o valor de docId
para cada entidade devolvida.
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"]
}'
No pedido acima, limit=3
indica que o sistema retornará resultados de pesquisa de três grupos, com cada grupo contendo a única entidade mais semelhante ao vetor de consulta.
Configurar o tamanho do grupo
Por padrão, a Pesquisa de agrupamento retorna apenas uma entidade por grupo. Se você quiser vários resultados por grupo, ajuste os parâmetros 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"]
}'
No exemplo acima.
group_size
: Especifica o número desejado de entidades a serem retornadas por grupo. Por exemplo, definirgroup_size=2
significa que cada grupo (ou cadadocId
) deve idealmente retornar dois dos parágrafos (ou pedaços) mais semelhantes. Segroup_size
não for definido, o sistema retorna por padrão um resultado por grupo.strict_group_size
: Este parâmetro booleano controla se o sistema deve aplicar estritamente a contagem definida porgroup_size
. Quandostrict_group_size=True
, o sistema tentará incluir o número exato de entidades especificadas porgroup_size
em cada grupo (por exemplo, dois parágrafos), a menos que não haja dados suficientes nesse grupo. Por defeito (strict_group_size=False
), o sistema dá prioridade ao número de grupos especificado pelo parâmetrolimit
, em vez de garantir que cada grupo contém entidadesgroup_size
. Esta abordagem é geralmente mais eficiente nos casos em que a distribuição de dados é desigual.
Para obter detalhes adicionais sobre os parâmetros, consulte search().
Considerações
Número de grupos: O parâmetro
limit
controla o número de grupos dos quais os resultados da pesquisa são retornados, em vez do número específico de entidades em cada grupo. Definir umlimit
apropriado ajuda a controlar a diversidade da pesquisa e o desempenho da consulta. A redução delimit
pode reduzir os custos de computação se os dados forem densamente distribuídos ou se o desempenho for uma preocupação.Entidades por grupo: O parâmetro
group_size
controla o número de entidades retornadas por grupo. Ajustargroup_size
com base no seu caso de uso pode aumentar a riqueza dos resultados da pesquisa. No entanto, se os dados forem distribuídos de forma desigual, alguns grupos podem retornar menos entidades do que o especificado porgroup_size
, particularmente em cenários de dados limitados.Tamanho rigoroso do grupo: Quando
strict_group_size=True
, o sistema tentará retornar o número especificado de entidades (group_size
) para cada grupo, a menos que não haja dados suficientes nesse grupo. Essa configuração garante contagens consistentes de entidades por grupo, mas pode levar à degradação do desempenho com distribuição desigual de dados ou recursos limitados. Se não forem necessárias contagens rigorosas de entidades, a definição destrict_group_size=False
pode melhorar a velocidade da consulta.