ROC 曲线详解

前言

ROC 曲线是一种坐标图式的分析工具,是由二战中的电子和雷达工程师发明的,发明之初是用来侦测敌军飞机、船舰,后来被应用于医学、生物学、犯罪心理学。

如今,ROC 曲线已经被广泛应用于机器学习领域的模型评估,说到这里就不得不提到 Tom Fawcett 大佬,他一直在致力于推广 ROC 在机器学习领域的应用,他发布的论文《An introduction to ROC analysis》[1]更是被奉为 ROC 的经典之作(引用 2.2w 次),知名机器学习库 scikit-learn 中的 ROC 算法就是参考此论文实现,可见其影响力!

不知道大多数人是否和我一样,对于 ROC 曲线的理解只停留在调用 scikit-learn 库的函数,对于它的背后原理和公式所知甚少。

前几天我重读了《An introduction to ROC analysis》终于将 ROC 曲线彻底搞清楚了,独乐乐不如众乐乐!如果你也对 ROC 的算法及实现感兴趣,不妨花些时间看完全文,相信你一定会有所收获!

图片

一、什么是 ROC 曲线

下图中的蓝色曲线就是 ROC 曲线,它常被用来评价二值分类器的优劣,即评估模型预测的准确度。

二值分类器,就是字面意思它会将数据分成两个类别(正/负样本)。例如:预测银行用户是否会违约、内容分为违规和不违规,以及广告过滤、图片分类等场景。篇幅关系这里不做多分类 ROC 的讲解。

图片

坐标系中纵轴为 TPR(真阳率/命中率/召回率)最大值为 1,横轴为 FPR(假阳率/误判率)最大值为 1,虚线为基准线(最低标准),蓝色的曲线就是 ROC 曲线。其中 ROC 曲线距离基准线越远,则说明该模型的预测效果越好。(TPR: True positive rate; FPR: False positive rate)

  • ROC 曲线接近左上角:模型预测准确率很高

  • ROC 曲线略高于基准线:模型预测准确率一般

  • ROC 低于基准线:模型未达到最低标准,无法使用

二、背景知识

考虑一个二分类模型, 负样本(Negative) 为 0,正样本(Positive) 为 1。即:

  • 标签 y 的取值为 0 或 1。

  • 模型预测的标签为 \hat{y},取值也是 0 或 1。

因此,将 y\hat{y} 两两组合就会得到 4 种可能性,分别称为:

图片

2.1 公式

ROC 曲线的横坐标为 FPR(False Positive Rate),纵坐标为 TPR(True Positive Rate)。FPR 统计了所有负样本中 预测错误(FP) 的比例,TPR 统计了所有正样本中 预测正确(TP) 的比例,其计算公式如下,其中 # 表示统计个数,例如 #N 表示负样本的个数,#P 表示正样本的个数

