前文傳送門:
【翻譯】Sklearn 與 TensorFlow 機器學習實用指南 —— Chapter 0.前言
【翻譯】Sklearn 與 TensorFlow 機器學習實用指南 —— 第1章 機器學習概覽(上)
【翻譯】Sklearn 與 TensorFlow 機器學習實用指南 —— 第1章 機器學習概覽(下)
【翻譯】Sklearn 與 TensorFlow 機器學習實用指南 —— 第2章 一個完整的機器學習項目(上)
【翻譯】Sklearn 與 TensorFlow 機器學習實用指南 —— 第2章 一個完整的機器學習項目(中)
【翻譯】Sklearn 與 TensorFlow 機器學習實用指南 —— 第2章 一個完整的機器學習項目(中二)
【翻譯】Sklearn 與 TensorFlow 機器學習實用指南 —— 第2章 一個完整的機器學習項目(下)
在第一章我們提到過最常用的監督學習任務是回歸(用於預測某個值)和分類(預測某個類別)。在第二章我們探索了一個回歸任務:預測房價。我們使用了多種算法,諸如線性回歸,決策樹,和隨機森林(這個將會在後面的章節更詳細地討論)。現在我們將我們的注意力轉到分類任務上。
MNIST在本章當中,我們將會使用 MNIST 這個數據集,它有著 70000 張規格較小的手寫數字圖片,由美國的高中生和美國人口調查局的職員手寫而成。這相當於機器學習當中的「Hello World」,人們無論什麼時候提出一個新的分類算法,都想知道該算法在這個數據集上的表現如何。機器學習的初學者遲早也會處理 MNIST 這個數據集。
Scikit-Learn 提供了許多輔助函數,以便於下載流行的數據集。MNIST 是其中一個。下面的代碼獲取 MNIST
>>> from sklearn.datasets import fetch_mldata>>> mnist = fetch_mldata('MNIST original')>>> mnist{'COL_NAMES': ['label', 'data'],'DESCR': 'mldata.org dataset: mnist-original','data': array([[0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0], ..., [0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0]], dtype=uint8),'target': array([ 0., 0., 0., ..., 9., 9., 9.])}
一般而言,由 sklearn 加載的數據集有著相似的字典結構,這包括:
讓我們看一下這些數組
>>> X, y = mnist["data"], mnist["target"]>>> X.shape(70000, 784)>>> y.shape(70000,)
MNIST 有 70000 張圖片,每張圖片有 784 個特徵。這是因為每個圖片都是28*28像素的,並且每個像素的值介於 0~255 之間。讓我們看一看數據集的某一個數字。你只需要將某個實例的特徵向量,reshape為28*28的數組,然後使用 Matplotlib 的imshow函數展示出來。
%matplotlib inlineimport matplotlibimport matplotlib.pyplot as pltsome_digit = X[36000]some_digit_image = some_digit.reshape(28, 28)plt.imshow(some_digit_image, cmap = matplotlib.cm.binary, interpolation="nearest")plt.axis("off")plt.show()
這看起來像個 5,實際上它的標籤告訴我們:
>>> y[36000]5.0
圖3-1 展示了一些來自 MNIST 數據集的圖片。當你處理更加複雜的分類任務的時候,它會讓你更有感覺。
先等一下!你總是應該先創建測試集,並且在驗證數據之前先把測試集晾到一邊。MNIST 數據集已經事先被分成了一個訓練集(前 6000 張圖片)和一個測試集(最後 10000 張圖片)
X_train, X_test, y_train, y_test = X[:60000], X[60000:], y[:60000], y[60000:]
讓我們打亂訓練集。這可以保證交叉驗證的每一折都是相似(你不會期待某一折缺少某類數字)。而且,一些學習算法對訓練樣例的順序敏感,當它們在一行當中得到許多相似的樣例,這些算法將會表現得非常差。打亂數據集將保證這種情況不會發生。
import numpy as npshuffle_index = np.random.permutation(60000)X_train, y_train = X_train[shuffle_index], y_train[shuffle_index]
訓練一個二分類器
現在我們簡化一下問題,只嘗試去識別一個數字,比如說,數字 5。這個「數字 5 檢測器」就是一個二分類器,能夠識別兩類別,「是 5」和「非 5」。讓我們為這個分類任務創建目標向量:
y_train_5 = (y_train == 5) # True for all 5s, False for all other digits.y_test_5 = (y_test == 5)
現在讓我們挑選一個分類器去訓練它。用隨機梯度下降分類器 SGD,是一個不錯的開始。使用 Scikit-Learn 的SGDClassifier類。這個分類器有一個好處是能夠高效地處理非常大的數據集。這部分原因在於SGD一次只處理一條數據,這也使得 SGD 適合在線學習(online learning)。我們在稍後會看到它。讓我們創建一個SGDClassifier和在整個數據集上訓練它。
from sklearn.linear_model import SGDClassifiersgd_clf = SGDClassifier(random_state=42)sgd_clf.fit(X_train, y_train_5)
SGDClassifier依賴於訓練集的隨機程度(所以被命名為 stochastic,隨機之義)。如果你想重現結果,你應該固定參數random_state
現在你可以用它來查出數字 5 的圖片。
>>> sgd_clf.predict([some_digit])array([ True], dtype=bool)
分類器猜測這個數字代表 5(True)。看起來在這個例子當中,它猜對了。現在讓我們評估這個模型的性能。
對性能的評估評估一個分類器,通常比評估一個回歸器更加玄學。所以我們將會花大量的篇幅在這個話題上。有許多量度性能的方法,所以拿來一杯咖啡和準備學習許多新概念和首字母縮略詞吧。
使用交叉驗證測量準確性評估一個模型的好方法是使用交叉驗證,就像第二章所做的那樣。
實現交叉驗證
在交叉驗證過程中,有時候你會需要更多的控制權,相較於函數cross_val_score()或者其他相似函數所提供的功能。這種情況下,你可以實現你自己版本的交叉驗證。事實上它相當直接。以下代碼粗略地做了和cross_val_score()相同的事情,並且輸出相同的結果。
from sklearn.model_selection import StratifiedKFoldfrom sklearn.base import cloneskfolds = StratifiedKFold(n_splits=3, random_state=42)for train_index, test_index in skfolds.split(X_train, y_train_5): clone_clf = clone(sgd_clf) X_train_folds = X_train[train_index] y_train_folds = (y_train_5[train_index]) X_test_fold = X_train[test_index] y_test_fold = (y_train_5[test_index]) clone_clf.fit(X_train_folds, y_train_folds) y_pred = clone_clf.predict(X_test_fold) n_correct = sum(y_pred == y_test_fold) print(n_correct / len(y_pred)) # prints 0.9502, 0.96565 and 0.96495
StratifiedKFold類實現了分層採樣(詳見第二章的解釋),生成的折(fold)包含了各類相應比例的樣例。在每一次迭代,上述代碼生成分類器的一個克隆版本,在訓練折(training folds)的克隆版本上進行訓,在測試折(test folds)上進行預測。然後它計算出被正確預測的數目和輸出正確預測的比例。
讓我們使用cross_val_score()函數來評估SGDClassifier模型,同時使用 K 折交叉驗證,此處讓k=3。記住:K 折交叉驗證意味著把訓練集分成 K 折(此處 3 折),然後使用一個模型對其中一折進行預測,對其他折進行訓練。
>>> from sklearn.model_selection import cross_val_score>>> cross_val_score(sgd_clf, X_train, y_train_5, cv=3, scoring="accuracy")array([ 0.9502 , 0.96565, 0.96495]
哇!在交叉驗證上有大於 95% 的精度(accuracy)?這看起來很令人吃驚。先別高興,讓我們來看一個非常笨的分類器去分類,看看其在「非 5」這個類上的表現。
from sklearn.base import BaseEstimatorclass 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.909 , 0.90715, 0.9128 ])
沒錯,這個笨的分類器也有 90% 的精度。這是因為只有 10% 的圖片是數字 5,所以你總是猜測某張圖片不是 5,你也會有90%的可能性是對的。
這證明了為什麼精度通常來說不是一個好的性能度量指標,特別是當你處理有偏差的數據集,比方說其中一些類比其他類頻繁得多。
混淆矩陣對分類器來說,一個好得多的性能評估指標是混淆矩陣。大體思路是:輸出類別A被分類成類別 B 的次數。舉個例子,為了知道分類器將 5 誤分為 3 的次數,你需要查看混淆矩陣的第五行第三列。
為了計算混淆矩陣,首先你需要有一系列的預測值,這樣才能將預測值與真實值做比較。你或許想在測試集上做預測。但是我們現在先不碰它。(記住,只有當你處於項目的尾聲,當你準備上線一個分類器的時候,你才應該使用測試集)。相反,你應該使用cross_val_predict()函數
from sklearn.model_selection import cross_val_predicty_train_pred = cross_val_predict(sgd_clf, X_train, y_train_5, cv=3)
就像 cross_val_score(),cross_val_predict()也使用 K 折交叉驗證。它不是返回一個評估分數,而是返回基於每一個測試折做出的一個預測值。這意味著,對於每一個訓練集的樣例,你得到一個乾淨的預測(「乾淨」是說一個模型在訓練過程當中沒有用到測試集的數據)。
現在使用 confusion_matrix()函數,你將會得到一個混淆矩陣。傳遞目標類(y_train_5)和預測類(y_train_pred)給它。
>>> from sklearn.metrics import confusion_matrix>>> confusion_matrix(y_train_5, y_train_pred)array([[53272, 1307], [ 1077, 4344]])
混淆矩陣中的每一行表示一個實際的類, 而每一列表示一個預測的類。該矩陣的第一行認為「非 5」(反例)中的 53272 張被正確歸類為 「非 5」(他們被稱為真反例,true negatives), 而其餘 1307 被錯誤歸類為"是 5" (假正例,false positives)。第二行認為「是 5」 (正例)中的 1077 被錯誤地歸類為「非 5」(假反例,false negatives),其餘 4344 正確分類為 「是 5」類(真正例,true positives)。一個完美的分類器將只有真反例和真正例,所以混淆矩陣的非零值僅在其主對角線(左上至右下)。
>>> confusion_matrix(y_train_5, y_train_perfect_predictions)array([[54579, 0], [ 0, 5421]])
混淆矩陣可以提供很多信息。有時候你會想要更加簡明的指標。一個有趣的指標是正例預測的精度,也叫做分類器的準確率(precision)。
公式 3-1 準確率
$$precision = \frac{TP}{TP + FP}$$
其中 TP 是真正例的數目,FP 是假正例的數目。
想要一個完美的準確率,一個平凡的方法是構造一個單一正例的預測和確保這個預測是正確的(precision = 1/1 = 100%)。但是這什麼用,因為分類器會忽略所有樣例,除了那一個正例。所以準確率一般會伴隨另一個指標一起使用,這個指標叫做召回率(recall),也叫做敏感度(sensitivity)或者真正例率(true positive rate, TPR)。這是正例被分類器正確探測出的比率。
公式 3-2 Recall
$$recall = \frac{TP}{TP + FN}$$
FN 是假反例的數目。
如果你對於混淆矩陣感到困惑,圖 3-2 將對你有幫助
Python愛好者社區歷史文章大合集:
Python愛好者社區歷史文章列表(每周append更新一次)
福利:文末掃碼立刻關注公眾號,「Python愛好者社區」,開始學習Python課程:
關注後在公眾號內回復「課程」即可獲取:
小編的Python入門視頻課程!!!
崔老師爬蟲實戰案例免費學習視頻。
丘老師數據科學入門指導免費學習視頻。
陳老師數據分析報告製作免費學習視頻。
玩轉大數據分析!Spark2.X+Python 精華實戰課程免費學習視頻。
丘老師Python網絡爬蟲實戰免費學習視頻。