TensorFlowでAutoEncoderを可視化してみたよ

# TensorFlow?

Google製の機械学習ライブラリ[TensorFlow](https://www.tensorflow.org/)を触りはじめました。
その名のとおり、テンソル（多次元配列）のデータフローを組み立てると、その計算グラフから自動微分してくれる仕組みを提供してくれるため、とてもディープラーニング（深層学習）と相性がいいです。
さらにTensorFlowにはTensorBoardというログ可視化ツールが付いていて、これが素晴らしいです。ちょっとしたコードで達成感が味わえるので、入門用にもうってつけです。
ということで私も『[ディープラーニング勉強会 AutoEncoder](http://qiita.com/piyo7/items/60576759430910ffe5be)』で勉強したDenoising AutoEncoderを、ザクッと実装して可視化してみました。今回はミニバッチ学習も取り入れてます。

# TensorBoard!

TensorBoardで可視化したデータフローは、こんな感じになります。

![GRAPHS](https://qiita-image-store.s3.amazonaws.com/0/55208/ccee948e-1675-6ae9-a3d4-c4cdcfe33ade.png)

入力層にノイズをかけて、隠れ層でエンコードして、出力層でデコードして、入力と出力を比較して損失を計算する。という一連のフローがそのまま描かれていますね。
上図は分りやすいようにグルーピングしていて、たとえば隠れ層の「hidden」をクリックすると、もっと詳しいフローを見ることができます。

![GRAPHS_hidden](https://qiita-image-store.s3.amazonaws.com/0/55208/ea1d2fa6-77a5-be95-8cf8-1822acde85eb.png)

重みを掛けて、バイアスを足して、活性化関数に食わせる、というニューラルネットワークの基本的な計算手順が、グラフとして描かれているのが面白いところですね。
Variableと表示されているノードは、自動微分による最適化の対象となります。

またTensorBoardは、他にもいろいろと可視化することができます。
今回はMNISTの手書き数字画像をAutoEncoderに学習させてみたので、本当に出力画像がノイズのかかった入力画像を引きもどせているのか確認したり、

![IMAGES](https://qiita-image-store.s3.amazonaws.com/0/55208/eb1c6cfa-c67a-e811-9a2e-6d45caeb612b.png)

学習が進むにつれ損失が減少していることを確認することもできます。

![EVENTS](https://qiita-image-store.s3.amazonaws.com/0/55208/f64e8a65-cb98-c105-4fc5-0a0e4ab3ddd2.png)

こうやって気軽に試行錯誤しながら、良さそうなモデルが出来たら、そのまま[Google Cloud Machine Learning](https://cloud.google.com/ml/)で大規模データをブン回せると、とても楽しそうですね。夢が広がります。

# 実装

```autoencoder.py
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data

flags = tf.app.flags
FLAGS = flags.FLAGS
flags.DEFINE_integer('batch_size', 256, '')
flags.DEFINE_integer('epoch_num', 10, '')
flags.DEFINE_integer('hidden_num', 64, '')
flags.DEFINE_float('learning_rate', 0.01, '')
flags.DEFINE_float('noise_rate', 0.3, '')
flags.DEFINE_float('noise_strength', 0.2, '')
flags.DEFINE_string('summary_dir', 'summary', '')


def main():
    mnist_data = input_data.read_data_sets('MNIST_data', one_hot=True)

    with tf.name_scope('input'):
        input_layer = tf.placeholder(tf.float32, shape=[None, 784])
        input_summary = tf.image_summary('tag', tf.reshape(input_layer, [-1, 28, 28, 1]), 10)

    with tf.name_scope('noisy_input'):
        noise_layer = tf.maximum(tf.random_uniform(shape=tf.shape(input_layer),
                                                   minval=-FLAGS.noise_strength * (2 / FLAGS.noise_rate - 1),
                                                   maxval=FLAGS.noise_strength),
                                 -FLAGS.noise_strength)
        noisy_input_layer = tf.minimum(tf.maximum(input_layer + noise_layer, 0.0), 1.0)
        noisy_input_summary = tf.image_summary('tag', tf.reshape(noisy_input_layer, [-1, 28, 28, 1]), 10)

    with tf.name_scope('hidden'):
        w1 = tf.Variable(tf.random_uniform([784, FLAGS.hidden_num], minval=-1, maxval=1))
        b1 = tf.Variable(tf.zeros([FLAGS.hidden_num]))
        hidden_layer = tf.sigmoid(tf.matmul(noisy_input_layer, w1) + b1)

    with tf.name_scope('output'):
        w2 = tf.transpose(w1)
        b2 = tf.Variable(tf.zeros([784]))
        output_layer = tf.sigmoid(tf.matmul(hidden_layer, w2) + b2)
        output_summary = tf.image_summary('tag', tf.reshape(output_layer, [-1, 28, 28, 1]), 10)

    with tf.name_scope('loss'):
        loss = tf.nn.l2_loss(input_layer - output_layer)
        loss_summary = tf.scalar_summary('loss', loss)

    train_step = tf.train.GradientDescentOptimizer(FLAGS.learning_rate).minimize(loss)

    sess = tf.InteractiveSession()
    sess.run(tf.initialize_all_variables())

    if tf.gfile.Exists(FLAGS.summary_dir):
        tf.gfile.DeleteRecursively(FLAGS.summary_dir)

    input_writer = tf.train.SummaryWriter(FLAGS.summary_dir + '/input', sess.graph)
    noisy_input_writer = tf.train.SummaryWriter(FLAGS.summary_dir + '/noisy_input')
    output_writer = tf.train.SummaryWriter(FLAGS.summary_dir + '/output')

    step = 0
    while mnist_data.train.epochs_completed < FLAGS.epoch_num:
        step += 1
        images, _ = mnist_data.train.next_batch(FLAGS.batch_size)
        loss_result, _ = sess.run([loss_summary, train_step], feed_dict={input_layer: images})
        input_writer.add_summary(loss_result, step)

    input_result, noisy_input_result, output_result =\
        sess.run([input_summary, noisy_input_summary, output_summary], feed_dict={input_layer: images})
    input_writer.add_summary(input_result)
    noisy_input_writer.add_summary(noisy_input_result)
    output_writer.add_summary(output_result)


if __name__ == '__main__':
    main()
```

```
$ python autoencoder.py 
Extracting MNIST_data/train-images-idx3-ubyte.gz
Extracting MNIST_data/train-labels-idx1-ubyte.gz
Extracting MNIST_data/t10k-images-idx3-ubyte.gz
Extracting MNIST_data/t10k-labels-idx1-ubyte.gz

$ tensorboard --logdir=summary/
```

# 実行環境

```
$ python --version
Python 3.5.1
```

```requirements.txt
numpy==1.11.1             # via tensorflow
protobuf==3.0.0b2         # via tensorflow
six==1.10.0               # via protobuf, tensorflow
tensorflow==0.9.0
wheel==0.29.0             # via tensorflow
```