如何理解机器学习和统计中的AUC?

最近对机器学习有点兴趣,想了解下
关注者
1,878
被浏览
1,169,987

70 个回答

之前各位的回答从各个角度解释了AUC的意义和计算方法,但是由于本人实在愚钝,一直没能参透AUC的意义和计算方法之间的联系,直到刚才突然有所顿悟,本着尽量言简意赅、浅显易懂的原则,在这里记录一下。

首先,在试图弄懂AUC和ROC曲线之前,一定,一定要彻底理解混淆矩阵的定义!!!

混淆矩阵中有着Positive、Negative、True、False的概念,其意义如下:

  • 称预测类别为1的为Positive(阳性),预测类别为0的为Negative(阴性)。
  • 预测正确的为True(真),预测错误的为False(伪)。

对上述概念进行组合,就产生了如下的混淆矩阵:

然后,由此引出True Positive Rate(真阳率)、False Positive(伪阳率)两个概念:

  • TPRate=\frac{TP}{TP+FN}
  • FPRate=\frac{FP}{FP+TN}

仔细看这两个公式,发现其实TPRate就是TP除以TP所在的列,FPRate就是FP除以FP所在的列,二者意义如下:

  • TPRate的意义是所有真实类别为1的样本中,预测类别为1的比例。
  • FPRate的意义是所有真实类别为0的样本中,预测类别为1的比例。

如果上述概念都弄懂了,那么ROC曲线和AUC就so easy了:

按照定义,AUC即ROC曲线下的面积,而ROC曲线的横轴是FPRate,纵轴是TPRate,当二者相等时,即y=x,如下图:

表示的意义是:对于不论真实类别是1还是0的样本,分类器预测为1的概率是相等的。

换句话说,分类器对于正例和负例毫无区分能力,和抛硬币没什么区别,一个抛硬币的分类器是我们能想象的最差的情况,因此一般来说我们认为AUC的最小值为0.5(当然也存在预测相反这种极端的情况,AUC小于0.5,这种情况相当于分类器总是把对的说成错的,错的认为是对的,那么只要把预测类别取反,便得到了一个AUC大于0.5的分类器)。

而我们希望分类器达到的效果是:对于真实类别为1的样本,分类器预测为1的概率(即TPRate),要大于真实类别为0而预测类别为1的概率(即FPRate),即y>x,因此大部分的ROC曲线长成下面这个样子:

最理想的情况下,既没有真实类别为1而错分为0的样本——TPRate一直为1,也没有真实类别为0而错分为1的样本——FP rate一直为0,AUC为1,这便是AUC的极大值。


说了这么多还是不够直观,不妨举个简单的例子。

首先对于硬分类器(例如SVM,NB),预测类别为离散标签,对于8个样本的预测情况如下:

得到混淆矩阵如下:

进而算得TPRate=3/4,FPRate=2/4,得到ROC曲线:

最终得到AUC为0.625。

对于LR等预测类别为概率的分类器,依然用上述例子,假设预测结果如下:

这时,需要设置阈值来得到混淆矩阵,不同的阈值会影响得到的TPRate,FPRate,如果阈值取0.5,小于0.5的为0,否则为1,那么我们就得到了与之前一样的混淆矩阵。其他的阈值就不再啰嗦了。依次使用所有预测值作为阈值,得到一系列TPRate,FPRate,描点,求面积,即可得到AUC。


最后说说AUC的优势,AUC的计算方法同时考虑了分类器对于正例和负例的分类能力,在样本不平衡的情况下,依然能够对分类器作出合理的评价。

例如在反欺诈场景,设欺诈类样本为正例,正例占比很少(假设0.1%),如果使用准确率评估,把所有的样本预测为负例,便可以获得99.9%的准确率

但是如果使用AUC,把所有样本预测为负例,TPRate和FPRate同时为0(没有Positive),与(0,0) (1,1)连接,得出AUC仅为0.5,成功规避了样本不均匀带来的问题。

水平有限,欢迎拍砖~

分三部分,第一部分是对AUC的基本介绍,包括AUC的定义,解释,以及算法和代码,第二部分用逻辑回归作为例子来说明如何通过直接优化AUC来训练,第三部分,内容完全由@李大猫原创——如何根据auc值来计算真正的类别,换句话说,就是对auc的反向工程。

1. 什么是AUC?

