如何計算AUC
前文《什麼是好的推薦,重新理解AUC》從什麼是好的推薦系統,引出了AUC的定義,並基於定義推匯出AUC的計算、AUC的優點(為什麼用AUC評估分類模型)、AUC的缺點,在文末留了幾個可以繼續討論的點,本文將繼續討論AUC,主要介紹AUC的具體計算方法,後續文章將會相繼分享過擬合,AUC離線和線上表現不一致等問題。
前文根據AUC的定義推匯出AUC的計算公式:
,其中
為正樣本數量,
為負樣本數量,
為正樣本預測得分,
為負樣本預測得分。
1 方法1
用指示函式表示上式中正樣本預測值大於負樣本預測值的正負樣本對,則得到
,其中
表示預測得分。
根據上式,AUC的計算的關鍵則是得到所有正負樣本對,以及得到每個正負樣本對的指示函式值。這是一個簡單的排列組合問題。
以一個例子進行計算說明。
樣本編號
真實分類
預測值
A
1
0。4
B
1
0。8
C
0
0。2
D
0
0。4
E
0
0。5
在給出的例子中,包含有2個正樣本(A, B)和3個負樣本(C, D, E),因此一共有6個(2*3)正負樣本對,即公式中分母為6。
接下來計算公式中的分子,即每個正負樣本對的指示函式值:
以A為正樣本形成的正負樣本對為(A, C), (A, D), (A, E),指示函式值分別為1,0。5,0;
以B為正樣本形成的正負樣本對為(B, C), (B, D), (B, E),指示函式值分別為1,1,1。
因此
這種方法計算AUC的時間複雜度為
,獲取正負樣本的數量時間複雜度為
,計算所有樣本對的指示函式值時間複雜度為
,故時間複雜度為
。
2 方法2
用方法1計算AUC易於理解,但需要對所有樣本對計算指示函式值,有沒有不需要對所有樣本對遍歷的方法?
AUC計算的關鍵是找到所有正樣本預測值大於負樣本預測值的正負樣本對。
如果引入排序,則大小關係就可以確定;
如果有正樣本的排序序號,則可以知道樣本對(當前樣本,<=當前正樣本預測值樣本) 的數量,這其中的樣本對包括正負樣本對,也包括正正樣本對,則減去正正樣本對就是AUC計算需要的正負樣本對。
根據以上兩點分析,則可以對所有樣本按照預測值從低到高排序。排序後可以得到每個正樣本的序號,用
表示第i個正樣本的序號,則樣本對(當前樣本,<=當前正樣本預測值的樣本) 的數量為
,其中正正樣本對有若干個,用
表示,
包括了和當前正樣本本身形成的正正樣本對。
則由當前正樣本形成的正負樣本對(正樣本預測值>負樣本預測值)的數量=
,對所有正樣本形成的正負樣本對(正樣本預測值>負樣本預測值)求和,即得到了AUC計算公式的分子,即
。
對每個正樣本而言,
值未知,而對所有正樣本的
求和,其值為
。具體計算如下:得分最高的正樣本,
是所有正樣本的數量,即
,得分第2高的正樣本,
,得分最低的正樣本 ,
,因此所有正樣本的
形成一個等差數列
,該等差數列求和值為
。
因此
。
這種方法計算AUC的時間複雜度為
,獲取正負樣本的數量時間複雜度為
,排序的時間複雜度
,故時間複雜度為
。
繼續以上面例子為例,按照預測得分從低到高排序
樣本編號
真實分類
預測值
排序值
A
1
0。4
2
B
1
0。8
5
C
0
0。2
1
D
0
0。4
3
E
0
0。5
4
在上面的例子中出現了得分相等的情況,這時候排序值由相同得分的排序值算平均值,所以計算AUC時,樣本A的排序值等於樣本A本身和它相同得分樣本D的排序值均值,即(2+3)/2=2。5,因此帶入AUC的計算公式,得
。
3 程式碼實現AUC計算
推薦相關崗位的面試中,面試官經常問到AUC的計算,主要想考察面試者對AUC的理解,也就是本文介紹的方法1或者方法2。下面將給3種計算的程式碼實現:
python中的sklearn工具
本文中的方法1
本文中的方法2
在方法2的實現程式碼中,為了方便,當預測得分相同時,沒有按照定義用排序值的均值,而是直接使用排序均值。使用這種近似,對本文中的例子的AUC有影響,但生產環境的資料集大,
這種近似對AUC的影響極小
。
import
numpy
as
np
from
sklearn。metrics
import
roc_auc_score
# python sklearn包計算auc
def
get_auc
(
y_labels
,
y_scores
):
auc
=
roc_auc_score
(
y_labels
,
y_scores
)
(
‘AUC calculated by sklearn tool is
{}
’
。
format
(
auc
))
return
auc
# 方法1計算auc
def
calculate_auc_func1
(
y_labels
,
y_scores
):
pos_sample_ids
=
[
i
for
i
in
range
(
len
(
y_labels
))
if
y_labels
[
i
]
==
1
]
neg_sample_ids
=
[
i
for
i
in
range
(
len
(
y_labels
))
if
y_labels
[
i
]
==
0
]
sum_indicator_value
=
0
for
i
in
pos_sample_ids
:
for
j
in
neg_sample_ids
:
if
y_scores
[
i
]
>
y_scores
[
j
]:
sum_indicator_value
+=
1
elif
y_scores
[
i
]
==
y_scores
[
j
]:
sum_indicator_value
+=
0。5
auc
=
sum_indicator_value
/
(
len
(
pos_sample_ids
)
*
len
(
neg_sample_ids
))
(
‘AUC calculated by function1 is
{:。2f}
’
。
format
(
auc
))
return
auc
# 方法2計算auc, 當預測分相同時,未按照定義使用排序值的均值,而是直接使用排序值,當資料量大時,對auc影響小
def
calculate_auc_func2
(
y_labels
,
y_scores
):
samples
=
list
(
zip
(
y_scores
,
y_labels
))
rank
=
[(
values2
,
values1
)
for
values1
,
values2
in
sorted
(
samples
,
key
=
lambda
x
:
x
[
0
])]
pos_rank
=
[
i
+
1
for
i
in
range
(
len
(
rank
))
if
rank
[
i
][
0
]
==
1
]
pos_cnt
=
np
。
sum
(
y_labels
==
1
)
neg_cnt
=
np
。
sum
(
y_labels
==
0
)
auc
=
(
np
。
sum
(
pos_rank
)
-
pos_cnt
*
(
pos_cnt
+
1
)
/
2
)
/
(
pos_cnt
*
neg_cnt
)
(
‘AUC calculated by function2 is
{:。2f}
’
。
format
(
auc
))
return
auc
if
__name__
==
‘__main__’
:
y_labels
=
np
。
array
([
1
,
1
,
0
,
0
,
0
])
y_scores
=
np
。
array
([
0。4
,
0。8
,
0。2
,
0。4
,
0。5
])
get_auc
(
y_labels
,
y_scores
)
calculate_auc_func1
(
y_labels
,
y_scores
)
calculate_auc_func2
(
y_labels
,
y_scores
)
上述程式碼執行結果
圖1 auc計算程式碼執行結果
由於方法2做了近似,例子中的樣本數量少,影響較大。在實際推薦業務,由於資料量非常大,近似對auc值的影響可以忽略不計。
預告:下一篇將分享過擬合問題。
推薦系列文章:
郭婷:什麼是好的推薦,重新理解AUC
郭婷:為什麼需要推薦
工作相關的內容會同步在”播播筆記“這個公眾號更新
生活的思考和記錄會更新在”吾之“這個公眾號