#!/usr/bin/env python # coding: utf-8 # # TensorFlow による CNN MNIST 実習
ハンズオン資料 #

2016/05/21 機械学習 名古屋 第4回勉強会

# ## はじめに # + この資料は、TensorFlow を用いて、CNN(畳み込みニューラルネット)により MNIST の学習を実施することを目的とするものです。 # + この資料に掲載のコードは、本日の読書会の資料([第6章](http://antimon2.github.io/MLNGY_201605/slides/Chapter6_ConvolutionalNeuralNetwork.slides.html))、前回の読書会の資料([第2章](http://antimon2.github.io/MLNGY_201604/slides/Chapter2_FeedforwardNeuralNetwork.slides.html)・[第3章](http://antimon2.github.io/MLNGY_201604/slides/Chapter3_StochasticGradientDescent.slides.html))、および TensorFlow 公式のチュートリアル [Deep MNIST for Experts](https://www.tensorflow.org/versions/master/tutorials/mnist/pros/) を元に構築しております。 # ## 目標 # + TensorFlow を用いた基本的な CNN を自分で書ける。 # + 学習の精度を上げる(オプション、もしくは宿題) # ## 環境等 # 以下の環境を前提とします。 # # + Python(必須)(2.7.x / 3.x どちらでもOK) # + TensorFlow(必須)(0.8 を想定、0.6 / 0.7 でもOK) # + matplotlib(任意、結果を確認する際に利用) # + IPython(任意) # ## TensorFlow # + [公式サイト](https://www.tensorflow.org/) # + Google 製の「データフローグラフを用いた数値計算ライブラリ」(公式の説明を私訳) # + DeepLearning 用の機能も豊富。 # + 最新バージョンは v0.8 # インストールの詳細省略。 # インストールが成功していれば、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。 # ## CNN 構築 # ### 訓練データ(及び正解データ)用の変数(プレースホルダ)用意 # In[ ]: # 訓練データ:長さ784のベクトルデータ(=28x28 の画像データ) x = tf.placeholder(tf.float32, [None, 784]) # 正解データ:長さ10のベクトルデータ(0,1,…,9 の対応する要素のみ 1、他は 0) d = tf.placeholder(tf.float32, [None, 10]) # ### 重みの初期化 # shape(行列の行数と列数、もしくはベクトルの要素数)を指定して、重み(およびバイアス)を返すユーティリティ関数を用意しておく。 # In[ ]: # 重み(フィルター)は tf.truncated_normal() で初期化(tf.random_normal() でも大体同じ) def weight_variable(shape): initial = tf.truncated_normal(shape, stddev=0.1) return tf.Variable(initial) # バイアスは固定値 0.1 で初期化 def bias_variable(shape): initial = tf.constant(0.1, shape=shape) return tf.Variable(initial) # ### 畳み込み層とプーリング層 # 同様、畳み込み層とプーリング層も定型のものを返すユーティリティ関数定義。 # In[ ]: # 畳み込み層は、tf.nn.conv2d() を利用。 # ストライドは (1, 1)、パディングあり(=入力と出力の画像サイズが同じ) def conv2d(x, W): return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME') # プーリング層は、tf.nn.max_pool() を利用(最大プーリング)。 # プーリングサイズ・ストライドはいずれも (2, 2)、パディングあり(=出力の画像サイズは入力の1/2) def max_pool_2x2(x): return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME') # ### 第1畳み込み層 # + 入力サイズ:28 x 28(=784:訓練データのサイズ) # + フィルター(重み): # + サイズ:5 x 5(ストライド:1 x 1) # + 入力チャネル数:1 # + 出力チャネル数:32 # + プーリング(最大プーリング): # + サイズ:2 x 2(ストライド:2 x 2) # + 参考出力サイズ:14 x 14 x 32 # + 正規下層:なし # In[ ]: # 1件分のデータ(長さ784)を 28x28x1(W=28, K=1)に変換 x_image = tf.reshape(x, [-1,28,28,1]) # フィルターは 5x5x1x32(H=5, K=1, M=32) W_conv1 = weight_variable([5, 5, 1, 32]) b_conv1 = bias_variable([32]) # 畳み込み層・プーリング層を定義(活性化関数は正規化線形関数) h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1) h_pool1 = max_pool_2x2(h_conv1) # ### 第2畳み込み層 # + 参考入力サイズ:14 x 14 x 32 # + フィルター(重み): # + サイズ:5 x 5(ストライド:1 x 1) # + 入力チャネル数:32 # + 出力チャネル数:64 # + プーリング(最大プーリング): # + サイズ:2 x 2(ストライド:2 x 2) # + 参考出力サイズ:7 x 7 x 64 # + 正規下層:なし # In[ ]: # フィルターは 5x5x32x64(H=5, K=32, M=64) W_conv2 = weight_variable([5, 5, 32, 64]) b_conv2 = bias_variable([64]) # 畳み込み層・プーリング層を定義(活性化関数は正規化線形関数) h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2) h_pool2 = max_pool_2x2(h_conv2) # ### 全結合層 # + 入力サイズ:3136(=7\*7\*64=前の層の出力サイズ) # + 出力サイズ:1024 # + 活性化関数:正規化線形関数(ReLU) # In[ ]: # 入力サイズ 3136(=7x7x64)、出力サイズ 1024 W_fc1 = weight_variable([7 * 7 * 64, 1024]) b_fc1 = bias_variable([1024]) # 前層(プーリング層2)の出力(7x7x64 の 3-D)を 1-D に変換してから適用 h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64]) h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1) # ### 出力層 # + 入力サイズ:1024(前の層の出力サイズ) # + 出力サイズ:10(正解データの要素数) # + ドロップアウト:実施 # + 活性化関数:ソフトマックス関数 # In[ ]: # ドロップアウトの定義 keep_prob = tf.placeholder(tf.float32) h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob) # 入力1024、出力10 W_fc2 = weight_variable([1024, 10]) b_fc2 = bias_variable([10]) # ソフトマックス出力 y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2) # ### 誤差関数(=交差エントロピー) # In[ ]: x_entropy = tf.reduce_mean(-tf.reduce_sum(d * tf.log(y_conv), reduction_indices=[1])) # Tensorflow v0.7 以前の場合は↓ # x_entropy = -tf.reduce_sum(d * tf.log(y_conv)) # ### 最適化 # + 以下では、公式チュートリアルに倣って `tf.train.AdamOptimizer` を利用した例を示す。 # + 引数(=学習係数)は 1e-4(=0.0001) # + 他に、`tf.train.GradientDescentOptimizer`、`tf.train.MomentumOptimizer` 等を利用しても良い。 # In[ ]: optimizer = tf.train.AdamOptimizer(1e-4) train_step = optimizer.minimize(x_entropy) # ## 学習 # ### TensorFlow による学習の流れ # 0. 変数の初期化 # 1. セッションの開始 # 2. ミニバッチを利用した学習ステップのループ # 3. (オプション)精度の確認 # 4. (オプション)推測結果確認 # ### 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_conv,1), tf.argmax(d, 1)) # 平均(=一致している個数÷全データ数)を計算 accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) # ### 3. ミニバッチを利用した学習ステップのループ # In[ ]: # ループ回数は 5000回 # バッチサイズは 50 # 200イテレーションごとに現在の精度を算出し出力 for i in range(1, 5001): batch_xs, batch_ys = mnist.train.next_batch(50) sess.run(train_step, feed_dict={x: batch_xs, d: batch_ys, keep_prob: 0.5}) if i % 200 == 0: train_accuracy = sess.run(accuracy, feed_dict={x: batch_xs, d: batch_ys, keep_prob: 1.0}) 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, keep_prob: 1.0})) # ↑だいたい0.98前後になる(と思われます)。 # ### 5. 推測結果の確認 # matplotlib が入っている場合、以下を実行して実際のデータと推測結果を確認してみる。 # In[ ]: get_ipython().run_line_magic('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_conv, feed_dict={x: [imageData], d: [label], keep_prob: 1.0}) print("Classified as: %d" % np.argmax(result)) image = np.reshape(imageData, [28, 28]) plt.imshow(image, cmap = cm.Greys) # plt.show() # ↑Jupyter 以外では必要 # ## 発展 # ### さらなる精度向上のためにできること # + イテレーション回数を調整する # + TensorBoard を利用するか、自分で matplotlib を用いるなどして学習曲線を引く # + 公式チュートリアルでは、20000回イテレーションで 0.992 まで向上している。 # + 畳み込み層(+プーリング層)を追加してみる # + 正規化層を追加してみる # + ※おそらく今回のMNISTではあまり効果は出ないと思われます…。 # + フィルターのサイズ、ストライドの値、パディングの設定等を変更してみる # + `tf.train.AdagradOptimizer` の代わりに `tf.train.MomentumOptimizer` を試してみる。 # + `optimizer = tf.train.MomentumOptimizer(0.001, 0.9)` 等 # In[ ]: