第二十三章 解析PR曲线、ROC曲线、AUC、AP(工具)

news2024/11/29 10:44:08

混淆矩阵Confusion Matrix

混淆矩阵定义

混淆矩阵是机器学习中总结分类模型预测结果的情形分析表,以矩阵形式将数据集中的记录按照真实的类别与分类模型预测的类别判断两个标准进行汇总。其中矩阵的行表示真实值,矩阵的列表示预测值,下面我们先以二分类为例,看下矩阵表现形式,如下:

预测/真实1(Postive)0(Negative)
1 (Postive)TP(True Postive:真阳)FP (False Postive:假阳)
0(Negative)FN (False Negative:假阴)TN (True Negative:真阴)

img

在讲矩阵之前,我们先复习下之前在讲分类评估指标中定义的一些符号含义,如下:

  1. TP(True Positive):将正类预测为正类数,真实为0,预测也为0
  2. FN(False Negative):将正类预测为负类数,真实为0,预测为1
  3. FP(False Positive):将负类预测为正类数, 真实为1,预测为0
  4. TN(True Negative):将负类预测为负类数,真实为1,预测也为1

刚才分析的是二分类问题,那么对于多分类问题,混淆矩阵表示的含义也基本相同,这里我们以三类问题为例,看看如何根据混淆矩阵计算各指标值。

img

与二分类混淆矩阵一样,矩阵行数据相加是真实值类别数,列数据相加是分类后的类别数,那么相应的就有以下计算公式;

  1. 精确率_类别1=a/(a+d+g)
  2. 召回率_类别1=a/(a+b+c)

python 实现混淆矩阵

混淆矩阵(Confusion Matrix),是一种在深度学习中常用的辅助工具,可以让你直观地了解你的模型在哪一类样本里面表现得不是很好。

示例代码一如下:

在这里插入图片描述

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

# 使用 seaborn 风格设置
sns.set()

# 创建混淆矩阵
C2 = confusion_matrix([0, 1, 2, 0, 1, 2, 0, 2, 2, 0, 1, 1], [0, 1, 1, 2, 1, 0, 0, 2, 2, 0, 1, 1])

# 创建子图
f, ax = plt.subplots()

# 打印混淆矩阵
print(C2)

# 绘制热力图
sns.heatmap(C2, annot=True, ax=ax)

# 设置标题和轴标签
ax.set_title('Confusion Matrix')  # 标题
ax.set_xlabel('Predicted')  # x轴
ax.set_ylabel('True')  # y轴

# 显示图像
plt.show()

示例代码二如下:

在这里插入图片描述

import matplotlib.pyplot as plt
import numpy as np


def plot_Matrix(cm, classes, title=None, cmap=plt.cm.Blues):
    plt.rc('font', family='Times New Roman', size='8')  # 设置字体样式、大小

    # 按行进行归一化
    cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
    print("Normalized confusion matrix")
    str_cm = cm.astype(np.str).tolist()
    for row in str_cm:
        print('\t'.join(row))
    # 占比1%以下的单元格,设为0,防止在最后的颜色中体现出来
    for i in range(cm.shape[0]):
        for j in range(cm.shape[1]):
            if int(cm[i, j] * 100 + 0.5) == 0:
                cm[i, j] = 0

    fig, ax = plt.subplots()
    im = ax.imshow(cm, interpolation='nearest', cmap=cmap)
    # ax.figure.colorbar(im, ax=ax) # 侧边的颜色条带

    ax.set(xticks=np.arange(cm.shape[1]),
           yticks=np.arange(cm.shape[0]),
           xticklabels=classes, yticklabels=classes,
           title=title,
           ylabel='Actual',
           xlabel='Predicted')

    # 通过绘制格网,模拟每个单元格的边框
    ax.set_xticks(np.arange(cm.shape[1] + 1) - .5, minor=True)
    ax.set_yticks(np.arange(cm.shape[0] + 1) - .5, minor=True)
    ax.grid(which="minor", color="gray", linestyle='-', linewidth=0.2)
    ax.tick_params(which="minor", bottom=False, left=False)

    # 将x轴上的lables旋转45度
    plt.setp(ax.get_xticklabels(), rotation=45, ha="right",
             rotation_mode="anchor")

    # 标注百分比信息
    fmt = 'd'
    thresh = cm.max() / 2.
    for i in range(cm.shape[0]):
        for j in range(cm.shape[1]):
            if int(cm[i, j] * 100 + 0.5) > 0:
                ax.text(j, i, format(int(cm[i, j] * 100 + 0.5), fmt) + '%',
                        ha="center", va="center",
                        color="white" if cm[i, j] > thresh else "black")
    fig.tight_layout()
    plt.savefig('cm.jpg', dpi=300)
    plt.show()


