• 文章
  • 任何*嵌入模型都可以成为后期交互模型……如果你给它机会!
返回机器学习

任何*嵌入模型都可以成为后期交互模型……如果你给它机会!

Kacper Łukawski

·

2024年8月14日

Any* Embedding Model Can Become a Late Interaction Model... If You Give It a Chance!

* 至少是任何开源模型,因为你需要访问其内部结构。

你可以将密集嵌入模型用于后期交互

Qdrant 1.10 引入了对多向量表示的支持,后期交互是这类模型的一个突出示例。本质上,文档和查询都由多个向量表示,而识别最相关文档涉及根据相应的查询和文档嵌入之间的相似性计算分数。如果你不熟悉这种范式,我们更新的混合搜索文章解释了多向量表示如何提升检索质量。

图 1:我们可以可视化相应的文档-查询嵌入对之间的后期交互。

Late interaction model

有许多专门的后期交互模型,例如 ColBERT,但看来常规的密集嵌入模型也可以有效地以这种方式使用

在这项研究中,我们将证明,传统上用于单向量表示的标准密集嵌入模型,可以通过使用输出 token 嵌入作为多向量表示,有效地适应后期交互场景。

通过测试 Qdrant 的多向量功能进行检索,我们将展示这些模型在检索性能上可以与专门的后期交互模型匹敌或超越,同时提供更低的复杂性和更高的效率。这项工作重新定义了密集模型在高级搜索管道中的潜力,为优化检索系统提供了一种新方法。

理解嵌入模型

嵌入模型的内部工作原理可能会让一些人感到惊讶。模型不会直接在输入文本上操作;相反,它需要一个 tokenization 步骤将文本转换为 token 标识符序列。然后,每个 token 标识符会通过一个嵌入层,该层将其转换为密集向量。本质上,嵌入层充当一个查找表,将 token 标识符映射到密集向量。然后将这些向量作为输入送入 transformer 模型。

图 2:在向量添加到 transformer 模型之前进行的 tokenization 步骤。

Input token embeddings

输入 token 嵌入是上下文无关的,并在模型训练过程中学习。这意味着每个 token 总是获得相同的嵌入,无论其在文本中的位置如何。在此阶段,token 嵌入不知道它们出现的上下文。transformer 模型的作用是使这些嵌入情境化。

关于 transformer 模型中注意力的作用,已经有很多讨论,但本质上,这种机制负责捕获 token 间的关系。每个 transformer 模块接收一个 token 嵌入序列作为输入,并产生一个输出 token 嵌入序列。两个序列长度相同,每个 token 嵌入都在当前步骤中通过来自其他 token 嵌入的信息得到丰富。

图 3:产生输出 token 嵌入序列的机制。

Output token embeddings

图 4:嵌入模型执行的最后一步是对输出 token 嵌入进行池化,以生成输入文本的单个向量表示。

Pooling

有几种池化策略,但无论模型使用哪种,输出始终是单个向量表示,这不可避免地会丢失一些关于输入的信息。这类似于给出某人详细、一步步前往最近杂货店的指示,与只是大致指向一个方向。虽然模糊的方向在某些情况下可能足够,但详细的指示更有可能达到期望的结果。

使用输出 Token 嵌入作为多向量表示

我们经常忽略输出 token 嵌入,但事实是——它们也充当输入文本的多向量表示。那么,为什么不探索在多向量检索模型中使用它们,类似于后期交互模型呢?

实验发现

我们进行了几项实验,以确定是否可以有效地使用输出 token 嵌入来替代传统的后期交互模型。结果非常有前景。

数据集模型实验NDCG@10
SciFactprithivida/Splade_PP_en_v1稀疏向量0.70928
colbert-ir/colbertv2.0后期交互模型0.69579
all-MiniLM-L6-v2单个密集向量表示0.64508
输出 token 嵌入0.70724
BAAI/bge-small-en单个密集向量表示0.68213
输出 token 嵌入0.73696
NFCorpusprithivida/Splade_PP_en_v1稀疏向量0.34166
colbert-ir/colbertv2.0后期交互模型0.35036
all-MiniLM-L6-v2单个密集向量表示0.31594
输出 token 嵌入0.35779
BAAI/bge-small-en单个密集向量表示0.29696
输出 token 嵌入0.37502
ArguAnaprithivida/Splade_PP_en_v1稀疏向量0.47271
colbert-ir/colbertv2.0后期交互模型0.44534
all-MiniLM-L6-v2单个密集向量表示0.50167
输出 token 嵌入0.45997
BAAI/bge-small-en单个密集向量表示0.58857
输出 token 嵌入0.57648

