分類問題(三)混淆矩陣,Precision與Recall


混淆矩陣

衡量一個分類器性能的更好的辦法是混淆矩陣。它基於的思想是:計算類別A被分類為類別B的次數。例如在查看分類器將圖片5分類成圖片3時,我們會看混淆矩陣的第5行以及第3列。

為了計算一個混淆矩陣,我們首先需要有一組預測值,之后再可以將它們與標注值(label)進行對比。我們也可以在測試集上做預測,但是最好是先不要動測試集(測試集僅需要在最后的階段使用,在我們有了一個准備上線的分類器后,最后再用測試集測試性能)。接下來,我們可以使用cross_val_predict() 方法:

from sklearn.model_selection import cross_val_predict

y_train_pred
= cross_val_predict(sgd_clf, X_train, y_train_5, cv=3) y_train_pred.shape >(60000,)

 

與cross_val_score() 方法一樣,cross_val_predict() 會執行K-折交叉驗證,但是不會返回評估分數,而是返回在每個測試折上的預測值,加起來就是整個訓練數據集的預測值。現在我們可以使用confusion_matricx() 方法獲取混淆矩陣。直接傳入label數據(y_train_5)以及預測數據(y_train_pred)即可:

from sklearn.metrics import confusion_matrix

confusion_matrix(y_train_5, y_train_pred) >array([[
53892, 687], [ 1891, 3530]])

 

在這個混淆矩陣中,每一行代表一個真實類別,每一列代表一個預測類別。第一行代表的是“非5”(亦稱為negative class)圖片:53892張圖片被分類為“非5“類別(它們亦稱為true negatives)。剩下的687 張圖片被錯誤的分類為”非5“(亦稱為false positives)。第二行代表的是”真5“(亦稱為 positive class):1891張圖片被錯誤地分類為”非5“類別(亦稱為false negatives),剩下的3530 張圖片被正確地分類為”真5“(亦稱為true positives)。一個完美的分類器應該僅包含true positives 以及true negatives,所以它的混淆矩陣應該僅有主對角線上有非0數值,其他值應都為0。例如,假設我們有了以下一個完美的預測:

y_train_perfect_predictions = y_train_5
confusion_matrix(y_train_5, y_train_perfect_predictions)
>array([[54579,     0],
       [    0,  5421]])

 

混淆矩陣可以給我們提供很多信息,但是有時候我們可能需要一個更精准的指標。一個比較好的方式是:查看positive predictions的精准度。它也稱為分類器的精度(precision),它的公式為:

Precision

Precision=TP / (TP + FP)

這里TP 是true positives 的數量,FP 是false positive 的數量。

對於精度,我們仍有辦法去構造一個完整精度。比如假設測試集里全部是數字5,然后模型的邏輯是僅輸出True。這樣就可以構造一個 100% 精度的模型。所以精度(precision)一般與另一個指標一起用,這另一個指標稱為回調(recall),也稱為sensitivity或true positive rate(TPR):它是分類器正確分類positive 條目的比率,公式為:

Recall

TP / (TP+FN)

這里FN是false negatives的數目。

如果對混淆矩陣的這些概念比較模糊的話,可以看看下圖:

 

PrecisionRecall

Sk-learn提供了一些方法用於計算分類器的各個指標,包括精准率(precision)與回調率(recall):

from sklearn.metrics import precision_score, recall_score

print(precision_score(y_train_5, y_train_pred))  #3530/(3530+687)
print(recall_score(y_train_5, y_train_pred))  #3530/(3530+1891)
>0.8370879772350012
 0.6511713705958311

 

從precision與recall來看,這個分類器的表現並不像之前准確度(accuracy)那樣亮眼了。當這個分類器認為某張圖片是數字5時,它僅有83.7% 的概率是正確的。並且它僅識別出了65.1%的數字5圖片。

一般我們還會將precision和recall結合成一個指標:F1分數。特別是在需要使用一個簡單的辦法對比兩個分類器時。F1分數是precision與recall的調和平均數(harmonic mean):

 

如果一個分類器的recall與precision分數都比較高的話,則最終才會得到一個較高的F1分數。其中任意一個recall或是precision比較低的話,F1分數都不會太高。

在sk-learn中,直接調用f1_score() 方法即可計算F1分數:

from sklearn.metrics import f1_score

f1_score(y_train_5, y_train_pred)
>0.7325171197343846

 

F1分數會比較傾向於那些precision值與recall值接近的分類器。不過這個需求並不是在任何場景下都是必須的,在一些場景下,我們可能更關心精准率,而在另一些場景下更關注回調率。例如,如果我們訓練一個分類器,用於為孩子們檢測一些健康的視頻。在這個場景下,我們可能會更傾向於使用:一個可以更精准的判斷視頻是否為健康的視頻(高精准),但是可能會誤殺掉一些健康的視頻(低回調)的分類器。而不是一個有着高回調,但是會讓小部分不健康的視頻通過的分類器。另一方面,假設我們訓練一個判斷監控里小偷的分類器,即使這個分類器只有30%的精准率(precision)也是可以的,只要是它有99%左右的回調率(recall),依然可以達到我們的需求(即使警報可能會響很多次,但是基本都會抓到小偷)。

不過可惜的是,precision與recall無法二者兼得:增加precision會降低recall,反之亦然。這個被稱為精准/回調折中(precision/recall tradeoff)。

 

