機器學習實戰_分類(二)

咱們訓練一個RandomForestClassifier,而後拿它的的ROC曲線和ROC AUC數值去跟SGDClassifier的比較。首先你須要獲得訓練集每一個樣例的數值可是因爲隨機森林分類器的工做方式,RandomForestClassifier不提供decision_function()方法。相反,它提供了predict_proba()方法。Skikit-Learn分類器一般兩者中的一個。predict_proba()方法返回一個數組,數組的每一行表明一個樣例,每一列表明一個類。數組當中的值的意思是:給定一個樣例屬於給定類的機率。好比,70%的機率這幅圖是數字 5。git

from sklearn.ensemble import RandomForestClassifier
forest_clf = RandomForestClassifier(random_state=42)
y_probas_forest = cross_val_predict(forest_clf, X_train, y_train_5, cv=3,
                                    method="predict_proba")

可是要畫 ROC 曲線,你須要的是樣例的分數,而不是機率。一個簡單的解決方法是使用正例的機率看成樣例的分數。算法

y_scores_forest = y_probas_forest[:, 1] # score = proba of positive class 預測爲正例機率
fpr_forest, tpr_forest, thresholds_forest = roc_curve(y_train_5,y_scores_forest)

如今你即將獲得 ROC 曲線。將前面一個分類器的 ROC 曲線一併畫出來是頗有用的,能夠清楚地進行比較數組

plt.plot(fpr, tpr, "b:", label="SGD")
plot_roc_curve(fpr_forest, tpr_forest, "Random Forest")
plt.legend(loc="bottom right")
plt.show()

clipboard.png

如你所見,RandomForestClassifier的 ROC 曲線比SGDClassifier的好得多:它更靠近左上角。因此,它的 ROC AUC 也會更大。dom

>>> roc_auc_score(y_train_5, y_scores_forest)
0.99312433660038291

如今你知道如何訓練一個二分類器,選擇合適的標準,使用交叉驗證去評估你的分類器,選擇知足你須要的準確率/召回率折衷方案,和比較不一樣模型的 ROC 曲線和 ROC AUC 數值。如今讓咱們檢測更多的數字,而不只僅是一個數字 5。函數

多類別分類

一些算法(好比隨機森林分類器或者樸素貝葉斯分類器)能夠直接處理多類分類問題。其餘一些算法(好比 SVM 分類器或者線性分類器)則是嚴格的二分類器。而後,有許多策略可讓你用二分類器去執行多類分類。測試

  • 一個方法是:訓練10個二分類器,每個對應一個數字(探測器 0,探測器 1,探測器 2,以此類推)。而後當你想對某張圖片進行分類的時候,讓每個分類器對這個圖片進行分類,選出決策分數最高的那個分類器(One vs all 裏面分數最高的One)。這叫作「一對全部」(OvA)策略
  • 另外一個策略是對每一對數字都訓練一個二分類器:一個分類器用來處理數字 0 和數字 1,一個用來處理數字 0 和數字 2,一個用來處理數字 1 和 2,以此類推。這叫作「一對一」(OvO)策略。若是有 N 個類。你須要訓練N*(N-1)/2個分類器。

一些算法(好比 SVM 分類器)在訓練集的大小上很難擴展,因此對於這些算法,OvO 是比較好的,由於它能夠在小的數據集上面能夠更多地訓練,較之於巨大的數據集而言。可是,對於大部分的二分類器來講,OvA 是更好的選擇。Scikit-Learn 能夠探測出你想使用一個二分類器去完成多分類的任務,它會自動地執行 OvA(除了 SVM 分類器,它使用 OvO)讓咱們試一下SGDClassifier.spa

>>> sgd_clf.fit(X_train, y_train) # y_train, not y_train_5
>>> sgd_clf.predict([some_digit])
array([ 5.])

上面的代碼在訓練集上訓練了一個SGDClassifier。這個分類器處理原始的目標class,從 0 到 9(y_train),而不是僅僅探測是否爲 5 (y_train_5)。而後它作出一個判斷(在這個案例下只有一個正確的數字)。在幕後,Scikit-Learn 實際上訓練了 10 個二分類器,每一個分類器都產到一張圖片的決策數值,選擇數值最高的那個類rest

爲了證實這是真實的,你能夠調用decision_function()方法。不是返回每一個樣例的一個數值,而是返回 10 個數值,一個數值對應於一個類code

