milvus-logo
LFAI
홈페이지
  • 사용자 가이드

그룹 검색

그룹화 검색을 사용하면 Milvus는 지정된 필드의 값에 따라 검색 결과를 그룹화하여 더 높은 수준에서 데이터를 집계할 수 있습니다. 예를 들어, 기본 ANN 검색을 사용하여 현재 책과 유사한 책을 찾을 수 있지만 그룹화 검색을 사용하여 해당 책에서 논의되는 주제와 관련된 책 카테고리를 찾을 수 있습니다. 이 주제에서는 그룹화 검색을 사용하는 방법과 주요 고려 사항에 대해 설명합니다.

개요

검색 결과의 엔티티가 스칼라 필드에서 동일한 값을 공유하면 특정 속성이 유사하다는 것을 나타내며, 이는 검색 결과에 부정적인 영향을 미칠 수 있습니다.

컬렉션에 여러 문서( docId로 표시됨)가 저장되어 있다고 가정해 보겠습니다. 문서를 벡터로 변환할 때 가능한 한 많은 의미론적 정보를 유지하기 위해 각 문서는 관리하기 쉬운 작은 단락(또는 청크)으로 분할되어 별도의 엔티티로 저장됩니다. 문서가 더 작은 섹션으로 나뉘어져 있더라도 사용자는 여전히 자신의 요구와 가장 관련성이 높은 문서를 식별하는 데 관심이 있는 경우가 많습니다.

ANN Search ANN 검색

이러한 컬렉션에서 근사 이웃(ANN) 검색을 수행할 때, 검색 결과에 동일한 문서의 여러 단락이 포함될 수 있으며, 이로 인해 의도한 사용 사례와 맞지 않는 다른 문서가 간과될 수 있습니다.

Grouping Search 그룹 검색

검색 결과의 다양성을 높이기 위해 검색 요청에 group_by_field 매개변수를 추가하여 그룹화 검색을 활성화할 수 있습니다. 다이어그램에 표시된 것처럼 group_by_fielddocId 으로 설정할 수 있습니다. 이 요청을 받으면 Milvus는 다음과 같이 합니다.

  • 제공된 쿼리 벡터를 기반으로 ANN 검색을 수행하여 쿼리와 가장 유사한 모든 엔티티를 찾습니다.

  • 검색 결과를 지정된 group_by_field 에 따라 그룹화합니다(예: docId).

  • limit 매개변수에 정의된 대로 각 그룹에 대해 각 그룹에서 가장 유사한 엔티티가 있는 상위 결과를 반환합니다.

기본적으로 그룹 검색은 그룹당 하나의 엔터티만 반환합니다. 그룹당 반환할 결과 수를 늘리려면 group_sizestrict_group_size 매개변수를 사용하여 이를 제어할 수 있습니다.

이 섹션에서는 그룹화 검색의 사용법을 보여 주는 예제 코드를 제공합니다. 다음 예제에서는 컬렉션에 id, vector, chunk, 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},
]


검색 요청에서 group_by_fieldoutput_fields 을 모두 docId 로 설정합니다. Milvus는 지정된 필드별로 결과를 그룹화하고 반환된 각 엔티티에 대해 docId 값을 포함하여 각 그룹에서 가장 유사한 엔티티를 반환합니다.

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

위의 요청에서 limit=3 은 시스템이 세 그룹의 검색 결과를 반환하며, 각 그룹에는 쿼리 벡터와 가장 유사한 단일 엔티티가 포함되어 있음을 나타냅니다.

그룹 크기 구성

기본적으로 그룹 검색은 그룹당 하나의 엔티티만 반환합니다. 그룹당 여러 개의 결과를 표시하려면 group_sizestrict_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"]
}'

위 예제에서는

  • group_size: 그룹당 반환할 엔티티 수를 지정합니다. 예를 들어 group_size=2 을 설정하면 각 그룹(또는 각 docId)은 가장 유사한 단락(또는 청크) 두 개를 반환하는 것이 이상적입니다. group_size 을 설정하지 않으면 기본적으로 그룹당 하나의 결과를 반환합니다.

  • strict_group_size: 이 부울 매개변수는 시스템이 group_size 에 설정된 개수를 엄격하게 적용할지 여부를 제어합니다. strict_group_size=True 이면 시스템은 해당 그룹에 데이터가 충분하지 않는 한 group_size 에 지정된 정확한 수의 엔티티(예: 두 단락)를 각 그룹에 포함하려고 시도합니다. 기본적으로(strict_group_size=False), 시스템은 각 그룹에 group_size 엔티티가 포함되도록 하기보다는 limit 매개변수에 지정된 그룹 수를 충족하는 것을 우선시합니다. 이 접근 방식은 일반적으로 데이터 분포가 고르지 않은 경우에 더 효율적입니다.

추가 매개변수에 대한 자세한 내용은 search()를 참조하세요.

고려 사항

  • 그룹 수: limit 매개변수는 각 그룹 내의 특정 엔티티 수가 아니라 검색 결과가 반환되는 그룹의 수를 제어합니다. 적절한 limit 을 설정하면 검색 다양성 및 쿼리 성능을 제어하는 데 도움이 됩니다. 데이터가 고밀도로 분산되어 있거나 성능이 우려되는 경우 limit 을 줄이면 계산 비용을 줄일 수 있습니다.

  • 그룹별 엔티티: group_size 매개변수는 그룹당 반환되는 엔티티 수를 제어합니다. 사용 사례에 따라 group_size 을 조정하면 검색 결과의 풍부함을 높일 수 있습니다. 그러나 데이터가 고르지 않게 분산된 경우 일부 그룹은 특히 제한된 데이터 시나리오에서 group_size 에서 지정한 것보다 적은 수의 엔티티를 반환할 수 있습니다.

  • 엄격한 그룹 크기: strict_group_size=True 을 선택하면 해당 그룹에 데이터가 충분하지 않는 한 시스템에서 각 그룹에 대해 지정된 수의 엔티티(group_size)를 반환하려고 시도합니다. 이 설정은 그룹당 일관된 엔티티 수를 보장하지만 데이터 분포가 고르지 않거나 리소스가 제한되어 있는 경우 성능이 저하될 수 있습니다. 엄격한 엔티티 수가 필요하지 않은 경우 strict_group_size=False 을 설정하면 쿼리 속도가 향상될 수 있습니다.

번역DeepL

Try Managed Milvus for Free

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

Get Started
피드백

이 페이지가 도움이 되었나요?