参考
- AUC的计算方法
- AUC计算方法与Python实现代码
- 百面机器学习127页
1.使用概率定义计算auc
1.1计算公式推导
- M为正样本数量,N为负样本数量。
- r a n k i rank_i ranki为第i条样本序号。将所有样本按照预测得分升序排列,预测分最小的序号为1,预测分最大的序号为T=M+N。式1分子的求和部分,表示将所有正样本的序号求和。
- 对预测得分相同的多条样本,序号要取原始序号的平均值。
假设第1大的正样本序号为 r a n k a 1 rank_{a1} ranka1,则预测得分比它小的样本有 r a n k a 1 − 1 rank_{a1}-1 ranka1−1个,其中包含了M-1个正样本,因此预测得分比它小的负样本个数为 n 1 = r a n k a 1 − M n_1=rank_{a1}-M n1=ranka1−M。
假设第i大的正样本序号为 r a n k a i rank_{ai} rankai,同理可得预测得分比它小的负样本个数为 n i = r a n k a i − M + i − 1 n_i=rank_{ai}-M+i-1 ni=rankai−M+i−1。
根据AUC的概率定义 A U C = P ( P 正 样 本 > P 负 样 本 ) = ∑ I ( P 正 样 本 , P 负 样 本 ) / ( M ∗ N ) AUC=P(P_{正样本}>P_{负样本})=\sum I(P_{正样本}, P_{负样本})/ (M*N) AUC=P(P正样本>P负样本)=∑I(P正样本,P负样本)/(M∗N),分子可以通过 n i n_i ni来计算, ∑ I ( P 正 样 本 , P 负 样 本 ) = ∑ a i ∈ 正 样 本 n a i = ∑ a i ∈ 正 样 本 r a n k a i − M ( M + 1 ) / 2 \sum I(P_{正样本}, P_{负样本})=\sum_{ai∈正样本} n_{ai}=\sum_{ai∈正样本} rank_{ai} – M(M+1)/2 ∑I(P正样本,P负样本)=∑ai∈正样本nai=∑ai∈正样本rankai−M(M+1)/2,从而得到最终结果式2。
1.2修复代码bug
根据式2,博文2给出了如下Python实现代码,这个代码有bug,没有处理预测得分相同的情况。
#有bug,没有处理预测得分相同的情况 def calAUC1(prob,labels): f = list(zip(prob,labels)) #(prob, label)列表 rank = [values2 for values1,values2 in sorted(f,key=lambda x:x[0])] #升序排序后的label列表 rankList = [i+1 for i in range(len(rank)) if rank[i]==1] #正样本序号列表,从1开始 posNum = 0 negNum = 0 for i in range(len(labels)): #统计正负样本个数 if(labels[i]==1): posNum+=1 else: negNum+=1 auc = 0 auc = (sum(rankList)- (posNum*(posNum+1))/2)/(posNum*negNum) #计算auc print(auc) return auc
下面是与sklearn.metrics.roc_auc_score比较的结果,预测得分有重复时,上面的calAUC1代码给出错误结果。
y_true = np.array([1, 1, 0, 0, 1, 1, 0]) #预测得分无重复,结果正确 y_scores = np.array([0.8, 0.7, 0.6, 0.5, 0.4, 0.2, 0.3]) # roc_auc_score is 0.33333 # calAUC1 is 0.33334 #预测得分有重复,结果错误! y_scores = np.array([0.8, 0.7, 0.5, 0.5, 0.5, 0.5, 0.3]) # roc_auc_score is 0.33334 # calAUC1 is 1.0
增加处理预测得分相同的情况后,修复后的代码如下:
#修复bug,处理了预测得分相同的情况 def calAUC(prob, labels): assert len(prob) == len(labels) n = len(prob) f = list(zip(prob, labels)) # (prob, label)列表 f = sorted(f, key=lambda x: x[0]) # 按照预测得分升序排列 tuple = [[f[i][0], f[i][1], i + 1] for i in range(n)] # [prob, lable, rank], rank从1开始。不能用元祖,否则修改不了rank值 # 预测分数相同时,将rank值平均 i = 0 while i < n - 1: j = i while j + 1 < n and tuple[j][0] == tuple[j + 1][0]: # 判断预测分是否相同 j = j + 1 if j > i: # i~j预测得分相同 mean = sum([tuple[k][2] for k in range(i, j + 1)]) / (j + 1 - i) for k in range(i, j + 1): tuple[k][2] = mean # rank值取平均 i = j + 1 posRankList = [x[2] for x in tuple if x[1] == 1] # 正样本序号列表,从1开始 posNum = len([x for x in labels if x == 1]) # 正样本数量 negNum = len([x for x in labels if x == 0]) # 负样本数量 auc = (sum(posRankList) - (posNum * (posNum + 1)) / 2) / (posNum * negNum) # 计算auc return auc
下面是测试结果,不管预测得分有无重复,修改后的calAUC与sklearn.metrics.roc_auc_score结果一致。
y_true = np.array([1, 1, 0, 0, 1, 1, 0]) #预测得分无重复,结果正确 y_scores = np.array([0.8, 0.7, 0.6, 0.5, 0.4, 0.2, 0.3]) # roc_auc_score is 0.33333 # calAUC is 0.33334 #预测得分有重复,结果正确! y_scores = np.array([0.8, 0.7, 0.5, 0.5, 0.5, 0.5, 0.3]) # roc_auc_score is 0.33334 # calAUC is 0.33334
2.使用面积定义计算auc
代码如下,暂未处理预测得分相同的情况。
# 使用面积定义计算auc def aucArea(prob, labels): f = list(zip(prob, labels)) lableSort = [label for p, label in sorted(f, key=lambda x: x[0], reverse=True)] # 降序排列 posNum = len([x for x in labels if x == 1]) # 正样本数量 negNum = len([x for x in labels if x == 0]) # 负样本数量 area = 0 h = 0 for label in lableSort: if label == 1: h += 1 else: area += h # 向右移动时,更新面积 area = area / (posNum * negNum) return area
y_true = np.array([1, 1, 0, 0, 1, 1, 0]) #预测得分无重复,结果正确 y_scores = np.array([0.8, 0.7, 0.6, 0.5, 0.4, 0.2, 0.3]) # roc_auc_score is 0.33333 # aucArea is 0.33334 #预测得分有重复,结果错误! y_scores = np.array([0.8, 0.7, 0.5, 0.5, 0.5, 0.5, 0.3]) # roc_auc_score is 0.33334 # aucArea is 0.66666
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/176296.html原文链接:https://javaforall.net