# 构造一个混淆矩阵
cm = np.array([[10, 20, 30], [40, 50, 60], [70, 80, 90]])
classes = ['class1', 'class2', 'class3']

# 调用函数绘制混淆矩阵
plot_Matrix(cm, classes, title='Confusion Matrix')

PR曲线 & AP

Recall召回率(查全率):

R e c a l l = T P T P + F N Recall=\frac{TP}{TP+FN} Recall=TP+FNTP

含义:TP除以第一列,即预测为1实际为1的样本在所有真实为1类别中的占比。

Presession精准率(查准率):

P r e s e s s i o n = T P T P + F P Presession=\frac{TP}{TP+FP} Presession=TP+FPTP

含义:FP除以第一行,即预测为1实际为1的样本在所有预测为1类别中的占比。

PR曲线:

同理ROC曲线。在模型预测的时候,我们输出的预测结果是一堆[0,1]之间的数值,怎么把数值变成二分类?设置一个阈值,大于这个阈值的值分类为1,小于这个阈值的值分类为0。ROC曲线就是我们从[0,1]设置一堆阈值,每个阈值得到一个(Presession,Recall)对,纵轴为Presession,横轴为Recall,把所有的(Presession,Recall)对对连起来就得到了PR曲线。

img

AP(PR曲线下的面积):

跟TPR和FPR不一样的是,在PR关系中,是一个此消彼长的关系,但往往我们希望二者都是越高越好,所以PR曲线是右上凸效果越好(也有例外,有比如在风险场景当预测为1实际为0时需要赔付时,大致会要求Recall接近100%,可以损失Precision)。所以除了特殊情况,通常情况都会使用Precision-recall曲线,来寻找分类器在Precision与Recall之间的权衡。

AP就是Precision-recall 曲线下面的面积,通常来说一个越好的分类器,AP值越高。

还有一个mAP的概念,mAP是多个类别AP的平均值。这个mean的意思是对每个类的AP再求平均,得到的就是mAP的值,mAP的大小一定在[0,1]区间,越大越好。该指标是目标检测算法中最重要的一个。

精准率和召回率是相互制约的,如果想要精准率提高,召回率则会下降,如果要召回率提高,精准率则会下降,我们需要找到二者之间的一个平衡。

P-R曲线的生成方法:根据学习器的预测结果对样本进行排序,排在前面的是学习器认为最可能是正例的样本,排在最后的是最不可能是正例的样本,按此顺序逐个将样本作为正例预测,则每次可以计算出当前的查全率、查准率,以查全率为横轴、查准率为纵轴做图,得到的查准率-查全率曲线即为P-R曲线。也就是说对每个样本预测其为正例的概率,然后将所有样本按预测的概率进行排序,然后依次将排序后的样本做为正例进行预测,从而得到每次预测的查全率与查准率。这个依次将样本做为正例的过程实际上就是逐步降低样本为正例的概率的域值,通过降低域值,更多的样本会被预测为正例,从而会提高查全率,相对的查准率可能降低,而随着后面负样本的增加,查全率提高缓慢甚至没有提升,精度降低会更快。

img

代码实现如下:

import matplotlib.pyplot as plt
from sklearn.metrics import precision_recall_curve
import numpy as np


plt.figure('P-R Curve')
plt.title('Precision/Recall Curve')
plt.xlabel('Recall')
plt.ylabel('Precision')

