计算AUC的方法以及代码实现

Posted bitcarmanlee

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了计算AUC的方法以及代码实现相关的知识,希望对你有一定的参考价值。

1.传统的AUC计算方法

传统的AUC计算方法,就是计算ROC曲线下围成的面积。具体的详情可以参考下文
入门选手都能理解的ROC曲线与AUC值

在比较早期的Machine Learning相关资料中,最常见介绍的计算AUC方法就是这种。由于我们实际样本是有限的,得到的AUC曲线必然是一个阶梯状。因此实际计算时候,可以将score先排序。假设score越大,样本为正例的概率越大,我们一边扫描就可以得到我们想要的AUC。但是这样做有个缺点,就是当多个测试样本的score相等的时候,我们调整一下阈值,得到的不是曲线一个阶梯往上或者往右的延展,而是斜着向上形成一个梯形。此 时,我们就需要计算这个梯形的面积。因此,用这种方法计算得到AUC是比较麻烦的。

2.Wilcoxon-Mann-Whitney test

这是一个two-sample test的非参数检验版本,对标的参数检验版本是t-test。

具体可以参考
Mann–Whitney U test

不要问我原理,说实话我也不是特别懂,没有深入研究过…
重点看结论:
AUC跟Wilcoxon-Mann-Witney Test是等价的。Wilcoxon-Mann-Witney Test就是测试任意给一个正类样本和一个负类样本,正类样本的score有多大的概率大于负类样本的score。有了这个定义,我们就得到了另外一中计 算AUC的办法:得到这个概率。

具体来说,假设样本中有正负样本分别为M,N个,那么样本中一共会有MN个样本对。我们在这MN个样本对中,计算出正样本分数比负样本分数高的样本数假设为C,那么AUC的值为
C / M ∗ N C / M*N C/MN

从上面的分析过程,不难看出该方法的计算复杂度为 O ( n 2 ) O(n^2) O(n2),其中n为样本总数,n = M + N。

3.快速计算方法

上面第二种方法的计算复杂度较高,我们可以稍作改进,得到更为快速的计算方法

按概率从高到矮排个降序, 对于正样本中概率最高的,排序为rank_1, 比它概率小的有M-1个正样本(M为正样本个数), (rank_1 - M) 个负样本。
正样本概率第二高的, 排序为rank_2, 比它概率小的有M-2个正样本,(rank_2 - M + 1) 个 负样本。

以此类推
正样本中概率最小的, 排序为rank_M,比它概率小的有0个正样本,rank_M - 1 个负样本。

总共有MxN个正负样本对(N为负样本个数)。把所有比较中 正样本概率大于负样本概率 的例子都算上, 得到公式(rank_1 - M + rank_2 - M + 1 … + rank_M - 1) / (MxN) 就是正样本概率大于负样本概率的可能性了。 化简后(因为后面是个等差数列)得:

A U C = ∑ i ∈ p o s C l a s s r a n k i − M ( M + 1 ) 2 M ∗ N AUC = \\frac\\sum_i \\in posClass rank_i - \\fracM(M+1)2M * N AUC=MNiposClassranki2M(M+1)

4.代码实现

前面理论都已经说完,接下来又到了show me the code环节。
直接上code

#!/usr/bin/env python
# -*- coding: utf-8 -*-

#Author: WangLei
#date: 2022/11/3

import numpy as np
from sklearn.metrics import roc_auc_score


def gen_score_and_label():
    y_scores = np.zeros(100)
    y_labels = np.zeros(100)
    for i in range(100):
        y_scores[i] = np.random.random()
        y_labels[i] = np.random.choice([0, 1])
    return y_labels, y_scores



def cal_auc_self(y_labels, y_scores):
    f = sorted(list(zip(y_scores, y_labels)), key=lambda x: x[0])
    rank = [value2 for _, value2 in f]
    rankindex = [i+1 for i in range(len(rank)) if rank[i] == 1]
    pos_count = np.sum(y_labels == 1)
    neg_count = np.sum(y_labels == 0)
    quick_auc = (np.sum(rankindex) - pos_count * (pos_count + 1) / 2) / (pos_count * neg_count)
    print("quick_auc is: ", quick_auc)


    fpos = [v1 for v1, v2 in f if v2 == 1]
    fneg = [v1 for v1, v2 in f if v2 == 0]
    rightcount = 0
    for score_pos in fpos:
        for score_neg in fneg:
            if score_pos > score_neg:
                rightcount += 1

    slow_auc = rightcount / (pos_count * neg_count)
    print("slow_auc is: ", slow_auc)


def main():
    y_labels, y_scores = gen_score_and_label()
    print('sklearn AUC: ', roc_auc_score(y_labels, y_scores))
    cal_auc_self(y_labels, y_scores)


main()

某一次运行的结果为:

sklearn AUC:  0.5292467948717948
quick_auc is:  0.5292467948717948
slow_auc is:  0.5292467948717948

可以看出,sklearn中auc,以及我们用上面第二,第三种方法计算得到的auc值,都是完全一致的。

以上是关于计算AUC的方法以及代码实现的主要内容,如果未能解决你的问题,请参考以下文章

auc计算方法总结

ACU个人详解的理解及代码实现

机器学习系列-ROC曲线以及AUC计算

ROC和AUC介绍以及如何计算AUC

ROC和AUC介绍以及如何计算AUC

并行计算学习之用MPI实现梯形积分法