TensorFlowを遊び倒す! 2-1. MNIST For Experts

本記事は、当社オウンドメディア「Doors」に移転しました。

約5秒後に自動的にリダイレクトします。


※TensorFlowとは2015年11月9日にオープンソース化されたGoogleの機械学習ライブラリです。
 本ブログでは、実際のTensorFlowの使い方を連載方式でご紹介しています。


皆様こんにちは。

テクノロジー&ソフトウェア開発本部の佐藤貴海です。
本日は「Deep MNIST For Experts」の使い方についてご紹介します。

Deep MNIST For Experts

前回の「MNIST For ML Beginners」に続き、今回からいよいよ"Deep"です。前回は単なるロジスティック回帰でしたが、今回からは学習する層を重ねて、MNIST判別99%超えを目指します。

今回の手法

前回は、28×28ピクセルの画像(784次元)を、そのままSoftmax Regressionに与えて学習しました。

今回は、Softmax Regressionを使う点は同じですが、与える入力を多層の(Deepな)ニューラルネットワークで生成します。

いわゆる「ディープラーニング」と呼ばれる手法は、Softmax Regressionといった一般的な教師あり学習の手法に適用させる説明変数を、多層のニューラルネットワークで作ってしまおう、という手法です。

もともとは人手で試行錯誤して変数を作っていた部分を自動化できる、という点が「ディープラーニング」の謳い文句です。「表現学習」と呼ばれることもあります。

学習する方法としては、以下の2つがあります。

  • ニューラルネットワークを教師なし学習で作り、その出力を教師あり学習にする手法
  • ニューラルネットワーク全体を、教師あり学習で学習する方法

今回は後者のひとつである、Softmax Regressionとニューラルネットワークを同時に学習する手法をとります。

実際に作るモデル

今回のモデルは、以下の計6層で構成します。

  1. 畳み込み層
  2. プーリング層
  3. 畳み込み層
  4. プーリング層
  5. 全結合層
  6. Softmax Regression層

いわゆる畳み込みニューラルネットワーク(Convolutional neural network: CNN)です。

さすがに、このブログ上でCNNを解説するのは無理があるので、CNNについて雰囲気を記載するとこんな感じです。

  • 物体画像認識のコンペで圧勝した(ILSVRC 2012)
  • 生物の脳の視覚野に着想を得ており、筋も良さそう
  • 全層全結合層にするとパラメータ数が爆発するので、畳み込みとプーリングでパラメータ数を減らせる

TensorFlowでの実装方法への解説は、コードに直接コメント形式で記載しましたので、以下をご参照ください。

さらに、CNNについての詳細なTensorFlowでの実装は「4-1. Convolutional Neural Networks」で紹介する予定です。お楽しみに。

実際に動かしてみた

チュートリアルに従い、以下のコードを実際に動かしてみます。

# -*- coding: utf-8 -*-
# Author: takami.sato

import tensorflow as tf
import input_data

def weight_variable(shape):
    """適度にノイズを含んだ(対称性の除去と勾配ゼロ防止のため)重み行列作成関数
    """

    initial = tf.truncated_normal(shape, stddev=0.1)
    return tf.Variable(initial)

def bias_variable(shape):
    """バイアス行列作成関数
    """
    initial = tf.constant(0.1, shape=shape)
    return tf.Variable(initial)

def conv2d(x, W):
    """2次元畳み込み関数
    """
    return tf.nn.conv2d(x,
                        W,
                        strides=[1, 1, 1, 1], # 真ん中2つが縦横のストライド
                        padding='SAME')

def max_pool_2x2(x):
    """2x2マックスプーリング関数
    """

    return tf.nn.max_pool(x,
                          ksize=[1, 2, 2, 1],
                          strides=[1, 2, 2, 1], # 真ん中2つが縦横のストライド
                          padding='SAME')

