使用Qdrant向量数据库重新排序混合搜索结果

混合搜索结合了密集和稀疏检索,以提供精确和全面的结果。通过使用ColBERT进行重新排序,您可以进一步优化搜索输出,以实现最大相关性。

在本指南中,我们将向您展示如何在Qdrant中实现带有重排的混合搜索,利用密集、稀疏和后期交互嵌入来创建一个高效、高精度的搜索系统。让我们开始吧!

概述

让我们从分解架构开始:

image3.png

处理向量数据库(VDB)中的密集、稀疏和延迟交互嵌入

数据摄取阶段

以下是我们如何设置高级混合搜索的方法。这个过程与我们之前所做的类似,但增加了一些强大的功能:

  1. Documents: Just like before, we start with the raw input—our set of documents that need to be indexed for search.
  2. Dense Embeddings: We’ll generate dense embeddings for each document, just like in the basic search. These embeddings capture the deeper, semantic meanings behind the text.
  3. Sparse Embeddings: This is where it gets interesting. Alongside dense embeddings, we’ll create sparse embeddings using more traditional, keyword-based methods. Specifically, we’ll use BM25, a probabilistic retrieval model. BM25 ranks documents based on how relevant their terms are to a given query, taking into account how often terms appear, document length, and how common the term is across all documents. It’s perfect for keyword-heavy searches.
  4. Late Interaction Embeddings: Now, we add the magic of ColBERT. ColBERT uses a two-stage approach. First, it generates contextualized embeddings for both queries and documents using BERT, and then it performs late interaction—matching those embeddings efficiently using a dot product to fine-tune relevance. This step allows for deeper, contextual understanding, making sure you get the most precise results.
  5. Vector Database: All of these embeddings—dense, sparse, and late interaction—are stored in a vector database like Qdrant. This allows you to efficiently search, retrieve, and rerank your documents based on multiple layers of relevance.

image2.png

搜索系统中的查询检索和重新排序过程

检索阶段

现在,让我们讨论一下用户提交查询后如何获取最佳结果:

  1. User’s Query: The user enters a query, and that query is transformed into multiple types of embeddings. We’re talking about representations that capture both the deeper meaning (dense) and specific keywords (sparse).
  2. Embeddings: The query gets converted into various embeddings—some for understanding the semantics (dense embeddings) and others for focusing on keyword matches (sparse embeddings).
  3. Hybrid Search: Our hybrid search uses both dense and sparse embeddings to find the most relevant documents. The dense embeddings ensure we capture the overall meaning of the query, while sparse embeddings make sure we don’t miss out on those key, important terms.
  4. Rerank: Once we’ve got a set of documents, the final step is reranking. This is where late interaction embeddings come into play, giving you results that are not only relevant but tuned to your query by prioritizing the documents that truly meet the user’s intent.

实现

让我们在本节中看看它的实际应用。

额外设置

这次,我们使用的是FastEmbed——一个轻量级的Python库,专为生成嵌入而设计,并且它支持流行的文本模型,开箱即用。首先,你需要安装它:

pip install fastembed

以下是我们将从FastEmbed中提取的模型:

from fastembed import TextEmbedding, LateInteractionTextEmbedding, SparseTextEmbedding 

数据摄取

和之前一样,我们将把文档转换为嵌入,但由于FastEmbed,这个过程更加简单,因为所有你需要的模型都方便地集中在一个地方。

嵌入

首先,让我们加载我们需要的模型:

dense_embedding_model = TextEmbedding("sentence-transformers/all-MiniLM-L6-v2")
bm25_embedding_model = SparseTextEmbedding("Qdrant/bm25")
late_interaction_embedding_model = LateInteractionTextEmbedding("colbert-ir/colbertv2.0")

现在,让我们将我们的文档转换为嵌入:

dense_embeddings = list(dense_embedding_model.embed(doc for doc in documents))
bm25_embeddings = list(bm25_embedding_model.embed(doc for doc in documents))
late_interaction_embeddings = list(late_interaction_embedding_model.embed(doc for doc in documents))

由于我们正在处理多种类型的嵌入(密集、稀疏和后期交互),我们需要将它们存储在一个支持多向量设置的集合中。我们之前创建的集合在这里不适用,因此我们将创建一个专门用于处理这些不同类型嵌入的新集合。

创建集合

现在,我们正在Qdrant中为我们的混合搜索设置一个新的集合,并配置正确的设置以处理我们正在使用的所有不同向量类型。

以下是操作方法:

from qdrant_client.models import Distance, VectorParams, models

client.create_collection(
    "hybrid-search",
    vectors_config={
        "all-MiniLM-L6-v2": models.VectorParams(
            size=len(dense_embeddings[0]),
            distance=models.Distance.COSINE,
        ),
        "colbertv2.0": models.VectorParams(
            size=len(late_interaction_embeddings[0][0]),
            distance=models.Distance.COSINE,
            multivector_config=models.MultiVectorConfig(
                comparator=models.MultiVectorComparator.MAX_SIM,
            )
        ),
    },
    sparse_vectors_config={
        "bm25": models.SparseVectorParams(modifier=models.Modifier.IDF
        )
    }
)

