混淆矩阵:准确率为何会骗人
一张 2x2 的表,把分类器答对和答错的部分按类型拆开。所有分类指标都由这四个格子推导而来,它也说明了为什么单个数字永远不够用。
一个准确率数字只告诉你分类器答对的频率,却藏起了更有用的信息:它怎么错的。混淆矩阵就是把「怎么错的」放回去的那张表;一旦会读它,大多数分类指标就不再需要死记,而是一眼就明白。
这张表
二分类问题里,它是一个 2x2 的网格。行是真实标签,列是模型的预测。
预测为正 预测为负
实际为正 TP (85) FN (15)
实际为负 FP (30) TN (870)这就是精确率、召回率与准确率里那四个计数:真正例、假负例、假正例、真负例。对角线(TP、TN)是模型答对的地方,非对角线(FP、FN)是答错的地方——而这两个非对角线格子,通常是两个完全不同的问题。
为什么一个数字抓不住这些
看上面那张表。1000 个样本里答对 85 + 870 = 955 个,准确率 95.5%。听起来很强。
现在去读那些格子。实际为正的 100 个里,模型抓到了 85 个、漏了 15 个(召回率 0.85)。它标记出的 115 个里,有 30 个是错的(精确率 0.74)。这个模型好不好,完全取决于哪种错更要命:是那 15 个漏报,还是那 30 个误报。准确率抹掉了这个区别,混淆矩阵把它留住了。
类别不平衡会让这种误导更严重。把问题反过来,让正例变得稀少——1000 个样本里只有 10 个是正例。一个每次都预测「阴性」的模型会得到:
预测为正 预测为负
实际为正 0 10
实际为负 0 990准确率 99%,召回率 0。矩阵一眼就能看出正例那一列是空的;准确率用一个很大的数字把它盖住了。这就是必须优先看混淆矩阵的最重要理由。
从中读出的各种比率
每个派生指标都是格子之间的比值,沿着某一行或某一列读:
- 召回率 / 灵敏度 / 真正例率 = TP / (TP + FN)——横着读实际为正的那一行。真正的正例里,抓住了多少。
- 特异度 / 真负例率 = TN / (TN + FP)——横着读实际为负的那一行。真正的负例里,有多少被正确地排除掉。
- 精确率 = TP / (TP + FP)——竖着读预测为正的那一列。标记出来的东西里,有多少是对的。
- 假正例率 = FP / (FP + TN) = 1 − 特异度。真负例里出现误报的比例。它是 ROC 曲线的横轴。
横着读还是竖着读,诀窍全在这里。召回率和特异度以真实标签(行)为条件,精确率以预测(列)为条件。这就是为什么类别比例一变,模型明明没变,精确率却会跟着动——这一点在看懂这张网格之前,总会让人意外。
两个类别以上
N 个类别就是 N x N 的矩阵。对角线是正确的预测;每个非对角线格子都讲出一种具体的混淆——多少次把「5」读成了「6」,多少次把「猫」叫成了「狗」。矩阵的名字就是从这儿来的,而这些格子是诊断的金矿:最大的那几个非对角线格子,直接指出模型分不清哪几对类别,而那正是标注精力或特征该投的地方。
想把多类矩阵压成一个数字,就分别算出每个类别的精确率和召回率,再取平均。**宏平均(macro-average)**对每个类别一视同仁(稀有类别重要时适用)。**微平均(micro-average)**先把所有计数汇总再算,于是会被高频类别主导。选哪一种,又是一个裸准确率会替你默默做出、而且很可能做错的决定。
把它当作第一步
在信任任何标量指标之前,先把矩阵打印出来。一行代码的事,却能回答标量回答不了的问题:误差是对称的,还是一边倒?有没有哪个类别被完全忽略了?哪几对类别总被搞混?几乎每一个「我们的准确率很高,但模型感觉不正常」的故事,最后都停在一张从来没被看过的混淆矩阵上。
要点回顾
- 矩阵把预测拆成 TP、FP、FN、TN,把单个准确率数字揉在一起的两类错误分开。
- 类别不平衡时,高准确率可以和零召回并存——那一列空的正例在矩阵里一目了然,在标量里却看不见。
- 召回率和特异度横着读真实标签的行;精确率竖着读预测的列——这就是精确率会随类别比例变化的原因。
- N 个类别时,最大的那几个非对角线格子,点名的正是模型分不清的类别。
- 在信任任何派生指标之前,先看混淆矩阵。
下一篇: ROC 与 AUC