Tensorflowで手書き数字(MNIST)を認識させる

引き続きTensorFlowを触っていきます。

ちなみに、某所で「テンサーフローは使ったことありますか?」と聞かれて、テンサーフロー?なんのこっちゃ?と一瞬思ったのですが、TensorFlowのことですね。私は「テンソルフロー」と呼んでました。だって、TensorFlowのTensorって数学のテンソルでしょ?テンソルをフローで計算するってことでしょ?数学ではテンソルと呼ぶのだからテンソルフローで良いじゃん。と思ったわけ。

でも一方で英語読みだとTensorはテンソーフローとかテンサーフローみたいな感じになるんですよね。多分。どっちが正しいんでしょう。どっちでもいいんですけどね。

この辺は考え出すときりが無く、たとえば私の会社では私がひそかに「ゴンゴン」とあだ名している人がいます。この人は英語の発音にこだわる人です。たとえばdeviceを私はデバイスを言いますが、ゴンゴンはディバイスとか言うんですね。CoffeScriptはコーヒースクリプトと私は読んでますが、ゴンゴンは「カーフィスクリプト」とか言います。おっ、英語勉強中の意識高い系かな、と思いきや「status」が書けなかったり「status」「state」の違いが良くわかってなかったりして、もう指摘するのもめんどくせえ、日本人しかいねーんだから日本語使えや、とか思うのですが。

とりあえず私はこれからもテンソルフローと呼んでいきます。

というわけで、今回は手書き文字の学習をやってみたい。以下、公式チュートリアルをやっていく。

MNIST For ML Beginners

1層のニューラルネット

まずは1層10ユニットだけの普通のニューラルネットをやってみます。

コードは以下の通りです。コメントは私の理解です。「理解だと?理解なんてものは概ね願望に基づくものだ」って公安9課の人が言ってました。

# -*- coding: utf-8 -*-
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data

# mnistのデータを引っ張ってくる
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)

# 入力xの定義。プレースホルダを利用して後から値を供給(Feed)してあげる
x = tf.placeholder(tf.float32, [None, 784])

# 変数。ウェイトw とバイアスb。
W = tf.Variable(tf.zeros([784, 10]))
b = tf.Variable(tf.zeros([10]))

# 出力。活性化関数はsoftmax。 
y = tf.nn.softmax(tf.matmul(x, W) + b)

y_ = tf.placeholder(tf.float32, [None, 10])

cross_entropy = -tf.reduce_sum(y_*tf.log(y))

train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)

init = tf.initialize_all_variables()

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})

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})

ここで、tf.argmaxがちょっと意味不明だったんですが、テンソルの中で最大の要素のインデックスを返してくれるみたいです。第二引数は次元。以下、例。

import numpy as np
import tensorflow as tf

sess = tf.InteractiveSession()
a = np.array([1.1, 1.2, 1.3], np.float32)

tf.argmin(a, 0).eval() # returns 0
tf.argmax(a, 0).eval() # returns 2

b = np.array([[1., 5.], [6., 2.], [4., 8.]])

tf.argmax(b, 0).eval() # [1, 2]
tf.argmax(b, 1).eval() # [1, 0, 1]
tf.argmin(b, 0).eval() # [0, 1]
tf.argmin(b, 1).eval() # [0, 1, 0]

tf.equalは単に一致・不一致を真偽値で返します。

a = np.array([1, 2, 3, 4, 5])
b = np.array([1, 2, 4, 4, 5])
c = tf.equal(a, b)
c.eval() # [ True,  True, False,  True,  True ]

真偽値をfloatにキャストするとこうなります。

d = tf.cast(r, "float")
d.eval() # [ 1.,  1.,  0.,  1.,  1.]

で、最終的に平均を求めるとこうなる。

tf.reduce_mean(d).eval() # 0.8

reduceという名前は単に畳み込み関数であるという意味ですね。他にreduce_sumとかreduce_minとかreduce_maxとかあります。

で、実行すると0.91前後の値が表示されます。正答率91%くらいですね。でも91%という値は

Getting 91% accuracy on MNIST is bad. It's almost embarrassingly bad.

らしいので次はもうちょっと高度なことにチャレンジしてみます。