#y_true为样本实际的类别,y_scores为样本为正例的概率
y_true = np.array([1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0])
y_scores = np.array([0.9, 0.75, 0.86, 0.47, 0.55, 0.56, 0.74, 0.62, 0.5, 0.86, 0.8, 0.47, 0.44, 0.67, 0.43, 0.4, 0.52, 0.4, 0.35, 0.1])
precision, recall, thresholds = precision_recall_curve(y_true, y_scores)

plt.plot(recall,precision)
plt.show()

ROC曲线 & AUC

真阳率:

T P R = T P T P + F N = F P P TPR=\frac{TP}{TP+FN}=\frac{FP}{P} TPR=TP+FNTP=PFP

含义:TP除以第一列,即预测为1实际为1的样本在所有真实1类别中的占比。

假阳率:

F P R = F P F P + T N = F P N FPR=\frac{FP}{FP+TN}=\frac{FP}{N} FPR=FP+TNFP=NFP

含义:FP除以第二列,即预测为1实际为0的样本在所有真实0类别中的占比。

ROC曲线:

在模型预测的时候,我们输出的预测结果是一堆[0,1]之间的数值,怎么把数值变成二分类?设置一个阈值,大于这个阈值的值分类为1,小于这个阈值的值分类为0。ROC曲线就是我们从[0,1]设置一堆阈值,每个阈值得到一个(TPR,FPR)对,纵轴为TPR,横轴为FPR,把所有的(TPR,FPR)对连起来就得到了ROC曲线。

img

代码实现:

import matplotlib
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import precision_recall_curve, roc_curve

plt.figure()
plt.title('PR Curve')
plt.xlabel('Recall')
plt.ylabel('Precision')
plt.grid()

# 只是理解两种曲线的含义,所以数据简单的构造
confidence_scores = np.array([0.9, 0.46, 0.78, 0.37, 0.6, 0.4, 0.2, 0.16])
confidence_scores = sorted(confidence_scores, reverse=True)  # 置信度从大到小排列
print(confidence_scores)

data_labels = np.array([1, 1, 0, 1, 0, 0, 1, 1])  # 置信度所对应的标签

# 精确率,召回率,阈值
precision, recall, thresholds = precision_recall_curve(data_labels, confidence_scores)
print(precision)
print(recall)
print(thresholds)
plt.plot(recall, precision)
plt.show()

# 真正率,假正率
fpr, tpr, thresholds = roc_curve(data_labels, confidence_scores)
# print(fpr)
# print(tpr)
plt.figure()
plt.grid()
plt.title('Roc Curve')
plt.xlabel('FPR')
plt.ylabel('TPR')

from sklearn.metrics import auc

auc = auc(fpr, tpr)  # AUC计算
plt.plot(fpr, tpr, label='roc_curve(AUC=%0.2f)' % auc)
plt.legend()
plt.show()

AUC(area under the curve):

(1)计算方法一

AUC即ROC曲线下的面积。曲线越靠近左上角,意味着TPR>FPR,模型的整体表现也就越好。所以我们可以断言,ROC曲线下的面积越大,模型效果越好。

最坏的情况是,总是有TPR=FPR,如下图,表示对于不论真实类别是1还是0的样本,分类器预测为1的概率是相等的。换句话说,分类器对于正例和负例毫无区分能力。如果AUC小于0.5,那么只要把预测类别取反,便得到了一个AUC大于0.5的分类器。

img

(2)计算方法二

AUC还有一种解释就是任取一对正负样本,正样本的预测值大于负样本的预测值的概率。 显然可以写出计算AUC伪代码为:

1.统计所有正样本个数P,负样本个数N;

2.遍历所有正负样本对,统计正样本预测值大于负样本预测值的样本总个数number

3. A U C = n u m b e r / ( P ∗ N ) AUC = number / (P * N) AUC=number/(PN) 一些计算细节是当正负样本预测值刚好相等时,该样本记为0.5个。

PS: 在实际代码实现过程中其实可以和第一种方法一样进一步优化,可以先对正负样本排序,利用dp的思想迭代计算个数,可以将复杂度从 O ( N 2 ) O(N^2) O(N2)降低为 O ( N l o g N ) O(NlogN) O(NlogN)

