【人工智能】【Python】混淆矩阵的各项指标计算

混淆矩阵的基本概念

在机器学习领域,混淆矩阵(confusion matrix),又称为可能性表格或是错误矩阵。它是一种特定的矩阵用来呈现算法性能的可视化效果,通常是监督学习(非监督学习,通常用匹配矩阵:matching matrix)。其每一列代表预测值,每一行代表的是实际的类别。这个名字来源于它可以非常容易的表明多个类别是否有混淆(也就是一个class被预测成另一个class)。

假设有一个用来对猫(cats)、狗(dogs)、兔子(rabbits)进行分类的系统,混淆矩阵就是为了进一步分析性能而对该算法测试结果做出的总结。假设总共有 27 只动物:8只猫, 6条狗, 13只兔子。结果的混淆矩阵如下图:

1715924285269.png

混淆矩阵的结构

混淆矩阵通常以二维表的形式展示,对于二分类问题,结构如下:

预测正类 (Positive)预测负类 (Negative)
实际正类 (Positive)TPFN
实际负类 (Negative)FPTN

这里的正负也就是预测正确和预测错误。

基本用语

1715924487144.png

Condition Positive (P) = 测试数据中正例的数量。此处是猫=5。

Condition Negatives (N) = 测试数据中反例的数量。此处是非猫=8。

True Positive (TP) = 是正例,且很幸运我也猜它是正例的数量。此处4表示有4张图片是猫,且我也猜它是猫。TP越大越好,因为表示我猜对了。这个时候如果你质问我猫不是5张图片吗?那么别急,重新看看TP的定义。然后再看看FN的定义。

False Negative (FN) = 明明是正例,但很不幸,我猜它是反例的数量。此处1表示有1张图片明明是猫,但我猜它非猫。FN越小越好,因为FN说明我猜错了。现在来看5张猫图片是不是全了。其中4张猫我猜是猫,1张猫我猜非猫。

True Negative (TN) = 是反例,且很幸运我也猜它是反例的数量。此处5表示有5张图片是非猫,且我也猜它是非猫。TN越大越好,因为表示我猜对了。

False Positive (FP) = 明明是反例,但很不幸我猜它是正例的数量。此处3表示有3张图片是非猫,但我猜它是猫。FP越小越好,因为FP说明我猜错了。

我感觉这么记这些缩写比较好:第一个True or False是指预测对不对,也就是说反斜对角线上开头都是True,正斜对角线上开头都是False。然后第二个Positive or Negative与列索引保持一致即可。

多分类问题的扩展

对于多分类问题,上述指标可以扩展为对每个类别分别计算,然后通过宏平均(macro-average)或微平均(micro-average)等方式综合得到整体指标:

  • 宏平均(Macro-average):对每个类别计算指标,然后取平均值,不考虑类别的样本数量。
  • 微平均(Micro-average):将所有类别的TP、FP、TN、FN累加后计算指标,考虑了类别的样本数量。
1715924839682.png

计算评估标准

预测正类 (Positive)预测负类 (Negative)
实际正类 (Positive)TPFN
实际负类 (Negative)FPTN

对着表格看一下以下公式:

准确率(Accuracy)准确率表示模型预测正确的样本占总样本的比例。它衡量了模型整体的分类性能。准确率 = (TP+TN)/(TP+TN+FP+FN)

精确率(Precision)精确率表示模型预测为正类的样本中,实际为正类的比例。它衡量了模型在正类预测中的准确性。精确率 = TP/(TP+FP)

召回率(Recall)召回率表示实际为正类的样本中,被模型正确预测为正类的比例。它衡量了模型对正类样本的识别能力。召回率 = TP/(TP+FN)

F1分数(F1 Score)F1分数是精确率和召回率的调和平均数,用于综合衡量模型的精确率和召回率。它在两者之间取得平衡,尤其适用于类不平衡的数据集。

F1分数 = 2 x (精确率 x 召回率)/(精确率 + 召回率)

源代码

def confusion_matrix_scores(confusion_matrix):
    # 将 confusion_matrix 转换为 NumPy 数组,方便进行矩阵操作
    confusion_matrix = np.array(confusion_matrix)
    # 获取类别数量
    num_classes = confusion_matrix.shape[0]
    # 计算样本总数
    total_samples = np.sum(confusion_matrix)

    # 存储每个类别的精确率,召回率和F1分数
    per_class_values = []
    for i in range(num_classes):
        # 计算类别 i 的真阳性数(TP)
        tp_i = confusion_matrix[i, i]

        # 计算类别 i 的精确率(Precision)
        col_i_sum = np.sum(confusion_matrix[:, i])  # 计算第 i 列的总和
        precision_i = tp_i / col_i_sum if col_i_sum != 0 else 0  # 添加除零检查

        # 计算类别 i 的召回率(Recall)
        row_i_sum = np.sum(confusion_matrix[i, :])  # 计算第 i 行的总和
        recall_i = tp_i / row_i_sum if row_i_sum != 0 else 0  # 添加除零检查

        # 计算类别 i 的F1分数
        if precision_i + recall_i != 0:
            f1_i = 2 * precision_i * recall_i / (precision_i + recall_i)
        else:
            f1_i = 0

        # 将计算结果存储到列表per_class_values中
        per_class_values.append((precision_i, recall_i, f1_i))
        # 打印类别 i 的各项指标
        print(f"类别{i}的精确率:{100*precision_i:.2f}%,召回率:{100*recall_i:.2f}%,F1分数:{100*f1_i:.2f}%")

    # 计算总的准确率(Accuracy)
    accuracy = np.trace(confusion_matrix) / total_samples  # np.trace(confusion_matrix) 返回对角线元素之和,即所有TP的总和
    # 将每个类别的精确率、召回率和F1分数解包
    precisions, recalls, f1s = zip(*per_class_values)

    # 计算微平均(micro-average)的精确率、召回率和F1分数
    micro_precision = np.mean(precisions)
    micro_recall = np.mean(recalls)
    micro_f1 = np.mean(f1s)

    print(f"总准确率:{100*accuracy:.2f}%", end=" ")
    print(f"总精确率:{100*micro_precision:.2f}%", end=" ")
    print(f"总召回率:{100*micro_recall:.2f}%", end=" ")
    print(f"总F1分数:{100*micro_f1:.2f}%")

此函数传入的confusion_matrix为Python中的二维列表,当然你也可以直接传入numpy数组,然后把函数第一行代码的转换为numpy数组删掉。

© 版权声明
THE END
点赞12 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容