TensorFlow Tutorial でニューラルネットワークを使う[Python]

f:id:yoghurt1131:20151112021353j:plain

Googleが発表した人工知能ライブラリTensorFlowを動かしてみました。 チュートリアルを追いながら、ニューラルネットワークの勉強(復習)をしたので理解は深まりましたが、誤りや間違いもあると思います。見つけた場合はご指摘ください。

Download & Install

MacOSでのダウンロードは次のようになります。Ubuntu/LinuxだとGPU使うバージョンもダウンロードできる模様 ダウンロードページ

# Only CPU-version is available at the moment.
$ pip install https://storage.googleapis.com/tensorflow/mac/tensorflow-0.5.0-py2-none-any.whl

動作確認

$ python

>>> import tensorflow as tf
>>> hello = tf.constant('Hello, TensorFlow!')
>>> sess = tf.Session()
"can't determine number of CPU cores: assuming 4
I tensorflow/core/common_runtime/local_device.cc:25] Local device intra op parallelism threads: 4"
>>> print sess.run(hello)
Hello, TensorFlow!
>>> a = tf.constant(10)
>>> b = tf.constant(32)
>>> print sess.run(a+b)
42

動作はしましたが僕のマシンでは警告がでました

>>> sess = tf.Session()
[f:id:yoghurt1131:20151112021353j:plain]can't determine number of CPU cores: assuming 4
I tensorflow/core/common_runtime/local_device.cc:25] Local device intra op parallelism threads: 4

CPUのコア数がわからないとのこと。どこかで設定すればいいのかな?

MNIST For ML Beginners

ページに行くと二つのチュートリアルが用意されている

  • intro_mnist:初心者向け
  • expert_mnist:Machine Learning経験者・上級者向け

初心者向けのチュートリアルでやります。 このチュートリアルではMNISTのデータセットを用いて手書き数字を識別するための識別器を作成します。

MNIST

Download Data Set

公式ページから落としてきてもいいんですが、Googleさんがデータセットを取ってくるPythonプログラムを用意してくれています。

Call Data

これを使ってデータセットを呼び出すことができます

import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
mnist.train # 訓練データ
mnist.test  # テストデータ

画像データやそのラベルは次のコードで呼び出せます

mnist.train.images # 画像データ
mnist.train.labels   # ラベルデータ

画像は28x28ピクセルで表されているため、一つ一つの画像データ・ラベルデータはサイズが28x28=784の配列で表現されています。これは多次元配列ではなくフラットな一次元の長さ784の配列になっています。 mnist.train.imagesを呼び出すと画像60000枚x784ピクセルの2次元配列が呼び出されることになります。 ラベルデータは数字0〜9の10次元のベクトルの中で、正解の数字に1が立つようなベクトルになっています。例えば、0が書かれた画像のラベルデータは[1,0,0,0,0,0,0,0,0,0,0]となります。

Softmax Regressions

最終的には、画像を認識させてそれが0〜9のどの数字に当てはまるかを確率値で出力します。活性化関数にはソフトマックス関数を用いています。ソフトマックス関数は多クラス分類における出力値を正規化するような役割を果たしています。

ソフトマックス関数では、 1. 出力のevidenceを求める 2. それを確率値にする という2つのステップがあります。

まず、ニューラルネットワークの出力evidenceは、次のような形で求められます。Wは重み・bはバイアスになります。

 {evidence_i = \sum_j W_{i,j} x_j + b_j}

これにソフトマックス関数をかけて確率値yを得ます。

 {y = softmax(evidence_i)}

イメージはこんな感じ

f:id:yoghurt1131:20151111142323p:plain

数式で表すならばシンプルに次のように書けます。

 { y = softmax(Wx + b) }

Implementing the Regression

インストール時に書きましたがTensorFlowのライブラリは次のように読み込めます。

import tensorflow as tf`

次にデータの入れ物(placeholder)を用意します

x = tf.placeholder("float", [None, 784])

重みとバイアスも宣言が必要です。これらの値は計算中に頻繁に変わることが予想されるのでTensorFlowのVariableというメソッドで扱います。

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

モデルは次のように呼び出せます。

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

Training

コスト関数としてクロスエントロピーを導入します。コスト関数は学習された重みwとバイアスbがどれだけ目指す値に近づいたかの指標となります。

 {H_{y'}(y) = - \sum_i y_i' \log y_i }

yは予測確率分布、y'は正解のベクトルです

クロスエントロピーを導入するために、正解ラベルを入れるためのプレースホルダーを定義し、それを使ってクロスエントロピーの式を定義します。

y_ = tf.placeholder("float", [None,10])
cross_entropy = -tf.reduce_sum(y_*tf.log(y))

このコスト関数の値を最小化する重みとバイアスを見つけるのが、最終的な目標となります。 コスト関数の最小化は勾配降下法を用いて行い、プログラムでは次のように記述されます。

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

tf.train.GradientDescentOptimizer(0.01)が勾配降下法でありその際の学習率は0.01としています。 後に述べますが実際には計算量の関係から確率的勾配降下法というのを用いて全てのデータを用いてコスト関数の最小化を図るのではなく、無作為に少量のデータを抽出しそれを用いてコスト関数の最小化を目指します。

それでは学習するための準備をしていきます。

init = tf.initialize_all_variables()  # 値の初期化
sess = tf.Session()                     # セッションの初期化
sess.run(init)                              

直前に書いたように確率的勾配降下法を用いて少しずつ学習を行います。訓練データセットから100個ずつ無作為にデータを持ってきてそれ1000回を学習させています。これによってニューラルネットワーク内の重みwとバイアスbが決まっていきます。

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

Evaluating Our Model

最初に、予測ラベルが正解ラベルと一致しているかの判定を次の文で定義します。

correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))

この結果をfloatに変換して平均を取ります。下記のコードは[True, False, True, True]が与えられると[1,0,1,1]に変換して、平均0.75という結果を求めます。

accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))

最後に結果の出力。sessionを通して出力されます。

print sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels})
# > 0.9126

91.26%の精度で認識ができていることが確認できました。チュートリアルでも91%くらいの精度が出ると書いてあるので、機能しているようです。

参考