SEワンタンの独学備忘録

IT関連の独学した内容や資格試験に対する取り組みの備忘録

【機械学習】入門⑱ KerasによるMNIST(手書き数字)の分析の基本


今回は前記事のKerasライブラリを活用して手書き数字の認識に挑戦していきます。
実装部は全てjupyter notebookで実施しています。

MNISTデータベース

MNIST

MNISTは、手書き数字画像60,000枚と、テスト画像10,000枚を集めた、画像データセットです。
今回のようにディープラーニングの入門学習などにもよく用いられるようです。

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

データの取得

Kerasでは以下のようにデータセットの取得を行うことができます。
学習に使用する訓練データを「x_train」として、学習結果の検証に使うテストデータを「x_test」としています。

# MNISTデータダウンロード
from keras.datasets import mnist

(x_train, y_train),(x_test, y_test)=mnist.load_data()

・ファイルへのデータの保存
※使用には必須ではない

# データの保存
npy.savez('mnist_raw.npz',x_train=x_train,y_train=y_train,x_test=x_test,y_test=y_test)

・ファイルからデータの取り出し
※使用には必須ではない

# データの読み出し
mnist_raw_data = npy.load('mnist_raw.npz')

x_train=mnist_raw_data['x_train']
y_train=mnist_raw_data['y_train']
x_test=mnist_raw_data['x_test']
y_test=mnist_raw_data['y_test']
データ構造の確認

学習に活用するライブラリの詳細な中身はまだしも、活用するデータについては確実に抑えておいた方がいいでしょう。

■x_train
学習を行う画像データが以下のような形式で格納されています。

・実装例

print(x_train[0])

・出力結果

[[  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   3  18  18  18 126 136 175  26 166 255 247 127   0   0   0   0]
 [  0   0   0   0   0   0   0   0  30  36  94 154 170 253 253 253 253 253 225 172 253 242 195  64   0   0   0   0]
 [  0   0   0   0   0   0   0  49 238 253 253 253 253 253 253 253 253 251  93  82  82  56  39   0   0   0   0   0]
 [  0   0   0   0   0   0   0  18 219 253 253 253 253 253 198 182 247 241  0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0  80 156 107 253 253 205  11   0  43 154  0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0  14   1 154 253  90   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0 139 253 190   2   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0  11 190 253  70   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0  35 241 225 160 108   1   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0  81 240 253 253 119  25   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0  45 186 253 253 150  27   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0  16  93 252 253 187   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0 249 253 249  64   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0  46 130 183 253 253 207   2   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0  39 148 229 253 253 253 250 182   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0  24 114 221 253 253 253 253 201  78   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0  23  66 213 253 253 253 253 198  81   2   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0  18 171 219 253 253 253 253 195  80   9   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0  55 172 226 253 253 253 253 244 133  11   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0 136 253 253 253 212 135 132  16   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]]

x_trainは配列で一つ一つは画像データとして28×28の要素から成っています。
その中の個別要素は0-255の範囲でモノクロカラーの色の濃さを表しているため出力すると上記のようになります。
画像データでも確かにこのように数値化すればプログラムにより認識できそうという感じはしてきますね。

■y_train

・実装例

print(y_train[0])

・出力結果

5

「y」には画像データの正解を表す数値が格納されています。

■イメージとして出力する

先ほどの配列をイメージとして出力してみます。

・実装例

# データのイメージ出力
import numpy as npy
import matplotlib.pyplot as plt
%matplotlib inline

plt.figure(1,figsize=(4,4))
plt.subplots_adjust(wspace=0.5)
plt.gray()

plt.subplot(1,1,1)
img=x_train[0,:,:]
plt.pcolor(255-img)
plt.text(24.5,26,"%d"%y_train[0],color='cornflowerblue',fontsize=18)
plt.xlim(0,27)
plt.ylim(27,0)
plt.show()

・出力結果

f:id:wantanBlog:20200723233715p:plain

グラフのプロットの色を要素の値から設定することで手書き文字としてのイメージを出力することができました。

2層フィードフォワードネットワークモデルの実装

まずは2層フィードフォワードネットワークモデルにより、手書き数字の認識(分類)問題に挑みます。

データの整形

MNISTから取得したデータをディープラーニングにかけやすくするためにデータ整形を行います。

・実装例

#データ整形
from keras.utils import np_utils

# 28×28⇒784配列変換
x_train=x_train.reshape(60000,784)
# 0-255⇒0-1に変換
x_train=x_train.astype('float32')
x_train=x_train/255
# 1-of-K符号化法で表現
num_classes=10
y_train=np_utils.to_categorical(y_train,num_classes)

x_test=x_test.reshape(10000,784)
x_test=x_test.astype('float32')
x_test=x_test/255
y_test=np_utils.to_categorical(y_test,num_classes)

print(x_train[0,150:200])
print(y_train[0])

・出力結果

[0.         0.         0.01176471 0.07058824 0.07058824 0.07058824
 0.49411765 0.53333336 0.6862745  0.10196079 0.6509804  1.
 0.96862745 0.49803922 0.         0.         0.         0.
 0.         0.         0.         0.         0.         0.
 0.         0.         0.11764706 0.14117648 0.36862746 0.6039216
 0.6666667  0.99215686 0.99215686 0.99215686 0.99215686 0.99215686
 0.88235295 0.6745098  0.99215686 0.9490196  0.7647059  0.2509804
 0.         0.         0.         0.         0.         0.
 0.         0.        ]