AUC是一个模型评价指标,只能用于二分类模型的评价,对于二分类模型,还有很多其他评价指标,比如logloss,accuracy,precision。如果你经常关注数据挖掘比赛,比如kaggle,那你会发现AUC和logloss基本是最常见的模型评价指标。为什么AUC和logloss比accuracy更常用呢?因为很多机器学习的模型对分类问题的预测结果都是概率,如果要计算accuracy,需要先把概率转化成类别,这就需要手动设置一个阈值,如果对一个样本的预测概率高于这个预测,就把这个样本放进一个类别里面,低于这个阈值,放进另一个类别里面。所以这个阈值很大程度上影响了accuracy的计算。使用AUC或者logloss可以避免把预测概率转换成类别。

AUC是Area under curve的首字母缩写。Area under curve是什么呢,从字面理解,就是一条曲线下面区域的面积。所以我们要先来弄清楚这条曲线是什么。这个曲线有个名字,叫ROC曲线。ROC曲线是统计里面的概率,最早由电子工程师在二战中提出来(更多关于ROC的资料可以参考维基百科)。

ROC曲线是基于样本的真实类别和预测概率来画的,具体来说,ROC曲线的x轴是伪阳性率(false positive rate),y轴是真阳性率(true positive rate)。那么问题来了,什么是真、伪阳性率呢?对于二分类问题,一个样本的类别只有两种,我们用0,1分别表示两种类别,0和1也可以分别叫做阴性和阳性。当我们用一个分类器进行概率的预测的时候,对于真实为0的样本,我们可能预测其为0或1,同样对于真实为1的样本,我们也可能预测其为0或1,这样就有四种可能性:

真阳性率=(真阳性的数量)/(真阳性的数量+伪阴性的数量)

伪阳性率=(伪阳性的数量)/(伪阳性的数量+真阴性的数量)

有了上面两个公式,我们就可以计算真、伪阳性率了。但是如何根据预测的概率得到真伪阳性、阴性的数量。

我们来看一个具体例子,比如有5个样本:

真实的类别(标签)是y=c(1,1,0,0,1)

一个分类器预测样本为1的概率是p=c(0.5,0.6,0.55,0.4,0.7)

如文章一开始所说,我们需要选定阈值才能把概率转化为类别,选定不同的阈值会得到不同的结果。如果我们选定的阈值为0.1,那5个样本被分进1的类别,如果选定0.3,结果仍然一样。如果选了0.45作为阈值,那么只有样本4被分进0,其余都进入1类。一旦得到了类别,我们就可以计算相应的真、伪阳性率,当我们把所有计算得到的不同真、伪阳性率连起来,就画出了ROC曲线,我们不需要手动做这个,因为很多程序包可以自动计算真、伪阳性率,比如在R里面,下面的代码可以计算以上例子的真、伪阳性率并且画出ROC曲线:

library(ROCR)
p=c(0.5,0.6,0.55,0.4,0.7)
y=c(1,1,0,0,1)
pred = prediction(p, y)
perf = performance(pred,"tpr","fpr")
plot(perf,col="blue",lty=3, lwd=3,cex.lab=1.5, cex.axis=2, cex.main=1.5,main="ROC plot")




上面代码可以画出下图:

得到了ROC曲线,那么曲线下面的面积也就可以算出来了,同样,我们可以通过程序得到面积:

auc = performance(pred,"auc")
auc = unlist(slot(auc, "y.values"))

这个ROC的面积也就是我们要计算的AUC值。

通过AUC的定义我们知道了AUC是什么,怎么算,但是它的意义是什么呢。如果从定义来理解AUC的含义,比较困难,实际上AUC和Mann–Whitney U test有密切的联系,我会在第三部说明。从Mann–Whitney U statistic的角度来解释,AUC就是从所有1样本中随机选取一个样本, 从所有0样本中随机选取一个样本,然后根据你的分类器对两个随机样本进行预测,把1样本预测为1的概率为p1,把0样本预测为1的概率为p0,p1>p0的概率就等于AUC。所以AUC反应的是分类器对样本的排序能力。根据这个解释,如果我们完全随机的对样本分类,那么AUC应该接近0.5。另外值得注意的是,AUC对样本类别是否均衡并不敏感,这也是不均衡样本通常用AUC评价分类器性能的一个原因。

有朋友用python,下面代码用于在python里面计算auc:

