البحث الهجين متعدد النواقل
في العديد من التطبيقات، يمكن البحث عن كائن ما من خلال مجموعة غنية من المعلومات مثل العنوان والوصف، أو بطرائق متعددة مثل النص والصور والصوت. على سبيل المثال، يتم البحث عن تغريدة تحتوي على نص وصورة إذا كان النص أو الصورة يتطابقان مع دلالات استعلام البحث. يعزز البحث الهجين تجربة البحث من خلال الجمع بين عمليات البحث عبر هذه المجالات المتنوعة. يدعم Milvus ذلك من خلال السماح بالبحث في حقول متجهات متعددة، وإجراء العديد من عمليات البحث في أقرب جار تقريبي (ANN) في وقت واحد. يعد البحث الهجين متعدد المتجهات مفيدًا بشكل خاص إذا كنت ترغب في البحث في كل من النصوص والصور، أو في حقول نصية متعددة تصف نفس الشيء، أو في متجهات كثيفة ومتناثرة لتحسين جودة البحث.
سير عمل البحث المختلط
يدمج البحث الهجين متعدد المتجهات بين طرق بحث مختلفة أو يمتد على نطاق واسع من طرائق مختلفة:
بحث المتجهات المتفرقة والكثيفة: تعتبر المتجهات الكثيفة ممتازة لالتقاط العلاقات الدلالية، في حين أن المتجهات المتفرقة فعالة للغاية لمطابقة الكلمات المفتاحية بدقة. ويجمع البحث الهجين بين هذه الأساليب لتوفير فهم مفاهيمي واسع النطاق وملاءمة دقيقة للمصطلحات، وبالتالي تحسين نتائج البحث. من خلال الاستفادة من نقاط القوة في كل طريقة، يتغلب البحث المختلط على قيود الأساليب المستقلة، مما يوفر أداءً أفضل للاستعلامات المعقدة. فيما يلي دليل أكثر تفصيلاً حول الاسترجاع الهجين الذي يجمع بين البحث الدلالي والبحث في النص الكامل.
البحث المتجه متعدد الوسائط: البحث المتجه متعدد الوسائط هو تقنية قوية تتيح لك البحث عبر أنواع مختلفة من البيانات، بما في ذلك النصوص والصور والصوت وغيرها. الميزة الرئيسية لهذا النهج هي قدرته على توحيد الطرائق المختلفة في تجربة بحث سلسة ومتماسكة. على سبيل المثال، في البحث عن المنتجات، قد يقوم المستخدم بإدخال استعلام نصي للعثور على المنتجات الموصوفة بالنصوص والصور. من خلال الجمع بين هذه الطرائق من خلال طريقة بحث مختلطة، يمكنك تحسين دقة البحث أو إثراء نتائج البحث.
مثال
لنفكر في حالة استخدام واقعية حيث يتضمن كل منتج وصفًا نصيًا وصورة. بناءً على البيانات المتاحة، يمكننا إجراء ثلاثة أنواع من عمليات البحث:
البحث النصي الدلالي: يتضمن ذلك الاستعلام عن الوصف النصي للمنتج باستخدام متجهات كثيفة. يمكن إنشاء تضمينات النص باستخدام نماذج مثل BERT و Transformers أو خدمات مثل OpenAI.
البحث عن النص الكامل: هنا، نستعلم عن الوصف النصي للمنتج باستخدام مطابقة الكلمات الرئيسية مع متجهات متناثرة. يمكن استخدام خوارزميات مثل BM25 أو نماذج التضمين المتناثرة مثل BGE-M3 أو SPLADE لهذا الغرض.
البحث متعدد الوسائط عن الصور: تستعلم هذه الطريقة عن الصورة باستخدام استعلام نصي مع متجهات كثيفة. يمكن إنشاء تضمينات الصور باستخدام نماذج مثل CLIP.
سيرشدك هذا الدليل إلى مثال على بحث هجين متعدد الوسائط يجمع بين طرق البحث المذكورة أعلاه، بالنظر إلى الوصف النصي الخام وتضمينات الصور للمنتجات. سنوضح كيفية تخزين البيانات متعددة المتجهات وإجراء عمليات بحث هجينة باستخدام استراتيجية إعادة الترتيب.
إنشاء مجموعة ذات حقول متجهات متعددة
تتضمن عملية إنشاء مجموعة ثلاث خطوات رئيسية: تحديد مخطط المجموعة، وتكوين معلمات الفهرس، وإنشاء المجموعة.
تحديد المخطط
بالنسبة للبحث المختلط متعدد المتجهات، يجب أن نحدد حقول متجهات متعددة داخل مخطط المجموعة. بشكل افتراضي، يمكن أن تستوعب كل مجموعة ما يصل إلى 4 حقول متجهة. ومع ذلك، إذا لزم الأمر، يمكنك ضبط proxy.maxVectorFieldNum لتضمين ما يصل إلى 10 حقول متجهة في مجموعة حسب الحاجة.
يدمج هذا المثال الحقول التالية في المخطط:
id: يعمل كمفتاح أساسي لتخزين المعرفات النصية. هذا الحقل من نوع البياناتINT64.text: يستخدم لتخزين المحتوى النصي. هذا الحقل من نوع البياناتVARCHARبحد أقصى للطول 1000 بايت. يتم تعيين الخيارenable_analyzerعلىTrueلتسهيل البحث في النص الكامل.text_dense: يستخدم لتخزين المتجهات الكثيفة للنصوص. هذا الحقل من نوع البياناتFLOAT_VECTORمع بُعد متجه يبلغ 768.text_sparse: يستخدم لتخزين المتجهات المتفرقة للنصوص. هذا الحقل من نوع البياناتSPARSE_FLOAT_VECTOR.image_dense: يستخدم لتخزين المتجهات الكثيفة لصور المنتج. هذا الحقل هو من نوع البياناتFLOAT_VETORمع بُعد متجه يبلغ 512.
نظرًا لأننا سوف نستخدم خوارزمية BM25 المدمجة لإجراء بحث عن النص الكامل في حقل النص، فمن الضروري إضافة ميلفوس Function إلى المخطط. لمزيد من التفاصيل، يرجى الرجوع إلى البحث عن النص الكامل.
from pymilvus import (
MilvusClient, DataType, Function, FunctionType
)
client = MilvusClient(
uri="http://localhost:19530",
token="root:Milvus"
)
# Init schema with auto_id disabled
schema = MilvusClient.create_schema(auto_id=False)
# Add fields to schema
schema.add_field(field_name="id", datatype=DataType.INT64, is_primary=True, description="product id")
schema.add_field(field_name="text", datatype=DataType.VARCHAR, max_length=1000, enable_analyzer=True, description="raw text of product description")
schema.add_field(field_name="text_dense", datatype=DataType.FLOAT_VECTOR, dim=768, description="text dense embedding")
schema.add_field(field_name="text_sparse", datatype=DataType.SPARSE_FLOAT_VECTOR, description="text sparse embedding auto-generated by the built-in BM25 function")
schema.add_field(field_name="image_dense", datatype=DataType.FLOAT_VECTOR, dim=512, description="image dense embedding")
# Add function to schema
bm25_function = Function(
name="text_bm25_emb",
input_field_names=["text"],
output_field_names=["text_sparse"],
function_type=FunctionType.BM25,
)
schema.add_function(bm25_function)
import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.common.DataType;
import io.milvus.common.clientenum.FunctionType;
import io.milvus.v2.service.collection.request.AddFieldReq;
import io.milvus.v2.service.collection.request.CreateCollectionReq;
import io.milvus.v2.service.collection.request.CreateCollectionReq.Function;
import java.util.*;
MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder()
.uri("http://localhost:19530")
.token("root:Milvus")
.build());
CreateCollectionReq.CollectionSchema schema = client.createSchema();
schema.addField(AddFieldReq.builder()
.fieldName("id")
.dataType(DataType.Int64)
.isPrimaryKey(true)
.autoID(false)
.build());
schema.addField(AddFieldReq.builder()
.fieldName("text")
.dataType(DataType.VarChar)
.maxLength(1000)
.enableAnalyzer(true)
.build());
schema.addField(AddFieldReq.builder()
.fieldName("text_dense")
.dataType(DataType.FloatVector)
.dimension(768)
.build());
schema.addField(AddFieldReq.builder()
.fieldName("text_sparse")
.dataType(DataType.SparseFloatVector)
.build());
schema.addField(AddFieldReq.builder()
.fieldName("image_dense")
.dataType(DataType.FloatVector)
.dimension(512)
.build());
schema.addFunction(Function.builder()
.functionType(FunctionType.BM25)
.name("text_bm25_emb")
.inputFieldNames(Collections.singletonList("text"))
.outputFieldNames(Collections.singletonList("text_sparse"))
.build());
import (
"context"
"fmt"
"github.com/milvus-io/milvus/client/v2/column"
"github.com/milvus-io/milvus/client/v2/entity"
"github.com/milvus-io/milvus/client/v2/index"
"github.com/milvus-io/milvus/client/v2/milvusclient"
)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
milvusAddr := "localhost:19530"
client, err := milvusclient.New(ctx, &milvusclient.ClientConfig{
Address: milvusAddr,
})
if err != nil {
fmt.Println(err.Error())
// handle error
}
defer client.Close(ctx)
function := entity.NewFunction().
WithName("text_bm25_emb").
WithInputFields("text").
WithOutputFields("text_sparse").
WithType(entity.FunctionTypeBM25)
schema := entity.NewSchema()
schema.WithField(entity.NewField().
WithName("id").
WithDataType(entity.FieldTypeInt64).
WithIsPrimaryKey(true),
).WithField(entity.NewField().
WithName("text").
WithDataType(entity.FieldTypeVarChar).
WithEnableAnalyzer(true).
WithMaxLength(1000),
).WithField(entity.NewField().
WithName("text_dense").
WithDataType(entity.FieldTypeFloatVector).
WithDim(768),
).WithField(entity.NewField().
WithName("text_sparse").
WithDataType(entity.FieldTypeSparseVector),
).WithField(entity.NewField().
WithName("image_dense").
WithDataType(entity.FieldTypeFloatVector).
WithDim(512),
).WithFunction(function)
import { MilvusClient, DataType } from "@zilliz/milvus2-sdk-node";
const address = "http://localhost:19530";
const token = "root:Milvus";
const client = new MilvusClient({address, token});
// Define fields
const fields = [
{
name: "id",
data_type: DataType.Int64,
is_primary_key: true,
auto_id: false
},
{
name: "text",
data_type: DataType.VarChar,
max_length: 1000,
enable_match: true
},
{
name: "text_dense",
data_type: DataType.FloatVector,
dim: 768
},
{
name: "text_sparse",
data_type: DataType.SPARSE_FLOAT_VECTOR
},
{
name: "image_dense",
data_type: DataType.FloatVector,
dim: 512
}
];
// define function
const functions = [
{
name: "text_bm25_emb",
description: "text bm25 function",
type: FunctionType.BM25,
input_field_names: ["text"],
output_field_names: ["text_sparse"],
params: {},
},
];
export bm25Function='{
"name": "text_bm25_emb",
"type": "BM25",
"inputFieldNames": ["text"],
"outputFieldNames": ["text_sparse"],
"params": {}
}'
export schema='{
"autoId": false,
"functions": [$bm25Function],
"fields": [
{
"fieldName": "id",
"dataType": "Int64",
"isPrimary": true
},
{
"fieldName": "text",
"dataType": "VarChar",
"elementTypeParams": {
"max_length": 1000,
"enable_analyzer": true
}
},
{
"fieldName": "text_dense",
"dataType": "FloatVector",
"elementTypeParams": {
"dim": "768"
}
},
{
"fieldName": "text_sparse",
"dataType": "SparseFloatVector"
},
{
"fieldName": "image_dense",
"dataType": "FloatVector",
"elementTypeParams": {
"dim": "512"
}
}
]
}'
إنشاء فهرس
from pymilvus import MilvusClient
# Prepare index parameters
index_params = client.prepare_index_params()
# Add indexes
index_params.add_index(
field_name="text_dense",
index_name="text_dense_index",
index_type="AUTOINDEX",
metric_type="IP"
)
index_params.add_index(
field_name="text_sparse",
index_name="text_sparse_index",
index_type="SPARSE_INVERTED_INDEX",
metric_type="BM25",
params={"inverted_index_algo": "DAAT_MAXSCORE"}, # or "DAAT_WAND" or "TAAT_NAIVE"
)
index_params.add_index(
field_name="image_dense",
index_name="image_dense_index",
index_type="AUTOINDEX",
metric_type="IP"
)
import io.milvus.v2.common.IndexParam;
import java.util.*;
Map<String, Object> denseParams = new HashMap<>();
IndexParam indexParamForTextDense = IndexParam.builder()
.fieldName("text_dense")
.indexName("text_dense_index")
.indexType(IndexParam.IndexType.AUTOINDEX)
.metricType(IndexParam.MetricType.IP)
.build();
Map<String, Object> sparseParams = new HashMap<>();
sparseParams.put("inverted_index_algo": "DAAT_MAXSCORE");
IndexParam indexParamForTextSparse = IndexParam.builder()
.fieldName("text_sparse")
.indexName("text_sparse_index")
.indexType(IndexParam.IndexType.SPARSE_INVERTED_INDEX)
.metricType(IndexParam.MetricType.BM25)
.extraParams(sparseParams)
.build();
IndexParam indexParamForImageDense = IndexParam.builder()
.fieldName("image_dense")
.indexName("image_dense_index")
.indexType(IndexParam.IndexType.AUTOINDEX)
.metricType(IndexParam.MetricType.IP)
.build();
List<IndexParam> indexParams = new ArrayList<>();
indexParams.add(indexParamForTextDense);
indexParams.add(indexParamForTextSparse);
indexParams.add(indexParamForImageDense);
indexOption1 := milvusclient.NewCreateIndexOption("my_collection", "text_dense",
index.NewAutoIndex(index.MetricType(entity.IP)))
indexOption2 := milvusclient.NewCreateIndexOption("my_collection", "text_sparse",
index.NewSparseInvertedIndex(entity.BM25, 0.2))
indexOption3 := milvusclient.NewCreateIndexOption("my_collection", "image_dense",
index.NewAutoIndex(index.MetricType(entity.IP)))
)
const index_params = [{
field_name: "text_dense",
index_name: "text_dense_index",
index_type: "AUTOINDEX",
metric_type: "IP"
},{
field_name: "text_sparse",
index_name: "text_sparse_index",
index_type: "IndexType.SPARSE_INVERTED_INDEX",
metric_type: "BM25",
params: {
inverted_index_algo: "DAAT_MAXSCORE",
}
},{
field_name: "image_dense",
index_name: "image_dense_index",
index_type: "AUTOINDEX",
metric_type: "IP"
}]
export indexParams='[
{
"fieldName": "text_dense",
"metricType": "IP",
"indexName": "text_dense_index",
"indexType":"AUTOINDEX"
},
{
"fieldName": "text_sparse",
"metricType": "BM25",
"indexName": "text_sparse_index",
"indexType": "SPARSE_INVERTED_INDEX",
"params":{"inverted_index_algo": "DAAT_MAXSCORE"}
},
{
"fieldName": "image_dense",
"metricType": "IP",
"indexName": "image_dense_index",
"indexType":"AUTOINDEX"
}
]'
إنشاء مجموعة
قم بإنشاء مجموعة باسم demo مع مخطط المجموعة والفهارس التي تم تكوينها في الخطوتين السابقتين.
from pymilvus import MilvusClient
client.create_collection(
collection_name="my_collection",
schema=schema,
index_params=index_params
)
CreateCollectionReq createCollectionReq = CreateCollectionReq.builder()
.collectionName("my_collection")
.collectionSchema(schema)
.indexParams(indexParams)
.build();
client.createCollection(createCollectionReq);
err = client.CreateCollection(ctx,
milvusclient.NewCreateCollectionOption("my_collection", schema).
WithIndexOptions(indexOption1, indexOption2))
if err != nil {
fmt.Println(err.Error())
// handle error
}
res = await client.createCollection({
collection_name: "my_collection",
fields: fields,
index_params: index_params,
})
export CLUSTER_ENDPOINT="http://localhost:19530"
export TOKEN="root:Milvus"
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/collections/create" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-d "{
\"collectionName\": \"my_collection\",
\"schema\": $schema,
\"indexParams\": $indexParams
}"
إدراج البيانات
يقوم هذا القسم بإدراج البيانات في المجموعة my_collection استنادًا إلى المخطط المحدد مسبقًا. أثناء الإدراج، تأكد من تزويد جميع الحقول، باستثناء تلك التي تحتوي على قيم مُنشأة تلقائيًا، بالبيانات بالتنسيق الصحيح. في هذا المثال
id: عدد صحيح يمثل معرف المنتجtext: سلسلة تحتوي على وصف المنتجtext_dense: قائمة مكونة من 768 قيمة فاصلة عائمة تمثل التضمين الكثيف للوصف النصيimage_dense: قائمة مكونة من 512 قيمة فاصلة عائمة تمثل التضمين الكثيف لصورة المنتج
يمكنك استخدام نفس النماذج أو نماذج مختلفة لإنشاء تضمينات كثيفة لكل حقل. في هذا المثال، يحتوي التضمينان الكثيفان في هذا المثال على أبعاد مختلفة، مما يشير إلى أنه تم إنشاؤهما بواسطة نماذج مختلفة. عند تحديد كل عملية بحث لاحقًا، تأكد من استخدام النموذج المقابل لإنشاء تضمين الاستعلام المناسب.
نظرًا لأن هذا المثال يستخدم دالة BM25 المدمجة لتوليد تضمينات متناثرة من حقل النص، فلن تحتاج إلى توفير متجهات متناثرة يدويًا. ومع ذلك، إذا اخترت عدم استخدام BM25، فيجب عليك حساب التضمينات المتفرقة مسبقًا وتوفيرها بنفسك.
from pymilvus import MilvusClient
data=[
{
"id": 0,
"text": "Red cotton t-shirt with round neck",
"text_dense": [0.3580376395471989, -0.6023495712049978, 0.18414012509913835, ...],
"image_dense": [0.6366019600530924, -0.09323198122475052, ...]
},
{
"id": 1,
"text": "Wireless noise-cancelling over-ear headphones",
"text_dense": [0.19886812562848388, 0.06023560599112088, 0.6976963061752597, ...],
"image_dense": [0.6414180010301553, 0.8976979978567611, ...]
},
{
"id": 2,
"text": "Stainless steel water bottle, 500ml",
"dense": [0.43742130801983836, -0.5597502546264526, 0.6457887650909682, ...],
"image_dense": [-0.6901259768402174, 0.6100500332193755, ...]
}
]
res = client.insert(
collection_name="my_collection",
data=data
)
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import io.milvus.v2.service.vector.request.InsertReq;
Gson gson = new Gson();
JsonObject row1 = new JsonObject();
row1.addProperty("id", 0);
row1.addProperty("text", "Red cotton t-shirt with round neck");
row1.add("text_dense", gson.toJsonTree(text_dense1));
row1.add("image_dense", gson.toJsonTree(image_dense));
JsonObject row2 = new JsonObject();
row2.addProperty("id", 1);
row2.addProperty("text", "Wireless noise-cancelling over-ear headphones");
row2.add("text_dense", gson.toJsonTree(text_dense2));
row2.add("image_dense", gson.toJsonTree(image_dense2));
JsonObject row3 = new JsonObject();
row3.addProperty("id", 2);
row3.addProperty("text", "Stainless steel water bottle, 500ml");
row3.add("text_dense", gson.toJsonTree(dense3));
row3.add("image_dense", gson.toJsonTree(sparse3));
List<JsonObject> data = Arrays.asList(row1, row2, row3);
InsertReq insertReq = InsertReq.builder()
.collectionName("my_collection")
.data(data)
.build();
InsertResp insertResp = client.insert(insertReq);
_, err = client.Insert(ctx, milvusclient.NewColumnBasedInsertOption("my_collection").
WithInt64Column("id", []int64{0, 1, 2}).
WithVarcharColumn("text", []string{
"Red cotton t-shirt with round neck",
"Wireless noise-cancelling over-ear headphones",
"Stainless steel water bottle, 500ml",
}).
WithFloatVectorColumn("text_dense", 768, [][]float32{
{0.3580376395471989, -0.6023495712049978, 0.18414012509913835, ...},
{0.19886812562848388, 0.06023560599112088, 0.6976963061752597, ...},
{0.43742130801983836, -0.5597502546264526, 0.6457887650909682, ...},
}).
WithFloatVectorColumn("image_dense", 512, [][]float32{
{0.6366019600530924, -0.09323198122475052, ...},
{0.6414180010301553, 0.8976979978567611, ...},
{-0.6901259768402174, 0.6100500332193755, ...},
}).
if err != nil {
fmt.Println(err.Error())
// handle err
}
const { MilvusClient, DataType } = require("@zilliz/milvus2-sdk-node")
var data = [
{id: 0, text: "Red cotton t-shirt with round neck" , text_dense: [0.3580376395471989, -0.6023495712049978, 0.18414012509913835, ...], image_dense: [0.6366019600530924, -0.09323198122475052, ...]},
{id: 1, text: "Wireless noise-cancelling over-ear headphones" , text_dense: [0.19886812562848388, 0.06023560599112088, 0.6976963061752597, ...], image_dense: [0.6414180010301553, 0.8976979978567611, ...]},
{id: 2, text: "Stainless steel water bottle, 500ml" , text_dense: [0.43742130801983836, -0.5597502546264526, 0.6457887650909682, ...], image_dense: [-0.6901259768402174, 0.6100500332193755, ...]}
]
var res = await client.insert({
collection_name: "my_collection",
data: data,
})
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/insert" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-d '{
"data": [
{"id": 0, "text": "Red cotton t-shirt with round neck" , "text_dense": [0.3580376395471989, -0.6023495712049978, 0.18414012509913835, ...], "image_dense": [0.6366019600530924, -0.09323198122475052, ...]},
{"id": 1, "text": "Wireless noise-cancelling over-ear headphones" , "text_dense": [0.19886812562848388, 0.06023560599112088, 0.6976963061752597, ...], "image_dense": [0.6414180010301553, 0.8976979978567611, ...]},
{"id": 2, "text": "Stainless steel water bottle, 500ml" , "text_dense": [0.43742130801983836, -0.5597502546264526, 0.6457887650909682, ...], "image_dense": [-0.6901259768402174, 0.6100500332193755, ...]}
],
"collectionName": "my_collection"
}'
إجراء بحث هجين
إنشاء مثيلات AnnSearchRequest متعددة
يتم تنفيذ البحث الهجين من خلال إنشاء عدة AnnSearchRequest في الدالة hybrid_search() ، حيث يمثل كل AnnSearchRequest طلب بحث ANN أساسي لحقل متجه معين. لذلك، قبل إجراء بحث مختلط، من الضروري إنشاء AnnSearchRequest لكل حقل متجه.
بالإضافة إلى ذلك، من خلال تكوين المعلمة expr في AnnSearchRequest ، يمكنك تعيين شروط التصفية للبحث الهجين الخاص بك. يرجى الرجوع إلى البحث المصفى والتصفية.
في البحث المختلط، يدعم كل AnnSearchRequest بيانات استعلام واحدة فقط في البحث المختلط.
لتوضيح إمكانيات حقول متجهات البحث المختلفة، سنقوم بإنشاء ثلاثة طلبات بحث AnnSearchRequest باستخدام نموذج استعلام. سنستخدم أيضًا متجهاته الكثيفة المحسوبة مسبقًا لهذه العملية. ستستهدف طلبات البحث حقول المتجهات التالية:
text_denseللبحث الدلالي عن النص الدلالي، مما يسمح بفهم السياق واسترجاعه بناءً على المعنى بدلاً من المطابقة المباشرة للكلمات المفتاحيةtext_sparseللبحث في النص الكامل أو مطابقة الكلمات المفتاحية، مع التركيز على مطابقة الكلمات أو العبارات الدقيقة داخل النص.image_denseللبحث متعدد الوسائط من نص إلى صورة، لاسترداد صور المنتجات ذات الصلة بناءً على المحتوى الدلالي للاستعلام.
from pymilvus import AnnSearchRequest
query_text = "white headphones, quiet and comfortable"
query_dense_vector = [0.3580376395471989, -0.6023495712049978, 0.5142999509918703, ...]
query_multimodal_vector = [0.015829865178701663, 0.5264158340734488, ...]
# text semantic search (dense)
search_param_1 = {
"data": [query_dense_vector],
"anns_field": "text_dense",
"param": {"nprobe": 10},
"limit": 2
}
request_1 = AnnSearchRequest(**search_param_1)
# full-text search (sparse)
search_param_2 = {
"data": [query_text],
"anns_field": "text_sparse",
"limit": 2
}
request_2 = AnnSearchRequest(**search_param_2)
# text-to-image search (multimodal)
search_param_3 = {
"data": [query_multimodal_vector],
"anns_field": "image_dense",
"param": {"nprobe": 10},
"limit": 2
}
request_3 = AnnSearchRequest(**search_param_3)
reqs = [request_1, request_2, request_3]
import io.milvus.v2.service.vector.request.AnnSearchReq;
import io.milvus.v2.service.vector.request.data.BaseVector;
import io.milvus.v2.service.vector.request.data.FloatVec;
import io.milvus.v2.service.vector.request.data.SparseFloatVec;
import io.milvus.v2.service.vector.request.data.EmbeddedText;
float[] queryDense = new float[]{-0.0475336798f, 0.0521207601f, 0.0904406682f, ...};
float[] queryMultimodal = new float[]{0.0158298651f, 0.5264158340f, ...}
List<BaseVector> queryTexts = Collections.singletonList(new EmbeddedText("white headphones, quiet and comfortable");)
List<BaseVector> queryDenseVectors = Collections.singletonList(new FloatVec(queryDense));
List<BaseVector> queryMultimodalVectors = Collections.singletonList(new FloatVec(queryMultimodal));
List<AnnSearchReq> searchRequests = new ArrayList<>();
searchRequests.add(AnnSearchReq.builder()
.vectorFieldName("text_dense")
.vectors(queryDenseVectors)
.params("{\"nprobe\": 10}")
.topK(2)
.build());
searchRequests.add(AnnSearchReq.builder()
.vectorFieldName("text_sparse")
.vectors(queryTexts)
.topK(2)
.build());
searchRequests.add(AnnSearchReq.builder()
.vectorFieldName("image_dense")
.vectors(queryMultimodalVectors)
.params("{\"nprobe\": 10}")
.topK(2)
.build());
queryText := entity.Text({"white headphones, quiet and comfortable"})
queryVector := []float32{0.3580376395471989, -0.6023495712049978, 0.18414012509913835, ...}
queryMultimodalVector := []float32{0.015829865178701663, 0.5264158340734488, ...}
request1 := milvusclient.NewAnnRequest("text_dense", 2, entity.FloatVector(queryVector)).
WithAnnParam(index.NewIvfAnnParam(10))
annParam := index.NewSparseAnnParam()
annParam.WithDropRatio(0.2)
request2 := milvusclient.NewAnnRequest("text_sparse", 2, queryText).
WithAnnParam(annParam)
request3 := milvusclient.NewAnnRequest("image_dense", 2, entity.FloatVector(queryMultimodalVector)).
WithAnnParam(index.NewIvfAnnParam(10))
const query_text = "white headphones, quiet and comfortable"
const query_vector = [0.3580376395471989, -0.6023495712049978, 0.18414012509913835, ...]
const query_multimodal_vector = [0.015829865178701663, 0.5264158340734488, ...]
const search_param_1 = {
"data": query_vector,
"anns_field": "text_dense",
"param": {"nprobe": 10},
"limit": 2
}
const search_param_2 = {
"data": query_text,
"anns_field": "text_sparse",
"limit": 2
}
const search_param_3 = {
"data": query_multimodal_vector,
"anns_field": "image_dense",
"param": {"nprobe": 10},
"limit": 2
}
export req='[
{
"data": [[0.3580376395471989, -0.6023495712049978, 0.18414012509913835, ...]],
"annsField": "text_dense",
"params": {"nprobe": 10},
"limit": 2
},
{
"data": ["white headphones, quiet and comfortable"],
"annsField": "text_sparse",
"limit": 2
},
{
"data": [[0.015829865178701663, 0.5264158340734488, ...]],
"annsField": "image_dense",
"params": {"nprobe": 10},
"limit": 2
}
]'
بالنظر إلى أن المعلمة limit مضبوطة على 2، فإن كل AnnSearchRequest يُرجع نتيجتي بحث. في هذا المثال، يتم إنشاء 3 مثيلات AnnSearchRequest ، مما ينتج عنه إجمالي 6 نتائج بحث.
تكوين استراتيجية إعادة الترتيب
لدمج مجموعات نتائج بحث الشبكة النشطة وإعادة ترتيبها، من الضروري اختيار استراتيجية إعادة ترتيب مناسبة. يقدم ميلفوس نوعين من استراتيجيات إعادة الترتيب:
استراتيجية إعادة الترتيب المرجحة: استخدم هذه الاستراتيجية إذا كانت النتائج تحتاج إلى التركيز على مجال متجه معين. يسمح لك WeightedRanker بتعيين وزن أكبر لحقول متجهات معينة، وإبرازها بشكل أكثر بروزًا.
RRRFRanker (مصنف دمج الرتب المتبادل): اختر هذه الاستراتيجية عند عدم الحاجة إلى تركيز محدد. يوازن RRRFRanker بشكل فعال بين أهمية كل حقل متجه.
لمزيد من التفاصيل حول آليات إعادة الترتيب هذه، يرجى الرجوع إلى إعادة الترتيب.
في هذا المثال، بما أنه لا يوجد تركيز معين على استعلامات بحث محددة، سنستمر في استخدام استراتيجية RRFRanker.
from pymilvus import RRFRanker
ranker = RRFRanker(100)
import io.milvus.v2.service.vector.request.ranker.BaseRanker;
import io.milvus.v2.service.vector.request.ranker.RRFRanker;
BaseRanker reranker = new RRFRanker(100);
reranker := milvusclient.NewRRFReranker().WithK(100)
import { MilvusClient, DataType } from "@zilliz/milvus2-sdk-node";
const rerank = RRFRanker("100");
export rerank='{
"strategy": "rrf",
"params": { "k": 100}
}'
إجراء بحث هجين
قبل الشروع في البحث الهجين، تأكد من تحميل المجموعة. إذا كانت أي حقول متجهة داخل المجموعة تفتقر إلى فهرس أو لم يتم تحميلها في الذاكرة، فسيحدث خطأ عند تنفيذ طريقة البحث الهجين.
from pymilvus import MilvusClient
res = client.hybrid_search(
collection_name="my_collection",
reqs=reqs,
ranker=ranker,
limit=2
)
for hits in res:
print("TopK results:")
for hit in hits:
print(hit)
import io.milvus.v2.common.ConsistencyLevel;
import io.milvus.v2.service.vector.request.HybridSearchReq;
import io.milvus.v2.service.vector.response.SearchResp;
HybridSearchReq hybridSearchReq = HybridSearchReq.builder()
.collectionName("my_collection")
.searchRequests(searchRequests)
.ranker(reranker)
.topK(2)
.build();
SearchResp searchResp = client.hybridSearch(hybridSearchReq);
resultSets, err := client.HybridSearch(ctx, milvusclient.NewHybridSearchOption(
"my_collection",
2,
request1,
request2,
request3,
).WithReranker(reranker))
if err != nil {
fmt.Println(err.Error())
// handle error
}
for _, resultSet := range resultSets {
fmt.Println("IDs: ", resultSet.IDs.FieldData().GetScalars())
fmt.Println("Scores: ", resultSet.Scores)
}
const { MilvusClient, DataType } = require("@zilliz/milvus2-sdk-node")
res = await client.loadCollection({
collection_name: "my_collection"
})
import { MilvusClient, RRFRanker, WeightedRanker } from '@zilliz/milvus2-sdk-node';
const search = await client.search({
collection_name: "my_collection",
data: [search_param_1, search_param_2, search_param_3],
limit: 2,
rerank: rerank
});
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/advanced_search" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-d "{
\"collectionName\": \"my_collection\",
\"search\": ${req},
\"rerank\": {
\"strategy\":\"rrf\",
\"params\": ${rerank}
},
\"limit\": 2
}"
فيما يلي الإخراج:
["['id: 1, distance: 0.006047376897186041, entity: {}', 'id: 2, distance: 0.006422005593776703, entity: {}']"]
باستخدام المعلمة limit=2 المحددة لـ "البحث الهجين"، سيعيد ميلفوس ترتيب النتائج الست التي تم الحصول عليها من عمليات البحث الثلاث. في النهاية، سيعيد فقط النتيجتين الأكثر تشابهًا.