模型評估 | 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

Tags: