モデルごとに前処理が必要だったり必要でなかったり、さらに前処理段階でパラメータ調整しないといけないようなシーンがあったりすると、たくさんデータフレームを分けて作って、、、とやるのは非常に面倒です。
scikit learn では Pipeline を利用することで、そのあたりをすっきりと書くことができてとっても便利だなと思いましたので、こちらにメモを残しておこうと思います。
・モデルごとに異なる前処理を施して結果を見たい
・前処理段階でのハイパーパラメータ調整も同時に行いたい
モデルごとに異なる前処理を施して結果を見たい
以下のような場面を考えます。
・Logistic Regression を使う際には StandardScaler を使いたい
・SVM を使う際には、MinMaxScaler で正規化したい
・RandomForest を使う際には、正規化は行いたくない
1 2 3 4 5 6 7 8 9 10 11 12 |
from sklearn.datasets import load_breast_cancer from sklearn.model_selection import train_test_split, GridSearchCV from sklearn.preprocessing import MinMaxScaler, StandardScaler from sklearn.pipeline import Pipeline from sklearn.svm import SVC from sklearn.ensemble import RandomForestClassifier from sklearn.linear_model import LogisticRegression # データの準備 dataset = load_breast_cancer() x_train, x_val, t_train, t_val = train_test_split(dataset.data, dataset.target, random_state=2020) |
各モデルごとに GridSearchCV でパラメータ調整を考えます。
Pipeline の中には、key-value の形で、前処理と分類器の暫定的なものをとりあえず入れておき、GridSearch にかける組み合わせとして、param_grid の中で前処理や分類器の他の選択肢を記載してあげます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
pipe = Pipeline([("preprocessing", StandardScaler()), ("classifier", SVC())]) param_grid = [ { "classifier": [SVC()], "preprocessing": [MinMaxScaler()], "classifier__gamma": [0.001, 0.01, 0.1, 1, 10, 100], "classifier__C": [0.001, 0.01, 0.1, 1, 10, 100] }, { "classifier": [LogisticRegression()], "preprocessing": [StandardScaler()], "classifier__C": [0.001, 0.01, 0.1, 1, 10, 100] }, { "classifier": [RandomForestClassifier()], "preprocessing": [None], "classifier__max_depth": [2, 3, 4, 5, 6], "classifier__n_estimators": [50, 100, 150, 200] } ] |
各分類器のハイパーパラメータは、keyの文字列にアンダーバー2個(”__”)くっつけることで、それがハイパーパラメータだと認識してくれます。
あとは、通常のGridSearchと同様です。
1 2 3 4 5 |
grid = GridSearchCV(pipe, param_grid, cv=3) grid.fit(x_train, t_train) result = pd.DataFrame(grid.cv_results_) result.sort_values("rank_test_score").loc[:, ["param_classifier", "param_preprocessing", "mean_test_score", "rank_test_score"]] |
C=1 のロジスティック回帰が 1 番という結果になりました。
とてもスッキリかけて見やすくていいですね。
前処理段階でのハイパーパラメータ調整も同時に行いたい
先程は異なるモデルに異なる前処理で、ということでしたが、この前処理の箇所でもパラメータ探索をしたい、という時もあります。
以下のような場面を考えます。
・モデルはリッジ回帰を使う
・正規化は、StandardScaler か RobustScaler どちらか使う
・多項式特徴量を追加したい
この場合、リッジ回帰のパラメータ alpha と、多項式特徴量のパラメータ degree を調整する必要がありますが、degree の変更は、前処理に当たる部分なので、パイプライン化しないとかなり面倒です。
こちらも、sklearn組み込みのボストン住宅価格のデータセットで見てみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
from sklearn.linear_model import Ridge from sklearn.preprocessing import PolynomialFeatures, RobustScaler from sklearn.datasets import load_boston # データの準備 dataset = load_boston() x_train, x_val, t_train, t_val = train_test_split(dataset.data, dataset.target, random_state=2020) pipe = Pipeline([ ("scaler", StandardScaler()), ("poly", PolynomialFeatures()), ("regressor", Ridge()) ]) param_grid = { "poly__degree": [1, 2, 3], "regressor__alpha": [0.001, 0.01, 0.1, 1, 10, 100] } grid = GridSearchCV(pipe, param_grid, cv=3) grid.fit(x_train, t_train) result = pd.DataFrame(grid.cv_results_) result.sort_values("rank_test_score").loc[:, ["param_poly__degree", "param_regressor__alpha", "param_scaler", "mean_test_score"]] |
Pipeline に入れてあげれば、param_grid の中に多項式特徴量のdegree も探索範囲に同時に含めてあげられます。便利ですね!
最後まで読んでいただきありがとうございました。
参考
Python ではじめる機械学習