TensorFlow mnistエキスパート編の記録(TensorBoardの視覚化)

# はじめに
[Preferred Networks](https://www.preferred-networks.jp/)さんの[分散深層強化学習](https://research.preferred.jp/2015/06/distributed-deep-reinforcement-learning/)をニュースで見て、こんなん作ってみたい！と思いDeepLearningを始めました。ikkiです。
先日も[ロボット展](http://biz.nikkan.co.jp/eve/irex/)に行ってきまして、[Preferred Networks](https://www.preferred-networks.jp/)さんの展示物とワークショップに参加してきましたが、技術の早さといい、考え方といい素晴らしいですね。尊敬です。
じゃあ、Chainer使えよ！という意見もありますが、それはそれで。いずれしっかり勉強しようとは思っています。

今回は先日TensorFlowをやり始めた際の内容を忘れないうちに(忘れかけてますが)記録しておこうと思います。
まずは、インストール方法からとも思いましたが、途中で飽きたのでやめました。他で優秀にまとめられてますしね。
ちなみにこちらを参考にさせていただきました。ありがとうございます。
「[TensorFlow (GPU版) を Ubuntu にインストールしてみた](http://qiita.com/naomi7325/items/789b256a2c61dbe8ed00)」

# MNISTチュートリアル〜エキスパート〜
[Deep MNIST for Experts](https://www.tensorflow.org/versions/master/tutorials/mnist/pros/index.html)をやってみました。と言っても、このチュートリアルも優秀なものが多いため、ほとんどメモ書き程度。自分のプログラムを載せて満足しておきます。ほとんどコピペですがね。
今回はこちらを参考にさせていただきました。ありがとうございます。
「[TensorFlow 畳み込みニューラルネットワークで手書き認識率99.2%の分類器を構築](http://qiita.com/haminiku/items/36982ae65a770565458d)」

```lang:mnist_expert.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

####################################################################
# mnistをtensorflowで実装
# コードの分割化などを行っていないため若干見にくい
####################################################################

from __future__ import absolute_import,unicode_literals
import input_data
import tensorflow as tf

# mnistデータの読み込み
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)

# cross_entropyを実装
sess = tf.InteractiveSession()      # 対話型セッションを始める(長時間プログラムには向かない)
# 式に用いる変数設定
x = tf.placeholder("float", shape=[None,784])       # 入力
y_ = tf.placeholder("float", shape=[None,10])       # 誤差関数用変数 真のclass Distribution
W = tf.Variable(tf.zeros([784,10]))     # 重み
b = tf.Variable(tf.zeros([10]))         # バイアス
sess.run(tf.initialize_all_variables())     # 変数の初期化(変数使用の際必ず必要)
y = tf.nn.softmax(tf.matmul(x,W)+b)     # y=softmax(Wx+b)微分も勝手に行ってくれる
cross_entropy = -tf.reduce_sum(y_*tf.log(y))        # クロスエントロピーを定義

# 学習アルゴリズムと最小化問題
# ココでは最急降下法(勾配降下法)の最小化を解く。学習率0.01
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)

# 1000回学習 バッチサイズ50(この中身の処理は未だ勉強不足)
for i in range(1000):
    batch = mnist.train.next_batch(50)
    train_step.run(feed_dict={x:batch[0],y_:batch[1]})

# 結果の表示
correct_prediction = tf.equal(tf.argmax(y,1),tf.argmax(y_,1))# 尤もらしいClassすなわちargmax(y)が教師ラベルと等しいか
accuracy = tf.reduce_mean(tf.cast(correct_prediction,"float"))# 各サンプル毎に評価して平均を計算
print accuracy.eval(feed_dict={x: mnist.test.images,y_: mnist.test.labels})# xに画像y_にそのラベルを代入
# 出てくる結果はこの時点で精度91%前後

###############################################################
# 以下で深層畳み込みニューラルネットワークを構築する。
# 深層化することで精度99%を目指す。
###############################################################

# 重みとバイアスの初期化
# 勾配消失問題のために、小さいノイズをのせて重みを初期化する関数？どういうこと？
def weight_variable(shape):     # 重みの初期化
    initial = tf.truncated_normal(shape,stddev=0.1)
    return tf.Variable(initial)

def bias_variable(shape):       # バイアスの初期化
    initial = tf.constant(0.1,shape=shape)
    return tf.Variable(initial)


# convolutionとpoolingの定義
def conv2d(x,W):
    return tf.nn.conv2d(x,W,strides=[1,1,1,1],padding='SAME')

def max_pool_2x2(x):
    return tf.nn.max_pool(x,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME')


# 第1レイヤー 5x5パッチで32の特徴を計算
# [5,5,1,32]は、5,5でパッチサイズを、1で入力チャンネル数、32で出力チャンネル
W_conv1 = weight_variable([5,5,1,32])   # 変数定義
b_conv1 = bias_variable([32])           # 変数定義
x_image = tf.reshape(x,[-1,28,28,1])    # 画像を28*28のモノクロ画像に変換

h_conv1 = tf.nn.relu(conv2d(x_image,W_conv1)+b_conv1)   # ReLU処理?

h_pool1 = max_pool_2x2(h_conv1)# プーリング層1の作成


# 第2レイヤー 5x5パッチで64の特徴量を計算
W_conv2 = weight_variable([5,5,32,64])  # 変数定義
b_conv2 = bias_variable([64])           # 変数定義

h_conv2 = tf.nn.relu(conv2d(h_pool1,W_conv2)+b_conv2)

h_pool2 = max_pool_2x2(h_conv2)# プーリング層2の作成


# 全結合層（フルコネクションレイヤー）への変換
W_fc1 = weight_variable([7*7*64,1024])  # どういう変換?
b_fc1 = bias_variable([1024])
h_pool2_flat = tf.reshape(h_pool2,[-1,7*7*64])

h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat,W_fc1)+b_fc1)


# Dropoutを行う
keep_prob = tf.placeholder("float")

h_fc1_drop = tf.nn.dropout(h_fc1,keep_prob)


# 読み出しレイヤー
W_fc2 = weight_variable([1024,10])
b_fc2 = bias_variable([10])

y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop,W_fc2)+b_fc2)


# モデルの学習と評価
cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv))
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)# Adam法を使用。

correct_prediction = tf.equal(tf.argmax(y_conv,1),tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction,"float"))  # cast?
sess.run(tf.initialize_all_variables())
for i in range(20000):
    batch = mnist.train.next_batch(50)
    if i % 100 == 0:
        train_accuracy = accuracy.eval(feed_dict={x: batch[0], y_: batch[1], keep_prob:1.0})
        print "step %d, training accuracy %g" % (i,train_accuracy)
    train_step.run(feed_dict={x:batch[0],y_:batch[1],keep_prob:0.5})# 0.5に抑えている?

# 結果表示
print "test accuracy %g" % accuracy.eval(feed_dict={x:mnist.test.images,y_:mnist.test.labels,keep_prob:1.0})
```

ふむふむ、ということでプログラムは通ったのだが、このままだとTensorFlowのキラーアプリとなるであろうTensorBoardが使いづらい？というか、プログラムとしても見難い。

# MNISTチュートリアル〜改良型〜
ということで、改良を加えてみました。今回はこちらにお世話になりました。この方はお若いのに本当に優秀で毎度おもしろいものを作っているなと思います。ありがとうございます。
「[kivantium活動日記　TensorFlowでアニメゆるゆりの制作会社を識別する](http://kivantium.hateblo.jp/entry/2015/11/18/233834)」

```lang:mnist_expert_kai.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

####################################################################
# mnistをtensorflowで実装
# コードの分割化などを行っていないため若干見にくい(->直した)
####################################################################
import sys
import cv2
import numpy as np
import tensorflow as tf
import tensorflow.python.platform
import input_data

# mnistデータの読み込み
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)

NUM_CLASSES = 10    # モデルのクラス数

def inference(images_placeholder, keep_prob):
    ####################################################################
    #  予測モデルを作成する関数
    #引数: 
    #  images_placeholder: 画像のplaceholder
    #  keep_prob: dropout率のplaceholder
    #返り値:
    #  y_conv: 各クラスの確率(のようなもの)
    ####################################################################

    # 重みを標準偏差0.1の正規分布で初期化
    def weight_variable(shape):
      initial = tf.truncated_normal(shape, stddev=0.1)
      return tf.Variable(initial)

    # バイアスを標準偏差0.1の正規分布で初期化
    def bias_variable(shape):
      initial = tf.constant(0.1, shape=shape)
      return tf.Variable(initial)

    # 畳み込み層の作成
    def conv2d(x, W):
      return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')

    # プーリング層の作成
    def max_pool_2x2(x):
      return tf.nn.max_pool(x, ksize=[1, 2, 2, 1],strides=[1, 2, 2, 1], padding='SAME')
    
    # 入力を28x28x1に変形
    x_image = tf.reshape(images_placeholder, [-1, 28, 28, 1])

    # 畳み込み層1の作成
    with tf.name_scope('conv1') as scope:
        W_conv1 = weight_variable([5, 5, 1, 32])
        b_conv1 = bias_variable([32])
        h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)

    # プーリング層1の作成
    with tf.name_scope('pool1') as scope:
        h_pool1 = max_pool_2x2(h_conv1)
    
    # 畳み込み層2の作成
    with tf.name_scope('conv2') as scope:
        W_conv2 = weight_variable([5, 5, 32, 64])
        b_conv2 = bias_variable([64])
        h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)

    # プーリング層2の作成
    with tf.name_scope('pool2') as scope:
        h_pool2 = max_pool_2x2(h_conv2)

    # 全結合層1の作成
    with tf.name_scope('fc1') as scope:
        W_fc1 = weight_variable([7*7*64, 1024])
        b_fc1 = bias_variable([1024])
        h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])
        h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
        # dropoutの設定
        h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

    # 全結合層2の作成
    with tf.name_scope('fc2') as scope:
        W_fc2 = weight_variable([1024, NUM_CLASSES])
        b_fc2 = bias_variable([NUM_CLASSES])

    # ソフトマックス関数による正規化
    with tf.name_scope('softmax') as scope:
        y_conv=tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)

    # 各ラベルの確率のようなものを返す
    return y_conv


def loss(logits, labels):
    ####################################################################
    #  lossを計算する関数
    #引数:
    #  logits: ロジットのtensor, float - [batch_size, NUM_CLASSES]
    #  labels: ラベルのtensor, int32 - [batch_size, NUM_CLASSES]
    #返り値:
    #  cross_entropy: 交差エントロピーのtensor, float
    ####################################################################

    # 交差エントロピーの計算
    cross_entropy = -tf.reduce_sum(labels*tf.log(logits))
    # TensorBoardで表示するよう指定
    tf.scalar_summary("cross_entropy", cross_entropy)
    return cross_entropy


def training(loss, learning_rate):
    ####################################################################
    #  訓練のopを定義する関数
    #引数:
    #  loss: 損失のtensor, loss()の結果
    #  learning_rate: 学習係数
    #返り値:
    #  train_step: 訓練のop
    ####################################################################

    train_step = tf.train.AdamOptimizer(learning_rate).minimize(loss)
    return train_step


def accuracy(logits, labels):
    ####################################################################
    #  正解率(accuracy)を計算する関数
    #引数: 
    #  logits: inference()の結果
    #  labels: ラベルのtensor, int32 - [batch_size, NUM_CLASSES]
    #返り値:
    #  accuracy: 正解率(float)
    ####################################################################

    correct_prediction = tf.equal(tf.argmax(logits, 1), tf.argmax(labels, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
    tf.scalar_summary("accuracy", accuracy)
    return accuracy


if __name__ == '__main__':
    # 式に用いる変数設定
    x_image = tf.placeholder("float", shape=[None,784])       # 入力
    y_label = tf.placeholder("float", shape=[None,10])       
    W = tf.Variable(tf.zeros([784,10]))     # 重み
    b = tf.Variable(tf.zeros([10]))         # バイアス
    #y_label = tf.nn.softmax(tf.matmul(x_image,W)+b)     # y=softmax(Wx+b)微分も勝手に行ってくれる
    keep_prob = tf.placeholder("float")
    #init_op = tf.initialize_all_variables()    # 変数の初期化(変数使用の際必ず必要)

    with tf.Session() as sess:
        logits = inference(x_image,keep_prob)   # inference()を呼び出してモデルを作成
        loss_value = loss(logits,y_label)       # loss()を呼び出して損失を計算
        train_op = training(loss_value,1e-4)    # training()を呼び出して訓練（1e-4は学習率）
        accur = accuracy(logits,y_label)     # accuracy()を呼び出して精度を計算
        init_op = tf.initialize_all_variables()
        sess.run(init_op)

        for step in range(20001):
            batch = mnist.train.next_batch(50)
            if step % 100 == 0:
                train_accur = accur.eval(feed_dict={x_image: batch[0], y_label: batch[1], keep_prob:1.0})
                print "step %d, training accuracy %g" % (step,train_accur)
            train_op.run(feed_dict={x_image:batch[0],y_label:batch[1],keep_prob:0.5})# 0.5に抑えている?

        # 結果表示
        print "test accuracy %g" % accur.eval(feed_dict={x_image:mnist.test.images,y_label:mnist.test.labels,keep_prob:1.0})
```

inference(),loss(),training(),accuracy()のように分けたほうが良いということで参考にさせていただきました。
ここで問題点が2点発生しました。
1点目がmain内のinit_opです。Session()外でinit_opを定義すると何故かエラー。Session()内だと通るというのをどこかで見たので、Session()内に書きなおしました。
2点目が、一番最後の結果表示部分です。前記事にも書いた**Out of GPU Memory**というエラーが出てしまいました。
この時はこちら（[Github TensorFlow #157](https://github.com/tensorflow/tensorflow/pull/157)）にてconvolutional.pyを書き換えることにより解決しました。
どちらのエラーもどうして直ったのか理解出来ていません。。。英語できｎ・・・

# MNISTチュートリアル〜TensorBoard用〜
それで、上のプログラムをTensorBoardで見られるように改良したものが以下の通り

```lang:mnist_expert_kai2.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

####################################################################
# mnistをtensorflowで実装
# コードの分割化などを行っていないため若干見にくい(->直した)
####################################################################
import sys
import cv2
import numpy as np
import tensorflow as tf
import tensorflow.python.platform
import input_data

# mnistデータの読み込み
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)

NUM_CLASSES = 10    # モデルのクラス数

def inference(images_placeholder, keep_prob):
    ####################################################################
    #  予測モデルを作成する関数
    #引数: 
    #  images_placeholder: 画像のplaceholder
    #  keep_prob: dropout率のplaceholder
    #返り値:
    #  y_conv: 各クラスの確率(のようなもの)
    ####################################################################

    # 重みを標準偏差0.1の正規分布で初期化
    def weight_variable(shape):
      initial = tf.truncated_normal(shape, stddev=0.1)
      return tf.Variable(initial)

    # バイアスを標準偏差0.1の正規分布で初期化
    def bias_variable(shape):
      initial = tf.constant(0.1, shape=shape)
      return tf.Variable(initial)

    # 畳み込み層の作成
    def conv2d(x, W):
      return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')

    # プーリング層の作成
    def max_pool_2x2(x):
      return tf.nn.max_pool(x, ksize=[1, 2, 2, 1],strides=[1, 2, 2, 1], padding='SAME')
    
    # 入力を28x28x1に変形
    x_images = tf.reshape(images_placeholder, [-1, 28, 28, 1])

    # 畳み込み層1の作成
    with tf.name_scope('conv1') as scope:
        W_conv1 = weight_variable([5, 5, 1, 32])
        b_conv1 = bias_variable([32])
        h_conv1 = tf.nn.relu(conv2d(x_images, W_conv1) + b_conv1)

    # プーリング層1の作成
    with tf.name_scope('pool1') as scope:
        h_pool1 = max_pool_2x2(h_conv1)
    
    # 畳み込み層2の作成
    with tf.name_scope('conv2') as scope:
        W_conv2 = weight_variable([5, 5, 32, 64])
        b_conv2 = bias_variable([64])
        h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)

    # プーリング層2の作成
    with tf.name_scope('pool2') as scope:
        h_pool2 = max_pool_2x2(h_conv2)

    # 全結合層1の作成
    with tf.name_scope('fc1') as scope:
        W_fc1 = weight_variable([7*7*64, 1024])
        b_fc1 = bias_variable([1024])
        h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])
        h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
        # dropoutの設定
        h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

    # 全結合層2の作成
    with tf.name_scope('fc2') as scope:
        W_fc2 = weight_variable([1024, NUM_CLASSES])
        b_fc2 = bias_variable([NUM_CLASSES])

    # ソフトマックス関数による正規化
    with tf.name_scope('softmax') as scope:
        y_conv=tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)

    # 各ラベルの確率のようなものを返す
    return y_conv


def loss(logits, labels):
    ####################################################################
    #  lossを計算する関数
    #引数:
    #  logits: ロジットのtensor, float - [batch_size, NUM_CLASSES]
    #  labels: ラベルのtensor, int32 - [batch_size, NUM_CLASSES]
    #返り値:
    #  cross_entropy: 交差エントロピーのtensor, float
    ####################################################################

    # 交差エントロピーの計算
    cross_entropy = -tf.reduce_sum(labels*tf.log(logits))
    # TensorBoardで表示するよう指定
    tf.scalar_summary("cross_entropy", cross_entropy)
    return cross_entropy


def training(loss, learning_rate):
    ####################################################################
    #  訓練のopを定義する関数
    #引数:
    #  loss: 損失のtensor, loss()の結果
    #  learning_rate: 学習係数
    #返り値:
    #  train_step: 訓練のop
    ####################################################################

    train_step = tf.train.AdamOptimizer(learning_rate).minimize(loss)
    return train_step


def accuracy(logits, labels):
    ####################################################################
    #  正解率(accuracy)を計算する関数
    #引数: 
    #  logits: inference()の結果
    #  labels: ラベルのtensor, int32 - [batch_size, NUM_CLASSES]
    #返り値:
    #  accuracy: 正解率(float)
    ####################################################################

    correct_prediction = tf.equal(tf.argmax(logits, 1), tf.argmax(labels, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
    tf.scalar_summary("accuracy", accuracy)
    return accuracyｄ


if __name__ == '__main__':
    with tf.Graph().as_default():
        x_image = tf.placeholder("float", shape=[None,784])       # 入力
        y_label = tf.placeholder("float", shape=[None,10])       # 誤差関数用変数 真のclass Distribution
        W = tf.Variable(tf.zeros([784,10]))     # 重み
        b = tf.Variable(tf.zeros([10]))         # バイアス
        #y_label = tf.nn.softmax(tf.matmul(x_image,W)+b)     # y=softmax(Wx+b)微分も勝手に行ってくれる
        keep_prob = tf.placeholder("float")
        #init_op = tf.initialize_all_variables()    # 変数の初期化(変数使用の際必ず必要)
        logits = inference(x_image,keep_prob)   # inference()を呼び出してモデルを作成
        loss_value = loss(logits,y_label)       # loss()を呼び出して損失を計算
        train_op = training(loss_value,1e-4)    # training()を呼び出して訓練（1e-4は学習率）
        accur = accuracy(logits,y_label)     # accuracy()を呼び出して精度を計算
        init_op = tf.initialize_all_variables()
        sess = tf.Session()
        sess.run(init_op)
        # TensorBoardで表示する値の設定
        summary_op = tf.merge_all_summaries()
        summary_writer = tf.train.SummaryWriter('/tmp/mnist_data', sess.graph_def)
        
        # 訓練の実行
        for step in range(20001):
            batch = mnist.train.next_batch(50)
            if step % 100 == 0:
                train_accur = sess.run(accur,feed_dict={x_image: batch[0], y_label: batch[1], keep_prob:1.0})
                print "step %d, training accuracy %g" % (step,train_accur)
            sess.run(train_op,feed_dict={x_image:batch[0],y_label:batch[1],keep_prob:0.5})# 0.5に抑えている?
            # 1 step終わるたびにTensorBoardに表示する値を追加する
            summary_str = sess.run(summary_op, feed_dict={
                x_image: batch[0],
                y_label: batch[1],
                keep_prob: 1.0})
            summary_writer.add_summary(summary_str, step)

        # 結果表示
        print "test accuracy %g"%sess.run(accur, feed_dict={
                x_image:mnist.test.images,
                y_label:mnist.test.labels,
                keep_prob:1.0})
```
変更点はmainの中身だけ。with tf.Graph().as_default():というもので始まっています。その他、summaryなんちゃらやいろいろ変更点がありますが、説明できるほど理解出来ていないので書けません。。。すみません。とりあえず動きます。

このあと、以下の様にTerminalで実行する。今回はグラフが'/tmp/mnist_data'内にあるので
`tensorboard --logdir /tmp/mnist_data/`
と実行し、Google Chrome上で http://localhost:6006/ に接続すればなんかカッコいいグラフを見ることができます。

まだ、理解が追い付いていない部分も多いですので、もし、何か問題があればコメントください。