跳到主要内容

实战交叉编码器重排序:余弦相似度遗漏了什么

· 阅读需 11 分钟
Tian Pan
Software Engineer

你的RAG管道检索了前10个文档,但LLM的答案依然有误。你将检索数量增加到50,结果还是错的。令人沮丧的是:正确的文档一直都在向量数据库里——只是排在第23位。这不是召回率的问题,而是排序的问题,而余弦相似度正是罪魁祸首。

向量搜索在找到语义相邻内容方面做得不错,但"语义相邻"和"对这个具体查询最有用"并不是一回事。余弦相似度衡量的是嵌入空间中两个向量之间的夹角,而这个夹角只能捕捉粗粒度的主题接近度。它无法捕捉查询中特定词语与文档中特定词语之间的细粒度交互——"如何防止缓冲区溢出"与"缓冲区溢出利用技术"在向量层面差异微妙,但对于你的检索系统来说却至关重要。

重排序是解决这一架构问题的方案。本文介绍交叉编码器重排序的工作原理、何时将其与BM25混合检索和MMR多样性相结合,以及告诉你是否值得将其加入管道的延迟计算。

余弦相似度实际计算的是什么(以及它计算不了什么)

要理解为什么重排序是必要的,需要精确了解余弦相似度衡量的是什么。双编码器模型——大多数基于嵌入的向量搜索背后使用的就是这种模型——将查询和每个文档分别编码成固定维度的向量,然后测量它们之间的夹角。512维向量的语义内容在编码时就已固定,对于它最终将面对什么查询一无所知。

这造成了一个结构性盲点。两个向量夹角的余弦值告诉你它们"在谈论相似的事情",但不能告诉你文档是否真正回答了查询。一个大量涵盖缓冲区溢出理论的文档,在几何上可能与关于预防的查询接近——但一个只有一段具体介绍缓解技术的文档,尽管更有用,分数却可能更低。

在实践中,还有两个额外的失效模式会加剧这一问题:

高维空间中的集线器现象。 在高维嵌入空间中,某些向量成为"集线器"——无论实际内容相关性如何,它们都以不成比例的频率出现在大量查询向量的最近邻中。这些集线器持续污染top-k结果。

丢弃幅度信息。 余弦相似度将所有向量归一化为单位长度,丢弃了向量幅度中编码的信息。某些嵌入模型在幅度中编码了置信度或信息量;余弦相似度在设计上就丢弃了这些信息。

核心问题在于,双编码器针对在数百万文档上的高效检索进行了优化。这种效率要求独立于查询对文档进行编码。交叉编码器放弃了这种效率,换来了更好的排序质量。

交叉编码器的实际工作原理

交叉编码器将查询和候选文档作为联合输入,将它们一起送入Transformer的注意力层。查询中的每个token都可以关注文档中的每个token。模型输出单一的相关性分数。

这与计算两个预计算向量之间的相似度有本质区别。交叉编码器可以检测到查询中的"溢出预防"与文档中的"栈金丝雀实现"之间的特定对齐关系,即使嵌入模型会将两者映射到与"C++内存安全"几乎相同的向量区域。

代价是计算量。你无法提前编码文档并缓存它们——每个查询-文档对都需要一次新的前向传播。在CPU上使用标准MiniLM大小的模型(约2200万参数),对50个文档对评分需要100-150毫秒。当你对小型候选集进行重排序时,这是可以接受的,但这也是为什么交叉编码器存在于两阶段管道的第二阶段,而不是第一阶段。

质量提升是显著的。在标准基准测试中,交叉编码器始终比双编码器检索高出5-10个nDCG点。在更难的基准测试中,独立评估显示,仅增加约120毫秒的延迟就能带来33-40%的准确率提升。这一比率在不同查询类型下保持稳定:交叉编码器对多跳推理查询特别有价值,在这类查询中,相关性信号取决于查询意图与文档内容之间的微妙交互。

构建两阶段管道

标准的生产配置结合了三个组件:BM25稀疏检索、密集向量搜索和交叉编码器重排序。

第一阶段:使用倒数秩融合的混合检索

并行运行BM25和向量搜索可以获得互补的信号。BM25擅长精确关键词匹配——如果用户搜索特定的函数名或错误代码,BM25能可靠地找到它。向量搜索处理释义和语义变体。单独使用任何一个都不够。

合并这些结果集的标准方法是倒数秩融合(RRF)。RRF不尝试规范化和组合不兼容的分数尺度,而是将每个结果列表转换为排名位置并进行组合:

RRF_score(doc) = 1/(rank_BM25 + 60) + 1/(rank_vector + 60)

常数60是通过实验确定的,能产生稳定的排名;它防止某个列表中排名靠前的文档压倒来自另一个列表的信号。在两个系统中都排名靠前的文档会可靠地浮出水面。只在一个列表中出现的文档获得部分分数。结果是一个包含约50-100个候选的合并列表,准备好进入第二阶段。

第二阶段:交叉编码器重排序

将你的前50-100个RRF候选通过交叉编码器。交叉编码器对每个查询-文档对评分并返回排名列表。你的LLM实际读取的文档现在更多地应该在第1或第2位。

