落ちこぼれないためのTensorFlow Tutorialコード


[前記事](http://qiita.com/TomokIshii/items/f355d8e87d23ee8e0c7a)にて，Deep Learning Framework "TensorFlow" のドキュメントが難しい，という点に触れた．本記事では，TensorFlowの2層ネットワークを使って MNIST（手書き数字の分類問題）を解くことをやってみたい．

### 動機 - for Beginners と for Experts の間を埋めたい

TensorFlowのTutorialに目を通すと，最初に ”MNIST for Beginners” があってその次が "Deep MNIST for Experts" となっており，かなり技術レベルのジャンプがあるように見える．

- For Beginners ... Softmax Regression （ソフトマックス関数による多クラス，ロジスティック回帰）
- （ネットワークモデルの基本，MLP (Multi Layer Perceptron Neural Network)は?  ）
- For Experts ... Convolutional Neural Network （畳込みニューラルネット）

いきなりCNN（畳込みニューラルネット）ではなく，多層ネットワーク(MLP)モデルを入れて段階的に進んだ方がよりよく理解できるのではないだろうか？ 本記事では，そのような間を埋めるTutorialコードを作成してみた．特徴は以下の通り．

- 2層ネットワーク(MLP)のモデル．（（入力＋）隠れ層 ＋ 出力層）
- 技法のレベルとしてはオンライン講座，"Coursera Machine Learning (Stanford)" に出てきた範囲．
- ユニットを選別する計算技法 "Dropout" は用いない．（なお本記事のタイトル「落ちこぼれないための」は，「"Dropout"を使わない」にかかっています．）

（やっていることは，GitHub TensorFlowリポジトリにあった全結合モデルのサンプルコード "mnist.py" に近いかもしれません．但し，自分でみて理解しやすい短いコードを目指しました．）

### コードの説明

以下，部分ごとにコードを見ていきたい．

```py
import tensorflow as tf

# Import data
import input_data
mnist = input_data.read_data_sets("../MNIST_data/", one_hot=True)

```

まず，必要なモジュールのimport. 本当なら"input_data.py"を読み解いた方が良かったのかもしれないが，結構，いろいろな機能が入っているので，今回は中身を解析することをせずにブラックボックスとしてこれを使用することにした．

次に使用する変数の準備である．

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

w_h = tf.Variable(tf.random_normal([784, 625], mean=0.0, stddev=0.05))
w_o = tf.Variable(tf.random_normal([625, 10], mean=0.0, stddev=0.05))
b_h = tf.Variable(tf.zeros([625]))
b_o = tf.Variable(tf.zeros([10]))

```

`x`, `y_` は，訓練データ（テストデータ）を入れるプレースホルダ，`w_h, w_o, b_h, b_o` は学習パラメータ（weightとbias，隠れ層用と出力層用）である．正規分布の乱数を生成する "tf.random_normal()" で Random Initialize している．乱数のパラメータは「小さい値」という大ざっばな基準により，mean=0.0, stddev = 0.05 とした．（biasの方は，ゼロで初期化．）

```py
# Create the model
def model(X, w_h, b_h, w_o, b_o):
    h = tf.sigmoid(tf.matmul(X, w_h) + b_h)
    pyx = tf.nn.softmax(tf.matmul(h, w_o) + b_o)
    
    return pyx

y_hypo = model(x, w_h, b_h, w_o, b_o)

# Cost Function basic term
cross_entropy = -tf.reduce_sum(y_*tf.log(y_hypo))

```

ここが本コードで重要な部分，ニューラルネットワークのモデルを記述している部分である．

隠れ層は，入力層の値から線形予測子を計算し，それをSigmoid関数に入れて算出する．
  
```math
\textbf{u} ^{(h)} = \textbf{w} ^{(h)} \textbf{z} ^{(i)} + \textbf{b}^{(h)}
```

```math
\textbf{z} ^{(h)} = f^{(h)}(\textbf{u}^{(h)})
```

```math
f^{(h)}  \ : \ Sigmoid()\ ...\ \texttt{activation function}
```

出力層は，隠れ層の値から線形予測子を求め，それをSoftmax関数に入れて算出する．
  
```math
\textbf{u} ^{(o)} = \textbf{w} ^{(o)} \textbf{z} ^{(h)} + \textbf{b}^{(o)}
```

```math
\textbf{z} ^{(o)} = f^{(o)} (\textbf{u} ^{(o)})
```

```math
f^{(o)} \ :\ Softmax()\ ...\ \texttt{activation function} 
```

（以上，def model() の中身）  

このモデルにて自分のモデルの値 `y_hypo` を計算，さらに訓練データのラベル `y_` と合わせて cross entropy値を求める．（これがコスト関数の主要部である．）  

次に正則化(Regularization) の項を計算する．

```py
# Regularization terms (weight decay)
L2_sqr = tf.nn.l2_loss(w_h) + tf.nn.l2_loss(w_o)
lambda_2 = 0.01

```

正則化(Regularization)の項は，２乗ノルム(`L2_sqr`)（重み減衰） を用いた．TensorFlowでは，これを算出する "tf.nn.l2_loss()" がサポートされている．

```py
# the loss and accuracy
loss = cross_entropy + lambda_2 * L2_sqr
train_step = tf.train.GradientDescentOptimizer(0.001).minimize(loss)
correct_prediction = tf.equal(tf.argmax(y_hypo,1), tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))