\text{FPR}=\frac{\#\text{FP}}{\#\text{N}} $$ $$\text{TPR}=\frac{\#\text{TP}}{\#\text{P}}

2.2 计算方法

下面举一个实际例子作为讲解,以下表 5 个样本为例,讲解如何计算 FPR 和 TPR

id真实标签  y预测标签 \hat{y}
111
210
300
411
501

正样本数 \#P=3,负样本数\#N=2

其中 y=0\hat{y}=1的样本有 1 个,即 \#FP=1,所以 FPR=1/2=0.5

其中 y=1\hat{y}=1 的样本有 2 个,即 \#TP=2,所以 FPR=2/3

FPR 和 TPR 的取值范围均是 0 到 1 之间。对于 FPR,我们希望其越小越好。而对于 TPR,我们希望其越大越好。

至此,我们已经介绍完如何计算 FPR 和 TPR 的值,下面将会讲解如何绘制 ROC 曲线。

三、绘制 ROC 曲线

讲到这里,可能有的同学会问:ROC 不是一条曲线吗?讲了这么多它到底应该怎么画呢?下面将分为两部分讲解如何绘制 ROC 曲线,直接打通你的“任督二脉”彻底拿下 ROC 曲线:

  • 第一部分:通过手绘的方式讲解原理

  • 第二部分:Python 代码实现,代码清爽易读

3.1 手绘 ROC 曲线

一般在二分类模型里(标签取值为 0 或 1),会默认设定一个阈值 (threshold)。当预测分数大于这个阈值时,输出 1,反之输出 0。我们可以通过调节这个阈值,改变模型预测的输出,进而画出 ROC 曲线。

以下面表格中的 20 个点为例,介绍如何人工画出 ROC 曲线,其中正样本和负样本都是 10 个,即 \#P = \#N = 10

id真实标签预测分数id真实标签预测分数
11.9111.4
21.8120.39
30.7131.38
41.6140.37
51.55150.36
61.54160.35
70.53171.34
80.52180.33
91.51191.30
100.505200.1

当设定阈值为 0.9 时,只有第一个点预测为 1,其余都为 0,故 \#FP=0\#TP=1,计算出 FPR=0/10=0TPR=1/10=0.1,画出点 (0,0.1)

当设定阈值为 0.8 时,只有前两个点预测为 1,其余都为 0,故 $\#FP=0、\#TP=2$,计算出 FPR=0/10=0 、TPR=2/10=0.2,画出点 (0,0.2)

当设定阈值为 0.7 时,只有前三个点预测为 1,其余都为 0,故 \#FP=1\#TP=2,计算出 FPR=1/10=0.1TPR=2/10=0.2,画出点 (0.1,0.2)。

以此类推,画出的 ROC 曲线如下:

图片

因此,在画 ROC 曲线前,需要将预测分数从大到小排序,然后将预测分数依次设定为阈值,分别计算 FPRTPR。而对于基准线,假设随机预测为正样本的概率为 x,即 \Pr(\hat{y}=1)=x 由于 FPR 计算的是负样本中,预测为正样本的概率,因此 FPR= x(同理,TPR= x)。所以,基准线为从点 (0, 0) 到 (1, 1) 的斜线

3.2 Python 代码

接下来,我们将结合代码讲解如何在 Python 中绘制 ROC 曲线。

下面的代码参考了《An Introduction to ROC Analysis》[2]中的算法 1(伪代码)。值得一提的是,知名机器学习库 scikit-learn 的 roc_curve 函数[3] 也参考了这个算法。

图片

下面我自己实现的 roc 函数可以理解为是简化版的 roc_curve,这里的代码逻辑更加简洁易懂,算法的时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)。

完整的代码如下:

# import numpy as np
def roc(y_true, y_score, pos_label):
    """
    y_true:真实标签
    y_score:模型预测分数
    pos_label:正样本标签,如“1”
    """
    # 统计正样本和负样本的个数
    num_positive_examples = (y_true == pos_label).sum()
    num_negtive_examples = len(y_true) - num_positive_examples

    tp, fp = 0, 0
    tpr, fpr, thresholds = [], [], []
    score = max(y_score) + 1
    
    # 根据排序后的预测分数分别计算fpr和tpr
    for i in np.flip(np.argsort(y_score)):
        # 处理样本预测分数相同的情况
        if y_score[i] != score:
            fpr.append(fp / num_negtive_examples)
            tpr.append(tp / num_positive_examples)
            thresholds.append(score)
            score = y_score[i]
            
        if y_true[i] == pos_label:
            tp += 1
        else:
            fp += 1

    fpr.append(fp / num_negtive_examples)
    tpr.append(tp / num_positive_examples)
    thresholds.append(score)

    return fpr, tpr, thresholds

导入上面 3.1 表格中的数据,通过上面实现的 roc 方法,计算 ROC 曲线的坐标值。

import numpy as np

y_true = np.array(
    [1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0]
)
y_score = np.array([
    .9, .8, .7, .6, .55, .54, .53, .52, .51, .505,
    .4, .39, .38, .37, .36, .35, .34, .33, .3, .1
])

fpr, tpr, thresholds = roc(y_true, y_score, pos_label=1)

最后,通过 Matplotlib 将计算出的 ROC 曲线坐标绘制成图。

import matplotlib.pyplot as plt

