4. 混淆矩阵:TP、FP、TN、FN 的定义与计算

聊到模型精度评估,绕不开一个东西——混淆矩阵。

说实话,我刚入行那会儿,觉得这玩意儿就是个四宫格表格,没啥技术含量。后来在项目里被坑过几次,才明白这四格子里藏着的门道,比一个 mAP 指标丰富得多。

你想想看,一个模型说它准确率 99%,听起来很牛对吧?但如果你的正样本只占 1%,那模型只要全部判负,准确率也是 99%。这种「虚假繁荣」我见过太多次了。所以,咱们得把混淆矩阵讲透。

4.1 四个基本概念:TP、FP、TN、FN

先明确一个前提:我们讨论的是二分类场景。YOLO 的多类别检测,本质上是对每个类别做二分类判断。

四个术语,我习惯用一个「预测 vs 真实」的对照表来记:

术语 全称 真实标签 预测结果 通俗理解
TP True Positive 正样本 预测为正 模型说「是」,实际也是「是」
FP False Positive 负样本 预测为正 模型说「是」,实际是「否」——误报
TN True Negative 负样本 预测为负 模型说「否」,实际也是「否」
FN False Negative 正样本 预测为负 模型说「否」,实际是「是」——漏检

核心记忆口诀:

第一个字母 T/F 表示预测对/错,第二个字母 P/N 表示预测的结果。

TP = 预测对了,结果是正;FP = 预测错了,结果是正。

在 YOLO 的检测任务里,怎么判断一个预测框是 TP 还是 FP?

关键看 IoU(交并比)。我一般设 IoU ≥ 0.5 算匹配成功。如果预测框和某个真实框的 IoU 超过阈值,且类别一致,就算 TP。如果预测框没匹配到任何真实框,或者类别错了,就算 FP。至于 FN,就是那些真实存在但模型没检测出来的目标。

我的经验:

我曾经在一个行人检测项目里,发现 FP 特别高。排查了半天,原来是模型把路边的垃圾桶当成了人。这就是典型的 FP——模型「过于积极」了。

4.2 从四个值到混淆矩阵

混淆矩阵就是把 TP、FP、TN、FN 放到一个 2×2 的表格里。行是真实标签,列是预测结果。

预测为正 (Positive) 预测为负 (Negative)
真实为正 (Positive) TP FN
真实为负 (Negative) FP TN

这个矩阵看起来简单,但它能衍生出几乎所有重要的评估指标。比如:

  • 准确率 (Accuracy) = (TP + TN) / (TP + TN + FP + FN)
  • 精确率 (Precision) = TP / (TP + FP) —— 模型说「是」的时候,到底有多靠谱
  • 召回率 (Recall) = TP / (TP + FN) —— 模型把真正的正样本找出了多少
  • 特异度 (Specificity) = TN / (TN + FP) —— 模型对负样本的判断能力

注意:

在类别极度不平衡的数据集里,Accuracy 会骗人。我建议你优先看 Precision 和 Recall 的组合,或者直接看 F1-score。

4.3 用 Python 构建混淆矩阵

理论说完了,咱们动手写代码。OpenCV 本身不提供混淆矩阵的计算函数,但我们可以用 NumPy 或者 scikit-learn 来构建。

方法一:手动计算(适合理解原理)

import numpy as np

# 模拟数据:真实标签和预测标签
y_true = np.array([1, 0, 1, 1, 0, 1, 0, 0, 1, 0])
y_pred = np.array([1, 0, 1, 0, 0, 1, 0, 1, 1, 0])

# 初始化矩阵
tp = np.sum((y_true == 1) & (y_pred == 1))
fp = np.sum((y_true == 0) & (y_pred == 1))
tn = np.sum((y_true == 0) & (y_pred == 0))
fn = np.sum((y_true == 1) & (y_pred == 0))

confusion_matrix = np.array([[tp, fn],
                             [fp, tn]])

print("混淆矩阵:")
print(confusion_matrix)
print(f"TP={tp}, FP={fp}, TN={tn}, FN={fn}")

方法二:用 scikit-learn(推荐,生产环境用这个)

from sklearn.metrics import confusion_matrix
import seaborn as sns
import matplotlib.pyplot as plt

