Qdrant 中的多向量重排序表示

时间:30分钟难度:中等

多向量表示(Multivector Representations)是 Qdrant 最强大的功能之一。然而,大多数人并没有有效地使用它们,导致了巨大的内存开销、缓慢的插入速度和计算资源的浪费。

在本教程中,你将了解如何有效地在 Qdrant 中使用多向量表示。

什么是多向量表示?

在大多数向量引擎中,每个文档由单个向量表示——这种方法适用于短文本,但处理较长的文档时往往比较吃力。单向量表示会对标记(token)级嵌入进行池化,这显然会导致部分信息的丢失。

多向量表示提供了一种更细粒度的替代方案,即使用多个向量(通常在标记或短语级别)来表示单个文档。这使得特定查询词与文档相关部分之间的匹配更加精确。这种匹配在像 ColBERT 这样的“后期交互”(Late Interaction)模型中尤为有效,它们保留了标记级嵌入并在查询时进行交互,从而得出相关性评分。

Multivector Representations

正如你稍后将在本教程中看到的,Qdrant 原生支持多向量,因此也原生支持后期交互模型。

为什么标记级向量很有用

有了标记级向量,ColBERT 等模型可以将特定的查询标记与文档中最相关的部分进行匹配,通过后期交互实现高精度的检索。

在后期交互中,每个文档被转换为多个标记级向量,而不是单个向量。查询也会被分词并嵌入为多个向量。然后,使用相似度函数 MaxSim 对查询向量和文档向量进行匹配。你可以在此处查看其计算方式。

在传统检索中,查询和文档被转换为单一嵌入,然后计算相似度。这属于“早期交互”,因为信息在检索前就被压缩了。

什么是重排序(Rescoring),为什么要使用它?

重排序包含两个步骤:

  • 使用快速模型检索相关文档。
  • 使用更准确但较慢的模型(如 ColBERT)对它们进行重排序。

为什么默认索引每个向量是一个问题

在多向量表示中(例如 ColBERT 等后期交互模型所使用的表示),单个逻辑文档会产生数百个标记级向量。在 Qdrant 中使用 HNSW 对这些向量中的每一个进行单独索引,可能导致:

  • 高内存(RAM)占用
  • 由于维护 HNSW 图的复杂性导致插入时间缓慢

然而,由于多向量通常用于重排序阶段(在进行第一次密集向量检索之后),因此通常不需要使用 HNSW 索引这些标记级向量。

相反,它们可以作为多向量字段存储(不使用 HNSW 索引),并在查询时用于重排序,这减少了资源开销并提高了性能。

有关此内容的更多信息,请查看 Qdrant 在我们的“使用 Qdrant 实现 PDF 检索规模化”教程中的详细分解。

使用 Qdrant,你可以完全控制索引的工作方式。你可以通过将 HNSW 的 m 参数设置为 0 来禁用索引。

from qdrant_client import QdrantClient, models

client = QdrantClient("https://:6333")
collection_name = "dense_multivector_demo"
client.create_collection(
    collection_name=collection_name,
    vectors_config={
        "dense": models.VectorParams(
            size=384,
            distance=models.Distance.COSINE
            # Leave HNSW indexing ON for dense
        ),
        "colbert": models.VectorParams(
            size=128,
            distance=models.Distance.COSINE,
            multivector_config=models.MultiVectorConfig(
                comparator=models.MultiVectorComparator.MAX_SIM
            ),
            hnsw_config=models.HnswConfigDiff(m=0)  # Disable HNSW for reranking
        )
    }
)

通过在多向量上禁用 HNSW,你可以:

  • 节省计算资源。
  • 减少内存使用量。
  • 加快向量上传速度。

如何使用 FastEmbed 生成多向量

让我们演示如何使用 FastEmbed 有效地使用多向量,它将 ColBERT 封装在一个简单的 API 中。

安装 FastEmbed 和 Qdrant

pip install qdrant-client[fastembed]>=1.14.2

分步指南:ColBERT + Qdrant 设置

确保 Qdrant 正在运行并创建一个客户端

from qdrant_client import QdrantClient, models

# 1. Connect to Qdrant server
client = QdrantClient("https://:6333")

1. 编码文档

接下来,编码你的文档

from fastembed import TextEmbedding, LateInteractionTextEmbedding
# Example documents and query
documents = [
    "Artificial intelligence is used in hospitals for cancer diagnosis and treatment.",
    "Self-driving cars use AI to detect obstacles and make driving decisions.",
    "AI is transforming customer service through chatbots and automation.",
    # ...
]
query_text = "How does AI help in medicine?"

dense_documents = [
    models.Document(text=doc, model="BAAI/bge-small-en")
    for doc in documents
]
dense_query = models.Document(text=query_text, model="BAAI/bge-small-en")

colbert_documents = [
    models.Document(text=doc, model="colbert-ir/colbertv2.0")
    for doc in documents
]
colbert_query = models.Document(text=query_text, model="colbert-ir/colbertv2.0")

2. 创建 Qdrant 集合

然后创建一个同时包含两种向量类型的 Qdrant 集合。请注意,我们为 dense(密集)向量保留了索引,但为将用于重排序的 colbert 向量关闭了索引。

collection_name = "dense_multivector_demo"
client.create_collection(
    collection_name=collection_name,
    vectors_config={
        "dense": models.VectorParams(
            size=384,
            distance=models.Distance.COSINE
            # Leave HNSW indexing ON for dense
        ),
        "colbert": models.VectorParams(
            size=128,
            distance=models.Distance.COSINE,
            multivector_config=models.MultiVectorConfig(
                comparator=models.MultiVectorComparator.MAX_SIM
            ),
            hnsw_config=models.HnswConfigDiff(m=0)  # Disable HNSW for reranking
        )
    }
)

3. 上传文档(密集向量 + 多向量)

现在上传向量,设置 batch_size=8。虽然我们文档不多,但始终建议使用批处理。

points = [
    models.PointStruct(
        id=i,
        vector={
            "dense": dense_documents[i],
            "colbert": colbert_documents[i]
        },
        payload={"text": documents[i]}
    ) for i in range(len(documents))
]
client.upload_points(
    collection_name="dense_multivector_demo", 
    points=points, 
    batch_size=8
)

单次调用实现检索 + 重排序

现在让我们执行搜索

results = client.query_points(
    collection_name="dense_multivector_demo",
    prefetch=models.Prefetch(
        query=dense_query,
        using="dense",
    ),
    query=colbert_query,
    using="colbert",
    limit=3,
    with_payload=True
)
  • 密集向量快速检索出前几个候选文档。
  • Colbert 多向量使用标记级的 MaxSim 进行细粒度精确重排序。
  • 返回前 3 个结果。

结论

正确使用时,多向量搜索是向量数据库最强大的功能之一。在 Qdrant 中利用此功能,你可以:

  • 原生存储标记级嵌入。
  • 禁用索引以减少开销。
  • 在一次 API 调用中完成快速检索和精确重排序。
  • 高效扩展后期交互。

结合 FastEmbed 和 Qdrant 可以构建出生产就绪的 ColBERT 风格重排序流水线,且不会浪费资源。你可以在本地执行此操作,也可以使用 Qdrant Cloud。Qdrant 提供了易于使用的 API 来开启你的搜索引擎之旅,如果你准备好深入研究,请在 Qdrant Cloud 免费注册并开始构建。

此页面有用吗?

感谢您的反馈!🙏

很抱歉听到这个消息。😔 你可以在 GitHub 上编辑此页面,或者创建一个 GitHub Issue。