分类阈值->混淆矩阵
在做二分类任务时,模型一般会对每个样本输出一个分值s(有时这个分值也表示样本是正例的概率)。
在这个分值区间里,设置一个阈值t,就可以把在阈值之上的预测为正例,阈值之下的预测为负例。
根据样本真实的标签和预测的结果,可以分为四种情况,统计四种情况的样本个数,就可以得到一个混淆矩阵(confusion matrix):
正样本 | 负样本 | |
---|---|---|
预测为正 | TP(真正例) | FP(假正例) |
预测为负 | FN(假负例) | TN(真负例) |
比如下图,正样本和负样本在分值区间上是两个正态分布,阈值t会把这两个分布切分成四块。
左右调整阈值t的大小,这四个值也会随之变化。于是,每个阈值t都会得到一个混淆矩阵。
假如我们要比较N个模型,每个模型设置K个阈值,那么就有N*K个混淆矩阵。用这么多混淆矩阵来比较这些模型的效果显然不现实,更何况阈值的选取还可以有无限多个。
混淆矩阵->TPR/FPR
所以接下来,我们根据混淆矩阵里的四个值,定义两个指标
True Positive Rate (TPR,真正例率) = \(\frac{TP}{TP+FN}\)
False Positive Rate (FPR,假正例率) = \(\frac{FP}{TN+FP}\)
对于真正例率TPR,分子是得分>t里面正样本的数目,分母是总的正样本数目。
对于假正例率FPR,分子是得分>t里面负样本的数目,分母是总的负样本数目。
一个好的模型,肯定是TPR越大越好,FPR越小越好。
分类阈值->TPR/FPR->ROC
那么调整阈值t时,因为混淆矩阵里四个值的变化,这两个指标也会随之变化。
比如,当t变小时,因为FN变小,TPR会变大;因为TN变小,FPR也会变大。反之,当t变大时,TPR和FPR都会变小。
那么如果我们取各种不同的阈值t,并以TPR和FPR作为坐标轴,画出各个t值对应的点,就可以得到这么一个图:
因为好的模型肯定是TPR大而FPR小,所以这些点都在左上的部分。又因为t变大时,TPR和FPR都会变小,所以这些点分布的形状是上凸的。
连接这些点,可以得到一个上凸的曲线,称为ROC曲线。
当t很小时,TN和FN是0,那么TPR和FPR都是1。当t很大时,TP和FP都是0,那么TPR和FPR都是0。所以曲线是从右上角的(1,1)直到左下角的(0,0)。
随机模型的ROC曲线
随机的模型不区分正负样本,所以在得分>t的样本里,正负样本的比例应该与总体正负样本的比例相同。
即\(\frac{TP}{FP}=\frac{P}{N}=\frac{TP+FN}{TN+FP}\)。因此\(\frac{TP}{TP+FN}=\frac{FP}{TN+FP}\),即TPR=FPR。
所以随机模型的ROC曲线是一条连接(0,0)和(1,1)的直线。
我们也可以画出正负例在随机模型的分值上的分布,如下图的两种情况,正负例在所有分值上均匀分布,或者正负例在分值上的分布相同,TPR和FPR都是随着t的变化始终相等。
ROC->AUC
有了ROC曲线,比较多个模型的效果,就是比较多条ROC曲线。
曲线越凸,模型效果越好。
但二维的曲线比较起来也还是不方便。
所以接下来我们定义ROC曲线下的面积为AUROC(Area Under the ROC Curve),大部分时候简写为AUC。
如果现在有两个模型,一个A模型始终比另一个模型B好,也就是在所有阈值t下,A的TPR都更高,FPR都更小。
那么A的ROC曲线就比B的更凸,A的曲线下面积,即AUC就更大。
随机模型的AUC是对角线下的面积,即0.5。所以任何合理的模型的AUC都应该大于0.5。
AUC的另一种解释
AUC还可以解释为,随机选取一个正样本和一个负样本,模型给正样本的分值高于负样本分值的概率。
这个解释与以上用ROC曲线面积来做的定义之间的关系,见StackExchange。
AUC的计算
根据以上对AUC的解释,可以实现一种最简单的\(O(n^2)\)复杂度的计算方法。
点击查看代码
import numpy as np
y = np.array([1, 0, 0, 0, 1, 0, 1, 0, 0, 1 ])
pred = np.array([0.9, 0.4, 0.3, 0.9, 0.35, 0.6, 0.65, 0.32, 0.8, 0.7])
def auc():
pos = pred[y==1]
neg = pred[y==0]
correct = 0
for i in pos:
for j in neg:
if i > j:
correct += 1
elif i == j:
correct += 0.5
return correct / (len(pos) * len(neg))
print(auc())
from sklearn import metrics
fpr, tpr, thresholds = metrics.roc_curve(y, pred, pos_label=1)
print(metrics.auc(fpr, tpr))
AUC的适用与不适用
适用
- 仅关心相对序,不考虑分值大小时
- 与阈值无关时
- 要求对正负样本比例不敏感时,如对负样本下采样
不适用
- 关心分值大小时
- 某类正样本或负样本更重要时,如email spam classification
参考
https://stats.stackexchange.com/questions/132777/what-does-auc-stand-for-and-what-is-it/133435#133435
https://developers.google.com/machine-learning/crash-course/classification/roc-and-auc
https://www.dataschool.io/roc-curves-and-auc-explained/
https://tracholar.github.io/machine-learning/2018/01/26/auc.html
https://stats.stackexchange.com/questions/190216/why-is-roc-auc-equivalent-to-the-probability-that-two-randomly-selected-samples
https://ccrma.stanford.edu/workshops/mir2009/references/ROCintro.pdf
https://zhuanlan.zhihu.com/p/411010918
https://medium.com/@penggongting/understanding-roc-auc-pros-and-cons-why-is-bier-score-a-great-supplement-c7a0c976b679
https://stats.stackexchange.com/questions/105501/understanding-roc-curve
https://en.wikipedia.org/wiki/Receiver_operating_characteristic