在做數據處理時,需要用到不同的手法,如特征標准化,主成分分析,等等會重復用到某些參數,sklearn中提供了管道,可以一次性的解決該問題
先展示先通常的做法
import pandas as pd from sklearn.preprocessing import StandardScaler from sklearn.decomposition import PCA from sklearn.linear_model import LogisticRegression df = pd.read_csv('wdbc.csv') X = df.iloc[:, 2:].values y = df.iloc[:, 1].values # 標准化 sc = StandardScaler() X_train_std = sc.fit_transform(X_train) X_test_std = sc.transform(X_test) # 主成分分析PCA pca = PCA(n_components=2) X_train_pca = pca.fit_transform(X_train_std) X_test_pca = pca.transform(X_test_std) # 邏輯斯蒂回歸預測 lr = LogisticRegression(random_state=1) lr.fit(X_train_pca, y_train) y_pred = lr.predict(X_test_pca)
先對數據標准化,然后做主成分分析降維,最后做回歸預測
現在使用管道
from sklearn.pipeline import Pipeline pipe_lr = Pipeline([('sc', StandardScaler()), ('pca', PCA(n_components=2)), ('lr', LogisticRegression(random_state=1))]) pipe_lr.fit(X_train, y_train) pipe_lr.score(X_test, y_test)
Pipeline對象接收元組構成的列表作為輸入,每個元組第一個值作為變量名,元組第二個元素是sklearn中的transformer或Estimator。
管道中間每一步由sklearn中的transformer構成,最后一步是一個Estimator。我們的例子中,管道包含兩個中間步驟,一個StandardScaler和一個PCA,這倆都是transformer,邏輯斯蒂回歸分類器是Estimator。
當管道pipe_lr執行fit方法時,首先StandardScaler執行fit和transform方法,然后將轉換后的數據輸入給PCA,PCA同樣執行fit和transform方法,最后將數據輸入給LogisticRegression,訓練一個LR模型。
對於管道來說,中間有多少個transformer都可以。工作方式如下
使用管道減少了很多代碼量
現在回歸模型的評估和調參
訓練機器學習模型的關鍵一步是要評估模型的泛化能力。如果我們訓練好模型后,還是用訓練集取評估模型的性能,這顯然是不符合邏輯的。一個模型如果性能不好,要么是因為模型過於復雜導致過擬合(高方差),要么是模型過於簡單導致導致欠擬合(高偏差)。可是用什么方法評價模型的性能呢?這就是這一節要解決的問題,你會學習到兩種交叉驗證計數,holdout交叉驗證和k折交叉驗證, 來評估模型的泛化能力
一、holdout交叉驗證(評估模型性能)
holdout方法很簡單就是將數據集分為訓練集和測試集,前者用於訓練,后者用於評估
如果在模型選擇的過程中,我們始終用測試集來評價模型性能,這實際上也將測試集變相地轉為了訓練集,這時候選擇的最優模型很可能是過擬合的。
更好的holdout方法是將原始訓練集分為三部分:訓練集、驗證集和測試集。訓練機用於訓練不同的模型,驗證集用於模型選擇。而測試集由於在訓練模型和模型選擇這兩步都沒有用到,對於模型來說是未知數據,因此可以用於評估模型的泛化能力。下圖展示了holdout方法的步驟:
缺點:它對數據分割的方式很敏感,如果原始數據集分割不當,這包括訓練集、驗證集和測試集的樣本數比例,以及分割后數據的分布情況是否和原始數據集分布情況相同等等。所以,不同的分割方式可能得到不同的最優模型參數
二、K折交叉驗證(評估模型性能)
k折交叉驗證的過程,第一步我們使用不重復抽樣將原始數據隨機分為k份,第二步 k-1份數據用於模型訓練,剩下那一份數據用於測試模型。然后重復第二步k次,我們就得到了k個模型和他的評估結果(譯者注:為了減小由於數據分割引入的誤差,通常k折交叉驗證要隨機使用不同的划分方法重復p次,常見的有10次10折交叉驗證)
然后我們計算k折交叉驗證結果的平均值作為參數/模型的性能評估。使用k折交叉驗證來尋找最優參數要比holdout方法更穩定。一旦我們找到最優參數,要使用這組參數在原始數據集上訓練模型作為最終的模型。
k折交叉驗證使用不重復采樣,優點是每個樣本只會在訓練集或測試中出現一次,這樣得到的模型評估結果有更低的方法。
下圖演示了10折交叉驗證:
10次10折交叉驗證我的理解是將按十種划分方法,每次將數據隨機分成k分,k-1份訓練,k份測試。獲取十個模型和評估結果,然后取10次的平均值作為性能評估
from sklearn.model_selection import StratifiedKFold
pipe_lr = Pipeline([('sc', StandardScaler()), ('pca', PCA(n_components=2)), ('lr', LogisticRegression(random_state=1))]) pipe_lr.fit(X_train, y_train) kfold = StratifiedKFold(y=y_train, n_folds=10, random_state=1) scores= [] for k, (train, test) in enumerate(kfold): pipe_lr.fit(X_train[train], y_train[train]) score = pipe_lr.score(X_train[test], y_train[test]) scores.append(scores) print('Fold: %s, Class dist.: %s, Acc: %.3f' %(k+1, np.bincount(y_train[train]), score))print('CV accuracy: %.3f +/- %.3f' %(np.mean(scores), np.std(scores)))
更簡單的方法
from sklearn.model_selection import StratifiedKFold pipe_lr = Pipeline([('sc', StandardScaler()), ('pca', PCA(n_components=2)), ('lr', LogisticRegression(random_state=1))]) pipe_lr.fit(X_train, y_train) scores = cross_val_score(estimator=pipe_lr, X=X_train, y=y_train, cv=10, n_jobs=1) print('CV accuracy scores: %s' %scores) print('CV accuracy: %.3f +/- %.3f' %(np.mean(scores), np.std(scores)))
cv即k
三、學習曲線(調試算法)
from sklearn.model_selection import learning_curve pipe_lr = Pipeline([('scl', StandardScaler()), ('clf', LogisticRegression(penalty='l2', random_state=0))]) train_sizes, train_scores, test_scores = learning_curve(estimator=pipe_lr, X=X_train, y=y_train, train_sizes=np.linspace(0.1, 1.0, 10), cv=10, n_jobs=1) train_mean = np.mean(train_scores, axis=1) train_std = np.std(train_scores, axis=1) test_mean = np.mean(test_scores, axis=1) test_std = np.std(test_scores, axis=1) plt.plot(train_sizes, train_mean, color='blue', marker='0', markersize=5, label='training accuracy') plt.fill_between(train_sizes, train_mean + train_std, train_mean - train_std, alpha=0.15, color='blue') plt.plot(train_sizes, test_mean, color='green', linestyle='--', marker='s', markersize=5, label='validation accuracy') plt.fill_between(train_sizes, test_mean + test_std, test_mean - test_std, alpha=0.15, color='green') plt.grid() plt.xlabel('Number of training samples') plt.ylabel('Accuracy') plt.legend(loc='lower right') plt.ylim([0.8, 1.0]) plt.show()
learning_curve中的train_sizes參數控制產生學習曲線的訓練樣本的絕對/相對數量,此處,我們設置的train_sizes=np.linspace(0.1, 1.0, 10),將訓練集大小划分為10個相等的區間。learning_curve默認使用分層k折交叉驗證計算交叉驗證的准確率,我們通過cv設置k。
上圖中可以看到,模型在測試集表現很好,不過訓練集和測試集的准確率還是有一段小間隔,可能是模型有點過擬合
四、驗證曲線解決過擬合和欠擬合(調試算法)
驗證曲線和學習曲線很相近,不同的是這里畫出的是不同參數下模型的准確率而不是不同訓練集大小下的准確率
from sklearn.model_selection import validation_curve param_range = [0.001, 0.01, 0.1, 1.0, 10.0, 100.0] pipe_lr = Pipeline([('scl', StandardScaler()), ('clf', LogisticRegression(penalty='l2', random_state=0))]) train_scores, test_scores = validation_curve(estimator=pipe_lr, X=X_train, y=y_train, param_name='clf__C', param_range=param_range, cv=10) train_mean = np.mean(train_scores, axis=1) train_std = np.std(train_scores, axis=1) test_mean = np.mean(test_scores, axis=1) test_std = np.std(test_scores, axis=1) plt.plot(param_range, train_mean, color='blue', marker='o', markersize=5, label='training accuracy') plt.fill_between(param_range, train_mean + train_std, train_mean - train_std, alpha=0.15, color='blue') plt.plot(param_range, test_mean, color='green', linestyle='--', marker='s', markersize=5, label='validation accuracy') plt.fill_between(param_range, test_mean + test_std, test_mean - test_std, alpha=0.15, color='green') plt.grid() plt.xscale('log') plt.xlabel('Parameter C') plt.ylabel('Accuracy') plt.legend(loc='lower right') plt.ylim([0.8, 1.0]) plt.show()
我們得到了參數C的驗證曲線。
和learning_curve方法很像,validation_curve方法使用采樣k折交叉驗證來評估模型的性能。在validation_curve內部,我們設定了用來評估的參數,這里是C,也就是LR的正則系數的倒數。
觀察上圖,最好的C值是0.1。
總之,我們可以使用學習曲線判斷算法是否擬合程度(欠擬合或者過擬合),然后使用驗證曲線評估參數獲取最好的參數
機器學習算法中有兩類參數:從訓練集中學習到的參數,比如邏輯斯蒂回歸中的權重參數,另一類是模型的超參數,也就是需要人工設定的參數,比如正則項系數或者決策樹的深度。
權重參數可以通過驗證曲線來獲取最好的參數,而超參數則可以使用網格搜索調參
五、網格搜索調參(調試算法)
網格搜索其實就是暴力搜索,事先為每個參數設定一組值,然后窮舉各種參數組合,找到最好的那組
from sklearn.model_selection import GridSearchCV from sklearn.svm import SVC pipe_svc = Pipeline([('scl', StandardScaler()), ('clf', SVC(random_state=1))]) param_range = [0.0001, 0.001, 0.01, 0.1, 1.0, 10.0, 100.0, 1000.0] param_grid = [{'clf__C': param_range, 'clf__kernel': ['linear']}, {'clf__C': param_range, 'clf__gamma': param_range, 'clf__kernel': ['rbf']}] gs = GridSearchCV(estimator=pipe_svc, param_grid=param_grid, scoring='accuracy', cv=10, n_jobs=-1) gs = gs.fit(X_train, y_train) print(gs.best_score_) print(gs.best_params_)
GridSearchCV中param_grid參數是字典構成的列表。對於線性SVM,我們只評估參數C;對於RBF核SVM,我們評估C和gamma。
最后, 我們通過best_parmas_得到最優參數組合。
sklearn人性化的一點是,我們可以直接利用最優參數建模(best_estimator_)
clf = gs.best_estimator_ clf.fit(X_train, y_train) print('Test accuracy: %.3f' %clf.score(X_test, y_test))
網格搜索雖然不錯,但是窮舉過於耗時,sklearn中還實現了隨機搜索,使用 RandomizedSearchCV類,隨機采樣出不同的參數組合
六、嵌套交叉驗證(選擇算法)
結合k折交叉驗證和網格搜索是調參的好手段。可是如果我們想從茫茫算法中選擇最合適的算法,用什么方法呢?這就是下面要介紹的嵌套交叉驗證
嵌套交叉驗證外層有一個k折交叉驗證將數據分為訓練集和測試集。還有一個內部交叉驗證用於選擇模型算法。下圖演示了一個5折外層交叉沿則和2折內部交叉驗證組成的嵌套交叉驗證,也被稱為5*2交叉驗證
sklearn中如下使用嵌套交叉驗證
svc的精確度
gs = GridSearchCV(estimator=pipe_svc, param_grid=param_grid, scoring='accuracy', cv=10, n_jobs=-1) scores = cross_val_score(gs, X, y, scoring='accuracy', cv=5) print('CV accuracy: %.3f +/- %.3f' %(np.mean(scores), np.std(scores)))
決策樹分類器精確度
gs = GridSearchCV(estimator=DecisionTreeClassifier(random_state=0), param_grid=[{'max_depth': [1,2,3,4,5,6,7, None]}], scoring='accuracy', cv=5) scores = cross_val_score(gs, X_train, y_train, scoring='accuracy', cv=5) print('CV accuracy: %.3f +/- %.3f' %(np.mean(scores), np.std(scores)))
比較下兩者的精確度,即可知道那種算法更加合適
七、混淆矩陣(性能評價指標)
除了准確率,還有不少評價指標,如查准率,查全率,F1值等
混淆矩陣(confusion matrix), 能夠展示學習算法表現的矩陣。混淆矩陣是一個平方矩陣,其中記錄了一個分類器的TP(true positive)、TN(true negative)、FP(false positive)和FN(false negative):
from sklearn import metrics metrics.calinski_harabaz_score(input, y_pred)
python實現混淆矩陣
其實就是一個2*2的矩陣
TP實際為真,判斷成功(判斷為真)的個數
FN實際為真,判斷錯誤(判斷為假)
FP實際為假,判斷錯誤(判斷為真)
TN實際為假,判斷成功(判斷為假)
摘自https://www.gitbook.com/book/ljalphabeta/python-