LightGBMの学習過程をTensorboardで表示する

# 1.この記事の内容

[LightGBM](https://lightgbm.readthedocs.io/en/v3.3.2/)の学習過程を[Tensorboard](https://www.tensorflow.org/tensorboard?hl=ja)で確認する方法を紹介します．

## 1-1.ポイント

- Tensorboardが読み込む学習ログを出力するために，[torch.utils.tensorboard](https://pytorch.org/docs/stable/tensorboard.html)を使う
- LightGBMのcallbacksに[add_scalars](https://pytorch.org/docs/stable/tensorboard.html#torch.utils.tensorboard.writer.SummaryWriter.add_scalars)でログを出力するカスタムコールバックを指定する

# 2.背景

[AI Dashboard](https://github.com/ryoma-jp/AI_Dashboard)には，最初にTensorFlowでDNNを学習する機能を実装しましたが，その際に学習過程をTensorboardで表示するようにしました．
その後，テーブルデータをLightGBMで学習する機能を実装した際に，学習過程の表示はTensorboardで統一したくなりました．

# 3.手順詳細

プログラム全体は[筆者のGitHub](https://github.com/ryoma-jp/AI_Dashboard/blob/1619bb1478e838422d631e66e6f3b0f60b014d99/django_project/app/machine_learning/lib/trainer/trainer_lgb.py)で公開しています．
本文には主要な箇所のみを記載します．

## 3-1. ログ出力準備

- SummaryWriterオブジェクト生成
```python:create summary writer object
from torch.utils.tensorboard import SummaryWriter

...

self.writer = SummaryWriter(log_dir=Path(output_dir, 'logs'))
```

- ログ出力するカスタムコールバックを定義します
```python:custom callback
class LogSummaryWriterCallback:
    """ Writing log callable class
    """
    
    def __init__(self, period=1, writer=None):
        self.period = period
        self.writer = writer
    
    def __call__(self, env):
        if (self.period > 0) and (env.evaluation_result_list) and (((env.iteration+1) % self.period)==0):
            if (self.writer is not None):
                scalars = {}
                for (name, metric, value, is_higher_better) in env.evaluation_result_list:
                    if (metric not in scalars.keys()):
                        scalars[metric] = {}
                    scalars[metric][name] = value
                    
                for key in scalars.keys():
                    self.writer.add_scalars(key, scalars[key], env.iteration+1)
            else:
                print(env.evaluation_result_list)
 
```
## 3-2. LightGBMの学習処理

- [lightgbm.train](https://lightgbm.readthedocs.io/en/latest/pythonapi/lightgbm.train.html)に与える``metric``パラメータでログ出力するメトリクスを指定します．  
下記は``L1``，``L2``，``RMSE``のログを出力する例です．
```python:parameters
self.params = {
    'objective': 'regression',
    'metric': 'l1,l2,rmse',
    'num_leaves': 32,
    'max_depth': 4,
    'feature_fraction': 0.5,
    'subsample_freq': 1,
    'bagging_fraction': 0.8,
    'min_data_in_leaf': 5,
    'learning_rate': learning_rate,
    'boosting': 'gbdt',
    'lambda_l1': 1,
    'lambda_l2': 5,
    'verbosity': -1,
    'random_state': 42,
    'early_stopping_rounds': 100,
}
```
- ``valid_names``と``valid_sets``にログ出力対象のデータを指定し，カスタムコールバックを指定します．
```python:training
self.model = lgb.train(
    self.params,
    train_data,
    valid_names=valid_names,
    valid_sets=valid_sets,
    num_boost_round=50000,
    callbacks=[
        lgb.log_evaluation(period=100),
        LogSummaryWriterCallback(period=100, writer=self.writer)
    ]
)
```

# 4.Tensorboardの出力例

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/983907/f3a165ae-66b9-f6da-48e6-a33b77d37e60.png)

# 5.さいごに

AI DashboardにはTensorで学習する処理を実装していたので，TensorFlowのAPIからできないか考えてみたのですが，``tf.keras.metrics``クラスを介する必要があり，データ型の整合(``tf.Torch``とPython標準のデータ型との変換)が複雑になりそうでしたので，``torch.utils.tensorboard``を使用する方法を採用しました．

このような使い方をしたくなる人がどれくらいいるか分かりませんが，ご参考まで．

# 6.関連リンク

- [Tensorboard](https://www.tensorflow.org/tensorboard?hl=ja)
- [torch.utils.tensorboard](https://pytorch.org/docs/stable/tensorboard.html)
- [lightgbm](https://lightgbm.readthedocs.io/en/v3.3.2/)
- [lightgbm.callback](https://lightgbm.readthedocs.io/en/latest/_modules/lightgbm/callback.html)
- [lightgbm metric-parameters](https://lightgbm.readthedocs.io/en/latest/Parameters.html#metric-parameters)