有監督機器學習模型的評估指標

在本篇文章中,我們使用著名的手寫數字數據集mnist做爲例子.使用sklearn.datasetsfetch_openml()方法可以下載該數據集:

from sklearn.datasets import fetch_openml

# 下載minst數據集,data_home屬性指定存儲數據集的路徑
mnist = fetch_openml('MNIST original', data_home='dataset_home')
X, y = mnist['data'], mnist['target']	# X每一行存儲一張28*28的點陣圖,y爲對應的數字

# 展示一張圖片
some_digit_image = X[36000].reshape(28, 28)
plt.imshow(some_digit_image, cmap=matplotlib.cm.binary, interpolation='nearest')
plt.show()

在這裏插入圖片描述

交叉驗證

在訓練時,我們常使用k折交叉驗證擴充數據集.

我們將給定樣本分爲kk份,每次拿出k1k-1份進行訓練,留下11份用於測試,重複kk次.取每一次誤差的平方和作爲最終的誤差.

在這裏插入圖片描述

使用sklearn.model_selection模塊的cross_val_predict()方法可以使用k折交叉驗證進行預測,cross_val_score()方法可以使用k折交叉驗證對計算某指標.

函數各參數意義:

  • estimator: 待訓練的模型.
  • Xy: 訓練數據.
  • cv: 交叉驗證的折數.
  • scoring: cross_val_score()方法計算的指標.
  • method: cross_val_predict()方法調用的模型的方法.
sgd_clf = SGDClassifier(loss='log')	# 創建一個邏輯迴歸分類器

# 進行3折交叉驗證,評估其準確率
cross_val_score(sgd_clf, X_train, y_train, cv=3, scoring='accuracy')

# 進行3折交叉驗證,調用分類器的predict方法進行預測
cross_val_predict(sgd_clf, X_train, y_train, cv=3, method='predict')

# 進行3折交叉驗證,調用分類器的predict_proba方法得到概率值
cross_val_predict(sgd_clf, X_train, y_train, cv=3, method='predict_proba')

評估指標

混淆矩陣

混淆矩陣是一張分類模型預測結果的情形分析表,其行索引爲列表爲樣本的真實類別,列索引爲樣本的預測結果,表格中每一項表示對應真實類別與預測結果的樣本的數量.

在主對角線上的樣本預測正確,非主對角線上的樣本預測錯誤.
在這裏插入圖片描述
當我們進行二分類時,混淆矩陣退化成爲一個2×2的表格.

預測值
負例(Negative) 正例(Positive)


負例(Negative) 真陰性(True Negative,TN) 假陽性(False Positive,FP)
第一類錯誤
正例(Positive) 假陰性(False Negative,FP)
第二類錯誤
真陽性(True Positive,TP)

調用sklearn.metrics模塊的confusion_matrix(y_true, y_pred)方法可以計算混淆矩陣,其中y_true爲真實值數組,y_pred爲預測值數組.

print(y_true)	# array([False, False, False, ..., False, False, False])
print(y_pred)	# array([False, False, False, ..., False, False, False])

print(confusion_matrix(y_labeled, y_predict)) 
# 得到 array([[61413, 2274], [ 6093, 220]], dtype=int64)

準確率(Accuracy),精確率(Precision),召回率(Recall)

根據二分類的混淆矩陣,我們分別定義三個指標

預測值
負例(Negative) 正例(Positive)


負例(Negative) 真陰性(True Negative,TN) 假陽性(False Positive,FP)
第一類錯誤
正例(Positive) 假陰性(False Negative,FP)
第二類錯誤
真陽性(True Positive,TP)
  1. 準確率(Accuracy): 所有樣本中被正確預測的比例.

    Accuracy=TP+TNTP+FP+TN+FN Accuracy = \frac{TP + TN}{TP + FP + TN + FN}

準確率Accuracy關心的是整體的正確率,在樣本正負例個數不平衡時不是一個好的評估指標.考慮如下情況: 當樣本中絕大多數都是負例時,我們的分類器只要無腦判負即可使得準確率Accuracy很高,但這並不是一個好的模型.

