今回は手書きの数字をディープランニングを通じて学んでみましょう。前回アヤメの分類問題の記事をご覧になっていない方はそちらをまずは一読ください。
ディープラーニングで手書き数字の判定
手書き数字の判定を行いましたが、認識精度はそれほど高いものではありませんでした。そこで、ディープラーニングを利用して、再度手書き数字の認識に挑戦してみます。TensorFlow の利用例としても、よく利用される例題です。
MNIST データを利用しよう
MNISTのデータは、白黒画像の手書き数字のデータセットです。学習用の画像は6万枚、テスト用の画像は1万枚もの画像が用意されています。それぞれの画像は、28 × 28 ピクセル、グレースケールです。グレースケールというのは、0(白)~255(黒)の範囲の値で構成される画像です。Keras には、機械学習で使えるさまざまなデータセットが用意されており、MNIST のデータも、Kerasのデータセットに含まれています。それでは、MNIST がどんなデータであるのか、実際に確認してみましょう。
import keras
from keras.datasets import mnist
from matplotlib import pyplot
# MNIST のデータを読み込み
(X_train, y_train), (X_test, y_test)=mnist.load_data()
# データを4×8に出力
for i in range (0, 32):
pyplot.subplot(4, 8, i + 1)
Dyplot.imshow (X_train[i], cmap=’gray’)
pyplot.show ()
実行すると、以下の画像が表示されます。3章で扱った手書き数字のデータセットに比べると、質も量も大きなデータセットです。
仮想マシン (VirtualBox) でプログラムを実行した場合、メモリー不足でMNIST データのダウンロードの途中でエラーが起きることがあります。その場合、設定でメモリーの割り当てを増やす必要があります。VirtualBox の設定を開き、システム>マザーボードとタブを開きます。そして、メインメモリーの割当量を増やします。一度仮想マシンの電源を落とした状態でないと変更できません。
とは言え、実際のデータを見てみないと、どんなデータなのか実感できないでしょう。先ほど読み出した MNIST のデータをJupyter Notebook で表示してみましょう。
X_train
実行してみましょう。これを見ると三次元の配列であり、確かに、1つの画像が 25 × 25個の二次元配列となっていることが確認できます。
最低限のニューラルネットワークでMNIST;分類を解く
それでは、前回アヤメの分類を行ったのと同じ、最低限のニューラルネットワークを使って、MNISTの手書き数字を分類してみましょう。
一次元の配列に変換して正規化
まず、画像1つが二次元なので、28× 28で784の一次元配列に変換しましょう。また、データの範m+00から1.0の間に正規化する必要もあるので、色の最大の値255 で割りましょう。
# データを28*28=784 の一次元配列に変換
X train = X_train.reshape (-1, 784). astype(‘float32’) / 255
X_test=X_test.reshape (-1, 784).astype(‘float32’) / 255
# データを確認
X_train
プログラムをJupyter Notebook で実行してみると、以下のように、各画像が一次元に変換されていることを確認できます(データが二次元なのは、一次元の画像データが複数あるためです)。
また、前回のアヤメの分類問題のときと同じく、ラベルデータをOne-Hot ベクトルに変換しておきましょう。前回は、map0 関数を使って変換しましたが、Keras の機能を使って変換することもできます。
# ラベルデータを One-Hot ベクトルに直す
y_train = keras.utils.to_categorical (y_train.astype (‘int32’), 10)
y_test = keras.utils.to_categorical (y_test.astype(‘int32’), 10)
Keras でモデルを構築
それでは、Keras でニューラルネットワークのモデルを組んで、分類問題に挑戦してみましょう。
# 入力と出力を指定(* 1)
in_size = 28 * 28
out_size = 10
# モデル構造を定義(* 2)
Dense = keras.layers.Dense
model = keras.models.Sequential ()
model.add(Dense (512, activation=’relu’, input_shape=(in_size,)))
model.add (Dense (out_size, activation=’softmax’))
# モデルを構築(* 3)
model.compile(
loss=’categorical_crossentropy’,
optimizer=’adam’,
metrics=[‘accuracy’])
# 学習を実行(* 4)
model.fit(X_train, y_train,
batch_size=20, epochs=20)
# モデルを評価(* 5)
score = model.evaluate (X_test, y_test, verbose=1)
print (‘E =’, score[1], ‘loss=’, score[0])
データ数が多いため、プログラムの実行にはある程度時間がかかります。簡単なニューラルネットワークなので、それほど結果に期待していませんでしたが、それでも最終結果は、正解率 0.9808(約98%)とかなり良いものとなりました。プログラムを見てみましょう。(※ 1)では、入力と出力のサイズを指定します。入力は画像1枚のサイズ (28 × 28 ピクセル)で、出力は0から9までのいずれかなので、10段階となります。(※2)の部分では、簡単なニューラルネットワークのモデル構造を定義します。ここでは、前節よりも個数を増やして、ユニット数が512個あるネットワーク構造を構築します。(※3)の部分でモデルを構築し、(※4)の部分では、実際に学習を実行します。そして、最後の(※5)の部分でモデルを評価します。
MLP でMNIST の分類問題に挑戦しよう
次に、『多層パーセプトロン(multilayer perceptron)』のアルゴリズムを利用して、MNIST の分類問題に挑戦してみましょう。多層パーセプトロンは略して MLP と呼ばれています。MLPは、以前紹介した、以下のような構造のニューラルネットワークです。

