TensorFlow Tutorial MNIST For ML Beginners やった

TensorFlow が公開されたので, これを使いながらニューラルネットを勉強することにしました.
気分次第で勉強の記録を残します.

# インストールしてみる
pip があれば, CPU 版は readme の指示通りにコマンドを打つだけです. どうせ我が家に GPU はありません.
https://github.com/tensorflow/tensorflow

Ubuntu と Mac で正常に動くの確認できました.
Windows の人はやり方わからないので頑張ってください.
インストールが済んだら `import tensorflow as tf` と打って怒られないことを確認しましょう.

# 動かしてみる
まずは以下のチュートリアルから始めてみました.
http://tensorflow.org/tutorials/mnist/beginners/index.md

まあなんか出力を 10 次元とした logistic regression っぽいあれで, いつもの MNIST の手書き文字分類です.
図か式を書こうと思ったけれど, チュートリアルの図がとても綺麗だったのでまぁいいやと思いました.

チュートリアルのコードを動かすためには [input_data.py](https://tensorflow.googlesource.com/tensorflow/+/master/tensorflow/g3doc/tutorials/mnist/input_data.py) が必要なので注意されたく.

```python
# -*- coding: utf-8 -*-

import input_data
import tensorflow as tf

# MNIST データセットのダウンロードと読み込み
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)

# 重みと閾値を表す変数を用意する (初期値はゼロとする)
W = tf.Variable(tf.zeros([784, 10]))
b = tf.Variable(tf.zeros([10]))

# 訓練時に特徴ベクトルを入れるための変数
x = tf.placeholder("float", [None, 784])

# Softmax 関数を定義
y = tf.nn.softmax(tf.matmul(x, W) + b)

# 訓練時に真のラベルの値を入れるための変数
y_ = tf.placeholder("float", [None,10])

# 損失関数を cross entropy で定義
cross_entropy = -tf.reduce_sum(y_ * tf.log(y))

# 学習手法を定義 (ステップサイズ 0.01 の勾配法で cross entropy 最小化を目標とする)
train_step = tf.train.GradientDescentOptimizer(0.005).minimize(cross_entropy)

# セッションを準備
sess = tf.Session()

# 変数の初期化処理
init = tf.initialize_all_variables()
sess.run(init)

for i in range(1000):
    # mini batch で使用する分のデータ
    batch_xs, batch_ys = mnist.train.next_batch(100)
    # 勾配を用いた更新を行う
    sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})

# 正答率を返す関数を定義
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))

# 結果を眺める
print sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels})
```

# 読んでみる

## データの準備
```python
# MNIST データセットのダウンロードと読み込み
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
```
Google が用意した MNIST データセットダウンロード用の謎のモジュールを使ってデータを用意します. 別にこんなの要らないのですが, 書き換えるのも面倒なので使っておきましょう.
我が家は ADSL 回線なのでダウンロードに少し時間がかかりますが, 皆さんの家なら大丈夫でしょう.

## 更新対象となる変数の準備
```python
# 重みと閾値を表す変数を用意する (初期値はゼロとする)
W = tf.Variable(tf.zeros([784, 10]))
b = tf.Variable(tf.zeros([10]))
```
重みを表す行列 W と閾値 b を用意します. numpy の array みたいなもんですが, TensorFlow で扱うために Variable という型で用意します. 面倒ですが我慢しましょう. 
以下のように numpy の array を Variable に変換することもできるみたいです.

```python
W = tf.Variable(np.random.uniform(-1, 1, size=[784, 10]))
```

## 変数とか関数っぽいものを準備

```python
# 訓練時に特徴ベクトルを入れるための変数
x = tf.placeholder("float", [None, 784])
# Softmax 関数を定義
y = tf.nn.softmax(tf.matmul(x, W) + b)
# 訓練時に真のラベルの値を入れるための変数
y_ = tf.placeholder("float", [None,10])
# 損失関数を cross entropy で定義
cross_entropy = -tf.reduce_sum(y_ * tf.log(y))
```

placeholder とかいう不可解なものが出てきました. これは値がまだ与えられていない変数で, 後から `x` や `y_` の値を指定したときに, それを使った演算結果である `y` や `cross_entropy` の値がどうなるかを評価してやることが出来ます.
具体的には `x` に特徴行列, `y` に真のラベルを入れたときに損失関数である `cross_entropy` を評価します.

## 最適化手法の選択
```python
# 学習手法を定義 (ステップサイズ 0.01 の勾配法で cross entropy 最小化を目標とする)
train_step = tf.train.GradientDescentOptimizer(0.005).minimize(cross_entropy)
```
最適化手法と最小化したい値を指定します. ここでは最急降下法を指定しています. 0.005 はステップサイズです. どうでも良いですが, ニューラルネットでは learning rate と呼ぶのに対して最適化の人は機械学習以外でも勾配法を使うのでステップサイズと言うことが多いです.

## セッションの作成
```python
# セッションを準備
sess = tf.Session()
# 変数の初期化処理
init = tf.initialize_all_variables()
sess.run(init)
```

ここにきてセッションという謎の概念が登場しました. なんだかよくわからないけれど, TensorFlow ではこのセッション単位で変数などを管理するようです. セッションを作って初期化の処理をしてやらないと, Variable などここまでで作ってきたものの値を連れてくることができません. 初期化処理が終わった後は, 例えば以下のようにセッションを介して `W` の中身を見ます.

```python
>>> sess.run(W)
array([[ 0.6923129 , -0.20792764,  0.03128824, ...,  0.91015261,
         0.84531021, -0.81436723],
       [-0.6045441 ,  0.18968499, -0.48082295, ..., -0.65939605,
         0.61858588, -0.2352511 ],
       [-0.56046396, -0.35212722, -0.44472805, ...,  0.82507199,
         0.77793002, -0.87778318],
       ..., 
       [ 0.73705292,  0.13759996, -0.33590671, ...,  0.15150025,
        -0.2162281 , -0.36046752],
       [-0.90121216, -0.09728234, -0.40505442, ...,  0.02105984,
        -0.46720058, -0.49198067],
       [ 0.29820383,  0.80599529,  0.97673845, ..., -0.43288365,
        -0.73505884, -0.8707968 ]], dtype=float32)
```

## 学習する
```python
for i in range(1000):
    # mini batch で使用する分のデータ
    batch_xs, batch_ys = mnist.train.next_batch(100)
    # 勾配を用いた更新を行う
    sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})
```

具体的な値を定めていなかった `x`, `y_` にデータを入れて, 指定した学習手法を使って `W`, `b` を更新しています. この手続きももちろんセッションを介して行います.

## 残り
```python
# 正答率を返す関数を定義
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
# 結果を眺める
print sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels})
```

なんか正答率を眺めています.

# 感想
Theano とかなり似ているけれど, Theano よりも若干取っつき易い気がした. (Theano * 2 + Chainer) / 3 みたいな印象.
[TensorBoard](http://tensorflow.org/how_tos/summaries_and_tensorboard/index.md) が凄そうなので触ってみたい.