🤖
【16日目】パイプラインを構築する【2021アドベントカレンダー】
2021年1人アドベントカレンダー(機械学習)、16日目の記事になります。
テーマは パイプライン になります。
scikit-learn の pipelineを使います。
Colab のコードはこちら
Pipeline の基本形
基本形は下記になります。
from sklearn.pipeline import Pipeline
# Pipeline の中身
step = [
(
"name", # 名称
StandardScaler(), # 実行したいインスタンス
),
]
pipe = Pipeline(step) # Pipeline のインスタンスの宣言
# Pipeline の実行
pipe.fit()
pipe.predict()
pipe.fit_transform()
pipe.transform()
実行できるメソッドに限りがあるので要注意です。
また、指定したカラムだけを対象にできる、ColumnTransformer という Pipeline もあります。
例えば数値データのカラムだけに StandardScaler() を適用する、といったことが可能です。
from sklearn.compose import ColumnTransformer
# カラムの指定
number_columns = df.select_dtypes(include="number").columns
# 数値データ用の変換 Pipeline
numeric_transformer = Pipeline(steps=[
('num_imputer', SimpleImputer(strategy='median')),
('scaler', StandardScaler())]
)
columns_transformers = ColumnTransformer(
transformers=[
('num', numeric_transformer, number_columns), # 名称、pipeline のインスタンス、対象とするカラム
]
)
詳細
カラムの指定
指定するカラムを作成します。
数値データのカラム、OneHotエンコーディングの対象にするカーディナリティの低いカラム、Ordinalエンコーディングの対象にするカーディナリティの高いカラムを作成します。
# 数値データカラム名を取得
number_columns = df.drop(["Global_Sales", "NA_Sales", "PAL_Sales", "JP_Sales", "Other_Sales"], axis=1).select_dtypes(include="number").columns
# カテゴリデータカラム名を取得
category_columns = df.drop(["Global_Sales", "NA_Sales", "PAL_Sales", "JP_Sales", "Other_Sales"], axis=1).select_dtypes(include="object").columns
# カテゴリデータカラムの各カラムのカテゴリーの数を取得
category_unique_num = df.select_dtypes(include="object").nunique()
# カテゴリーの数にしきい値を設けて、カテゴリー数の多いカラムと少ないカラムに分ける
thread = 10
many_kinds_category_columns = category_unique_num[category_unique_num >= thread].index
few_kinds_category_columns = category_unique_num[category_unique_num < thread].index
カラムごとの Pipeline を作成
# 数値データ用の変換
numeric_transformer = Pipeline(steps=[
('num_imputer', SimpleImputer(strategy='median')),
('scaler', StandardScaler())]
)
# カテゴリーのエンコーディング法則を指定する
ordinal_all_cols_mapping = []
for column in many_kinds_category_columns:
ordinal_one_cols_mapping = []
for category in natsorted(X_train[column].unique()):
ordinal_one_cols_mapping.append(category)
ordinal_all_cols_mapping.append(ordinal_one_cols_mapping)
# カテゴリー数が多いカテゴリーデータ用の変換
many_kinds_categorical_transformer = Pipeline(
steps=[
('imputer', SimpleImputer(strategy='most_frequent')),
('ordinal', OrdinalEncoder(
handle_unknown = 'use_encoded_value', # 未知数をunknown valueに置き換える設定
unknown_value = -1,
categories = ordinal_all_cols_mapping
)
)
])
# カテゴリー数が少ないカテゴリーデータ用の変換
few_kinds_categorical_transformer = Pipeline(
steps=[
('imputer', SimpleImputer(strategy='most_frequent')),
('onehot', OneHotEncoder(handle_unknown='ignore'))
]
)
ColumnTransformerの作成
カラムごとの Pipeline を ColumnTransformer にまとめます。
columns_transformers = ColumnTransformer(
transformers=[
('num', numeric_transformer, number_columns),
('many_kinds', many_kinds_categorical_transformer, many_kinds_category_columns),
('few_kinds', few_kinds_categorical_transformer, few_kinds_category_columns)
]
)
パイプラインの作成
学習モデルと ColumnTransformer をまとめた Pipeline を作成します。
step = [
("columns_transformers", columns_transformers),
('model', lgb.LGBMRegressor(random_state=42))
]
# パイプラインの作成
pipe = Pipeline(
step
)
学習・推論
pipe.fit(X_train, y_train)
# 推論
y_pred = pipe.predict(X_test)
GroupKFold で Pipeline を実行
# 学習・推論
gkf = GroupKFold(n_splits=5)
groups = X_train["Genre"]
best_params, history = {}, []
cv_result = []
for i, (train_index, test_index) in enumerate(gkf.split(X_train, y_train, groups)):
X_train_gkf, X_test_gkf = X_train.iloc[train_index], X_train.iloc[test_index]
y_train_gkf, y_test_gkf = y_train.iloc[train_index], y_train.iloc[test_index]
# 学習、推論
pipe.fit(X_train_gkf, y_train_gkf)
y_pred = pipe.predict(X_test_gkf)
rmse = mean_squared_error(y_test_gkf, y_pred, squared=False)
cv_result.append(rmse)
print("RMSE:", cv_result)
print("RMSE:", np.mean(cv_result))
Pipeline を使ってStacking
下記の様に設定することで、Pipeline を使って Stacking することも可能です。
for i, (model_name, model) in enumerate(models.items()):
print(i, model)
# パイプライン全体の設定
step = [
("columns_transformers", columns_transformers),
('model', model)
]
# パイプラインの作成
pipe = Pipeline(
step
)
一層目 学習用の予測結果を作成
Stacking 用に一層目の各種モデルの予測データを作成します。
from sklearn.linear_model import Ridge
from sklearn.linear_model import Lasso
from sklearn.linear_model import LinearRegression
from sklearn.linear_model import ElasticNet
from sklearn.svm import SVR
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.ensemble import RandomForestRegressor
import xgboost as xgb
from catboost import CatBoostRegressor
models = {
"ridge":Ridge(random_state=SEED),
"lasso":Lasso(random_state=SEED),
"linear":LinearRegression(),
"elastic_net":ElasticNet(random_state=SEED),
"svm":SVR(),
"random_forest":RandomForestRegressor(random_state=SEED),
"gradient":GradientBoostingRegressor(random_state=SEED),
"catboost":CatBoostRegressor(random_state=SEED,
silent=True, # ログを非表示
),
"xgboost":xgb.XGBRegressor(
random_state=SEED,
objective='reg:squarederror'
),
"lightgbm":lgb.LGBMRegressor(random_state=SEED),
}
gkf = GroupKFold(n_splits=5)
groups = X_train["Genre"]
cv_result_stck = {}
pred_df = pd.DataFrame()
for i, (model_name, model) in enumerate(models.items()):
print(i, model)
# パイプライン全体の設定
step = [
("columns_transformers", columns_transformers),
('model', model)
]
# パイプラインの作成
pipe = Pipeline(
step
)
each_model_df = pd.DataFrame()
each_model_result = []
for train_index, test_index in gkf.split(X_train, y_train, groups):
X_train_gkf, X_test_gkf = X_train.iloc[train_index], X_train.iloc[test_index]
y_train_gkf, y_test_gkf = y_train.iloc[train_index], y_train.iloc[test_index]
pipe.fit(X_train_gkf, y_train_gkf)
y_pred = pipe.predict(X_test_gkf)
tmp_df = pd.DataFrame(
[y_pred],
columns=test_index
)
rmse = mean_squared_error(y_test_gkf, y_pred, squared=False)
each_model_result.append(rmse)
each_model_df = pd.concat([each_model_df , tmp_df.T]) # 各KFold ごとの予測結果をDataFrameに縦に並べる
cv_result_stck[model_name] = each_model_result # 各モデルのRMSEを集計
each_model_df.columns = [model_name] # カラム名をモデル名に変更
pred_df = pd.concat([pred_df, each_model_df.sort_index()], axis=1) # 予測結果集計用DataFrameに各モデルの予測結果をくっつける
ベストモデルを選択します。
best_model_score = 9999
best_model_name = ""
for model_name, rmse in cv_result_stck.items():
print(model_name, np.mean(rmse))
if best_model_score > np.mean(rmse):
best_model_score = np.mean(rmse)
best_model_name = model_name
print()
print("Best Model is ", best_model_name, "! Best Score is ", best_model_score, "!")
二階層目 各モデルの予測結果をもとに予測
一層目で作成した各種モデルの予測データをもとに学習・推論を行います。
# 学習・推論
gkf = GroupKFold(n_splits=5)
y_train
y_test
groups = pred_df[best_model_name] # 最もスコアの良かった model_name をGroup に設定
cv_result_stck = []
step = [
("numeric_transformers", numeric_transformer), # 全て数値カラムなので、数値カラムのTransformerに変更
('model', models[best_model_name]) # 最もスコアの良かったモデルを使用
]
# パイプラインの作成
pipe = Pipeline(
step
)
for train_index, test_index in gkf.split(pred_df, y_train, groups):
X_train_gkf, X_test_gkf = pred_df.iloc[train_index], pred_df.iloc[test_index]
y_train_gkf, y_test_gkf = y_train.reset_index(drop=True).iloc[train_index], y_train.reset_index(drop=True).iloc[test_index]
pipe.fit(X_train_gkf, y_train_gkf)
y_pred = pipe.predict(X_test_gkf)
rmse = mean_squared_error(y_test_gkf, y_pred, squared=False)
cv_result_stck.append(rmse)
16日目は以上になります、最後までお読みいただきありがとうございました。
Discussion