模型评估 | Python实现ROC

  • 2019 年 12 月 25 日
  • 筆記

1

01

ROC曲线的计算方法

首先从混合矩阵说起,混合矩阵是真实类别与预测类别的交叉表,其中矩阵的行表示真实值,矩阵的列表示预测值,二分类问题的混合矩阵如下:

TP是“真正”的数量,即真实值为正例,预测结果也为正例的数量;FP是“假正”的数量,即真实值为负例,预测结果却为正例的数量;同理,TN和FN分别代表“真负”和“假负”的数量。然后定义两个比率,真正率和假正率:

(1)真正率TPR(True Postive Rate),亦叫灵敏度(Sensitivity):TPR=TP/(TP+FN),即模型预测为正例中真实正例占所有正例的比例。

(2)假正率FPR(False Postive Rate),亦叫1-特异度(1-Specificity):FPR=FP/(FP+TN),即模型预测为正例中真实负例占所有负例的比例。

对模型得到的预测概率或评分设置不同的阈值,就会得到一系列值对(FPR, TPR)。比如设置预测概率的阈值为0.7,概率>=0.7的预测为正例,概率<0.7的预测为负例,对应可以计算出一组(FPR, TPR);然后不断调整阈值,就会得到多组(FPR, TPR);想象一下,阈值从最大值逐渐减少到最小值的过程:当阈值为最大(概率=1)时,则有(FPR, TPR)=(0, 0),随着阈值逐渐减小,预测为正例的样本逐渐增多,预测为正例中的“真正”和“假正”也逐渐增多,所以FPR和TPR逐渐增大,当阈值为最小(概率=0)时,则有(FPR, TPR)=(1, 1);最后在坐标轴上将FPR作为横坐标,TPR为纵坐标,画出的曲线即为ROC曲线;ROC曲线下方区域的面积,被称为AUC值,AUC值越大,表示模型区分度越好。

计算AUC值,也就是ROC曲线下方的面积,可以采用积分的思想,将ROC曲线下方的区域,分割成若干个小区域,用相应梯形的面积近似每个小区域的面积,将所有梯形的面积相加就得到了AUC值的近似值,分割的数量越大,越精确,若分割的数量趋向无穷,则梯形面积的和收敛于真实的面积值,如下面所示:

图中的黑色虚线对角线是随机情况下的ROC曲线,对应的AUC值为0.5;红色虚线是理想情况下的ROC曲线,此时模型具有完美的区分度,对应的AUC值为1;现实情况下的ROC曲线介于两者之间,AUC值一般大于0.5而小于1.

1

02

代码实现

Python实现如下,其中preds是预测结果,可以为预测概率或评分;labels是目标变量,取值0或1;n表示在计算AUC值时切分的数量;asc表示数据是否升序排列,当preds为评分时升序,当preds为概率时降序。

def PlotROC(preds, labels, n, asc):        # preds is score: asc=1      # preds is prob: asc=0        pred = preds  # 预测值      bad = labels  # 取1为bad, 0为good      acds = DataFrame({'bad': bad, 'pred': pred}, columns=['pred', 'bad'])      acds['good'] = 1 - acds.bad        if asc==1:          acds1 = acds.sort_values(by='pred', ascending=True)      elif asc==0:          acds1 = acds.sort_values(by='pred', ascending=False)        acds1.index = range(len(acds1.pred))        N_1 = sum(acds1.bad)      N_0 = sum(acds1.good)        qe = list(np.arange(0, 1, 1.0/n))      qe.append(1)      qe = qe[1:]        ac_index = Series(acds1.index)      ac_index = ac_index.quantile(q = qe)      ac_index = np.ceil(ac_index).astype(int)      ac_index = list(ac_index)        FPR = [0]      TPR = [0]      area = [0]      for i in ac_index:          temp_df = acds1.loc[acds1.index <= i]          FPR = FPR + [1.0*sum(temp_df.good)/N_0]          TPR = TPR + [1.0*sum(temp_df.bad)/N_1]          area = area + [(TPR[-1] + TPR[-2]) * (FPR[-1] - FPR[-2])/2]        AUC = round(sum(area), 4)      AUCdf = DataFrame({'FPR': FPR, 'TPR': TPR, 'area': area})        # chart      plt.plot(FPR, TPR, color='blue', linestyle='-', linewidth=2)      plt.plot([0, 1], [0, 1], color='gray', linestyle='--', linewidth=2)      plt.xlabel('FPR')      plt.ylabel('TPR')      plt.title('AUC=%s' %AUC, fontsize=15)        return AUCdf

运行代码,设置n=10,n越大AUC值越精确:

AUC_df = PlotROC(predict, y, 10, 0)

ROC曲线:

AUC_df数据框存放FPR、TPR以及对应每个分割区域的近似面积area:

area之和是AUC值,如下:

AUC_df.area.sum()  Out: 0.8059249074652213