RAG Baseline:BM25、Embedding、Rerank 与引用评测

系列:开源 AI 论文复现实验与代码解读
日期:2026-08-28
适合读者:研究生、科研新人、希望建立可审计 RAG 实验的工程型研究者
检索日期:2026-08-28
复现价值与目标:先把系统拆成可证伪的链
原始 RAG 论文把外部文档看作非参数记忆,并将检索器与生成器结合用于知识密集任务。本文不追求端到端训练,而是复现更基础的实验账本:冻结语料快照、切分规则、查询集和 gold support;保存每阶段的 doc ID 与分数;最后让每个回答陈述指向可回查证据。
最小目标不是证明某一检索器"最好",而是定位误差。若 gold 段落从未进入 top-100,是召回失败;进入候选却没到 top-5,是排序失败;证据正确但答案说错,是生成或提示失败;答案有 [d3] 却不被 d3 支撑,则是归因失败。四者必须使用不同指标,不能被一个总分抹平。
数据合同与逐样本日志
一套可复现 RAG 实验,首先需要数据合同,而不是模型名称。语料表至少保存 doc_id、来源 URI、快照或文件哈希、chunk_id、标题、正文、原文字符区间和切分器版本。doc_id 必须跨运行稳定;若重新切分,应生成新版本并保留父文档关系,不能让同一个 ID 悄悄指向另一段文字。动态网页还要保存抓取时间,否则几周后即使 URL 相同,证据内容也可能已经改变。
查询表至少保存 query_id、原始问题、gold support ID、是否可回答、数据切分和标注说明。gold support 的含义也要写清:它是"包含答案字符串",还是"足以独立支撑陈述"?前者适合便宜的 answer matching,后者才接近引用评测。多跳问题若要求两段证据,命中其中一段不能算完整召回;不可回答问题则需要把正确拒答纳入协议,而不是强迫系统总找一条引用。
每个查询应写一行 JSONL 日志,依次记录 BM25 与 Embedding 的排名和原始分数、融合候选、Rerank 分数、送入生成器的实际文本、模型输出、解析出的引用 ID、延迟与错误。这样一次最终答案失败可以回放到具体阶段,也能检查 top-k 之后是否又被去重、权限过滤或上下文截断悄悄删掉。只保存平均 Recall 和最终回答,会失去最有研究价值的失败轨迹。
语料、查询和模型都应分版本。若用 dev set 选择 chunk 长度、融合权重或候选 k,最终 test 必须保持封存;若持续增加文档,应把时间切分和索引时间写入结果。否则"新方法提升"可能只是索引看到了更新后的证据,或测试查询近重复泄漏进训练集合。
核心模块:四步而不是一个黑盒
1. BM25:词面匹配的强基线
BM25 对查询词 t 和文档 d 的常见单项得分可写为:
I D F ( t ) ⋅ f ( t , d ) ( k 1 + 1 ) f ( t , d ) + k 1 ( 1 − b + b ⋅ ∣ d ∣ / a v g d l ) IDF(t)\cdot\frac{f(t,d)(k_1+1)}{f(t,d)+k_1(1-b+b\cdot |d|/avgdl)} IDF(t)⋅f(t,d)+k1(1−b+b⋅∣d∣/avgdl)f(t,d)(k1+1)
其中 f(t,d) 是词频,|d| 是文档 token 数,avgdl 是语料平均长度;k1 控制词频饱和,b 控制长度归一化。实现中的张量并不复杂,本质是"查询词 × 候选文档"的分数表。难点反而是分词、大小写、中文切词、停用词与字段权重:预处理一变,所谓 BM25 对照组就不再相同。Lucene 官方 BM25Similarity 的默认值为 k1=1.2, b=0.75,本文 toy 也使用这组值,但不声称与 Lucene 的分词和 IDF 细节完全等价。
2. Embedding:把查询和文档独立编码
双塔稠密检索分别计算查询向量 e q ∈ R D e_q\in\mathbb{R}^D eq∈RD 与文档向量 e d ∈ R D e_d\in\mathbb{R}^D ed∈RD,再用内积或余弦相似度排序:
s ( q , d ) = e q T e d s(q,d)=e_q^Te_d s(q,d)=eqTed
文档向量可离线索引,查询只编码一次,因此能扩展到大语料。DPR 论文使用独立的 question encoder 与 passage encoder,并用批内负例训练。稠密检索能跨越部分词面差异,却会受模型领域、query/document 前缀、池化、截断、归一化和索引近似误差影响。本文脚本的默认 256 维特征哈希只是检查"编码---相似度---排序"接口;--backend neural 才调用固定模型名的 Sentence Transformer。
3. Rerank:只对小候选集做联合阅读
Cross-Encoder 将 [query, passage] 一起送入 Transformer,输出一个相关性标量 s r ∈ R s_r\in\mathbb{R} sr∈R。它允许 query token 与 passage token 交互,通常比独立向量更精细,却必须对每个候选重新前向计算。因此合理协议是先让 BM25/Embedding 取 top-k,再重排几十或几百条,而不是对全库逐条打分。
候选集合并也必须写明。本文示例用 Reciprocal Rank Fusion:对每条排序中的名次 r 累加 1 / ( 60 + r ) 1/(60+r) 1/(60+r)。这种融合不要求 BM25 与余弦分数同尺度,适合作为透明基线;它不是论文中唯一或默认方案。Reranker 只能重排已召回候选,漏掉的 gold 文档无法被"救回"。
4. 引用:存在、正确与完整是三回事
引用标记存在,只能证明格式正确。至少应区分:validity 检查 doc ID 是否存在;citation precision 检查单个引用是否相关或支持陈述;citation recall/completeness 检查每个可验证陈述是否被引用集合完整支持。ALCE 论文使用 NLI 模型评估陈述与引用段落的蕴含关系,并明确区分 citation recall 与 precision。
本文 toy evaluator 不运行 NLI,而使用人工给定的 claim→support doc 集合作为 oracle。它故意放入一个错误引用和一个不存在的 ID,确保测试能分别得到 2/3 validity、1/3 precision、1/2 recall。真实实验必须把 oracle 标注或 NLI 判断误差一起报告,不能把自动 entailment 当作事实裁判。