plt.plot(fpr, tpr)
plt.axis("square")
plt.xlabel("False positive rate")
plt.ylabel("True positive rate")
plt.title("ROC curve")
plt.show()

图片

至此,ROC 的基础知识部分就全部讲完了,如果还想深入了解的同学可以继续往下看。

四、联邦学习中的 ROC 平均

图片

顾名思义,ROC 平均就是将多条 ROC 曲线“平均化”。那么,什么场景需要做 ROC 平均呢?例如:横向联邦学习中,由于样本都在用户本地,服务器可以采用 ROC 平均的方式,计算近似的全局 ROC 曲线

ROC 的平均有两种方法:垂直平均、阈值平均,下面将逐一进行讲解,并给出 Python 代码实现。

4.1 垂直平均

图片

垂直平均(Vertical averaging)的思想是,选取一些 FPR 的点,计算其平均的 TPR 值。下面是论文中的算法描述的伪代码,看不懂可直接略过看 Python 代码实现部分。

图片

下面是 Python 的代码实现:

# import numpy as np
def roc_vertical_avg(samples, FPR, TPR):
    """
    samples:选取FPR点的个数
    FPR:包含所有FPR的列表
    TPR:包含所有TPR的列表
    """
    nrocs = len(FPR)
    tpravg = []
    fpr = [i / samples for i in range(samples + 1)]

    for fpr_sample in fpr:
        tprsum = 0
        # 将所有计算的tpr累加
        for i in range(nrocs):
            tprsum += tpr_for_fpr(fpr_sample, FPR[i], TPR[i])
        # 计算平均的tpr
        tpravg.append(tprsum / nrocs)

    return fpr, tpravg

# 计算对应fpr的tpr
def tpr_for_fpr(fpr_sample, fpr, tpr):
    i = 0
    while i < len(fpr) - 1 and fpr[i + 1] <= fpr_sample:
        i += 1

    if fpr[i] == fpr_sample:
        return tpr[i]
    else:
        return interpolate(fpr[i], tpr[i], fpr[i + 1], tpr[i + 1], fpr_sample)

# 插值
def interpolate(fprp1, tprp1, fprp2, tprp2, x):
    slope = (tprp2 - tprp1) / (fprp2 - fprp1)
    return tprp1 + slope * (x - fprp1)

4.2 阈值平均

图片

阈值平均(Threshold averaging)的思想是,选取一些阈值的点,计算其平均的 FPR 和 TPR。

图片

下面是 Python 的代码实现:

# import numpy as np
def roc_threshold_avg(samples, FPR, TPR, THRESHOLDS):
    """
    samples:选取FPR点的个数
    FPR:包含所有FPR的列表
    TPR:包含所有TPR的列表
    THRESHOLDS:包含所有THRESHOLDS的列表
    """
    nrocs = len(FPR)
    T = []
    fpravg = []
    tpravg = []

    for thresholds in THRESHOLDS:
        for t in thresholds:
            T.append(t)
    T.sort(reverse=True)

    for tidx in range(0, len(T), int(len(T) / samples)):
        fprsum = 0
        tprsum = 0
        # 将所有计算的fpr和tpr累加
        for i in range(nrocs):
            fprp, tprp = roc_point_at_threshold(FPR[i], TPR[i], THRESHOLDS[i], T[tidx])
            fprsum += fprp
            tprsum += tprp
        # 计算平均的fpr和tpr
        fpravg.append(fprsum / nrocs)
        tpravg.append(tprsum / nrocs)

    return fpravg, tpravg

# 计算对应threshold的fpr和tpr
def roc_point_at_threshold(fpr, tpr, thresholds, thresh):
    i = 0
    while i < len(fpr) - 1 and thresholds[i] > thresh:
        i += 1
    return fpr[i], tpr[i]

五、最后

本文由浅入深地详细介绍了 ROC 曲线算法,包含算法原理、公式、计算、源码实现和讲解,希望能够帮助读者一口气搞懂 ROC。