y_true = [1, 0, 1, 1, 0, 1, 0, 0, 1, 0]
y_pred = [1, 0, 1, 0, 0, 1, 0, 1, 1, 0]

cm = confusion_matrix(y_true, y_pred)
print("混淆矩阵:\n", cm)

# 可视化
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
            xticklabels=['Negative', 'Positive'],
            yticklabels=['Negative', 'Positive'])
plt.xlabel('Predicted')
plt.ylabel('True')
plt.title('Confusion Matrix')
plt.show()

我的习惯:

在 YOLO 的验证脚本里,我会在每个 epoch 结束后,把验证集的预测结果和真实标注做比对,生成混淆矩阵。这样能直观看到模型在哪些类别上容易混淆。

4.4 混淆矩阵的可视化

光有数字不够,可视化才能一眼看出问题。我一般用 Seaborn 的 heatmap,或者用 Matplotlib 自己画。

下面是一个更完整的可视化代码,适合直接用在 YOLO 的评估脚本里:

import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np

def plot_confusion_matrix(cm, class_names, title='Confusion Matrix'):
    """
    绘制混淆矩阵
    cm: 混淆矩阵 (numpy array)
    class_names: 类别名称列表
    """
    plt.figure(figsize=(8, 6))
    sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
                xticklabels=class_names,
                yticklabels=class_names)
    plt.title(title, fontsize=16)
    plt.xlabel('Predicted Label', fontsize=12)
    plt.ylabel('True Label', fontsize=12)
    plt.tight_layout()
    plt.show()

# 示例:3 类别的混淆矩阵
cm_multi = np.array([[50, 2, 1],
                     [3, 45, 4],
                     [2, 3, 40]])
class_names = ['Person', 'Car', 'Bicycle']
plot_confusion_matrix(cm_multi, class_names)

为什么我强调可视化?

有一次我在做交通场景的检测,模型整体 mAP 有 0.78,看起来还行。但画出混淆矩阵后,我发现「摩托车」和「自行车」这两个类别的混淆特别严重——模型经常把摩托车认成自行车。这就是混淆矩阵的价值:它告诉你问题出在哪里,而不是只告诉你好不好。

4.5 多类别场景下的混淆矩阵

YOLO 检测的是多类别,所以我们的混淆矩阵是 N×N 的,N 是类别数。

对于每个类别,我们都可以单独计算它的 TP、FP、TN、FN。做法是:把当前类别当作正类,其他所有类别当作负类。

类别 TP FP FN Precision Recall
Person 120 15 8 0.889 0.938
Car 200 25 10 0.889 0.952
Bicycle 45 20 12 0.692 0.789

看到没?Bicycle 的 Precision 和 Recall 明显偏低。这时候我就会去检查:是不是训练数据里自行车样本太少?还是自行车和摩托车的特征太像?

避坑指南:

我曾经在计算多类别混淆矩阵时,忘了把「背景」类别排除掉。结果矩阵里多了一行一列,所有指标都算错了。记住:YOLO 的预测中,背景类(置信度低于阈值)不应该参与混淆矩阵的计算。

4.6 本章知识结构图

下面这张图,把混淆矩阵的核心逻辑串起来了:

混淆矩阵核心知识结构 混淆矩阵 TP (真正例) FP (假正例) TN (真负例) FN (假负例) Precision Recall F1-Score Accuracy 应用场景:YOLO 验证集评估 · 类别混淆分析 · 模型对比 阈值调优 · 数据不平衡诊断 TP/FP/TN/FN → 混淆矩阵 → 评估指标 → 模型优化决策

这张图把整个逻辑链条理清了:从四个基本值,到混淆矩阵,再到衍生指标,最后落到实际应用。你在做模型评估时,就按这个路径走,不会乱。

4.7 小结

混淆矩阵不是终点,它是起点。通过它,你能看到模型的「偏科」情况——哪个类别检测得好,哪个类别容易误报,哪个类别经常漏检。

我个人习惯,每次训练完模型,第一件事就是看混淆矩阵。比看 mAP 直观多了。mAP 告诉你「考了多少分」,混淆矩阵告诉你「哪道题错了」。后者对改进模型更有帮助。