Recall@k 是什么
Recall@k 衡量检索质量:对每个查询,看前 k 个候选里是否出现了标注的相关片段,命中就算这条查询通过,最后取通过查询的占比。它回答的是“答案有没有被捞进候选池”,与生成端质量解耦,因此是重建索引、换模型、调参前后最可靠的回归基线。需要注意两种口径:命中率口径只要命中任一相关片段即算成功;召回比例口径统计被捞出的相关片段占全部相关片段的比例,数值更低但更严格。
基本信息
| 指标类型 | 检索层指标,与生成质量无关 |
|---|---|
| 常见 k 值 | 10(粗排候选)、20、50、100 |
| 配套指标 | MRR、NDCG、Context Recall |
| 必要前提 | 每个查询有标注的相关片段 id |
典型使用场景
- 索引参数回归:调 efSearch、nprobe、num_candidates 后对比 Recall@k 曲线找拐点
- 换 embedding 模型或重排模型时做 A/B:先看召回率是否守住底线
- 评估切分策略:切片过碎或过大都会让 Recall@k 下滑
- 判断候选池是否足够:重排前的召回上限由它决定
- 上线门禁:把 Recall@k 下降幅度写进回归阈值,超过即阻断发布
Recall@k 计算
Recall@k 计算
def recall_at_k(queries, retrieved, gold, k):
hit = 0
for qid in queries:
got = set(retrieved[qid][:k]) # 前 k 个候选 id
if got & set(gold[qid]): # 命中任一相关片段即算通过
hit += 1
return hit / len(queries)
print(recall_at_k(qids, retrieved_ids, gold_ids, k=10))k 值与读法
| k 取值 | 对应环节 | 观察重点 |
|---|---|---|
| k=10 | 重排输入候选 | 低于 0.8 说明粗排漏捞,先修检索 |
| k=20~50 | 候选池上限 | 再扩大收益趋缓说明切分或模型是瓶颈 |
| k=100 | 近似索引召回上限 | 与精确检索差距大说明索引参数太保守 |
指标偏低怎么查
- 先看标注本身:相关片段 id 是否与当前索引口径一致,切分变更后旧标注往往失效
- 再看切分:切片过碎导致答案被拆到多块,任一块都凑不出完整语义
- 然后看检索参数:efSearch、nprobe、num_candidates 是否偏小
- 最后看查询侧:是否有改写、前缀、指令缺失导致表示错位
报告指标时必须写明口径(命中率还是召回比例)与 k 值:同名指标口径不同会导致跨版本对比失真。切分参数变更后要重新生成标注基线,否则回归对比失去意义。