>>> some_digit_scores = sgd_clf.decision_function([some_digit])
>>> some_digit_scores
array([[-311402.62954431, -363517.28355739, -446449.5306454 ,
        -183226.61023518, -414337.15339485, 161855.74572176,
        -452576.39616343, -471957.14962573, -518542.33997148,
        -536774.63961222]])

最高數值是對應於類別 5 :orm

>>> np.argmax(some_digit_scores)    # 找最大值的索引
5
>>> sgd_clf.classes_
array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9.])
>>> sgd_clf.classes[5]    # 用索引匹配類別
5.0
一個分類器被訓練好了以後,它會保存目標類別列表到它的屬性classes_ 中去,按照值排序。在本例子當中,在classes_ 數組當中的每一個類的索引方便地匹配了類自己,好比,索引爲 5 的類剛好是類別 5 自己。但一般不會這麼幸運。

若是你想強制 Scikit-Learn 使用 OvO 策略或者 OvA 策略,你可使用OneVsOneClassifier類或者OneVsRestClassifier類。建立一個樣例,傳遞一個二分類器給它的構造函數。舉例子,下面的代碼會建立一個多類分類器,使用 OvO 策略,基於SGDClassifier。

>>> from sklearn.multiclass import OneVsOneClassifier
>>> ovo_clf = OneVsOneClassifier(SGDClassifier(random_state=42))
>>> ovo_clf.fit(X_train, y_train)
>>> ovo_clf.predict([some_digit])
array([ 5.])
>>> len(ovo_clf.estimators_)
45

訓練一個RandomForestClassifier一樣簡單:

>>> forest_clf.fit(X_train, y_train)
>>> forest_clf.predict([some_digit])
array([ 5.])

此次 Scikit-Learn 沒有必要去運行 OvO 或者 OvA,由於隨機森林分類器可以直接將一個樣例分到多個類別。你能夠調用predict_proba(),獲得樣例對應的類別的機率值的列表

>>> forest_clf.predict_proba([some_digit])
array([[ 0.1, 0. , 0. , 0.1, 0. , 0.8, 0. , 0. , 0. , 0. ]])

你能夠看到這個分類器至關確信它的預測:在數組的索引 5 上的 0.8,意味着這個模型以 80% 的機率估算這張圖片表明數字 5。它也認爲這個圖片多是數字 0 或者數字 3,分別都是 10% 的概率。

如今固然你想評估這些分類器。像日常同樣,你想使用交叉驗證。讓咱們用cross_val_score()來評估SGDClassifier的精度。

>>> cross_val_score(sgd_clf, X_train, y_train, cv=3, scoring="accuracy")
array([ 0.84063187, 0.84899245, 0.86652998])

在全部測試折(test fold)上,它有 84% 的精度。若是你是用一個隨機的分類器,你將會獲得 10% 的正確率。因此這不是一個壞的分數,可是你能夠作的更好。舉例子,簡單將輸入正則化,將會提升精度到 90% 以上。

>>> from sklearn.preprocessing import StandardScaler
>>> scaler = StandardScaler()
>>> X_train_scaled = scaler.fit_transform(X_train.astype(np.float64))    # 特徵正則化,沒說用哪一種
>>> cross_val_score(sgd_clf, X_train_scaled, y_train, cv=3, scoring="accuracy")
array([ 0.91011798, 0.90874544, 0.906636 ])

偏差分析:

首先,你能夠檢查混淆矩陣。你須要使用cross_val_predict()作出預測,而後調用confusion_matrix()函數,像你早前作的那樣。

>>> y_train_pred = cross_val_predict(sgd_clf, X_train_scaled, y_train, cv=3)
>>> conf_mx = confusion_matrix(y_train, y_train_pred)
>>> conf_mx
array([[5725, 3, 24, 9, 10, 49, 50, 10, 39, 4],
        [ 2, 6493, 43, 25, 7, 40, 5, 10, 109, 8],
        [ 51, 41, 5321, 104, 89, 26, 87, 60, 166, 13],
        [ 47, 46, 141, 5342, 1, 231, 40, 50, 141, 92],
        [ 19, 29, 41, 10, 5366, 9, 56, 37, 86, 189],
        [ 73, 45, 36, 193, 64, 4582, 111, 30, 193, 94],
        [ 29, 34, 44, 2, 42, 85, 5627, 10, 45, 0],
        [ 25, 24, 74, 32, 54, 12, 6, 5787, 15, 236],
        [ 52, 161, 73, 156, 10, 163, 61, 25, 5027, 123],
        [ 43, 35, 26, 92, 178, 28, 2, 223, 82, 5240]])