虽然 ROC 是个不起眼的知识点,但能网上能彻底讲清楚 ROC 的文章并不多。所以我又花时间重温了一遍 Tom Fawcett 的经典论文《An introduction to ROC analysis》[4],并将论文的内容抽丝剥茧、配上通俗易懂的 Python 代码,最终写出了这篇文章。再次致敬🫡 Tom Fawcett,感谢他在机器学习领域的贡献!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

模板初阶 C++

目录 泛型编程 函数模板 概念 格式 原理 函数模板的实例化 类模板 格式 类模板的实例化 泛型编程 当我们要实现一个交换函数&#xff0c;我们可以利用函数重载实现&#xff0c;但是有几个不好的地方 1.函数重载仅仅是类型不同&#xff0c;代码复用率较低&#xff0c;只…

pyorch Hub 系列#4:PGAN — GAN 模型

一、主题描述 2014 年生成对抗网络的诞生及其对任意数据分布进行有效建模的能力席卷了计算机视觉界。两人范例的简单性和推理时令人惊讶的快速样本生成是使 GAN 成为现实世界中实际应用的理想选择的两个主要因素。 然而&#xff0c;在它们出现后的很长一段时间内&#xff0c;GA…

知识蒸馏概述及开源项目推荐

文章目录 1.介绍2.知识2.1 基于响应的知识&#xff08;response-based&#xff09;2.2 基于特征的知识(feature-based)2.3 基于关系的知识(relation-based) 3.蒸馏机制3.1 离线蒸馏3.2 在线蒸馏3.3 自蒸馏 4.教师-学生架构5.蒸馏算法5.1 对抗性蒸馏&#xff08;Adversarial Dis…

Linux基础开发工具之调试器gdb

文章目录 1.编译成的可调试的debug版本1.1gcc test.c -o testdebug -g1.2readelf -S testdebug | grep -i debug 2.调试指令2.0quit退出2.1list/l/l 数字: 显示代码2.2run/r运行2.3断点相关1. break num/b num: 设置2. info b: 查看3. d index: 删除4. n: F10逐过程5. p 变量名…

Python文件、文件夹操作汇总

目录 一、概览 二、文件操作 2.1 文件的打开、关闭 2.2 文件级操作 2.3 文件内容的操作 三、文件夹操作 四、常用技巧 五、常见使用场景 5.1 查找指定类型文件 5.2 查找指定名称的文件 5.3 查找指定名称的文件夹 5.4 指定路径查找包含指定内容的文件 一、概览 ​在…

CSS注入的四种实现方式

目录 CSS注入窃取标签属性数据 简单的一个实验&#xff1a; 解决hidden 方法1&#xff1a;jsnode.js实现 侧信道攻击 方法2&#xff1a;对比波兰研究院的方案 使用兄弟选择器 方法3&#xff1a;jswebsocket实现CSS注入 实验实现&#xff1a; 方法4&#xff1a;window…

【云备份|| 日志 day6】文件业务处理模块

云备份day6 业务处理 业务处理 云备份项目中 &#xff0c;业务处理模块是针对客户端的业务请求进行处理&#xff0c;并最终给与响应。而整个过程中包含以下要实现的功能&#xff1a; 借助网络通信模块httplib库搭建http服务器与客户端进行网络通信针对收到的请求进行对应的业…

算法导论笔记4:散列数 hash

一 了解一些散列的基本概念&#xff0c;仅从文字角度&#xff0c;整理了最基础的定义。 发现一本书&#xff0c;《算法图解》&#xff0c;微信读书APP可读&#xff0c;有图&#xff0c;并且是科普性质的读物&#xff0c;用的比喻很生活化&#xff0c;可以与《算法导论》合并起…

Xshell远程登录 Linux小键盘数字输入变成字母解决办法

Xshell的设置问题&#xff0c;依次查看&#xff1a;文件-->属性-->终端-->VT模式-->初始数字键盘模式更改为&#xff1a;设置普通&#xff08;s&#xff09;

vue-常用指令

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;Vue篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来vue篇专栏内容-常用指令 目录 常用指令 1、v-cloak 2、数据绑定指令 3、v-once 4、v-bind&#xff08;重点&a…