def main():
    # mnistのダウンロード
    mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)

    sess = tf.InteractiveSession()
    with tf.device("/gpu:1"):
        # データ用可変2階テンソルを用意
        x = tf.placeholder("float", shape=[None, 784])
        # 正解用可変2階テンソルを用意
        y_ = tf.placeholder("float", shape=[None, 10])

        # 画像をリシェイプ 第2引数は画像数(-1は元サイズを保存するように自動計算)、縦x横、チャネル
        x_image = tf.reshape(x, [-1, 28, 28, 1])

        ### 1層目 畳み込み層

        # 畳み込み層のフィルタ重み、引数はパッチサイズ縦、パッチサイズ横、入力チャネル数、出力チャネル数
        # 5x5フィルタで32チャネルを出力(入力は白黒画像なので1チャンネル)
        W_conv1 = weight_variable([5, 5, 1, 32])
        # 畳み込み層のバイアス
        b_conv1 = bias_variable([32])
        # 活性化関数ReLUでの畳み込み層を構築
        h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)

        ### 2層目 プーリング層

        # 2x2のマックスプーリング層を構築
        h_pool1 = max_pool_2x2(h_conv1)

        ### 3層目 畳み込み層

        # パッチサイズ縦、パッチサイズ横、入力チャネル、出力チャネル
        # 5x5フィルタで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)

        ### 4層目 プーリング層
        h_pool2 = max_pool_2x2(h_conv2)

        ### 5層目 全結合層

        # オリジナル画像が28x28で、今回畳み込みでpadding='SAME'を指定しているため
        # プーリングでのみ画像サイズが変わる。2x2プーリングで2x2でストライドも2x2なので
        # 縦横ともに各層で半減する。そのため、28 / 2 / 2 = 7が現在の画像サイズ

        # 全結合層にするために、1階テンソルに変形。画像サイズ縦と画像サイズ横とチャネル数の積の次元
        # 出力は1024(この辺は決めです) あとはSoftmax Regressionと同じ
        W_fc1 = weight_variable([7 * 7 * 64, 1024])
        b_fc1 = bias_variable([1024])

        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)

        # ドロップアウトを指定
        keep_prob = tf.placeholder("float")
        h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

        ### 6層目 Softmax Regression層

        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)

    # 評価系の関数を用意
    cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv))
    train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
    correct_prediction = tf.equal(tf.argmax(y_conv,1), tf.argmax(y_,1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
    sess.run(tf.initialize_all_variables())

    for i in range(20000):
        batch = mnist.train.next_batch(50)
        if i%100 == 0:
            train_accuracy = accuracy.eval(feed_dict={x:batch[0],
                                                      y_: batch[1],
                                                      keep_prob: 1.0})
            print("step %d, training accuracy %g"%(i, train_accuracy))
        train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})

    print("test accuracy %g"%accuracy.eval(feed_dict={
        x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0}))

if __name__ == "__main__":
    main()

このような形で、層が複数のニューラルネットワークを構築することができます。

基本的に、積み重ねるだけの素直な実装方法となっています。

これを実行すると、以下の様な出力となります。

Extracting MNIST_data/train-images-idx3-ubyte.gz
Extracting MNIST_data/train-labels-idx1-ubyte.gz
Extracting MNIST_data/t10k-images-idx3-ubyte.gz
Extracting MNIST_data/t10k-labels-idx1-ubyte.gz
I tensorflow/core/common_runtime/local_device.cc:25] Local device intra op parallelism threads: 12
I tensorflow/core/common_runtime/gpu/gpu_init.cc:88] Found device 0 with properties:
name: GeForce GTX 960
major: 5 minor: 2 memoryClockRate (GHz) 1.1775
pciBusID 0000:03:00.0
Total memory: 2.00GiB
Free memory: 1.95GiB
I tensorflow/core/common_runtime/gpu/gpu_init.cc:88] Found device 1 with properties:
name: Tesla K40c
major: 3 minor: 5 memoryClockRate (GHz) 0.745
pciBusID 0000:01:00.0
Total memory: 11.25GiB
Free memory: 10.79GiB
I tensorflow/core/common_runtime/gpu/gpu_init.cc:45] cannot enable peer access from device ordinal 0 to device ordinal 1I tensorflow/core/common_runtime/gpu/gpu_init.cc:45] cannot enable peer access from device ordinal 1 to device ordinal 0
I tensorflow/core/common_runtime/gpu/gpu_init.cc:112] DMA: 0 1
I tensorflow/core/common_runtime/gpu/gpu_init.cc:122] 0:   Y N
I tensorflow/core/common_runtime/gpu/gpu_init.cc:122] 1:   N Y
I tensorflow/core/common_runtime/gpu/gpu_device.cc:643] Creating TensorFlow device (/gpu:0) -> (device: 0, name: GeForce GTX 960, pci bus id: 0000:03:00.0)
I tensorflow/core/common_runtime/gpu/gpu_device.cc:643] Creating TensorFlow device (/gpu:1) -> (device: 1, name: Tesla K40c, pci bus id: 0000:01:00.0)
I tensorflow/core/common_runtime/gpu/gpu_region_allocator.cc:47] Setting region size to 1881214976
I tensorflow/core/common_runtime/gpu/gpu_region_allocator.cc:47] Setting region size to 11002706535
I tensorflow/core/common_runtime/local_session.cc:45] Local session inter op parallelism threads: 12
step 0, training accuracy 0.14
step 100, training accuracy 0.78
step 200, training accuracy 0.94
step 300, training accuracy 0.88

(中略)

step 19800, training accuracy 1
step 19900, training accuracy 1
test accuracy 0.9926

無事、99%を超える精度を得ることができました。

TensorFlowでは、非常に少ないコード量で、畳み込み層やプーリング層といった様々な層を重ねて、"Deep"な学習をすることができます。(データの前処理を既存コードに頼っていますが。。。)


次回は、「2-2. Variables: Creation, Initializing, Saving, and Restoring」を公開予定です。



こういった先進的な取り組みをぜひ自分でもやってみたいという方を、
ブレインパッドでは募集しています。
興味・関心のある方は、ぜひ一度採用サイトを覗いてみてください。
http://www.brainpad.co.jp/recruit/stylea.htmlwww.brainpad.co.jp