天色グラフィティ

機械学習やプログラミングでいろいろ作って遊ぶブログ

なぜn_estimatorsやepochsをパラメータサーチしてはいけないのか

f:id:ejinote:20190630174727p:plain

ハイパーパラメータを探索するため、グリッドサーチやOptunaなどを利用することがあると思います。

しかし、「ハイパーパラメータ探索してみた」のようなQiita記事などでは間違って書かれていることも多いのですが、XGBoostやLightGBMの n_estimators ( num_boosting_rounds )やKerasの epochs をパラメータサーチの対象にしてはいけません。

いけません、というか、すごく無駄なことをしています

なぜ、n_estimatorsやepochsを探索すると無駄なのか

理由はシンプルで、これらのパラメータは「大きい値で精度確認する過程で小さい値の結果も分かる」からです。

LightGBMのn_estimatorsは構築する決定木の数を表しています。

f:id:ejinote:20190630173645p:plain

例として、n_estimators=5 (こんな小さい値で学習することはないですが、簡単のため)で学習を行ったとします。学習の過程で、n_estimators=1のスコアも、n_estimators=2のスコアも確認することができます。

f:id:ejinote:20190630173702p:plain

1, 2, ..., 5とn_estimatorsを振って確認した場合、合計15個の木を構築する必要がありますが、正しくやれば5個の木で十分です。単純計算で3倍、探索を高速化することができるでしょう。

n_estimatorsは学習率によっては5000程度になることもざらにあります。500, 1000, 1500, ... , 5000と500刻みに探索した場合、合計で27500個の木を構築することになります。本当なら5000個で済むのに。しかも、500刻みで探索していた場合、最適な点を見逃す可能性もあります。

Kerasのepochsに代表される、ニューラルネットワークのepoch数も同様です。

epochs=100で学習した場合、その過程で1epochから100epochまでのすべての学習状態を取得することができます。

では、どうすればいいのか?

一般的なのはEarlyStoppingを使用することでしょう。

テストデータとは別にバリデーションデータを用意し、バリデーションで対しての精度がサチったら(一定epochのあいだ向上が見られなかったら)終わりにするという方法です。

LightGBMのscikit-learn interfaceを使っているなら、eval_setearly_stopping_roundsを指定すれば動きます。n_estimatorsは大きめに取っておくとよいでしょう。

import lightgbm as lgb

params = {
    'n_estimators': 10000  # 大きめにとっておく
    # 他のパラメータは省略
}

model = lgb.LGBMClassifier(**params)
model.fit(X_train, y_train, 
          eval_set=[(X_valid, y_valid)], early_stopping_rounds=100)

scikit-learn interfaceを使っていないなら、lgb.train関数にvalid_setsearly_stopping_roundsを設定すれば動きます。num_boost_roundn_estimatorsと同様、大きめに取っておきましょう。

import lightgbm as lgb

train_set = lgb.Dataset(X_train, y_train)
valid_set = lgb.Dataset(X_valid, y_valid)
model = lgb.train(params, train_set, num_boost_round=10000, 
                  valid_sets=[valid_set], early_stopping_rounds=100)

KerasでEarlyStoppingを行うならkeras.callbacks.EarlyStoppingを使います。使う場合はkeras.callbacks.ModelCheckpointと組み合わせ、最も良いモデルを保存するようにしておくと良いでしょう。

from keras import callbacks

# モデルの定義とcompileは予めしておく

callbacks = [
    callbacks.ModelCheckpoint('best_model.h5', save_best_only=True)
    callbacks.EarlyStopping(patience=5)  # 5epoch改善が無ければ止める
]
model.fit(X_train, y_train, epochs=1000, callbacks=callbacks,
          validation_data=[(X_valid, y_valid)]

PyTorchなど、他のフレームワークの場合もEarlyStoppingの仕組みは実装されています。

まとめ

学習の途中経過が全て確認可能な手法を使うとき、n_estimatorsやepochsのような「学習回数」のパラメータを調整するのは時間がもったいないです。

他にもやりがちな例で各種ライブラリのverboseをチューニングしてしまう、というものがあります。verboseは学習中にどれくらいメッセージを吐き出すかというパラメータで、モデルの精度には一切影響を与えません。精度が変わらないので、もちろんチューニングする必要はありません。

機械学習モデルをチューニングする時はそれぞれのパラメータの意味を理解した上で行うようにしましょう。使っているアルゴリズムに対しての理解を深めることも助けになるでしょう。