开始前,会这些就够了
- 知道百分比怎样计算
遇到不熟悉的词,先看这 3 个解释
- 精确率与召回率
- 先约定要找的目标类别。精确率看“判为目标的里面,有多少真的符合”;召回率看“实际符合的里面,有多少被找出”。两者分母不同,需要结合误报和漏报代价判断。
- 分类阈值
- 把连续分数变成行动的一条分界线。例如约定分数大于或等于 0.5 才进入优先队列。调整分界线改变选中哪些对象,不会改变模型已经给出的分数。
- F1 的三种平均
- micro 先合并各类计数,macro 先算各类 F1 再等份平均,weighted 按各类真实数量加权。
假设客服助手会给工单打一个“需要优先处理”的分数。分数已经排好,值班同学接下来还得决定:从多少分开始进入优先队列?这条分数线叫阈值。调它会改变谁被选中,却不会让原来的排序自动变聪明。
下面七张工单和它们的分数都是虚构的。标签1表示人工核实确实需要优先处理,0表示不需要。我们故意把最高分设成一次误报,好观察一个容易被忽略的现象:门槛越高,精确率未必一路上升。
名单缩短了,留下的就一定更准吗?
七张虚构工单的分数保持不变。标签 1 表示人工核实确实需要优先处理;分数大于等于阈值,才进入优先名单。
| 工单 | 分数 | 真实标签 | 进入名单 | 结果 |
|---|---|---|---|---|
| A | 0.95 | 0 | 是 | 误报 |
| B | 0.90 | 1 | 是 | 符合标签 |
| C | 0.80 | 1 | 是 | 符合标签 |
| D | 0.70 | 0 | 是 | 误报 |
| E | 0.60 | 1 | 是 | 符合标签 |
| F | 0.40 | 1 | 否 | 漏报 |
| G | 0.20 | 0 | 否 | 符合标签 |
名单中确实需要处理的:3 / 5,精确率 60.0%。
四张实际需要处理的工单,找回 3 张:召回率 75.0%。
误报 2 张,漏报 1 张。
最高分 A 被故意设成误报,用来反驳“阈值越高,每一步都越准”。只提高阈值时,固定数据上的召回率不会上升,精确率却可能上下变化;这里没有训练模型,也没有替真实业务选择阈值。
“选中的对多少”和“该选的找到多少”
精确率问的是:进入优先队列的工单里,有多少确实该优先?如果选中五张,其中三张该优先,就是三除以五。召回率换了分母:所有确实该优先的四张里,找到了三张,就是三除以四。两者回答的是不同问题。
代码里 TP 是选对的数量,FP 是误报数量,FN 是漏掉的数量。标签来自人工核实,不能拿模型自己的判断同时当答案。分数这里只用于排序和比较大小;看到0.8,也不能直接读成“这张有八成概率该优先”。
分数不动,只移动那条线
约定分数大于或等于阈值就选中。阈值从0.5升到0.75时,一部分工单退出队列;从0.75升到0.92时,只剩最高分的那次误报。此时门槛很严格,精确率却掉到零,提醒我们高分也会出错。
在同一批固定分数上,阈值升高只会移除工单,不会补回被漏掉的真阳性,所以召回率不会上升。但精确率是两个变化数量的比值,移除的是误报还是选对的工单,决定了它可能升也可能降。
samples = [(0.95, 0), (0.90, 1), (0.80, 1), (0.70, 0),
(0.60, 1), (0.40, 1), (0.20, 0)]
positive = sum(label for _, label in samples)
for threshold in [0, 0.5, 0.75, 0.92, 1.0]:
selected = [label for score, label in samples if score >= threshold]
tp = sum(selected)
fp = len(selected) - tp
fn = positive - tp
precision = tp / len(selected) if selected else None
recall = tp / positive
shown = f'{precision:.1%}' if precision is not None else 'undefined'
print(f't={threshold:.2f} TP={tp} FP={fp} FN={fn} P={shown} R={recall:.1%}')怎样运行:保存为 threshold_queue.py,用 Python 3.10 或更新版本运行:python threshold_queue.py。仅用标准库,不联网。
运行后对照结果
t=0.00 TP=4 FP=3 FN=0 P=57.1% R=100.0%
t=0.50 TP=3 FP=2 FN=1 P=60.0% R=75.0%
t=0.75 TP=2 FP=1 FN=2 P=66.7% R=50.0%
t=0.92 TP=0 FP=1 FN=4 P=0.0% R=0.0%
t=1.00 TP=0 FP=0 FN=4 P=undefined R=0.0%零条警报,不等于完美识别
阈值升到1时,队列空了,也没有误报,但四张需要优先的工单全被漏掉。精确率的分母此时是零,本文显示为“未定义”。某些绘图工具会约定一个端点数值方便画线,那是展示规则,不能拿来宣布系统达到了完美精确率。
队列长度也应该一起看。假设每天只能人工复核几十张,即使某个阈值召回很高,排进几千张也可能处理不过来。反过来,为了少干活而把门槛拉得很高,会把更多漏报留给后续流程。这里需要明确实际代价,而不是迷信某个漂亮百分比。
先定目标,再用独立样本选门槛
选阈值前,先说清漏掉一张和多查一张分别意味着什么,再用验证数据比较方案;最后在未参与选择的测试数据上报告结果。训练模型、挑门槛和检验最终效果,各自需要合适的数据分工,不能反复盯着同一份结果挑最好看的数。
继续阅读 scikit-learn 的阈值指南时,重点看“相同分数、不同标签”的图,以及验证数据的说明。然后给本例再加一张高分误报,重新观察五个门槛。你练习的是决策规则,不是在证明一个真实客服模型能达到这些指标。
记住这三点
- 精确率看选中的对象里,真正该选的占多少;召回率看所有该选对象中,找回的占多少。
- 固定样本上提高阈值不会提高召回率,精确率却不保证单调上升。
- 门槛应结合漏报代价、误报代价和复核容量,并用独立数据检验。
别把例子用过头
- 分数不天然等于经过检验的概率;0.5也不是通用最佳门槛。
- 无预测命中时,精确率没有可计算的分母;绘图库的端点约定不是业务成绩。
自己试一小步
如果复核队列最多容纳三张工单,你会怎样展示门槛选择的代价?
- 给每个门槛补上队列长度。
- 另设两组误报和漏报代价,比较选择是否变化。
- 加入一条高分误报再计算,不改原有标签。
做到这些,就算完成
- 是否明确使用“大于等于”的边界规则?
- 是否同时显示精确率、召回率和队列长度?
- 是否把虚构样本结果与真实业务效果分开?
查原始资料
想确认一个细节,先看这份资料对应的部分,不必从头读完所有文档。
- scikit-learn:调整分类决策阈值 (新标签页)↗
分数与决策的区分、调阈值不改变原分数、业务目标及独立验证数据。
- scikit-learn:精确率与召回率曲线 (新标签页)↗
TP、FP、FN 的计算定义、阈值比较方式及绘图端点约定。
找一份资料继续练
先选一项,按下面的范围继续学;不必从头读完整门课程。
机器学习速成课程:用图和小练习理解模型 (新标签页)↗
只选分类模块的阈值、精确率与召回率,用同一组工单移动门槛,同时数误报、漏报和进入队列的数量。
查看学习准备与访问说明 →