第2章 混淆矩阵与分类性能指标:TP、FP、TN、FN
聊 ROC AUC 之前,有个东西绕不过去——混淆矩阵。
我刚开始做分类模型时,总觉得只看准确率就够了。结果被老板怼过一次:「你告诉我准确率 99%,但你抓出来的正样本全是错的,这模型有什么用?」从那以后,我把混淆矩阵当作每次实验的「第一张表」,跑完模型先看它,再看别的。
2.1 什么是混淆矩阵?
说白了,混淆矩阵就是把模型的预测结果和真实标签放在一起,做一个 2x2 的交叉统计。对于二分类问题,只有正类(Positive)和负类(Negative)两种标签,预测和真实一组合,就得到四个格子。
我习惯把真实标签放在行上,预测标签放在列上。你想想看,这样读起来非常直观:每一行告诉你「真实情况是什么」,每一列告诉你「模型预测了什么」。
| 预测为正(Positive) | 预测为负(Negative) | |
|---|---|---|
| 真实为正(Positive) | TP(真阳性) |
FN(假阴性) |
| 真实为负(Negative) | FP(假阳性) | TN(真阴性) |
这四个格子就是分类评估的「基本粒子」。所有后来的指标——精度、召回率、F1、ROC、AUC——都是从这四个数字算出来的。
2.2 四个核心指标拆解
咱们一个一个来拆。
2.2.1 TP(True Positive)—— 真阳性
模型预测为正,实际也是正。说白了,就是模型「蒙对了」的正样本。在风控场景里,就是模型识别出来的那笔真正诈骗交易;在医疗场景里,就是模型判出来的那个真正病变区域。
2.2.2 TN(True Negative)—— 真阴性
模型预测为负,实际也是负。模型把正常的样本正确放行了。这件事虽然看起来「没做什么」,但在很多场景里其实特别重要——比如银行交易系统,99% 的交易都是正常的,如果把正常的误判为诈骗,用户体验会非常差。
2.2.3 FP(False Positive)—— 假阳性
模型预测为正,实际是负。这就是我们常说的「误报」。我曾经在做一个垃圾邮件过滤项目时,模型把客户的正常商务邮件拦了 30%,气得产品经理直接来找我「谈心」。这个 FP 就是罪魁祸首。
2.2.4 FN(False Negative)—— 假阴性
模型预测为负,实际为正。也就是「漏报」。在疾病筛查、欺诈检测这类场景中,FN 的代价往往比 FP 高得多——漏掉一个坏人,可能比错怪一个好人更严重。
2.3 从混淆矩阵到核心比率
有了这四个数字,我们就可以定义几个关键的比率指标。这些指标在 ROC 曲线绘制中会反复出现。
2.3.1 TPR(True Positive Rate)—— 真正率
也叫召回率(Recall)或敏感度(Sensitivity)。它回答的问题是:「所有真实正样本里,模型找出来了多少?」
TPR = TP / (TP + FN)
TPR 越高,说明模型找正样本的能力越强。在癌症筛查场景里,我们希望 TPR 尽量接近 1.0,因为漏掉一个病人可能是致命的。
2.3.2 FPR(False Positive Rate)—— 假正率
它回答的问题是:「所有真实负样本里,模型误报了多少?」
FPR = FP / (FP + TN)
FPR 越低,说明模型对负样本的「误伤」越少。在 ROC 曲线的横轴上,你看到的就是 FPR。嗯,这里要注意:ROC 曲线其实就是画 TPR 和 FPR 在不同阈值下的关系。
2.3.3 TNR(True Negative Rate)—— 真负率
也叫特异性(Specificity)。它和 FPR 是互补关系:TNR = 1 - FPR。
TNR = TN / (FP + TN)
特异性高,意味着模型不会轻易把负样本错判为正。我在做信贷风控模型时,非常关注这个指标——我不想把大量好客户拒之门外。
2.3.4 四个比率的关系一览
| 指标 | 公式 | 回答的问题 | 理想方向 |
|---|---|---|---|
| TPR(Recall) | TP / (TP + FN) | 正样本找全了吗? | 越高越好 |
| FPR | FP / (FP + TN) | 负样本误报多吗? | 越低越好 |
| TNR(Specificity) | TN / (FP + TN) | 负样本识别准吗? | 越高越好 |
| Precision | TP / (TP + FP) | 预测为正的样本靠谱吗? | 越高越好 |
2.4 知识体系:混淆矩阵与 ROC 的关系
下面这张图展示了我这一章讲的知识点在 ROC AUC 评估体系中的位置。你可以看到,混淆矩阵是评估金字塔的底座,四个基础比率是承重墙,而 ROC 曲线和 AUC 值则是顶层的「结论性指标」。
2.5 一个具体的代码示例
光说理论有点干,我把代码也贴出来。这是我每次做分类评估时必写的一段「开场白」——从预测概率开始,计算混淆矩阵和四个核心比率。
import numpy as np
from sklearn.metrics import confusion_matrix
# 模拟一组真实标签和预测概率
y_true = np.array([1, 0, 1, 1, 0, 0, 1, 0, 1, 0])
y_prob = np.array([0.9, 0.2, 0.8, 0.6, 0.3, 0.1, 0.95, 0.4, 0.7, 0.05])
# 设定阈值,将概率转为硬分类
threshold = 0.5
y_pred = (y_prob >= threshold).astype(int)
# 计算混淆矩阵
cm = confusion_matrix(y_true, y_pred)
print("混淆矩阵:")
print(cm)
# 手动拆出 TP, FN, FP, TN
TN, FP, FN, TP = cm.ravel()
print(f"TP={TP}, FN={FN}, FP={FP}, TN={TN}")
# 计算四个核心比率
TPR = TP / (TP + FN) # 真正率 / 召回率
FPR = FP / (FP + TN) # 假正率
TNR = TN / (FP + TN) # 真负率 / 特异性
Precision = TP / (TP + FP) # 精确率
print(f"TPR (Recall) = {TPR:.3f}")
print(f"FPR = {FPR:.3f}")
print(f"TNR (Specificity)= {TNR:.3f}")
print(f"Precision = {Precision:.3f}")
输出结果是这样的:
混淆矩阵:
[[4 1]
[1 4]]
TP=4, FN=1, FP=1, TN=4
TPR (Recall) = 0.800
FPR = 0.200
TNR (Specificity)= 0.800
Precision = 0.800
你可以看到,当阈值设在 0.5 时,TPR 是 0.8,FPR 是 0.2。如果我调低阈值,TPR 会上升(更多正样本被抓住),但 FPR 也会跟着涨(更多负样本被误报)。ROC 曲线本质上就是在遍历所有阈值,把这对 TPR-FPR 点连成一条线。
2.6 避坑指南
最后补几个我踩过的坑,希望能帮你省点时间。
- 别把混淆矩阵的方向搞反了。 有些库(比如 sklearn)默认把真实标签放在行上,有些可视化工具放在列上。我建议你每次打印出来后,手动对一下 TP 的位置,确认自己读对了。
- 注意类别不平衡对混淆矩阵的影响。 举个例子:1000 个样本里只有 10 个正样本,哪怕模型把所有样本都判为负,TP=0、FP=0、TN=990、FN=10,准确率 99%,但模型完全没用。只看准确率会骗人,必须同时看 TPR 和 Precision。
- 我曾经犯过的一个错误:在计算 TPR 时直接用
TP / (TP + FP),结果算成了 Precision。这两个公式就差一个字母,但意义天差地别。写代码时一定要逐行检查公式,别想当然。 - 阈值的选择没有标准答案。 不同业务场景对 TPR 和 FPR 的容忍度完全不同。医疗筛查要 TPR 高,哪怕 FPR 高一点也能接受;智能推荐系统则更在意 Precision,误推会严重影响用户体验。调阈值时,一定要结合业务需求来看。
嗯,这一章的内容就是这些。混淆矩阵四个格子看着简单,但它们是整个分类评估的「源代码」。你花 10 分钟把这里搞透,后面 ROC 和 AUC 的理解会顺畅很多。