TensorFlow による 3LP MNIST 実習
ハンズオン資料

2016/04/16 機械学習 名古屋 第3回勉強会

はじめに

  • この資料は、TensorFlow を用いて、3LP(3層パーセプトロン)により MNIST の学習を実施することを目的とするものです。
  • この資料に掲載のコードは、本日の読書会の資料(第2章第3章)、および TensorFlow 公式のチュートリアル MNIST For ML Beginners を元に構築しております。

目標

  • TensorFlow を用いた基本的な DeepLearning を自分で書ける。
  • 正則化や、学習係数・モメンタム等の工夫を加えて、学習の精度を上げる(オプション、もしくは宿題)

環境等

以下の環境を前提とします。

  • Python(必須)(2.7.x / 3.x どちらでもOK)
  • TensorFlow(必須)(0.6 / 0.7 / 0.8[New!] どれでもOK)
  • matplotlib(任意、結果を確認する際に利用)
  • IPython(任意)

TensorFlow

インストールの詳細省略。
インストールが成功していれば、Python のインタラクティブシェル(もしくは ipython, Jupyter 等)で↓以下のようにすれば利用開始。

In [ ]:
import tensorflow as tf

※ 今回は TensorBoard は不使用。

MNIST

  • 機械学習定番の、多クラス分類(教師あり学習)問題「手書き数字認識」のサンプルデータ。
  • 多くの機械学習ライブラリで、手軽に体験できるよう「MNIST をロードする関数(またはサンプル)」が用意されている。

TensorFlow では、以下のようにすると MNIST データセット(訓練データおよびテストデータ)を扱えるようになる:

In [ ]:
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)

※↑データが存在しなければネットワーク経由でダウンロードから開始。
 ダウンロード後(or ダウンロード済のデータが存在する場合)そのデータを読み込んでくれる。
※⇒既にダウンロード済のデータ(4つの.gzファイル)を持っていたらそれを所定の場所に置いておくだけでもOK。

3LP 構築

訓練データ(及び正解データ)用の変数(プレースホルダ)用意

In [ ]:
# 訓練データ:長さ784のベクトルデータ(=28x28 の画像データ)
x = tf.placeholder(tf.float32, [None, 784])
# 正解データ:長さ10のベクトルデータ(0,1,…,9 の対応する要素のみ 1、他は 0)
d = tf.placeholder(tf.float32, [None, 10])

第1層

  • 入力サイズ:784(訓練データのサイズ)
  • 出力サイズ:128
  • 重み初期化:ガウス分布による乱数で初期化(平均 0.0, 分散 0.05)
  • バイアス初期化:ゼロ値
  • 活性化関数:正規化線形関数(ReLU)
In [ ]:
W1 = tf.Variable(tf.random_normal([784, 128], mean=0.0, stddev=0.05))
b1 = tf.Variable(tf.zeros([128]))
z1 = tf.nn.relu(tf.matmul(x,  W1) + b1)

第2層

  • 入力サイズ:128(前の層の出力サイズ)
  • 出力サイズ:64
  • 重み初期化:ガウス分布による乱数で初期化(平均 0.0, 分散 0.05)
  • バイアス初期化:ゼロ値
  • 活性化関数:正規化線形関数(ReLU)
In [ ]:
W2 = tf.Variable(tf.random_normal([128, 64], mean=0.0, stddev=0.05))
b2 = tf.Variable(tf.zeros([64]))
z2 = tf.nn.relu(tf.matmul(z1, W2) + b2)

第3層(出力層)

  • 入力サイズ:64(前の層の出力サイズ)
  • 出力サイズ:10(正解データの要素数)
  • 重み初期化:ガウス分布による乱数で初期化(平均 0.0, 分散 0.05)
  • バイアス初期化:ゼロ値
  • 活性化関数:ソフトマックス関数
In [ ]:
W3 = tf.Variable(tf.random_normal([64, 10], mean=0.0, stddev=0.05))
b3 = tf.Variable(tf.zeros([10]))
y  = tf.nn.softmax(tf.matmul(z2, W3) + b3)

誤差関数(=交差エントロピー)

In [ ]:
x_entropy = -tf.reduce_sum(d * tf.log(y))

最適化(=確率的勾配降下法)

  • 以下では tf.train.GradientDescentOptimizer を利用した例を示す。
    • 引数(=学習係数)は 0.01
  • 他に、tf.train.AdagradOptimizertf.train.MomentumOptimizer 等を利用しても良い。
In [ ]:
optimizer = tf.train.GradientDescentOptimizer(0.01)
train_step = optimizer.minimize(x_entropy)

学習

TensorFlow による学習の流れ

  1. 変数の初期化
  2. セッションの開始
  3. ミニバッチを利用した学習ステップのループ
  4. (オプション)精度の確認
  5. (オプション)推測結果確認

1. 変数の初期化

In [ ]:
init = tf.initialize_all_variables()

※↑これも「変数を初期化する」という命令を宣言しているだけ。
 この後 sess.run(init) することで(そのセッション内で実際に)変数の初期化が実行される。

2. セッションの開始

In [ ]:
sess = tf.Session()
sess.run(init)
# ↑実際にはここで始めて各変数が初期化される

2.5. (先に)精度の確認(の準備)

今回は分かりやすくするため、ループ中にも随時精度を確認してみる。
そのため先に精度を確認する各種準備。

In [ ]:
# y(=学習結果の出力)と d(正解データ)で一致しているかどうかを確認
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(d, 1))
# 平均(=一致している個数÷全データ数)を計算
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

3. ミニバッチを利用した学習ステップのループ

In [ ]:
# ループ回数は 2000回
# バッチサイズは 100
# 200イテレーションごとに現在の精度を算出し出力
for i in range(1, 2001):
    batch_xs, batch_ys = mnist.train.next_batch(100)
    sess.run(train_step, feed_dict={x: batch_xs, d: batch_ys})
    if i % 200 == 0:
        train_accuracy = sess.run(accuracy, feed_dict={x: batch_xs, d: batch_ys})
        print('  step, accurary = %6d: %6.3f' % (i, train_accuracy))

4. 精度の確認

テストデータを利用して精度の確認。

In [ ]:
print(sess.run(accuracy, feed_dict={x: mnist.test.images, d: mnist.test.labels}))

↑だいたい0.96前後になる(と思われます)。

5. 推測結果の確認

matplotlib が入っている場合、以下を実行して実際のデータと推測結果を確認してみる。

In [ ]:
%matplotlib inline
# ↑Jupyter 上で実行するときに必要

import matplotlib
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import random
In [ ]:
n = len(mnist.test.images)
i = random.randrange(n)

imageData = mnist.test.images[i]
label = mnist.test.labels[i]

result = sess.run(y, feed_dict={x: [imageData], d: [label]})
print("Classified as: %d" % np.argmax(result))

image = np.reshape(imageData, [28, 28])

plt.imshow(image, cmap = cm.Greys)
# plt.show()
# ↑Jupyter 以外では必要

発展

さらなる精度向上のためにできること

  • イテレーション回数を調整する
    • TensorBoard を利用するか、自分で matplotlib を用いるなどして学習曲線を引く
  • 層を増やしてみる(4層、5層、…)
  • 各層のユニット数(サイズ)を変えてみる
  • 学習係数を変更してみる
  • 正則化項を追加してみる
  • tf.train.GradientDescentOptimizer の代わりに、tf.train.AdagradOptimizertf.train.MomentumOptimizer を試してみる。
    • optimizer = tf.train.MomentumOptimizer(0.1, 0.9) とするだけでもかなり変わる(はず)
  • CNN に手を出してみる
    • 次回は↑をやる予定。