from sklearn import metrics
def aucfun(act,pred):
  fpr, tpr, thresholds = metrics.roc_curve(act, pred, pos_label=1)
  return metrics.auc(fpr, tpr)



2. 可以直接优化AUC来训练分类器吗?

答案是肯定的,我在这里给出一个简单的例子,通过直接优化AUC来训练一个逻辑回归模型。大家应该知道逻辑回归通常是通过极大似然估计来训练的,也就是最大化极大似然函数,这是统计里的概念,在机器学习里,对应logloss函数。我们通过下面的例子来训练一个逻辑回归是的AUC最大化:

#生成训练数据
set.seed(1999)
x1 = rnorm(1000)          
x2 = rnorm(1000)
z = 1 + 2*x1 + 3*x2       
pr = 1/(1+exp(-z))        
y = rbinom(1000,1,pr)     

#使用logloss作为训练目标函数
df = data.frame(y=y,x1=x1,x2=x2)
glm.fit=glm( y~x1+x2,data=df,family="binomial")

#下面使用auc作为训练目标函数
library(ROCR)

CalAUC <- function(real,pred){
  rocr.pred=prediction(pred,real)
  rocr.perf=performance(rocr.pred,'auc')
  as.numeric(rocr.perf@y.values)
}
#初始值
beta0=c(1,1,1)

loss <- function(beta){
  z=beta[1]+beta[2]*x1+beta[3]*x2
  pred=1/(1+exp(-z))
  -CalAUC(y,pred)
}

res=optim(beta0,loss,method = "Nelder-Mead",control = list(maxit = 100))
cat("直接用AUC训练:",-res$value)
cat("使用glm函数",CalAUC(y,glm.fit$fitted.values))

程序输出结果:

直接用AUC训练: 0.9339833

使用glm函数 0.9338208

可见,通过直接优化AUC,我们得到的AUC比直接优化logloss稍大一点点点点。

根据@西方不得不败 提示,glmnet包自带根据AUC来训练逻辑回归的功能,这里就不展开说啦。

理论上讲,直接优化AUC在一定条件下就是rankboost算法(感兴趣可以参见

paper

)。

对于最近十分火热的xgboost包,也提供了直接优化AUC的功能,使用起来很简单,只要把目标函数设置为:

objective = 'rank:pairwise'

3. 从AUC到真实类别(label)?

最开始思考这个问题是做一个网上的比赛,二分类问题,每次提交一组预测系统会计算出一个AUC值,因为这个比赛只有5000样本,并且系统显示的AUC值有小数点后面7、8位,所以我想是否可以通过可能通过AUC值计算出样本的真实label来。也许并没有实际价值,但是问题本身是很有趣的,像是在破解密码。

一个naive但是可行但是效率很低的办法, 就是每次生成一组预测值,里面只有一个样本预测为1,其余都是0,然后提交预测计算AUC,然后根据这个AUC来判断此样本的label,但是这样效率太低了,5000个样本,需要5000次提交。

思考了很久,最后发现可以通过AUC的另一个计算公式入手。也就是第一部分说的U statistic。

3.1 根据一个AUC值计算样本中0,1的数目

根据AUC与U statistic的联系,我们可以用下面的代码计算AUC:

auc=(sum(rank(c(p(label==1),p(label==0)))[1:n1])-n1*(n1+1)/2)/n0/n1

上面label表示样本的真实类别,p表示预测为1的概率,rank是R的内置函数,n0表示真实数据中0的数目,n1表示1的数目,n0+n1=n表示数据所有样本数目,根据这个简单的一行代码,我们可以不用任何包来计算AUC。

上面公式很有趣,n0,n1还有label都是固定的,p不同导致auc不一致,观察sum里面,可以发现这个sum本质是什么?就是计算pred里面对应的真实label为1的那些预测值的rank的和。

继续使用第一部分的例子,5个样本的预测值的rank:

rank(c(0.5,0.6,0.55,0.4,0.7))
[1] 2 4 3 1 5




其中真实为1的样本(1,2,5)的对应rank是2,4,5,这三个rank值的和2+4+5=11,n0=2,n1=3,于是根据上面的AUC公式:(11-3*(3+1)/2)/2/3=5/6=0.83333,这个结果与我们在第一部分用AUC定义算的值完全一样。

所以说,真正影响auc的,就是预测值里面对本来是1的样本的预测的rank的和。

