LightGBMとOptunaを導入・動かしてみる

Source Code Software Computer機械学習

はじめに

Kaggleなどデータ分析でよく使用されているLightGBMについて、Optunaでハイパーパラメータを調整してとりあえず動かしてみるまでの手順をまとめました。

2020/4/13追記

optuna 0.18より、LightGBM用のハイパーパラメータチューニング機能が実装されたようです。

従来版に加え、新版でのチューニング方法を追記しました。(optuna 1.3.0)

2020/6/6追記

optunaのバージョンアップに伴い、内容を変更しました。

環境は以下の通りです。

  • python 3.7.7
  • lightgbm 2.3.1
  • optuna 1.5.0
  • scikit-learn 0.23.1

各パッケージのインストール

各パッケージはpipで導入します。

pip install lightgbm
pip install optuna

windows以外のOSについてやGPU版など、いろいろとオプションがあるみたいなので、詳しくはリンク先を参照してください。

lightgbm
LightGBM Python Package

LightGBMを動かしてみる

scklearnのbostonデータセットを使って回帰問題についての学習をさせました。

lightgbmのインターフェースは、 lightgbm形式と、 sklearn形式とが用意されています。ここではsklearn形式を使用しています。sklearn形式では、本来の形式と比較して調整できるハイパーパラメータが少ないなどのデメリットがある一方で、sklearn形式に対応した関連パッケージとの連携がとりやすくなります。

LightGBM使用例

import lightgbm as lgb
from sklearn.model_selection import train_test_split
import sklearn.datasets
from sklearn.metrics import mean_squared_error,r2_score
import numpy as np

# scikit-learnでお試しデータの準備
boston = sklearn.datasets.load_boston()
X_trainval, X_test, y_trainval, y_test = train_test_split(boston.data, boston.target, random_state=0)
X_train, X_val, y_train, y_val = train_test_split(X_trainval, y_trainval, random_state=0)

# LightGBMで学習+予測
model = lgb.LGBMRegressor(random_state=0)
model.fit(X_train, y_train,eval_set=(X_val,y_val),early_stopping_rounds=100) # early_stoppingでモデル改善が進まなくなったときには学習を打ち切るように設定

y_trainval_pred = model.predict(X_trainval)
y_test_pred = model.predict(X_test)

# 評価値
r2_trainval = r2_score(y_trainval, y_trainval_pred)
r2_test = r2_score(y_test, y_test_pred)

print("r2_train:{0:.4}".format(r2_trainval))
print("r2_test:{0:.4}".format(r2_test))
実行結果

r2_train:0.957
r2_test:0.7629

Optunaによるハイパーパラメータの調整の新版・従来版の違い

詳細な説明は公式を参照ですが、概略としては以下のようです。

  • 従来版 :試行ごとに全てのハイパーパラメータを変化させながら探索する
  • 新版  :各パラメータで最良のものを1つずつ順番に探索する(Step-Wise)

ハイパーパラメータの調整:新版

新版における最適化を行うには、「import optuna」ではなく、lightGBMのimportを以下の1行目のように書き換えます。

また、新版では、lightGBMのインターフェースはsklearn形式でなく、lightGBM形式で実装しています。

最適化後のトレニンーグデータでのr2 は 0.981と、最適化前の0.957と比較して改善した結果となりました。

また、テストデータでのスコアも、0.7629→0.7690と若干改善しています。

import optuna.integration.lightgbm as lgb_o
from sklearn.model_selection import train_test_split
import sklearn.datasets
from sklearn.metrics import r2_score

# scikit-learnでお試しデータの準備
boston = sklearn.datasets.load_boston()
X_trainval, X_test, y_trainval, y_test = train_test_split(boston.data, boston.target, random_state=0)
X_train, X_val, y_train, y_val = train_test_split(X_trainval, y_trainval, random_state=0)


# LightGBM用のデータセットに変換
train = lgb_o.Dataset(X_train, y_train)
val = lgb_o.Dataset(X_val, y_val)

# ハイパーパラメータサーチ&モデル構築
params = {'objective': 'regression',
          'metric': 'rmse',
          'random_seed':0} 

gbm_o = lgb_o.train(params,
                    train,
                    valid_sets=val,
                    early_stopping_rounds=100,
                    verbose_eval=200,)

# 調整後モデルで予測の実行
y_trainval_pred = gbm_o.predict(X_trainval,num_iteration=gbm_o.best_iteration)
y_test_pred = gbm_o.predict(X_test,num_iteration=gbm_o.best_iteration)

# ベストパラメータの取得
best_params = gbm_o.params
print("  Params: ")
for key, value in best_params.items():
    print("    {}: {}".format(key, value))

# 評価スコアの計算
r2_trainval = r2_score(y_trainval, y_trainval_pred)
r2_test = r2_score(y_test, y_test_pred)

print("r2_train:{0:.4}".format(r2_trainval))
print("r2_test:{0:.4}".format(r2_test))
調整後のベストパラメータ

Params:
 objective: regression # 設定値
 metric: rmse # 設定値
 random_seed: 0 # 設定値
 lambda_l1: 0.1449187981656151
 lambda_l2: 8.02851945799398
 num_leaves: 31
 feature_fraction: 1.0
 bagging_fraction: 1.0
 bagging_freq: 0
 min_child_samples: 10

実行結果

r2_train:0.981
r2_test:0.769

