LightGBMのハイパーパラメータチューニング（Optuna）をしてみた

* 製造業出身のデータサイエンティストがお送りする記事
* 今回はLightGBMのハイパーパラメータチューニング（Optuna）を実装してみました。

##はじめに
過去に回帰モデル系の記事は纏めておりますので、細かい内容は他の記事を参考にしてください

* [scikit-learnを活用した機械学習の予測モデル](https://qiita.com/DS27/items/aa3f6d0f03a8053e5810)
* [不確かさを扱える新しい勾配ブースティングNGBoost](https://qiita.com/DS27/items/a0039c4eec78364c95ab)
* [PyCaretを使ってみた](https://qiita.com/DS27/items/bc56163b2cc48403deb0)

今回はLightGBMのハイパーパラメータチューニング（Optuna）について書きます。

##LightGBMの実装
まずは、LightGBMをハイパーパラメータチューニングをせずに実装してみます。
今回もUCI Machine Learning Repositoryで公開されているボストン住宅の価格データを用いて予測モデルを構築します。

|項目|概要|
|:---|:---|
|データセット|・boston house-price　|
|サンプル数|・506個　|
|カラム数|・14個　|

pythonのコードは下記の通りです。

```php
# ライブラリーのインポート
import os

import pandas as pd
import numpy as np

import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline

# ボストンの住宅価格データ
from sklearn.datasets import load_boston

# 前処理
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split

# LightGBM
import lightgbm as lgb

# 評価指標
from sklearn.metrics import r2_score
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import mean_squared_error

# データセットの読込み
boston = load_boston()

# 説明変数の格納
df = pd.DataFrame(boston.data, columns = boston.feature_names)
# 目的変数の追加
df['MEDV'] = boston.target

# データの中身を確認
df.head()
```

![スクリーンショット 2020-11-09 20.33.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/743453/2ffa1a41-98dd-2798-1f6d-8f3385bf5f75.png)

次にLightGBMの事前準備をします。

```php
# ランダムシード値
RANDOM_STATE = 10

# 学習データと評価データの割合
TEST_SIZE = 0.2

# 学習データと評価データを作成
x_train, x_test, y_train, y_test = train_test_split(df.iloc[:, 0:df.shape[1]-1],
                                                    df.iloc[:, df.shape[1]-1],
                                                    test_size=TEST_SIZE,
                                                    random_state=RANDOM_STATE)

# trainのデータセットの3割をモデル学習時のバリデーションデータとして利用する
x_train, x_valid, y_train, y_valid = train_test_split(x_train,
                                                    y_train,
                                                    test_size=TEST_SIZE,
                                                    random_state=RANDOM_STATE)

# LightGBMを利用するのに必要なフォーマットに変換
lgb_train = lgb.Dataset(x_train, y_train)
lgb_eval = lgb.Dataset(x_valid, y_valid, reference=lgb_train)
```

次にハイパーパラメータの設定をします。

```php
# LightGBMのパラメータ設定
params = {
    'boosting_type': 'gbdt',
    'objective': 'regression',
    'metric': {'l2', 'l1'},
    'num_leaves': 50,
    'learning_rate': 0.05,
    'feature_fraction': 0.9,
    'bagging_fraction': 0.8,
    'bagging_freq': 5,
    'vervose': 0
}
```

あとは、モデルの学習と予測を行います。

```php

# LightGBM学習
gbm = lgb.train(params,
                lgb_train,
                num_boost_round=200,
                valid_sets=lgb_eval,
                early_stopping_rounds=50
               )

# LightGBM推論
y_pred = gbm.predict(x_test, num_iteration=gbm.best_iteration)

# 評価
def calculate_scores(true, pred):
    """全ての評価指標を計算する

    Parameters
    ----------
    true (np.array)       : 実測値
    pred (np.array)       : 予測値

    Returns
    -------
    scores (pd.DataFrame) : 各評価指標を纏めた結果

    """
    scores = {}
    scores = pd.DataFrame({'R2': r2_score(true, pred),
                          'MAE': mean_absolute_error(true, pred),
                          'MSE': mean_squared_error(true, pred),
                          'RMSE': np.sqrt(mean_squared_error(true, pred))},
                           index = ['scores'])
    return scores

scores = calculate_scores(y_test, y_pred)
print(scores)
```

出力結果は下記のようになります。

```
              R2       MAE        MSE      RMSE
scores  0.782247  3.091336  22.772769  4.772082
```

##LightGBMのハイパーパラメータチューニング（Optuna）
次は、Optunaを使ってハイパーパラメータチューニングをしてみます。

```php
# LightGBMのパラメータ設定
params = {
    'boosting_type': 'gbdt',
    'objective': 'regression',
    'metric': {'l2', 'l1'},
    'num_leaves': 50,
    'learning_rate': 0.05,
    'feature_fraction': 0.9,
    'bagging_fraction': 0.8,
    'bagging_freq': 5,
    'vervose': 0
}
```

あとは、モデルの学習と予測を行います。

```php
# ライブラリーのインポート
import os

import pandas as pd
import numpy as np

import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline

# ボストンの住宅価格データ
from sklearn.datasets import load_boston

# 前処理
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split

# LightGBM
import optuna.integration.lightgbm as lgb

# 評価指標
from sklearn.metrics import r2_score
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import mean_squared_error

# データセットの読込み
boston = load_boston()

# 説明変数の格納
df = pd.DataFrame(boston.data, columns = boston.feature_names)
# 目的変数の追加
df['MEDV'] = boston.target

# データの中身を確認
df.head()

# ランダムシード値
RANDOM_STATE = 10

# 学習データと評価データの割合
TEST_SIZE = 0.2

# 学習データと評価データを作成
x_train, x_test, y_train, y_test = train_test_split(df.iloc[:, 0:df.shape[1]-1],
                                                    df.iloc[:, df.shape[1]-1],
                                                    test_size=TEST_SIZE,
                                                    random_state=RANDOM_STATE)

# trainのデータセットの3割をモデル学習時のバリデーションデータとして利用する
x_train, x_valid, y_train, y_valid = train_test_split(x_train,
                                                    y_train,
                                                    test_size=TEST_SIZE,
                                                    random_state=RANDOM_STATE)

# LightGBMを利用するのに必要なフォーマットに変換
lgb_train = lgb.Dataset(x_train, y_train)
lgb_eval = lgb.Dataset(x_valid, y_valid, reference=lgb_train)
```

ここからハイパーパラメータチューニングになります。

```php

# ベストなパラメータ、途中経過を保存する
params = {
    'objective': 'mean_squared_error',
    'metric': 'mae',
    "verbosity": -1,
    "boosting_type": "gbdt",
}

best_params, history = {}, []

# LightGBM学習
gbm = lgb.train(params,
                lgb_train,
                num_boost_round=200,
                valid_sets=[lgb_train, lgb_eval],
                early_stopping_rounds=50
               )

best_params = gbm.params
best_params
```

最終的に得られたパラメータは下記です。

```
{'objective': 'mean_squared_error',
 'metric': 'l1',
 'verbosity': -1,
 'boosting_type': 'gbdt',
 'feature_pre_filter': False,
 'lambda_l1': 6.927335476175293e-05,
 'lambda_l2': 0.034542125556912426,
 'num_leaves': 31,
 'feature_fraction': 0.8999999999999999,
 'bagging_fraction': 1.0,
 'bagging_freq': 0,
 'min_child_samples': 5,
 'num_iterations': 200,
 'early_stopping_round': 50}
```

あとは、得られたハイパーパラメータを使って予測します。

```php
# LightGBM推論
y_pred = gbm.predict(x_test, num_iteration=gbm.best_iteration)

# 評価
def calculate_scores(true, pred):
    """全ての評価指標を計算する

    Parameters
    ----------
    true (np.array)       : 実測値
    pred (np.array)       : 予測値

    Returns
    -------
    scores (pd.DataFrame) : 各評価指標を纏めた結果

    """
    scores = {}
    scores = pd.DataFrame({'R2': r2_score(true, pred),
                          'MAE': mean_absolute_error(true, pred),
                          'MSE': mean_squared_error(true, pred),
                          'RMSE': np.sqrt(mean_squared_error(true, pred))},
                           index = ['scores'])
    return scores

scores = calculate_scores(y_test, y_pred)
print(scores)
```

得られた結果は下記です。

```
              R2       MAE        MSE      RMSE
scores  0.847045  2.843303  15.996148  3.999518
```

##さいごに
最後まで読んで頂き、ありがとうございました。
今回LightGBMのハイパーパラメータチューニング（Optuna）をしてみました。
Optunaを使ったらパラメータチューニングはこんなに簡単にできるですね。便利なので、今後も使っていこうと思います。

訂正要望がありましたら、ご連絡頂けますと幸いです。