【TensorFlowのTutorialをざっくり日本語訳していく】2. Deep MNIST For Experts

##はじめに

Googleが提供を開始したディープラーニング用のライブラリTensorFlowについて、忘備録ついでに投稿させてもらいます。TensorFlowはTutorialの解説が詳しいので、それを日本語にしてみました。
・TensorFlowについて-> http://www.tensorflow.org/
・今回の翻訳原本 ->  http://www.tensorflow.org/tutorials/mnist/pros/index.md
生粋の日本人なので違和感のある翻訳や誤訳もあり得ますので自己責任でお願いします。
[参考 【TensorFlowのTutorialをざっくり日本語訳していく】1. MNIST For ML begginers ]

今回のTutorialは岡谷さんの著書「深層学習」の第6章を読みながら見るとわかりやすいでしょう。本記事ではストライドやパディングなど専門用語も出てくるので参考資料は必須です。


それでは、今回作成するモデルは畳み込みニューラルネットワーク(CNN)と言われるモデルです。

##MNISTデータの読み込み
*ここらへんは飛ばし気味でいくので前回の記事を参考にしてください*

MNISTデータセットを取得するスクリプトを実行します。

```
import input_data
mnist = input_data.read_data_sets('MNIST_data', one_hot=True)
```

##TensorFlowのInteractiveSessionを開始
TensorFlowは自身の計算を行うにあたり高度に効率的なC++バックエンドを活用します。このバックエンドへの結合(connection)をsessionと呼びます。TensorFlowの一般的な使い方は、まずグラフを作り、そしてsessionを実行する、というものです。

しかしここでは便利なInteractiveSessionクラスーーこれは、ユーザーがどのようにコードを書くかについて、TensorFlowの柔軟性をより高めるものですーーを用います。これはiPythonのように、インタラクティブに作業をする時に役に立ちます。もしInteractiveSessionを使わなかった場合、ユーザーはsessionを開始する前にcomputation graphの全体を作り上げ、その後にgraphを実行する必要があります。

```
import tensorflow as tf
sess = tf.Interactive Session()
```


###Computation Graph
TensorFlowは、単一の重い命令をpythonの外で独立に実行する代わりに、相互に作用する命令全体をpythonの外で実行するための"グラフ(graph)"を記述することが可能です。これと似た手法はTheanoやTorchでも用いられています。

従ってpythonコードの役割は、このexternal computational graphを構築し、そのgraphのどの部分を実行するか命令を下すことです。

##Softmax Regression Modelの構築
*前回の記事参照*

```
x = tf.placeholder("float", shape=[None, 784])
y_ = tf.placeholder("float", shape=[None, 10])

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)

cross_entropy = -tf.reduce_sum(y_*tf.log(y))

train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)

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))

accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))

print accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels})

```

##多層畳み込みネットワークの構築
MNIST正答率91%ではembarrassingly badです。
小さなConvolutional neural networkを構築し、99.2%の正答率を目指します。

###Weightの初期化
CNNを作るために、たくさんのweightとbiasesを作る必要があります。一般的には、対称性を破りゼロ勾配を防ぐために小さなノイズを添えてweightsを初期化するべきでしょう。今回はReLUニューロンを用いているので、わずかな正の値を持った初期biasで各素子を初期化する("死んだニューロン"を避けるため)こともまた良い手法です。モデルを作るたびにこれを繰り返す代わりに、それを実行してくれる便利な2つの関数を作りましょう。

```
def weight_vairable(shape):
   initial = tf.truncated_normal(shape, stddev=0.1)
   return tf.Variable(initial)


def bias_variable(shape):
   initial = tf.variable(0.1, shape=shape):
   return tf.Variable(initial)
```

##畳み込みとプーリング

TensorFlowは畳み込みとプーリングにおいて多大な柔軟性を与えてくれます。どのように境界を扱おうか？スライドはどれくらいをとろうか？今回の例では最もありきたりなものを使いましょう。このCNNは1のストライド(stride)をとり、ゼロパディングを使うので、出力は入力と同じサイズになります。プーリングは2*2ブロックにまたがるplain old max 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層目の畳み込み層
すでに私たちは第1層目を実行することができます。まず畳み込みがあり、そのあとにくるのはmax poolingです。畳み込みは5x5のパッチそれぞれに対し、32の特徴を計算します(*5x5のパッチに対して32個の数字を返すということかな？)。そのweightテンソルの形は[5,5,1,32]です。最初の2つの次元はパッチサイズで、その次が入力チャネルの数、そして最後はアウトプットのチャネル数です。またbiasベクトルも用意します。そのベクトルの要素はそれぞれ、各出力チャネルに与えられます。

```
w_conv1 = weight_variable([5,5,1,32])
b_conv1 = bias_variable([32])
```

この層に合わせるために、まずはxベクトルを4次元のテンソルに整形します。第二・第三次元は画像の幅と高さに対応し、第四次元は色のチャンネルに対応します。

```
x_image = tf.reshape(x,[-1,28,28,1])
```

それではx_imageをweightテンソルで畳み込み、biasを足し、ReLU関数に代入し、max poolを取りましょう

```
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1)+b_conv1)
h_pool1 = max_pool_2x2(h_conv1)
```


##第2層目の畳み込み層
第2層目では64の特徴量を、5x5のパッチについて取ります。

```
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)
```

##全結合層
今や画像サイズは7x7まで落ちています。これに、1024のニューロン素子を持った全結合層を加え、画像全体を処理させます。プーリング層からのテンソル
量をベクトルのバッチに変換し、weight行列と掛け合わせ、biasを加え、ReLUに代入します。

```
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_fc = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1)+t_fc1)
```

###ドロップアウト
過学習の問題を避ける為、読み出し層の前にドロップアウトを適用します。ドロップアウトの間にニューロンの出力を保存するために確率分布を用い、その確率分布のためのPlaceholderを用意します。(よく意味がわからない)
これによってトレーニングのあいあはドロップアウトをONにし、テストの間はOFFにする、ということができます。TensorFlowのtf.nn.dropout命令は、ニューロン出力をマスキングすることに加え、自動的にスケーリングも行ってくれます(..?)これによってドロップアウトは追加的なスケーリングなしにただ機能するのです。(..??)

```
keep_prob = tf.placeholder("float")
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
```

###読み出し層
最後に、Softmax層を加えます。

```
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)
```


##トレーニングとモデルの評価
上述の単層Softmaxネットワークとの違いは以下のとおりです。
・Steepest Gradient Optimizerをより洗練されたADAM optimizerで置き換えています。
・ドロップアウトのレートを操作するために追加的なパラメーターkeep_probをfeed_dictに加えています。
・トレーニングの100回ごとにログを残すようにしています。

```
cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv))
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
correct_prediction = tf.equal(tf.argmax(y_conv,1), tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
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})

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