这里发生了什么?我们正在创建一个名为“hybrid-search”的集合,并且我们正在配置它以处理:

  • 密集嵌入 来自模型 all-MiniLM-L6-v2,使用余弦距离进行比较。
  • Late interaction embeddings 来自 colbertv2.0,同样使用余弦距离,但采用多向量配置以使用最大相似度比较器。
  • 稀疏嵌入 来自 BM25,用于基于关键字的搜索。它们使用 dot_product 进行相似度计算。

此设置确保所有不同类型的向量都能正确存储和比较,以支持您的混合搜索。

插入或更新数据

接下来,我们需要将文档及其多个嵌入插入到hybrid-search集合中:

from qdrant_client.models import PointStruct
points = []
for idx, (dense_embedding, bm25_embedding, late_interaction_embedding, doc) in enumerate(zip(dense_embeddings, bm25_embeddings, late_interaction_embeddings, documents)):
  
    point = PointStruct(
        id=idx,
        vector={
            "all-MiniLM-L6-v2": dense_embedding,
            "bm25": bm25_embedding.as_object(),
            "colbertv2.0": late_interaction_embedding,
        },
        payload={"document": doc}
    )
    points.append(point)

operation_info = client.upsert(
    collection_name="hybrid-search",
    points=points
)

这段代码通过创建一个PointStruct对象列表将所有内容整合在一起,每个对象包含嵌入和相应的文档。

对于每个文档,它添加:

  • 密集嵌入用于深层次的语义含义。
  • BM25 嵌入 用于强大的基于关键字的搜索。
  • ColBERT embeddings 用于精确的上下文交互。

完成后,这些点使用upsert方法上传到我们的“hybrid-search”集合中,确保一切就绪。

检索

为了检索,是时候将用户的查询转换为所需的嵌入。以下是你可以如何做到这一点:

dense_vectors = next(dense_embedding_model.query_embed(query))
sparse_vectors = next(bm25_embedding_model.query_embed(query))
late_vectors = next(late_interaction_embedding_model.query_embed(query))

混合搜索的真正魔力在于prefetch参数。这使您可以一次性运行多个子查询,结合密集和稀疏嵌入的力量。以下是设置方法,之后我们执行混合搜索:

prefetch = [
        models.Prefetch(
            query=dense_vectors,
            using="all-MiniLM-L6-v2",
            limit=20,
        ),
        models.Prefetch(
            query=models.SparseVector(**sparse_vectors.as_object()),
            using="bm25",
            limit=20,
        ),
    ]

这段代码通过运行两个子查询来启动混合搜索:

  • 一种使用来自“all-MiniLM-L6-v2”的密集嵌入来捕捉查询的语义。
  • 另一种使用来自BM25的稀疏嵌入进行强关键词匹配。

每个子查询限制为20个结果。这些子查询使用prefetch参数捆绑在一起,使它们能够并行运行。

重新排序

现在我们已经得到了初步的混合搜索结果,是时候使用后期交互嵌入对它们进行重新排序以获得最大精度。以下是您可以如何做到这一点:

results = client.query_points(
         "hybrid-search",
        prefetch=prefetch,
        query=late_vectors,
        using="colbertv2.0",
        with_payload=True,
        limit=10,
)

让我们看看在应用重新排名后位置如何变化。注意一些文档如何根据其与后期交互嵌入的相关性在排名中发生变化。

文档第一次查询排名第二次查询排名排名变化
在机器学习中,特征缩放是归一化自变量或特征范围的过程。目标是确保所有特征对模型的贡献相等,特别是在SVM或k近邻等算法中,距离计算很重要。11No Change
特征缩放通常用于数据预处理,以确保特征在同一尺度上。这对于基于梯度下降的算法尤为重要,因为尺度较大的特征可能会不成比例地影响成本函数。26Moved Down
无监督学习算法,如聚类方法,可能会受益于特征缩放,这确保了具有较大数值范围的特征不会主导学习过程。34Moved Down
数据预处理步骤,包括特征缩放,可以显著影响机器学习模型的性能,使其成为建模流程中的关键部分。52Moved Up

太好了!我们现在已经探索了重新排序的工作原理并成功实现了它。

重新排序的最佳实践

重新排序可以显著提高搜索结果的相关性,尤其是在与混合搜索结合使用时。以下是一些需要牢记的最佳实践:

  • 实施混合重排序:结合基于关键字(稀疏)和基于向量(密集)的搜索结果,以实现更全面的排序系统。
  • 持续测试和监控: 定期评估您的重新排序模型,以避免过拟合,并及时进行调整以保持性能。
  • 平衡相关性和延迟: 重新排序可能在计算上非常昂贵,因此要在相关性和速度之间找到平衡。因此,第一步是检索相关文档,然后对其进行重新排序。

结论

重新排序是一种强大的工具,可以提升搜索结果的相关性,特别是在与混合搜索方法结合使用时。虽然由于其复杂性可能会增加一些延迟,但将其应用于较小的、预过滤的结果子集可以确保速度和相关性。

Qdrant 提供了一个易于使用的 API 来开始构建您自己的搜索引擎,所以如果您准备好开始,请在 Qdrant Cloud 免费注册并开始构建

这个页面有用吗?

感谢您的反馈!🙏

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