用sklearn流水線優化機器學習流程
在大多數機器學習專案中,你要處理的資料不大可能恰好是生成最優模型的理想格式。有很多資料變換的步驟例如分類變數編碼、特徵縮放和歸一化需要執行。Scikit-learn的預處理模組中包含了內建的函式來支援這些常用的變換。
但是,在一個典型的機器學習工作流中你將需要應用這些變換至少兩次。一次是在訓練時,另一次是在你要用模型預測新資料時。當然你可以寫一個函式來重用這些變換,但是你還是需要首先執行這個函式,然後再呼叫模型。Scikit-learn的流水線/pipeline就是一個簡化此操作的工具,具有如下優點:
- 讓工作流程更加簡單易懂
- 強制步驟實現和執行順序
- 讓工作更加可重現
在本文中,我將使用一個貸款預測方面的
1、變換器 / Transformer
學程式設計,上匯智網,線上程式設計環境,一對一助教指導。
首先我將訓練和測試檔案匯入jypyter notebook。我刪除了Load_ID列,因為在訓練和預測中並不需要它。我使用pandas的dtypes函式來獲取資料集的簡要資訊:
import pandas as pd
train = pd.read_csv('train.csv')
test = pd.read_csv('test.csv')
train = train.drop('Loan_ID', axis=1)
train.dtypes
可以看到資料中既有分類變數也有數值變數,因此我至少需要應用one-hot編碼變換以及某種尺度的縮放。我使用scikit-learn的流水線來執行這些變換,同時應用fit方法進行訓練。
在構建流水線之前我將訓練資料拆分為訓練集和測試集,這樣我可以驗證模型的效能:
X = train.drop('Loan_Status', axis=1)
y = train['Loan_Status']
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
構造流水線的第一步是定義變換器型別。在下面的程式碼中,我建立了一個運用StandardScaler的數值變換器,它同時包含了一個SimpleImputer來填充丟失的值。這是scikit-learn中的一個相當出色的函式,它有很多選項來定義如何填充丟失值。我選擇使用中位資料(median)但是也可能其他選項會有更好的效果。分類變換器也有一個支援各種填充方法的SimpleImputer,燃火利用OneHotEncoder將分類值轉換為整數:
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler, OneHotEncodernumeric_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='median')),
('scaler', StandardScaler())])categorical_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='constant', fill_value='missing')),
('onehot', OneHotEncoder(handle_unknown='ignore'))])
接下來我們使用ColumnTransformer變換資料幀中的列。在此之前已經使用pandas的dtype方法進行了列表排序:
numeric_features = train.select_dtypes(include=['int64', 'float64']).columns
categorical_features = train.select_dtypes(include=['object']).drop(['Loan_Status'], axis=1).columns
from sklearn.compose import ColumnTransformer
preprocessor = ColumnTransformer(
transformers=[
('num', numeric_transformer, numeric_features),
('cat', categorical_transformer, categorical_features)])
2、分類器訓練
接下來的步驟是建立一個流水線將前面建立的前處理器與分類器整合在一起。在這裡我使用一個簡單的RandomForestClassifier:
from sklearn.ensemble import RandomForestClassifier
rf = Pipeline(steps=[('preprocessor', preprocessor),
('classifier', RandomForestClassifier())])
你可以簡單地對原始資料呼叫fit方法,預處理步驟將會先執行,然後再訓練分類器:
rf.fit(X_train, y_train)
要預測新資料也一樣,流水線也會先進行預處理,然後再進行預測:
y_pred = rf.predict(X_test)
3、模型選擇
流水線可以用於模型選擇過程。下面的示例程式碼對一組scikit-learn分類器逐個應用變換並訓練模型。
from sklearn.metrics import accuracy_score, log_loss
from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import SVC, LinearSVC, NuSVC
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier, AdaBoostClassifier, GradientBoostingClassifier
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.discriminant_analysis import QuadraticDiscriminantAnalysisclassifiers = [
KNeighborsClassifier(3),
SVC(kernel="rbf", C=0.025, probability=True),
NuSVC(probability=True),
DecisionTreeClassifier(),
RandomForestClassifier(),
AdaBoostClassifier(),
GradientBoostingClassifier()
]for classifier in classifiers:
pipe = Pipeline(steps=[('preprocessor', preprocessor),
('classifier', classifier)])
pipe.fit(X_train, y_train)
print(classifier)
print("model score: %.3f" % pipe.score(X_test, y_test))
[外鏈圖片轉存失敗(img-WAIAk0il-1569282274148)(scikit-learn-pipeline-guide/model-select.png)]
4、模型引數搜尋
流水線也可以用於網格搜尋(grid search)以找到模型的最佳引數。為此我們需要首先為模型建立一個引數網格。重要的一點是你需要給每個引數名新增分類器的名稱。在上面的程式碼中我將分類器命名
為classifier,因此我給每個引數都添加了classifier__
。接下來我建立一個網格搜尋物件,它包含了原始的流水線。當我呼叫fit方法時,就會在網格搜尋交叉驗證之前首先對資料執行變換。
param_grid = {
'classifier__n_estimators': [200, 500],
'classifier__max_features': ['auto', 'sqrt', 'log2'],
'classifier__max_depth' : [4,5,6,7,8],
'classifier__criterion' :['gini', 'entropy']}
from sklearn.model_selection import GridSearchCV
CV = GridSearchCV(rf, param_grid, n_jobs= 1)
CV.fit(X_train, y_train)
print(CV.best_params_)
print(CV.best_score_)
在我開始使用流水線之前,經常發現我看不懂以前某個專案的處理流程了。流水線讓整個機器學習流程清晰易懂,容易維護。希望這教程對你學習scikit-learn的pipeline有所幫助。
原文連結:Scikit-learn流水線原理與實