面试知识库
中 进阶

检索评测指标与离线评测#

一句话答案#

检索评测用标注好的「query → 相关文档」集合(qrels),对系统返回的 top-k 算 Recall@k / Precision@k / Hit Rate(找没找到)、MRR(第一个对的排多前)、NDCG(分级相关性加位置折扣);RAG 里要分层评测:召回层、精排层、生成层和端到端分开看,才能定位是哪一层的问题。

核心要点

ES写入链路与搜索质量 列过这几个指标的一句话定义,Agent评测平台设计 讲了数据集版本化和发布门禁,本篇讲指标的计算细节、各自的盲区、标注集怎么建,以及离线指标和线上效果为什么对不上。

1. 指标定义与各自的盲区#

设某个 query 的相关文档集合为 R,系统返回的前 k 条为 S_k:

指标公式回答什么问题盲区
Hit Rate@k1 如果 `R ∩ S_k≥ 1`,否则 0,对 query 取平均
Recall@k`R ∩ S_k/
Precision@k`R ∩ S_k/ k`
MRRmean(1 / 第一个相关文档的名次),k 内没有则记 0第一条对的排得多靠前只看第一条,后面的全不管
MAP每个相关文档命中位置的 Precision 求和再除以R(没召回的相关文档记 0),再对 query 平均
NDCG@kDCG@k / IDCG@k分级相关性 + 位置折扣的综合排序质量依赖增益映射,档位定义不同结果不可比

RAG 场景里,Hit Rate@k 和 Recall@k 通常最重要:LLM 读的是整个 top-k,答案所在的 chunk 在第 1 位还是第 3 位,对生成的影响往往没有「在不在 top-k 里」大;但 k 较大时位置仍有影响,长上下文中段的信息容易被忽略(见 长上下文处理)。排序指标(MRR、NDCG)更适合评 reranker,或者 top-k 很小的场景。

2. NDCG:分级相关性怎么算#

DCG@k  = Σ_{i=1}^{k}  (2^{rel_i} − 1) / log2(i + 1)      (指数增益,也有直接用 rel_i 的线性增益版本)
IDCG@k = 把所有已标注文档按 rel 从高到低理想排序后的 DCG@k
NDCG@k = DCG@k / IDCG@k ∈ [0, 1]
plaintext
  • 位置折扣 1/log2(i+1):第 1 位折扣为 1,第 2 位约 0.63,第 3 位 0.5,越往后越不值钱。
  • 分级相关性:以电商的 ESCI 标注为例,E(Exact 完全匹配)、S(Substitute 替代品)、C(Complement 配件)、I(Irrelevant 无关)四档,可以映射成 3/2/1/0。指数增益会拉大高档位的权重。
  • IDCG 要用全部已标注的相关文档算,不能只用系统召回到的那些,否则系统漏召回反而不扣分,这是常见的实现 bug。
  • 档位定义本身会影响结论:比如每个 query 只有一个 E、指数增益又很陡时,NDCG 主要由这个 E 的位置决定,接近 MRR,S 和 C 的相对顺序对分数的影响被明显压低。

计算好的逐 query 分数要保留,不要只存平均值:做版本对比、找 bad case、算置信区间都要用到。

3. 分层评测:定位问题出在哪一层#

层评什么输入指标
召回层各路召回和融合后的候选池query → 候选池 top-NRecall@N(N 取进 rerank 的条数),分路单独算
精排层reranker 在固定候选池上的排序同一批候选NDCG@k、MRR;同时报告候选池的 Recall@N,作为精排的上限
生成层给定正确上下文时 LLM 答得对不对query + 标准上下文答案正确率、Faithfulness
端到端用户实际拿到的答案query答案正确率、Context Recall/Precision、任务完成率
  • 归因:召回到了但答错 → 生成层问题;没召回到 → 检索层问题;召回池里有但被 rerank 挤出 top-k → 精排层问题。逐 query 打标签后按类别统计,就知道下一步该改哪一层。
  • 端到端和生成层的评测方法(LLM-as-Judge、RAGAS 的 Faithfulness / Context Precision 等)见 LLM评测方法。
  • 离线评测和线上检索的执行方式要一致:离线常用暴力精确检索,线上是 ANN 近似检索;编码时的池化方式、归一化、截断长度只要有一处不一致,query 和文档就不在同一个向量空间里,不会报错,只是效果变差。

4. 标注集怎么建#

来源做法风险
人工标注写标注规范(每一档的判定标准和例子),多人标注算一致性(如 Cohen’s Kappa)贵、慢;标注员对领域理解不一致
合成 query让 LLM 读一个 chunk 生成它能回答的问题,这个 chunk 就是正例生成的问题和原文措辞高度重合,偏向 BM25,比真实 query 简单;一个问题可能多个 chunk 都能回答,但只标了一个
线上日志用点击、采纳、复制作为弱标签位置偏差(排在前面的更容易被点);只能看到系统展示过的文档
公开分级数据集如 ESCI(电商搜索 E/S/C/I 四档)、MS MARCO、BEIR领域和自有语料不同,需要和自有文档对齐(如按商品 ID join)

合成 query 的质量控制:

  • 要求 LLM 改写措辞、生成不同难度(直接问、换说法、多跳),不要照抄原文。
  • 往返过滤:用生成的 query 检索,原 chunk 不在 top-N 里的,要么丢弃,要么人工复核,因为可能是 query 本身有问题。
  • 分层抽样人工复核,按 query 类型(事实型、比较型、无答案型)保持比例。
  • 一定要放一部分无答案 query,否则测不出系统在应该拒答时会不会乱答。

标注不全(incomplete judgments):大库里绝大多数文档没人标过,未标注的文档一律按不相关计算。后果是:

  • 新系统找到了没标过的相关文档,却被算作错误,所以离线指标对「和标注时的系统不同的新系统」不公平。TREC 的 pooling 做法是把多个系统的 top-k 合并起来送标,降低这种偏差。
  • 标注覆盖率很低时,小 k 的 Recall 基本量不出差异。可以加大 k,或者只在有标注的子集上评测,先确认这把尺子能区分出好坏。

5. 离线指标涨了,线上为什么没涨#

原因机制
指标和实际用途不一致离线量排序(NDCG),线上却用分数做阈值判别,或者 LLM 对 top-k 内部顺序不敏感
query 分布不同评测集是合成的或旧的,线上是口语化、多轮上下文、带错别字的 query
标注不全或过时新系统找到的新相关文档被当成错误;文档更新后标注没更新
执行方式不同离线用暴力检索,线上用 ANN;离线不带 filter,线上带 filter;编码配置不一致
下游还有其他环节召回提升被后面的 rerank、去重、配额、截断吃掉
样本量太小几十条 query 的端到端 A/B 噪声很大,涨跌落在置信区间内就只能说「没测出差异」
延迟和超时新方案更慢,线上超时降级,实际跑的是降级路径

对比两个版本时,用成对的统计方法:同一批 query 上逐条算差值,用 bootstrap 或配对 t 检验给置信区间,而不是只比较两个平均数。

面试回答(2分钟版)

检索评测先要有一份标注集,也就是每个 query 对应哪些文档相关,最好是分级的。指标上,Hit Rate@k 看 top-k 里有没有至少一条对的,Recall@k 看相关文档找回了多少,Precision@k 看 top-k 里有多少是对的,MRR 看第一条对的排第几,NDCG 同时考虑分级相关性和位置折扣:DCG 是每个位置的增益 2^rel − 1 除以 log2(i+1) 再求和,除以理想排序的 IDCG 得到 0 到 1 的分数。注意 IDCG 要用全部已标注文档算,否则漏召回不扣分。RAG 里我最看重召回层的 Recall 和 Hit Rate,因为 LLM 读的是整个 top-k;排序指标主要用来评 reranker。评测要分层:召回层看候选池的 Recall@N,精排层在固定候选池上看 NDCG,生成层给正确上下文看 LLM 答得对不对,最后看端到端,这样一条 bad case 能归因到具体哪一层。标注集可以人工标、用 LLM 从 chunk 合成 query、或者用 ESCI 这类公开分级数据。合成 query 容易和原文措辞重合、偏简单,要做往返过滤和人工抽检。离线涨了线上没涨,常见原因是 query 分布不同、标注不全、离线暴力检索和线上 ANN 不一致,或者指标和线上用途根本不是一回事。结合项目时可以讲:评测集怎么来、先怎么确认这把尺子能区分好坏(比如标注覆盖率低时加大 k),以及离线涨了为什么没上线或上线后怎么验证。

追问与易错

追问方向:

  • “Recall@k 和 Hit Rate@k 有什么区别,RAG 里看哪个?” → Hit Rate 只看 top-k 里是否至少有一条相关,Recall 看相关文档找回的比例。答案集中在一个 chunk 时两者差不多;需要多个 chunk 拼出答案时要看 Recall。
  • “MRR 和 NDCG 各适合什么场景?” → MRR 只关心第一条相关文档的位置,适合「只要一个答案」的场景(如 FAQ 匹配);NDCG 支持分级相关性并考虑所有位置,适合评整个排序列表、评 reranker。
  • “NDCG 的 IDCG 怎么算?常见 bug 是什么?” → 用该 query 全部已标注文档按档位从高到低排序后算 DCG@k。常见 bug 是只用系统召回到的文档算 IDCG,这样漏召回的相关文档不扣分,分数虚高。
  • “指数增益 2^rel − 1 和线性增益有什么区别?” → 指数增益拉大高档位的权重,一条高档位文档的位置变化对分数影响更大;线性增益各档之间差距均匀。两种算出的数不可比,报告时要写清楚用的是哪种。
  • “合成评测集有什么问题,怎么控制质量?” → LLM 容易照抄原文措辞,偏向 BM25、比真实 query 简单,而且一个问题可能有多个 chunk 能答但只标了一个。做法是要求改写和分难度生成、做往返检索过滤、按类型分层抽样人工复核,并加入无答案 query。
  • “标注不全对评测有什么影响?” → 未标注文档按不相关计算,新系统找到的未标注相关文档会被当成错误;覆盖率很低时小 k 的指标区分不出好坏。可以用 pooling 把多个系统的 top-k 合并送标、加大 k,或者只在有标注的子集上评。
  • “怎么判断两个版本的差异是不是真的?” → 在同一批 query 上逐条算差值,用 paired bootstrap 或配对 t 检验给置信区间;区间包含 0 就只能说没测出差异,这时要扩大评测集,而不是直接下结论。
  • “离线指标涨了线上没涨,你会怎么排查?” → 先确认线上怎么用这一层的输出(排序还是阈值判别);再对比离线和线上的执行方式(ANN、filter、编码配置);然后看线上 query 分布和评测集差多少,把线上 bad case 回灌到评测集。
  • “ESCI 的四档怎么用在训练和评测里?” → E 是完全匹配、S 是替代品、C 是配件、I 是无关。评测时映射成分级增益算 NDCG;训练时 E 当正例,S、C 可以当难负例或中间档。具体怎么映射取决于业务把「替代品」和「配件」看成相关还是不相关。

易错点:

  • ❌ “指标越多越好” → 先定线上实际怎么用这一层的输出,再选对应的指标;和用途不一致的指标涨了也没用。
  • ❌ “只看平均值” → 要保留逐 query 分数,按 query 类型拆分,做配对统计。
  • ❌ “离线评测用暴力检索,结论可以直接搬到线上 ANN” → 两者结果不同,编码配置不一致时不会报错,只会让效果变差。