精确率、召回率与数据增广:把 99.7% 的骗子改造成能用的筛查器
这一章讲三件事: 上章那个 99.7% 的骗子怎么被两个新指标当场拆穿;训练为什么会被 400:1 的失衡数据拉塌、怎么救;以及救活之后冒出来的过拟合,怎么用数据增广压下去。 位置:三步流水线第 ③ 步(分类)的收官。本章结束时,分类器第一次达到「对放射科医生有用」的水平。
1. 两只狗:错误的两个方向
书里用两只狗给这章定了调1:
- Chirpy(小猎犬):见什么都叫——消防车、猫头鹰、末班车。窃贼来了它一定叫,但你每晚被它吵醒三次。从不错过,代价是全是误报。
- Dozer(老猎犬):睡得很死,但一旦叫了,几乎可以确定出了事。从不乱报,代价是漏掉的多。
上一章的模型就是 Dozer 的极端版:它一次都不「叫」(全判阴性),所以永远不误报——也永远抓不到结节。两种错,代价完全不同:筛查场景漏掉一个肿瘤是事故,多报一个可疑点只是让医生多看一眼。单一指标分不清这两种错,这正是 99.7% 能骗人的原因。
2. 把「对」和「错」各拆成两种
判定一个二分类结果,先按「真实是什么 × 模型说什么」分成四格2:
模型说「是结节」 模型说「不是」
真是结节 真阳性 TP(抓到了) 假阴性 FN(漏了)
不是结节 假阳性 FP(误报) 真阴性 TN(放对)
图说:左列是「报警」,右列是「放行」;上行错是漏,下行错是冤。
两个新指标各盯一列3:
- 召回率(recall)= TP ÷ (TP+FN):真结节里,抓住了多少。「别漏」。医学文献里它也叫灵敏度(sensitivity)。
- 精确率(precision)= TP ÷ (TP+FP):报警的里面,有多少是真的。「别冤」。
Chirpy 召回率满分、精确率惨淡;Dozer 相反。一个好筛查器要两个都高——但它们是跷跷板:阈值往「多报」挪,召回涨精确跌。
3. F1:把两个数压成一个,但压得有讲究
调参时要一个数来定输赢,把精确率(P)和召回率(R)合成 F1。书里把候选方案逐个试了一遍,每种输法都讲得明白4:
- 算术平均 (P+R)/2:P=1、R=0 和 P=R=0.5 都得 0.5——Dozer 式废物和平衡选手同分,不行。
- 取小 min(P,R):方向对(逮住短板),但太粗:P 从 0.5 涨到 0.9,只要 R=0.5 不动,分数一动不动,白改进。
- 相乘 P×R(几何平均的平方):在指标都还低的早期,两个小于 1 的数一乘直接塌到接近 0,前期进步全被压没。
- F1 = 2PR/(P+R),即 P 和 R 的调和平均(倒数的平均再取倒数,天然被较小的那个拽住):等值线有一道深「肘弯」,把分数往「两个都高」的角落赶,短板一动分数就动。
主走查来了——拿新指标重读上一章的 E1 日志5:
旧指标(第 13 章):E1 trn 99.7% correct ← 看着像 A+
新指标(本 章):E1 trn 0.0000 prc, 0.0000 rcl, nan f1
图说:同一个模型,同一轮训练。换个量法,从 A+ 变成零分。
逐格对:模型全判阴性,TP=0 → 召回率 0/(0+136)=0;精确率 0/(0+0) 是除零,报 nan(计算里「不是一个数」的标记);F1 同为 nan。书里有句狠话:从 A+ 跌到零,算你走运——如果指标没把这个模型报成垃圾,那是指标的缺陷,不是模型的优点5。
4. 治根:为什么 400:1 会把训练拉塌
指标只是照出病,接着治病。为什么模型塌成「全判阴 性」?书里把第一个 epoch 的梯度来源拆成了三组6:
50 万个阴性样本:约一半已被判对(25 万,梯度≈0,不推)
一半还想把输出往「阴」拉(25 万,使劲推)
1215 个阳性样本:约一半想把输出往「阳」拉(约 600,使劲推)
推力比 ≈ 250000 : 600 ≈ 400 : 1 —— 方向没有任何悬念
换成批次视角更直观:batch 32 个样本,平均 15 个批次才轮到 1 个阳性;其余 14 个批次里,每一个样本都在把模型往「判阴」推6。模型不是学坏了,它是找到了损失函数面前的最优解——我们给的目标本身有问题。
解法:交替采样,把数据掰回 1:1
书里改了 Dataset 的取样逻辑:加一个 ratio_int 开关,奇数下标返回阴性样本、偶数下标返回阳性样本,取模回绕——阳性样本不够就循环着用;__len__ 硬编码(把数值直接写死在代码里)成 200000(不再等于真实候选数),让每个 epoch 短一点7。DataLoader 自带的 sampler 机制也能做加权采样,书里讨论后放弃了:那样「哪些样本算阳性」的知识会泄漏到 Dataset 外面,破坏封装7。
一个刻意的「不修」:验证集保持 400:1 不平衡——因为真实世界就是不平衡的,评估必须模拟部署环境8。
效果分两步兑现,先看第一轮(E1,平衡后)9:训练集 F1 0.928——上一章同一轮是 nan(精确率召回率双零);验证集召回率 79.4%,136 个真结节抓住了 108 个,上一章是 0 个;精确率 0.110、F1 0.194。
代价记在阴性那一侧:阴性样本的正确率从 100% 退到 98.4%,拿这 1.6% 换来阳性那边从 0% 到 79.4%。而且这一轮只喂了 20 万个样本,不到不平衡版那 50 多万的一半,时间也省了一半以上9。
精确率 0.110 看着惨,书里紧跟着算了一笔账把它救回来:54971 个阴性样本里哪怕只错 1%,也是 约 550 个假警报,而整个验证集统共只有 136 个真阳性——误报是真阳性的四倍。但换个方向看这笔账:医生多看 550 个小方块,省掉的是亲手翻完 55107 个;筛掉 98.4% 的阴性,等于把放射科医生的效率放大约 10 倍9。筛查器的精确率可以低,只要误报便宜。
第二步是作者的追问:还有 20.6% 的阳性漏在外面(136 个里 28 个没抓到),再多训几轮会不会补上?会——第二轮 E2 的召回率涨到 84.6%(115/136),比 E1 多抓 7 个10。这个 84.6% 就是下一节那条曲线的起点,而不是终点。
5. 新病:1215 个阳性被背了下来
「多训几轮」这个念头,继续往下跑就变成了一个老故事。把 20 轮的验证集召回率排成一列看10:
E2 84.6%(115/136) ← 上一节的终点,这里是起点
E5 82.4%(112/136)
E10 76.5%(104/136)
E20 72.1%( 98/136)
图说:同期训练集 F1 从 0.987 升到 0.997。训练集越来越好,验证集越来越差。
一升一降是过拟合的教科书形状(第 05 章的定义):训练损失逼近零、验证损失反而在涨10。多训的那 18 轮不但没补上漏掉的 28 个,还又赔进去 17 个。
原因书里点破了:阳性训练样本只有 1215 个,交替采样让每个 epoch 反复见同一批,重复再多次,也不会变得更难记10。书的比喻:好模型看脸龄,学的是皱纹和灰发;过拟合模型记的是「那个发型、那副眼镜——是 Frank,62.8 岁」,连 Frank 的儿子都被认成 Frank11。
解法:数据增广——让模型想背也背不下
数据增广(data augmentation)是对训练样本做合成修改,每个 epoch 看到的都是没见过的变体:记住所有变体比学会规律还难,模型被迫选后者12。
书里先给了一个反例划清「什么不算增广」:把每张图角上 4 个像素的红色通道改成随机值,数据集能「扩大 40 亿倍」——但主体原封不动,照背不误。增广的变体必须像真实数据的另一种可能,不能只像原图的噪点版12。
五种真增广,每条都有医学语境的讲究13:
| 增广 | 做什么 | 讲究 |
|---|---|---|
| 翻转 | 镜像 | 上下方向对应重力,临床项目得先问专家能不能翻 |
| 平移 | 挪几个体素 | 中心不必死守在正中央 |
| 缩放 | 放大缩小 | 结节本来就有大有小 |
| 旋转 | 转个角度 | 只在横断面(X-Y)内转——体素非立方,绕别轴转会拧坏形状 |
| 加噪 | 体素值加随机扰动 | 破坏性:加多了会淹没真实结构,实测单用反而更差 |
工程上一条死规矩:缓存必须放在增广之前。第 12 章的磁盘缓存存的是「原始切块」;如果存增广后的结果,每个 epoch 读到的都是同一批变体,增广白做——而且增广函数本身一改,旧缓存全是错的13。
结果(书里 TensorBoard 曲线的结论):全增广版召回率最高、也最扛过拟合,代价是总正确率和精确率都降;单用旋转几乎追平全增广且精确率更高。书里按场景选了全增广:筛查要的是召回率,F1 只用来决定保存哪个 epoch 的权重14。
6. 作者的判断与证据
| 说法 | 性质 |
|---|---|
| 精确率/召回率/F1 的定义与取舍 | 标准内容,四种合成法的对比是书里的推演,可复算4 |
| 「从 A+ 到零算你走运」 | 作者观点,但论证在书内站得住5 |
| 400:1 拉塌梯度的三组账 | 有据,基于第一个 epoch 的实际样本数6 |
| 交替采样优于 sampler 方案 | 作者的工程判断,理由是封装;可争论7 |
| 「效率放大 10 倍」 | 有据的换算(550 假警报 vs 55107 全量),依赖「误报便宜」的前提9 |
| 噪声增广有害、旋转最划算 | 实测结论(各增广单独跑的曲线),换数据集未必成立14 |
书里还有一个必须转达的专栏:模型从带有现实偏见的数据里学习,会把偏见一并学走——「和人一样,种族歧视是学来的」。这问题至今没有通用解法15。
7. 边界与局限
- F1 给精确率和召回率同等权重;筛查场景其实更看重召回率,书里用「F1 只决定保存点、场景决定选哪个模型」绕开了,更重的 场合该用带权重的 Fβ(书外概念,书里没讲)。
- 交替采样让每个 epoch 只见到数据的一个子集,极端样本可能长期轮不上。
- 增广参数(平移几个体素、噪声多大)是手调的,书里没给系统搜法。
- 「效率 10 倍」依赖误报成本低的假设;若每个假警报都触发昂贵复查,结论要重算。
8. 可带走的
- 错误分两种,代价各不同;单一指标分不清,先想清你的场景怕哪种。
- 召回率管漏、精确率管冤;阈值是两者的跷跷板。
- F1 是调和平均,天然逮短板;别用算术平均,它给 Dozer 式模型发奖。
- 指标若没把垃圾模型报成垃圾,是指标的缺陷——换指标比调模型优先。
- 不平衡会定死梯度方向:算一下「15 个批次才见 1 个阳性」这种账,比看 loss 更早预知塌方。
- 训练集可以掰平衡,验证集必须保持真实分布——评估要模拟部署。
- 小样本(样本很少的那个类别)类被反复见 = 被背下来;增广让「背」比「学」更难。
- 增广要像真实数据的另一种可能;缓存放在增广之前;破坏性增广(噪声)慎用。
9. 原文地图
| 主题 | 原书章 | 原文位置 |
|---|---|---|
| Chirpy 与 Dozer | ch14 | text/22-ch14-14-improving-training-with-metrics-and-augmentat.txt:110(搜「Chirpy」) · :148(搜「Dozer barks」) |
| TP/FP/FN/TN 四格 | ch14 | text/22-ch14-14-improving-training-with-metrics-and-augmentat.txt:140(搜「false positive is an event」) |
| 召回率与精确率 | ch14 | text/22-ch14-14-improving-training-with-metrics-and-augmentat.txt:251(搜「Make sure you never miss」) · :266(搜「sensitivity」) |
| F1 与四种合成法 | ch14 | text/22-ch14-14-improving-training-with-metrics-and-augmentat.txt:8(搜「F1 score」) · :430(搜「harmonic mean」) · :434(搜「deep elbow」) |
| 新指标现形 nan | ch14 | text/22-ch14-14-improving-training-with-metrics-and-augmentat.txt:542(搜「nan f1」) |
| 400:1 与 15 批次账 | ch14 | text/22-ch14-14-improving-training-with-metrics-and-augmentat.txt:616(搜「400:1 ratio」) · :677(搜「15 batches」) |
| ratio_int 交替采样 | ch14 | text/22-ch14-14-improving-training-with-metrics-and-augmentat.txt:768(搜「ratio_int」) · :838(搜「200000」) |
| 验证集保持不平衡 | ch14 | text/22-ch14-14-improving-training-with-metrics-and-augmentat.txt:716(搜「real world is imbalanced」) |
| 平衡后 E1 的实测日志 | ch14 | text/22-ch14-14-improving-training-with-metrics-and-augmentat.txt:896(搜「0.9277 f1」) · :900(搜「0.7941 recall」) · :903(搜「108 of 136」) |
| 550 假警报与 10 倍效率 | ch14 | text/22-ch14-14-improving-training-with-metrics-and-augmentat.txt:914(搜「54,971 × 0.01」) · :922(搜「radiologist」) |
| 「再多训几轮」与 20 epoch 过拟合 | ch14 | text/22-ch14-14-improving-training-with-metrics-and-augmentat.txt:926(搜「20.6% of positive samples」) · :939(搜「115 of 136」) · :963(搜「0.7206」) |
| Frank 脸龄比喻 | ch14 | text/22-ch14-14-improving-training-with-metrics-and-augmentat.txt:1042(搜「Frank」) |
| 40 亿倍伪增广 | ch14 | text/22-ch14-14-improving-training-with-metrics-and-augmentat.txt:1095(搜「4 billion」) |
| 五种增广与缓存顺序 | ch14 | text/22-ch14-14-improving-training-with-metrics-and-augmentat.txt:1106(搜「five specific types」) · :1174(搜「caching」) · :1247(搜「X-Y plane」) |
| 增广结果取舍 | ch14 | text/22-ch14-14-improving-training-with-metrics-and-augmentat.txt:1435(搜「rotation-augmented」) · :1439(搜「use case」) |
| 偏见专栏 | ch14 | text/22-ch14-14-improving-training-with-metrics-and-augmentat.txt:700(搜「racism is learned」) |