評估指標與評分(上):二分類指標

朝南煙發表於2022-05-28

精度可以作為度量模型好壞的一個指標,它表示預測正確的樣本數佔所有樣本數的比例。

但是在實踐中,我們通常不僅對精確的預測感興趣,還希望將這些預測結果用於更大的決策過程

1、 二分類指標

我們先看一下測量精度可能會怎麼誤導我們

1.1錯誤型別

⭐精度並不能很好地度量預測效能,因為我們所犯得錯誤並不包括我們感興趣的所有資訊:

例如:有一個自動化測試篩選癌症,如果測試結果為陰性,則認為該患者是健康的,若是陽性則需要進一步篩查。在這裡我們將陽性測試結果稱為正類,陰性結果稱為負類,

於是就有了以下兩種常見的錯誤型別

第一類錯誤:假正例(錯誤的陽性預測,可能導致額外費用)
第二類錯誤:假反例(錯誤的陰性預測,可能使得病人無法及早發現病情,造成嚴重後果)

1.2不平衡資料集

不平衡資料集:一個類別比另一個類別出現次數多很多的資料集

  • 精度無法幫助我們區分:不變的‘未點選’模型潛在的優秀模型

下面將用到:

  • 兩個虛擬分類器:dummy_majority(始終預測多數類),dummy(產生隨機輸出)
  • 兩個常用的分類模型:LogisticRegression,DecissionTree

建立資料集

  #建立一個不平衡資料集

  from sklearn.datasets import load_digits
  import numpy as np

  digits = load_digits()
  y = digits.target==9

  print("類別:{}".format(np.bincount(y)))

  '''
  `類別:[1617  180]`
  '''

建立四個模型

  from sklearn.dummy import DummyClassifier
  from sklearn.model_selection import train_test_split
  from sklearn.linear_model import LogisticRegression
  from sklearn.tree import DecisionTreeClassifier


  X_train,X_test,y_train,y_test = train_test_split(digits.data,y,random_state=0)

  #構建始終預測大多數的模型



  #始終預測多數類
  dummy_majority = DummyClassifier(strategy='most_frequent').fit(X_train,y_train)
  pred_most_frequent = dummy_majority.predict(X_test)

  print("始終預測多數類 Test score:{:.2f}".format(dummy_majority.score(X_test,y_test)))

  #產生隨機輸出
  dummy = DummyClassifier().fit(X_train,y_train)
  pred_dummy = dummy.predict(X_test)
  print("產生隨機輸出 Test score:{:.2f}".format(dummy.score(X_test,y_test)))


  #決策樹
  tree = DecisionTreeClassifier(max_depth=2).fit(X_train,y_train)
  pred_tree = tree.predict(X_test)
  print("決策樹 Test score:{:.2f}".format(tree.score(X_test,y_test)))

  #線性迴歸

  lrg = LogisticRegression(C=0.1).fit(X_train,y_train)
  pred_logreg = lrg.predict(X_test)
  print("線性迴歸 Test score:{:.2f}".format(lrg.score(X_test,y_test)))


  '''
  ```
  始終預測多數類 Test score:0.90
  產生隨機輸出 Test score:0.82
  決策樹 Test score:0.92
  線性迴歸 Test score:0.98
  ```
  '''

?

從上面我們可以發現,想要對這種不平衡的資料集評估效能,精度並不是一種合適的度量

  • 因為竟然連隨機輸出的預測精度都達到了0.81

我們希望有一個指標可以淘汰這些無意義的預測(比如,預測多數類、隨機預測等)

1.3 混淆矩陣

⭐對於二分類問題的評估結果,可以使用:混淆矩陣

  from sklearn.metrics import confusion_matrix

  #檢查上面的LogisticRegression的評估結果

  confusion = confusion_matrix(y_test,pred_logreg)
  print("Confusion metrix:\n{}".format(confusion))

  '''
  ```
  Confusion metrix:
  [[402   1]
   [  6  41]]
  ```
  '''

  #混淆矩陣的含義
  mglearn.plots.plot_confusion_matrix_illustration()

?

混淆矩陣主對角線上的元素對應正確的分類,

而其他元素則告訴我們一個類別中有多少樣本被錯誤地劃分到其他類別中

?

二分類當中

我們將正類中正確分類的樣本稱為真正例(TP),將反類中正確分類的樣本稱為真反例(TN)

