怎么证明 Rerank 真的有用?nDCG、P95 延迟与冻结测试集
结论先放前面:证明一个检索策略有效,不能靠"挑几条 query 看效果",而要靠三件事:分级相关性指标(nDCG)、尾部延迟(P95)、以及"调参用 dev、验收用冻结 test"的纪律。少任何一件,你的结论都可能是自欺欺人。
文章目录
- 怎么证明 Rerank 真的有用?nDCG、P95 延迟与冻结测试集
- 一、先讲一个自欺欺人的场景
- 二、指标一:nDCG,比 Top1 更细的尺子
- 2.1 Top1 和 MRR 的局限
- 2.2 nDCG 的手算逻辑
- 2.3 真实手算示例
- 三、指标二:P50/P95,平均值会骗你
- 四、纪律一:dev 调参,test 冻结
- 4.1 问题:边调边测 = 过拟合
- 4.2 解法:拆分 + 冻结
- 五、纪律二:hard negative,让指标不虚高
- 5.1 问题:太简单的评估集
- 5.2 解法:加入近似干扰文档
- 六、把四件事串起来:一次可信的评估长什么样
- 七、常见自欺行为自查表
- 八、写在最后
一、先讲一个自欺欺人的场景
假设你刚给检索链路加了 Rerank,想证明它有用。最简单的办法:
- 挑 3 条你记得的 query 跑一遍。
- 发现 Rerank 把相关文档排到了第一。
- 宣布:Rerank 有效,上线。
问题在哪?
- 样本偏差:你挑的 query 可能恰好是 Rerank 擅长的。
- 指标单一:只看 Top1,不知道整体排序是否变好。
- 没有代价意识:Rerank 让 P95 延迟从 500ms 涨到 2 秒,你没提。
- 边调边测:你一边看结果一边调参数,调出的"最优参数"只是过拟合了这几条 query。
我在 EasySearch 2.3 上做 Week3 实验时,专门设计了一套流程来防这些坑。这篇文章把它摊开讲。
二、指标一:nDCG,比 Top1 更细的尺子
2.1 Top1 和 MRR 的局限
Week2 我们用过 Top1 Acc 和 MRR,但它们有个共同问题:只关心"第一个"相关结果。
实际检索里,一个 query 可能有多个相关文档,相关程度还不同:
relevance=2:直接相关,能直接解决问题。relevance=1:部分相关,有参考价值。relevance=0:不相关。
假设两个排序结果:
排序A:[relevance=1, relevance=2, relevance=0] 排序B:[relevance=2, relevance=1, relevance=0]Top1 指标看:A 的第一名是部分相关,B 的第一名是直接相关——B 更好。但如果只看"有没有命中",A 和 B 前两名都包含了同样的文档集合。Top1 和 Recall 都没法完整刻画这种"排序质量"的差异。
这就是nDCG要解决的问题。
2.2 nDCG 的手算逻辑
nDCG 分三步算:
第一步:DCG(折损累积增益)
位置越靠后,相关性的贡献打折扣:
DCG = Σ (2^rel - 1) / log2(rank + 1)第二步:IDCG(理想 DCG)
把所有文档按相关性从高到低排,算出的 DCG 就是理论最大值。
第三步:nDCG = DCG / IDCG
归一化到 0~1,1.0 表示完美排序。
2.3 真实手算示例
用我们实验报告里的例子:返回前三名等级为[1, 2, 0]。
DCG = (2^1-1)/log2(2) + (2^2-1)/log2(3) + (2^0-1)/log2(4) = 1/1.0 + 3/1.585 + 0/2.0 = 2.8928理想排序是[2, 1, 0]:
IDCG = 3/1.0 + 1/1.585 + 0/2.0 = 3.6309nDCG = 2.8928 / 3.6309 = 0.7967关键点:nDCG 惩罚的是"部分相关排在直接相关前面"。即使相关文档都召回了,排错位置也会扣分。
三、指标二:P50/P95,平均值会骗你
只看平均延迟是最常见的自欺方式。来看我们实验的真实延迟数据(EasySearch 2.3 + 本地 CPU):
| 策略 | P50 (ms) | P95 (ms) |
|---|---|---|
| RRF | 167.82 | 577.74 |
| RRF+Rerank(5) | 752.32 | 1661.41 |
| RRF+Rerank(10) | 1445.16 | 1967.96 |
如果只看 P50,Rerank(10) 的中位请求约 1.45 秒,好像“可以接受”。
但这 25 条串行 dev 样本算出的插值 P95 已接近 2 秒。样本很小,不能直接外推成精确的线上用户比例。
P95 能暴露中位数看不到的尾部延迟;它是否可接受,还要结合真实并发、目标硬件和业务 SLO 再判断。
工程原则:评估检索策略,质量指标(nDCG)和尾部延迟(P95)必须同时出现。只报质量的,是在隐藏成本;只报延迟的,是在回避效果。
四、纪律一:dev 调参,test 冻结
这是最容易被忽视、也最重要的一条。
4.1 问题:边调边测 = 过拟合
如果你用同一批 query 反复做这两件事:
- 跑实验,看指标。
- 发现某个 query 效果不好,调参数。
那么你调出来的“最优参数”可能过拟合这批 query。反复使用 dev 做模型选择属于开发集过拟合风险;只有当 test 信息被用于调参时,才构成更明确的测试集泄漏。两者要区分。
4.2 解法:拆分 + 冻结
我的做法(Week3 实验):
40 条 query ├── 25 条 dev(开发集):随便调参、随便跑、随便看 └── 15 条 test(测试集):人工复核标签后冻结冻结的含义:
- test 只在所有参数定下来之后,运行一次。
- 跑完的结果就是最终结论,不管好坏都不许回头改参数再跑。
- 如果 test 结果不好,承认它,分析原因,改进留给下一轮。
我目前的实验状态如实说:25 条 dev 已经跑完(用于选参数),15 条 test 还在人工复核标签,没有冻结,所以没有最终结论。dev 数字可以作为开发阶段实验结果,但不能包装成冻结 test 或泛化结论。
五、纪律二:hard negative,让指标不虚高
5.1 问题:太简单的评估集
如果你的文档库里,相关文档和不相关文档差异巨大(比如 query 问"Elasticsearch 黄灯",候选里混着"苹果手机评测"),那任何检索策略都能拿高分。
这种评估集测出来的指标是虚高的,没有区分度。
5.2 解法:加入近似干扰文档
hard negative(难负例)指的是:主题和关键词接近,但不能真正回答 query 的文档。
我的 Week3 文档集(80 条)专门加了这类文档。比如:
- query 问"磁盘 await 很高接口变慢"(期望:磁盘 IO 文档)
- 部分相关文档:
ops-cpu-003“Load Average 高但 CPU 不高”,标签为 relevance=1,因为正文也涉及不可中断 IO 等待。 - hard negative:
ops-net-005“跨可用区调用延迟升高”,同样描述调用变慢,但标签为 relevance=0,不能直接回答磁盘 await 问题。
有了 hard negative,才能测出策略之间真正的差异——比如我们的实验就发现,Rerank 在好几个这种 case 上把部分相关文档排到了直接相关前面,导致 nDCG 下降。
六、把四件事串起来:一次可信的评估长什么样
一次可信的 Rerank 效果评估,流程应该是:
1. 建评估集:80+ 文档(含 hard negative),40 条 query(0/1/2 分级标签) 2. 拆 dev/test:25 dev 调参,15 test 冻结 3. dev 上跑:对比 BM25 / KNN / RRF / RRF+Rerank 的 Top1/MRR/nDCG@10 4. dev 上选参:确定 rerank 候选数(5/10/20) 5. 冻结参数,test 只跑一次 6. 报告:质量指标 + P50/P95 延迟 + badcase 分析 + 实验边界这里还要检查“候选深度”和“最终评估深度”是否一致。本项目的Rerank(5)只能返回 5 条,却报告 nDCG@10,因此不能把它与返回 Top10 的策略做完全公平的 nDCG 横向比较;10 与 20 都最终评估 Top10,可以直接比较。
我们在 dev 上的真实结果(80 文档、25 query):
| 策略 | Top1 | MRR | nDCG@10 | P95 (ms) |
|---|---|---|---|---|
| KNN | 0.960 | 0.960 | 0.891 | 320.65 |
| RRF | 0.840 | 0.925 | 0.871 | 577.74 |
| RRF+Rerank(10) | 0.920 | 0.946 | 0.881 | 1967.96 |
结论如实说:在这个 dev 集上,Rerank 没有超过单路 KNN;其 P95 约为 KNN 的 6.1 倍(1967.96ms vs 320.65ms)。这个结论只有在 test 冻结运行后才能最终确认,但 dev 数据已经足够说明“Rerank 不是免费的午餐”。
七、常见自欺行为自查表
发布检索效果结论前,对照检查:
- 评估集有没有 hard negative,还是全是"一眼就能区分"的文档?
- 指标有没有包含 nDCG(分级相关性)和 P95(尾部延迟)?
- test 是不是只跑了一次?有没有边看结果边调参?
- 有没有如实呈现"策略变差"的 case,还是只挑了变好的?
- 报告里有没有写明数据规模、硬件环境(CPU/GPU)?
- 结论有没有注明是 dev 还是冻结 test 的结果?
如果任何一条打叉,你的结论就先别急着发。
八、写在最后
检索系统的效果证明,本质上是一场和自己的博弈:
- 你的直觉想让你挑好看的数字。
- 你的工程纪律应该逼你看完整的数字。
nDCG 描述分级排序质量,P95 描述本次样本中的尾部延迟,冻结 test 用于减少调参污染。它们能让结论更可信,但小样本 P95 仍不能直接代表线上用户等待分布。
下一篇我会写:8 个真实 badcase 的证据链——Rerank 到底在哪些场景会翻车,以及怎么判断是召回的锅还是精排的锅。
环境:EasySearch 2.3.0 + Python 3.14.4 + sentence-transformers 5.6.0 + BAAI/bge-reranker-base(CPU)
你做检索评测时,有没有遇到过"dev 上调得很好,一上 test 就掉"的情况?欢迎评论区交流。