RAG系统的精排利器:Rerank技术详解

2 阅读8分钟

引言:向量检索的尴尬

问题场景

用户提问:"Python中如何处理大文件的内存溢出?"

向量检索召回Top-10结果:
  1. Python文件读取基础教程 (相似度: 0.82)
  2. 内存管理最佳实践 (相似度: 0.81)
  3. 大数据处理框架介绍 (相似度: 0.80)
  4. Python性能优化技巧 (相似度: 0.79)
  5. 分块读取大文件方案 (相似度: 0.78) ← 最相关!
  ...

问题:
  - 最相关的结果排在第5位
  - 向量相似度不等于实际相关性
  - 直接送给LLM会影响生成质量

为什么会这样?

Embedding模型的局限:

1. 语义匹配 ≠ 任务相关
   - "大文件"和"大数据"语义相似
   - 但对用户问题的帮助不同

2. 双塔架构的天然缺陷
   - Query和Doc分别编码
   - 只通过向量内积交互
   - 缺少深度语义交互

3. 单一相似度得分
   - 只看向量距离
   - 忽略了关键词、结构等其他信号

类比:
  就像相亲只看照片(Embedding)
  没有面对面深入交流(Rerank)

Rerank的核心价值

多阶段检索架构:

阶段1:向量召回(Embedding)
  - 从100万文档 → 召回Top-100
  - 目标:快速、广覆盖
  - 可以有漏召,但不能太慢

阶段2:重排序(Rerank)
  - 从100文档 → 精排Top-10
  - 目标:准确、强相关
  - 可以慢一点,但必须准

送给LLM:
  - 只给最相关的Top-10
  - 提升生成质量
  - 降低token成本

效果对比:
  直接用召回结果:答案准确率 65%
  加入Rerank:答案准确率 82% ↑

核心原理:深度交互 vs 向量匹配

Embedding(召回)的工作方式

双塔架构(Bi-Encoder):

Query: "如何优化Python性能"
   ↓ 独立编码
  [0.2, 0.5, 0.8, ...]  (768维向量)

Document: "Python性能优化的10个技巧"
   ↓ 独立编码
  [0.3, 0.4, 0.9, ...]  (768维向量)

计算相似度:
  cosine(query_vec, doc_vec) = 0.87

特点:
  ✓ Query和Doc可以预先编码并缓存
  ✓ 检索时只需计算向量内积(非常快)
  ✓ 适合百万级大规模检索

  ✗ Query和Doc没有深度交互
  ✗ 只能捕捉粗粒度语义匹配

Rerank的工作方式

单塔架构(Cross-Encoder):

输入拼接:
  "[CLS] 如何优化Python性能 [SEP] Python性能优化的10个技巧 [SEP]"

   ↓ 整体编码(Query和Doc一起处理)

  Transformer内部:
    - Query的每个词都能看到Doc的每个词
    - Doc的每个词都能看到Query的每个词
    - 充分的注意力交互

   ↓ 输出相关性得分

  0.94(比Embedding的0.87更准确)

特点:
  ✓ Query和Doc深度交互
  ✓ 可以捕捉细粒度匹配信号
  ✓ 相关性判断更准确

  ✗ 每个(Query, Doc)对都要重新计算
  ✗ 速度慢,只能处理少量候选

关键区别对比

维度Embedding (Bi-Encoder)Rerank (Cross-Encoder)
架构双塔独立编码单塔拼接编码
交互深度浅层(向量内积)深层(注意力交互)
可缓存性✅ Doc向量可预计算❌ 每次都要重算
速度极快(10ms/100万文档)慢(10ms/100文档)
准确性中等(粗排)高(精排)
适用阶段召回重排序
规模百万-千万级10-1000级

工程类比

Embedding = 海选(看简历)
  - 处理10000份简历
  - 5分钟快速筛选
  - 选出100人进入下一轮

Rerank = 面试(深入交流)
  - 面试100人
  - 每人30分钟深入沟通
  - 选出10人发offer

主流Rerank方法

方法1:Cross-Encoder(最常用)

原理

基于BERT的交叉编码器:

输入格式:
  [CLS] Query文本 [SEP] Document文本 [SEP]

内部处理:
  1. Token化
  2. 通过12层Transformer(每层都有交叉注意力)
  3. [CLS] token的输出 → 全连接层 → 相关性得分

训练数据:
  (Query, Doc+, Doc-) 三元组
  - Doc+:真正相关的文档(标签=1)
  - Doc-:不相关的文档(标签=0)

损失函数:
  二分类损失或排序损失

代表模型

1. BGE-Reranker系列(智源)
   - bge-reranker-base: 278M参数
   - bge-reranker-large: 560M参数
   - 中英文效果都很好

2. Cohere Rerank(商业)
   - API调用,无需部署
   - 效果强,但有成本

3. Cross-Encoder from Sentence-Transformers
   - ms-marco-MiniLM-L-6-v2(轻量)
   - ms-marco-electra-base(平衡)

效果示例

场景:从100个召回文档中精排出Top-10

召回阶段(Embedding):
  - MRR@10: 0.68
  - NDCG@10: 0.72

加入Rerank(Cross-Encoder):
  - MRR@10: 0.81 (+13分)
  - NDCG@10: 0.84 (+12分)