ただし、従来版と異なりrandom_seedの固定方法がわかりませんでした。

従って、実行ごとに結果は若干異なります。

ハイパーパラメータの調整:従来版

LightGBM専用でなく、従来の汎用的なコードで最適化を行うには、大まかには以下のような形になります。

Optuna使用 概略

# optunaで評価するスコアを計算して返す関数
def objectives(trial):
    パラメータサーチを行いたい変数と、その範囲設定
    評価するスコアの計算(r2, rmseなど)
    return 評価したスコア

# optunaによる最適化呼び出し
opt = optuna.create_study()
opt.optimize(objectives, n_trials=50) #試行回数の設定

パラメータサーチを行いたい変数と、その範囲の設定は以下のように行います。

数値パラメータの上下限はlow, highで設定します。

基本的にはlow,highに設定した値は含まれますが、loguniform, uniform,floatのhighは範囲に含まれないようです。

Optuna ハイパーパラメータサーチ設定

# カテゴリ変数
suggest_categorical(name, choices)
# カテゴリ変数 例
kernel = trial.suggest_categorical('kernel', ['linear', 'poly', 'rbf'])

# 離散パラメータ
suggest_discrete_uniform(name, low, high, 離散値のステップ)
# 離散パラメータ 例
 subsample = trial.suggest_discrete_uniform('subsample', 0.1, 1.0, 0.1)

# 整数パラメータ
suggest_int(name, low, high)
# 整数パラメータ 例
n_estimators = trial.suggest_int('n_estimators', 50, 400)

# 連続パラメータ(log)
suggest_loguniform(name, low, high)
# 連続パラメータ(log) 例
c = trial.suggest_loguniform('c', 1e-5, 1e2)

# 連続パラメータ
suggest_uniform(name, low, high)
# 連続パラメータ 例
dropout_rate = trial.suggest_uniform('dropout_rate', 0, 1.0)

# 小数パラメータ(ver1.3.0より実装)
suggest_float(name, low, high, )
# 小数パラメータ 例
trial.suggest_float('momentum', 0.0, 1.0)
trial.suggest_float('power_t', 0.2, 0.8, step=0.1) # 離散化ステップの設定
trial.suggest_float('learning_rate_init',1e-5, 1e-3, log=True) # logで設定

optunaでlightgbmの2パラメータを最適化したコードが以下の通りです。

最適化後のトレニンーグデータでのr2 は 0.9588と、最適化前の0.957と比較して改善した結果となりました。

各パラメータに拘りがない限りは新版の方が、コード簡略で良さそうな気はします。

LightGBM, Optuna組み合わせ例

import lightgbm as lgb
from sklearn.model_selection import train_test_split
import sklearn.datasets
import optuna

def objectives(trial):
    # scikit-learnでお試しデータの準備
    boston = sklearn.datasets.load_boston()
    X_trainval, X_test, y_trainval, y_test = train_test_split(boston.data, boston.target, random_state=0)
    X_train, X_val, y_train, y_val = train_test_split(X_trainval, y_trainval, random_state=0)

    # optunaでのハイパーパラメータサーチ範囲の設定
    params = {
            'num_leaves': trial.suggest_int('num_leaves', 2, 256),
            'min_child_samples': trial.suggest_int('min_child_samples', 2, 100),}

    # LightGBMで学習+予測
    model = lgb.LGBMRegressor(**params)# 追加部分
    model.fit(X_train, y_train,eval_set=(X_val,y_val),early_stopping_rounds=100,verbose=False)

    y_pred = model.predict(X_test)

    # 検証データを用いた評価
    score = model.score(X_val, y_val)
    
    return score

# optunaによる最適化呼び出し
opt = optuna.create_study(direction='maximize',sampler=optuna.samplers.RandomSampler(seed=0))
opt.optimize(objectives, n_trials=50)

# 最適パラメータ取得
trial = opt.best_trial
params_best = dict(trial.params.items())
params_best['random_seed'] = 0
    
# 最適パラメータで学習/予測    
model_o = lgb.LGBMRegressor(**params_best)# 追加部分
model_o.fit(X_train, y_train,eval_set=(X_val,y_val),early_stopping_rounds=100,verbose=False)    

y_trainval_pred = model_o.predict(X_trainval)
y_test_pred = model_o.predict(X_test)

r2_trainval = r2_score(y_trainval, y_trainval_pred)
r2_test = r2_score(y_test, y_test_pred)

# テストデータを用いた評価
print("r2_train:{0:.4}".format(r2_trainval))
print("r2_test:{0:.4}".format(r2_test))
実行結果

r2_train:0.9588
r2_test:0.7650

クロスバリデーション

参考:クロスバリデーションでハイパーパラメータサーチを行うには以下のようにできます

以上が、LightGBMとOptunaを導入して動かしてみるまでの使い方となります。

lightgbmの変数やoptunaの設定の詳細は以下のような公式を参照するとよいかと思います。

lightgbm.LGBMRegressor — LightGBM 4.3.0.99 documentation
https://github.com/pfnet/optuna/tree/master/examples
Optuna: A hyperparameter optimization framework — Optuna 4.0.0.dev documentation

コメント

  1. […] LightGBMとOptunaを導入・動かしてみるLightGBMとOptunaをインストールし、Optunaでハイパーパラメータを調整してLightGBMを動かしてみる手順のまとめです。kiseno-log.com2019.11.05 […]

タイトルとURLをコピーしました