Cloud9でTensorFlowのチュートリアル（初心者のためのMNIST）をやってみた～手書き画像の分類～

# はじめに
TensorFlowのチュートリアルで機械学習の初心者のためと書いてある「[MNIST For ML Beginners](https://www.tensorflow.org/get_started/mnist/beginners)」をやってみました。そこで機械学習のHelloWorldと書いてある、MNISTの数字認識を実装してみました。MNISTとは手書き画像のデータセットの事で、ここでは0～9の手書きの数字の画像を読み込んで、機会学習で分類をします。
TensorFlowのGithubに置いてあるサンプルコードは難しいそうに見えるのですが、必要な実装だけしてみると本当にシンプルで20行で実装できました。

# 環境
[Cloud9](https://c9.io)
Python 2.7.6
Sample Codes : [GitHub](https://github.com/takus69/learn_tf)
環境構築は「[クラウド統合開発環境Cloud9でTensorFlowを使う～GetStarted～ ](http://qiita.com/takus69/items/428d500a835ca2f514e4)」
TesorFlowの基本的な使い方は「[クラウド統合開発環境Cloud9でTensorFlowを使う〜使い方の基本〜](http://qiita.com/takus69/items/d7c84f8f21fc938a5604)」
を参照

# 学習処理のコード
Githubに置いてありますが、以下が実装したコードになります。

```py:mnist_softmax_train.py
from tensorflow.examples.tutorials.mnist import input_data
import tensorflow as tf

# Download gz files to MNIST_data directory
mnist = input_data.read_data_sets('MNIST_data', one_hot=True)

# Initializing
sess = tf.InteractiveSession()

x = tf.placeholder(tf.float32, shape=[None, 28*28])
y_ = tf.placeholder(tf.float32, shape=[None, 10])

W = tf.Variable(tf.zeros([28*28, 10]), name="W")
b = tf.Variable(tf.zeros([10]), name="b")

sess.run(tf.initialize_all_variables())

# Making model
y = tf.matmul(x, W) + b
cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(y, y_))

# Training
train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)
for i in range(1000):
  batch = mnist.train.next_batch(100)
  train_step.run(feed_dict={x: batch[0], y_: batch[1]})

# Evaluating
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
print(accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels}))

# Save train data
saver = tf.train.Saver()
saver.save(sess, 'param/softmax.param')
```

実行してみるとチュートリアルにも書いてあるように、92%ほどの精度が出ているようです。

# コードの中身

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

データを取得しているところです。MNIST_dataというディレクトリを作成して、そこに4ファイルダウンロードしています。中身はブラックボックスなので調べてみる必要があります。

```py
sess = tf.InteractiveSession()

x = tf.placeholder(tf.float32, shape=[None, 28*28])
y_ = tf.placeholder(tf.float32, shape=[None, 10])

W = tf.Variable(tf.zeros([28*28, 10]), name="W")
b = tf.Variable(tf.zeros([10]), name="b")

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

初期化処理です。xが画像データ（28ピクセル×28ピクセル）、y_がその画像のラベル（0～9）を保持します。placeholderなので、後で代入しています。
Wとbはパラメータで、Wが画像の各ピクセルの重みづけとbが切片と言った形でしょう。

```py
y = tf.matmul(x, W) + b
cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(y, y_))
```

yはパラメータとxから予測値を算出している式。次の式がソフトマックス回帰。
ロジスティック回帰は0と1を予測するモデルですが、それを拡張して複数のラベル（今回だと0～9）を予測できるようにしたもの。
チュートリアルには詳しい式も記載されていたので、それも勉強する必要があると思います。

```py
train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)
for i in range(1000):
  batch = mnist.train.next_batch(100)
  train_step.run(feed_dict={x: batch[0], y_: batch[1]})
```

GradientDescentOptimizerが確率的勾配降下法。mnist.train.next_batchで最初にダウンロードしたデータを順に取得して、feed_dict={x: batch[0], y_: batch[1]}で、変数に代入しています。繰り返し実行することで最適解を求めているところです。

```py
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
print(accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels}))
```

テストデータを使用して、y（予測値）とy_（実際の値）の精度を確認しているところです。

```py
saver = tf.train.Saver()
saver.save(sess, 'param/softmax.param')
```

学習したパラメータを保存している処理です。これで後からパラメータのみを使用して画像の分類を行えます。

# 自分の手書きデータの予測
上記で手書きの数値データを学習しました。そのパラメータを使って、自分で書いた手書きデータの分類を行ってみました。
自分の手書きデータは、同じようにGithubに置いてありますが、白黒のbmpです。parsebmp.py で読み込んでいます。MNISTのデータは、0～1の数値で学習しているのですが、この処理は簡単に0, 1のデータのみです。
実際に動かしてみると、60%の精度でした。92%には程遠いのですが、ある程度は判定できていると言ってよいでしょう。
⇒　手書きデータの予測には前処理が必要でした。詳細については[自分の手書きデータをTensorFlowで予測する](https://qiita.com/takus69/items/dd904dfc62372310c46f)の記事を参考にして下さい。

```py:mnist_softmax.py
from tensorflow.examples.tutorials.mnist import input_data
import tensorflow as tf
import sys
import numpy as np
import parsebmp as pb

def whatisit(file, sess):
  print("File name is %s" % file)
  data = pb.parse_bmp(file)

  # Show bmp data
  for i in range(len(data)):
    sys.stdout.write(str(int(data[i])))
    if (i+1) % 28 == 0:
      print("")

  # Predicting
  d = np.array([data])
  result = sess.run(y, feed_dict={x: d})

  # Show result
  print(result)
  print(np.argmax(result, 1))


if __name__ == "__main__":
  # Restore parameters
  x = tf.placeholder(tf.float32, shape=[None, 28*28])
  y_ = tf.placeholder(tf.float32, shape=[None, 10])
  W = tf.Variable(tf.zeros([28*28, 10]), name="W")
  b = tf.Variable(tf.zeros([10]), name="b")
  y = tf.matmul(x, W) + b
  
  sess = tf.InteractiveSession()
  saver = tf.train.Saver()
  saver.restore(sess, 'param/softmax.param')

  # My data
  whatisit("My_data/0.bmp", sess)
  whatisit("My_data/1.bmp", sess)
  whatisit("My_data/2.bmp", sess)
  whatisit("My_data/3.bmp", sess)
  whatisit("My_data/4.bmp", sess)
  whatisit("My_data/5.bmp", sess)
  whatisit("My_data/6.bmp", sess)
  whatisit("My_data/7.bmp", sess)
  whatisit("My_data/8.bmp", sess)
  whatisit("My_data/9.bmp", sess)
```

# おわりに
まずはコードをチュートリアルを見ながら実装してみました。本当に簡単に実装できてしまうのですが、以下の点を理解していく必要があると思います。

- 使用データの中身
- ソフトマックス回帰の詳細な計算式

また本当に理解するために、自分の手書きのデータなどをとりこんで予測させることができたらよいと思います。それが実用にも繋がると思います。
⇒　2017/03/27に自分の手書きデータの予測をやってみました。

# 更新履歴
- 2018/06/12: 手書きデータの予測について追記
- 2017/03/27: パラメータの保存方法、自分の手書きデータの予測内容を追記
- 2016/10/30: 新規投稿