AUC 的原理
衡量分类模型,不能仅考虑分对的情况,还要考虑分错的情况
- 真阳:正样本中被正确分为正样本的比例, T P R = T P T P + F N TPR=\frac{TP}{TP+FN} TPR=TP+FNTP;
- 假阳:负样本中被错误分为正样本的比例, F P R = F P F P + T N FPR=\frac{FP}{FP+TN} FPR=FP+TNFP。
- 划分标准:神经网络只是输出一个预测概率 p p p,需要根据人为设定的阈值去判断是否预测为正样本。因此,取不同的阈值,会得到不同的 T P R TPR TPR 和 F P R FPR FPR 取值。分别作为纵坐标、横坐标并连成线,即可获得 ROC 曲线。
- AUC:ROC 曲线下的面积,就是 AUC。
AUC 计算的三种方法
AUC 计算有两条思路:
- 一种是积分思想,用矩形近似计算 ROC 曲线下的面积;
- 一种是AUC作为概率,具体有两种实现方法。
1. 矩形近似法
根据 AUC 的定义,取不同的阈值获得不同的 TPR / FPR,根据定义计算。
(但此方法比较繁琐,而且只是近似计算,并不是很推荐)
原文:https://blog.csdn.net/weixin_44398263/article/details/123639531#:~:text=AUC%E7%9A%84%E5%85%A8%E5%90%8D%E6%98%AFA
### 计算AUC
## 对于正样本N和负样本M, 枚举正负样本对(Ni, Mj),如果正样本分值大于负样本,则+1,相等则+0.5,小于则不加分,再用总分除以N*M
import numpy as np
import matplotlib.pyplot as plt
def AUC(pre, label):
label = label == 1
print(label)
pos = pre[label]
neg = pre[~label]
pos = np.sort(pos)[::-1]
neg = np.sort(neg)[::-1]
print(pos, neg)
pre_sort = np.sort(pre)[::-1]
label_sort = label[np.argsort(pre)[::-1]]
possum, negsum = pos.shape[0], neg.shape[0]
TPR =[]
FPR = []
# 按照定义,逐个取每个预测结果作为阈值,计算 TPR FPR
for i in range(pre_sort.shape[0]):
TP = np.sum(label_sort[:i+1])
TPR.append(TP/possum)
FP = i+1 - TP
FPR.append(FP/negsum)
i, j, nextj = 0, 0, 0
auc = 0
while i < pos.shape[0] and j < neg.shape[0]:
if nextj:
j = nextj
nexj = 0
while i < pos.shape[0] and j < neg.shape[0] and neg[j] > pos[i]:
j += 1
while i < pos.shape[0] and j < neg.shape[0] and pos[i] >= neg[j]:
if pos[i] > neg[j]:
auc += 1 * (neg.shape[0]-j)
else:
nextj = j + 1
## 如果相等,找到第一个绝对大于的为nextj
while nextj < neg.shape[0] and neg[nextj]==neg[j]:
nextj += 1
auc += 0.5 * (nextj-j) + 1 * (neg.shape[0]-nextj)
i += 1
auc /= pos.shape[0] * neg.shape[0]
print(auc)
plt.plot(FPR, TPR)
plt.show()
pre = np.asarray([0.9, 0.8, 0.3, 0.1, 0.4, 0.9, 0.66, 0.7])
label = np.asarray([1,0,0,0,1,0,1,0])
auc = AUC(pre, label)
print(auc)
2. AUC 作为概率
原理
AUC等于,随机选取一正一负样本,正样本预测值 p p o s p_{pos} ppos大于负样本预测值 p n e g p_{neg} pneg的概率。
证明
原理证明参考:AUC公式的证明,本文直接将其作为定理。
参考:A Complete Guide to Area Under Curve (AUC)
根据定理,随机选取一正一负样本,计算正样本预测值 大于负样本预测值 的概率。直接统计 正样本预测值大于负样本预测值概率
的总数,除以正负样本组合数
,即可获得概率。对于 正样本预测值等于负样本预测值概率
的情况,按照 0.5 权重处理。
(此方法易于理解,实现简单,不考虑复杂度的情况下比较推荐)
import numpy as np
import pandas as pd
from sklearn.metrics import roc_auc_score
def AUC(preds,labels):
df = pd.DataFrame({'pred':preds,'label':labels})
# 从 label 取出正样本、负样本
ones = df[df['label']==1]
zeros = df[df['label']==0]
# 总的组合数
totoal_pairs = len(ones) * len(zeros)
# 正样本预测值大于负样本预测值的方案数
cnt1 = sum(ones['pred'].apply(lambda x : (x > zeros['pred'])).sum())
# 正样本预测值小于负样本预测值的方案数
cnt2 = sum(ones['pred'].apply(lambda x : (x < zeros['pred'])).sum())
# 正样本预测值大于负样本预测值的概率
p1 = cnt1 / totoal_pairs
# 正样本预测值小于负样本预测值的概率
p2 = cnt2 / totoal_pairs
# 正样本预测值等于负样本预测值的概率
p3 = 1 - p1 - p2
# 相等的情况占 0.5 权重
auc = p1 + 0.5 * p3
return auc
if __name__=='__main__':
labels = np.array([1, 0, 0, 0, 1, 0, 1, 0, ])
preds = np.array([0.9, 0.8, 0.3, 0.1, 0.4, 0.9, 0.66, 0.7])
print("AUC by hand:",AUC(preds,labels))
print("AUC by sklearn",roc_auc_score(labels,preds))
运行结果:
甚至,可以直接
O
(
M
×
N
)
O(M\times N)
O(M×N) 枚举样本对,计算概率:
原文:https://zhuanlan.zhihu.com/p/84035782
def AUC(label, pre):
"""
适用于python3.0以上版本
"""
#计算正样本和负样本的索引,以便索引出之后的概率值
pos = [i for i in range(len(label)) if label[i] == 1]
neg = [i for i in range(len(label)) if label[i] == 0]
auc = 0
for i in pos:
for j in neg:
if pre[i] > pre[j]:
auc += 1
elif pre[i] == pre[j]:
auc += 0.5
return auc / (len(pos)*len(neg))
if __name__ == '__main__':
label = [1,0,0,0,1,0,1,0]
pre = [0.9, 0.8, 0.3, 0.1, 0.4, 0.9, 0.66, 0.7]
print(AUC(label, pre))
from sklearn.metrics import roc_curve, auc
fpr, tpr, th = roc_curve(label, pre , pos_label=1)
print('sklearn', auc(fpr, tpr))
3. AUC 作为概率的计算公式
公式
按照上述定理,可得出 AUC 计算公式如下:
A
U
C
=
∑
i
∈
正样本
r
a
n
k
(
i
)
−
M
×
(
1
+
M
)
2
M
×
N
AUC=\frac{\sum_{i\in正样本}rank(i)-\frac{M\times(1+M)}{2}}{M\times N}
AUC=M×N∑i∈正样本rank(i)−2M×(1+M)
解释
设:正样本个数为 M M M 个,负样本个数为 N N N 个,则总样本数为 M + N M+N M+N。
- 按照求概率的方法,确定
总方案数
和目标方案数
,再求比值; 总方案数
:随机选取一正一负的方案数= M × N M\times N M×N;目标方案数
:选取的正样本预测概率 > 选取的负样本预测概率 的方案数。根据定义,正样本有 M M M 个,负样本有 N N N 个,这里拿正样本进行分类讨论:
- 首先,将样本按照模型预测的概率大小,增序排序,序列长度为 M + N M+N M+N;
- 对于概率最大的正样本,设下标为 r a n k 1 rank_1 rank1,则表明其前方(含自身)共有 r a n k 1 rank_1 rank1 个样本。同时根据定义,其又是概率最大的正样本,表明前方(含自身)共有 M M M 个正样本(因为其是预测概率最大的正样本,则其他所有的正样本概率都比它小,都在其前方)。现在要满足负样本的概率比其小,则负样本的下标 i i i 必须满足 i < r a n k 1 i<rank_1 i<rank1,这样的负样本个数有 r a n k 1 − M rank_1-M rank1−M 个,从中选一个,共有 r a n k 1 − M rank_1-M rank1−M 种方案;
- 对于概率次大的正样本,设下标为 r a n k 2 rank_2 rank2,则表明其前方(含自身)共有 r a n k 2 rank_2 rank2 个样本。同时根据定义,其又是概率次大的正样本,表明前方(含自身)共有 M − 1 M-1 M−1 个正样本。现在要满足负样本的概率比其小,则负样本的下标 i i i 必须满足 i < r a n k 2 i<rank_2 i<rank2,这样的负样本个数有 r a n k 2 − ( M − 1 ) rank_2-(M-1) rank2−(M−1) 个,从中选一个,共有 r a n k 2 − ( M − 1 ) rank_2-(M-1) rank2−(M−1) 种方案;
- 同理,对于概率最小的正样本,设下标为 r a n k M rank_M rankM,则表明其前方(含自身)共有 r a n k M rank_M rankM 个样本, 同时其又是概率最小的正样本,表明前方(含自身)共有 1 个正样本,负样本个数则为 r a n k M − 1 rank_M-1 rankM−1,从中选一个,共有 r a n k M − 1 rank_M-1 rankM−1 种方案;
- 将上述
M
M
M 中情况求和,可得
目标方案数
为 : r a n k 1 + r a n k 2 + . . . + r a n k M − ( 1 + 2 + . . . + M ) = ∑ r a n k i − M × ( 1 + M ) M × N rank_1+rank_2+...+rank_M-(1+2+...+M)=\sum rank_i-\frac{M\times (1+M)}{M\times N} rank1+rank2+...+rankM−(1+2+...+M)=∑ranki−M×NM×(1+M) . - 求概率:
A U C = ∑ i ∈ 正样本 r a n k ( i ) − M × ( 1 + M ) 2 M × N AUC=\frac{\sum_{i\in正样本}rank(i)-\frac{M\times(1+M)}{2}}{M\times N} AUC=M×N∑i∈正样本rank(i)−2M×(1+M)
细节:对于预测概率相同的样本,将其 rank 求平均。
原文:看完这篇AUC文章,搞定任何有关AUC的面试不成问题 - 知乎 (zhihu.com)
import numpy as np
import pandas as pd
y_pred = list(np.random.uniform(0.4,0.6, 2000))+ list(np.random.uniform(0.5, 0.7, 8000))
y_true =[0]*2000+[1]*8000
def calc_auc(y_true, y_pred):
pair = list(zip(y_true,y_pred))
pair = sorted(pair,key=lambda x:x[1])
df = pd.DataFrame([[x[0],x[1], i+1] for i,x in enumerate(pair)],columns=['y_true','y_pred','rank'])
#下面需要将预测值为一样的进行重新编号
for k,y in df.y pred.value_counts().items():
if v==1: # 说明该预测值k只出现了一次,没有重复,不考虑
continue
rank_mean = df[df.y pred == k]['rank'].mean( )
df.loc[df.y_pred ==k,"rank']= rank_mean
pos_df = df[df.y true ==1]
m = pos_df.shape[0] # 正样本数
n = df.shape[0]-m # 负样本数
return (pos_df['rank'].sum()-m*(m+1)/2)/(m *n)
print(calc_auc(y_true,y_pred))
from sklearn,metrics import roc_auc_score
print(roc_auc_score(y_true,y_pred))
总结
AUC 的实现方法分为 积分近似
和 基于概率
两种思路。
积分近似
严格按照 AUC 的定义,取不同阈值绘制 ROC 曲线,划分 bins 计算,但此方法实现较为繁琐,面试中一般不推荐此写法。
基于概率
可以直接根据定理统计概率,也可以根据计算公式去实现,面试中比较推荐实现这两种写法。
面试八股
原文
优点
- 全局评估能力,其他指标比如precision,recall,F1,根据区分正负样本阈值的变化会有不同的结果,而AUC不需要手动设定阈值,是一种整体上的衡量方法。
- 因此对正负样本均衡不敏感,在样本不均衡的情况下,也可以做出合理的评估;
- AUC衡量的是一种排序能力,因此特别适合排序类业务;
缺点
- 过于笼统,无法反映召回率、精确率等在实际业务中经常关心的指标;它没有给出模型误差的空间分布信息,AUC只关注正负样本之间的排序,并不关心正样本内部,或者负样本内部的排序,这样我们也无法衡量样本对于好坏客户的好坏程度的刻画能力;
- 忽略了预测的概率值和模型的拟合程度;
延伸
参考文献
A Complete Guide to Area Under Curve (AUC)
AUC公式的证明
看完这篇AUC文章,搞定任何有关AUC的面试不成问题 - 知乎 (zhihu.com)