Pencarian Pengelompokan
Pencarian pengelompokan memungkinkan Milvus mengelompokkan hasil pencarian berdasarkan nilai dalam bidang tertentu untuk mengumpulkan data pada tingkat yang lebih tinggi. Sebagai contoh, Anda dapat menggunakan pencarian ANN dasar untuk menemukan buku yang mirip dengan buku yang sedang dicari, tetapi Anda dapat menggunakan pencarian pengelompokan untuk menemukan kategori buku yang mungkin melibatkan topik-topik yang dibahas dalam buku tersebut. Topik ini menjelaskan cara menggunakan Pencarian Pengelompokan bersama dengan pertimbangan-pertimbangan utama.
Ikhtisar
Ketika entitas dalam hasil pencarian memiliki nilai yang sama dalam bidang skalar, hal ini mengindikasikan bahwa entitas tersebut memiliki kemiripan dalam atribut tertentu, yang dapat berdampak negatif pada hasil pencarian.
Anggaplah sebuah koleksi menyimpan beberapa dokumen (dilambangkan dengan docId). Untuk mempertahankan informasi semantik sebanyak mungkin saat mengubah dokumen menjadi vektor, setiap dokumen dipecah menjadi paragraf yang lebih kecil dan mudah diatur (atau potongan) dan disimpan sebagai entitas terpisah. Meskipun dokumen dibagi menjadi beberapa bagian yang lebih kecil, pengguna sering kali masih tertarik untuk mengidentifikasi dokumen mana yang paling relevan dengan kebutuhan mereka.
Pencarian ANN
Ketika melakukan pencarian Approximate Nearest Neighbor (ANN) pada koleksi seperti itu, hasil pencarian dapat mencakup beberapa paragraf dari dokumen yang sama, sehingga berpotensi menyebabkan dokumen lain terlewatkan, yang mungkin tidak sesuai dengan kasus penggunaan yang diinginkan.
Mengelompokkan Pencarian
Untuk meningkatkan keragaman hasil pencarian, Anda dapat menambahkan parameter group_by_field
dalam permintaan pencarian untuk mengaktifkan Pencarian Pengelompokan. Seperti yang ditunjukkan pada diagram, Anda dapat mengatur group_by_field
ke docId
. Setelah menerima permintaan ini, Milvus akan.
Melakukan pencarian ANN berdasarkan vektor kueri yang disediakan untuk menemukan semua entitas yang paling mirip dengan kueri.
Mengelompokkan hasil pencarian berdasarkan
group_by_field
yang ditentukan, sepertidocId
.Mengembalikan hasil teratas untuk setiap kelompok, seperti yang ditentukan oleh parameter
limit
, dengan entitas yang paling mirip dari setiap kelompok.
Secara default, Pencarian Pengelompokan hanya mengembalikan satu entitas per grup. Jika Anda ingin menambah jumlah hasil yang dikembalikan per grup, Anda dapat mengontrolnya dengan parameter group_size
dan strict_group_size
.
Melakukan Pencarian Pengelompokan
Bagian ini memberikan contoh kode untuk mendemonstrasikan penggunaan Pencarian Pengelompokan. Contoh berikut ini mengasumsikan koleksi mencakup bidang untuk id
, vector
, chunk
, dan 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},
]
Dalam permintaan pencarian, setel group_by_field
dan output_fields
ke docId
. Milvus akan mengelompokkan hasil berdasarkan bidang yang ditentukan dan mengembalikan entitas yang paling mirip dari setiap kelompok, termasuk nilai docId
untuk setiap entitas yang dikembalikan.
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"]
}'
Pada permintaan di atas, limit=3
menunjukkan bahwa sistem akan mengembalikan hasil pencarian dari tiga kelompok, dengan masing-masing kelompok berisi satu entitas yang paling mirip dengan vektor kueri.
Mengonfigurasi ukuran grup
Secara default, Pencarian Pengelompokan hanya mengembalikan satu entitas per grup. Jika Anda menginginkan beberapa hasil per grup, sesuaikan parameter group_size
dan 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"]
}'
Dalam contoh di atas.
group_size
: Menentukan jumlah entitas yang diinginkan untuk dikembalikan per grup. Sebagai contoh, pengaturangroup_size=2
berarti setiap grup (atau setiapdocId
) idealnya mengembalikan dua paragraf (atau potongan) yang paling mirip. Jikagroup_size
tidak disetel, sistem secara default mengembalikan satu hasil per grup.strict_group_size
: Parameter boolean ini mengontrol apakah sistem harus secara ketat memberlakukan hitungan yang ditetapkan olehgroup_size
. Ketikastrict_group_size=True
, sistem akan berusaha memasukkan jumlah entitas yang tepat yang ditentukan olehgroup_size
dalam setiap grup (misalnya, dua paragraf), kecuali jika tidak ada cukup data dalam grup tersebut. Secara default (strict_group_size=False
), sistem akan memprioritaskan untuk memenuhi jumlah grup yang ditentukan oleh parameterlimit
, daripada memastikan setiap grup berisi entitasgroup_size
. Pendekatan ini umumnya lebih efisien dalam kasus-kasus di mana distribusi data tidak merata.
Untuk detail parameter tambahan, lihat search().
Pertimbangan
Pengindeksan: Fitur pengelompokan ini hanya berfungsi untuk koleksi yang diindeks dengan jenis indeks berikut: FLAT, IVF_FLAT, IVF_SQ8, HNSW, HNSW_PQ, HNSW_PRQ, HNSW_SQ, DISKANN, SPARSE_INVERTED_INDEX.
Jumlah kelompok: Parameter
limit
mengontrol jumlah grup dari mana hasil pencarian dikembalikan, dan bukan jumlah spesifik entitas dalam setiap grup. Menetapkanlimit
yang sesuai membantu mengontrol keragaman pencarian dan kinerja kueri. Mengurangilimit
dapat mengurangi biaya komputasi jika data terdistribusi secara padat atau kinerja menjadi perhatian.Entitas per grup: Parameter
group_size
mengontrol jumlah entitas yang dikembalikan per grup. Menyesuaikangroup_size
berdasarkan kasus penggunaan Anda dapat meningkatkan kekayaan hasil pencarian. Namun, jika data tidak terdistribusi secara merata, beberapa grup mungkin mengembalikan lebih sedikit entitas daripada yang ditentukan olehgroup_size
, terutama dalam skenario data terbatas.Ukuran kelompok yang ketat: Ketika
strict_group_size=True
, sistem akan berusaha mengembalikan jumlah entitas yang ditentukan (group_size
) untuk setiap grup, kecuali jika tidak ada cukup data dalam grup tersebut. Pengaturan ini memastikan jumlah entitas yang konsisten per grup, tetapi dapat menyebabkan penurunan kinerja dengan distribusi data yang tidak merata atau sumber daya yang terbatas. Jika jumlah entitas yang ketat tidak diperlukan, pengaturanstrict_group_size=False
dapat meningkatkan kecepatan kueri.