在线制作仿真病历证明软件,易语言实现病例报告生成器,取画板快照+标签+编辑框

闲着无聊用易语言开发了一个病例生成器&#xff0c;当然我加了水印的&#xff0c;这个图片你就算截图你也用不了&#xff0c;模板是从百度图库搜的&#xff0c;很多&#xff0c;我就随便找了一个&#xff0c;然后实现逻辑就是加了一个画板&#xff0c;然后载入了素材图&#xf…

2023-11-12 LeetCode每日一题(Range 模块)

2023-03-29每日一题 一、题目编号 715. Range 模块二、题目链接 点击跳转到题目位置 三、题目描述 Range模块是跟踪数字范围的模块。设计一个数据结构来跟踪表示为 半开区间 的范围并查询它们。 半开区间 [left, right) 表示所有 left < x < right 的实数 x 。 实…

采用示波器显示扭矩传感器模拟信号

扭矩传感器输出的信号波形通常是模拟电压信号&#xff0c;可以通过示波器等仪器进行分析。扭矩传感器的输出信号波形通常有两种类型&#xff1a;正弦波和方波。 应变片传感器扭矩测量采用应变电测技术。在弹性轴上粘贴应变计组成测量电桥&#xff0c;当弹性轴受扭矩产生微小变…

【CASS精品教程】cass3d 11.0加载超大影像、三维模型、点云数据

CAD2016+CASS11.0(内置3d)下载与安装: 【CASS精品教程】CAD2016+CASS11.0安装教程(附CASS11.0安装包下载)https://geostorm.blog.csdn.net/article/details/132392530 一、cass11.0 3d支持的数据 cass11.0中的3d模块增加了多种数据的支持,主要有: 1. 三维模型 点击…

Linux学习教程(第二章 Linux系统安装)3

第二章 Linux系统安装 十一、Linux远程管理协议&#xff08;RFB、RDP、Telnet和SSH&#xff09; 提到远程管理&#xff0c;通常指的是远程管理服务器&#xff0c;而非个人计算机。个人计算机可以随时拿来用&#xff0c;服务器通常放置在机房中&#xff0c;用户无法直接接触到…

画面精美传奇手游幽冥传奇【幽冥灭龙传奇】win服务端+双端+GM授权后台+详细教程

搭建资源下载地址&#xff1a;画面精美传奇手游幽冥传奇幽冥灭龙传奇win服务端双端GM授权后台详细教程-海盗空间

Xilinx FPGA平台DDR3设计详解(一):DDR SDRAM系统框架

DDR SDRAM&#xff08;双倍速率同步动态随机存储器&#xff09;是一种内存技术&#xff0c;它可以在时钟信号的上升沿和下降沿都传输数据&#xff0c;从而提高数据传输的速率。DDR SDRAM已经发展了多代&#xff0c;包括DDR、DDR2、DDR3、DDR4和DDR5&#xff0c;每一代都有不同的…

GoF之工厂模式

Spring GoF之工厂模式工厂模式的三种形态简单工厂模式简单工厂模式优缺点 工厂方法模式工厂方法模式的优缺点 GoF之工厂模式 ● 设计模式&#xff1a;一种可以被重复利用的解决方案。 GoF有23种设计模式&#xff0c;还有其它的设计模式&#xff0c;比如&#xff1a;JavaEE的设…

使用 huggingface_hub 镜像下载 大模型

download.py &#x1f447; import os # 配置 hf镜像 os.environ[HF_ENDPOINT] https://hf-mirror.com# 设置保存的路径 local_dir "XXXXXX"# 设置仓库id model_id "sensenova/piccolo-large-zh"cmd f"huggingface-cli download --resume-downlo…

K8S知识点(六)

&#xff08;1&#xff09;资源管理方式1 其他参数 其他参数以json格式显示pod信息 以yaml显示pod信息&#xff1a; 用describe描述容器的详细信息&#xff1a;包括ip啊&#xff0c;镜像啊&#xff0c;端口啊&#xff0c;容器启动经历的历程 创建命名空间Pod&#xff1a; 查询…