Группировочный поиск
Группировочный поиск позволяет Milvus группировать результаты поиска по значениям в указанном поле, чтобы собрать данные на более высоком уровне. Например, вы можете использовать базовый поиск ANN, чтобы найти книги, похожие на ту, которую вы рассматриваете, но вы можете использовать группирующий поиск, чтобы найти категории книг, которые могут быть связаны с темами, обсуждаемыми в этой книге. В этой теме описывается использование группирующего поиска, а также ключевые моменты.
Обзор
Когда сущности в результатах поиска имеют одинаковое значение в скалярном поле, это указывает на их схожесть по определенному атрибуту, что может негативно повлиять на результаты поиска.
Предположим, что в коллекции хранится несколько документов (обозначаемых docId). Чтобы сохранить как можно больше семантической информации при преобразовании документов в векторы, каждый документ разбивается на более мелкие, управляемые абзацы (или куски) и хранится как отдельные сущности. Даже если документ разделен на более мелкие части, пользователи часто заинтересованы в том, чтобы определить, какие документы наиболее релевантны их потребностям.
ANN-поиск
При выполнении поиска по приближенным ближайшим соседям (ANN) в такой коллекции результаты поиска могут включать несколько абзацев из одного и того же документа, что может привести к пропуску других документов, что может не соответствовать предполагаемому сценарию использования.
Группировка поиска
Чтобы улучшить разнообразие результатов поиска, можно добавить параметр group_by_field
в запрос на поиск, чтобы включить группировочный поиск. Как показано на рисунке, можно установить group_by_field
на docId
. Получив этот запрос, Milvus.
Выполнит ANN-поиск на основе предоставленного вектора запроса, чтобы найти все сущности, наиболее похожие на запрос.
Сгруппирует результаты поиска по указанному
group_by_field
, напримерdocId
.Вернет верхние результаты для каждой группы, как определено параметром
limit
, с наиболее похожими сущностями из каждой группы.
По умолчанию группировочный поиск возвращает только одну сущность на группу. Если вы хотите увеличить количество результатов, возвращаемых для каждой группы, вы можете управлять этим с помощью параметров group_size
и strict_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_field
и output_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_size
и 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"]
}'
В примере выше.
group_size
: Указывает желаемое количество сущностей, возвращаемых в каждой группе. Например, если задатьgroup_size=2
, то каждая группа (или каждыйdocId
) в идеале должна возвращать два наиболее похожих абзаца (или фрагмента). Еслиgroup_size
не задан, система по умолчанию возвращает один результат на группу.strict_group_size
: Этот булевский параметр управляет тем, должна ли система строго придерживаться подсчета, заданногоgroup_size
. Если заданstrict_group_size=True
, система попытается включить в каждую группу точное количество сущностей, заданноеgroup_size
(например, два абзаца), если только в этой группе не будет достаточно данных. По умолчанию (strict_group_size=False
), системе приоритетнее удовлетворить количество групп, заданное параметромlimit
, чем гарантировать, что каждая группа содержит сущностиgroup_size
. Такой подход обычно более эффективен в случаях, когда распределение данных неравномерно.
Дополнительные сведения о параметрах см. в разделе Поиск().
Соображения
Количество групп: Параметр
limit
управляет количеством групп, из которых возвращаются результаты поиска, а не конкретным количеством сущностей в каждой группе. Установка подходящего значенияlimit
помогает контролировать разнообразие поиска и производительность запросов. Уменьшениеlimit
может снизить затраты на вычисления, если данные распределены плотно или производительность вызывает беспокойство.Сущности на группу: Параметр
group_size
управляет количеством сущностей, возвращаемых на группу. Настройка параметраgroup_size
в зависимости от конкретного случая использования может повысить насыщенность результатов поиска. Однако если данные распределены неравномерно, некоторые группы могут возвращать меньше сущностей, чем указано наgroup_size
, особенно в сценариях с ограниченным количеством данных.Строгий размер группы: При использовании
strict_group_size=True
система будет пытаться вернуть указанное количество сущностей (group_size
) для каждой группы, если только в этой группе нет достаточного количества данных. Эта настройка обеспечивает постоянное количество сущностей в группе, но может привести к снижению производительности при неравномерном распределении данных или ограниченных ресурсах. Если строгий подсчет сущностей не требуется, установкаstrict_group_size=False
может повысить скорость выполнения запросов.