下面例子判斷minst數據集中手寫圖片的數字是否爲5,我們創建一個無腦判負的分類器,並將其和邏輯迴歸模型相對比.

import numpy as np
from sklearn.base import BaseEstimator
from sklearn.datasets import fetch_openml
from sklearn.linear_model import SGDClassifier
from sklearn.model_selection import cross_val_score

# 獲取數據集並打亂原始數據集
mnist = fetch_openml('MNIST original', data_home='dataset_home')
X, y = mnist['data'], mnist['target']
shuffle_index = np.random.permutation(X.shape[0])
X_train, y_train = X[shuffle_index], y[shuffle_index]
y_train_5 = (y_train == 5)


# 創建一個邏輯迴歸分類器並進行交叉驗證
sgd_clf = SGDClassifier(loss='log', max_iter=1000, tol=1e-4)
cross_val_score(sgd_clf, X_train, y_train_5, cv=3, scoring='accuracy')
# 進行三折交叉驗證得到的準確率爲 array([0.96532956, 0.96897099, 0.96018515])
cross_val_score(sgd_clf, X_train, y_train_5, cv=3, scoring='precision')
# 進行三折交叉驗證得到的精確率爲 array([0.95501022, 0.67672576, 0.87068966])
cross_val_score(sgd_clf, X_train, y_train_5, cv=3, scoring='recall')
# 進行三折交叉驗證得到的召回率爲 array([0.89643705, 0.87262357, 0.70722433])


# 創建一個無腦判負的分類器並進行交叉驗證
class Never5Classifier(BaseEstimator):
    def fit(self, X, y=None):
        pass
    def predict(self, X):
        return np.zeros((len(X), 1), dtype=bool)
    
never_5_clf = Never5Classifier()
cross_val_score(never_5_clf, X_train, y_train_5, cv=3, scoring='accuracy')
# 進行三折交叉驗證得到的準確率爲 array([0.91188823, 0.90957014, 0.9079844 ])
cross_val_score(never_5_clf, X_train, y_train_5, cv=3, scoring='precision')
# 進行三折交叉驗證得到的精確率爲 array([0., 0., 0.])
cross_val_score(never_5_clf, X_train, y_train_5, cv=3, scoring='recall')
# 進行三折交叉驗證得到的召回率爲 array([0., 0., 0.])

可以看到,無腦判負的Never5Classifier雖然準確率很高,但是精確率和召回率都很低.

  1. 精確率(Precision): 所有預測爲正例的樣本中被正確預測的比例.

    Precision=TPTP+FP Precision = \frac{TP}{TP + FP}

  2. 召回率(Recall): 所有真實值爲正例的樣本中被正確預測的比例.

    Recall=TPTP+FN Recall = \frac{TP}{TP+FN}

精確率Precision和召回率Recall只關心正例,它們兩者是相互抑制(trade off)的關係,考慮如下情況:

對於一個分類系統,我們將用戶喜歡視爲正例,用戶不喜歡視爲負例,這時我們只關心推薦的商品中用戶喜歡的有多少(TP),因此應考慮精確率Precision和召回率Recall作爲評價指標.

  • 假設用戶實際喜歡的所有商品(TP+FN)有10000個,推薦系統向他推薦(TP+FP)10個商品,用戶最終下單(TP)了3個商品.則此時的精確率Precision爲30%,召回率Recall爲0.003%.
  • 爲了讓用戶多下單(增加TP),我們降低閾值(threshold),向用戶推薦(TP+FP)1000個商品,用戶最終下單(TP)了30個商品.則此時的精確率Precision爲3%,召回率Recall爲0.03%.召回率Recall上升,而精確率Precision卻下降了.(這有點類似於’言多必失’的道理).

在這裏插入圖片描述

調用sklearn.metrics模塊的accuracy_score(y_true, y_pred),precision_score(y_true, y_pred)recall_score(y_true, y_pred)分別可以計算預測結果的準確率,精確率和召回率,其中y_true爲真實值數組,y_pred爲預測值數組.

使用[precisions, recalls, thresholds] = precision_recall_curve(y_true, probas_pred)方法可以生成精確率Precision和召回率Recall曲線數據,示例如下:

