引言:向量检索的尴尬
问题场景
用户提问:"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@10 | NDCG@10 | 延迟 |
|---|---|---|---|
| BM25关键词 | 0.18 | 0.21 | 5ms |
| Embedding召回 | 0.33 | 0.39 | 25ms |
| + BGE-Reranker-base | 0.41 | 0.47 | 65ms |
| + BGE-Reranker-large | 0.43 | 0.49 | 95ms |
| + GPT-4 Rerank | 0.46 | 0.52 | 800ms |
关键发现:
- Rerank带来8-10个百分点的提升
- 轻量级Reranker(base)已有很好效果
- LLM Rerank最准但慢8-10倍