99% 的陷阱
假设你造了一个模型来筛查一种罕见病,这种病每 100 人里只有 1 人患上。你训练它、测试它,它报告 99% 的准确率。你或许想开香槟庆祝了。但有一个根本没学到任何东西的模型也能打平这个成绩:一行代码,对所有人都说「健康」。既然 100 人里确实有 99 人是健康的,这条偷懒的规则同样有 99% 的准确率——而它漏掉了每一个真正的病人。准确率只是数了预测与真相吻合的次数,在严重失衡的数据上,这个数字可以既响亮又毫无意义。
这正是被称为类别不平衡问题的核心:当一种结果远比另一种常见时,一个笼统的总分会把你唯一在乎的那些错误藏起来。解决之道一开始并不是换个更好的模型——而是换个更好的问题。我们不再问「它多久对一次?」,而是问「它对的是什么、错又错在哪个方向?」要回答这个问题,需要一个能把那单一数字拆开来看的工具。
混淆矩阵:四个格子
在二分类问题里,每一次预测都会落进四个格子之一,而混淆矩阵就是把它们逐个计数的那张表格。对有两种对法,错也有两种错法。真阳性是模型正确标记为患病的病人。真阴性是被正确判为健康的健康人。危险的是那两类错误:假阳性是被错误标记的健康人(虚惊一场),假阴性是被错误放行的病人(漏诊)。准确率把这四类混作一团;矩阵则把它们分得清清楚楚。
Predicted
Sick Healthy
Actual Sick TP=8 FN=2 <- 2 patients MISSED
Healthy FP=5 TN=985
(rare disease: only 10 sick out of 1000)精确率与召回率:两种不同的担忧
从这四个格子里,衍生出最重要的两个指标。精确率问的是:模型标记为阳性的所有东西里,有多少真的是阳性?它等于真阳性除以全部阳性预测——回答的是「它拉响警报时,我能信吗?」召回率问的恰好相反:所有真正是阳性的东西里,模型抓到了多少?它等于真阳性除以全部实际阳性——回答的是「它放跑了多少?」
你该在乎哪一个,完全取决于一个错误的代价有多大。对我们的疾病筛查器来说,漏掉一个病人(假阴性)可能致命,而虚惊一场不过是多做一次检查——所以我们要的是高召回率,哪怕牺牲一些精确率。把利害关系反过来看垃圾邮件过滤器:错误地把一封重要邮件扔进垃圾箱(假阳性)比放过一封垃圾邮件更糟,所以那里我们倚重精确率。世上没有放之四海皆好的指标;该用哪个,由任务说了算。
F1:一个诚实搭出来的数字
有时候你确实想要一个单一分数,用来给模型排名或随时间监控。F1 分数是把精确率和召回率结合起来的诚实做法:它取的是二者的调和平均,而不是普通的算术平均。调和平均会惩罚失衡——只要精确率或召回率有一个接近零,F1 也会被拽到接近零。所以你没法靠在一项上拿满分、另一项却忽略不计来钻空子,这正是它在不平衡数据上胜过准确率的原因。
用这个视角去审视我们那个偷懒的「人人都健康」模型,它立刻就垮了。它一个真病人都没抓到,所以召回率是 0,于是 F1 也是 0——尽管准确率还是闪亮的 99%。这个指标终于把那个总分藏起来的真相说了出来。但也别把 F1 当成万能的判决:它给精确率和召回率赋予同等权重,这未必和你真实的代价相符,而且它完全无视真阴性。它是把更锋利的工具,而不是盖棺定论的那句话。
选指标时别骗了自己
诚实评估的功夫,是一段在你信任任何数字之前要先跑一遍的短循环。它从你两类错误各自的代价出发,止于一个有意挑选出来的指标——而不是随便哪个碰巧看起来最漂亮的那个。
- 写下一个假阳性和一个假阴性各自的代价——用现实世界的话说,而不是用分数。
- 检查类别是否平衡。如果某一类很罕见,立刻把准确率从你的清单上划掉。
- 在留出的测试集上构建混淆矩阵,在算任何东西之前先把四个格子都看一遍。
- 挑选与代价相符的指标:漏检最伤时用召回率,虚警最伤时用精确率,需要一个均衡数字时用 F1。
- 和一个蠢笨的基线——「永远预测多数类」的模型——作比较,这样你才知道你的分数反映的是真本事,而不只是数据的失衡。
这些指标会贯穿本阶后面的内容。当你在排行榜上比较各个条目时,榜首分数往往是准确率或单一的 F1——而这里讲的道理,恰好说明了为什么榜单排名会美化一个在关键样本上悄悄失手的模型。诚实的度量不是真正工作之前的一道手续;在那些不平衡、高风险的问题上,它本身就是工作。