読者です 読者をやめる 読者になる 読者になる

Platinum Data Blog by BrainPad

株式会社ブレインパッドのデータ活用に関する取り組みや製品・サービス開発の裏側、社員の日常などをご紹介します。

TensorFlowを遊び倒す! 1-1. MNIST For ML Beginners

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



皆様こんにちは。

テクノロジー&ソフトウェア開発本部の佐藤貴海です。
今日から本題のTensorFlow入門編に突入します。今回は、「MNIST For ML Beginners」の使い方をご紹介しようと思います。

http://www.tensorflow.org/tutorials/mnist/beginners/index.md

MNISTとは

MNISTとは、「Mixed National Institute of Standards and Technology database」の略で、手書きの数字「0~9」に正解ラベルが与えられているデータセットです。
http://yann.lecun.com/exdb/mnist/:title

手軽に入手でき、データサイズも大きくないので深層学習周りではよく使用されます。

手書き文字は28ピクセル×28ピクセルの画像で与えられており、この784次元のデータを使って0~9を予測します。

f:id:bp-writer:20151229171905p:plain

Softmax Regression (多クラスロジスティック回帰)

このチュートリアルでは"For ML Beginners"と題しているだけあり、Softmax Regressionという多クラス分類(画像を0~9の10クラスに分類)するのに最もシンプルな手法を採用しています。

Softmax Regressionの日本語訳は、色々あるみたいですが、PRML(PATTERN RECOGNITION AND MACHINE LEARNING)では多クラスロジスティック回帰と呼ばれています。私も日本語訳がパッと出てこなかったのですが、ロジスティック回帰と一緒に習ってしまうからかもしれません。

簡単な解説(線形回帰との比較)

お馴染みの線形回帰の場合は、

説明変数ベクトル {x \in \mathbb{R}^{m}}
回帰係数ベクトル {a \in \mathbb{R}^{m}}の内積に
バイアス {b\in \mathbb{R}}を加えたモノが
目的変数 {y\in \mathbb{R}}となるように学習します。

{ \displaystyle
y = a^{T}x+b
}

これだと、出力(目的変数)が1個しか出てこないので、多クラスの判別には使えません。
これを多クラス(今回は10クラス)にするため、回帰係数を行列 {W \in \mathbb{R}^{10 \times m}}にして出力を無理矢理10個にしてみます。

{ \displaystyle
y = Wx+b
}
(同じ記号使ってすみません。ここでは {y\in \mathbb{R}^{10}, b\in \mathbb{R}^{10}}です。)

ただし、このままだと {y}は訳が分からない数字です。

活性化関数としてのSoftmax関数

いまは10個の数字を当てたいので、 {y}が以下のような性質を持っていると嬉しいわけです。

  •  {y}の10個の要素がそれぞれ0~9のどの数字かの確率になっていると嬉しい
  • 更に数字が"0かつ2"なんてことは無いので {y}の10個の要素の和は1になっていて欲しい

そこで、訳が分からない数字 {y}が上記の性質を持たせるために、Softmax関数という(活性化)関数を噛ませます。

{ \displaystyle
y = softmax(Wx+b)
}

これでそれっぽい {y\in \mathbb{R}^{10}}になったので、あとは、 {y}がしっかり予測になるように、 {W, b}を学習してしまえば良いわけです。

学習と活性化関数と誤差関数

学習とはつまり、 {y}の正解が0の場合[1, 0, 0, 0, 0, 0, 0, 0, 0, 0]に"近く"なる様な、 {W, b}を見つけてしまえば、それがゴールです。

この"近い"というのを定義しているのが、誤差関数という関数で、線形回帰の場合はお馴染みの二乗誤差を使っています。

今回も二乗誤差で学習することも可能ですが、せっかく {y}を確率として解釈できるようにしたので、より適した交差エントロピー誤差関数を使用します。

このように、活性化関数にSoftmax関数を使用し、誤差関数に交差エントロピー誤差関数を使用したものが多クラスロジスティック回帰です。

機械学習に始めて触れる方は、この活性化関数と誤差関数をかなり適当に決めてしまっているのに驚かれるかもしれません。実際、工学的には、上手くいけばどんな組合せであっても良いのです。結果が全てです。

ただし、「Softmax関数と交差エントロピー誤差関数」、「普通の線形回帰の二乗誤差(恒等関数と二乗誤差)」といった組み合わせは、正準連結(canonical link)という性質を持っており、よく使用されます。さらに知りたい方は一般化線形モデル(GLM)で調べて見て下さい。

TensorFlowで動かしてみる

前振りが長くなりましたが、実際に動かして見ます。

チュートリアルに載っている、断片的なコードを集めたものが以下となります。

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

import tensorflow as tf
import input_data


def main():
    # mnistのダウンロード
    mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
    
    # 複数GPUがあるので指定
    with tf.device("/gpu:1"):
        # n * 784 の可変2階テンソル
        x = tf.placeholder("float", [None, 784])
        
        # 重み行列とバイアスの宣言
        W = tf.Variable(tf.zeros([784, 10]))
        b = tf.Variable(tf.zeros([10]))
        
        # ソフトマックス層を定義
        y = tf.nn.softmax(tf.matmul(x, W) + b)

        # 正解用2階テンソルを用意
        y_ = tf.placeholder("float", [None, 10])

        # 誤差関数の交差エントロピー誤差関数を用意
        cross_entropy = -tf.reduce_sum(y_*tf.log(y))

        # 学習方法を定義 0.01は学習率
        train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)

    # 変数の初期化 ここでGPUにメモリ確保か
    init = tf.initialize_all_variables()
    sess = tf.Session()
    sess.run(init)

    # 1に一番近いインデックス(予測)が正解とあっているか検証し、その平均
    correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))

    # 学習開始
    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})
        #print sess.run(accuracy, feed_dict={x: batch_xs, y_: batch_ys})


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

if __name__ == "__main__":
    main()

ほとんど書き写しですが、今回は複数GPUがあるので"with tf.device("/gpu:1"):"陽に指定しています。

input_dataについては、こちらから各自DLをお願いします。
https://tensorflow.googlesource.com/tensorflow/+/master/tensorflow/examples/tutorials/mnist/input_data.py

基本的に、TensorFlowの簡単な使い方は以下の流れだと思われます。

  • 基本的に外から流し込むデータはtf.placeholderで確保し、学習するモデルパラメータはtf.Variableで確保する
  • 学習の各ステップでどうするか(どう最適化するか)を指定する(tf.train.GradientDescentOptimizer系のやつ)
  • 最後にセッションにそれらを読み込んで学習ループをこちらで回す

PFN(株式会社Preferred Networks)社が公開しているChainerも似たようなインターフェースだったので、GPUを使う以上、似てくると思われます。

これを実行すると・・・

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 1
I 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
0.9167

現在使ってるマシンには、GeForce GTX 960とTesla K40cの2枚が刺さっています。Teslaが/gpu:1に指定されてしまったので、先ほど陽に指定しました。

最終行に出てるのが、printされた精度です。大体92%出ており、チュートリアルの結果と一致しています。

簡単なSoftmax Regressionでも9割ぐらいの精度は出ています。

ちなみに以下の公式HPによると、現在は誤差率0.23%つまり99.77%の精度で判別できています。

MNIST handwritten digit database, Yann LeCun, Corinna Cortes and Chris Burges


次回は、「2-1.Deep MNIST for Experts」を公開予定です。



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