1、什么是AUROC?
接受者操作特征曲线下面积(area under the receiver operating characteristic,AUROC) 是一个用来衡量分类器性能的指标。其通过接受者操作特征曲线(receiver operating characteristic curve)与坐标轴之间的面积大小来反应分类器的性能,其意义可以理解为均匀抽取的随机阳性样本(正样本)排名在均匀抽取的随机阴性样本(负样本)之前的期望。AUROC是一个介于0到1之间的数值,当AUROC值接近于1是,表示分类器可以较好的分类正负样本。
2、二分类问题中ROC曲线的绘制
ROC曲线也称为感受性曲线(sensitivity curve),该曲线反应的是同一信号刺激在不同的判定标准下的不同判定结果。在介绍ROC曲线前,首先介绍一些基本的概念。
2.1一些基本概念
ROC曲线也称为感受性曲线(sensitivity curve),该曲线反应的是同一信号刺激在不同的判定标准下的不同判定结果。在介绍ROC曲线前,首先介绍一些二分类问题中的基本的表示。
P (Positive) :正样本,一般用1来表示。
N (Negative) :负样本,一般用0来表示。
TP(True Positive): 真阳,预测为1,真实为1
FP(False Positive):假阳,预测为1,真实为0。
TN(Ture Negative):真阴,预测为0,真实为0。
FN(False Negative): 假阴:预测为0,真实为1。
TPR (True Positive Rate):真阳率,所有真实(标签)为1的样本中,预测为1的样本的比例。该值越高越好。
FPR(False Positive Rate): 假阳率:所有真实(标签)为0的样本中,预测为1的样本的比例。该值越低越好。
2.2 ROC曲线绘制
在二分类问题中,我们可以得到一个样本为1的概率值,该概率值是一个0到1之间的数值,同时,我们也可以设定一个阈值,当概率值大于阈值时,则认为该样本为正样本,反之,则认为该样本为负样本。
我们可以设置多个阈值,每一个阈值下,都可以得到测试样本的测试结果,以及该阈值下的TPR与FPR,对于一个样本数为N的测试集,我们可以设置N+2个阈值,得到N+2组TPR与FPR值,将TPR作为x轴,FPR作为y轴,便可以将N个点放在坐标系中,将所有的点连接起来,便可以得到ROC曲线。
2.3 一个示例
示例:有2个正样本A与B,其预测为正样本的概率分别为0.4,0.9,有两个负样本C与D,其预测为正样本的概率分别为0.2,0.5。
由于我们有四个样本,所以我们可以设置5个阈值,0,0.2,0.4,0.5,0.9,置信度大于阈值,则视为正样本
当阈值为0时,A,B,C,D 均预测为正样本,此时TPR为2/2 = 1,FPR为2/2 = 1;
当阈值为0.2时,A,B,D预测为正样本,C预测为负样本,TPR为2/2 = 1,FPR为1/2 = 0.5;
当阈值为0.4时,B,D预测为正样本,A,C预测为负样本,TPR为1/2 = 0.5,FPR为1/2 = 0.5;
当阈值为0.5时,B 预测为正样本,A,C,D预测为负样本,TPR为1/2 = 0.5,FPR为0/2 = 0;
当阈值为0.9时,A,B,C,D均预测为负样本,TPR为0/2 = 0,FPR为0/2 = 0;
我们将5组TPR与FPR画在坐标轴,就可以得到ROC曲线了,重复的可以看作一个点。
2.4 程序实现
根据上述实例,我们可以发现一些规律。
(1)阈值可以用置信度的排序来选取。
(2)随着阈值的增加,TPR与FPR的分母是不变的,只有分子在变化。
(3)随着阈值的增加,分子的变化是单调递减的。
因此,只要知道了随着阈值的改变,分子是怎么变的,就可以得到ROC曲线了。
换言之,阈值每改变一次,只需要知道是TPR的分子减1还是FPR的分子减1就好了。
下面详细介绍ROC的绘制函数。
def compute_roc(pred_p, pred_n, labels):
函数的输入为两个变量,
pred_p:正样本经过Softmax得到的分类为正样本的置信度 N*1,N为样本数,
pred_q: 负样本经过Softmax得到分类为负样本的置信度 M*1,M为样本数
可以看到,正样本的总数为N,负样本的总数为M,因此,TPR与FPR的分母分别为N与M。
predict = np.concatenate((pred_p, pred_q), axis=0)
将所有的置信度拼接起来。
TPR_target = np.concatenate((np.ones(len(x1)), np.zeros(len(x2))), axis=0)
FPR_target = np.concatenate((np.zeros(len(x1)), np.ones(len(x2))), axis=0)
创建两个矩阵,分别记录样本的属性,
在TPR_target 中,正样本为1,负样本为0(TPR更关注正样本的个数,即有多少个正样本被正确分类)
在FPR_target 中,正样本为0,负样本为1(FPR更关注负样本的个数,即有多少个负样本被错误分类)
idx = predict.argsort()
s_TPR_target = TPR_target[idx]
s_FPR_target = FPR_target[idx]
n = len(predict) #样本总个数,N+M
将拼接后的置信度根据大小进行排序,得到索引,样例中,[0.4,0.9,0.2,0.5]的排序结果为[0.2,0.4,0.5,0.9],所以索引为[2,0,3,1 ]
即原始的predict中predict[2] 是最小的,predict[0]是第二小的,依次类推。排序后,标签也要根据排序结果重新排列,
s_ TPR _target [0] = TPR _target[2],s_ TPR _target [1] = TPR _target[0],依次类推。这个过程可以简写为 s_ TPR _target = TPR _target[idx]。
TPR = [0 for x in range(n+1)]
FPR = [0 for x in range(n+1)]
创建两个空数组,用于存放不同阈值下的TPR值与FPR值
下面开始计算不同的阈值下,TP的个数与FP的个数
首先,不考虑阈值为0,因为阈值为0时,所有样本都是正样本,此时TPR与FPR均为1 。当阈值为最小值0.2时,0.2对应的样本被分为负样本,其他被分为正样本,此时TP个数为s_ TPR _target 中[1:]的和,时TP个数为s_ FPR _target 中[2:]的和。根据这个方法,可得如下程序
for k in range(n-1):
# k表示第k个阈值(不包含0和1)
TP = s_ TPR _targe[k+1:].sum()
FP = s_ FPR _targe[k:].sum()
TPR [k] = TP / float(len(x1))
FPR [k] = FP / float(len(x2))
# 补充阈值为0的情况
CCR[n] = 0.0
FPR[n] = 0.0
3、开集问题中ROC曲线的绘制
开集识别是一个特殊的二分类问题,这里把测试集中的已知类看作正样本,未知类看作负样本。认为某一阈值下,低于阈值的样本为开集。
开集识别与普通二分类问题的区别在于,开集识别不仅仅要把已知类识别为已知类,还要把已知类正确分类,才认为分类正确。所以有人提出了OSCR这一指标来对AUROC进行补充。
开集识别中,需要对 TPR _targe进行纠正,将即使识别为已知类,也不能正确分类的样本的值定义为0。即TPR不会受到此样本的影响。
4、OSCR计算代码
def compute_oscr(pred_k, pred_u, labels):
# pred_k:已知类别经过Softmax得到的置信度 N*C,N为样本数,C为已知类的类别数
# pred_u:未知类别经过Softmax得到的置信度 M*C,M为样本数,C为已知类的类别数
# labels:已知类别标签 N*1,N为样本数
# x1,x2 最大置信度 N*1,M*1
x1, x2 = np.max(pred_k, axis=1), np.max(pred_u, axis=1)
# pred_k中最大置信度对应的类别
pred = np.argmax(pred_k, axis=1)
# pred_k中预测正确的样本
correct = (pred == labels)
m_x1 = np.zeros(len(x1))
m_x1[pred == labels] = 1
#注意,不同点在于这里
k_target = np.concatenate((m_x1, np.zeros(len(x2))), axis=0)
u_target = np.concatenate((np.zeros(len(x1)), np.ones(len(x2))), axis=0)
predict = np.concatenate((x1, x2), axis=0)
n = len(predict)
# Cutoffs are of prediction values
CCR = [0 for x in range(n+1)]
FPR = [0 for x in range(n+1)]
idx = predict.argsort()
s_k_target = k_target[idx]
s_u_target = u_target[idx]
for k in range(n-1):
CC = s_k_target[k+1:].sum()
FP = s_u_target[k:].sum()
# True Positive Rate
CCR[k] = float(CC) / float(len(x1))
# False Positive Rate
FPR[k] = float(FP) / float(len(x2))
CCR[n] = 0.0
FPR[n] = 0.0
# Positions of ROC curve (FPR, TPR)
ROC = sorted(zip(FPR, CCR), reverse=True)
OSCR = 0
# Compute AUROC Using Trapezoidal Rule
for j in range(n):
h = ROC[j][0] - ROC[j+1][0]
w = (ROC[j][1] + ROC[j+1][1]) / 2.0
OSCR = OSCR + h*w
return OSCR