第2章:混淆矩阵详解——TP、FP、TN、FN 的定义与实战意义
上一章我们聊了 ROC 曲线的整体框架。但在动手画曲线之前,有个基础概念必须得先啃透——混淆矩阵。
说白了,混淆矩阵就是一张表。它能把模型预测的结果和真实标签之间的所有关系,一字排开呈现在你面前。我刚开始做分类任务时,经常搞混 TP、FP 这些缩写。后来发现,只要你理解了这个矩阵,几乎所有的分类评估指标都能从这里推导出来。
2.1 从一次预测开始
假设我们有个二分类问题。比如判断一封邮件是不是垃圾邮件。真实情况有两种:“是垃圾邮件”(正类)或“不是垃圾邮件”(负类)。模型预测也有两种:“预测为垃圾邮件”或“预测为正常邮件”。
把这两种情况交叉组合,就会出现四种结果:
- TP (True Positive):真实是正类,模型也预测为正类。
- TN (True Negative):真实是负类,模型也预测为负类。
- FP (False Positive):真实是负类,但模型预测为正类。
- FN (False Negative):真实是正类,但模型预测为负类。
注意看,名字里第一个词是“True”或“False”,指的是预测对不对。第二个词是“Positive”或“Negative”,指的是模型预测的结果。我遇到过不少同学把“False Positive”理解成“预测为假正例”,那就全拧了。
2.2 矩阵长什么样
把它画成 2x2 矩阵,看起来更清楚:
| 预测为正类 | 预测为负类 | |
|---|---|---|
| 真实为正类 | TP | FN |
| 真实为负类 | FP | TN |
行是真实标签,列是预测结果。读的时候先看行再看列。比如第一行第一列:真实正类、预测正类,那就是 TP。
2.3 实际计算:拿代码说话
说了这么多理论,不如直接跑一段代码。假设我们有 10 个样本的真实标签和预测结果:
y_true = [1, 0, 1, 1, 0, 0, 1, 0, 1, 0]
y_pred = [1, 0, 0, 1, 1, 0, 1, 0, 0, 0]
手动推一下:
- y_true=1 且 y_pred=1:位置 0、3、6 → 3 个 TP
- y_true=0 且 y_pred=0:位置 1、5、7、9 → 4 个 TN
- y_true=0 但 y_pred=1:位置 4 → 1 个 FP
- y_true=1 但 y_pred=0:位置 2、8 → 2 个 FN
用 sklearn 验证一下:
from sklearn.metrics import confusion_matrix
y_true = [1, 0, 1, 1, 0, 0, 1, 0, 1, 0]
y_pred = [1, 0, 0, 1, 1, 0, 1, 0, 0, 0]
tn, fp, fn, tp = confusion_matrix(y_true, y_pred).ravel()
print(f"TP: {tp}, TN: {tn}, FP: {fp}, FN: {fn}")
输出结果:
TP: 3, TN: 4, FP: 1, FN: 2
跟我手算的结果完全一致。`ravel()` 是为了把矩阵展平成一维数组,顺序是 TN、FP、FN、TP。这个顺序很多新手会搞错,我建议你每次用之前都打印出来看一眼。
cm = confusion_matrix(y_true, y_pred)
tn = cm[0, 0]
fp = cm[0, 1]
fn = cm[1, 0]
tp = cm[1, 1]
2.4 为什么这几个数那么重要?
你想想看,几乎所有分类指标都是从这 4 个数算出来的:
- 准确率 (Accuracy):全部猜对的比例。(TP + TN) / 总数
- 精确率 (Precision):预测为正类中有多少是真的正类。TP / (TP + FP)
- 召回率 (Recall):真的正类中有多少被找出来了。TP / (TP + FN)
- 特异度 (Specificity):真的负类中有多少被正确排除。TN / (TN + FP)
举个例子。做癌症筛查时,FP 意味着“没病被误诊成有病”,病人要承受不必要的心理压力和后续检查。FN 意味着“有病没查出来”,可能会错过最佳治疗期。你说哪个更致命?
我曾在医疗影像项目里遇到过类似情况。当时的模型准确率高达 98%,但 FN 其实有 5 例。放到临床上,这就是 5 个漏诊患者。后来我们调整了分类阈值,哪怕牺牲一点精确率,也要把召回率提上去。这就是 TPR 和 FPR 的博弈。
2.5 从混淆矩阵到 TPR 和 FPR
铺垫了这么多,终于要聊到跟 ROC 曲线直接相关的两个指标了:
- 真正率 (TPR) = TP / (TP + FN) = 召回率
- 假正率 (FPR) = FP / (TN + FP)
ROC 曲线本质上就是不断改变分类阈值,算出一组 (FPR, TPR),然后把这些点连成线。
比如我们将判断垃圾邮件的阈值从 0.5 降到 0.3。预测为正类的样本变多了,TP 和 FP 会同时增加。对应的 TPR 和 FPR 也会变化。把这些变化画出来,就是 ROC 曲线。
下面这张图可以帮你直观理解 TP、FP、TN、FN 在数据空间中的位置关系:
2.6 实际业务中的权衡
不同场景对 FP 和 FN 的容忍度完全不同。我整理了一个清单,你可以参考一下:
| 场景 | 更讨厌 | 原因 |
|---|---|---|
| 癌症筛查 | FN | 漏诊可能延误治疗,后果严重 |
| 垃圾邮件过滤 | FP | 误杀正常邮件会影响用户体验 |
| 信用卡欺诈检测 | FN | 漏掉一笔欺诈交易就可能造成经济损失 |
| 内容审核 | FP | 误删合法内容会引发争议和流失 |
所以,在评估模型时一定要先明确业务约束。你想最大化召回率还是最大化精确率?这直接决定了你要在 ROC 曲线上选哪个点作为最终决策阈值。
混淆矩阵虽然基础,但它是理解 ROC 曲线的第一步。把这 4 个格子里的数字搞清楚,后面的灵敏度、特异度、AUC 这些概念才能接得住。