KerasでCallbackを使用する

# はじめに

Kerasで学習する際に便利そうなコールバックがあったので使ってみました。
今回は下記のコールバックを使用してみます。

- EarlyStopping
- TensorBoard
- CSVLogger

下記にKeras公式サイトのコールバックの説明があります。
https://keras.io/ja/callbacks/

# EarlyStopping

監視対象の値が変化しなくなったときに学習を終了します。エポック数を多めに指定しても途中で終了してくれるので安心です。
ここでは、エポック数10回の間に損失値の変化が0.0001より小さければ、改善がみられないと判断して学習を終了するようにします。

```python
es_cb = keras.callbacks.EarlyStopping(
    monitor='loss', min_delta=0.0001, patience=10, mode='auto')
```

# TensorBoard

TensorBoardによる可視化を行います。
オプションがいろいろあるのですが、今回はログ出力先だけ指定して残りはデフォルト値のままとします。

```python
tb_cb = keras.callbacks.TensorBoard(log_dir='./logs')
```

# CSVLogger

エポックの結果をCSVファイルに出力してくれます。

```python
csv_logger = keras.callbacks.CSVLogger('training.csv')
```

# Pythonプログラム

前回と同様、一次関数を学習させます。
```model.fit()```で学習時にコールバックの指定を追加しています。
また、```validation_data```を指定して、エポックごとにテストデータの検証も行うようにしています。

```python
model.fit(
    x_train, y_train,
    epochs=500,
    callbacks=[es_cb, tb_cb, csv_logger],
    validation_data=(x_test, y_test))
```

プログラムの全体です。

```python
import matplotlib.pyplot as plt
import numpy as np
from tensorflow import keras
from sklearn.model_selection import train_test_split

# Y = 2X + 3を求める関数とする。
x = np.linspace(0, 10, num=50)
y = 2 * x + 3

# トレーニングデータと検証データに分割
x_train, x_test, y_train, y_test = train_test_split(x, y, shuffle=False)

# ネットワークを作成
model = keras.models.Sequential([
    keras.layers.Dense(1, input_shape=(1,), activation="linear"),
])

# コールバックの作成
es_cb = keras.callbacks.EarlyStopping(
    monitor='loss', min_delta=0.0001, patience=10, mode='auto')
tb_cb = keras.callbacks.TensorBoard(log_dir='./logs')
csv_logger = keras.callbacks.CSVLogger('./training.csv')

# オプティマイザはSGD、損失関数はMSEを使用
model.compile(optimizer='sgd', loss='mse')

model.fit(
    x_train, y_train,
    epochs=500,
    callbacks=[es_cb, tb_cb, csv_logger],
    validation_data=(x_test, y_test))

print(model.get_weights())

plt.scatter(x, y, label='data', color='gray')
plt.plot(x_train, model.predict(x_train), label='train', color='blue')
plt.plot(x_test, model.predict(x_test), label='test', color='red')
plt.legend()
plt.show()
```

# 結果

エポック数に500を指定していますが、405エポックで学習が終了しました。

## TensorBoardの表示

TensorBoardを起動します。

```
$ tensorboard --logdir=./logs

Serving TensorBoard on localhost; to expose to the network, use a proxy or pass --bind_all
TensorBoard 2.4.1 at http://localhost:6006/ (Press CTRL+C to quit)
```

ブラウザで http://localhost:6006/ にアクセスするとTensorBoardを見ることができます。
グラフのオレンジの線がトレーニングデータ、青の線がテストデータです。
学習とともに損失値が小さくなっているのがわかります。

![tensorboard2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1106967/15e700b1-f514-faa9-3d94-41ff84b348cf.png)

## CSVLoggerの結果

CSVLoggerで指定したCSVファイルもできています。

```
epoch,loss,val_loss
0,254.1975860595703,116.32344818115234
1,35.86260986328125,6.254454612731934
2,5.318000793457031,0.04576351121068001
3,1.8426451683044434,1.2428233623504639
4,1.295862078666687,2.55403733253479
5,1.1909117698669434,2.0331602096557617
6,1.181778073310852,2.214395761489868
7,1.1415034532546997,3.4069273471832275
8,1.1195648908615112,3.5103976726531982
9,1.0969243049621582,2.3636646270751953
10,1.0725418329238892,3.2535252571105957
11,1.0593643188476562,2.874397039413452
(以下略)
```
PandasとMatplotlibでグラフ化してみます。
最初の損失値が大きいので、4番目以降のエポックのデータを使ってグラフにします。

```python
import pandas as pd
import matplotlib.pyplot as plt

df = pd.read_csv('training.csv', index_col='epoch')
df.iloc[3:].plot()
plt.legend()
plt.show()
```

TensorBoardと同じようなグラフを作成することができました。

![loss2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1106967/80c3b340-71b1-b481-b835-a3af883e7c62.png)

# おわりに

これでモデルを作成して、学習状況を確認する準備ができました。
次回はもっと複雑なデータを使った学習を行う予定です。