私的TensorFlow入門


[私的Chainer入門](http://qiita.com/fukuit/items/c745d3f5d2759db4fbac)してみたイキオイで、続きをやってみることにする。
とはいえ、chainerを極める方向に行くよりも、いろいろと食い散らかしておきたい。
まあ、そんなワケで、TensorFlowである。

[MNIST For ML Beginners](https://www.tensorflow.org/tutorials/mnist/beginners/)を参照しつつすすめる。

[私的Chainer入門](http://qiita.com/fukuit/items/c745d3f5d2759db4fbac)や[PythonでKNNを使った手書き文字認識](http://qiita.com/fukuit/items/d69d8ca1ad558c4de014)でもMNISTを扱ったワケだけれど、こいつをつかって「数字判別機を作る」というのは、機械学習のチュートリアルの基礎中の基礎だ。言うなれば、Oracleデータベースを初めて触る人がscott/tigerのempデータベースをselectするSQL文から入るようなもんだ。

# TensorFlowの準備から
PythonからTensorFlowを使う。AnacondaでPython環境を作っているので、[ドキュメント](https://www.tensorflow.org/get_started/os_setup#anaconda_installation)に従い、以下のようにインストールする。

```
% conda install -c conda-forge tensorflow
```

以上でインストール完了である。

## [MNIST For ML Beginners](https://www.tensorflow.org/tutorials/mnist/beginners/)に戻る

ワケの分からないまま、以下のコードを実行する。

```Python
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
```

とりあえず、ipythonで実行したところ、以下のようになった。

```
In [1]: from tensorflow.examples.tutorials.mnist import input_data

In [2]: mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
Successfully downloaded train-images-idx3-ubyte.gz 9912422 bytes.
Extracting MNIST_data/train-images-idx3-ubyte.gz
Successfully downloaded train-labels-idx1-ubyte.gz 28881 bytes.
Extracting MNIST_data/train-labels-idx1-ubyte.gz
Successfully downloaded t10k-images-idx3-ubyte.gz 1648877 bytes.
Extracting MNIST_data/t10k-images-idx3-ubyte.gz
Successfully downloaded t10k-labels-idx1-ubyte.gz 4542 bytes.
Extracting MNIST_data/t10k-labels-idx1-ubyte.gz

```

どうやら、データがダウンロードされたようだ。

```
% % ls -s1 MNIST_data/
total 22672
 3224 t10k-images-idx3-ubyte.gz
   16 t10k-labels-idx1-ubyte.gz
19368 train-images-idx3-ubyte.gz
   64 train-labels-idx1-ubyte.gz
```

# TensorFlowでのMNIST実装
[MNIST For ML Beginners](https://www.tensorflow.org/tutorials/mnist/beginners/)によると、chainerのチュートリアルであったように、28x28ピクセルの手書き数字の画像が0〜9のいずれであるのか？をy = Wx + bの線形の式で評価する。
違いは、chainerのチュートリアルでは、chainer.links.Linear()していたところを、Softmax Regressionを使って評価せよという点か。
このあたりの違いについては、勉強不足過ぎて分からない。

[チュートリアル](https://www.tensorflow.org/tutorials/mnist/beginners/)によると、数字1個あたり28x28ピクセルの画像データになっているが、これを1次元の28x28=784の配列データであるとみなして、それが55000個あるという前提で考えるようだ。すなわち全データが784行×55000列の行列であるとみなして、これをmnist.train.xsとしている。それぞれの列のラベル(すなわち、各列が0〜9のいずれであるか？を示す)を10行×55000列の行列として保持していて、ソレをmnist.train.ysとしている。なんで、10行か？というと、0を[1,0,0,0,0,0,0,0,0,0]、3を[0,0,0,1,0,0,0,0,0,0]のように表現しているから、だそうだ。

## データの読み込みと式の定義

```Python
import tensorflow as tf
x = tf.placeholder(tf.float32, [None, 784])
```

784列のデータをplaceholderとして、まずは定義する。

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

Variableとして学習の対象となるパラメーターを定義する。

そして、モデルを定義する。

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

matmul()というのは、行列の掛け算。ここで、softmax regressionによってyを得ることができる。なおyは以下のように示される10行の行列である。

```
<tf.Tensor 'Softmax_1:0' shape=(?, 10) dtype=float32>
```

## 最適化の準備

```Python
y_ = tf.placeholder(tf.float32, [None, 10])
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), reduction_indices=[1]))
```

さて、最適化というと最小二乗化法かなと思うところなんだけれど、どうやらそうではなく−Σy'log(y)というcross_entropy functionというのを用いるらしい。
ということで、このcross_entropyを最小化させるべく、[gradient descent algorithm](https://ja.wikipedia.org/wiki/%E6%9C%80%E6%80%A5%E9%99%8D%E4%B8%8B%E6%B3%95)を使う、と。これは、chainerのチュートリアルで呼んでいたSGDとはよく似ているけれど、違う手法だ。

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

では、変数を初期化し、実行の準備をする。
変数初期化の定義であるが、[チュートリアル](https://www.tensorflow.org/tutorials/mnist/beginners/)によると、
```init = tf.global_variables_initializer()```
と書くべきらしいのだが、実際にはこれはエラーになる。

```
Traceback (most recent call last):
  File "./mnist_train_tf.py", line 14, in <module>
    init = tf.global_variables_initializer()
AttributeError: module 'tensorflow' has no attribute 'global_variables_initializer'
```

どうやら、https://github.com/tensorflow/tensorflow/issues/5514#issuecomment-260840208 によると、tensorflowのr0.12から```global_variables_initializer()``` が定義されたようで、r0.11以前は```initialize_all_variables()```を使っていたようだ。チュートリアルはr0.12準拠だけれど、anaconda環境でinstallしたtensorflowはr0.11なので、エラーになっていたらしい。

## 最適化のための試行
tensorflowでSessionを定義して、そのsessionの中で、テストデータから100個ずつ選択して学習する。
というのを、1000回繰り返す。

```Python
sess = tf.Session()
sess.run(init)
for i in range(1000):
    batch_xs, batch_ys = mnist.train.next_batch(100)
    sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})

```

先に定義したtrain_stepはcross_entropyを最小化するためのものであり、そこにmnistから取得したデータbatch_xsとラベルbatch_ysをfeed_dictという引数を介して入力する。

## 実行結果

![20170104_004.png](https://qiita-image-store.s3.amazonaws.com/0/15553/82124322-1e4b-f456-fb4e-41c3cfaf1f17.png "20170104_004.png")

0.9184ということで、数値としては予想外に低いかなと思う。

[チュートリアル](https://www.tensorflow.org/tutorials/mnist/beginners/)にも、こんなふうに書いてある。

> This should be about 92%.
> 
> Is that good? Well, not really. In fact, it's pretty bad. This is because we're using a very simple model. With some small changes, we can get to 97%. The best models can get to over 99.7% accuracy! 

まあ、チュートリアル用に簡単なモデルを使ったからだよってことらしい。

# 本日のまとめ

- TensorFlowを使ってみた
	- chainerとはまた違ってた
	- release 0.12と0.11で違う名前になっているmethodがあった

ニューラルネットによるアプローチといっても、ツールごとにまったく実装方法が有るということが分かった。
そして、このコードをきちんと理解できるだけのニューラルネット・人工知能周辺の知識が全く足りてないことも分かった。

# 本日のコード

- https://github.com/fukuit/Python_SelfLearning/blob/master/tensorflow_lesson/mnist_train_tf.py

----

# 2017/02/22 の追記

TensorFlow の1.0が出て、インストール方法が変わった。手元のWindows機で試してみる。

## Windowsへのインストール
condaではなく、pipでインストールしろと、https://www.tensorflow.org/install/install_windows に書いてある。

## あれ？
とりあえず、installできたかどうかの確認のために、サンプルコードを実行してみると、動くんだけど、たくさんのwarningが出る。

```Python
>>> import tensorflow as tf
>>> hello = tf.constant('Hello, Tensorflow!')
>>> sess = tf.Session()
>>> print(sess.run(hello))
E c:\tf_jenkins\home\workspace\release-win\device\cpu\os\windows\tensorflow\core\framework\op_ke
rnel.cc:943] OpKernel ('op: "BestSplits" device_type: "CPU"') for unknown op: BestSplits
E c:\tf_jenkins\home\workspace\release-win\device\cpu\os\windows\tensorflow\core\framework\op_ke
rnel.cc:943] OpKernel ('op: "CountExtremelyRandomStats" device_type: "CPU"') for unknown op: Cou
ntExtremelyRandomStats
E c:\tf_jenkins\home\workspace\release-win\device\cpu\os\windows\tensorflow\core\framework\op_ke
rnel.cc:943] OpKernel ('op: "FinishedNodes" device_type: "CPU"') for unknown op: FinishedNodes
E c:\tf_jenkins\home\workspace\release-win\device\cpu\os\windows\tensorflow\core\framework\op_ke
rnel.cc:943] OpKernel ('op: "GrowTree" device_type: "CPU"') for unknown op: GrowTree
E c:\tf_jenkins\home\workspace\release-win\device\cpu\os\windows\tensorflow\core\framework\op_ke
rnel.cc:943] OpKernel ('op: "ReinterpretStringToFloat" device_type: "CPU"') for unknown op: Rein
terpretStringToFloat
E c:\tf_jenkins\home\workspace\release-win\device\cpu\os\windows\tensorflow\core\framework\op_ke
rnel.cc:943] OpKernel ('op: "SampleInputs" device_type: "CPU"') for unknown op: SampleInputs
E c:\tf_jenkins\home\workspace\release-win\device\cpu\os\windows\tensorflow\core\framework\op_ke
rnel.cc:943] OpKernel ('op: "ScatterAddNdim" device_type: "CPU"') for unknown op: ScatterAddNdim

E c:\tf_jenkins\home\workspace\release-win\device\cpu\os\windows\tensorflow\core\framework\op_ke
rnel.cc:943] OpKernel ('op: "TopNInsert" device_type: "CPU"') for unknown op: TopNInsert
E c:\tf_jenkins\home\workspace\release-win\device\cpu\os\windows\tensorflow\core\framework\op_ke
rnel.cc:943] OpKernel ('op: "TopNRemove" device_type: "CPU"') for unknown op: TopNRemove
E c:\tf_jenkins\home\workspace\release-win\device\cpu\os\windows\tensorflow\core\framework\op_ke
rnel.cc:943] OpKernel ('op: "TreePredictions" device_type: "CPU"') for unknown op: TreePredictio
ns
E c:\tf_jenkins\home\workspace\release-win\device\cpu\os\windows\tensorflow\core\framework\op_ke
rnel.cc:943] OpKernel ('op: "UpdateFertileSlots" device_type: "CPU"') for unknown op: UpdateFert
ileSlots
b'Hello, Tensorflow!'
```

Hello Tensorflow!は最後に表示されてるけれど、途中のこれはなんなんでしょうね。