# 獲取邏輯迴歸表達式計算正例的概率
y_scores = cross_val_predict(sgd_clf, X_train, y_train, cv=3, method='decision_function')

# 生成 precisions,recalls,thresholds曲線
precisions, recalls, thresholds = precision_recall_curve(y_train, y_scores)

# 繪製 precisions,recalls,thresholds曲線
plt.plot(thresholds, precisions[:-1], 'b--', label='Precision')
plt.plot(thresholds, recalls[:-1], 'r--', label='Recall')
plt.xlabel("Threshold")
plt.legend(loc='upper left')
plt.ylim([0, 1])
plt.show()

F1-score

因爲精確率Precision和召回率Recall是一個此消彼長的關係,因此我們有必要將兩者綜合成一個指標.

F1-Score被定義爲精確率Precision召回率Recall調和平均數:

F1=21Precision+1Recall=2Precision×RecallPrecision+Recall F_1 = \frac{2}{\frac{1}{Precision}+\frac{1}{Recall}} = 2 \cdot \frac{Precision \times Recall}{Precision + Recall}

更一般的,有Fβ-Score,β越大,召回率Recall所佔權重越高.

Fβ=1+β21Precision+β2Recall=(1+β2)Precision×Recallβ2Precision+Recall F_{\beta} = \frac{1+\beta^2}{\frac{1}{Precision} + \frac{\beta^2}{Recall}} = (1+\beta^2) \cdot \frac{Precision \times Recall}{\beta^2 \, Precision+Recall}

調用sklearn.metrics模塊的f1_score(y_true, y_pred)fbeta_score(y_true, y_pred, beta)方法分別可以計算預測結果的F1-Score和Fβ-Score,其中y_true爲真實值數組,y_pred爲預測值數組.

AUC

ROC(receiver operating characteristic)曲線.ROC曲線上每個點反映着對同一信號刺激的感受性,即在不同閾值(threshold)下的負正類率(False Positive Rate,FPR)和真正類率(True Positive Rate,TPR).

  • 真正類率TPR表示所有真實值爲正例的樣本中,被正確預測爲正例的樣本的比例.

    TPR=TPTP+FN TPR = \frac{TP}{TP+FN}

  • 負正類率FPR表示所有真實值爲負例的樣本中,被錯誤預測爲正例的樣本的比例.

    FPR=FPFP+TN FPR = \frac{FP}{FP+TN}

根據定義,易知ROC曲線爲一個過(0,0)(0,0)點和(1,1)(1,1)點的曲線.
在這裏插入圖片描述

AUC(Area under Curve)爲ROC曲線下方的面積,即TPR對FPR積分的結果.容易看出AUC更看重正例.

調用sklearn.metrics模塊的roc_curve(y_true, y_score)方法可以生成ROC曲線數據,調用roc_auc_score(y_true, y_score)方法可以計算AUC值.

# 使用邏輯迴歸分類器計算正例的概率
sgd_clf = SGDClassifier(loss='log', max_iter=1000, tol=1e-4)
y_scores_sgd = cross_val_predict(sgd_clf, X_train, y_train, cv=3, method='decision_function')
fpr_sgd, tpr_sgd, thresholds_sgd = roc_curve(y_train, y_scores)

# 使用隨機森林分類器計算正例的概率
forest_clf = RandomForestClassifier(random_state=42)
y_probas_forest = cross_val_predict(forest_clf, X_train, y_train, cv=3, method='predict_proba')
y_scores_forest = y_probas_forest[:, 1]
fpr_forest, tpr_forest, thresholds_forest = roc_curve(y_train, y_scores_forest)

# 繪製ROC曲線
plt.plot(fpr_sgd, tpr_sgd, 'b:', label='SGD(AUC=%.5f)' % roc_auc_score(y_train, y_scores_sgd))
plt.plot(fpr_forest, tpr_forest, label='Random Forest(AUC=%.5f)' % roc_auc_score(y_train, y_scores_forest))
plt.legend(loc='lower right')
plt.show()

在這裏插入圖片描述

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