這裏是一對數字。使用 Matplotlib 的matshow()函數,將混淆矩陣以圖像的方式呈現,將會更加方便

plt.matshow(conf_mx, cmap=plt.cm.gray)    # #灰度圖,對應位置的值越大色塊越亮
plt.show()

clipboard.png

這個混淆矩陣看起來至關好,由於大多數的圖片在主對角線上。在主對角線上意味着被分類正確。數字 5 對應的格子看起來比其餘數字要暗淡許多。這多是數據集當中數字 5 的圖片比較少,又或者是分類器對於數字 5 的表現不如其餘數字那麼好。你能夠驗證兩種狀況.

讓咱們關注僅包含偏差數據的圖像呈現。首先你須要將混淆矩陣的每個值除以相應類別的圖片的總數目。這樣子,你能夠比較錯誤率,而不是絕對的錯誤數(這對大的類別不公平)

row_sums = conf_mx.sum(axis=1, keepdims=True)
norm_conf_mx = conf_mx / row_sums

如今讓咱們用 0 來填充對角線。這樣子就只保留了被錯誤分類的數據。讓咱們畫出這個結果。(此時數值爲錯誤率)

np.fill_diagonal(norm_conf_mx, 0)
plt.matshow(norm_conf_mx, cmap=plt.cm.gray)
plt.show()

clipboard.png

如今你能夠清楚看出分類器製造出來的各種偏差。記住:行表明實際類別,列表明預測的類別。第 八、9 列至關亮,這告訴你許多圖片被誤分紅數字 8 或者數字 9。類似的,第 八、9 行也至關亮,告訴你數字 八、數字 9 常常被誤覺得是其餘數字。相反,一些行至關黑,好比第一行:這意味着大部分的數字 1 被正確分類(一些被誤分類爲數字 8 )。留意到偏差圖不是嚴格對稱的。舉例子,比起將數字 8 誤分類爲數字 5 的數量,有更多的數字 5 被誤分類爲數字 8。

分析混淆矩陣一般能夠給你提供深入的看法去改善你的分類器。回顧這幅圖,看樣子你應該努力改善分類器在數字 8 和數字 9 上的表現,和糾正 3/5 的混淆。舉例子,你能夠嘗試去收集更多的數據,或者你能夠構造新的、有助於分類器的特徵。舉例子,寫一個算法去數閉合的環(好比,數字 8 有兩個環,數字 6 有一個, 5 沒有)。又或者你能夠預處理圖片(好比,使用 Scikit-Learn,Pillow, OpenCV)去構造一個模式,好比閉合的環。

分析獨特的偏差,是得到關於你的分類器是如何工做及其爲何失敗的洞見的一個好途徑。可是這相對難和耗時。舉例子,咱們能夠畫出數字 3 和 5 的例子

cl_a, cl_b = 3, 5
X_aa = X_train[(y_train == cl_a) & (y_train_pred == cl_a)]
X_ab = X_train[(y_train == cl_a) & (y_train_pred == cl_b)]
X_ba = X_train[(y_train == cl_b) & (y_train_pred == cl_a)]
X_bb = X_train[(y_train == cl_b) & (y_train_pred == cl_b)]
plt.figure(figsize=(8,8))
plt.subplot(221); plot_digits(X_aa[:25], ../images_per_row=5)
plt.subplot(222); plot_digits(X_ab[:25], ../images_per_row=5)
plt.subplot(223); plot_digits(X_ba[:25], ../images_per_row=5)
plt.subplot(224); plot_digits(X_bb[:25], ../images_per_row=5)
plt.show()

clipboard.png

左邊兩個5*5的塊將數字識別爲 3,右邊的將數字識別爲 5。一些被分類器錯誤分類的數字(好比左下角和右上角的塊)是書寫地至關差,甚至讓人類分類都會以爲很困難(好比第 8 行第 1 列的數字 5,看起來很是像數字 3 )。可是,大部分被誤分類的數字,在咱們看來都是顯而易見的錯誤。很難明白爲何分類器會分錯。緣由是咱們使用的簡單的SGDClassifier,這是一個線性模型。它所作的所有工做就是分配一個類權重給每個像素,而後當它看到一張新的圖片,它就將加權的像素強度相加,每一個類獲得一個新的值。因此,由於 3 和 5 只有一小部分的像素有差別,這個模型很容易混淆它們。