官方论文与代码阅读路线
第一站读 RAG 论文第 2 节:作者把检索文档作为 latent variable,并区分整段输出共用文档的 RAG-Sequence 与每个 token 可依赖不同文档的 RAG-Token。本文的工程管线不是这两种概率模型的等价复刻,只借用了"检索外部记忆再生成"的问题结构。
第二站读 DPR 论文第 3 节和作者仓库。先看 generate_dense_embeddings.py 如何生成 passage 向量,再看 dense_retriever.py 中 query vector、index 和 search_knn 如何连接,最后检查 QA validation 的 answer matching。仓库已被归档为只读,复现时应记录 commit,而不是假定依赖仍能无缝安装。
第三站读 Sentence Transformers 官方 Retrieve & Re-Rank 示例:bi-encoder 负责快速候选检索,CrossEncoder 只重排候选。把示例换成自己数据时,最先固定模型卡、最大长度、是否归一化、batch size、设备和候选 k。
第四站读 ALCE 的 retrieval.py、eval.py 与配置。它不仅给最终回答打分,还保存 top-100 检索结果,并在 citation quality 中区分支撑完整性与无关引用。大型索引成本很高,作者仓库也明确给出磁盘和 GPU 负担;这正说明"下载成功"不等于"协议已经复现"。
最小实验:怎样运行与记录
纯标准库检查无需安装依赖:
bash
python3 code/minimal_rag_baseline.py --check-only
python3 code/minimal_rag_baseline.py --backend toy --top-k 4
真实神经后端需联网下载模型,本文未运行:
bash
python3 -m pip install -r code/requirements.txt
python3 code/minimal_rag_baseline.py --backend neural --top-k 4
脚本打印 BM25、dense 与 reranked doc ID,随后计算 Recall@3 和 MRR。这里的 Recall@k 定义为:对每个查询,top-k 中只要出现任一 gold support 就记为 1,再对查询平均;MRR 使用第一个相关文档名次的倒数。若一个查询有多条必要证据,应另报 set recall 或 nDCG,不能让"命中任意一条"掩盖多跳缺证据。
正式最小复现建议至少比较四组:BM25;Embedding;BM25+Embedding 融合;融合+Reranker。每组使用同一 corpus snapshot、chunk、query、gold、过滤规则与 k,记录逐查询排名,而不只报均值。生成阶段再增加 closed-book、gold-context 和 retrieved-context 三个对照,以区分模型能力上限、检索损失与生成损失。
评测协议与报告表
检索层优先报告 Recall@k、MRR 或 nDCG@k,并同时给候选规模、延迟和索引大小。Rerank 层既要看重排后的 top-k,也要报告 first-stage oracle recall:若 gold 不在候选集,二阶段没有改进空间。端到端层报告任务指标与逐样本答案,但必须并列 citation validity、precision、recall,最好再由人工抽样核对 NLI 误判。
切分是最容易被忽视的变量。chunk 太短会丢上下文,太长会稀释关键词并增加 Cross-Encoder/生成器截断;overlap 会制造近重复,令 Recall 看似改善却增加冗余引用。应冻结 chunk 代码与语料哈希,统计长度分布、重复率、空块和被截断比例。
至少运行多个 seed 的环节是模型训练与生成采样;确定性 BM25 不需要伪造 seed 方差。ANN 索引、GPU kernel 或 sampling 可能带来非确定性,应记录库版本、硬件和参数。若只运行一次神经检索,结果应标成探索性而非稳定结论。
失败分析:按阶段排查
第一类是词表错配。BM25 找不到同义表达,Embedding 又因领域外模型把缩写或专名压错位置。先检查逐查询 top-20、token 化和 query/document 输入模板,再决定是否混合检索或微调;不要直接调生成 prompt 掩盖召回失败。
第二类是分数不可比。直接相加 BM25、cosine 和 Cross-Encoder logits 会让某一路因尺度占优。可先用 RRF,或只在 dev set 上拟合归一化与权重;一旦用 test set 调权重,就发生评测泄漏。
第三类是重排退化。通用 reranker 可能不理解本领域相关性,或候选过长被截掉证据。应比较 rerank 前后逐查询名次,并保留 hard negatives。平均分提升但关键长文档下降,仍可能损害最终引用。
第四类是"引用装饰"。模型引用了真实 doc ID,却把段落没有说过的结论挂上去;也可能每句话堆五个来源来提高召回。修复需要 claim-level 对齐、无关引用惩罚和人工抽查,而不是只检查括号格式。
第五类是答案指标掩盖证据错误。Exact Match 可能因模型参数记忆而正确,即使检索到错误文档;反过来,证据正确也可能因答案表述与 gold 不同被判错。closed-book 与 gold-context 对照能帮助识别这两种情况。
后续科研问题
- 在固定延迟预算下,更多 first-stage candidates 与更强 Reranker 应如何分配计算?
- BM25 与 Embedding 的失败查询是否互补,互补性会随领域和 chunk 长度怎样变化?
- 用 hard negatives 微调 Reranker 后,提升来自真实证据识别,还是数据集词面捷径?
- citation recall 与 precision 的自动 NLI 判断对长陈述、多来源和矛盾证据有多稳健?
- 将"拒答"作为合法输出后,检索置信度、证据冲突和回答正确率如何共同校准?
总结
一个可信的 RAG Baseline 不是把向量库、LLM 和界面接起来,而是建立可回放的证据链:BM25 提供词面基线,Embedding 提供语义候选,Reranker 对小集合联合打分,生成器只能引用稳定 doc ID,评测则把召回、排序、答案和引用支撑分开。
本文完成的是协议级最小复现:公式、模块、官方代码路线、确定性脚本和故意失败的引用单元测试已经对齐;大规模索引、真实神经模型与论文 benchmark 尚未运行。下一步应先在冻结小数据上跑四组消融并保存逐查询日志,再考虑扩大语料或更换生成模型。
参考资料
检索日期:2026-08-28。官方文档、仓库和模型默认值会变化;真实复现需锁定版本、commit、模型 revision 与语料快照。
- Patrick Lewis et al., Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks, NeurIPS 2020。
- Vladimir Karpukhin et al., Dense Passage Retrieval for Open-Domain Question Answering, EMNLP 2020。
- Stephen Robertson et al., Okapi at TREC-3, NIST TREC-3 Proceedings, 1994;该官方目录收录原始论文与作者信息。
- Apache Lucene, BM25Similarity API,参数语义与默认值。
- Nandan Thakur et al., BEIR: A Heterogeneous Benchmark for Zero-shot Evaluation of Information Retrieval Models, 2021;官方仓库。
- Sentence Transformers, Retrieve & Re-Rank 官方示例。
- Tianyu Gao et al., Enabling Large Language Models to Generate Text with Citations, EMNLP 2023;ALCE 作者仓库。
- Meta Research, DPR 官方参考仓库 与
dense_retriever.py。 - Sentence Transformers,
cross-encoder/ms-marco-MiniLM-L6-v2模型卡,配套脚本可选神经 Reranker 的模型来源与用法。