要破解真实label,第一部要做的是找到样本里面0和1的数目,也就是n0和n1等于多少。这个问题不复杂。要知道相同预测值的rank也一致,就是说如果所有样本的预测只取0或者1,那对应的rank只有两个unique数值。

再观察AUC公式里面的sum:

sum(rank(c(pred(label==1),pred(label==0)))[1:n1])

这个sum是n1个数值的和,这n1个数值,当我们的pred只有两个不同取值的时候,仅包括两个unique的数值。继续用上面例子,一共有5个样本,我们生成第一组预测p如下:

> p=c(1,1,1,0,0)
> rank(p)
[1] 4.0 4.0 4.0 1.5 1.5

可见p的rank只有两个不同取值,1.5和4,这是因为预测概率p也只有两个不同取值。

然后我们还知道sum是n1个数的sum,我们不知道的是这n1个数,里面有几个1.5,几个4,假设其中有k1个1.5,k2个4,可以列出一个方程:

k1+k2=n1

k1*1.5+k2*4=sum(rank(c(p(label==1),p(label==0)))[1:n1])=auc*n0*n1+n1*(1+n1)/2

所以最终得到下面的方程组:

k1+k2=n1

k1*1.5+k2*4=0.833333*n0*n1+n1*(1+n1)/2

n0+n1=5

其中k1,k2和n1未知,两个方程,3个未知数,属于不定方程组,但是要知道k1,k2,和n1都是整数,并且有取值范围,要借出来很简单,写一个for 循环,1秒中就可以找到一组满足3个方程多k1,k2以及n1。

如果我们变更p,比如p=c(rep(1,m),rep(0,5-m)),通过一样的算法,可以计算出来前m个样本中1的数量。

通过这个算法,我可以算出来

这个贷款预测比赛

测试数据中有509个1和4491个0。

做到这里,差点就放弃了,但是后来突然又有了灵感,找到了下面的算法:

3.2 根据AUC破解样本的真实label

这里就省略思考过程了, 直接来破解算法:

对于一组总数为n的测试样本,我们先来计算

m=floor(log(n,base=2))+1

这个m表示我们通过两次auc计算可以计算出至少多少个样本的真实label,比如n=5000,那么m=13

也就是说通过我们两次提交,可以最少得到13个样本的label。这13个样本是可以自己随便指定的,比如我们对前13个样本感兴趣,那么具体做法如下:

fix1=2^c(0:12)
fix2=c(fix1[-1],fix1[1])
unfixed=setdiff(1:5000,fix1)
p1=c(fix1,unfixed)#第1组预测
p2=c(fix2,unfixed)#第2组预测






使用上面的两组预测p1和p2分别计算AUC,得到auc1和auc2,根据上面给出的auc算法:

sum(rank(c(p1(label==1),p1(label==0)))[1:n1])-n1*(1+n1)/2=auc1*n0*n1
sum(rank(c(p2(label==1),p2(label==0)))[1:n1])-n1*(1+n1)/2=auc2*n0*n1

两个公式相减:

sum(rank(c(p1(label==1),p1(label==0)))[1:n1])-sum(rank(c(p2(label==1),p2(label==0)))[1:n1])-n1*(1+n1)/2=(auc1-auc2)*n0*n1

得到的这个等式里,我们已经通过上面的方法找到了n0和n1,auc1和auc2是已知,所以等式右面值可以算出,那么等式左面呢,因为我们两个预测结果p1和p2只有前三个点的预测之不一样,其余点的预测值一样,rank也一样,那么等式左面的两个sum的差,其实只由前13个样本的真实label决定,具体来说:

sum1-sum2=y1*(fix1[1]-fix2[1])+y2*(fix1[2]-fix2[2])+...+y13*(fix1[13]-fix2[13])
=y1*(-1)+y2*(-2)+...+y12*(-2048)+y13*(4095)

上面的方程里面yi代表样本i的真实label,有且只有唯一解,以为这个方程本质上就是10进制数用2进制表达。所以通过两次auc计算,我们可以找到13个点的真实标签。比如对上面提到的贷款预测比赛,选定前13个label,auc1=0.50220853,auc2= 0.5017588,然后就可以算出来前13个test样本只有第三个样本是0,其余都是1。

但是13并不是上限,我有一些更好的结果,比较复杂,在这就不展开说了。

###########################################################

终于写完回答了,欢迎大家关注 微信公众号:机器会学习,搜索id:jiqihuixuexi,更多精彩原创文章每日更新!