3 和 5 之間的主要差別是鏈接頂部的線和底部的線的細線的位置。若是你畫一個 3,鏈接處稍微向左偏移,分類器極可能將它分類成 5。反之亦然。換一個說法,這個分類器對於圖片的位移和旋轉至關敏感。因此,減輕 3/5 混淆的一個方法是對圖片進行預處理,確保它們都很好地中心化和不過分旋轉。這一樣極可能幫助減輕其餘類型的錯誤

多標籤分類

先看一個簡單點的例子,僅僅是爲了闡明的目的

from sklearn.neighbors import KNeighborsClassifier
y_train_large = (y_train >= 7)
y_train_odd = (y_train % 2 == 1)
y_multilabel = np.c_[y_train_large, y_train_odd]
knn_clf = KNeighborsClassifier()
knn_clf.fit(X_train, y_multilabel)

這段代碼創造了一個y_multilabel數組,裏面包含兩個目標標籤。第一個標籤指出這個數字是否爲大數字(7,8 或者 9),第二個標籤指出這個數字是不是奇數接下來幾行代碼會建立一個KNeighborsClassifier樣例(它支持多標籤分類,但不是全部分類器均可以),而後咱們使用多目標數組來訓練它。如今你能夠生成一個預測,而後它輸出兩個標籤:

>>> knn_clf.predict([some_digit])
array([[False, True]], dtype=bool)

它工做正確。數字 5 不是大數(False),同時是一個奇數(True)

有許多方法去評估一個多標籤分類器,和選擇正確的量度標準,這取決於你的項目。舉個例子,一個方法是對每一個個體標籤去量度 F1 值(或者前面討論過的其餘任意的二分類器的量度標準),而後計算平均值。下面的代碼計算所有標籤的平均 F1 值:

>>> y_train_knn_pred = cross_val_predict(knn_clf, X_train, y_train, cv=3)
>>> f1_score(y_train, y_train_knn_pred, average="macro")
0.96845540180280221

這裏假設全部標籤有着同等的重要性,但可能不是這樣。特別是,若是你的 Alice 的照片比 Bob 或者 Charlie 更多的時候,也許你想讓分類器在 Alice 的照片上具備更大的權重。一個簡單的選項是:給每個標籤的權重等於它的支持度(好比,那個標籤的樣例的數目)。爲了作到這點,簡單地在上面代碼中設置average="weighted"。

多輸出分類

咱們即將討論的最後一種分類任務被叫作「多輸出-多類分類」(或者簡稱爲多輸出分類)。它是多標籤分類的簡單泛化,在這裏每個標籤能夠是多類別的(好比說,它能夠有多於兩個可能值)。

爲了說明這點,咱們創建一個系統,它能夠去除圖片當中的噪音。它將一張混有噪音的圖片做爲輸入,期待它輸出一張乾淨的數字圖片,用一個像素強度的數組表示,就像 MNIST 圖片那樣。注意到這個分類器的輸出是多標籤的(一個像素一個標籤)和每一個標籤能夠有多個值(像素強度取值範圍從 0 到 255)。因此它是一個多輸出分類系統的例子。

分類與迴歸之間的界限是模糊的,好比這個例子。按理說,預測一個像素的強度更相似於一個迴歸任務,而不是一個分類任務。並且,多輸出系統不限於分類任務。你甚至可讓你一個系統給每個樣例都輸出多個標籤,包括類標籤和值標籤。

讓咱們從 MNIST 的圖片建立訓練集和測試集開始,而後給圖片的像素強度添加噪聲,這裏是用 NumPy 的randint()函數。目標圖像是原始圖像。

noise = rnd.randint(0, 100, (len(X_train), 784))
noise = rnd.randint(0, 100, (len(X_test), 784))
X_train_mod = X_train + noise
X_test_mod = X_test + noise
y_train_mod = X_train
y_test_mod = X_test

讓咱們看一下測試集當中的一張圖片(是的,咱們在窺探測試集,因此你應該立刻鄒眉):

clipboard.png

左邊的加噪聲的輸入圖片。右邊是乾淨的目標圖片。如今咱們訓練分類器,讓它清潔這張圖片:

knn_clf.fit(X_train_mod, y_train_mod)
clean_digit = knn_clf.predict([X_test_mod[some_index]])
plot_digit(clean_digit)

clipboard.png

分類內容就這麼多。

相關文章
相關標籤/搜索