```

ここは，オプティマイザーに関する定義と，関係する数値の算定を行う箇所である．オプティマイザーが評価するのは正則項を加えたコスト関数．勾配降下法(Gradient Descent Optimize)を選び，その学習率(learning rate) を 0.001 とした．また，分類結果の判定とその精度を計算する式を記述する．


```py
# Train
init = tf.initialize_all_variables()

with tf.Session() as sess:
    sess.run(init)
    print('Training...')
    for i in range(20001):
        batch_xs, batch_ys = mnist.train.next_batch(100)
        train_step.run({x: batch_xs, y_: batch_ys})
        if i % 2000 == 0:
            train_accuracy = accuracy.eval({x: batch_xs, y_: batch_ys})
            print('  step, accurary = %6d: %6.3f' % (i, train_accuracy))

```

変数を初期化した後セッションを立ち上げ，訓練データを使ってパラメータの学習を行う．  今回は収束判定や早期打ち切り等は行わず，所定の反復回数（20,000回+）の計算を実施している．

学習が完了したら，テストデータを使って分類器の精度を算出する．

```py
# （with tf.Session() as sess: の内部になります）

    # Test trained model
    print('accuracy = ', accuracy.eval({x: mnist.test.images, y_: mnist.test.labels}))
```


- - - - -

### コードの再掲載と実行状況

以上説明したコードをまとめて，再度，掲載する．（約60行のコードです．）

```py
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import tensorflow as tf

# Import data
import input_data
mnist = input_data.read_data_sets("../MNIST_data/", one_hot=True)

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

w_h = tf.Variable(tf.random_normal([784, 625], mean=0.0, stddev=0.05))
w_o = tf.Variable(tf.random_normal([625, 10], mean=0.0, stddev=0.05))
b_h = tf.Variable(tf.zeros([625]))
b_o = tf.Variable(tf.zeros([10]))

# Create the model
def model(X, w_h, b_h, w_o, b_o):
    h = tf.sigmoid(tf.matmul(X, w_h) + b_h)
    pyx = tf.nn.softmax(tf.matmul(h, w_o) + b_o)
    
    return pyx

y_hypo = model(x, w_h, b_h, w_o, b_o)

# Cost Function basic term
cross_entropy = -tf.reduce_sum(y_*tf.log(y_hypo))

# Regularization terms (weight decay)
L2_sqr = tf.nn.l2_loss(w_h) + tf.nn.l2_loss(w_o)
lambda_2 = 0.01

# the loss and accuracy
loss = cross_entropy + lambda_2 * L2_sqr
train_step = tf.train.GradientDescentOptimizer(0.001).minimize(loss)
correct_prediction = tf.equal(tf.argmax(y_hypo,1), tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))

# Train
init = tf.initialize_all_variables()

with tf.Session() as sess:
    sess.run(init)
    print('Training...')
    for i in range(20001):
        batch_xs, batch_ys = mnist.train.next_batch(100)
        train_step.run({x: batch_xs, y_: batch_ys})
        if i % 2000 == 0:
            train_accuracy = accuracy.eval({x: batch_xs, y_: batch_ys})
            print('  step, accurary = %6d: %6.3f' % (i, train_accuracy))

    # Test trained model
    print('accuracy = ', accuracy.eval({x: mnist.test.images, y_: mnist.test.labels}))

```

（注：最初の３行 `from __future__ ... `は，Python-3との互換性のためのステートメントです．）

このコードを実行した状況が以下である．

```text
Training...
  step, accurary =      0:  0.130
  step, accurary =   2000:  0.900
  step, accurary =   4000:  0.910
  step, accurary =   6000:  0.930
  step, accurary =   8000:  0.920
  step, accurary =  10000:  0.960
  step, accurary =  12000:  0.950
  step, accurary =  14000:  0.950
  step, accurary =  16000:  0.960
  step, accurary =  18000:  0.960
  step, accurary =  20000:  0.960
accuracy =  0.9546

```

テストデータ分類の精度は，95.46 % となった．期待通り，Softmax関数による回帰計算の値（91%）と畳込みニューラルネットによる精度(99.2%) のほぼ中間の値となった．（ややねらった感があります．）


### 次のステップとしてやりたいこと
- 他のデータセットでの分類．（"MNIST" 以外のデータで，ということです．）
- ”TensorBoard" によるGraph Visualization.
- 他のオプティマイザー(AdaGrad, Adam, etc.)の使用，性能確認．
- ネットワーク構成（レイヤー数 x ユニット数）の影響調査．

（まだまだ勉強中ですが，少しずつ試しながらTensoFlowのコードをシェアできたらと思います．）

### 参考文献 (web site)
- TensorFlow ドキュメント http://www.tensorflow.org/
- 深層学習（講談社 機械学習プロフェッショナルシリーズ）
[http://www.kspub.co.jp/book/detail/1529021.html](http://www.kspub.co.jp/book/detail/1529021.html)
- Newmu Theano-Tutorials - GitHub [https://github.com/Newmu/Theano-Tutorials](https://github.com/Newmu/Theano-Tutorials)
- TensorFlowで回帰をやってみる - Qiita  
 [http://qiita.com/syoamakase/items/db883d7ebad7a2220233](http://qiita.com/syoamakase/items/db883d7ebad7a2220233)
- 初めてのTensorFlow - イントロダクションとしての線形回帰 - Qiita
[http://qiita.com/TomokIshii/items/f355d8e87d23ee8e0c7a](http://qiita.com/TomokIshii/items/f355d8e87d23ee8e0c7a)