接收机工作特性曲线揭秘(Python语言)
在数据科学中,评价模型性能是非常重要的,最常用的性能指标是分类分数。然而,当处理严重的类不平衡的欺诈数据集时,分类分数没有多大意义。相反,接收机工作特性或ROC曲线提供了更好的选择。ROC是针对噪声(假阳性率)的信号图(真阳性率)。模型性能是通过观察ROC曲线下的面积来决定的。最好的AUC是1,最差的是0.5(45度随机线)。任何小于0.5的值都意味着我们可以做与模型建议相反的事情,使值回到0.5以上。
虽然ROC曲线很常见,但是并没有那么多的教学资源来解释它是如何计算和推导出来的。在这篇文章中,我将逐步揭示如何使用Python绘制ROC曲线。然后,我将解释一个基本ROC曲线的特征。
类的概率分布
首先,让我们假设我们的假设模型产生了一些概率来预测每个记录的类别。和大多数二元欺诈模型一样,我们假设我们的类是“good”和“bad”,模型产生P(X=“bad”)的概率。为了创建这个概率分布,我们绘制了一个高斯分布,每个类的均值不同。Python实现如下:
import numpy as np
import matplotlib.pyplot as plt
def pdf(x, std, mean):
cons = 1.0 / np.sqrt(2*np.pi*(std**2))
pdf_normal_dist = const*np.exp(-(x-mean)**2)/2.0*(std**2))
return pdf_normal_dist
x = np.linspace(0, 1, num=100)
good_pdf = pdf(x,0.1,0.4)
bad_pdf = pdf(x,0.1,0.6)
现在我们有了分布,让我们创建一个Python函数来绘制分布
def plot_pdf(good_pdf, bad_pdf, ax):
ax.fill(x, good, "g", alpha=0.5)
ax.fill(x, bad,"r", alpha=0.5)
ax.set_xlim([0,1])
ax.set_ylim([0,5])
ax.set_title("Probability Distribution", fontsize=14)
ax.set_ylabel('Counts', fontsize=12)
ax.set_xlabel('P(X="bad")', fontsize=12)
ax.legend(["good","bad"])
现在让我们使用这个plot_pdf函数来生成图:
fig,ax = plt.subplots(1,1,figsize =(10,5))
plot_pdf(good,bad,ax)
现在我们有二元类的概率分布,现在我们可以使用这个分布来推导ROC曲线。
推导ROC曲线
为了从概率分布中得到ROC曲线,我们需要计算真阳性率(TPR)和假阳性率(FPR)。对于一个简单的例子,我们假设阈值为P(X= ' bad ')=0.6。
真正的阳性是在阈值的右边指定为“bad”的区域。假阳性表示在阈值右侧被指定为“good”的区域。Total positive是“bad”曲线下的总面积,Total negative是“good”曲线下的总面积。我们将图中所示的值进行划分,得到TPR和FPR。推导了不同阈值的TPR和FPR,得到了ROC曲线。利用这些知识,我们创建了ROC plot函数:
def plot_roc(good_pdf, bad_pdf, ax):
#Total
total_bad = np.sum(bad_pdf)
total_good = np.sum(good_pdf)
#Cumulative sum
cum_TP = 0
cum_FP = 0
#TPR and FPR list initialization
TPR_list=[]
FPR_list=[]
#Iteratre through all values of x
for i in range(len(x)):
#We are only interested in non-zero values of bad
if bad_pdf[i]>0:
cum_TP+=bad_pdf[len(x)-1-i]
cum_FP+=good_pdf[len(x)-1-i]
FPR=cum_FP/total_good
TPR=cum_TP/total_bad
TPR_list.append(TPR)
FPR_list.append(FPR)
#Calculating AUC, taking the 100 timesteps into account
auc=np.sum(TPR_list)/100
#Plotting final ROC curve
ax.plot(FPR_list, TPR_list)
ax.plot(x,x, "--")
ax.set_xlim([0,1])
ax.set_ylim([0,1])
ax.set_title("ROC Curve", fontsize=14)
ax.set_ylabel('TPR', fontsize=12)
ax.set_xlabel('FPR', fontsize=12)
ax.grid()
ax.legend(["AUC=%.3f"%auc])
现在让我们使用这个plot_roc函数来生成图
fig, ax = plt.subplots(1,1, figsize=(10,5))
plot_roc(good_pdf, bad_pdf, ax)
现在将概率分布和ROC绘制在彼此旁边以进行视觉比较:
fig,ax = plt.subplots(1,2,figsize =(10,5))
plot_pdf(good_pdf,bad_pdf,ax [0])
plot_roc(good_pdf,bad_pdf,ax [1])
plt.tight_layout()
分类的影响
现在我们可以得出两个图,让我们看看随着类分离(即模型性能)的改善,ROC曲线如何变化。我们通过改变概率分布中高斯的平均值来做到这一点。
x = np.linspace(0, 1, num=100)
fig, ax = plt.subplots(3,2, figsize=(10,12))
means_tuples = [(0.5,0.5),(0.4,0.6),(0.3,0.7)]
i=0
for good_mean, bad_mean in means_tuples:
good_pdf = pdf(x,0.1,good_mean)
bad_pdf = pdf(x,0.1,bad_mean)
plot_pdf(good_pdf, bad_pdf, ax[i,0])
plot_roc(good_pdf, bad_pdf, ax[i,1])
i+=1
plt.tight_layout()
正如你所看到的,随着我们增加类之间的分离,AUC增加。
展望超越AUC
除AUC之外,ROC曲线还可以帮助调试模型。通过观察ROC曲线的形状,我们可以评估模型是错误分类的。例如,如果曲线的左下角更接近随机线,则意味着该模型在X = 0时被错误分类。而如果它是右上角的随机数,则意味着错误发生在X = 1处。另外,如果曲线上存在尖峰(而不是平滑),则意味着模型不稳定。