跳到主要内容
版本:v2.5.x

多向量混合搜索

在许多应用中,可以通过标题和描述等丰富的信息集或文本、图像和音频等多种模式来搜索对象。例如,如果文本或图片与搜索查询的语义相符,就可以搜索包含一段文本和一张图片的推文。混合搜索将这些不同领域的搜索结合在一起,从而增强了搜索体验。Milvus 允许在多个向量字段上进行搜索,同时进行多个近似近邻(ANN)搜索,从而支持这种搜索。如果要同时搜索文本和图像、描述同一对象的多个文本字段或稠密和稀疏向量以提高搜索质量,多向量混合搜索尤其有用。

Hybrid Search Workflow混合搜索工作流程

多向量混合搜索集成了不同的搜索方法或跨越了各种模态的 Embedding:

  • 稀疏-稠密向量搜索稠密向量是捕捉语义关系的绝佳方法,而稀疏向量则是精确匹配关键词的高效方法。混合搜索结合了这些方法,既能提供广泛的概念理解,又能提供精确的术语相关性,从而改善搜索结果。通过利用每种方法的优势,混合搜索克服了单独方法的局限性,为复杂查询提供了更好的性能。以下是结合语义搜索和全文搜索的混合检索的详细指南

  • 多模态向量搜索:多模态向量搜索是一种功能强大的技术,可以跨文本、图像、音频等各种数据类型进行搜索。这种方法的主要优势在于它能将不同的模式统一为一种无缝、连贯的搜索体验。例如,在产品搜索中,用户可能会输入一个文本查询来查找用文本和图像描述的产品。通过混合搜索方法将这些模式结合起来,可以提高搜索准确性或丰富搜索结果。

示例

让我们考虑一个真实世界的使用案例,其中每个产品都包含文字描述和图片。根据可用数据,我们可以进行三种类型的搜索:

  • **语义文本搜索:**这涉及使用稠密向量查询产品的文本描述。可以使用BERTTransformers等模型或OpenAI 等服务生成文本嵌入。

  • 全文搜索:在这里,我们使用稀疏向量的关键词匹配来查询产品的文本描述。BM25等算法或BGE-M3SPLADE等稀疏 Embedding 模型可用于此目的。

  • **多模态图像搜索:**这种方法使用带有稠密向量的文本查询对图像进行查询。可以使用CLIP 等模型生成图像嵌入。

本指南将引导您通过一个结合上述搜索方法的多模态混合搜索示例,给出产品的原始文本描述和图像嵌入。我们将演示如何存储多向量数据并使用 Reranker 策略执行混合搜索。

创建具有多个向量字段的 Collection

创建 Collection 的过程包括三个关键步骤:定义 Collection Schema、配置索引参数和创建 Collection。

定义 Schema

对于多向量混合搜索,我们应该在一个 Collection schema 中定义多个向量字段。默认情况下,每个 Collection 最多可容纳 4 个向量字段。不过,如果有必要,可以根据需要调整proxy.maxVectorFieldNum ,在一个 Collection 中最多包含 10 个向量字段。

本示例将以下字段纳入 Schema 模式:

  • id:作为存储文本 ID 的主键。该字段的数据类型为INT64

  • text:用于存储文本内容。该字段的数据类型为VARCHAR ,最大长度为 1000 字节。enable_analyzer 选项设置为True ,以便于全文搜索。

  • text_dense:用于存储文本的稠密向量。该字段的数据类型为FLOAT_VECTOR ,向量维数为 768。

  • text_sparse:用于存储文本的稀疏向量。该字段的数据类型为SPARSE_FLOAT_VECTOR

  • image_dense:用于存储产品图像的稠密向量。该字段的数据类型为FLOAT_VETOR ,向量维数为 512。

由于我们将使用内置的 BM25 算法对文本字段进行全文搜索,因此有必要在 Schema 中添加 MilvusFunction 。有关详细信息,请参阅全文搜索

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)

创建索引

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"
)

创建 Collection

使用前两个步骤中配置的 Collection Schema 和索引创建名为demo 的 Collection。

from pymilvus import MilvusClient

client.create_collection(
collection_name="my_collection",
schema=schema,
index_params=index_params
)

插入数据

本节根据前面定义的 Schema 将数据插入my_collection Collection。在插入过程中,确保所有字段(有自动生成值的字段除外)的数据格式正确。在本例中

  • id代表产品 ID 的整数

  • text:包含产品描述的字符串

  • text_dense一个包含 768 个浮点数值的列表,表示文本描述的密集 Embedding

  • image_dense代表产品图片稠密 Embedding 的 512 个浮点数值的列表

您可以使用相同或不同的模型为每个字段生成稠密 Embedding。在本例中,两个高密度嵌入的维度不同,说明它们是由不同的模型生成的。以后定义每个查询时,请务必使用相应的模型生成相应的查询嵌入。

由于本示例使用内置的 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
)

创建多个 AnnSearchRequest 实例

混合搜索是通过在hybrid_search() 函数中创建多个AnnSearchRequest 来实现的,其中每个AnnSearchRequest 代表一个特定向量字段的基本 ANN 搜索请求。因此,在进行混合搜索之前,有必要为每个向量字段创建一个AnnSearchRequest

此外,通过在AnnSearchRequest 中配置expr 参数,可以为混合搜索设置过滤条件。请参阅过滤搜索过滤

说明

在混合搜索中,每个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]

参数limit 设置为 2 时,每个AnnSearchRequest 会返回 2 个搜索结果。在本示例中,创建了 3 个AnnSearchRequest 实例,总共产生了 6 个搜索结果。

配置 Reranker 策略

要对 ANN 搜索结果集进行合并和 Reranking,选择适当的 Reranking 策略至关重要。Milvus 提供两种重排策略:

  • 加权排名:如果结果需要强调某个向量字段,请使用该策略。WeightedRanker 可以为某些向量字段赋予更大的权重,使其更加突出。

  • RRFRanker(互易排名融合 Ranker):在不需要特别强调的情况下选择此策略。RRFRanker 能有效平衡每个向量字段的重要性。

有关这些重排机制的更多详情,请参阅Reranking

在本例中,由于没有特别强调特定的搜索查询,我们将采用 RRFRanker 策略。

from pymilvus import RRFRanker

ranker = RRFRanker(100)

在启动混合搜索之前,请确保已加载 Collection。如果 Collection 中的任何向量字段缺少索引或未加载到内存中,执行混合搜索方法时就会出错。

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)

输出结果如下:

["['id: 1, distance: 0.006047376897186041, entity: {}', 'id: 2, distance: 0.006422005593776703, entity: {}']"]

在为混合搜索指定limit=2 参数后,Milvus 将对三次搜索得到的六个结果进行 Reranker 排序。最终,它们将只返回最相似的前两个结果。