[0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]

Kerasライブラリを使用できるように、コメントに記載した内容の通りにデータ整形を行っています。

ニューラルネットワークモデルの定義

簡単な使用方法は前回の記事にも記載しているので、同様に使っていきます。

ネットワークモデルのタイプ
Sequential

中間層
ノードタイプ:Dense
ノード数:16ノード
入力次元:784
活性化関数:シグモイド関数

出力層
ノードタイプ:Dense
ノード数:10ノード
活性化関数:ソフトマックス関数

目的関数
クロスエントロピー誤差

オプティマイザ
Adam

# ニューラルネットワークモデルの定義
from keras.models import Sequential
from keras.layers import Dense, Activation
from keras.optimizers import Adam
npy.random.seed

model=Sequential()
model.add(Dense(16,input_dim=784,activation='sigmoid'))
model.add(Dense(10,activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer=Adam(),metrics=['accuracy'])
学習の実行

上記のネットワークモデルを使用して、データを元に学習を行います。
batch_size=1000、epochs=10としているので、全データを1000個ごとに分割してパラメータ更新を行う。これを10回繰り返す。

・実装例

# 学習の実行
import time

start_time=time.time()
history=model.fit(x_train,y_train,epochs=10,batch_size=1000,verbose=1,validation_data=(x_test,y_test))
score=model.evaluate(x_test,y_test,verbose=0)
print('TEST loss:', score[0])
print('TEST accuracy:', score[1])
print("Computation time:{0:.3f} sec".format(time.time()-start_time))

・出力結果

Epoch 1/10
60/60 [==============================] - 0s 5ms/step - loss: 2.0619 - accuracy: 0.3990 - val_loss: 1.7701 - val_accuracy: 0.5962
Epoch 2/10
60/60 [==============================] - 0s 3ms/step - loss: 1.5951 - accuracy: 0.6823 - val_loss: 1.4248 - val_accuracy: 0.7480
Epoch 3/10
60/60 [==============================] - 0s 3ms/step - loss: 1.3059 - accuracy: 0.7682 - val_loss: 1.1791 - val_accuracy: 0.7974
Epoch 4/10
60/60 [==============================] - 0s 3ms/step - loss: 1.0928 - accuracy: 0.8054 - val_loss: 0.9965 - val_accuracy: 0.8330
Epoch 5/10
60/60 [==============================] - 0s 3ms/step - loss: 0.9339 - accuracy: 0.8368 - val_loss: 0.8598 - val_accuracy: 0.8535
Epoch 6/10
60/60 [==============================] - 0s 3ms/step - loss: 0.8132 - accuracy: 0.8555 - val_loss: 0.7541 - val_accuracy: 0.8699
Epoch 7/10
60/60 [==============================] - 0s 3ms/step - loss: 0.7196 - accuracy: 0.8677 - val_loss: 0.6721 - val_accuracy: 0.8782
Epoch 8/10
60/60 [==============================] - 0s 3ms/step - loss: 0.6458 - accuracy: 0.8769 - val_loss: 0.6062 - val_accuracy: 0.8858
Epoch 9/10
60/60 [==============================] - 0s 3ms/step - loss: 0.5870 - accuracy: 0.8839 - val_loss: 0.5539 - val_accuracy: 0.8904
Epoch 10/10
60/60 [==============================] - 0s 3ms/step - loss: 0.5397 - accuracy: 0.8890 - val_loss: 0.5122 - val_accuracy: 0.8944
TEST loss: 0.512151837348938
TEST accuracy: 0.8944000005722046
Computation time:2.399 sec

パラメータの更新を600回行い、交差エントロピー誤差は「0.512」テストデータによる検証では「0.894」の正答率を出せたことがわかります。

経過の表示

「学習の実行」を行った時点でおおむね目的は果たせていますが、最後に交差エントロピー誤差正答率の経過をグラフ上に表示していきたいと思います。

・実装例

# 経過表示
plt.figure(1,figsize=(10,4))
plt.subplots_adjust(wspace=0.5)

# 交差エントロピー誤差の経過表示
plt.subplot(1,2,1)
plt.plot(history.history['loss'],label='trainig',color='black')
plt.plot(history.history['val_loss'],label='test',color='cornflowerblue')
plt.ylim(0,10)
plt.legend()
plt.grid()
plt.xlabel('epoch')
plt.ylabel('loss')

# 正答率の経過表示
plt.subplot(1,2,2)
plt.plot(history.history['accuracy'],label='training',color='black')
plt.plot(history.history['val_accuracy'],label='test',color='cornflowerblue')
plt.ylim(0,1)
plt.legend()
plt.grid()
plt.xlabel('epoch')
plt.ylabel('accuracy')
plt.show()

・出力結果
f:id:wantanBlog:20200725002841p:plain


今回の結果では手書き数字という単純な分類でありながら正答率は「0.894」とそこまで高い結果を出すことができませんでした。
今回はまず実装を行うことを重きに置いていたため、次回以降は精度をあげるための工夫を取り入れていきたいと思います。

ーーーーーーーーーーーーー
・今回のソース
python_dev/MNIST_1.ipynb at master · wantanblog/python_dev · GitHub