ROC曲线与AUC区域的理解与实践

时间:2022-12-07 17:42:20

Receiver Operating Characteristic Area Under the Curve (ROC and AUC).

如何向别人解释 ROC AUC 对评价机器学习算法的意义:

一个样本集,一半正样本,一半为负样本。如果一个机器学习算法将全部样本均预测为正(或者负),也即是一种 trivial 的方式,它的准确率即可达到 50%,fifty fifty 的正确率就像抛一枚硬币一样。引入 ROC AUC 的评估方式(evaluation)可以实现了分类性能对样本类别比例的稳定性

1. TPR FPR


ROC曲线与AUC区域的理解与实践

  • Precision(精确率): P=TPTP+FP
  • Recall(召回率): R=TPTP+FN
    • Fscore=21/P+1/R
  • TPR (True Positive Rate): tpr=TPTP+FN=TPP
  • FPR (False Positive Rate): FPR=FPFP+TN=FPN
def TPR(y_true, y_pred):
return ((y_true==1)*(y_pred==1)).sum()/(y_true==1).sum()
def FPR(y_true, y_pred):
return ((y_true==0)*(y_pred==1)).sum()/(y_true==0).sum()

2. plot ROC curve

对于一个特定的分类器与测试数据集,显然只能得到一个分类结果,即一组TPR与FPR值,而要画出一个曲线,我们便需要一系列的FPR与TPR值。我们来看 Wikipedia对其的定义,

In signal detection theory, a receiver operating characteristic (ROC), or simply ROC curve, is a graphical plot which illustrates the performance of a binary classifier system as its discrimination threshold is varied.

如何理解这里的 discrimination threshold呢?其实我们忽略了分类器的一个重要功能是“概率输出”,即表示分类器某个样本以多大的概率属于正样本或负样本。对于0,1二分类问题,一些分类器得到的其实不是简单的0和1,如神经网络,得到诸如0.5, 0.8这样的分类结果。这时,如果我们人为指定一个阈值(threshold),比如0.4,那么小于0.4的为0类,大于等于0.4的则为1类,则可得一个分类结果(该分类结果再跟真实的结果相结合,可进而计算TPR与FPR)。

阈值不同,可以得到不同的结果,但是由于分类器所决定的统计图始终是不变的。这时候就需要一个独立于阈值,只与分类器有关的评价指标,来衡量特定分类器的好坏。

假如我们已经得到了所有样本的概率输出(属于正样本的概率),现在的问题是如何改变“discrimination threshold”呢?我们根据每个训练样本属于正样本的概率值从大到小进行排序。下图为某一示例,共20个训练样本,”Class”一列表示每个样本真正的标签(p为正样本,n表示负样本),”Score”表示每个测试样本属于正样本的概率。


ROC曲线与AUC区域的理解与实践

接下来,我们从高到低,依次将”Score”值作为阈值(threshold)。每次选取一个不同的 threshold,我们就可以得到一组FPR和TPR,这样一来我们共得到20组FPR与TPR的值,即得ROC curve:

def main():
y_true = np.array([1, 1, 0, 1, 1, 1, 0, 0, 1, 0,
1, 0, 1, 0, 0, 0, 1, 0, 1, 0])
y_hat = np.array([.9, .8, .7, .6, .55, .54, .53, .52, .51, .505,
.4, .39, .38, .37, .36, .35, .34, .33, .30, .1])
threshs = sorted(y_hat, reverse=True)
tprs, fprs = [], []
for thresh in threshs:
y_pred = np.where(y_hat < thresh, 0, 1)
# np.where, numpy版的三目运算符
tprs.append(TPR(y_true, y_pred))
fprs.append(FPR(y_true, y_pred))
plt.plot(fprs, tprs, 'k--', lw=2)
plt.scatter(fprs, tprs, c='k', marker='x', s=50)
plt.plot(np.arange(-.05, 1.05, .01), np.arange(-.05, 1.05, .01), '--', color='lightgray')
plt.xlabel('False positive rate')
plt.ylabel('True positive rate')
plt.xlim([0-.05, 1+.05])
plt.ylim([0-.05, 1+.05])
plt.show()
if __name__ == '__main__':
main()


ROC曲线与AUC区域的理解与实践

当threshold取值越多,ROC曲线越光滑。

3. AUC 值的计算

AUC(Area Under Curve)被定义为ROC曲线下的面积,显然这个面积的数值不会大于1,整个ROC空间为坐标轴上的 [0,1]×[0,1] 。又由于ROC曲线一般都位于 y=x 这条直线的上方(如上图的虚线所示),所以AUC的取值范围在0.5和1之间。使用AUC作为评价指标是因为很多时候,ROC曲线并不能清晰地说明哪个分类器的效果更好,而作为一个数值,对应AUC更大的分类器效果更好。

rankM(M+1)/2M×N

首先把所有样本按照score排序,依次用rank表示,最大score的样本,rank=n (n=M+N),其次为 n-1。

这里不妨给出一份AUC的计算代码:

def AUC(y_true, scores):
M = (y_true==1).sum() # M:正样本个数
N = (y_true==0).sum() # N:负样本个数
I = np.argsort(scores)
rank = 0
for i in range(1, 1+len(I)):
if y_true[I[-i]] == 1:
rank += len(I)-i+1
return (rank-M*(M+1))/(M*N)

4. 为什么使用ROC Curve

既然已经存在那么多评价标准,为什么还要使用ROC和AUC呢?因为ROC曲线有个很好的特性:当训练集中的正负样本的分布(np.bincount(y_train))变化的时候,ROC曲线能够保持不变,也即ROC不受训练集类别分布的影响。因为在实际的训练集中经常会出现类不平衡(class imbalance)现象,即正样本占90%,或者相反(此时如果一个将所有样本当预测为正样本的分类器,也能得到90%的准确率,显然这样毫无意义)。

References

ROC曲线与AUC
ROC和AUC介绍以及如何计算AUC