TensorFlow 入門 〜 MNIST for beginner のプチ応用 〜

# TensorFlow のチュートリアル

[前回は、TensorFlow のGet Started といういことで](http://qiita.com/junichiro/items/8886f3976fc20f73335f)、チュートリアルよりもっと手前のイントロダクションにあった、線形回帰のサンプルコードの解説を行いました。

(参考)
[多分もっともわかりやすいTensorFlow 入門 (Introduction)](http://qiita.com/junichiro/items/8886f3976fc20f73335f)

今日はようやくチュートリアルにある、MNIST for Beginner をやりました。チュートリアルでは、MNIST こそ機械学習のHello World にあたると言っていますが、他のかたも仰っているように僕もあまり納得は行きませんでした。

> ドキュメントの説明「MNIST（手書き数字の分類）は機械学習の"Hello World" である．」という箇所に納得がいかない．CourseraのMachine Learning (Stanford)でもそうだったが，機械学習を初歩から学ぶ場合，やはり最初は Linear Regression（線形回帰）と，個人的に考える．

via [初めてのTensorFlow - イントロダクションとしての線形回帰](http://qiita.com/TomokIshii/items/f355d8e87d23ee8e0c7a)

こちらのチュートリアルはとてもわかりやすい日本語訳もいくつか見られましたし、内容も特に補足することはないので、解説もそれらに譲ることとします。

(参考)
* [TensorFlowチュートリアル - ML初心者のためのMNIST（翻訳）](http://qiita.com/KojiOhki/items/ff6ae04d6cf02f1b6edf)
* [【TensorFlowのTutorialをざっくり日本語訳していく】1. MNIST For ML Beginners](http://qiita.com/qooa/items/3719fec3cfe764674fb9)

**※ どちらも微妙に古いコードが残っていますが、理解を妨げるほどではありませんでした。**

(本家)
[MNIST For ML Beginners | TensorFlow](https://www.tensorflow.org/versions/master/tutorials/mnist/beginners/)


# sigmoid 関数を使ってみる

このチュートリアルをよくご覧頂けるとわかると思いますが、コスト関数としてソフトマックス関数を使っています。しかし、Coursera のMachine Learning の講座では、分類問題のコスト関数としてはずっとsigmoid を使ったものでやってきました。

```math
h_\theta(x) = \frac{ 1 }{ 1 + e^{ -\theta^T x } }
```

```math
J(\theta) = -\frac{ 1 }{ m }\sum_{ i = 1 }^{ m }\bigl( y^{ (i) } log h_\theta(x^{ (i) }) + (1 - y^{ (i) }) log (1 - h_\theta(x^{ (i) })) \bigr)
```

これですね。自分の理解を測るためにも、このチュートリアルをsigmoid をつかって書き換えてみようと思います。

(参考)
Cousera のMachine Learning への取り組みについて
-> [機械学習をゼロから1ヵ月間勉強し続けた結果](http://qiita.com/junichiro/items/3457e33e502086a200f1)
シグモイド関数が出てくるのはこのあたり
-> [機械学習を1ヵ月で実践レベルにする #7 (分類問題: ロジスティック回帰 #1)](http://qiita.com/junichiro/items/8de94c84c35e9851d42a)

いきなり答えを書いてしまいます。TensorFlow ではコスト関数の定義を書き換えるだけですので、そんなに難しくはありません。

```diff
diff -u mnist_softmax.py mnist_sigmoid.py
--- mnist_softmax.py	2017-01-12 10:12:21.000000000 +0900
+++ mnist_sigmoid.py	2017-01-12 10:50:52.000000000 +0900
@@ -56,9 +56,9 @@
   #
   # So here we use tf.nn.softmax_cross_entropy_with_logits on the raw
   # outputs of 'y', and then average across the batch.
-  cross_entropy = tf.reduce_mean(
-      tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y))
-  train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)
+  hx = tf.sigmoid(y)
+  loss = -tf.reduce_mean((y_ * tf.log(hx) + (1 - y_) * tf.log(1 - hx)), reduction_indices=[0])
+  train_step = tf.train.GradientDescentOptimizer(0.5).minimize(loss)
 
   sess = tf.InteractiveSession()
   tf.global_variables_initializer().run()

Diff finished.  Thu Jan 12 10:51:33 2017
```

cross_entropy で定義していたコスト関数を、先程の定義で書き換えるだけです(変数名も loss に変更しています)。sigmoid や log についても、それぞれ `tf.sigmoid` や `tf.log` というメソッドがあるのでそれを使いました。

もとのソフトマックス関数では 91% くらいの精度でしたが、こちらも同様に91% くらいの精度でした。実行速度もほとんど変わりません。(小数点以下1桁目あたりから若干ソフトマックスのほうが精度がよく感じる)

## 最急降下法を使ってみる

このチュートリアルでは学習過程で、確率的勾配法を使っています。

```python
  # Train
  for _ in range(1000):
    batch_xs, batch_ys = mnist.train.next_batch(100)
    sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})
```

このようにミニバッチを取り出して計算しています。せっかく、以前最急降下法を勉強しているので、最急降下法でもやってみましょう。

```diff
diff -u mnist_sigmoid.py mnist_sigmoid_all.py
--- mnist_sigmoid.py	2017-01-12 12:59:36.000000000 +0900
+++ mnist_sigmoid_all.py	2017-01-12 12:59:09.000000000 +0900
@@ -64,8 +64,7 @@
   tf.global_variables_initializer().run()
   # Train
   for _ in range(1000):
-    batch_xs, batch_ys = mnist.train.next_batch(100)
-    sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})
+    sess.run(train_step, feed_dict={x: mnist.train.images, y_: mnist.train.labels})
 
   # Test trained model
   correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))

Diff finished.  Thu Jan 12 13:54:43 2017
```

ミニバッチを取り出すところをやめて、x, y_ それぞれに全トレーニングデータをセットして実行してみます。結果的にはこちらも精度は91% くらいでした。

ソフトマックスもsigmoid も、確率勾配法も最急降下法もどれもほとんど精度には変わりはありませんでしたが、最急降下法だけは、当然ですが、ものすごい時間がかかりました。こうしてみると、確率勾配法がいかにすぐれているかがわかります。

(参考)
[機械学習に必要な最急降下法の実装に必要な知識まとめ](http://qiita.com/junichiro/items/527b1f211ed814528ded)

# さいごに

チュートリアルはただなぞるだけでなく、このようにして、自分がすでに持っている知識と組み合わせて、少し応用に広げると、自分が理解できているかを測ることができますし、またさらに理解を深めることもできますのでオススメです。

# リファレンス

* [落ちこぼれないためのTensorFlow Tutorialコード](http://qiita.com/TomokIshii/items/92a266b805d7eee02b1d)
* [初めてのTensorFlow - イントロダクションとしての線形回帰](http://qiita.com/TomokIshii/items/f355d8e87d23ee8e0c7a)
* (人気！) [機械学習をゼロから1ヵ月間勉強し続けた結果](http://qiita.com/junichiro/items/3457e33e502086a200f1)