精准/回調折中(Precision/Recall Tradeoff

為了理解這種折中,我們看一下SGDClassifier是如何做分類決策的。對每條數據,它首先根據決策方法,計算出一個分數,如果此分數大於某個閾值,則將這條數據分類為正類(positive class),反之則分類為負類(negative class)。

下圖是一個例子,低分在左邊,被分為負類,高分在右邊,被分為正類。假設決策閾值的位置在正中間(下圖中間的兩個5之間):我們可以看到閾值右邊有4個true positives(真正為數字5),以及1個false positive (真正為數字6)。所以,在這個閾值下,精准率precision是80%(4/5)。這個集合中一共有6個數字5,但是在這個閾值下,只檢測出了4個,所以召回率recall是67%(4/6)。

現在假設我們升高這個閾值(將箭頭向右移),則之前的那個數字6從false positive 變成了true negative,所以此時false positive 現在是0,precision是100%(3/3)。而之前的數字5 由true positive 變成了false negative,所以召回率recall現在是50%(3/6)。同樣,減少閾值后,召回率會上升,但是精准率precision會下降。

 

Sk-learn並不允許用戶直接設置閾值,但是可以指定一個決策分數,用於做預測。之前我們是調用predict() 方法做預測,現在我們可以先使用 decision_function() 方法,它會返回每條數據的分數。然后我們可以根據這些分數,提供的閾值進行預測:

y_scores = sgd_clf.decision_function([X_test[0], X_test[1], X_test[2], X_train[0]])
y_scores
>array([-8542.1753957 , -4410.49112461, -3416.59592945,  2164.22030239])

threshold = 0
y_demo_digit_pred = (y_scores > threshold)
y_demo_digit_pred
>array([False, False, False,  True])

 

SGDClassifier 使用的是0作為閾值,所以上面的方法返回的結果與直接調用 predict() 的結果是一致的。下面我們可以試着改一下這個閾值:

threshold = 8000
y_demo_digit_pred = (y_scores > threshold) y_demo_digit_pred >array([False, False, False, False])

 

這個也證明了:提高閾值后,recall會下降。最后這張圖片本來是數字5,並且在閾值為0的情況下,分類器可以將它正確識別。但是在閾值升高到8000后,此圖片便被識別為“非數字5”。

現在,我們如何決定使用哪個閾值呢?首先我們需要獲取訓練數據中所有數據的得分。再次使用cross_val_predict() 方法即可,但是這次我們要指定它返回決策分數,而不是做預測:

y_scores = cross_val_predict(sgd_clf, X_train, y_train_5, cv=3, method='decision_function')
y_scores
>array([  1200.93051237, -26883.79202424, -33072.03475406, ...,

        13272.12718981,  -7258.47203373, -16877.50840447])

 

現在有了這些分數,我們可以計算在各種可能的閾值下,precision與recall的值,使用precision_recall_curve()方法:

from sklearn.metrics import precision_recall_curve
precisions, recalls, thresholds = precision_recall_curve(y_train_5, y_scores)

 

最后我們可以畫出precision與recall的函數圖,以threshold為因變量,使用matplotlib:

def plot_precision_recall_vs_threshold(precisions, recalls, thresholds):
    plt.plot(thresholds, precisions[:-1], 'b--', label="Precision")
    plt.plot(thresholds, recalls[:-1], 'g--', label="Recall")

plot_precision_recall_vs_threshold(precisions, recalls, thresholds)
plt.show()

大家可能會好奇,為什么precision的圖相較於recall的圖抖動的更劇烈。這是因為:在閾值上升后,precision可能偶爾會下降(雖然一般它會上升)。為了便於理解,大家可以回看一下之前“精准/回調折中”里的那張圖。假設我們的閾值設在最中間,然后下一個閾值只向右移動了一個單位,則precision從80%(4/5)下降到了75%(3/4)。而另一邊,recall僅會在閾值增加的時候才會下降,所以它的曲線看起來更平滑。

另一個比較好的選擇“精准/回調折中”的辦法是直接畫出precision對應於recall的圖,如:

 

可以看到在大約recall在80% 左右的樣子,precision開始急速下降。所以我們一般會選擇一個它下降前的一個“精准/回調折中“(precision/recall tradeoff),例如在60%的回調左右。不過這個最終的決定取決於我們的項目需求。

下面假設我們定的目標是90%的precision。從第一副圖我們可以得知,閾值大約為8000 左右。為了更精確地獲取這個閾值,我們可以搜索滿足90%精准率的最小閾值(np.argmax() 可以返回第一個最大值,在這個例子中就是第一個True值):

threshold_90_precision = thresholds[np.argmax(precisions >=0.90)]
threshold_90_precision
>3370

 

下一步,做決策。這次我們不再用predict() 方法,而是使用:

y_train_pred_90 = (y_scores >= threshold_90_precision)
y_train_pred_90
>array([False, False, False, ...,  True, False, False])

 

然后我們檢查一下這些預測的precision與recall:

precision_score(y_train_5, y_train_pred_90)
>0.9000345901072293

recall_score(y_train_5, y_train_pred_90)
>0.4799852425751706

現在我們就有了一個90%精准率的分類器!正如你所見,創建一個高精准率的分類器其實很簡單,只需要設置更高的閾值即可。但是,如果一個分類器即使precision很高,而recall很低的話,這個分類器基本沒太大用處。所以如果有人說,他的分類器達到了99%的精准率,那我們可以繼續問問他“recall是多少?”

接下來我們還會繼續介紹另一種性能衡量的辦法: ROC曲線。它是另一種與Precision/Recall 曲線類似的曲線。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM