开始前,会这些就够了
- 知道百分比怎样计算
- 知道检索会返回一份有顺序的材料列表
遇到不熟悉的词,先看这 3 个解释
- 检索前 k 条的评测
- 在固定排序里只看前 k 条:精确率问这 k 条里有多少相关,召回率问全部相关材料中找到了多少。两个数字的分母不同。
- 候选召回
- 检索流程里,先从全部资料中选出一批可能有用的候选,供后续排序和核对。这一步关心有没有把需要的资料找进来,并不保证每份候选都相关。
- 重排
- 拿到一批候选资料后,再按与当前问题的相关程度重新安排顺序,让较合适的内容有机会排在前面。它处理的是已有候选,不是重新搜索整个资料库。
假设你给团队做了一个软件手册助手。它每次先取三条材料,同事提议改成十条:“多找一点,总能补到漏掉的吧?”这可以是值得试的改动,但它改变的是一批候选材料,是否更适合完成任务还得检查。
本文固定一份人工排好的列表,只改变取前几条。没有检索模型,也不调用大模型生成答案。我们先把“该找的找到多少”和“找到的有多少相关”算清楚,再讨论这些数字没有回答的问题。
多取几条,找到的和夹带的各有多少?
全库有十二份虚构材料,其中五份人工标为相关。候选固定为 D1 到 D10,只改变取前几条;这不是检索模型的真实输出。
| 排名 | 材料 | 人工标签 | 本次取回 |
|---|---|---|---|
| 1 | D1 | 1:相关 | 取回 |
| 2 | D2 | 0:不相关 | 取回 |
| 3 | D3 | 1:相关 | 取回 |
| 4 | D4 | 0:不相关 | 未取回 |
| 5 | D5 | 0:不相关 | 未取回 |
| 6 | D6 | 1:相关 | 未取回 |
| 7 | D7 | 0:不相关 | 未取回 |
| 8 | D8 | 1:相关 | 未取回 |
| 9 | D9 | 0:不相关 | 未取回 |
| 10 | D10 | 0:不相关 | 未取回 |
取回 3 条,相关的有 2 条。
Precision@k(取回的有多少相关):66.7%,2 ÷ 3。
Recall@k(全库相关的找到多少):40.0%,2 ÷ 5。分母始终包括候选之外的 D11。
k=0 时精确率未定义;全库已有五份相关材料,因此召回率为 0%。本文代码用 1 到 10,这里额外开放 0 观察空结果。扩大同一排序的前缀,召回率不会下降,精确率却可能上升或下降。没有运行大模型,也没有用这两个比例替代材料冲突、上下文预算或最终回答质量的检查。
先找到候选,再决定谁排前面
检索里的候选召回,是先从资料库里找出可能有用的一批材料;排序再决定谁排在前面。重排是在已有候选内重新排队。增加候选数量可能让原本漏掉的材料入选;如果仍取原列表前三条,只把后面的队伍拉长,前三条并不会自动变好。
评测要先固定问题、资料版本和人工相关标准。例如问题明确问某款软件第三版的配置,就应按这个需求判断材料,而不只是看它是否出现“配置”两个字。未标注的材料也不能直接当作不相关,否则计算会把判断缺口藏起来。
前k条里,找到多少相关材料
Precision@k 是前k条中相关材料占多少;Recall@k 是资料库中所有相关材料里,前k条找到了多少。本例把十二份虚构材料全部人工标成0或1,1表示相关。列表只含前十份,第十一份虽相关却没被召回,所以分母不能只数列表里的相关项。
代码中的四个k都不超过列表长度,也没有重复编号。同一份固定排序扩大前缀,已经找到的相关材料不会消失,因此召回率不会下降;精确率则可能上升或下降,取决于新加入的是什么。本例先降低再回升的精确率,就是一种可能情况。
relevance = {
'D1': 1, 'D2': 0, 'D3': 1, 'D4': 0,
'D5': 0, 'D6': 1, 'D7': 0, 'D8': 1,
'D9': 0, 'D10': 0, 'D11': 1, 'D12': 0,
}
ranked = [f'D{i}' for i in range(1, 11)]
total_relevant = sum(relevance.values())
assert len(ranked) == len(set(ranked))
assert all(doc_id in relevance for doc_id in ranked)
def measure(k):
if type(k) is not int or not 1 <= k <= len(ranked):
raise ValueError('k must be an integer within the ranked list')
hits = sum(relevance[doc_id] for doc_id in ranked[:k])
precision = hits / k
recall = hits / total_relevant if total_relevant else None
return hits, precision, recall
print('all relevant:', total_relevant)
for k in [3, 5, 6, 10]:
hits, precision, recall = measure(k)
shown = f'{recall:.1%}' if recall is not None else 'undefined'
print(f'k={k}: hits={hits}, P@k={precision:.1%}, R@k={shown}')怎样运行:保存为 retrieval_top_k.py,用 Python 3.10 或更新版本运行:python retrieval_top_k.py。仅用标准库,不联网。
运行后对照结果
all relevant: 5
k=3: hits=2, P@k=66.7%, R@k=40.0%
k=5: hits=2, P@k=40.0%, R@k=40.0%
k=6: hits=3, P@k=50.0%, R@k=60.0%
k=10: hits=4, P@k=40.0%, R@k=80.0%材料条数不能代替材料判断
两个比例都没有描述全部排序细节:只在同一批前k条内部交换顺序,它们可以保持不变。人先读哪条、后续程序把哪条放在前面,却可能改变。标签也只有相关与不相关两档,没有表达某条材料是否比另一条更直接、更完整。
相关材料之间还可能存在版本差异或相互冲突。例如两份故障指南都谈同一个开关,却适用于不同版本。增加数量可能补到解释差异的材料,也可能把更多待核对内容带进来。这要另外检查适用范围,不能靠相关材料多数表决。
取回十条,不等于十条都能用上
模型能接收的上下文有限,材料还要与问题等内容共享预算,并按所用模型的规则预留回答空间。十篇短通知与十份长手册,占用的长度不同。若先取十条、最后只装得下五条,就要记录实际送入的内容,而不只记录检索配置里的数字。
2024年的《Lost in the Middle》在当时受测模型和任务上发现,材料位置会影响结果。它提示我们检查排列与长度,并没有证明所有模型都会因材料变多而变差。实际改k时,应在同一组代表性问题上比较检索、实际输入和最终任务表现;本文只验证前面的计数。
记住这三点
- 扩大候选与改善排序是不同改动,比较时要固定问题、资料和相关标准。
- Recall@k 需要相关材料总数;Precision@k 只看取回前k条中的相关比例。
- 检索比例不直接等于回答质量,还要检查冲突、实际输入预算和任务表现。
别把例子用过头
- 本例完整知道十二份材料的标签;大资料库若标注不全,应说明 Recall@k 的评测范围。
- 召回率不下降的结论只针对同一固定排序的嵌套前缀;重排、过滤或资料变化后要重新计算。
- 更多资料既可能补足信息,也可能增加处理负担;本例没有测量生成质量或宣称通用最佳k。
自己试一小步
保持标签不变,只改变候选和顺序,你会怎样区分“找到了”和“排好了”?
- 保存原列表与四组指标,另建一个调整顺序的副本。
- 先只交换前三条的位置,再把原本靠后的材料移到前面,分别比较。
- 再模拟只能送入前五条的预算,单独记录候选列表和实际采用的列表。
做到这些,就算完成
- 是否保留全库相关总数,避免把未取回材料从分母删掉?
- 是否区分排序变化、候选变化和实际输入截取?
- 是否清楚说明这些计数尚未测量回答是否正确?
查原始资料
想确认一个细节,先看这份资料对应的部分,不必从头读完所有文档。
- Introduction to Information Retrieval:Evaluation of unranked retrieval sets (新标签页)↗
Precision 与 Recall 的不同分母,以及扩大取回集合时召回率的性质。
- Introduction to Information Retrieval:Evaluation of ranked retrieval results (新标签页)↗
Precision at k 的固定深度含义、单问题指标局限与代表性问题集合的必要性。
- Lost in the Middle: How Language Models Use Long Contexts (新标签页)↗
受测模型与多文档问答等任务中,输入长度及相关信息位置需要单独评估;不能只看检索召回。
找一份资料继续练
先选一项,按下面的范围继续学;不必从头读完整门课程。
Sentence Transformers:相关资料有没有进前几条 (新标签页)↗
只看问题、材料和相关 ID 三种输入,用两道问题和五条材料计算前三条找回了多少已标注的相关材料;先做纸上表格,不下载模型。
查看学习准备与访问说明 →Sentence Transformers:先找候选,再重新排队 (新标签页)↗
只看检索与重排的流程图,用候选卡片区分资料没找来和找来了却排在后面;不运行完整 Wikipedia 示例。
查看学习准备与访问说明 →