混淆矩阵:TP、FP、TN、FN 的定义
说实话,做模型评估这么多年,我觉得最直观的工具就是混淆矩阵。
很多初学者一上来就盯着准确率看。我刚开始也是这样。结果呢?
有一次,我帮业务部门做一个欺诈检测模型。数据里面 99% 都是正常交易,只有 1% 是欺诈。我随便写个模型,把所有交易都判成“正常”,准确率直接干到 99%。
业务方看着报告说:“厉害啊,这模型几乎不出错!”
我心里清楚,这模型屁用没有。它一个欺诈都没抓出来。
所以,光看准确率是不行的。你得把预测结果拆开看。这就是混淆矩阵的用处。
四个核心概念
混淆矩阵其实就是一个 2×2 的表格。它把你的预测和实际情况对比,得出四种结果。
我习惯用一个场景来记:假设你在后台排查线上Bug。
- TP (真正例):你报警说有 Bug,实际上真有 Bug。完美命中。
- FP (假正例):你报警说有 Bug,结果是个误报。虚惊一场。
- TN (真负例):你说没 Bug,确实没 Bug。一切正常。
- FN (假负例):你说没 Bug,结果线上已经崩了。严重漏报。
一句话记忆法:TP 和 TN 是你做对的事,FP 和 FN 是你犯的错。FP 是“狼来了”,FN 是“狼真的来了,你却睡着了”。
混淆矩阵的结构
咱们用一个二分类问题来看。假设要判断一封邮件是不是垃圾邮件。
预测结果有两类:Positive(垃圾邮件)和 Negative(正常邮件)。实际结果也一样。
混淆矩阵就这么画:
| 实际为 Positive | 实际为 Negative | |
|---|---|---|
| 预测为 Positive | TP | FP |
| 预测为 Negative | FN | TN |
你看,行是预测值,列是真实值。对角线上都是预测对的,反对角线上都是预测错的。
# 用 Python 构建混淆矩阵
from sklearn.metrics import confusion_matrix
y_true = [1, 0, 1, 1, 0, 1, 0, 0]
y_pred = [1, 0, 1, 0, 0, 1, 1, 0]
cm = confusion_matrix(y_true, y_pred)
print(cm)
# 输出:
# [[3 1]
# [1 3]]
输出结果怎么读?
左上角 3 是 TN(实际0预测0),右上角 1 是 FP(实际0预测1)。左下角 1 是 FN(实际1预测0),右下角 3 是 TP(实际1预测1)。
一个技巧:我习惯先看真实类别。在 sklearn 的 confusion_matrix 中,行对应真实值,列对应预测值。但很多教材是反过来的。每次用之前,我都先打印出来确认一下行列含义,免得算错了指标。
从混淆矩阵看模型表现
有了这四个数,就能算出很多评估指标了。
我见过不少分析师拿着混淆矩阵不知所措。其实核心就记住一个思路:你要关注哪类错误代价更大。
举个例子:
- 医疗诊断场景:FN(漏诊)可能让人丢掉性命,FP(误诊)只是多花点检查费。
- 垃圾邮件过滤:FN(放过垃圾邮件)用户点个举报就完事,FP(误杀正常邮件)用户可能永远看不到重要通知。
你想想看,同样一个模型,在不同场景下评估标准完全不一样。
常用派生指标
| 指标 | 公式 | 我的理解 |
|---|---|---|
| 准确率 (Accuracy) | (TP + TN) / (TP + TN + FP + FN) | 总的正确率。但数据不平衡时容易被骗。 |
| 精确率 (Precision) | TP / (TP + FP) | 你判断为“真”的那些,有多少是真正对的。别误报。 |
| 召回率 (Recall) | TP / (TP + FN) | 实际为“真”的那些,你抓出来了多少。别漏报。 |
| F1 分数 | 2 × (Precision × Recall) / (Precision + Recall) | 精确率和召回率的调和平均,均衡版评分。 |
| 特异度 (Specificity) | TN / (TN + FP) | 实际为“假”的那些,你判断对了多少。防误报用的。 |
我曾经掉进过一个坑。一个分类任务,正样本特别少,我拼命优化召回率。结果呢?召回率从 0.6 涨到了 0.95,但精确率掉到了 0.1。模型几乎把所有样本都判成正样本,召回当然高了,但业务方完全没法用。
所以我现在养成了一个习惯:拿到混淆矩阵,先把四个数写出来,然后根据自己的业务场景,选定最关注的 1-2 个指标去优化。不要试图同时最大化所有指标,那是不可能的。
多分类的混淆矩阵
二分类你懂了,多分类其实也一样。就是矩阵变大而已。
每个类别在自己的那一行是 Positive,其他都是 Negative。所以对于 3 分类,你会得到 3×3 的矩阵。
我个人习惯看多分类混淆矩阵时,重点关注对角线上的数值。对角线越亮,说明这个类别分得越好。如果某个类别在其他列上有大量样本,那就要想想是不是特征区分度不够。
from sklearn.metrics import confusion_matrix
import seaborn as sns
import matplotlib.pyplot as plt
y_true = ['猫', '狗', '猫', '鸟', '狗', '猫']
y_pred = ['猫', '狗', '猫', '猫', '狗', '鸟']
labels = ['猫', '狗', '鸟']
cm = confusion_matrix(y_true, y_pred, labels=labels)
# 可视化
sns.heatmap(cm, annot=True, fmt='d', xticklabels=labels, yticklabels=labels)
plt.title('多分类混淆矩阵')
plt.ylabel('真实值')
plt.xlabel('预测值')
plt.show()
注意:多分类时,每个类别的 TP、FP、FN、TN 都是基于“一对一”策略计算的。也就是说,计算“猫”这个类别的指标时,把“猫”当作 Positive,其他所有类别当作 Negative。不要混淆。
什么时候该用混淆矩阵?
我总结了几种必用的场景:
- 分类不平衡时。别只看准确率,骗人的。
- 误报和漏报代价不同时。必须分开看 FP 和 FN 的数量。
- 需要向业务方解释模型行为时。一张矩阵图,比罗列一堆数字直观得多。
- 调参时。每次跑完实验,先看混淆矩阵,再决定下一步怎么调。
最后说一句:混淆矩阵不是终点。它是一个起点。你从这四个数字里,能看到模型的优势和短板。剩下的事情,就是根据业务需求,去平衡你真正关心的指标了。