TP+FP+TN+FN:樣本總數。
TP+FN:實際正樣本數。
TP+FP:預測結果為正樣本的總數,包括預測正確的和錯誤的。
FP+TN:實際負樣本數。
TN+FN:預測結果為負樣本的總數,包括預測正確的和錯誤的

⭐總結混淆矩陣最常見的方法:準確率和召回率

(1)準確率

⭐表示的是預測為正的樣本中有多少是真正的正樣本

目標:限制假正例的數量

(2)召回率

⭐表示的是樣本中的正例有多少被預測為正類。

目標:找出所有正類樣本,避免假反例

癌症診斷很適合!

(3)f1分數

⭐F1分數(F1 Score),是統計學中用來衡量二分類模型精確度的一種指標。

  • 它同時兼顧了分類模型的精確率和召回率。

  • F1分數可以看作是模型精確率和召回率的一種調和平均,它的最大值是1,最小值是0。

    from sklearn.metrics import f1_score
    
    print("f1 score most frequent:{:.2f}".format(f1_score(y_test,pred_most_frequent)))
    print("f1 score dummy:{:.2f}".format(f1_score(y_test,pred_dummy)))
    print("f1 score tree:{:.2f}".format(f1_score(y_test,pred_tree)))
    print("f1 score logisticregression:{:.2f}".format(f1_score(y_test,pred_logreg)))
    
    '''
    ```
    f1 score most frequent:0.00
    f1 score dummy:0.12
    f1 score tree:0.55
    f1 score logisticregression:0.92
    ```
    '''
    

?
利用f1分數,我們可以總結預測效能,這個分數更加符合我們對好模型的直覺


  #用classification_report同時計算準確率,召回率,f1分數

  from sklearn.metrics import classification_report

  print(classification_report(y_test,pred_most_frequent,target_names=['not nine','nine']))

  '''
  ```
            precision    recall  f1-score   support

      not nine       0.90      1.00      0.94       403
          nine       0.00      0.00      0.00        47

      accuracy                           0.90       450
     macro avg       0.45      0.50      0.47       450
  weighted avg       0.80      0.90      0.85       450
  ```
  '''

(4)考慮不確定性

在sklean中大多數分類器提供了一個decision_fuction或者predict_proba方法來評估預測的不確定度

預測可以被看做是:以某個固定點作為decision_fuction或者predict_proba輸出的閾值,樣本點預測的不確定性超過閾值則被劃分為正類/負類

而且,通過修改閾值我們可以改變模型的準確率和召回率

在二分類問題中:

  • 使用0作為決策函式的閾值
  • 0.5作為predict_proba的閾值

from sklearn.model_selection import train_test_split
from mglearn.datasets import make_blobs
from sklearn.svm import SVC

X,y=make_blobs(n_samples=(400,50), centers=2, cluster_std=(7,2), random_state=22)

X_train, X_test, y_train, y_test=train_test_split(X, y, random_state=22)

svc=SVC(gamma=0.5).fit(X_train, y_train)

mglearn.plots.plot_decision_threshold()

classification_report函式

  #使用classification_report函式來評估兩個類別的準確率與召喚率

  print("Classification report values:")

  print(classification_report(y_test, svc.predict(X_test)))

  '''
  ```
  Classification report values:
                precision    recall  f1-score   support

             0       0.92      0.95      0.94       102
             1       0.38      0.27      0.32        11

      accuracy                           0.88       113
     macro avg       0.65      0.61      0.63       113
  weighted avg       0.87      0.88      0.88       113
  ```
  '''

?

從執行結果得知,對於類別1,我們得到了一個相當低的準確率,
不過類別0的準確率卻是不錯,所以分類器將重點放在類別0分類正確,而不是類別1.

假設我們在應用中,類別1具有高召回率更加重要,如癌症篩查(我們允許自動篩查的時候自動檢查到的為癌症的人通過人工檢查沒有得到癌症,但是不允許本人已經得到癌症了但是自動檢查卻將其漏過,這樣會使得病人錯過最佳治療時期)。這意味著我們更願意冒險有更多的假正例(假的類別為1),以換取更多的真正例(可以增大召回率)。

預設情況下,decision_function值大於0的點將被規劃為類別1,我們希望將更多的點劃為類別1,所以需要減少閾值。對應程式碼如下:

  y_pred_lower_threshold = svc.decision_function(X_test) > -.8

  #我們來看一下這個預測報告:

  print("The new Classification report values:")

  print(classification_report(y_test, y_pred_lower_threshold))

  '''
  ```
  The new Classification report values:
                precision    recall  f1-score   support

             0       0.94      0.87      0.90       102
             1       0.28      0.45      0.34        11

      accuracy                           0.83       113
     macro avg       0.61      0.66      0.62       113
  weighted avg       0.87      0.83      0.85       113
  ```
  '''

