1 分類
分類是將事物按特性進行分類,例如將手寫數字圖片分類為對應的數字。
1.1 MINIST數字圖片集分類
MINST就是一個70000張規格較小的手寫數字圖片,如何將他們分類為對應的數字?MINIST這個數據集是由矩陣數組結構,70000個矩陣,每個矩陣28*28=784,每個點代表一個像素值,取值范圍在0-256之間。
(1)獲取數據集
Scikit-Learn 提供了許多輔助函數,以便於下載流行的數據集。
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.])}
data是數據集,target是目標數字,也就是每個圖片矩陣代表的真實數字。
(2)獲取其中一個圖片數據轉換后畫圖顯示
x, y = mnist["data"], mnist["target"] #獲取數據集中的數據
%matplotlib inline#聲明使用jupyter的后端渲染圖片
import matplotlib import matplotlib.pyplot as plt #引入畫圖
some_digit = X[36000] #取第36000個數據,是一個784的長形數組
some_digit_image = some_digit.reshape(28, 28) #轉換為28*28的矩陣
plt.imshow(some_digit_image, cmap = matplotlib.cm.binary, interpolation="nearest") plt.axis("off") plt.show()#畫出矩陣圖片顯示數字
(3)准備數據訓練
分成了一個訓練集(前 60000 張圖片)和一個測試集(最后 10000 張圖片),冒號在前表示取前60000之前的數據,冒號在后表示取60000之后的數據。
X_train, X_test, y_train, y_test = X[:60000], X[60000:], y[:60000],y[60000:]
打亂訓練集,洗牌,避免不均勻分布。
import numpy as np
shuffle_index = np.random.permutation(60000) X_train, y_train = X_train[shuffle_index], y_train[shuffle_index]
1.2 二分類器訓練
二分類器就是是和否的分類,比較簡單。
(1)首先獲取訓練集和測試集中的為5的數據子集
y_train_5 = (y_train == 5) # True for all 5s, False for all other
digits. y_test_5 = (y_test == 5)
(2)用Scikit-Learn 的 SGDClassifier分類器去訓練。這個分類器能夠高效地處理非常大的數據 集。可以一次只處理一條數據,適合在線學習。
from sklearn.linear_model import SGDClassifier #引入
sgd_clf = SGDClassifier(random_state=42) #創建分類器對象
sgd_clf.fit(X_train, y_train_5)#訓練
sgd_clf.predict([some_digit]) #測試
array([ True], dtype=bool)#測試結果正確
1.3 性能評估
1.3.1 使用交叉驗證評估性能
讓我們使用 cross_val_score() 函數來評估 SGDClassifier 模型,同時使用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]
1.3.2 混淆矩陣
(1)混淆矩陣的定義
混淆矩陣是指列舉出判斷錯誤和判斷正確的次數的矩陣。例如上面的二分器,判斷為非5的正確的和錯誤,已經判斷為5的正確的和錯誤的。得到一個2*2的矩陣。為了計算混淆矩陣,首先你需要有一系列的預測值,這樣才能將預測值與真實值做比較。
(2)獲取預測值
from sklearn.model_selection import cross_val_predict y_train_pred = cross_val_predict(sgd_clf, X_train, y_train_5, cv=3)
cross_val_predict() 也使用 K折交叉驗證。返回基於每一個測試折做出的一個預測值。
(3)根據預測值和實際值生成混淆矩陣
在使用 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]])
53272表示判斷為正確的非5(true negative),1307表示判斷為錯誤的非5(false negative)。1077表示判斷為錯誤的5(false positive),4344表示判斷為正確的5(true positive)。
(4)准確率和召回率定義
准確率(precision):true positive/( true positive+ false positive)=4344/(1077+4344)
召回率(recall):也叫做敏 感度(sensitivity)或者真正例率(true positive rate, TPR)
recall=ture positive/(true postive+false negative)=4344/(4344+1307)
(1) 准確率和召回率函數計算
Scikit-Learn 提供了一些函數去計算分類器的指標,包括准確率和召回率。
>>> from sklearn.metrics import precision_score, recall_score
>>> precision_score(y_train_5, y_pred) # == 4344 / (4344 + 1307) 0.76871350203503808
>>> recall_score(y_train_5, y_train_pred) # == 4344 / (4344 + 1077) 0.79136690647482011
(2) 調和平均值F1
常結合准確率和召回率來綜合的判斷算法的准確性。還需要綜合實際應用場景,綜合考慮采用哪種作為准確性的判斷的需要。當准確性和召回率都高的時候,調和平均值才會高。
1.3.3 准確性和召回率的折中
(1)准確性和召回率不能同時提升?
通常情況下提高准確性會降低召回率,反之亦然,即他們是互斥的。為什么准確性和召回率不能同時提升?按照公式將false positive和false negative值都減小不就可以同時提高准確性和召回率,理想情況下,false positive和false negative都為0,准確性和召回率都為100%,為什么不能同時提高呢?
precision=true positive/( true positive+ false positive)=4344/(1077+4344)
recall=ture positive/(true postive+false negative)=4344/(4344+1307)
(2)原因分析
如下圖所示, SGDClassifier分類器會對分析的數據進行評分,然后按照分數從小到大排列,設定一個閾值,大於閾值時判定為positive,小於閾值判定為negative。閾值越大准確性越高,但是同時有些是5的數據被判定到negative中,變成false negative,雖然准確性達到100%,但是false negative的數量也增加了,召回率變成50%。
(3)獲取單個數據評分值
Scikit-Learn 不讓你直接設置閾值,但是它給你提供了獲取決策分數的方法decision_function()。這個方法返回每一個樣例的分數值,你自己設置閾值去預測。
>>>some_digit = X[36000]#取一個數據
>>> y_score = sgd_clf.decision_function([some_digit])#獲取這個數據的評分值
>>> y_scores #顯示評分值
array([ 161855.74572176])
>>> threshold = 0 #設置閾值為0
>>> y_some_digit_pred=(y_scores>threshold) #布爾值賦值顯示y_score大於0所以為true
array([ True], dtype=bool)
>>> threshold = 200000#重新設置閾值200000
>>> y_some_digit_pred =(y_score >threshold) # y_score小於閾值
>>> y_some_digit_pred array([False], dtype=bool)#得出false
(1) 獲取所有數據的評分值
用cross_val_predict()交叉預測方法,設置method="decision_function",得到的不是預測值,而是得到每一個樣例的分數值。
y_scores = cross_val_predict(sgd_clf, X_train, y_train_5, cv=3, method="decision_function")
(5)根據評分調用函數precision_recall_curve計算准確率和召回率
現在有了這些分數值。對於任何可能的閾值,使用 precision_recall_curve() ,你都可以計算 准確率和召回率:
from sklearn.metrics import precision_recall_curve
precisions, recalls, thresholds = precision_recall_curve(y_train_5, y_scores)
(6)定義畫圖函數畫出准確率和召回率相對於不同閾值的曲線
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") plt.xlabel("Threshold")
plt.legend(loc="upper left")
plt.ylim([0, 1])
plot_precision_recall_vs_threshold(precisions, recalls, thresholds) plt.show()
你可以畫出准確率和召回率的曲線圖,在曲線圖上根據實際項目應用確定准確率和召回率。
1.3.4 ROC曲線
(1)定義
ROC(Receiver Operating Characteristic)曲線是橫坐標為假正率,縱坐標為真正率的曲線圖。假正率是指判為真的當中假的數量,站總的假的數量的比值。真正率是指判為真的當中真的數量占總的真的數量的比值,和召回率意義一樣。還用之前的例子,如下圖所示,共有12個數字,其中6個是真5,其他6個是假5。左一位置處,真正率為6/6=100%,假正率為2/6的33.33%。中間位置,真正率為4/6=66.66%,假正率為1/6=16.67%。右側位置處,真正率為3/6=50%,假正率為0/6=0%。
(2) 曲線下方面積比較分類器性能
roc曲線下面的面積越大,或roc曲線越靠左上角,性能越好,測量ROC曲線下的面積(AUC)評估分類器的好壞。Scikit-Learn 提供了一個函 數來計算 ROC AUC:
>>> from sklearn.metrics import roc_auc_score
>>> roc_auc_score(y_train_5, y_scores)
0.97061072797174941
(3)畫出曲線圖比較性能
也可以畫出兩個分類器的ROC曲線,看誰更接近左上角。
1)計算SGDClassifier分類器
為了畫出 ROC 曲線,你首先需要計算各種不同閾值下的 TPR、FPR,使用 roc_curve() 函 數:
from sklearn.metrics import roc_curve
fpr, tpr, thresholds = roc_curve(y_train_5, y_scores)
2)計算RandomForestClassifier的TPR和FPR
RandomForestClassifier 不提供 decision_function() 方法。相反,它提供 predict_proba()方法,返回一個數組,每一行代表一個數字記錄,每一列代表所屬於的類別,數組中數值含義是這一行這個數是這一列這個類的概率。例如數字判斷中,每一列是“是5”和“非5”,每一行是一個圖片記錄,數組中的值是圖片“是5”和“非5”的概率,把這個概率值作為評分值。
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")
y_scores_forest = y_probas_forest[:, 1] # 是5的概率值作為評分
fpr_forest, tpr_forest, thresholds_forest = roc_curve(y_train_5,y_scores_forest)
3)畫出曲線圖
plt.plot(fpr, tpr, "b:", label="SGD") #SGD的ROC曲線圖
plot_roc_curve(fpr_forest, tpr_forest, "Random Forest") #隨機森林的ROC
plt.legend(loc="bottom right")
plt.show()
如你所見, RandomForestClassifier 的 ROC 曲線比 SGDClassifier 的好得多:它更靠近左上 角。所以,它的 ROC AUC 也會更大。
>>> roc_auc_score(y_train_5, y_scores_forest)
0.99312433660038291
1.4 多分類器
1.4.1 定義
二分類器只能區分兩個類,而多類分類器(也被叫做多項式分類器)可以區分多於兩個類。如隨機森林分類器或者朴素貝葉斯分類器可以直接處理多類分類。(比如 SVM 分類器或者線性分類器)則是嚴格的二分類器。
一對所有(ONE VS ALL):例如訓練0~9共10個分類器,你想對某張 圖片進行分類的時候,讓每一個分類器對這個圖片進行分類,選出決策分數最高的那個分類 器。
一對1=一(ONE VS ONE)0,1之間,0,2之間……,共N(N-1)/2分類器,當你想對一張圖片進行分類,你必須將這張圖片跑在全部45個二分類器上。然后 看哪個類勝出。
1.4.2 SGDClassifier實現多分類
>>> sgd_clf.fit(X_train, y_train) # 訓練
>>> sgd_clf.predict([some_digit])#預測輸出是 array([ 5.])
>>> 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]])#獲取評分
>>> np.argmax(some_digit_scores) #輸出評分最大的結果是5
1.4.3 OneVsOneClassifier 類 或者OneVsRestClassifier類
>>> 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
1.4.4 RandomForestClassifier多分類
隨機森林分類器能夠直接將一個樣例 分到多個類別。你可以調用 predict_proba() ,得到樣例對應的類別的概率值的列表。
>>> forest_clf.fit(X_train, y_train)
>>> forest_clf.predict([some_digit])
array([ 5.])
>>> forest_clf.predict_proba([some_digit]) array([[ 0.1, 0. , 0. , 0.1, 0. , 0.8, 0. , 0. , 0. , 0. ]])
1.5 誤差分析
探索方案,嘗試多種模型,選擇最好的模型,用GridSearchCV調試超參數優化模型。要使用 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()
主對角線上意味着被分類 正確。數字 5 對應的格子看起來比其他數字要暗淡許多。數據集當中數字 5 的圖片 比較少,又或者是分類器對於數字 5的表現不如其他數字那么好。
將混淆矩陣的每一個值除以相應類別的 圖片的總數目。這樣子,你可以比較錯誤率,而不是絕對的錯誤數(這對大的類別不公平)。用 0 來填充對角線正確的分類。最后得到的就是錯誤率的矩陣和圖像。
row_sums = conf_mx.sum(axis=1, keepdims=True)
norm_conf_mx = conf_mx / row_sums
np.fill_diagonal(norm_conf_mx, 0)
plt.matshow(norm_conf_mx, cmap=plt.cm.gray)
plt.show()
第 8、9 列相當亮,這告訴你許多圖片被誤分成數字 8 或者數字 9,第一行很暗,這意味着大部分的數字 1 被正確分類。分析混淆矩陣通常可以給你提供深刻的見解去改善你的分類器。回顧這幅圖,看樣子你應該 努力改善分類器在數字 8 和數字 9 上的表現,和糾正 3/5 的混淆
1.6 多標簽分類KNeighborsClassifier
(1)分類實現
讓你的分類器給 一個樣例輸出多個類別。例如一個人臉識別分類器,這個分類 器被訓練成識別三個人臉,Alice,Bob,Charlie;然后當它被輸入一張含有 Alice 和 Bob 的 圖片,它應該輸出 [1, 0, 1] 。(意思是:Alice 是,Bob 不是,Charlie 是)。這種輸出多個二 值標簽的分類系統被叫做多標簽分類系統。例如要對數字進行分類,兩個要求(1)大於等於7;(2)是奇數。
from sklearn.neighbors import KNeighborsClassifier #引入分類器
y_train_large = (y_train >= 7)#大於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)#進行分類訓練
>>> knn_clf.predict([some_digit]) #輸出5的預測值
array([[False, True]], dtype=bool)#結果為小於7,是奇數
(2)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" 。
1.7 多輸出分類
是多標簽分類的簡單泛化,在這里每一個標簽可以是多類別的(比如說,它可以有多於兩個可能值)。例如去除噪音的系統,輸入是含有噪音的圖片,輸出是一個干凈的數字圖片,由像素強度數組表示,一個像素是一個標簽,每個標簽的取值是 0~255。標簽的取值多范圍就是多輸出分類。
我們先給訓練數據加上噪音,然后再進行降噪處理。
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
knn_clf.fit(X_train_mod, y_train_mod) #訓練KNeighborsClassifier分類器
clean_digit = knn_clf.predict([X_test_mod[some_index]]) #輸出降噪預測值
plot_digit(clean_digit)
自己開發了一個股票智能分析軟件,功能很強大,需要的點擊下面的鏈接獲取: