對於許多機器學習演算法,你提供的特定資料表示非常重要。
- 首先對資料進行縮放,然後手動合併特徵,再利用無監督機器學習來學習特徵。
- 因此,大多數機器學習應用不僅需要應用單個演算法,而且還需要將許多不同的處理步驟和機器學習模型連結在一起。
舉一個例子來說明模型鏈的重要性。
我們知道,可以通過使用 MinMaxScaler 進行預處理來大大提高核 SVM 在 cancer 資料集上的效能。
下面這些程式碼實現了劃分資料、計算最小值和最大值、縮放資料與訓練 SVM:
from sklearn.datasets import load_breast_cancer
from sklearn.svm import SVC
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split
#載入和劃分資料
cancer = load_breast_cancer()
X_train,X_test,y_train,y_test = train_test_split(cancer.data,cancer.target,random_state=0)
#資料縮放
scaler = MinMaxScaler()
scaler.fit(X_train)
X_train_scaled = scaler.transform(X_train)
#在縮放後的資料上學習SVM
svc = SVC().fit(X_train_scaled,y_train)
X_test_scaled = scaler.transform(X_test)
print("Test score:{}".format(svc.score(X_test_scaled,y_test)))
'''
`Test score:0.972027972027972`
'''
1、用預處理進行引數選擇
現在,假設我們希望利用 GridSearchCV 找到更好的 SVC 引數。 我們應該怎麼做?一種簡單的方法可能如下所示:
from sklearn.model_selection import GridSearchCV
#網格引數
param_grid = {'C': [0.001, 0.01, 0.1, 1, 10, 100],
'gamma': [0.001, 0.01, 0.1, 1, 10, 100]}
#建立GridSearchCV例項,折數為五折
grid = GridSearchCV(SVC(),param_grid,cv=5)
#擬合
grid.fit(X_train_scaled,y_train)
#列印最優引數
print("Best parammetes:{}".format(grid.best_params_))
print("Best cross-validation accuracy:{:.3f}".format(grid.best_score_))
print("Test score:{:.3f}".format(grid.score(X_test_scaled,y_test)))
'''
```
Best parammetes:{'C': 1, 'gamma': 1}
Best cross-validation accuracy:0.981
Test score:0.972
```
'''
?
這裡我們利用縮放後的資料對 SVC 引數進行網格搜尋。但是,上面的程式碼中有一個不易察覺的陷阱。
在縮放資料時,我們使用了訓練集中的所有資料來找到訓練的方法。然後,我們使用縮放後的訓練資料來執行帶交叉驗證的網格搜尋。
對於交叉驗證中的每次劃分,原始訓練集的一部分被劃分為訓練部分,另一部分被劃分為測試部分。測試部分用於度量在訓練部分上所訓練的模型在新資料上的表現。但是,我們在縮放資料時已經使用過測試部分中所包含的資訊。
⭐交叉驗證每次劃分的測試部分都是訓練集的一部分,我們使用整個訓練集的資訊來找到資料的正確縮放。
對於模型來說,這些資料與新資料看起來截然不同。
如果我們觀察新資料(比如測試集中的資料),那麼這些資料並沒有用於對訓練資料進行縮放,其最大值和最小值也可能與訓練資料不同。
下面這個例子顯示了交叉驗證與最終評估這兩個過程中資料處理的不同之處:
mglearn.plots.plot_improper_processing()
?
因此,對於建模過程,交叉驗證中的劃分無法正確地反映新資料的特徵。
- 我們已經將這部分資料的資訊洩露(leak)給建模過程。
- 這將導致在交叉驗證過程中得到過於樂觀的結果, 並可能會導致選擇次優的引數。
⭐
為了解決這個問題,在交叉驗證的過程中,應該在進行任何預處理之前完成資料集的劃分。
任何從資料集中提取資訊的處理過程都應該僅應用於資料集的訓練部分,因此,任何交叉驗證都應該位於處理過程的 “最外層迴圈”。
在 scikit-learn 中,要想使用 cross_val_score 函式和 GridSearchCV 函式實現這一點,可以使用 Pipeline 類。
- Pipeline 類可以將多個處理步驟合併(glue)為單個 scikit-learn 估計器。
- Pipeline 類本身具有 fit、predict 和 score 方法,其行為與 scikit-learn 中的其他模型相同。
- Pipeline 類最常見的用例是將預處理步驟(比如資料縮放)與一個監督模型 (比如分類器)連結在一起。
2、構建管道
使用 Pipeline 類來表示在使用 MinMaxScaler 縮放資料之後再訓練一個 SVM 的工作流程(暫時不用網格搜尋)。
#構建一個由步驟列表組成的管道物件
from sklearn.pipeline import Pipeline
#每個步驟都是一個元組,其中包含一個名稱(你選定的任意字串)和一個估計器的例項
pipe = Pipeline([('scaler',MinMaxScaler()),('svm',SVC())])
'''
這裡我們建立了兩個步驟:第一個叫作 "scaler",是 MinMaxScaler 的例項;第二個叫作 "svm",是 SVC 的例項。
現在我們可以像任何其他 scikit-learn 估計器一樣來擬合這個管道:
'''
#首先對第一個步驟(縮放器)呼叫 fit,然後使用該縮放器對訓練資料進行變換,最後用縮放後的資料來擬合 SVM。
pipe.fit(X_train,y_train)
#要想在測試資料上進行評估,我們只需呼叫 pipe.score
print("Test score:{:.2f}".format(pipe.score(X_test,y_test)))
'''
```
Test score:0.97
```
'''
3、在網格搜尋中使用管道
在網格搜尋中使用管道的工作原理與使用任何其他估計器都相同。我們定義一個需要搜尋的引數網格,並利用管道和引數網格構建一個 GridSearchCV。
不過在指定引數網格時存在一處細微的變化。
- 我們需要為每個引數指定它在管道中所屬的步驟。
- 我們要調節的兩個引數 C 和 gamma 都是 SVC 的引數,屬於第二個步驟。
- 我們給這個步驟的名稱是 "svm":
- 為管道定義引數網格的語法是為每個引數指定步驟名稱,後面加上 __(雙下劃線),然後是引數名稱。
- 因此,要想搜尋 SVC 的 C 引數,必須使用 "svm__C" 作為引數網格字典的鍵,對 gamma 引數也是同理:
param_grid = {'svm__C': [0.001, 0.01, 0.1, 1, 10, 100],
'svm__gamma': [0.001, 0.01, 0.1, 1, 10, 100]}
#有了這個引數網格,我們可以像平常一樣使用 GridSearchCV:
#注意這裡的模型用的是pipe
grid = GridSearchCV(pipe,param_grid,cv=5)
grid.fit(X_train,y_train)
#列印
print("Best parammetes:{}".format(grid.best_params_))
print("Best cross-validation accuracy:{:.3f}".format(grid.best_score_))
print("Test score:{:.3f}".format(grid.score(X_test,y_test)))
'''
```
Best parammetes:{'svm__C': 1, 'svm__gamma': 1}
Best cross-validation accuracy:0.981
Test score:0.972
```
'''
#與前面所做的網格搜尋不同,現在對於交叉驗證的每次劃分來說,僅使用訓練部分對 MinMaxScaler 進行擬合,測試部分的資訊沒有洩露到引數搜尋中。
mglearn.plots.plot_proper_processing()
在交叉驗證中,資訊洩露的影響大小取決於預處理步驟的性質。使用測試部分來估計資料的範圍,通常不會產生可怕的影響,但在特徵提取和特徵選擇中使用測試部分,則會導致結果的顯著差異。
4、參考文獻
《python機器學習基礎教程》