?

通過調整後,類別1的召回率增大,準確率降低。
但是得到了更大的空間區域化為類別1.
如果需要的是更高的準確率的話,那麼也通過改變與之的方法得到更好的結果。

(5)準確率-召回率曲線

⭐一旦設定了一個具體目標(比如對某一類別的特定召回率或準確率),就可以適當的設定一個閾值

工作點:對分類器設定要求(比如90%的召回率)

but,在工作中,我們並不完全清楚工作點在哪裡。因此,為了更好地理解建模問題,很有啟發性的做法是:

  • 同時檢視所有完全可能的閾值或者準確率和召回率:
    • 準確率-召回率曲線
    • sklearn.metrics模組(引數:真實標籤,預測的不確定度,後者由decision_function或者predict_proba給出)

  from sklearn.metrics import precision_recall_curve
  import numpy as np
  from matplotlib import pyplot as plt


  #利用返回的準確率和召回率,閾值,我們可以繪製一條曲線
  X,y=make_blobs(n_samples=(4000,500), centers=2, cluster_std=(7,2), random_state=22)

  X_train, X_test, y_train, y_test=train_test_split(X, y, random_state=22)

  svc=SVC(gamma=0.05).fit(X_train, y_train)


  precision,recall,thresholds = precision_recall_curve(y_test,svc.decision_function(X_test))

  close_zero = np.argmin(np.abs(thresholds)) #找到最接近0的閾值

  plt.plot(precision[close_zero],recall[close_zero],'o',markersize=10,label="threshold zero",fillstyle='none',c='k',mew=2)
  plt.plot(precision,recall,label='precision recall curve')
  plt.xlabel("precision")
  plt.ylabel("recall")
  plt.legend()

?

黑色圓圈表示閾值為0的點,0是decision_function的預設值
利用這個模型可以得到約0.5的準確率,同時保持很高的召回率。
曲線左側相對平坦,說明在準確率提高的同時召回率沒有下降很多。

曲線越靠近右上角,則分類器越好(有高recall和高precision)

f1-分數只反映了準確率-召回率曲線上的一個點,即預設閾值對應的那個點。
對於自動化模型對比,我們可能希望總結曲線中包含的資訊,而不限於某個特定的閾值或工作點

總結準確率-召回率曲線的一種方法是:計算該曲線下的積分或面積,也叫做平均準確率

  • average_precision_score函式來計算平均準確率

(6)受試者工作特徵(ROC)與AUC

⭐ROC曲線圖是反映敏感性與特異性之間關係的曲線。
橫座標X軸為 1 – 特異性,也稱為假陽性率(誤報率),X軸越接近零準確率越高;
縱座標Y軸稱為敏感度,也稱為真陽性率(敏感度),Y軸越大代表準確率越好。

根據曲線位置,把整個圖劃分成了兩部分,曲線下方部分的面積被稱為AUC(Area Under Curve),用來表示預測準確性,
AUC值越高,也就是曲線下方面積越大,說明預測準確率越高。
曲線越接近左上角(X越小,Y越大),預測準確率越高。

  from sklearn.metrics import roc_curve
  fpr,tpr,thresholds = roc_curve(y_test,svc.decision_function(X_test))

  #畫圖
  plt.plot(fpr,tpr,label='ROC Curve')
  plt.xlabel("FPR")
  plt.ylabel("TPR(recall)")

  #找到最接近於0的閾值
  close_zero = np.argmin(np.abs(thresholds)) #找到最接近0的閾值
  plt.plot(fpr[close_zero],tpr[close_zero],'o',markersize=10,label="threshold zero",fillstyle='none',c='k',mew=2)
  plt.legend()

?

ROC曲線:理想的曲線要靠近左上角

  • 分類器的召回率很高,同時保證假正率很低

    #用一個數字來總結ROC曲線:曲線下的面積(AUC)
    #roc_auc_score
    
    from sklearn.metrics import roc_auc_score
    
    svc_auc = roc_auc_score(y_test,svc.decision_function(X_test))
    print("AUC for SVC:{:.3f}".format(svc_auc))
    
    '''
    `AUC for SVC:0.936`
    '''
    

對於不平衡的分類問題來說,AUC是一個比精度好得多的指標.

2、參考文獻

《python機器學習基礎教程》

相關文章