milvus-logo
LFAI
Home
  • Guide de l'utilisateur

Recherche groupée

Une recherche de regroupement permet à Milvus de regrouper les résultats de la recherche en fonction des valeurs d'un champ spécifié afin d'agréger les données à un niveau supérieur. Par exemple, vous pouvez utiliser une recherche ANN de base pour trouver des livres similaires à celui qui vous intéresse, mais vous pouvez utiliser une recherche de regroupement pour trouver les catégories de livres qui peuvent concerner les sujets abordés dans ce livre. Cette rubrique décrit l'utilisation de la recherche par regroupement, ainsi que les principales considérations à prendre en compte.

Vue d'ensemble

Lorsque des entités dans les résultats de recherche partagent la même valeur dans un champ scalaire, cela indique qu'elles sont similaires dans un attribut particulier, ce qui peut avoir un impact négatif sur les résultats de la recherche.

Supposons qu'une collection stocke plusieurs documents (désignés par docId). Pour conserver autant d'informations sémantiques que possible lors de la conversion des documents en vecteurs, chaque document est divisé en paragraphes (ou morceaux) plus petits et gérables, et stocké en tant qu'entités distinctes. Même si le document est divisé en sections plus petites, les utilisateurs sont souvent intéressés par l'identification des documents les plus pertinents pour leurs besoins.

ANN Search Recherche ANN

Lorsqu'une recherche par approximation du plus proche voisin (ANN) est effectuée sur une telle collection, les résultats de la recherche peuvent inclure plusieurs paragraphes du même document, ce qui peut entraîner l'oubli d'autres documents, ce qui peut ne pas correspondre au cas d'utilisation prévu.

Grouping Search Recherche groupée

Pour améliorer la diversité des résultats de la recherche, vous pouvez ajouter le paramètre group_by_field dans la requête de recherche afin d'activer la recherche groupée. Comme le montre le diagramme, vous pouvez définir group_by_field sur docId. A la réception de cette demande, Milvus va.

  • Effectuer une recherche ANN basée sur le vecteur de requête fourni pour trouver toutes les entités les plus similaires à la requête.

  • Regrouper les résultats de la recherche en fonction de l'adresse group_by_field spécifiée, par exemple docId.

  • Renvoie les meilleurs résultats pour chaque groupe, tel que défini par le paramètre limit, avec l'entité la plus similaire de chaque groupe.

Par défaut, la recherche par regroupement ne renvoie qu'une seule entité par groupe. Si vous souhaitez augmenter le nombre de résultats renvoyés par groupe, vous pouvez le contrôler à l'aide des paramètres group_size et strict_group_size.

Cette section fournit un exemple de code pour démontrer l'utilisation de la recherche par regroupement. L'exemple suivant suppose que la collection comprend les champs id, vector, chunk et 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},
]


Dans la requête de recherche, définissez group_by_field et output_fields sur docId. Milvus regroupera les résultats en fonction du champ spécifié et renverra l'entité la plus similaire de chaque groupe, y compris la valeur de docId pour chaque entité renvoyée.

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"]
}'

Dans la requête ci-dessus, limit=3 indique que le système renverra les résultats de recherche de trois groupes, chaque groupe contenant l'entité la plus similaire au vecteur de la requête.

Configurer la taille des groupes

Par défaut, la recherche groupée ne renvoie qu'une seule entité par groupe. Si vous souhaitez obtenir plusieurs résultats par groupe, modifiez les paramètres group_size et 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"]
}'

Dans l'exemple ci-dessus.

  • group_size: Spécifie le nombre d'entités à renvoyer par groupe. Par exemple, le paramètre group_size=2 signifie que chaque groupe (ou chaque docId) devrait idéalement renvoyer deux des paragraphes (ou morceaux) les plus similaires. Si group_size n'est pas défini, le système renvoie par défaut un résultat par groupe.

  • strict_group_size: Ce paramètre booléen détermine si le système doit appliquer strictement le décompte défini par group_size. Si strict_group_size=True, le système tentera d'inclure le nombre exact d'entités spécifié par group_size dans chaque groupe (par exemple, deux paragraphes), à moins qu'il n'y ait pas assez de données dans ce groupe. Par défaut (strict_group_size=False), le système donne la priorité au respect du nombre de groupes spécifié par le paramètre limit, plutôt que de s'assurer que chaque groupe contient des entités group_size. Cette approche est généralement plus efficace dans les cas où la distribution des données est inégale.

Pour plus de détails sur les paramètres, reportez-vous à search().

Points à prendre en compte

  • Nombre de groupes: Le paramètre limit contrôle le nombre de groupes à partir desquels les résultats de la recherche sont renvoyés, plutôt que le nombre spécifique d'entités dans chaque groupe. La définition d'une valeur appropriée pour limit permet de contrôler la diversité des recherches et les performances des requêtes. La réduction de limit peut réduire les coûts de calcul si les données sont densément distribuées ou si les performances sont un problème.

  • Entités par groupe: Le paramètre group_size contrôle le nombre d'entités renvoyées par groupe. L'ajustement de group_size en fonction de votre cas d'utilisation peut augmenter la richesse des résultats de la recherche. Toutefois, si les données sont réparties de manière inégale, certains groupes peuvent renvoyer moins d'entités que celles spécifiées par group_size, en particulier dans les scénarios où les données sont limitées.

  • Taille de groupe stricte: Avec strict_group_size=True, le système tentera de renvoyer le nombre d'entités spécifié (group_size) pour chaque groupe, à moins qu'il n'y ait pas assez de données dans ce groupe. Ce paramètre garantit un nombre cohérent d'entités par groupe, mais peut entraîner une dégradation des performances en cas de distribution inégale des données ou de ressources limitées. Si un nombre strict d'entités n'est pas nécessaire, le paramètre strict_group_size=False peut améliorer la vitesse de la requête.

Traduit parDeepLogo

Try Managed Milvus for Free

Zilliz Cloud is hassle-free, powered by Milvus and 10x faster.

Get Started
Feedback

Cette page a-t - elle été utile ?