@作者: 机器学习算法 @迪吉老农
AUC这个指标在排序问题里经常用到,之前也有个模糊的印象,就是一个排序正确的比例。
这个模糊印象是,
- 分母是选两个例子的的方式数
- 分子是这两个例子的预测顺序正确的次数
但是今天看了一个python的实现,发现不是很能理解里面的公式,于是赶紧查了一下维基百科的定义,
the probability that a classifier will rank a randomly chosen positive instance higher than a randomly chosen negative one (assuming 'positive' ranks higher than 'negative').
上面的意思是,
- 分母是分别选一个正例,一个负例的方式数
- 分子是这两个例子的预测顺序正确的次数
也就是去掉两个负例或者两个正例,这两种情况。想来也是,这种数据属于不知道是对还是错,无法标定,不应该放到准确率中计算。
于是自己试着用一个例子来辅助推导一下公式,如下表所示,是现实的正负例,是模型给出的预测的分数,
index | ||
---|---|---|
0 | 1 | 0.9 |
1 | 0 | 0.5 |
2 | 1 | 0.8 |
3 | 0 | 0.7 |
4 | 1 | 0.6 |
我们需要计算
其中的和是随机的一对正负例和的预测值。
按照定义,分母就是从正例选一个,从负例选一个的方式数,
分子要看预测的分数,一个直接的想法是去生成一个矩阵,比较预测分数,正例和负例谁大,如下面的表格,
正例 | 1 | 3 |
---|---|---|
0 | 1(.9>.5 ) |
1(.9>.7 ) |
2 | 1(.8>.5 ) |
1(.8>.7 ) |
4 | 1(.6>.5 ) |
0(.6<.7 ) |
然后去计算矩阵的sum
就是正确排序数
但是这个计算方式有性能问题,类似于冒泡排序的计算量;高效一点的实现就是先全排序,复杂度是,生成一个下面的表中rank值,表明每个值排在第几个位置,
index | tied_rank | ||
---|---|---|---|
0 | 1 | 0.9 | 5 |
1 | 0 | 0.5 | 1 |
2 | 1 | 0.8 | 4 |
3 | 0 | 0.7 | 3 |
4 | 1 | 0.6 | 2 |
注释,这里的tied_rank是指,分数一样的话,几个平分一个rank,比如,
>>> tied_rank([1.0, 0.1, 0.8, 0.7, 0.6])
[5.0, 1.0, 4.0, 3.0, 2.0]
>>> tied_rank([1.0, 0.1, 0.7, 0.7, 0.6])
[5.0, 1.0, 3.5, 3.5, 2.0]
继续说回来,如果一个正例在整体中从低分到高分,排在第个,那么他比个数大。不过,里面既有正例也有负例,我们必须知道里面的正例/负例数才行。所以还需要一个只保留正例的计算,如下表。假设他在正例中排第,在全体中排第,那么他比个负例大,也就是我们在分子中,要进行求和的对象。
index | tied_rank | pos_rank | ||
---|---|---|---|---|
0 | 1 | 0.9 | 5 | 3 |
2 | 1 | 0.8 | 4 | 2 |
4 | 1 | 0.6 | 2 | 1 |
所以,分子的计算可以写成,
上面的公式又可以化简,这是因为其实是是固定的值,只和正例的数目有关系,
所以最终的公式为
最后,贴一下网上开源的代码benhamner/Metrics,里面就是这个计算公式。
def auc(actual, posterior):
"""
Computes the area under the receiver-operater characteristic (AUC)
This function computes the AUC error metric for binary classification.
Parameters
----------
actual : list of binary numbers, numpy array
The ground truth value
posterior : same type as actual
Defines a ranking on the binary numbers, from most likely to
be positive to least likely to be positive.
Returns
-------
score : double
The mean squared error between actual and posterior
"""
r = tied_rank(posterior)
num_positive = len([0 for x in actual if x==1])
num_negative = len(actual)-num_positive
sum_positive = sum([r[i] for i in range(len(r)) if actual[i]==1])
auc = ((sum_positive - num_positive*(num_positive+1)/2.0) /
(num_negative*num_positive))
return auc
版权声明
以上文章为本人@迪吉老农原创,首发于简书,文责自负。文中如有引用他人内容的部分(包括文字或图片),均已明文指出,或做出明确的引用标记。如需转载,请联系作者,并取得作者的明示同意。感谢。