TensorFlowチュートリアル - 熟練者のためのディープMNIST（翻訳）

TensorFlowのチュートリアル（Deep MNIST for Experts）
http://www.tensorflow.org/tutorials/mnist/pros/index.html#deep-mnist-for-experts
の翻訳です。
翻訳の誤りなどあればご指摘お待ちしております。

---

TensorFlowは大規模な数値計算を行うための強力なライブラリです。TensorFlowが優れているタスクの１つは、ディープ・ニューラルネットワークを実装し、訓練することです。このチュートリアルでは、深い畳み込みMNIST分類器を構築しながら、TensorFlowモデルの基本的なビルディング・ブロックを学びます。

このチュートリアルは、ニューラルネットワークとMNISTデータセットに精通していることを前提とします。それらのバックグラウンドを持っていない場合は、[初心者のためのチュートリアル](http://www.tensorflow.org/tutorials/mnist/beginners/index.md)をチェックしてください。開始する前に[TensorFlowをインストール](https://www.tensorflow.org/versions/r0.9/get_started/os_setup.html)してください。

##セットアップ

モデルを作成する前に、まずMNISTデータセットをロードし、TensorFlowセッションを開始します。

##MNISTデータのロード

便宜のために、MNISTデータセットを自動的にダウンロードし、インポートする[スクリプト](https://www.tensorflow.org/code/tensorflow/examples/tutorials/mnist/input_data.py)を用意しました。このスクリプトは、データファイルを格納するディレクトリ「MNIST_data」を作成します。

```py
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('MNIST_data', one_hot=True)
```

ここでmnistはNumPyの配列のような、訓練、検証、テストのセットを保存する軽量クラスです。mnistはまた、データのミニバッチによって反復するための機能を提供します。この機能は後に使います。

##TensorFlow インタラクティブ・セッションの開始

Tensorflowでの計算は、高効率のC ++バックエンドに依存しています。このバックエンドとの接続をセッションと呼びます。 TensorFlowプログラムの一般的な使用法は、まずグラフを作成し、セッションでそれを起動することです。

ここでは、コードを構造化する方法についてTensorFlowをより柔軟にする便利なInteractiveSessionクラスを使用します。これを使えば、[計算グラフ](http://www.tensorflow.org/get_started/basic_usage.md#the-computation-graph)を実行する間に、グラフを構築する操作をはさむことができます。このことは、iPythonのようなインタラクティブな状況で作業する場合、特に便利です。InteractiveSessionを使用しない場合は、セッションを開始し、[グラフを起動](http://www.tensorflow.org/get_started/basic_usage.md#launching-the-graph-in-a-session)する前に、全体のグラフを構築する必要があります。

```py
import tensorflow as tf
sess = tf.InteractiveSession()
```

####計算グラフ

Pythonで効率的な数値計算を行うためには、通常、行列の乗算などの高コストな操作を、別の言語で実装された非常に効率的なコードを用いてPythonの外で行う、NumPyのようなライブラリを使用します。残念ながら、まだすべての操作をPythonからスイッチ・バックするには多くのオーバーヘッドがある場合があります。GPU、または分散環境で計算を実行したい場合、データ転送が高コストの場合には、このオーバーヘッドは特に悪いです。

TensorFlowもPythonの外に重い処理を持ち出しますが、このオーバーヘッドを少し回避する方法を取ります。単一の高コストな操作をPythonから独立して実行する代わりに、TensorFlowでは、完全にPythonの外で実行する操作を相互作用のグラフとして記述することができます。これは、TheanoやTorchのアプローチと同様です。

Pythonコードの役割は、この外部計算グラフを構築し、計算グラフのどの部分を実行すべきか記述することです。詳細については[基本的な使用方法](http://www.tensorflow.org/get_started/basic_usage.md)の[計算グラフ](http://www.tensorflow.org/get_started/basic_usage.md#the-computation-graph)の節を参照してください。

##ソフトマックス回帰モデルの構築

この節では、単一の線形レイヤーとソフトマックス回帰モデルを構築します。次の節では、多層畳み込みネットワークとソフトマックス回帰のケースでこれを拡張します。

###プレースホルダ―

入力画像と目標出力クラスのノードを作成することにより、計算グラフの構築を始めます。

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

ここでxとy_は特定の値ではありません。これは、プレースホルダー（TensorFlowに計算を走らせるよう依頼するとき、私たちが入力する値）です。

入力画像xは浮動小数点数の２次元テンソルからなります。ここでは、[None, 784]の形状を割り当てます、784は単一のフラット化MNIST画像の次元、最初の次元はバッチサイズに対応し、Noneは任意サイズのものとすることができることを示しています。目標出力クラスy_もまた、２次元テンソルからなり、各行は、対応するMNIST画像がどの数字クラスに属するかを示す、10次元の１-ホットベクトルです。

shape引数はオプションですが、これを指定するとTensorFlowは一貫性のないテンソル形状から生じるバグを自動的にキャッチすることができます。

###変数

モデルの重みWとバイアスbを定義します。これらを追加の入力のように扱うと想像されるかもしれませんが、TensorFlowではそれを処理するためのより良い方法があります：変数です。変数は、TensorFlowの相互作用のグラフ内にある、値です。それは計算により使用され、変更することもできます。一般的に、機械学習アプリケーションではモデル・パラメータは変数として持ちます。

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

tf.Variableの呼び出しで各パラメータの初期値を渡します。このケースでは、Wとbを共に0で満たされたテンソルとして初期化します。（784の入力特徴と10の出力を持っているので）Wは784x10行列であり、（10のクラスを持っているので）bは10次元ベクトルです。

セッション内で変数を使用する前に、そのセッションを使用して変数を初期化する必要があります。このステップでは、すでに指定されている初期値（0で満たされたテンソル）をとり、各変数に割り当てます。これは、一度にすべての変数について行うことができます。

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

###予測クラスとコスト関数

これで、回帰モデルを実装することができます。たった1行で！
ベクトル化入力画像xに重み行列Wを掛け、バイアスbを加え、各クラスに割り当てられているソフトマックス確率を計算します。

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

訓練中に最小化するコスト関数は、同様に簡単に指定することができます。コスト関数は、ターゲットとモデルの予測との間の交差エントロピーとします。

```py
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), reduction_indices=[1]))
```

tf.reduce_sumはすべてのクラスにわたって合計し、tf.reduce_meanはそれらの合計の平均をとることに注意してください。

##モデルの訓練

モデルと、訓練するコスト関数を定義したので、TensorFlowを使用して訓練することは簡単です。 TensorFlowは計算グラフ全体を知っているので、コストの各変数に対する勾配を見つけるための自動微分を使用することができます。 TensorFlowはさまざまな[組み込み最適化アルゴリズム](https://www.tensorflow.org/versions/r0.9/api_docs/python/train.html#optimizers)を持っています。この例では、交差エントロピーを下降するために、0.5のステップ長で、最急勾配降下を使用します。

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

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の訓練例をロードします。それから、feed_dictを使用してプレースホルダ―のテンソルxとy_を訓練例で置き換え、train_step操作を実行します。feed_dictを使用して計算グラフ内の任意のテンソルを置き換えることができることに注意してください（それは、プレースホルダーのみに制限されません。）

###モデルの評価

モデルはどのくらいうまくいっているのでしょうか？

最初に、どこで正しいラベルを予測したかを把握しましょう。 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))
```

結果はブール値のリストになります。正しいもの割合を決定するために、浮動小数点数にキャストして、平均値を取ります。たとえば、[True, False, True, True]は[1,0,1,1]になり、0.75になります。

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

最後に、テストデータでの精度を求めます。これは約91％になるはずです。

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

##多層畳み込みネットワークの構築

MNISTで91％の精度は悪いです。それはほとんどあきれるほど悪いです。この節では、単純なモデルから、適度に洗練されたモデルに修正します：小さな畳み込みニューラル・ネットワークに。これは約99.2％の精度です（ステート・オブ・ジ・アートではないが、立派な精度です。）

###重みの初期化

このモデルを作成するには、多くの重みとバイアスを作成する必要があります。一般的に、対称性を破り、0勾配を防ぐために、少量のノイズで重みを初期化する必要があります。ここでは、ReLUニューロンを使用するため、「死んだニューロン」を避けるために、わずかに正の初期バイアスでそれらを初期化することをお勧めします。モデルの構築中に繰り返しこれを行う代わりに、２つの便利な関数を作成しましょう。

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

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

畳み込みとプーリング操作において、また、TensorFlowは多くの柔軟性を提供します。境界はどのように扱うのでしょうか？ストライド間隔は？この例では、常に普通のバージョンを選択するつもりです。畳み込みでは１のストライドを使用し、出力が入力と同じサイズになるようにゼロでパディングします。プーリングは、2×2ブロック上の簡単な古典的最大プーリングです。コードをクリーンに保つために、それらの操作も関数に抽象化しましょう。

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

###第１畳み込み層

これで第１層を実装することができます。この層は、畳み込みと、それに続く最大プーリングで構成されます。畳み込みは、それぞれ5×5のパッチのための32の特徴を計算します。その重みテンソルは[5, 5, 1, 32]の形状を持つことになります。最初の２つの次元はパッチのサイズであり、次は入力チャネルの数、最後は出力チャネルの数です。また、各出力チャネルの成分を有するバイアス・ベクトルを持つことになります。

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

この層に合わせるために、まずxを４次元テンソルに変形します。第２、第３の次元は画像の幅と高さに対応し、最後の次元はカラー・チャネルの数に対応します。

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

それから、x_imageと重みテンソルとを畳み込みし、バイアスを加え、ReLU関数を適用し、最後に最大プーリングします。

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

###第２畳み込み層

ディープ・ネットワークを構築するために、このタイプの複数の層を積み重ねます。第２層は、それぞれ5×5のパッチのための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)
```

###高密度結合層

ここで画像サイズは7×7に縮小されています、画像全体の処理を可能にするために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)
```

####ドロップアウト

過学習を軽減するために、読み出し層の前に[ドロップアウト](https://www.cs.toronto.edu/%7Ehinton/absps/JMLRdropout.pdf)を適用します。ニューロンの出力がドロップアウト時に保持される確率のプレースホルダーを作成します。これで、トレーニング中にドロップアウトをオンにし、テスト中にはオフにすることができるようになります。TensorFlowのtf.nn.dropoutはニューロン出力をマスクするだけでなく、スケーリングを自動的に処理するので、ドロップアウトは追加のスケーリングせずに動作します。[^1]

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

###読み出し層

最後に、上記の単層ソフトマックス回帰のように、ソフトマックス層を追加します。

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

###モデルの訓練と評価

モデルはどのくらいうまくいっているのでしょうか？モデルを訓練し評価するために、上記の単純な単層ソフトマックス・ネットワークの場合とほぼ同じコードを使用します。最急勾配降下オプティマイザを、より洗練されたADAMオプティマイザで置き換え、ドロップアウト率を制御するために追加のパラメータkeep_probをfeed_dictに含め、そして、訓練プロセスの100反復ごとにログを出力します。

```py
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y_conv), reduction_indices=[1]))
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, tf.float32))
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を使用して、かなり洗練されたディープ・ラーニング・モデルを、迅速かつ容易に、構築・訓練・評価する方法を学びました。

[^1]: この小さな畳み込みネットワークでは、パフォーマンスは実際にはドロップアウトありとなしでほぼ同じです。ドロップアウトは、多くの場合、過学習を低減するのに非常に有効ですが、非常に大規模なニューラルネットワークを訓練する際に、最も有効です。