ブレンディングが理解できればスタッキングは理解できたも同然です。中間のLevelに複数のモデルを追加するだけだからです。順を追って見ていきましょう。
実装に入る前に、全体構成について示しておきます。
ここでのスタッキングでは、「Level 0」と「Level 1」と「Level 2」の3層の構成になるように、以下の手順でモデルやデータセットを作成していきます。
この構成と流れを図にすると、図7のようになります。
それでは、Level 0層から実装していきましょう。
まずは、性能(=精度)が高くなりそうな機械学習の手法を用いて、ベースとなるモデルを複数作ります。筆者の場合は、先ほどのモデルブレンディングと同じ5つのベースモデルを使い回しました。
1つのベースモデルの実装例は、前掲のリスト1に示したコードと全く同じになるため、説明は割愛します。
ここまでで、Level 0層のベースモデルが準備できました。
Level 1層のメタモデルを作る前に、そのモデルに入力するためのデータセットを作ります。このデータセットは、先ほど作成したベースモデルによる予測結果から作成します。
この手順も、前掲のリスト2に示したコードと全く同じになるため、説明は割愛します。
次に、Level 1層に複数のメタモデルを作成します。筆者の場合は、以下の3つを作りました。
いずれもこれまでの連載内容で作れるモデルです。
機械学習モデルの構築はワンパターンなので、スタッキング[Level 1]メタモデルの実装であっても、前掲のリスト1とコード内容は大差ありません。違うのは、リスト1でXGBRegressorクラスを使っていた部分が、例えば下記のリスト5の太字部分ではscikit-learnのRandomForestRegressorというランダムフォレストのクラスを使用している点くらいです。
# [Level 1]メタモデル2: ランダムフォレスト
import numpy as np
import pandas as pd
from sklearn.preprocessing import OrdinalEncoder, StandardScaler
from sklearn.metrics import mean_squared_error
from sklearn.ensemble import RandomForestRegressor
# 事前にハイパーパラメーターのチューニングをしておく(参考:前掲のリスト3)
# params = study.best_params
params = {
'n_estimators': 199,
'min_samples_split': 20
}
# 新しい「5分割済みの訓練&検証データ」と「テストデータ」のロード
# は、前掲のリスト2と同様のコードで実行済み。
# サンプルのSubmission用ファイルもロード
df_sample_submission = pd.read_csv('../input/30-days-of-ml/sample_submission.csv')
# 利用する特徴量の選択
useful_features = ['pred_1', 'pred_2', 'pred_3']
valid_scores = [] # 「検証データに対する評価スコア」を保存する変数
valid_predictions = {} # 「検証データに対する予測結果」を保存する変数
test_predictions = [] # 「テストデータに対する予測結果」を保存する変数
for fold in range(5):
X_train = df_train[df_train.kfold != fold].reset_index(drop=True)
X_valid = df_train[df_train.kfold == fold].reset_index(drop=True)
X_test = df_test.copy()
# 検証データのIDを保存しておく
valid_ids = X_valid.id.values.tolist()
y_train = X_train.target
y_valid = X_valid.target
X_train = X_train[useful_features]
X_valid = X_valid[useful_features]
X_test = X_test[useful_features]
# ランダムフォレストモデルの訓練(fit)
model = RandomForestRegressor(
n_jobs=-1, # 全CPUを使う場合(scikit-learnはサポート範囲の広さを重視してGPUには非対応)
criterion='mse', # scikit-learn v1.0からは'squared_error'
random_state=42,
**params) # チューニング済みのハイパーパラメーターをセット
model.fit(X_train, y_train)
# 検証データをモデルに入力して予測する
preds_valid = model.predict(X_valid)
# 検証データのIDと、それに対する予測値をセットで、ループ外の変数に保存する
valid_predictions.update(dict(zip(valid_ids, preds_valid)))
# 「検証データに対する評価スコア」を取得してループ外の変数に保存し、スコアを出力
score_valid = mean_squared_error(y_valid, preds_valid, squared=False)
valid_scores.append(score_valid)
print(fold, score_valid)
# 出力例: 0 0.7249324634721162
# 同様に、テストデータをモデルに入力して予測する
preds_test = model.predict(X_test)
# 予測結果をループ外の変数に保存
test_predictions.append(preds_test)
# 5回分の「検証データによる評価スコア」を平均する
score_validation = np.mean(valid_scores)
print('score_validation:', score_validation)
# 出力例: score_validation: 0.7258260367801279
# 「検証データに対する予測結果」をCSVファイルに保存
valid_predictions = pd.DataFrame.from_dict(valid_predictions, orient='index').reset_index()
valid_predictions.columns = ['id', 'pred_2'] # 列名を[id]と[pred_2]にする
valid_predictions.to_csv('valid_level1_preds2.csv', index=False)
# 5回分の「テストデータに対する予測結果」を平均し、それをCSVファイルに保存
X_sample_submission = df_sample_submission.copy()
X_sample_submission.target = np.mean(np.column_stack(test_predictions), axis=1)
X_sample_submission.columns = ['id', 'pred_2'] # 列名を[id]と[pred_2]にする
X_sample_submission.to_csv('test_level1_preds2.csv', index=False)
ランダムフォレストのハイパーパラメーターは事前にチューニングしておきました。なおXGBoostでは、ブースティング法の仕組み上、「決定木が先頭から順々に構築(build sequentially)されるので早期停止が適切」と前回説明しました。一方、(前々々回も名前だけ出したアンサンブル学習の)バギング法を用いたランダムフォレストは、「決定木が並列に構築(build parallel)されるのでチューニングが適切」です。よってリスト5でまずは、ハイパーパラメーターn_estimators(=構築する決定木の数)をOptunaで自動チューニングしておいたというわけです。
リスト5では、併せてチューニングしたmin_samples_splitというパラメーターも使っています。ランダムフォレストには、その他、min_sample_leafやmax_depthなどさまざまなパラメーターがあるので、必要に応じてそれぞれチューニングするとよいでしょう。
本稿では、中間のレベルは[Level 1]の1つだけです。しかし中間のLevel層をもっと増やしていくことには、精度の向上は徐々に弱くなっていくものの多少の効果があるそうです(※筆者は試しておらず、具体的なことは把握していません。参考:『Kaggleで勝つデータ分析の技術』)。
Level 2層のメタモデルを作る前に、そのモデルに入力するためのデータセットを作ります。このデータセットは、先ほど作成した、Level 1層のメタモデルによる予測結果から作成します。
先ほどの[Level 0→1]が、5つの「検証データに対する予測結果」と「テストデータに対する予測結果」からデータセットを作成したのに対し、今度は3つから作成します。それ以外は、前掲のリスト2と同じなので、説明は割愛します。
最後に、Level 2層のメタモデルを作成します。
これも、リスト4のコードがほぼ使い回せます。使える特徴量が5個から3個に減るので、絞り込み用の変数の記述をuseful_features = ["pred_1", "pred_2", "pred_3"]のように書き換えるくらいしか違いはありません。
以上が交差検証を用いたモデルスタッキングです。ブレンディングさえ理解しておけばスタッキングは同じ考え方で実装できることが分かったと思います。
以上、今回の後編では、YouTube動画の5回目〜6回目で体験したことを説明しました。モデルのブレンディングやスタッキングもKaggleコンペでは外せないスキルだと思います。本稿がそのスキル習得のヒントになっているとうれしいです。
さて本連載では、Kaggle主催の「“30 Days of ML”公式プログラム」をベースに、下記4本の記事を公開しました。
後半の前中後編のベースとなっているYouTube動画を公開したAbhishek Thakur氏は(先ほども書籍名を出しましたが)、
を執筆されており、日本語版も販売されていますので、さらに学びたい方は手に取ってみてはいかがでしょうか。
本連載の記事では、あっさりとした概要説明にとどまっているところが多々ありますが、Kaggleに取り組むための必須スキルのオーバービューにはなっているのではないかと思います。Kaggleなどのコンペにチャレンジする人のバイブル的な書籍である(先ほども書籍名を出しましたが)、
を初心者が読むと「ハードルが高い」と思われることも多いかと想像しますが、上記4本の記事を理解することで、かなりハードルが下がるのではないかと思います。ぜひ、上記4本を完全に理解するまで何度か読んでみてから、この書籍にチャレンジしてみてください。
『Kaggleで勝つデータ分析の技術』は持っていると、辞書的な使い方もできますし、Kaggleに取り組む際には手元に置いておくのがオススメです。
初心者が読むにはハードルが高くても読んでみないといけなさそうですね。というか、一色さんはほんとに初心者なのかしら(笑)。
さて次回ですが、BERTやGPTといったところを試してはいるのですが、コンペに参加するほどの余裕がまだないので、コンペ参加の体験記事は数カ月後になりそうです。そこで、いったんTips的な情報を挟むことにして、ローカル環境のVisual Studio Codeを使ってKaggleコンペに参加した方法をまとめようかと思っています。お楽しみに。
Copyright© Digital Advantage Corp. All Rights Reserved.