代价:
  - 延迟增加: 20ms → 50ms
  - 但准确率大幅提升

方法2:LLM-based Rerank

原理

直接用大语言模型打分:

Prompt模板:
  """
  请评估以下文档与问题的相关性,打分0-10:

  问题:{query}
  文档:{document}

  相关性得分:
  """

模型输出:
  8.5

优势:
  - 理解能力强(推理、逻辑)
  - 可以用prompt调整标准
  - 无需专门训练

劣势:
  - 速度慢(每个Doc要调用一次)
  - 成本高(尤其商业API)
  - 可能不稳定(输出格式)

适用场景

✓ 小规模重排(10-20个候选)
✓ 对准确率要求极高
✓ 有推理需求(如"文档是否回答了问题的子问题X")

✗ 大规模重排(100+文档)
✗ 实时性要求高(<100ms)
✗ 成本敏感

方法3:RankGPT(LLM排序)

原理

不是逐个打分,而是让LLM直接排序:

Prompt:
  """
  以下是检索到的10个文档,请按照与问题的相关性重新排序:

  问题:{query}

  文档列表:
  [1] 文档1内容
  [2] 文档2内容
  ...
  [10] 文档10内容

  请输出重排后的文档ID序列(如:[3,1,7,2,...])
  """

模型输出:
  [5, 1, 3, 8, 2, 7, 4, 9, 6, 10]

优势:
  - 一次调用完成所有排序
  - 比逐个打分更高效
  - 可以考虑文档间的相对关系

挑战:
  - Prompt长度限制(只能排10-20个)
  - 对模型能力要求高
  - 输出格式解析有风险

方法4:混合重排

多信号融合

综合多个信号进行排序:

信号1:向量相似度(Embedding)
  score_vec = 0.85

信号2:关键词匹配(BM25)
  score_bm25 = 0.72

信号3:深度语义(Cross-Encoder)
  score_rerank = 0.91

最终得分:
  final_score = 0.3 × score_vec
              + 0.2 × score_bm25
              + 0.5 × score_rerank
              = 0.865

权重调整:
  - 根据业务场景调优
  - 可以用机器学习自动学习权重(LTR)

规则增强

在重排时加入业务规则:

规则1:时间新鲜度
  - 最近1个月的文档:得分 × 1.2
  - 超过1年的文档:得分 × 0.8

规则2:来源权威性
  - 官方文档:得分 × 1.3
  - 用户评论:得分 × 0.9

规则3:内容质量
  - 文档长度太短(<100字):得分 × 0.7
  - 包含代码示例:得分 × 1.1

好处:
  ✓ 融合业务知识
  ✓ 灵活可控
  ✓ 不依赖模型训练

实际应用架构

标准三阶段检索

阶段1:粗召回(Embedding)
  输入:100万文档
  方法:向量检索(ANN)
  输出:Top-1000
  耗时:20-50ms
  目标:高召回率

阶段2:重排序(Rerank)
  输入:1000文档
  方法:Cross-Encoder
  输出:Top-100
  耗时:50-100ms
  目标:高精度

阶段3:精排(可选)
  输入:100文档
  方法:LLM打分或混合排序
  输出:Top-10
  耗时:100-200ms
  目标:极致准确

最终:送给LLM生成答案

性能优化架构

批处理优化

朴素方案(慢):
  for doc in documents:
      score = reranker(query, doc)  # 100次串行调用

优化方案(快):
  scores = reranker(query, documents)  # 1次批量调用

  内部实现:
    - 将100个(query, doc)对打包成batch
    - GPU并行计算
    - 一次前向传播处理所有对

  提速:3-5倍

分层Rerank

当候选文档很多时(如1000个):

方案1:全部Rerank(慢)
  - 1000个文档全部过Cross-Encoder
  - 耗时:500ms

方案2:分层Rerank(快)
  - 轻量级Rerank:1000 → 200(耗时100ms)
    使用小模型(如6层BERT)
  - 重量级Rerank:200 → 10(耗时50ms)
    使用大模型(如12层BERT)
  - 总耗时:150ms(节省70%)

  效果几乎不损失!

成本权衡

不同方案的成本对比:

方案A:只用Embedding
  - 延迟:30ms
  - 成本:$0.001/千次
  - 准确率:72%

方案B:Embedding + Cross-Encoder
  - 延迟:80ms
  - 成本:$0.005/千次(自部署)
  - 准确率:84% ↑

方案C:Embedding + LLM Rerank
  - 延迟:300ms
  - 成本:$0.05/千次(API调用)
  - 准确率:87% ↑

选择策略:
  - 实时问答(<100ms):方案A或B
  - 复杂推理:方案C
  - 大部分RAG场景:方案B(最佳性价比)

效果对比与数据

典型提升幅度

实验:MS MARCO数据集(文档检索)

方法MRR@10NDCG@10延迟
BM25关键词0.180.215ms
Embedding召回0.330.3925ms
+ BGE-Reranker-base0.410.4765ms
+ BGE-Reranker-large0.430.4995ms
+ GPT-4 Rerank0.460.52800ms

关键发现

  • Rerank带来8-10个百分点的提升
  • 轻量级Reranker(base)已有很好效果
  • LLM Rerank最准但慢8-10倍