这些实验的源代码是开源的,并使用了 beir-qdrant,这是 Qdrant 与 BeIR 库的集成。虽然此软件包不是由 Qdrant 团队正式维护的,但对于那些有兴趣尝试各种 Qdrant 配置以了解它们如何影响检索质量的人来说,它可能很有用。所有实验均使用 Qdrant 在精确搜索模式下进行,确保结果不受近似搜索的影响。

即使是简单的 all-MiniLM-L6-v2 模型也可以以后期交互模型的方式应用,从而对检索质量产生积极影响。然而,最好的结果是使用 BAAI/bge-small-en 模型实现的,它优于稀疏模型和后期交互模型。

重要的是要注意,ColBERT 没有在 BeIR 数据集上进行训练,这使得其性能完全超出领域。尽管如此,all-MiniLM-L6-v2训练数据集 也缺乏任何 BeIR 数据,但其表现仍然非常出色。

密集模型与后期交互模型的比较分析

检索质量不言而喻,但还有其他重要因素需要考虑。

我们测试的传统密集嵌入模型比后期交互模型或稀疏模型复杂度更低。参数更少,这些模型预计在推理期间更快,维护成本效益更高。以下是实验中使用的模型比较

模型参数数量
prithivida/Splade_PP_en_v1109,514,298
colbert-ir/colbertv2.0109,580,544
BAAI/bge-small-en33,360,000
all-MiniLM-L6-v222,713,216

使用输出 token 嵌入的一个反对意见是与 ColBERT 等模型相比,存储要求增加。例如,all-MiniLM-L6-v2 模型生成 384 维的输出 token 嵌入,这是 ColBERT 等模型生成的 128 维嵌入的三倍。这种增加不仅导致更高的内存使用,还影响检索的计算成本,因为计算距离需要更多时间。通过向量压缩来缓解这个问题将非常有意义。

探索多向量表示的量化

二进制量化通常对高维向量更有效,这使得输出维度相对较低的 all-MiniLM-L6-v2 模型不太适合这种方法。然而,标量量化似乎是一个可行的替代方案。下表总结了量化对检索质量的影响。

数据集模型实验NDCG@10
SciFactall-MiniLM-L6-v2输出 token 嵌入0.70724
输出 token 嵌入 (uint8)0.70297
NFCorpusall-MiniLM-L6-v2输出 token 嵌入0.35779
输出 token 嵌入 (uint8)0.35572

重要的是要注意,量化并不总是以相同水平保持检索质量,但在本例中,标量量化对检索性能的影响似乎最小。影响可以忽略不计,而内存节省是巨大的。

我们成功地在内存使用减少四倍的情况下保持了原始质量。此外,量化向量需要 384 字节,而 ColBERT 需要 512 字节。这使得内存使用量减少了 25%,而检索质量几乎保持不变。

实际应用:使用密集模型提升检索质量

如果你正在使用其中一个句子 transformer 模型,输出 token 嵌入会默认计算。虽然单个向量表示在存储和计算方面更高效,但无需丢弃输出 token 嵌入。根据我们的实验,这些嵌入可以显著提升检索质量。你可以将单个向量和输出 token 嵌入都存储在 Qdrant 中,使用单个向量进行初始检索步骤,然后使用输出 token 嵌入对结果进行重新排序。

图 5:一个仅依赖输出 token 嵌入进行重新排序的单模型管道。

Single model reranking

为了演示这个概念,我们在 Qdrant 中实现了一个简单的重新排序管道。此管道使用密集嵌入模型进行初始过采样检索,然后完全依赖输出 token 嵌入进行重新排序步骤。

单模型检索和重新排序基准测试

