よっしーの私的空間

機械学習を中心に興味のあることについて更新します

LightGBMでOptunaを使用するときの再現性確保について

Optunaとはハイパーパラメータチューニングを自動で実施してくれる大変便利なフレームワークで、LightGBMを使う人は良く使うんじゃないかなと思います。今回はそんなOptunaを使用するときの再現性の確保方法についてまとめます。私が使用しているパッケージのバージョンは以下の通りです。

  • lightgbm 2.3.1
  • optuna 2.8.0

1.概要

主なポイントは以下です。

  • lightgbm.train()ではなく、lightgbm.LightGBMTuner()を使用して訓練する。
  • 以下のパラメータを使用する。
    • deterministic
    • force_row_wiseまたはforce_col_wise
    • optuna_seed
  • 乱数シードを固定する(numpy.random.seedおよびrandom.seed)。

上記を1つずつ解説します。てっとり早くソース全文を確認したい方は本稿末尾をご参照ください。sklearnのirisデータ(アヤメデータ)を使用したサンプルコードを掲載しています。

2.解説

2.1.訓練用の関数としてLightGBMTunerを使用する

LightGBMを使用するときに、多くの人は以下のような実装をするのではないかな思います。

import optuna.integration.lightgbm as lgb

'''
データの前処理等を実施(省略)
'''
# 訓練実施
lgb.train(params, lgb_train, lgb_eval)

上記のlightgbm.train()をlightgbm.LightGBMTuner()に変更します。そうすることで、後述するパラメータが使えるようになります。
注意点ですが、lightgbm.LightGBMTuner()を使用すると、訓練の実施やモデルの取得等を明示的に定義する必要があります。実装は以下の通りです。

# 訓練方法の定義
booster = lgb.LightGBMTuner(
    params = params, 
    train_set = lgb_train,
    valid_sets=lgb_eval,
    optuna_seed=123,
)

# 訓練の実施
booster.run()

# 訓練で得た最良のパラメータを表示
booster.best_params

# 訓練で得た最良のモデル(Boosterオブジェクト)を取得する
best_booster = booster.get_best_booster()

2.2.再現性確保用のパラメータの使用

以下のようにパラメータを使用します。

params = {
    'objective': 'multiclass ',
    'num_class':3,
    'metric': 'multi_logloss',
    'verbosity': -1,
    'boosting_type': 'gbdt',
    'deterministic':True, #再現性確保用のパラメータ
    'force_row_wise':True  #再現性確保用のパラメータ
    }

booster = lgb.LightGBMTuner(
    params = params, 
    train_set = lgb_train,
    valid_sets=lgb_eval,
    optuna_seed=123, #再現性確保用のパラメータ
)

2.3.乱数シードの固定

numpy等で使用する乱数シードを固定する。実装は以下の通りです。

import numpy as np
import random as rn

np.random.seed(123)
rn.seed(123)

参考:sklearnのirisデータを使用したサンプルコード

# 乱数シードの固定
import numpy as np
import random as rn

np.random.seed(123)
rn.seed(123)

# ライブラリのインポート
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix

import pandas as pd

from optuna.integration import lightgbm as lgb

# iris(アヤメ)データインポート
iris = load_iris()

# 訓練用データとテストデータに分割
x_train, x_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.3,random_state=123)

#LGB用のデータに変形
lgb_train = lgb.Dataset(x_train, y_train)
lgb_eval = lgb.Dataset(x_test, y_test)

# 訓練方法の定義
params = {
    'objective': 'multiclass ',
    'num_class':3,
    'metric': 'multi_logloss',
    'verbosity': -1,
    'boosting_type': 'gbdt',
    'deterministic':True, #再現性確保用のパラメータ
    'force_row_wise':True  #再現性確保用のパラメータ
    }

booster = lgb.LightGBMTuner(
    params = params, 
    train_set = lgb_train,
    valid_sets=lgb_eval,
    optuna_seed=123, #再現性確保用のパラメータ
)

# 訓練の実施
booster.run()

# 訓練で得た最良のパラメータを表示
booster.best_params

# 訓練で得た最良のモデル(Boosterオブジェクト)を取得する
best_booster = booster.get_best_booster()

# テストデータに対して予測を実施
pred = best_booster.predict(x_test)
pred = np.argmax(pred, axis=1)

# 混同行列(Confusion Matrix)の表示
cm = confusion_matrix(y_test, pred)
print(cm)

私の環境では、上記を実行することで、ハイパーパラメータが以下の通り固定されました。もしかしたら、環境によって結果は変わるかもしれませんが、同一環境であれば同一の結果になると思います。