Reranker(重排器)

0 阅读6分钟

向量检索负责"快",Reranker负责"准"

1.为什么有了向量检索,还需要Reranker?

在RAG系统中,向量检索(Vector Retrieval)负责从海量知识库中快速召回Top-K条相关文本。它的优势是快——即使数据库里有上亿条文档,也能在毫秒级完成检索。

但"快"的代价是"精度有限"。向量检索(双塔模型)把文本压缩成固定长度的向量,这个过程必然会损失信息。所以Top-K的顺序不一定完全准确,可能:

  • 真正相关的文档被排在了第11位,没有被召回
  • 排名靠前的文档其实和问题关系不大

Reranker(重排器) 的任务就是:在候选集已经很小(比如Top-50)之后,再仔细判断"当前问题与每条候选文本是否真正相关",用更强的模型重新打分,重新排序。

一句话总结: 向量检索是"海选",Reranker是"决赛评委"。

2.两种主流模型框架:Bi-Encoder和Cross-Encoder

要理解Reranker为什么比向量检索更精准,需要先理解两种模型架构的区别:

架构代表模型特点
Bi-Encoder(双塔)Sentence-BERT、OpenAI Embedding快,适合召回
Cross-Encoder(交叉编码器)BERT-Reranker、Cohere Rerank准,适合重排

2.1 Bi-Encoder(双塔编码器)

Bi-Encoder有两个独立的编码器(或共享权重的同一个编码器):

  • 问题塔: 将用户问题编码成一个向量
  • 文档塔 将每条候选文档分别编码成向量

💡之前的Embedding模型就是Bi-Encoder架构。

工作流程:

  1. 所有文档的向量可以离线预先计算好,存入向量数据库
  2. 用户提问时,只需要实时计算问题向量(1次推理)
  3. 用向量相似度计算,从数据库中快速检索Top-K

优点: 速度快,因为文档向量可以提前算好,在线只需要算一个问题向量。 缺点: 问题和文档在编码过程中互相看不到对方,各自独立编码成向量,会丢失交互信息。

2.2 Cross-Encoder(交叉编码器)

Cross-Encoder只有一个编码器,但它同时接收问题和文档,把[问题 + 文档]拼接在一起作为一个整体输入:

输入:[CLS] 今天天气怎么样? [SEP] 明天北京晴转多云,气温15-25℃[SEP]
                        ↓
                 Cross-Encoder 模型
                        ↓
                 输出:相关性分数(0-1)

关键区别: 在Cross-Encoder的注意力机制(Self-Attention)中,问题可以看到文档的每一个词,文档也可以看到问题的每一个词。它们之间可以进行充分的语义交互,从而更准确判断相关性。

优点: 精度高,能捕捉细粒度的语义关系 缺点: 无法预先计算,每判断一对[问题 + 文档]都要做一次完整的模型推理,速度慢。如果有50条候选文档,就需要推理50次。

3.Bi-Encoder vs Cross-Encoder:直观对比

对比维度Bi-Encoder(向量检索)Cross-Encoder(Reranker)
输入方式问题和文档分开编码问题和文档拼接在一起输入
交互程度无交互,只能通过向量相似度间接比较充分交互,注意力机制互相可见
在线推理次数1次(只算问题向量)N次(N = 候选文档数)
速度极快(毫秒级)较慢(取决于模型大小)
精度一般更高
适用场景第一阶段召回(海量)→几百/几千第二阶段精排(几百 →Top-K)
能否离线预处理✅文档向量可以提前算好❌必须在线实时计算

4.Reranker的典型工作流程

在实际的RAG系统中,两个阶段配合使用:

用户提问
    ↓
1.Bi-Encoder 向量检索(海量知识库 → Top-50)
    ↓
2.Cross-Encoder Reranker精排(Top-50 → 重新打分排序)
    ↓
3.返回最终的Top-5给LLM生成答案

为什么不能只用Cross-Encoder? 因为Cross-Encoder太慢了。如果知识库有100万文档,每条都和问题做一次完整推理,延迟会高达数秒甚至数十秒,用户无法接受。

为什么不能只用Bi-Encoder? 因为Bi-Encoder精度有限,可能把真正相关的文档排到后面。用Cross-Encoder在少量候选集上做二次筛选,能显著提升最终结果质量。

这就是经典的 "粗排 + 精排" 思想,在推荐系统,搜索引擎中已经应用了很多年,RAG只是沿用了这套成熟架构。

5.代码示例

from sentence_transformers import CrossEncoder
import numpy as np

# 加载 Cross-Encoder 模型(轻量级,适合入门)
model = CrossEncoder('cross-encoder/ms-marco-MiniLM-L-6-v2')

# 用户问题
query = "入职需要准备什么材料?"

# 向量检索召回的 Top-5 候选(来自知识库)
candidates = [
    "新员工入职需携带身份证、学历证书复印件、一寸照片。",
    "公司每周三下午举办新人培训会。",
    "离职手续需要提前一个月提交书面申请。",
    "入职材料包括劳动合同、体检报告、工资卡信息。",
    "公司食堂午餐时间为12:00-13:00。"
]

# 构建 (问题, 候选文档) 对
pairs = [[query, doc] for doc in candidates]

# Cross-Encoder 推理,得到相关性分数(0~1 之间)
scores = model.predict(pairs)

# 按分数从高到低排序
ranked = sorted(zip(candidates, scores), key=lambda x: x[1], reverse=True)

# 输出结果
print("Reranker 排序结果:")
for idx, (doc, score) in enumerate(ranked, 1):
    print(f"{idx}. (score: {score:.4f}) {doc}")

预期输出:

#Reranker 排序结果:
#1. (score: 0.98) 新员工入职需携带身份证、学历证书复印件、一寸照片。
#2. (score: 0.95) 入职材料包括劳动合同、体检报告、工资卡信息。
#3. (score: 0.45) 公司每周三下午举办新人培训会。
#4. (score: 0.12) 公司食堂午餐时间为12:00-13:00。
#5. (score: 0.08) 离职手续需要提前一个月提交书面申请。

可以看到,Cross-Encoder 成功把真正相关的文档排到了前面,把不相关的文档(食堂、离职)压到了最后。

6.Reranker的使用建议

  1. 候选集大小适中: Reranker的候选集通常建议在50~200条 之间。太少的话,召回可能遗漏好结果,太多的话,Reranker推理太慢。
  2. 模型选型: 中文场景优先选BAAI/bge-reranker-v2-m3;英文场景可以用ms-marco-MiniLM系列。
  3. 延迟敏感场景: 如果对响应速度要求极高,可以: 用更小的 Reranker 模型(如 MiniLM-L-2) 减少候选集数量(如 Top-20) 甚至跳过 Reranker,直接用向量检索结果
  4. 评估效果: 建议用一些测试用例对比“加 Reranker”和“不加 Reranker”的最终回答质量,验证在你的业务场景中是否值得增加这一环节。

一句话记住:向量检索解决“找得到”的问题,Reranker 解决“排得准”的问题。两者配合,才能在速度和精度之间取得最佳平衡。