note.nkmk.me

TensorFlowでMNISTを分類(ソフトマックス編)

Date: 2017-08-19 / tags: Python, TensorFlow, 機械学習
スポンサーリンク

TensorFlowでMNISTを分類

Googleの機械学習ライブラリ、TensorFlow。

公式のチュートリアルにもある、ソフトマックス回帰を用いたMNISTの分類をやってみる。

MNISTは手書き数字のデータセット。scikit-learnを使って分類してみた例は以下の記事を参照。

訓練データとテストデータを準備

公式のチュートリアルではTensorFlowの関数を使っているが、ここでは、scikit-learnの関数を使う。

import tensorflow as tf
import numpy as np
from sklearn import datasets, model_selection, utils

mnist = datasets.fetch_mldata('MNIST original', data_home='data/src/download')

X = mnist.data / 255
y = mnist.target
Y = np.identity(10)[y.astype(int)]

train_size = 60000
test_size = 10000

X_train, X_test, Y_train, Y_test = model_selection.train_test_split(
    X, Y, test_size=test_size, train_size=train_size)

画像データは255で割って0〜1に規格化し、正解ラベルはnumpy.identity()でone-hot表現に変換している。

モデルの構築

入出力データ

まずは、画像データと正解ラベルのためのプレースホルダーを定義する。

n_in = 784
n_out = 10

x = tf.placeholder(tf.float32, [None, n_in])
y_ = tf.placeholder(tf.float32, [None, n_out])

画像データは28×28ピクセルをフラット化して784次元で、正解ラベルは0〜9の数字なので10次元。サンプル数は実行する時に決定できるようにNoneを用いる。TensorFlowのプレースホルダーについては、以下の記事を参照。

ソフトマックス回帰

重みをW、バイアスをbとすると、ここで構築したいモデルは、

$$ y = softmax(Wx + b) $$

となる。Wx + bの部分のモデルを構築する。

W = tf.Variable(tf.zeros([n_in, n_out]))
b = tf.Variable(tf.zeros([n_out]))
y = tf.matmul(x, W) + b

訓練

TensorFlowにはソフトマックス関数tf.nn.softmax()も用意されているが、ここでは、ソフトマックスを適用した後に交差エントロピー誤差を算出するtf.nn.softmax_cross_entropy_with_logits()を使用し、さらにtf.reduce_mean()で交差エントロピー誤差の平均を求める。

cross_entropy = tf.reduce_mean(
    tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y))

その値を最小にするように最適化を行う。学習率0.5でtf.train.GradientDescentOptimizerクラスのインスタンスを生成し、minimizeメソッドに最小化させたいcross_entropyを渡す。

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

評価

分類の結果がどれくらい正しいかを確認するオペレーションを定義する。

correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

tf.equal()で分類結果と正解ラベルを比較し、その正解率を算出している。

処理の実行

バッチサイズ、エポック数

まず、バッチサイズとエポック数を決める。

batch_size = 100
batch_num = (int)(train_size // batch_size)

epochs = 20

ここではとりあえず適当に決めている。バッチサイズ(バッチあたりのサンプル数)を100とすると、訓練データのサンプル数が60000なので、600回バッチ処理を行うようにする。

エポック数を20としたので、それを20回繰り返す。

セッション

セッションを開始し、実際に処理を行う。

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())

    for epoch in range(epochs):
        X_, Y_ = utils.shuffle(X_train, Y_train)

        for i in range(batch_num):
            batch_X = X_train[i * batch_size: (i+1) * batch_size]
            batch_Y = Y_train[i * batch_size: (i+1) * batch_size]
            sess.run(train_step, feed_dict={x: batch_X, y_: batch_Y})

        loss, acc = sess.run([cross_entropy, accuracy], feed_dict={x: X_test, y_: Y_test})
        print('epoch: {:2}, loss: {:.5f}, accuracy: {:.5f}'.format(epoch, loss, acc))

    acc = sess.run(accuracy, feed_dict={x: X_test, y_: Y_test})
    print('Final accuracy: {:.5f}'.format(acc))

エポック毎に、utils.shuffle()でデータをシャッフルしている。

結果

エポック毎のロスと正解率の結果は以下の通り。

# epoch:  0, loss: 0.31313, accuracy: 0.91310
# epoch:  1, loss: 0.29504, accuracy: 0.91840
# epoch:  2, loss: 0.28769, accuracy: 0.92080
# epoch:  3, loss: 0.28376, accuracy: 0.92210
# epoch:  4, loss: 0.28138, accuracy: 0.92240
# epoch:  5, loss: 0.27983, accuracy: 0.92290
# epoch:  6, loss: 0.27879, accuracy: 0.92340
# epoch:  7, loss: 0.27808, accuracy: 0.92510
# epoch:  8, loss: 0.27758, accuracy: 0.92560
# epoch:  9, loss: 0.27724, accuracy: 0.92610
# epoch: 10, loss: 0.27701, accuracy: 0.92650
# epoch: 11, loss: 0.27687, accuracy: 0.92710
# epoch: 12, loss: 0.27679, accuracy: 0.92700
# epoch: 13, loss: 0.27677, accuracy: 0.92730
# epoch: 14, loss: 0.27678, accuracy: 0.92710
# epoch: 15, loss: 0.27683, accuracy: 0.92690
# epoch: 16, loss: 0.27690, accuracy: 0.92720
# epoch: 17, loss: 0.27699, accuracy: 0.92710
# epoch: 18, loss: 0.27710, accuracy: 0.92720
# epoch: 19, loss: 0.27723, accuracy: 0.92710
# Final accuracy: 0.92710

エポックを重ねても正解率はあまり向上していない。

最終的な正解率は92%。単純なソフトマックス回帰だとこれくらい。

スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事