1. 程式人生 > >Model selection and evaluation

Model selection and evaluation

查準率、查全率與F1

對於二分類問題,可將樣例根據其真實類別與學習器預測類別的組合劃分為

  • 真正例(true positive) :真的正樣本,本來就是正樣本
  • 假正例(false positive) :假的正樣本,其實是負樣本
  • 真反倒(true negative) :真的負樣本,本來就是負樣本
  • 假反例(false negative) 假的負樣本,其實是正樣本

顯然有TP+FP+TN+FN=樣例總數.上述4種情況可以理解為: 以預測結果的角度對每個樣本的判決產生看法

  • 查準率/正確率P(確保預測為正的樣本中有更多樣本是正確的) :P = \frac{TP}{TP+FP},分類正確的正樣本/所有判斷為正的樣本總數
  • 查全率/召回率R(確保所有正樣本中有更多樣本被正確分類
    ):R=\frac{TP}{TP+FN},分類正確的正樣本/資料集中的正樣本

查準率和查全率是一對矛盾的度量.一般來說,查準率高時,查全率往往偏低;而查全率高時,查準率往往偏低.

例如,若希望將好瓜儘可能多地選出來,則可通過增加選瓜的數量來實現,如果將所有西瓜都選上,那麼所有的好瓜也必然都被選上了,但這樣查準率就會較低;若希望選出的瓜中好瓜比例儘可能高,則可只挑選最有把握的瓜, 但這樣就難免會漏掉不少好瓜,使得查全率較低.通常只有在一些簡單任務中才可能使查全率和查準率都很高.

P-R 曲線

在很多情形下我們可根據學習器的預測結果對樣例進行排序,排在前面的是學習器認為"最可能"是正例的樣本,排在最後的則是學習器認為"最不可能"是正例的樣本.通過置信度就可以對所有樣本進行排序,再逐個樣本的選擇閾值,在該樣本之前的都屬於正例,該樣本之後的都屬於負例。每一個樣本作為劃分閾值時,都可以計算對應的P和R.

  • 若一個學習器的P-R 曲線被另一個學習器的曲線完全"包住" , 則可斷言後者的效能優於前者,中學習器A 的效能優於學習器C;
  • 兩個學習器的P-R 曲線發生了交叉,
  1. "平衡點"一個度量,它是" 查準率=查全率"時的取值,越大效能越好,所以學習器A 優於B
  2. F1度量:F1 = \frac{2\times P \times R}{P+R}=\frac{2\times TP}{N +TP-TN},N為樣例總數,

在一些應用中,對查準率和查全率的重視程度有所不同.例如在商品推薦系統中,為了儘可能少打擾使用者,更希望推薦內容確是使用者感興趣的,此時查準率更重要;而在逃犯資訊檢索系統中,更希望儘可能少漏掉逃犯,此時查全率更重要. F1 度量的一般形式F_{\beta }能讓我們表達出對查準率/查全率的不同偏好,

F_{\beta } = \frac{(1+\beta^{2})\times P \times R}{(\beta^{2}\times P)+R}

\beta = 1退化為標準的F1; \beta > 1時查全率有更大影響; \beta < 1

時查準率有更大影響.

ROC 與AUC

根據上一小節,在不同的應用任務中,我們可根據任務需求來採用不同的截斷點,例如若我們更重視"查準率",則可選擇排序中靠前的位置進行截斷;若更重視"查全率",則可選擇靠後的位置進行截斷.因此,排序本身的質量好壞,體現了綜合考慮學習器在不同任務下的"期望泛化效能"的好壞,或者說"一般情況下"泛化效能的好壞. ROC 曲線則是從這個角度出發來研究學習器泛化效能的有力工具.

  • 縱軸是"真正例率" (True Positive Rate,TPR) TPR = \frac{TP}{TP+FN}.所有正樣本中被正確分類的比例
  • 橫軸是"假正例率" (False PositiveRate,FPR)FPR = \frac{FP}{TN+FP}.所有負樣本中被錯誤分類的比例

  • 若一個學習器的ROC曲線被另一個學習器的曲線完全"包住" , 則可斷言後者的效能優於前者.
  • 兩個學習器的P-R 曲線發生了交叉, AUC度量,AUC = \frac{1}{2}\sum_{i=1}^{m-1}(x_{i+1}-x_{i})\cdot (y_{i}+y_{i+1})
import numpy as np
import matplotlib.pylab as plt
from sklearn.datasets import make_hastie_10_2
from sklearn.ensemble import AdaBoostClassifier

def plotROC(predStrengths, classLabels):
    """
    每遇到一個+1標籤,沿著y軸下降一個步長,降低真正例率;
    每遇到一個其他標籤,沿著x軸倒退一個步長,降低假正例率;
    :param predStrengths:
    :param classLabels:
    :return:
    """
    cursor = (1.0, 1.0)                                 # 遊標位置
    ySum = 0.0                                          # 計算AUC的變數
    numPositiveClass = sum(np.array(classLabels) == 1.0)
    yStep = 1 / float(numPositiveClass)                 # 確定了y軸步長
    xStep = 1 / float(len(classLabels) - numPositiveClass)# 確定了y軸步長
    #陣列值從小到大的索引值
    sortedIndicies = predStrengths.argsort()             #從小到大順序排列,從(1.0,1.0)開始畫一直到(0,0)
    fig = plt.figure()
    fig.clf()
    ax = plt.subplot(111)
    # loop through all the values, drawing a line segment at each point
    for index in sortedIndicies.tolist():
        if classLabels[index] == 1.0:
            delX = 0
            delY = yStep
        else:
            delX = xStep
            delY = 0
            ySum += cursor[1]
        # draw line from cursor to (cursor[0]-delX,cursor[1]-delY)
        ax.plot([cursor[0], cursor[0] - delX], [cursor[1], cursor[1] - delY], c='b')
        cursor = (cursor[0] - delX, cursor[1] - delY)
    ax.plot([0, 1], [0, 1], 'b--')
    plt.xlabel('False positive rate');
    plt.ylabel('True positive rate')
    plt.title('ROC cursorve for AdaBoost horse colic detection system')
    ax.axis([0, 1, 0, 1])
    plt.show()
    # 每個小矩形相加,矩形的寬度為xStep,因此對矩形的高度進行相加得到ySum
    print("the Area Under the cursorve is: ", ySum * xStep)

if __name__ == "__main__":
    X, y = make_hastie_10_2(n_samples=4000, random_state=1)
    X_test, y_test = X[2000:], y[2000:]
    X_train, y_train = X[:2000], y[:2000]
    clf = AdaBoostClassifier(n_estimators=100)
    clf.fit(X_train,y_train)
    preds = clf.predict_proba(X_test)
    plotROC(preds[:,1],y_test)