类别不平衡问题中如何选择PR与ROC

这里特指负样本数量远大于正样本时,在这类问题中,我们往往更关注正样本是否被正确分类,即TP的值。PR曲线更适合度量类别不平衡问题中:

  1. 因为在PR曲线中TPR和FPR的计算都会关注TP,PR曲线对正样本更敏感。
  2. 而ROC曲线正样本和负样本一视同仁,在类别不平衡时ROC曲线往往会给出一个乐观的结果。

理解的动态图:

https://zhuanlan.zhihu.com/p/92218196

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1256309.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

cephadm部署ceph quincy版本,使用ceph-csi连接

环境说明 IP主机名角色 存储设备 192.168.2.100 master100 mon,mgr,osd,mds,rgw 大于5G的空设备192.168.2.101node101mon,mgr,osd,mds,rgw大于5G的空设备192.168.2.102node102mon,mgr,osd,mds,rgw大于5G的空设备 关闭防火墙 关闭并且禁用selinux 配置主机名/etc/hosts …

【Amazon】在Amazon EKS集群中安装部署最小化KubeSphere容器平台

文章目录 一、准备工作二、部署 KubeSphere三、访问 KubeSphere 控制台四、安装Amazon EBS CSI 驱动程序4.1 集群IAM角色建立并赋予权限4.2 安装 Helm Kubernetes 包管理器4.3 安装Amazon EBS CSI 驱动程序 五、常见问题六、参考链接 一、准备工作 Kubernetes 版本必须为&…

RocketMQ的push消费方式实现的太聪明了

最近仍然畅游在RocketMQ的源码中,这几天刚好翻到了消费者的源码,发现RocketMQ的对于push消费方式的实现简直太聪明了,所以趁着我脑子里还有点印象的时候,赶紧来写一篇文章,来掰扯一下,防止过两天就忘得一干…

[网络] 5. TCP 链接的建立与释放~汇总

大部分内容源于网络加之个人理解~巨人的肩膀有多大决定你可以看得多远~ 文章目录 1. 三次握手说一下三次握手的过程为什么是三次握手 2. 四次挥手说一下四次挥手的过程为什么需要四次挥手有可能出现三次挥手吗,什么时候会出现呢?为…

video标签在h5中被劫持问题