我们的测试重点是使用同一个模型进行检索和重新排序。报告的指标是 NDCG@10。在所有测试中,我们应用了 5 倍的过采样因子,这意味着检索步骤返回 50 个结果,然后在重新排序步骤中缩小到 10 个。以下是一些 BeIR 数据集的结果

数据集all-miniLM-L6-v2BAAI/bge-small-en
仅密集嵌入密集 + 重新排序仅密集嵌入密集 + 重新排序
SciFact0.645080.702930.682130.73053
NFCorpus0.315940.342970.296960.35996
ArguAna0.501670.453780.588570.57302
Touche-20200.169040.196930.130550.19821
TREC-COVID0.472460.63790.457880.53539
FiQA-20180.368670.415870.310910.39067

基准测试的源代码是公开可用的,你可以在 beir-qdrant 软件包的仓库中找到它

总的来说,使用相同模型添加重新排序步骤通常会提高检索质量。然而,各种后期交互模型的质量通常基于在使用 BM25 进行初始检索时的重新排序性能进行报告。这项实验旨在展示如何有效地使用单个模型进行检索和重新排序,结果非常有前景。

现在,让我们探索如何使用 Qdrant 1.10 中引入的新 Query API 来实现这一点。

为后期交互设置 Qdrant

Qdrant 1.10 中的新 Query API 使得构建更复杂的检索管道成为可能。我们可以使用池化后创建的单个向量进行初始检索步骤,然后使用输出 token 嵌入对结果进行重新排序。

假设集合名为 my-collection 并配置为存储两个命名向量:dense-vectoroutput-token-embeddings,以下是在 Qdrant 中创建这样一个集合的方式

from qdrant_client import QdrantClient, models

client = QdrantClient("http://localhost:6333")

client.create_collection(
    collection_name="my-collection",
    vectors_config={
        "dense-vector": models.VectorParams(
            size=384,
            distance=models.Distance.COSINE,
        ),
        "output-token-embeddings": models.VectorParams(
            size=384,
            distance=models.Distance.COSINE,
            multivector_config=models.MultiVectorConfig(
                comparator=models.MultiVectorComparator.MAX_SIM
            ),
        ),
    }
)

由于它们是由同一个 all-MiniLM-L6-v2 模型生成的,两个向量的大小相同。

from sentence_transformers import SentenceTransformer

model = SentenceTransformer("all-MiniLM-L6-v2")

现在,我们可以创建一个重新排序管道,而不是仅使用单个密集向量进行搜索 API 调用。首先,我们使用密集向量检索 50 个结果,然后使用输出 token 嵌入对它们进行重新排序,以获得前 10 个结果。

query = "What else can be done with just all-MiniLM-L6-v2 model?"

client.query_points(
    collection_name="my-collection",
    prefetch=[
        # Prefetch the dense embeddings of the top-50 documents
        models.Prefetch(
            query=model.encode(query).tolist(),
            using="dense-vector",
            limit=50,
        )
    ],
    # Rerank the top-50 documents retrieved by the dense embedding model
    # and return just the top-10. Please note we call the same model, but
    # we ask for the token embeddings by setting the output_value parameter.
    query=model.encode(query, output_value="token_embeddings").tolist(),
    using="output-token-embeddings",
    limit=10,
)

亲自尝试实验

在实际场景中,你可能更进一步,先计算 token 嵌入,然后执行池化以获得单个向量表示。这种方法可以让你一次完成所有操作。

开始尝试在 Qdrant 中构建复杂重新排序管道的最简单方法是使用 Qdrant Cloud 上的永久免费集群并阅读 Qdrant 的文档

这些实验的源代码是开源的,并使用了 beir-qdrant,这是 Qdrant 与 BeIR 库的集成。

未来方向和研究机会

在检索过程中使用输出 token 嵌入的初步实验取得了可喜的成果。然而,我们计划进行进一步的基准测试以验证这些发现,并探索将稀疏方法用于初始检索。此外,我们旨在研究量化对多向量表示的影响及其对检索质量的影响。最后,我们将评估检索速度,这是许多应用的关键因素。

此页面是否有用?

感谢你的反馈!🙏

得知此情况,我们感到很抱歉。😔 你可以在 GitHub 上编辑此页面,或者创建一个 GitHub issue。