ROC曲线有意义吗?

时间:2019-03-01 16:41:47

标签: python deep-learning statistics roc auc

此代码返回并根据预测值和真实值绘制真实阳性率,错误阳性率,真实阳性计数,错误阳性计数:

def get_all_stats(y_true , y_pred) : 

    def perf_measure(y_true, y_pred):

        TP = 0
        FP = 0
        TN = 0
        FN = 0

        for i in range(len(y_true)): 
            if y_true[i] == 1 and y_pred[i] == 1:
                TP += 1
            if y_pred[i]==1 and y_true[i]!=y_pred[i]:
                FP += 1
            if y_true[i]== 0 and y_pred[i]==0:
                TN += 1
            if y_pred[i]==0 and y_true[i] != y_pred[i]:
                FN += 1

        if(FP == 0) : 
            FPR = 0;
        else : 
            FPR = FP / (FP + TN)

        if(TP == 0) : 
            TPR = 0
        else : 
            TPR = TP / (TP + FN)

        return(TN , FPR, FN , TPR , TP , FP)

    tn, fpr, fn, tpr, tp , fp = perf_measure(y_true, y_pred)

    return tpr , fpr , tp , fp

tpr1 , fpr1 , tp1 , fp1 = get_all_stats(y_true=[1,1,1] , y_pred=[1,0,0])
tpr2 , fpr2 , tp2 , fp2 = get_all_stats(y_true=[1,0,1] , y_pred=[0,1,0])
tpr3 , fpr3 , tp3 , fp3 = get_all_stats(y_true=[0,0,0] , y_pred=[1,0,0])

plt.figure(figsize=(12,6))
plt.tick_params(labelsize=12)

print(tpr1 , fpr1 , tp1 , fp1)
print(tpr2 , fpr2 , tp2 , fp2)
print(tpr3 , fpr3 , tp3 , fp3)

plt.plot([fpr1,fpr2,fpr3], [tpr1 , tpr2, tpr3], color='blue', label='')
plt.ylabel("TPR",fontsize=16)
plt.xlabel("FPR",fontsize=16)
plt.legend()

生成的结果ROC图为:

enter image description here

为了模拟三种不同的假阳性率和真阳性率,并且不同的阈值通过使用不同的函数三次实现get_all_stats来计算这些值

tpr1 , fpr1 , tp1 , fp1 = get_all_stats(y_true=[1,1,1] , y_pred=[1,0,0])
tpr2 , fpr2 , tp2 , fp2 = get_all_stats(y_true=[1,0,1] , y_pred=[0,1,0])
tpr3 , fpr3 , tp3 , fp3 = get_all_stats(y_true=[0,0,0] , y_pred=[1,0,0])

有9个实例的真实值分别为1或0:[1,1,1,1,0,1,0,0,0]

在阈值1处,预测值为[1,0,0],而在此阈值处的真实值为[1,1,1]

在阈值2处,预测值为[0,1,0],而在此阈值处的真实值为[1,0,1]

在阈值3处,预测值为[1,0,0],而在此阈值处的真实值为[0,0,0]

可以看到,生成的分类器图与“典型” ROC曲线不同:

enter image description here

当它首先下降时,假阳性率和真阳性率降低,从而导致线“向后移动”。我是否正确实施了ROC曲线?可以为该曲线计算AUC吗?

1 个答案:

答案 0 :(得分:2)

好的,因为您有很多代表,所以很乐于提供帮助->帮助了很多其他人。我们走了。

此ROC曲线没有意义。问题在于,您仅在不同阈值的数据子集上计算FPR / TPR。在每个阈值处,您应该使用所有数据全部来计算FPR和TPR。因此,您的绘图中似乎有3点,但是对于y_true = [1,1,1,1,0,1,0,0,0]y_pred = [1,0,0,0,1,0,1,0,0],您在FPR / TPR中只应得到1点。但是,为了确保您具有实际的ROC曲线,您也不能只在不同的阈值处组成y_pred值-这些值必须来自实际的预测概率,然后将其适当地阈值化。我有点修改了您的代码,因为我喜欢使用numpy;这是计算ROC曲线的方法。

# start with the true labels, as you did
y_true = np.array([1, 1, 1, 1, 0, 1, 0, 0, 0])
# and a predicted probability of each being a "1"
# I just used random numbers for these, but you would get them
# from your classifier
predictions = np.array([
    0.07485627, 0.72546085, 0.60287482,
    0.90537829, 0.75789236, 0.01852192,
    0.85425979, 0.36881312, 0.63893516
])

# now define a set of thresholds (the more thresholds, the better
# the curve will look). There's a smarter way to do this in practice
# (you can sort the predicted probabilities and just have one threshold
# between each), but this is just to help with understanding
thresholds = np.linspace(0, 1, 11) # 0.1, 0.2, ..., 1.0

fprs = []
tprs = []

# we can precompute which inputs are actually 1s/0s and how many of each
true_1_idx = np.where(y_true == 1)[0]
true_0_idx = np.where(y_true == 0)[0]
n_true_1 = len(true_1_idx)
n_true_0 = len(true_0_idx)

for threshold in thresholds:
    # now, for each threshold, we use that on the underlying probabilities
    # to get the actual predicted classes
    pred_classes = predictions >= threshold
    # and compute FPR/TPR from those
    tprs.append((pred_classes[true_1_idx] == 1).sum() / n_true_1)
    fprs.append((pred_classes[true_0_idx] == 1).sum() / n_true_0)

plt.figure(figsize=(12,6))
plt.tick_params(labelsize=12)

plt.plot(fprs, tprs, color='blue')
plt.ylabel("TPR",fontsize=16)
plt.xlabel("FPR",fontsize=16)

enter image description here

请注意,随着FPR(x轴)的增加,ROC曲线在TPR(y轴)中始终不会减少;也就是说,当您向右移动时,它会上升。从阈值的工作原理可以清楚地看出。在阈值0时,所有预测均为“ 1”,因此我们的FPR = TPR =1。增大阈值将得到较少的预测“ 1”,因此FPR和TPR只能保持不变或减小。

请注意,即使我们使用最佳阈值,由于我们有有限的数据量,曲线上仍然会有跳跃,因此我们可以通过任何阈值获得有限数量的不同TPR / FPR对。但是,如果您有足够的数据,那么看起来就很平滑。在这里,我在上面的代码中替换了几行以得到更平滑的图:

n_points = 1000
y_true = np.random.randint(0, 2, size=n_points)
predictions = np.random.random(n_points)

thresholds = np.linspace(0, 1, 1000)

enter image description here

如果不清楚,则AUC为0.5可能是最坏的情况,您可以看到这就是我们通过随机“预测”得到的结果。如果您的AUC小于0.5,则可以将每个预测都大于0.5(并且您的模型/训练可能有问题)。

如果您实际上想在实践中绘制ROC曲线,而不仅仅是自己写一点,以学习更多,请使用sklearn的roc_curve。他们也有roc_auc_score为您获得AUC。