▲多層パーセプトロン(MLP)の構造
特徴としては、入力層からデータを入力した後、複数の隠れ層を経て出力層に出力されます。これをKeras で表すには、以下のようなモデルを定義します。
in_size= 28 * 28
out_size = 10
~省略~
Model=Sequential()
model.add(Dense (512, activation=’relu’, input_shape=(in_size,)))
model.add (Dropout (0.2))
model.add (Dense (512, activation=’relu’))
model.add(Dropout (0.2))
model.add (Dense (out_size, activation=’softmax’))
上記のプログラムでは、MLP の特徴を反映して、model.add) メソッドを用いて、複数の隠れ層を追加していることがわかることでしょう。
ドロップアウト -忘れることで精度が高まる?!
ところで、model.add) で、Dropout() 関数が使われています。これは、ドロップアウト処理を加えるものです。ドロップアウトというのは、入力値のうちいくらかをランダムに0にセットする機能です。つまり、覚えたことを忘れることを意味するのですが、これにより過学習を防ぐことができます。忘れることで学習精度が上がるというのは、とても興味深いと思いませんか。
MLPの完全なプログラム
ここまで、少しずつプログラムを紹介しましたが、ここで完全なプログラムを一気に紹介します。
# MLP でMNISTの分類問題に挑戦
import keras
from keras.models import Sequential
from keras.layers import Dense, Dropout
from keras.optimizers import RMSprop
from keras.datasets import mnist
import matplotlib.pyplot as plt
# 入力と出力を指定
in_size=28 * 28
out_size = 10
# MNIST のデータを読み込み(*1)
(X_train, y_train), (X_test, y_test) = mnist.load_data()
# データを28*28=784 の一次元配列に変換
X_train = X_train.reshape(-1, 784).astype (‘float32’) / 255
X_test = X_test.reshape (-1, 784).astype (‘float32’) / 255
# ラベルデータをOne-Hot ベクトルに直す
y_train = keras.utils.to_categorical(y_train.astype (‘int32’),10)
y_test = keras.utils.to_categorical (y_test.astype (‘int32′),10)
# MLP モデル構造を定義(*2)
model =Sequential ()
model.add(Dense (512, activation=’relu’, input_shape=D(in_size,)))
model.add (Dropout (0.2))
model.add(Dense (512, activation=’relu’))
model.add (Dropout (0.2))
model.add (Dense (out_size, activation=’softmax’))
# モデルを構築(*3)
model.compile(
loss=’categorical_crossentropy’
optimizer=RMSprop(),
metrics=[‘accuracy’])
#学習を実行(*4)
hist = model.fit(X_train, y_train,
batch_size=128,
epochs=50,
verbose=1,
validation_data=(X_test, y_test))
# モデルを評価(*5)
score = model.evaluate(X_test, y_test, verbose=1)
print (‘ 正解率=’, score [1], ‘loss=’,score[0])
# 学習の様子をグラフへ描画(*6)
# 正解率の推移をプロット
plt.plot (hist.history[‘accuracy’])
plt.plot (hist.history[‘val_accuracy’])
plt.title(‘Accuracy’)
plt.legend ([‘train’, ‘test’], loc=’upper left’)
plt.show ()
# ロスの推移をプロット
plt.plot (hist.history[‘loss’])
plt.plot (hist.history[‘val_loss’])
plt.title(‘Loss’)
plt.legend ([‘train’, ‘test’], loc=’upper left’)
plt.show()
プログラムを実行すると、正解率は0.985(98.5%) になり、先ほどよりも若干、精度が改善されました。プログラムを確認してみましょう。(※1)の部分では、MNIST のデータを読み込み、ニューラルネットワークに与えるのに相応しい形式に変換します。(※2)の部分では、MLP のモデルを定義します。この部分については、すでに紹介した通り、隠れ層を多層にしているところがポイントです。(※3)の部分でモデルを構築し、(※4)で学習を実行します。最後に、(※5)の部分で最終的なスコアを算出して表示します。また、先ほども紹介したように、Kerasのfit) メソッドには、正解率(accuracy) の履歴が記録されます。そこで、(※6)の部分では、その値を利用して正解率と損失関数の値グラフを描画します。「損失関数 (loss)」とは正解とどれくらい離れているかを表す数値です。
改良のヒント
先ほど MLP を使うことで、MNIST で正解率 0.985(98.5%) という値を叩き出すことができました。しかし、『畳み込みニューラルネットワーク(Convolutional Neural Network、略称:CNN)』を利用することで、より高い精度を出すことができます。CAINは畳み込み層(convolution layer) とブーリング層 (pooling layer) から構成されるニューラルえットワークです。画像データを解析する際に高い精度を出せることで有名ですが、音声認識や額始出、レコメンド機能や翻訳など、さまざまな用途で利用されます。
▲ CNN の仕組み
この仕組みを、TensorFlow と Keras を使って実装してみましょう。基本的には、MLP を使った前回のプログラムと同じです。しかし、モデルの組み方、また、パラメーターの持たせ方が少しずつ変わっています。それでは、最初にプログラムを見てみましょう。
# CNN でMNIST の分類問題に挑戦
import keras
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras.optimizers import RMSprop
from keras.datasets import mnist
import matplotlib.pyplot as plt
# 入力と出力を指定(*1)
im_rows = 28 # 画像の縦ピクセルサイズ
im_cols = 28 # 画像の横ピクセルサイズ
im_color=1# 画像の色空間 / グレースケール
in_shape=(im_rows, im_cols, im_ċolor)
out_size = 10
# MNIST のデータを読み込み
(X_train, y_train), (X_test, y_test) = mnist.load_data()
# 読み込んだデータを三次元配列に変換(*1a)
X_train = X_train.reshape(-1, im_rows, im_cols, im_color)
X_train = X_train.astype (‘float32’) / 255
x-test=X_test.reshape (-1, im_rows, im_cols, im_color)
x-test=X_test.astype (‘float32’) / 255
# ラベルデータを One-Hot ベクトルに直す
y_train= keras.utils.to_categorical(y_train.astype (‘int32’),10)
y_test= keras.utils.to_categorical (y_test.astype(‘int32’),10)
# CNN モデル構造を定義(*2)
Model=Sequential ()
model.add (Conv2D(32,
kernel_size=(3, 3),
activation=’relu’,
input_shape=in_shape))
model.add (Conv2D (64, (3, 3), activation=’relu’))
model.add (MaxPooling2D (pool_size=(2, 2)))
model.add(Dropout (0.25))
model.add (Flatten())
model.add(Dense(128, activation=’relu’))
model.add (Dropout (0.5))
model.add (Dense(out_size, activation=’softmax’))
# モデルを構築(*3)
model.compile(
loss=’categorical_crossentropy’,
optimizer=RMSprop (),
metrics=[‘accuracy’])
# 学習を実行 (*4)
hist = model.fit(X_train, y_train,
batch_size=128,
epochs=12,
verbose=1,
validätion_data=(X_test, y_test))
# モデルを評価(* 5)
Score =model.evaluate (X_test, y_test, verbose=1)
print (‘ 正解率=’, score[1], ‘loss=’, score [0])
# 学習の様子をグラフへ描画(*6)
# 正解率の推移をプロット
plt.plot (hist.history [‘accuracy’])
plt.plot (hist.history[‘val_accuracy’])
plt.title(‘Accuracy’)
plt.legend ([‘train’, ‘test’], loc=’upper left’)
plt.show()
# ロスの推移をプロット
plt.plot (hist.history[‘loss’])
plt.plot (hist.history[‘val_loss’])
plt.title(‘Loss’)
plt.legend([‘train’, ‘test’], loc=’upper left’)
plt.show()
プログラムを実行すると、以下のように結果が表示されます。実行には時間がかかりますが、先ほどのMLPよりもより精度が高くなり、実行結果には正解率0.9894(約 99%)と高精度な値が表示されました。それでは CNN の仕組みを考えつつ、少しずつプログラムを見ていきましょう。ここまでの部分で、画像の分類問題では、二次元の画像を一次元のベクトルに変形して、ニューラルネットワークに学習させていました。しかし、CNNでは二次元の画像の形を畳み込みすることで、画像の特徴を掴んだ分類を行います。ここでプログラムの(※1)の部分を見てみましょう。CNN では、畳み込み層を構成するために、画像の縦·横·色の三次元に変換する必要があります。そのために、(※ 1a)の部分では、読み込んだMNIST のデータを三次元に変形します。この処理を行うことで、以下のようなデータとなります。
0だけが入った配列を見ると、何の意味があるのか一見わからないと思います。しかし、一般的な画像はカラーであり、赤·緑·青と光の三原色を指定することになります。つまり、1ピクセルごとに、1つの配列が用意されると考えてください。Zして、このプログラムのメインとなるのが、(※2)の部分です。CNN のモデルを定義します。Conv2D0が畳み込み層の作成、MaxPooling2D) がプーリング層の作成、Flatten0 では入力を平滑化します。畳み込み層では、画像の特徴量の畳み込みを行います。どういうことかと言うと、画像の各部分にある特徴を調べます。そして、プーリング層では、画像データの特徴を残しつつ圧縮します。圧縮することで、その後の処理が行いやすくなります。プログラムの(※ 3) 以降はこれまでと同じで、モデルを構築し、(※4)で学習を実行し、(※ 5)でモデルを評価し、(※6)で学習の様子のグラフを描画します。ここでは、TensorFlow と Keras を利用して、MNIST の手書き数字の分類を行ってみました。MLP やCNN などのアルゴリズムを紹介しました。ディープラーニングの手法を使うことで、かなり高精度の分類ができることがわかったのではないでしょうか。
この節のまとめ
・TensorFlow/Keras を使うと手軽にディープラーニングの手法を実践できる
・MLP や CNN のアルゴリズムを実装する方法を紹介した
・ディープラーニングを実践する場合も、これまでの機械学習とほとんど同じ手順で問題を解くことができる
いかがでしたでしょうか。数字の認識は今後の筆跡認証などの応用に用いられる可能性もある重要なアルゴリズムの一つです。次回は写真に写った物体を認識するアルゴリズムをご紹介させていただく予定なのでよろしくお願いします。