对于大多数团队来说,首选的开源模型是cross-encoder/ms-marco-MiniLM-L-6-v2。它速度快(CPU上100对约50毫秒)、稳定,与纯向量检索相比提供约35%的准确率提升,所需基础设施极少。如果你需要更高质量且有延迟预算,Jina Reranker v3在BEIR基准测试中达到61.94 nDCG@10(开源模型中最佳),典型候选集的延迟在200毫秒以内。Cohere Rerank API在质量上领先(公开基准测试ELO评分1627),但会增加API成本和延迟。

当多样性重要时添加MMR

重排序让你在第1位得到最相关的文档。但有时你希望前5个位置彼此不同——涵盖多部分问题的不同方面,或降低LLM上下文窗口包含五个说同样事情的文档的风险。

最大边际相关性(MMR)是标准解决方案。重排序后,将MMR作为后处理步骤应用:

MMR_score(doc) = (1 − λ) × relevance_score − λ × max_similarity_to_already_selected

λ参数控制多样性-相关性权衡。λ=0时,你得到来自交叉编码器的纯相关性排名。λ=1时,你最大化多样性。0.3到0.7之间的值给出有用的中间行为。该算法贪婪地逐一选择文档,通过每个剩余候选与已选择内容的相似度来惩罚它。

MMR增加的延迟可以忽略不计——它只是在评分完成后应用的简单算术。当你的查询有多个子组件,或用户持续抱怨结果重复时,应用它。当查询足够聚焦,使得top-k文档自然涵盖不同方面时,跳过它。

延迟预算计算

以下是200毫秒总预算的具体计算:

  • 混合检索(BM25+向量搜索,并行):40-60毫秒
  • 交叉编码器重排序,CPU上前50个候选:100-150毫秒
  • 为LLM准备上下文:10-20毫秒
  • 总计:约160-230毫秒

这在CPU上处于200毫秒预算的边缘。保持在预算内的两种方法:

  1. 缩小重排序窗口。 前30个候选而不是50个,大约减少40%的重排序时间,质量损失最小——候选31-50的边际价值很低。
  2. 使用GPU推理。 T4 GPU将50个候选的交叉编码器延迟降至30-50毫秒,即使使用更高质量的模型,也能轻松实现200毫秒以内。

团队犯的错误是孤立地计算延迟成本,而不是问它换来了什么。无论如何,检索需要40毫秒。问题是额外的100毫秒是否能给你足够的排名提升。对于大多数检索质量有可衡量业务影响的用例——客户支持准确性、代码搜索、法律文档检索——是值得的。

不值得的情况:你的查询很简单,向量搜索大多数时候已经在第1位返回正确文档。在已经很好的检索之上添加重排序是没有回报的开销。先进行监测;只有在你能证明排名问题存在时才添加重排序。

需要注意的生产失效模式

领域不匹配。 现成的交叉编码器是在MS MARCO(网络搜索查询和段落)上训练的。如果你的语料库是医疗文档、法律文件或内部工程规格,模型中嵌入的相关性判断可能无法迁移。症状:重排序器自信地为无用文档分配高分。修复方法:使用带有人工相关性标签的领域特定查询-文档对进行微调。

模型更新时的静默回归。 重排序模型版本更新——即使是小版本——也可能使评分分布变化足以降低下游LLM输出质量,而不会有任何明显的错误信号。在生产中固定确切的模型版本,并维护一个包含20-50个已知预期排名的查询-文档对的回归测试套件。在任何模型更新上线之前运行这个测试套件。

没有回退的级联故障。 如果你的交叉编码器是外部API调用并且超时,除非你有回退,否则整个检索管道会失败。实现断路器逻辑:交叉编码器失败时,回退到BM25+向量RRF排名并提供服务。这比重排序结果差,但不是500错误。

重排序无法修复上游检索缺口。 重排序提高了已检索候选上的排名精度。如果正确的文档根本不在你的前100个候选集中,重排序无济于事。在投资重排序基础设施之前,验证你的查询分布的召回率@100是否足够高。如果不高,你的问题是检索,而不是排序——首先修复你的分块策略和嵌入模型。

决策框架

在以下情况下将重排序添加到你的管道:你的检索召回率@100是可接受的但召回率@5很差(文档被检索到但排名较低),你的查询足够复杂以至于出现排名失败,并且你的延迟预算允许100-200毫秒的开销。

在以下情况下跳过:你的精度问题实际上是召回率问题(根本没有检索到正确的文档),你的查询很简单且有明显的关键词信号,或者你的规模使得每次查询的推理成本令人望而却步。

默认起点:部署ms-marco-MiniLM-L-6-v2,对前50个候选重排序,对照一组已知好的查询的保留集,测量你的召回率@1和召回率@3指标的变化。如果你看到有意义的改进,100毫秒的开销几乎肯定是值得付出的。如果你没有看到改进,你已经诊断出你的问题在别处——这同样是有价值的信息。

向量搜索解决了检索中困难的部分:在毫秒内跨数百万文档找到候选。重排序解决了对用户最重要的部分:确保正确的答案就是他们实际看到的。

References:Let's stay in touch and Follow me for more thoughts and updates