将video的视频链接转为blob export const encryptionVideo (options: URL) > {return new Promise((resolve, reject) > {window.URL window.URL || window.webkitURL;var xhr new XMLHttpRequest();xhr.open(GET, options.url, true);xhr.responseType blob;xhr.onl…

正则化与正则剪枝

写在前面:本博客仅作记录学习之用,部分图片来自网络,如需引用请注明出处,同时如有侵犯您的权益,请联系删除! 文章目录 引言正则化为什么会过拟合拉格朗日与正则化梯度衰减与正则化 应用解决过拟合网络剪枝 …

JPA 自关联 设置单向多对一

Spring boot 3 JPA中,遇到一个需求,建一个数据字典表: Dictionary,存放两级数据,第一级为字典项目,第二级为项目内容,查询时要把parent_id对应父项的名称也一起查出来,返回前端。 …

VUE简易计划清单

目录 效果预览图 完整代码 效果预览图 完整代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>…

Web前端 ---- 【Vue】什么?代码堆在一起不好维护?辛辛苦苦改造的单文件组件用不了?看我直接Vue Cli脚手架安排

目录 前言 单文件组件 什么是单文件组件 单文件组件的内容 Es6模块的导入和导出 创建单文件组件 Vue Cli脚手架 前言 继上篇文章Vue组件的使用介绍了如何使用Vue组件&#xff0c;但是发现有一个很重要的问题&#xff0c;就是代码复用性很差&#xff0c;并且无法提供样式…

文件的写入和读取操作

题目&#xff1a; 编写一个程序&#xff0c;实现以下功能&#xff1a; 1. 创建一个新的文本文件&#xff0c;并将用户输入的数据写入文件中。 2. 打开已存在的文本文件&#xff0c;并将其中的数据显示在屏幕上。 #include <stdio.h> #include <stdlib.h> void wri…

事件委派+自定义属性+编程式导航实现路由跳转及传参

当我们页面中有许多a标签需要实现点击跳转到同一个页面并携带不同的参数时&#xff0c;我们就可以使用事件委派自定义属性编程式导航 的方式&#xff0c;用最小的内存实现路由跳转的最大效率。 为什么我们不用router-link 进行跳转&#xff1f; 要知道&#xff0c;我们页面中…

汇编:关于栈的知识

1.入栈和出栈指令 2. SS与SP 3. 入栈与出栈 3.1 执行push ax ↑↑ 3.2 执行pop ax ↓↓ 3.3 栈顶超界的问题 4. 寄存器赋值 基于8086CPU编程时&#xff0c;可以将一段内存当作栈来使用。一个栈段最大可以设为64KB&#xff08;0-FFFFH&#xff09;。 1.入栈和出栈指令…

058-第三代软件开发-文件Model

第三代软件开发-文件Model 文章目录 第三代软件开发-文件Model项目介绍文件Model 关键字&#xff1a; Qt、 Qml、 关键字3、 关键字4、 关键字5 项目介绍 欢迎来到我们的 QML & C 项目&#xff01;这个项目结合了 QML&#xff08;Qt Meta-Object Language&#xff09;…

[计算机网络]应用层概述

0.写在前面: 该层为教学模型的最后一层,某种意义上来说是最接近各位开发者的一层,正因如此,这层中的很多定义和概念大家都有属于自己的理解, 完全按照书本反而才是异类,因此在这里我会去结合我做前端开发的一些经验,来处理和讲解一些概念,另外本层中的部分协议也不会过多阐述了…

提升逼格,自己搭建博客网站不求人

背景 对于一个热爱分享知识和经验的大佬来说&#xff0c;搭建一个自己的个人博客是十分必要的。因为各个免费写博客平台都会有每天写博客限制&#xff0c;比如我现在这篇文章的限制&#xff0c;就是每天最多发表3篇&#xff0c;同时还给我的博客添加一大波广告&#xff0c;真是…

async函数和await关键字

async写在一个函数a前面&#xff0c;该函数变为异步函数&#xff0c;可在里面使用await关键字&#xff0c;await后面一般跟一个promise对象&#xff08;axios函数返回一个promise对象&#xff0c;里面有异步任务&#xff09;&#xff0c;await会原地等待该异步任务结果&#xf…

JVM基础篇:垃圾回收

1.前言 1.1C/C的内存管理 在C/C这类没有自动垃圾回收机制的语言中&#xff0c;一个对象如果不再使用&#xff0c;需要手动释放&#xff0c;否则就会出现内存泄漏。我们称这种释放对象的过程为垃圾回收&#xff0c;而需要程序员编写代码进行回收的方式为手动回收。内存泄漏指的…

WiFi 发射链路 MCS 自适应机制介绍

链路适配是指发射机选择最优的MCS向特定的接收机发送数据的过程。链路自适应算法的实现有其特殊性&#xff0c;但通常基于测量的数据包错误率(PER)。大多数算法监视PER并调整MCS以跟踪一个最佳的长期平均值&#xff0c;以平衡由于使用更高MCS发送更短数据包而减少的开销和由于更…

坚鹏:中国工商银行数字化背景下银行公司业务如何快速转型培训

中国工商银行作为全球最大的银行&#xff0c;资产规模超过40万亿元&#xff0c;最近几年围绕“数字生态、数字资产、数字技术、数字基建、数字基因”五维布局&#xff0c;深入推进数字化转型&#xff0c;加快形成体系化、生态化实施路径&#xff0c;促进科技与业务加速融合&…

jupyter notebook 添加conda环境变量为内核(kenel)

第一步&#xff1a;安装ipykernel 在激活环境后&#xff0c;需要安装ipykernel包&#xff0c;以便将Conda环境添加到Jupyter Notebook中。使用以下命令安装&#xff1a; pip install ipykernel第二步&#xff1a;将Conda环境添加到Jupyter 需要将Conda环境添加到Jupyter Not…