TensorFlow Deep MNIST for Experts 翻訳

# はじめに
[前々回](http://qiita.com/uramonk/items/13bb92e9c6a1b548ac4b)と[前回](http://qiita.com/uramonk/items/c207c948ccb6cd0a1346)でBeginner向けチュートリアルの翻訳と実際にTensorFlowを使って機械学習を行いました。
今回はExpert向けのチュートリアルを翻訳してみました。

# Deep MNIST for Experts
TensorFlowは大規模な数値計算を実行するための強力なライブラリです。
優れているタスクの一つは深いニューラルネットワークの訓練と実行です。
このチュートリアルでは我々は深い畳込みMNIST分類器を構築しながらTensorFlowモデルの基本的な構成要素を学びます。

このイントロダクションではニューラルネットワークとMNISTデータセットについて精通していると仮定します。
もしあなたがそれらの背景を持ち得ない場合、[初心者のためのイントロダクション](https://www.tensorflow.org/versions/master/tutorials/mnist/beginners/index.html)を確認してください。
始める前にTensorFlowを必ずインストールしてください。

## Setup
我々のモデルを作成する前に、我々は最初にMNISTデータセットを読み込み、そしてTensorFlowのセッションを開始する。

### Load MNIST Data
あなたの都合の良いように、我々は自動的にMNISTデータセットをダウンロードしインポートする[スクリプト](https://tensorflow.googlesource.com/tensorflow/+/master/tensorflow/examples/tutorials/mnist/input_data.py)を含めた。
これはデータファイルを保存するための'MNIST_data'ディレクトリを作成する。

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

このmnistは訓練や検証、テストのセットをNumPyの配列として保存する軽量のクラスである。
これはまたデータのミニバッチを通して反復する関数を提供し、我々は以下で使用する。

### Start TensorFlow InteractiveSession
TensorFlowは計算をするために高い効果のあるC++バックエンドを頼りにしている。
このバックエンドへの接続はセッションと呼ばれる。
TensorFlowプログラムの通常の使い方は、最初にグラフを作成し、それからセッションの中でそれを開始することである。

ここで我々はその代わりに、どうあなたのコードを構築するかについてTensorFlowをより柔軟にする便利なInteractiveSessionクラスを使う。
これはあなたが、グラフを開始すると共に[計算グラフ](https://www.tensorflow.org/versions/master/get_started/basic_usage.html#the-computation-graph)を構築する処理を差し込むことを可能にする。
これはiPythonのように双方向の前後関係の中で動かすときに特に便利である。
もしあなたがInteractiveSessionを使っていない場合、あなたはセッションを開始し[グラフを開始する](https://www.tensorflow.org/versions/master/get_started/basic_usage.html#launching-the-graph-in-a-session)前に全体の計算グラフを構築するべきである。

```py
import tensorflow as tf
sess = tf.InteractiveSession()
```
#### Computation Graph (グラフの計算)

Pythonで効率の良い数値計算をするために、我々は一般的に、他の言語で実装された大変効果的なコードを使い、Pythonの外側で行列の掛け算のような高い処理をするNumPyのようなライブラリを使う。
不幸なことに、Pythonのすべての処理へ切り替えるのには多くのオーバーヘッドがまだあるだろう。
このオーバーヘッドは、もしあなたがGPU上やデータを転送するのに高いコストがあるだろう分散した方法で計算をしたい場合、特に悪い。

TensorFlowはまたPythonの外側でその困難な仕事をするが、このオーバーヘッドを避けるためにさらにステップを取る。
Pythonから独立している一つの高価な処理を走らせる代わりに、TensorFlowはPythonの外側で完全に走る相互作用する処理のグラフを記述する。
このアプローチはTheanoやTorchを使うのに似ている。

Pythonコードの役割はしたがってこの外部の計算グラフを構築することであり、どの計算グラフの一部を実行すべきか指示することである。
より詳細については[基本的な使い方](https://www.tensorflow.org/versions/master/get_started/basic_usage.html)の[計算グラフ](https://www.tensorflow.org/versions/master/get_started/basic_usage.html#the-computation-graph)セクションを見れば良い。

## Build a Softmax Regression Model (ソフトマックス回帰モデルの構築)
このセクションでは、我々は一つの線形層のソフトマックス回帰モデルを構築する。
次のセクションでは、我々は多層畳み込みネットワークのソフトマックス回帰の場合にこれを拡張する。

### Placeholders
我々は画像の入力や対象となる出力クラスのためにノードを作ることによって計算グラフを構築し始める。

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

この`x`と`y_`は特別な値ではない。
むしろ、それらはそれぞれplaceholder、我々がTensorFlowに計算を走らせるのを求めるときに入力する値、である。

入力画像`x`は浮動小数点数の2Dテンソルから成る。
ここで我々はそれに[None, 784]の形を割り当てる。783は1行に平行にされたMNIST画像の次元数であり、最初の次元のNoneは、バッチサイズに対応する、どんなサイズにもなることを示す。
対象の出力クラス`y_`はまた2Dテンソルから成る。それぞれの列は、MNIST画像になるのに対応する数字を示しているワンホットな10次元ベクトルである。

placeholderのshape引数はオプションであるが、それはTensorFlowに一致しないテンソルの形から由来するバグを自動的にキャッチするのを許可する。

### Variables
我々は今我々のモデルのために重み`W`とバイアス`b`を定義する。
我々は追加の入力のようなこれらを扱うことを想像できるが、TensorFlowはそれらを扱うための更によち方法をもつ。Variableである。
VariableはTensorFlowの計算グラフで存在する値である。
機械学習アプリケーションで、たいていモデルのパラメーターはVariableである。

```py
W = tf.Variable(tf.zeros([784,10]))
b = tf.Variable(tf.zeros([10]))
```

我々は`tf.Variable`を呼ぶことでそれらパラメーターのために初期値を渡す。
この場合、我々は全てゼロのテンソルとして`W`と`b`の両方を初期化する。
`W`は784x10の行列（なぜなら我々は784の入力特徴と10の出力を持つ）であり、`b`は10次元のベクトル（なぜなら我々は10のクラスを持つ）である。

Variableがセッションで使われるだろう前に、それらはセッションを使うことで初期化されなければならない。
このステップでは、すでにはっきりさせた初期値（全てゼロのテンソルの場合）を取り、それらにそれぞれのVariableを割り当てる。
これは一回で全てのVariableのために実施されることができる。

```py
sess.run(tf.initialize_all_variables())
```

### Predicted Class and Cost Function (予測されたクラスとコスト関数)

我々は今我々の回帰モデルを実装できる。
それはたった一行のみで！
我々はベクトル化した入力画像`x`と重み行列`W`を掛け算し、バイアス`b`を加え、そしてそれぞれのクラスへ割り当てられるソフトマックス確率の計算をする。

```py
y = tf.nn.softmax(tf.matmul(x,W) + b)
```

訓練中に最小化されるコスト関数は簡単に記述される。
我々のコスト関数は対象とモデル予測間の交差エントロピーとなる。

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

`tf.reduce_sum`は全てのクラスはもちろん、ミニバッチ中の全ての画像にわたって合計することに注意すること。
我々は全体のミニバッチのために交差エントロピーを計算している。

## Train the Model (モデルの訓練)
我々が我々のモデルと訓練コスト関数を定義した今、TensorFlowを用いた訓練は単純である。
なぜならTensorFlowは全ての計算グラフを知っており、それぞれの変数に関してコストの勾配を見つけるために自動識別を使う。
TensorFlowはさまざまな[備え付けの最適化アルゴリズム](https://www.tensorflow.org/versions/master/api_docs/python/train.html#optimizers)を持つ。
例えば、我々は0.01の長さのステップを踏み、交差エントロピーを下降するための最急勾配降下を使う。

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

この1行で実際にTensorFlowが行ったことは、計算グラフへ新しい処理を追加することだ。
これらの勾配を計算するものを含んだ処理は、パラメーター更新ステップを計算し、パラメーターへ更新されたステップを適用する。

返される処理`train_step`は、それが実行されるとき、パラメータを更新する勾配降下を適用する。
モデルの訓練はしたがって何度も`train_step`を実行することによって熟練されるだろう。

```py
for i in range(1000):
  batch = mnist.train.next_batch(50)
  train_step.run(feed_dict={x: batch[0], y_: batch[1]})
```
それぞれの訓練の反復で、我々は50の訓練の見本を読み込む。
我々はそれから`placeholder`テンソルの`x`と`_y`を訓練の見本と入れ替えるために`feed_dict`を用い、`train_step`の処理を実行する。
注意として、あなたは`feed_dict`を用いることでアタナの計算グラフのなかでどのテンソルも入れ替えることができる。ただ`placeholder`に限られずに。

### Evaluate the Model (モデルの評価)
どのように我々のモデルを良くするのか？

最初に我々はどこで正しいラベルを予測したかを計算する。
`tf.argmax`は、ある軸にそってテンソルの中で最も高い入力のインデックスを与える大変便利な関数である。
例えば、`tf.argmax(_y,1)`が真のラベルなのに対し、`tf.argmax(y,1)`は、我々のモデルが考える各入力で最も似ているラベルである。
我々は我々の予測が真実にマッチしているかどうかを確認するための`tf.equal`を使うことができる。

```py
correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
```

これはbooleanのリストを与える。
部分が正しいかを決定するために、我々は浮動小数点にキャストし、それから平均を取る。
例えば、`[True, False, True, True]`は`[1,0,1,1]`になり、`0.75`となる。

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

最後に、我々はテストデータで我々の精度を評価する。
これは約91%の正しさになるべきである。

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

## Build a Multilayer Convolutional Network (多層畳み込みネットワークの構築)
MNISTで約91%の正確さを得ることはよろしくない。
ほぼ困惑するほどに悪い。
このセクションでは、我々はそれを修正し、大変簡単なモデルからほどよく洗練された何か、小さな畳込みニューラルネットワークへと飛び移る。
これは約99.2%の正確さを与える。最先端ではないが、実直である。

### Weight initialization (重みの初期化)
このモデルを作成するために、我々は多くの重みとバイアスを作成する必要がある。
一つは一般的に対称性の破れのために小さな量のノイズとともに重みを初期化し、勾配が0になるのを防ぐ。
我々は正規化線形関数（ReLU）ニューロンを使って以来、死のニューロンを防ぐためにわずかに陽性である初期バイアスとともにそれらを初期化するのに良い慣習である。
我々がモデルを構築する間にこれを繰り返し行う代わりに、我々のためにそれを行う2つの使いやすい関数を作成しよう。

```py
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 and pooling
TensorFlowはまた我々に畳込みや部分サンプリング処理の中で多くの柔軟性を与えてくれる。
どのように境界をうまく扱えばよいのか？
我々の歩幅のサイズはなにか？
この例の中で、我々はいつも平凡なバージョンを選択する。
我々の畳み込みは一つの歩幅を使い、出力が入力と同じサイズになるように0に詰められる。
我々の部分サンプリングは2x2のブロックを超える古い最大部分サンプリング（層）の計画である。
我々のコードを綺麗に保つために、また抽象的なこれらの処理を関数としよう。

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

### First Convolutional Layer (第一畳み込み層)
我々は今我々の第一層を実装できる。
それは畳み込みから成り、max-poolingの後に起こる。
畳み込みは5x5のパッチのために32の特徴を計算する。
その重みテンソルは`[5, 5, 1, 32]`の形を持つ。
最初の2次元はパッチサイズであり、次は入力チャンネルの数、最後は出力チャンネルである。
我々はまた各出力チャンネルのための要素をもつバイアスベクトルを持つ。

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

層へ適用するために、我々は最初に`x`を、画像の幅と高さに対応する2番目と3番目の次元と、カラーチャンネルの数に対応する最後の次元をもつ4Dテンソルへと作り変える。

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

我々はそれから`x_image`と重みテンソルの畳み込みを作り、バイアスを加え、ReLU関数を適用し、最後にmax poolを計算する。

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

### Second Convolutional Layer (第二畳み込み層)
深いネットワークを構築する代わりに、我々はこのタイプの幾つかの層を積み重ねる。
2番目の層は各5x5のパッチのために64の特徴を持つ。

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

### Densely Connected Layer (密に接続された層)
画像サイズが7x7まで減少させられた今、我々は全画像の処理を許可する1024のニューロンとともに完全に接続された層を加える。
我々はテンソルを部分サンプリング層からベクトルのバッチへと作り変え、重み行列を掛け、バイアスを加え、ReLUを適用する。

```py
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
過剰な適用を減少させるため、我々は読みだし層の前にドロップアウトを適用する。
我々はニューロンの出力をドロップアウトの間保つ確率のための`placeholder`を作る。
これは我々に訓練の間ドロップアウトをすることを許可し、そしてテストの間は実行を辞める。
TensorFlowの`tf.nn.dropout`オペレーションは自動的に、それらへのマスキングに加えてニューロン出力をスケーリングし、そしてドロップアウトは追加のスケーリングなしに実行する。

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

### Readout Layer (読み出し層)
最後に、我々は上記ソフトマックス回帰の一つの層と同様、ソフトマックス層を追加する。

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

### Train and Evaluate the Model (モデルの訓練と評価)
どのくらいこのモデルが良いのか？
訓練と評価をするために、我々は上記シンプルな1つのソフトマックスネットワークの層とほとんど同じコードを使う。
異なる点はこのとおりである。
我々は最急勾配降下最適化プログラムをもっと洗練されたADAM最適化プログラムへと作り変える。
我々はドロップアウト率を制御するために追加のパラメータ`keep_prob`を`deed_dict`へ含める。
そして我々は訓練プロセスの中で全て100回の繰り返しへロギングを加える。

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

このコードの実行後の最後のテストセットの精度は約99.2%になるべきである。

我々はどのように素早く簡単に構築し、訓練し、そしてTensorFlowを用いでかなり洗練された深層学習のモデルを評価するかを学習した。

# おわりに
翻訳は以上です。
ビギナー向けチュートリアルよりは複雑になっていますが、各層で重みを掛けてバイアスを足して関数を適用して、というところは似ていますね。
次はこの内容をもっと理解していきながら実際にコードを実行していきたいと思います。