学習中モデルをJupyter Notebook上のTensorBordでモニタリングする

#概要
TensorFlowを用いてモデルのトレーニングをしている際，トレーニングがうまくいっているかどうか確認したくなります．`model.fit()`に引数`verbose=1`を与えることで下記のようにトレーニングの進捗状況を確認することが可能です．

```
Train on 60000 samples, validate on 10000 samples
Epoch 1/5
60000/60000 [==============================] - 11s 184us/sample - loss: 0.4968 - accuracy: 0.8223 - val_loss: 0.4216 - val_accuracy: 0.8481
Epoch 2/5
60000/60000 [==============================] - 11s 176us/sample - loss: 0.3847 - accuracy: 0.8587 - val_loss: 0.4056 - val_accuracy: 0.8545
Epoch 3/5
60000/60000 [==============================] - 11s 176us/sample - loss: 0.3495 - accuracy: 0.8727 - val_loss: 0.3600 - val_accuracy: 0.8700
Epoch 4/5
60000/60000 [==============================] - 11s 179us/sample - loss: 0.3282 - accuracy: 0.8795 - val_loss: 0.3636 - val_accuracy: 0.8694
Epoch 5/5
60000/60000 [==============================] - 11s 176us/sample - loss: 0.3115 - accuracy: 0.8839 - val_loss: 0.3438 - val_accuracy: 0.8764
```

しかし，この出力からトレーニングがうまくいっているかどうか理解するのは難しいです．

そこで，TensorBordを使って可視化することを考えます．TensorBordは，下図のようにトレーニングの進捗状況を可視化し，トレーニングがうまくいっているかどうかを視覚的に理解することを助けてくれます．

|![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207140/132ff0db-062e-9bc7-df59-66090a84a71f.png)|
|---|
|ここでは，トレーニングがうまくいった場合とそうでない場合のaccuracyとlossをグラフにプロットしています．|


TensorBordではaccuracyやloss以外に，distoributionsやhistogramも可視化して確認することができます．

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207140/15f8b136-942c-3c94-69bc-268b15bc4ab7.png)|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207140/e6795bd5-48c8-db1c-b2fc-87733d6611d3.png)|
|---|---|
|distributionsの可視化|histogramの可視化|

学習中モデルをモニタリングするとき，accuracyやlossはTensorBordの更新ボタンを押すことで現在の状況を確認することができますが，distributionsやhistogramはその限りではありません．この方法を探しても日本語では見つからなかったのでここに書き留めておきます．

#環境
DockerでTensorFlowのnightlyイメージをpullしてきます．TensorFlowのバージョンを出力させると`2.1.0-dev20200101`となっていました．

```bash
docker pull tensorflow/tensorflow:nightly-gpu-py3-jupyter
```

これを起動します．このとき，重要なのがポートフォワーディングです．まず，jupyterを起動するために8888番ポートをフォワーディングします．さらに，TensorBoardがデフォルトで使用する6006番ポートをフォワーディングしなければなりません．ポートフォワーディングには`-p`オプションを使用します．

```bash
docker run --gpus all -it -v `pwd`:/tf -p 8888:8888 -p 6006:6006 tensorflow/tensorflow:nightly-gpu-py3-jupyter
```

上記のコマンドを実行すると，Jupyter Notebookを開くためのリンクが表示されるので，そこにアクセスしてJupyter Notebookを開きます．

あとは適当にTensorFlowでプログラミングをしていきます．

#TensorBordを使う準備
TensorBordを使うためにはextentionを読み込む必要があるようです．また，そのextensionを読み込もうとするとcloud-tpu-clientをインストールせよと言われます．なので，次のようにすると良いでしょう．

```python
!pip install cloud-tpu-client
%load_ext tensorboard
```

次に，可視化の対象となるlogファイルを学習中に書き出すcallbackを作ります．作りますといっても自分で実装して作る必要はなく`tensorflow.keras.callbacks.TensorBoard`というクラスのインスタンスを生成すれば良いです．

```python
tb_cb = tf.keras.callbacks.TensorBoard(
    log_dir=log_dir,
    histogram_freq=1,
    write_images=True
)
```

このインスタンスを`model.fit()`の引数に渡すことでlogファイルが生成されます．

```python
history=model.fit(
        datagen_train,
        steps_per_epoch=len(x_train) // batch_size,
        validation_data=datagen_validate,
        validation_steps=len(x_validate) // batch_size,
        epochs=epochs,
        shuffle=False,
        callbacks=[tb_cb] 
    )
```

#Jupyter Notebook上でTensorBordを起動する
ここがひじょうに重要なところです．Juoyter Notebook上でTensorBoardを起動するのは簡単なのですが，オプションをきちんと設定しないと，distributionsやhistoryを学習中に見ることができません．私の場合，次のように書くとうまくいきました．

```python
%tensorboard --logdir log --bind_all --port 6006 --reload_multifile true
```

基本的にTensorBoardは`-logdir`オプションでlogファイルを保存したディレクトリを指定することで起動できます．私の場合（もしかすると，Docker + Jupyter Notebookの組み合わせ?）は，`--bind_all`と`--port 6006`を追加しないと起動しませんでした．起動すると，出力セルのなかでTensorBoardを操作できる状態になります．

さて，最後の`--reload_multifile true`を追加することが重要です．これをなくすとdistributionsやhistoryを学習中に見ることができません．

なお，このTensorBoardの起動は`model.fit()`の前にしておく必要があります．なぜなら，Jupyter Notebookは同時に1つのセルしか実行できないからです．

#まとめ

最後に，全体の流れをまとめてみます．

1. 必要な場合は環境を構築

    ```bash 
docker pull tensorflow/tensorflow:nightly-gpu-py3-jupyter
docker run --gpus all -it -v `pwd`:/tf -p 8888:8888 -p 6006:6006 tensorflow/tensorflow:nightly-gpu-py3-jupyter
    ```

2. extentionの読込み

    ```python
!pip install cloud-tpu-client
%load_ext tensorboard
    ```

3. データの前処理や学習モデルの構築
人によってさまざまなのでここでは省略します．

4. `tensorflow.keras.callbacks.TensorBoard`というクラスのインスタンスを生成

    ```python
tb_cb = tf.keras.callbacks.TensorBoard(
    log_dir=log_dir,
    histogram_freq=1,
    write_images=True
)
    ```

5. TensorBoardの起動

    ```python
%tensorboard --logdir log --bind_all --port 6006 --reload_multifile true
    ```

6. トレーニングの開始

    ```python
history=model.fit(
        datagen_train,
        steps_per_epoch=len(x_train) // batch_size,
        validation_data=datagen_validate,
        validation_steps=len(x_validate) // batch_size,
        epochs=epochs,
        shuffle=False,
        callbacks=[tb_cb] 
    )
    ```

以上です．

#参照

1. https://www.tensorflow.org/tensorboard/tensorboard_in_notebooks
ここにJupyter Notebook上でTensorBoardを起動する方法が載っています．

2. https://github.com/tensorflow/tensorboard
ここのREADME.mdのFrequently Asked Questionsのセクションに `--reload_multifile`オプションに関する記述があります．