AUC(Area under curve)是機(jī)器學(xué)習(xí)常用的二分類(lèi)評(píng)測(cè)手段,直接含義是ROC曲線(xiàn)下的面積。
ROC曲線(xiàn)理解起來(lái)更加的復(fù)雜且計(jì)算工程更加麻煩,因此從計(jì)算概率的角度理解AUC
也可以理解為:隨機(jī)抽出一對(duì)樣本(一個(gè)正樣本,一個(gè)負(fù)樣本),然后用訓(xùn)練得到的分類(lèi)器來(lái)對(duì)這兩個(gè)樣本進(jìn)行預(yù)測(cè),預(yù)測(cè)得到正樣本的概率大于負(fù)樣本概率的概率。
那如何實(shí)現(xiàn)這樣的計(jì)算呢?
我們從字面上的意思開(kāi)始理解
一對(duì)樣本,根據(jù)模型預(yù)測(cè)的結(jié)果,預(yù)測(cè)得到正樣本的概率大于負(fù)樣本概率的概率。
因此計(jì)算方法即為如下:
在有M個(gè)正樣本,N個(gè)負(fù)樣本的數(shù)據(jù)集里。
一共有 MxN 對(duì)樣本(一對(duì)樣本即,一個(gè)正樣本與一個(gè)負(fù)樣本)。
統(tǒng)計(jì)這 MxN 對(duì)樣本里,正樣本的預(yù)測(cè)概率大于負(fù)樣本的預(yù)測(cè)概率的個(gè)數(shù)。

舉個(gè)例子:
| class | label | pre |
|---|---|---|
| A | 0 | 0.1 |
| B | 0 | 0.4 |
| C | 1 | 0.3 |
| D | 1 | 0.8 |
假設(shè)有4條樣本。2個(gè)正樣本,2個(gè)負(fù)樣本,那么MxN=4。
即總共有4個(gè)樣本對(duì)。分別是:
(C,A), (C,B), (D,A), (D,B)
在(C,B)樣本對(duì)中,正樣本C預(yù)測(cè)的概率小于負(fù)樣本B預(yù)測(cè)的概率(也就是C的得分比B低),記為0
在(D,B)樣本對(duì)中,正樣本D預(yù)測(cè)的概率大于負(fù)樣本B預(yù)測(cè)的概率(也就是D的得分比B高),記為1
所以最后的AUC結(jié)果即為:
(C,A), (C,B), (D,A), (D,B) =1+0+1+1
總樣本對(duì)為MxN=4
所以結(jié)果為:

如果遇見(jiàn)得分一樣的呢?
| class | label | pre |
|---|---|---|
| A | 0 | 0.1 |
| B | 0 | 0.4 |
| C | 1 | 0.4 |
| D | 1 | 0.8 |
同樣本是4個(gè)樣本對(duì),對(duì)于樣本對(duì)(C,B)其I值為0.5。

from sklearn.metrics import roc_auc_score
def calcAUC(labels, probs):
N = 0
P = 0
neg_prob = []
pos_prob = []
for _,i in enumerate(labels):
if (i == 1):
P += 1
pos_prob.append(probs[_])
else:
N += 1
neg_prob.append(probs[_])
number = 0
for pos in pos_prob:
for neg in neg_prob:
if (pos > neg):
number += 1
elif (pos == neg):
number += 0.5
return number / (N * P)
y = np.array([1,0,0,0,1,0,1,0,])
pred = np.array([0.9, 0.8, 0.3, 0.1,0.4,0.9,0.66,0.7])
print('auc=',calcAUC(y,pred))
print('roc_auc=',roc_auc_score(y,pred))
auc= 0.5666666666666667
roc_auc= 0.5666666666666667
上面的計(jì)算方法用了2個(gè)循環(huán),時(shí)間復(fù)雜度高,因此我們對(duì)其進(jìn)行如下優(yōu)化。
首先按照預(yù)測(cè)的概率從小到高排列:
| class | label | pre |
|---|---|---|
| A | 0 | 0.1 |
| C | 1 | 0.3 |
| B | 0 | 0.4 |
| D | 1 | 0.8 |
顯然比C小的就是A
比D小的就是A,B
C的位置是2 D的位置是4
2+4 =A+C+A+B+C+D -----------(1)
而我們的目標(biāo)是:
1+2=A+A+B -----------(2)
因此需要將(1)式減去 C+C+D
顯然就是將正樣本進(jìn)行排列然后用等差數(shù)列求和公式即:

所以最后的求解公式為:

如果出現(xiàn)預(yù)測(cè)相同的情況
| class | label | pre | rangk |
|---|---|---|---|
| A | 0 | 0.1 | 1 |
| B | 1 | 0.5 | 2 |
| C | 1 | 0.5 | 3 |
| D | 0 | 0.5 | 4 |
| E | 0 | 0.5 | 5 |
| F | 1 | 0.8 | 6 |
| G | 1 | 0.9 | 7 |
那就是正負(fù)樣本平分位置即可

用python實(shí)現(xiàn):
import numpy as np
from sklearn.metrics import roc_curve
from sklearn.metrics import auc
def auc_calculate(labels,preds,n_bins=100):
postive_len = sum(labels) #正樣本數(shù)量(因?yàn)檎龢颖径际?)
negative_len = len(labels) - postive_len #負(fù)樣本數(shù)量
total_case = postive_len * negative_len #正負(fù)樣本對(duì)
pos_histogram = [0 for _ in range(n_bins)]
neg_histogram = [0 for _ in range(n_bins)]
bin_width = 1.0 / n_bins
for i in range(len(labels)):
nth_bin = int(preds[i]/bin_width)
if labels[i]==1:
pos_histogram[nth_bin] += 1
else:
neg_histogram[nth_bin] += 1
accumulated_neg = 0
satisfied_pair = 0
for i in range(n_bins):
satisfied_pair += (pos_histogram[i]*accumulated_neg + pos_histogram[i]*neg_histogram[i]*0.5)
accumulated_neg += neg_histogram[i]
return satisfied_pair / float(total_case)
if __name__ == '__main__':
y = np.array([1,0,0,0,1,0,1,0,])
pred = np.array([0.9, 0.8, 0.3, 0.1,0.4,0.9,0.66,0.7])
fpr, tpr, thresholds = roc_curve(y, pred, pos_label=1)
print("sklearn:",auc(fpr, tpr))
print("驗(yàn)證:",auc_calculate(y,pred))
sklearn: 0.5666666666666667
驗(yàn)證: 0.5666666666666667
參考:
AUC計(jì)算的兩種實(shí)現(xiàn)方式以及python代碼
AUC詳解與python實(shí)現(xiàn)