小白也能看懂的 AUC 详解
简介
上篇文章 小白也能看懂的 ROC 曲线详解 介绍了 ROC 曲线。本文介绍 AUC。AUC 的全名为Area Under the ROC Curve,即 ROC 曲线下的面积,最大为 1。
根据 ROC 和 AUC 的关系,我们可以得到如下结论
- ROC 曲线接近左上角 ---> AUC 接近 1:模型预测准确率很高
- ROC 曲线略高于基准线 ---> AUC 略大于 0.5:模型预测准确率一般
- ROC 低于基准线 ---> AUC 小于 0.5:模型未达到最低标准,无法使用
二分类 AUC
由 AUC 名称可知,可以先计算 ROC 曲线,得到 TPR 和 FPR 的坐标后再分段计算面积即可得到 AUC
下面是对应的 Python 代码
def auc_from_roc(fpr, tpr): """ 计算ROC面积 fpr: 从小到大排序的fpr坐标 tpr: 从小到大排序的tpr坐标 """ area = 0 for i in range(len(fpr) - 1): area += trapezoid_area(fpr[i], fpr[i + 1], tpr[i], tpr[i + 1]) return area def trapezoid_area(x1, x2, y1, y2): """ 计算梯形面积 x1, x2: 横坐标 (x1 <= x2) y1, y2: 纵坐标 (y1 <= y2) """ base = x2 - x1 height_avg = (y1 + y2) / 2 return base * height_avg
也可以直接从真实标签和模型预测分数中计算 ROC,算法的时间复杂度为\(O(n\log n)\),参考文献 1 中的算法 2
# import numpy as np def auc_binary(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, tp_prev, fp_prev, area = 0, 0, 0, 0, 0 score = -np.inf for i in np.flip(np.argsort(y_score)): if y_score[i] != score: area += trapezoid_area(fp_prev, fp, tp_prev, tp) score = y_score[i] fp_prev = fp tp_prev = tp if y_true[i] == pos_label: tp += 1 else: fp += 1 area += trapezoid_area(fp_prev, fp, tp_prev, tp) area /= num_positive_examples * num_negtive_examples return area
多分类 AUC
现在考虑多分类的情况,假设类别数为\(C\)。
一种想法是将某一类别设为正样本类别,其余类别设为负样本类别,然后计算二分类下的 AUC。这种方法叫做一对多,即 One-Vs-Rest (OVR)。可以得到\(C\)个二分类的 AUC,然后计算平均数得到多分类的 AUC。
另一种想法是将某一类别设为正样本类别,另外一个类别(非自身)设为负样本类别计算二分类的 AUC。这种方法叫做一对一,即 One-Vs-One (OVO)。可以得到\(C(C-1)\)个二分类的 AUC,然后计算平均数。
当计算平均数时,可以考虑算数平均数(称为 macro),或者加权平均数(称为 weighted)。其中,加权为各类别的样本所占比例。因此,两两组合可以的得到四种计算多分类 AUC 的方法。值得一提的是,知名机器学习库 scikit-learn 的 roc_auc_score 函数 包含了上述四种方法。
- 一对多 + 算数平均数(OVR + macro)
- 一对多 + 加权平均数(OVR + weighted)
- 一对一 + 算数平均数(OVO + macro)
- 一对一 + 加权平均数(OVO + weighted)
一对多 + 算数平均数
多分类 AUC 的计算公式为
\(\text{AUC}_\text{total}=\frac{1}{C}\sum_{c_i\in C}\text{AUC}(c_i)\)
其中\(\text{AUC}(c_i)\)是将类别\(c_i\)作为正样本类别(剩余作为负样本类别),计算的二分类 AUC。
# sklearn.metrics.roc_auc_score(y_true, y_score, average='macro', multi_class='ovr') def auc_ovr_macro(y_true, y_score): auc = 0 C = max(y_true) + 1 for i in range(C): auc += auc_binary(y_true, y_score[:, i], pos_label=i) return auc / C
一对多 + 加权平均数
多分类 AUC 的计算公式为
\(\text{AUC}_\text{total}=\sum_{c_i\in C}\text{AUC}(c_i)p(c_i)\)
其中,权重\(p(c_i)=\frac{\sum\mathbb{I}\{y=c_i\}}{n}\),即标签为\(c_i\)的样本所占比例,权重之和为 1。
# sklearn.metrics.roc_auc_score(y_true, y_score, average='weighted', multi_class='ovr') def auc_ovr_weighted(y_true, y_score): auc = 0 C = max(y_true) + 1 n = len(y_true) for i in range(C): p = sum(y_true == i) / n auc += auc_binary(y_true, y_score[:, i], pos_label=i) * p return auc
一对一 + 算数平均数
多分类 AUC 的计算公式为
\(\text{AUC}_\text{total}=\frac{2}{C(C-1)}\sum_{{c_i,c_j}\in C,\ c_i<c_j}\text{AUC}(c_i,c_j)\)
其中,\(\text{AUC}(c_i,c_j)=\frac{\text{AUC}(c_i|c_j)+\text{AUC}(c_j|c_i )}{2}\)。即将\(c_i\)作为正样本类别、\(c_j\)作为负样本类别计算二分类 \(\text{AUC}(c_i|c_j)\);然后将\(c_j\)作为正样本类别、\(c_i\)作为负样本类别计算二分类 \(\text{AUC}(c_j|c_i)\)。\(\text{AUC}(c_i,c_j)\) 为其计算的算数平均值。由于将\(c_i\)和\(c_j\)组合计算,共得到 \(C(C-1)/2\) 个二分类 AUC。
# sklearn.metrics.roc_auc_score(y_true, y_score, average='macro', multi_class='ovo') def auc_ovo_macro(y_true, y_score): auc = 0 C = max(y_true) + 1 for i in range(C - 1): i_index = np.where(y_true == i)[0] for j in range(i + 1, C): j_index = np.where(y_true == j)[0] index = np.concatenate((i_index, j_index)) auc_i_j = auc_binary(y_true[index], y_score[index, i], pos_label=i) auc_j_i = auc_binary(y_true[index], y_score[index, j], pos_label=j) auc += (auc_i_j + auc_j_i) / 2 return auc * 2 / (C * (C - 1))
一对一 + 加权平均数
多分类 AUC 的计算公式为
\(\text{AUC}_\text{total}=\sum_{{c_i,c_j}\in C,\ c_i<c_j}\text{AUC}(c_i,c_j)p(c_i,c_j)\)
其中,权重\(p(c_i,c_j)=\frac{\sum\mathbb{I}\{y=c_i\}+\sum\mathbb{I}\{y=c_j\}}{(C-1)n}\),即标签为\(c_i\)和\(c_j\)的样本所占比例,分母中的系数 \(C-1\) 使得权重之和为 1。
# sklearn.metrics.roc_auc_score(y_true, y_score, average='weighted', multi_class='ovo') def auc_ovo_weighted(y_true, y_score): auc = 0 C = max(y_true) + 1 n = len(y_true) for i in range(C - 1): i_index = np.where(y_true == i)[0] for j in range(i + 1, C): j_index = np.where(y_true == j)[0] index = np.concatenate((i_index, j_index)) p = len(index) / n / (C - 1) auc_i_j = auc_binary(y_true[index], y_score[index, i], pos_label=i) auc_j_i = auc_binary(y_true[index], y_score[index, j], pos_label=j) auc += (auc_i_j + auc_j_i) / 2 * p return auc
参考文献
- Fawcett, Tom. "An introduction to ROC analysis." <em>Pattern recognition letters</em> 27, no. 8 (2006): 861-874. https://www.researchgate.net/profile/Tom-Fawcett/publication/222511520_Introduction_to_ROC_analysis/links/5ac7844ca6fdcc8bfc7fa47e/Introduction-to-ROC-analysis.pdf
- Hand, David J., and Robert J. Till. "A simple generalisation of the area under the ROC curve for multiple class classification problems." <em>Machine learning</em> 45 (2001): 171-186. https://link.springer.com/content/pdf/10.1023/A:1010920819831.pdf
作者:PrimiHub-Kevin

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
开源 2 年、打磨 13 年、300 万行代码的开源项目
从刻在兽骨上的甲骨文,再到写在纸上的汉字,每一次信息载体的变更都是文化进步的重要标志。在如今这个信息数字化的时代,我们在享受着数字化便利的同时,数据也在我们看不见的地方飞速增长着,数据的重要性不言而喻。那应该如何将海量数据完整、有序、持久化地保存下来呢? 程序员小伙伴看到这里应该猜到了我们的今天的主角,没错就是「数据库」。 一、分布式数据库 程序员熟知的单体数据库如 MySQL、Oracle 在二十世纪末诞生并大行其道,直到 2010 年左右移动互联网爆发,席卷而来的海量数据,让单体数据库面临了前所未有的挑战,这也让数据库迎来了百花齐放的时代。 为了解决海量数据存储的问题,程序员们做了诸多尝试,比如堆机器配置、利用分库分表配合中间件实现分布式架构等,却发现治标不治本、换汤不换药,只是延长了问题出现的周期,还引发了维护成本高、上手难度大等问题。因此,新型分布式数据库应运而生,它基于分布式原理把数据处理和存储分到多台普通机器上处理,从根本上解决了单体数据库存储海量数据的瓶颈和性能问题,并优化了传统分布式数据库的数据一致性问题。 分布式数据库虽然能解决数据量瓶颈的问题,但换数据库是个类似动心...
- 下一篇
优雅设计之美:实现Vue应用程序的时尚布局
前言 页面布局是减少代码重复和创建可维护且具有专业外观的应用程序的基本模式。如果使用的是Nuxt,则可以提供开箱即用的优雅解决方案。然而,令人遗憾的是,在Vue中,这些问题并未得到官方文档的解决。 经过多次尝试,小编得出了一个运行良好且可扩展而不会令人头疼的架构的模式。下面用一个简单的例子为大家介绍一下。 设置需求 布局架构需要满足的需求: 页面应声明每个部分的布局和组件。 对页面的更改不应影响其他页面。 如果布局的某些部分在页面中是通用的,则只应声明一次。 设置Vue路由 小编需要在页面之间导航,这就是小编要设置 vue-router 的原因。 Vue-cli 脚手架vite提供了在创建新项目时包含它的选项,但如果您没有用脚手架创建项目,可以通过下面的方式设置路由。 1. 安装 vue-router 依赖项 npm i -D vue-router@4 2. 声明路由 const routes = [ { path: "/", component: () => import("./pages/HomePage.vue") }, { path: "/explore", compon...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- SpringBoot2整合Redis,开启缓存,提高访问速度
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- MySQL8.0.19开启GTID主从同步CentOS8
- Mario游戏-低调大师作品
- Linux系统CentOS6、CentOS7手动修改IP地址
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- Docker安装Oracle12C,快速搭建Oracle学习环境
- CentOS7安装Docker,走上虚拟化容器引擎之路