機器學習系列(二十九)——精準率precision和召回率recall

本篇主要內容:混淆矩陣(Confusion Matrix) 、精準率(Precision)、召回率(Recall )

準確率的陷阱

在之前我們都是用分類準確度來評價分類算法的好壞,但其實用準確率來評價分類算法的好壞是存在很大問題的。
準確率的問題
現在有一個癌症預測系統,輸入你的體檢信息,來判斷是否有癌症,這個系統能達到99.9%的預測準確率,那這是一個好系統嗎?不少人會覺得99.9%的準確率當然是一個好系統,實際真的是這樣嗎?想象一種情況,如果這種癌症本身的發病率只有0.01%,那這就意味着,根本不需要任何機器學習算法,無論是什麼樣的體檢信息,都預測他是健康的不會患癌症,這樣肯定有99.99%的準確率,而現在的系統只有99.9%的準確率還不如沒有機器學習的準確率高,說明這個系統是失敗的。
這就是用分類準確度來衡量一個分類系統性能好壞的問題所在。這樣的問題發生在數據是極度偏斜(Skewed Data)的情況下,即不同類別的數量差異巨大,這樣的情況下,只使用分類準確度是遠遠不夠的。於是有了混淆矩陣,在混淆矩陣上我們可以得到比分類準確度還要好的分類性能評價指標。


混淆矩陣Confusion Matrix

什麼是混淆矩陣呢?以二分類問題爲例,對於二分類問題,混淆矩陣是一個2×2的矩陣:

真實\預測 0 1
0 預測negative正確
TN
預測positive錯誤
FP
1 預測negative錯誤
FN
預測positive正確
TP

混淆矩陣中行代表真實值,列代表預測值,0-Negative,1-Positive,1往往是我們比較關注的類別。
比如現在有10000個人,預測患癌症的情況,1代表患癌症,0代表未患癌症,相應的混淆矩陣:

真實\預測 0 1
0 9978 12
1 2 8

該矩陣表示有9978個人實際沒有患癌症預測結果也是沒有患癌症,有12個人實際沒有患癌症但預測結果是患癌症;有2個人患了癌症預測結果是沒患,有8個人患了癌症預測結果也是患癌症。

精準率與召回率

基於混淆矩陣,我們有兩個新的衡量分類算法性能的指標——精準率和召回率。精準率precision的計算公式爲:
precision=\frac{TP}{TP+FP}

精準率是我們預測我們關注的事件,它相應的有多準確。對於上面癌症預測的例子,相應的精準率計算爲:\frac{8}{8+12}=40\%它表示預測結果爲1中預測正確(實際也爲1)的概率,在有偏數據中,1通常是我們比較關注的對象,因此叫精準率,比如癌症預測中,癌症是我們真正關注的對象。本例精準率的含義是我們每做出100次患病的預測,有40次是準確的。
和精準率相對應的召回率recall的計算公式爲:
recall=\frac{TP}{FN+TP}

召回率是我們關注的事件真實的發生了,在真實發生的我們關注的事件中,我們成功預測了多少
對於癌症預測的例子,相應的召回率計算爲:\frac{8}{8+2}=80\%含義是每有100個癌症患者,通過現在的機器學習算法,我們能成功的找出其中的80個患者。
爲什麼說精準率和召回率是比準確率要優秀的評價指標呢,仍然考慮最初的例子,有10000個人,癌症病發率爲0.01%,現在有一個算法,不管怎樣都預測他是健康的,這樣的準確率是99.99%,但是儘管準確率高,實際是一點幫助都沒有的。相應的精準率精準率和召回率呢?首先給出此時的混淆矩陣:

真實\預測 0 1
0 9999 0
1 1 0

計算得到精準率爲\frac{0}{0+0}是沒有意義的,對於沒有意義的精準率直接定義爲它可能的最小值也就是0。召回率\frac{0}{10+0}=0,可以看到對於我們都知道的沒有意義的算法,準確率能達到99%以上,而精準率和召回率卻都是最低值,這就是精準率和召回率的意義。

實現精準率與召回率

在手寫數字數據集上進行測試,不過由於手寫數字本身是10個類別且每個類別數量是差不多的,這裏我們人工將其變爲兩個類別(標籤等於9爲類1,標籤不等於9的爲類0)以達到skewed data的效果:

import numpy as np
from sklearn import datasets
digits = datasets.load_digits()
X = digits.data
y = digits.target.copy()

'''人工加入偏斜'''
y[digits.target==9]=1
y[digits.target!=9]=0
'''10分類變爲2分類,用Logistic分類'''
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(X,y,random_state=666)
from sklearn.linear_model import LogisticRegression
log_reg = LogisticRegression()
log_reg.fit(X_train,y_train)
log_reg.score(X_test,y_test)

在Logistic迴歸下準確率爲97.5%,不過這並不能說明什麼,因爲不等於9的本身就在全部數據中佔有90%的比例,就算不用機器學習算法全部預測爲類1,準確率也有90%,於是考慮精準率和召回率,定義混淆矩陣計算函數:

'''計算混淆矩陣各項'''
def TN(y_true, y_predict):
    assert len(y_true)==len(y_predict)
    return np.sum((y_true==0)&(y_predict==0))
def FP(y_true,y_predict):
    assert len(y_true)==len(y_predict)
    return np.sum((y_true==0)&(y_predict==1))
def FN(y_true,y_predict):
    assert len(y_true)==len(y_predict)
    return np.sum((y_true==1)&(y_predict==0))
def TP(y_true,y_predict):
    assert len(y_true)==len(y_predict)
    return np.sum((y_true==1)&(y_predict==1))
'''混淆矩陣'''
def confusion_matrix(y_true,y_predict):
    return np.array([
        [TN(y_true,y_predict),FP(y_true,y_predict)],
        [FN(y_true,y_predict),TP(y_true,y_predict)]
    ])
'''Logistic分類的混淆矩陣'''
confusion_matrix(y_test,y_log_predict)

相應的混淆矩陣:

有了混淆矩陣,再定義精準率和召回率計算函數:

'''精準率'''
def precision_score(y_true,y_predict):
    tp = TP(y_true,y_predict)
    fp = FP(y_true,y_predict)
    '''分母爲0'''
    try:
        return tp/(tp+fp)
    except:
        return 0.0
'''召回率'''
def recall_score(y_true,y_predict):
    tp = TP(y_true,y_predict)
    fn = FN(y_true,y_predict)
    '''分母爲0'''
    try:
        return tp/(tp+fn)
    except:
        return 0.0

調用計算函數,得到精準率和召回率爲:

精準率和召回率看起來還是不錯的,在實際中它們有時精準率小有時召回率小,具體如何平衡它們,以及它們如何評價模型將在下篇介紹。
同樣,對於這些操作,我們寫的是模擬sklearn的邏輯的,sklearn中也封裝好了相應的模塊,接下來使用sklearn中的混淆矩陣和準確率召回率計算:

得到了同樣的結果。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章