[TF]KerasからTensorboardを使用する方法

[keras](https://github.com/fchollet/keras)は**Theano/Tensorflow**のwrapper libraryで、**keras**を使うことで、Theano/Tensorflowでベタで書くよりも少ないコード量で記述することができます。
kerasに関しては、すでに説明しているページもあるのでそちらに譲るとして、kerasからTensorboardを使う方法を調べたので簡単に説明したいと思います。

**Tensorboard**用のlogを出力するときは**Callback**という仕組みを使用します。登録した**Callback**は決まったタイミングで呼ばれます。Callされるタイミングは、**Callback**の種類により異なりますが、**Tensorboard**用の**Callback**は**学習の直前**と**毎epochの終了時**にCallされます。

学習の直前では、**tf.merge_all_summaries**と**tf.train.SummaryWriter**が呼ばれます。
毎epochの終わりに、**add_summary**が呼ばれてlogが出力されます。
また、毎epochでは多すぎるというような場合には、callbackを呼ぶ頻度を変更することもできます。

**Tensorflow**用の**Callback**は、**keras.callbacks.TensorBoard**で作成します。
作成したCallbackはfitに渡します。
**histogram_freq**には、**Tensorboard**のhistogram用データを出力する頻度を指定します。
histogram_freq=1だと、毎epochデータが出力されます。

```lang:python
    tb_cb = keras.callbacks.TensorBoard(log_dir=log_filepath, histogram_freq=1)
    cbks = [tb_cb]
            
    history = model.fit(X_train, Y_train, batch_size=batch_size, nb_epoch = nb_epoch, verbose=1, callbacks=cbks, validation_data=(X_test, Y_test))

```

**Dropout**のように**Training**と**Test**でネットワーク構造が変わる場合は、Model構築前に下記の記述が必要になります。記述しないとModel構築時に死にます。

```lang:python
    KTF.set_learning_phase(1)
```

**learning_phase**は**Training**と**Test**で動作が異なる場合に使います。**Training**と**Test**でネットワークが変わらない場合は入りません。

**learning_phase**は、**Training**時に**１**にセットされ、**Test**時には**０**にセットされます。この値によりネットワークを切り替えています。値の入力は**keras**が自動で行っているので何もしなくてよいです。例えば**Validation**の時は**0**が設定されます。

# 簡単な例

簡単な**Maltilayer Neural Network**の例はこんな感じになります。

```lang:python
import numpy as np
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers.core import Dense, Dropout, Activation
from keras.optimizers import SGD
from keras.utils import np_utils
import keras.callbacks
import keras.backend.tensorflow_backend as KTF
import tensorflow as tf

batch_size = 128
nb_classes = 10
nb_epoch   = 20
nb_data    = 28*28
log_filepath = './log'

# load data
(X_train, y_train), (X_test, y_test) = mnist.load_data()

# reshape
X_train = X_train.reshape(X_train.shape[0], X_train.shape[1]*X_train.shape[2])
X_test = X_test.reshape(X_test.shape[0], X_test.shape[1]*X_test.shape[2])

# rescale
X_train = X_train.astype(np.float32)
X_test  = X_test.astype(np.float32)
X_train /= 255
X_test  /= 255

# convert class vectors to binary class matrices (one hot vectors)
Y_train = np_utils.to_categorical(y_train, nb_classes)
Y_test = np_utils.to_categorical(y_test, nb_classes)

old_session = KTF.get_session()

with tf.Graph().as_default():
    session = tf.Session('')
    KTF.set_session(session)
    KTF.set_learning_phase(1)
    # build model
    model = Sequential()
    model.add(Dense(512, input_shape=(nb_data,), init='normal',name='dense1'))
    model.add(Activation('relu', name='relu1'))
    model.add(Dropout(0.2, name='dropout1'))
    model.add(Dense(512, init='normal', name='dense2'))
    model.add(Activation('relu', name='relu2'))
    model.add(Dropout(0.2, name='dropout2'))
    model.add(Dense(10, init='normal', name='dense3'))
    model.add(Activation('softmax', name='softmax1'))       
    model.summary()

    model.compile(loss='categorical_crossentropy', optimizer=SGD(lr=0.01), metrics=['accuracy'])

    tb_cb = keras.callbacks.TensorBoard(log_dir=log_filepath, histogram_freq=1)
    cbks = [tb_cb]
            
    history = model.fit(X_train, Y_train, batch_size=batch_size, nb_epoch = nb_epoch, verbose=1, callbacks=cbks, validation_data=(X_test, Y_test))

    score = model.evaluate(X_test, Y_test, verbose=0)
    print('Test score:', score[0])
    print('Test accuracy;', score[1])

KTF.set_session(old_session)
```

これを実行すると**Keras**の実行時のログが標準出力に出力されます。

```
Layer (type)                       Output Shape        Param #     Connected to                     
====================================================================================================
dense1 (Dense)                     (None, 512)         401920      dense_input_1[0][0]              
____________________________________________________________________________________________________
relu1 (Activation)                 (None, 512)         0           dense1[0][0]                     
____________________________________________________________________________________________________
dropout1 (Dropout)                 (None, 512)         0           relu1[0][0]                      
____________________________________________________________________________________________________
dense2 (Dense)                     (None, 512)         262656      dropout1[0][0]                   
____________________________________________________________________________________________________
relu2 (Activation)                 (None, 512)         0           dense2[0][0]                     
____________________________________________________________________________________________________
dropout2 (Dropout)                 (None, 512)         0           relu2[0][0]                      
____________________________________________________________________________________________________
dense3 (Dense)                     (None, 10)          5130        dropout2[0][0]                   
____________________________________________________________________________________________________
softmax1 (Activation)              (None, 10)          0           dense3[0][0]                     
====================================================================================================
Total params: 669706
____________________________________________________________________________________________________
Train on 60000 samples, validate on 10000 samples
Epoch 1/20
60000/60000 [==============================] - 5s - loss: 1.2522 - acc: 0.6646 - val_loss: 0.5603 - val_acc: 0.8695
Epoch 2/20
60000/60000 [==============================] - 5s - loss: 0.5533 - acc: 0.8445 - val_loss: 0.3862 - val_acc: 0.8962
Epoch 3/20
60000/60000 [==============================] - 5s - loss: 0.4381 - acc: 0.8728 - val_loss: 0.3291 - val_acc: 0.9084
Epoch 4/20
60000/60000 [==============================] - 5s - loss: 0.3879 - acc: 0.8867 - val_loss: 0.2963 - val_acc: 0.9147
Epoch 5/20
60000/60000 [==============================] - 6s - loss: 0.3536 - acc: 0.8975 - val_loss: 0.2753 - val_acc: 0.9198
Epoch 6/20
60000/60000 [==============================] - 5s - loss: 0.3271 - acc: 0.9047 - val_loss: 0.2575 - val_acc: 0.9268
Epoch 7/20
60000/60000 [==============================] - 5s - loss: 0.3059 - acc: 0.9113 - val_loss: 0.2421 - val_acc: 0.9301
Epoch 8/20
60000/60000 [==============================] - 5s - loss: 0.2873 - acc: 0.9168 - val_loss: 0.2302 - val_acc: 0.9331
Epoch 9/20
60000/60000 [==============================] - 5s - loss: 0.2751 - acc: 0.9199 - val_loss: 0.2198 - val_acc: 0.9359
Epoch 10/20
60000/60000 [==============================] - 5s - loss: 0.2612 - acc: 0.9246 - val_loss: 0.2092 - val_acc: 0.9386
Epoch 11/20
60000/60000 [==============================] - 5s - loss: 0.2507 - acc: 0.9274 - val_loss: 0.1998 - val_acc: 0.9416
Epoch 12/20
60000/60000 [==============================] - 6s - loss: 0.2383 - acc: 0.9311 - val_loss: 0.1930 - val_acc: 0.9437
Epoch 13/20
60000/60000 [==============================] - 5s - loss: 0.2288 - acc: 0.9340 - val_loss: 0.1859 - val_acc: 0.9461
Epoch 14/20
60000/60000 [==============================] - 5s - loss: 0.2195 - acc: 0.9367 - val_loss: 0.1776 - val_acc: 0.9492
Epoch 15/20
60000/60000 [==============================] - 5s - loss: 0.2107 - acc: 0.9391 - val_loss: 0.1715 - val_acc: 0.9511
Epoch 16/20
60000/60000 [==============================] - 5s - loss: 0.2040 - acc: 0.9405 - val_loss: 0.1658 - val_acc: 0.9514
Epoch 17/20
60000/60000 [==============================] - 5s - loss: 0.1969 - acc: 0.9423 - val_loss: 0.1607 - val_acc: 0.9533
Epoch 18/20
60000/60000 [==============================] - 5s - loss: 0.1922 - acc: 0.9442 - val_loss: 0.1559 - val_acc: 0.9554
Epoch 19/20
60000/60000 [==============================] - 5s - loss: 0.1853 - acc: 0.9454 - val_loss: 0.1518 - val_acc: 0.9558
Epoch 20/20
60000/60000 [==============================] - 6s - loss: 0.1813 - acc: 0.9470 - val_loss: 0.1472 - val_acc: 0.9568
('Test score:', 0.1472099754229188)
('Test accuracy;', 0.95679999999999998)
```

**Tensorboard**で出力されたlogを見ると下記のようになります。

![image](https://qiita-image-store.s3.amazonaws.com/0/100523/3db34bbd-da20-9ad0-4da5-0849993b3185.png)

**Weight**、**Bias**、**Activation**のヒストグラムは下記のように出力されます。
HistogramのTagがちょっとあれです、今のところ名前は使用されないみたいです。

![image](https://qiita-image-store.s3.amazonaws.com/0/100523/3e5337a5-ad05-e597-6adc-5d013dd9abd1.png)

Graphはこんな感じです。

![image](https://qiita-image-store.s3.amazonaws.com/0/100523/10321bae-80fb-457c-85cb-576606a3dc1c.png)

**Keras**上から簡単に**Tensorboard**のログが出力出来ました。
**Keras**は出できたばかりで改善の余地はまだまだありますが日々更新されているようです。
**Tensorflow**をベタで書くよりは記述もスッキリしるので、
ちょっと試したいときにはとても便利だと思います。