Rerank · 重排¶
一句话理解
两阶段检索的第二阶段——把召回阶段的 Top 50/100 候选交给一个更贵更准的 Cross-Encoder 模型精排成 Top 5/10。工业 RAG / 语义搜索的差异化关键——加 Rerank 通常 Recall/NDCG 涨 5-10 点(BEIR 子集平均,具体数字随业务而异)。
SSOT · 主定义页
Rerank · Cross-Encoder 原理 · 召回与 Rerank 黄金比 等以本页为主定义。RAG / Hybrid 等页引用时链回这里。
TL;DR
- 架构差异:召回用 Bi-Encoder(独立编码)、Rerank 用 Cross-Encoder(query+doc 联合编码)
- 核心价值:精度换代价——Rerank 比向量检索准一大截,但慢 100-1000 倍
- 典型选型:bge-reranker v2 / Cohere Rerank 3 / Jina Reranker v2
- 应用位:召回 Top 50 → Rerank → Top 5 给 LLM
- 延迟预算:10 个候选 50-100ms;100 个候选 200-500ms
- "不加 rerank"是 RAG 效果失败的最常见原因之一
1. 业务痛点 · 为什么只有向量检索不够¶
Bi-Encoder 的局限¶
向量检索用的是 Bi-Encoder: - query 和 doc 各自独立编码成向量 - 相似度 = 内积 / 余弦 - 优点:doc 向量可以离线预计算,在线只算 query 一次
这个独立编码的设计决定了上限:q_vec 和 d_vec 是各自独立产生的,没交互。语义复杂的时候(比如"比较 A 和 B 的差异"),向量没法表达细粒度匹配。
Cross-Encoder 的威力¶
Rerank 用 Cross-Encoder: - query + doc 拼成一个输入,一起丢进 Transformer - 全部注意力头交互,产出一个匹配分 - 精度高得多,但每对 query-doc 都要独立跑一次模型
代价:假设召回 100 个候选,Cross-Encoder 要跑 100 次模型。比 Bi-Encoder 慢 100-1000 倍。
量化差异(BEIR 数据集)¶
| 管线 | 平均 NDCG@10 |
|---|---|
| 纯 BM25 | 0.43 |
| 纯 Dense (Bi-Encoder) | 0.47 |
| Hybrid (BM25 + Dense) | 0.53 |
| Hybrid + Rerank | 0.60-0.62 |
加 Rerank 再涨 5-10 个点——这在工业界是差异化级别的提升。
2. 原理深度¶
两阶段检索架构¶
flowchart LR
q[Query] --> stage1[Stage 1: 召回<br/>Bi-Encoder + BM25]
stage1 -->|TopN 50-200| stage2[Stage 2: Rerank<br/>Cross-Encoder]
stage2 -->|TopK 5-10| final[给 LLM / 展示]
为什么要两阶段?
| 阶段 | 范围 | 模型 | 延迟 |
|---|---|---|---|
| 召回 | 百万 - 千万候选 | Bi-Encoder(便宜) | 1-20ms |
| Rerank | 50-200 候选 | Cross-Encoder(贵) | 50-500ms |
直接用 Cross-Encoder 查 100 万 doc = 不可能(几小时延迟)。召回先降到 100 级,再 rerank 精排。
Cross-Encoder 的模型结构¶
典型实现(bge-reranker):
Input: [CLS] query_tokens [SEP] doc_tokens [SEP]
↓
BERT (base / large / multilingual)
↓
[CLS] embedding
↓
Linear + Sigmoid
↓
relevance_score ∈ [0, 1]
训练:对比学习。给 (query, positive_doc, negative_doc) 三元组,让 positive 分 > negative 分。
为什么 Cross-Encoder 更准¶
注意力机制在 query 和 doc 之间全交互。举例:
- Query: "HNSW 的 M 参数"
- Doc 1: "HNSW 是一种图索引,用于最近邻搜索"
- Doc 2: "HNSW 里 M 参数控制图的连通度,调优建议..."
Bi-Encoder:两个 doc 都和 query 语义接近,分数可能差不多。
Cross-Encoder:doc_2 里 "M 参数" 和 query 里 "M 参数" 有显式 token 级对齐,分数显著更高。
3. 关键机制 · 模型家族¶
bge-reranker 系列(开源 SOTA · 团队推荐)¶
| 模型 | 参数量 | 语言 | 延迟 / doc(GPU) |
|---|---|---|---|
bge-reranker-base |
110M | 中英 | 5ms |
bge-reranker-large |
335M | 中英 | 15ms |
bge-reranker-v2-m3 |
560M | 100+ 语言 | 25ms |
bge-reranker-v2-gemma |
2B | 多语 + 最高精度 | 80ms |
Cohere Rerank¶
- Rerank 3:云 API,通用最强之一
- Rerank 3 Multilingual:100+ 语言
- 商业授权,API $1 / 1000 searches
Jina Reranker¶
- v2:开源 + API 双版本
- 多语言、长文档(最多 8k tokens)
- 部署成本友好
其他¶
- Voyage Rerank
- LLM-as-Reranker(RankGPT / RankZephyr)
- FlashRank(轻量级本地,牺牲精度换速度)
LLM-as-Reranker 与 LTR¶
- LLM-as-Reranker: 直接 Prompt LLM 给出相关性分;零样本好,延迟高
- Learning-to-Rank (LTR): LambdaMART / XGBoost-Rank,输入 BM25/vector/freshness/CTR 等特征,工业搜索常用
4. 工程细节¶
延迟控制¶
Rerank 是管线的延迟大头。延迟取决于:
优化方向: - 减少候选数:召回精一点,给 rerank 少一点(从 200 → 50) - 小模型:bge-reranker-base 就够大多数场景 - 批处理:一次性把 N 对 query-doc 送进 GPU - 量化:INT8 能减延迟 30-50% - 并行:多 GPU / 分布式推理
召回与 Rerank 的黄金比¶
经验数据:
| 召回 TopN | Rerank TopK | 场景 |
|---|---|---|
| 20-30 | 5 | 严格延迟(RAG 实时客服) |
| 50-100 | 10 | 标准 RAG |
| 100-200 | 10-20 | 精度优先(合规 / 法律) |
| 200-500 | 10 | 离线批分析 |
部署模式¶
| 模式 | 优 | 劣 |
|---|---|---|
| 独立 Rerank 服务 | 资源隔离、独立扩缩 | 多一跳网络 |
| 向量库内建(Weaviate / Cohere) | 一次 API 搞定 | 绑定供应商 |
| LLM Gateway 统一(LiteLLM / Portkey) | 容易灰度 / 替换 | 中间层抖动 |
部署服务参考¶
- Triton Inference Server —— NVIDIA 推理服务
- TEI (Text Embedding Inference) —— HF 官方 rerank + embed 服务
- vLLM —— 虽然主要是 LLM,也支持 rerank
- Infinity —— 轻量高性能
5. 性能数字¶
bge-reranker-large(GPU)¶
| 候选数 | 延迟(A100) | 延迟(T4) |
|---|---|---|
| 10 | 30ms | 80ms |
| 50 | 80ms | 300ms |
| 100 | 150ms | 600ms |
Cohere Rerank 3(API)¶
| 候选数 | 延迟 | 成本 |
|---|---|---|
| 10 | 100-200ms | $0.001 |
| 50 | 200-400ms | $0.005 |
| 100 | 300-600ms | $0.01 |
精度(BEIR 子集平均)¶
| 模型 | NDCG@10(相对纯 Dense 提升) |
|---|---|
| BM25 + Dense + bge-reranker-base | +5 |
| BM25 + Dense + bge-reranker-large | +7 |
| BM25 + Dense + Cohere Rerank 3 | +8 |
| BM25 + Dense + bge-reranker-v2-gemma | +9 |
6. 代码示例¶
使用 bge-reranker(HuggingFace)¶
from FlagEmbedding import FlagReranker
reranker = FlagReranker('BAAI/bge-reranker-large', use_fp16=True)
query = "HNSW 的 M 参数如何调优"
candidates = [
"HNSW 里 M 参数控制图的连通度...",
"Flat 索引适合小规模向量...",
]
pairs = [[query, doc] for doc in candidates]
scores = reranker.compute_score(pairs)
ranked = sorted(zip(candidates, scores), key=lambda x: -x[1])
用 TEI (Text Embedding Inference) 部署¶
docker run --gpus all -p 8080:80 \
-v $PWD/data:/data \
ghcr.io/huggingface/text-embeddings-inference:latest \
--model-id BAAI/bge-reranker-large \
--revision main
import requests
r = requests.post("http://localhost:8080/rerank", json={
"query": query,
"texts": candidates,
"return_scores": True,
})
Cohere Rerank API¶
import cohere
co = cohere.Client("...")
results = co.rerank(
model="rerank-3-multilingual",
query=query,
documents=candidates,
top_n=10,
)
集成到 RAG 管线¶
def rag_answer(query: str) -> str:
candidates = hybrid_retrieve(query, k=50) # Stage 1: 召回
pairs = [[query, c.content] for c in candidates]
scores = reranker.compute_score(pairs) # Stage 2: Rerank
top_k = sorted(zip(candidates, scores), key=lambda x: -x[1])[:10]
context = "\n\n".join([c.content for c, _ in top_k])
return llm.generate(f"Based on:\n{context}\n\nAnswer: {query}") # Stage 3: LLM
7. 陷阱与反模式¶
- 没加 Rerank:RAG 效果上不去最常见原因;先加 rerank 再调其他
- Rerank 但延迟没做预算:100 个候选 × bge-large 轻松 500ms → 用户感觉慢
- 候选数固定不按场景调:延迟严的场景用 30 候选,精度优先用 100+
- 模型中文用英文版:bge-reranker 有中英双语版别用 fp16 本地 + 纯英文模型
- Rerank 结果不截断:Cross-Encoder 输出分绝对值没意义、截断靠相对排名
- 训练一个自定义 rerank:多数时候直接用现成 SOTA 就够;自训练 ROI 低
- 忽视长文档:Cross-Encoder 输入有 token 限制(通常 512),长 doc 要截断或分段取最大
- 批量不够:一条一条跑 → GPU 利用率 10%;batch=16-32 能到 90%
8. 横向对比 · 延伸阅读¶
权威阅读¶
- Sentence-BERT (Reimers & Gurevych, EMNLP 2019) —— Bi-Encoder 奠基
- ColBERT (Khattab & Zaharia, SIGIR 2020) —— Late interaction 思想
- bge-reranker 技术报告 —— 当前开源 SOTA
- Cohere Rerank 技术博客
- Jina Reranker v2
- Is ChatGPT Good at Search? Investigating LLMs as Re-Ranking Agents (Sun et al., 2023)
相关¶
- Hybrid Search · 向量数据库 · HNSW
- RAG on Lake · RAG 评估