コンテンツにスキップ

第4章 手書き文字認識

1 データの読み込み

ここでは手書きで描いた数字の画像データを読み込んでそれが何という数字を書いたかを認識(予測)します。

今回は機械学習の練習で使う有名なデータセットのうち、kerasに内蔵されている手書き文字画像MNINSTのデータを使用します。今回の画像は28x28と大きい画像になっています。

さっそく、読み込んでみましょう。 ファイル deep1.ipynb に以下を記述します。

# MNISTのデータをダウンロード
from tensorflow.keras.datasets import mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()

x_train.shape

60000個の 28 x 28 の画像があります。

最初のデータを表示してみます。

x_train[0]

すると二次元配列になっており、0~255までの数が入っています。これが手書き文字の画像です。データは縦28ドット×横28ドットです。

この数字が何を表しているかは、y_train[0] に答えが入っています。

y_train[0]

2 画像の表示

数値ではわかりにくいので画像で表示してみましょう。 matplotlibでimshow関数にこのデータを渡すと画像として表示されます。

for文を使って最初の15個を表示してみましょう。 matplotlibのsubplot機能を使って3行×5列の複数の画像を表示してみます。

import matplotlib.pyplot as plt

for i in range(15):
    plt.subplot(3, 5, i+1)
    plt.axis("off")
    plt.title( str(y_train[i]) )
    plt.imshow( x_train[i], cmap="gray" )

3 データ

では、これを学習しましょう。ただし、ディープラーニングで学習する場合には、255のような値よりも0~1の値にした方が処理しやすくなりますので事前に変換しておきます。これを正規化と言います。

# 画素値を0~255から0~1に変換
x_train = x_train.astype('float32') / 255.0
x_test = x_test.astype('float32') / 255.0

4 モデルの構築

今回もはKerasを使用します。

import keras
model = keras.models.Sequential()

from keras import layers

model.add(layers.Flatten(input_shape=(28, 28)))
model.add(layers.Dense(128, activation='relu'))
model.add(layers.Dense(10, activation="softmax"))
model.summary()

今回は まず、Flattenで28x28のデータを一次元配列にします。そして、layers.Denseで最初の層に128個のニューロンを配置します。

最後に10個のニューロンになる層を追加します。これが出力層です。この出力層が10個なのは最終的に0~9のどの数値であるかを予測するためです。0番目の数値が一番大きいなら0と予測されたことになります。

5 学習と評価

学習前にcompileを行います。これは前回と同じです。

model.compile(optimizer="adam",
              loss="sparse_categorical_crossentropy",
              metrics=["accuracy"])

学習にはfitを使用します。一回の学習に時間がかかるため、epochsを10とします。model.evaluateで評価を得ます。戻り値は損失率と正解率です。

history = model.fit(x_train, y_train, epochs=10, 
        validation_data=(x_test, y_test))

test_loss, test_acc = model.evaluate(x_test, y_test)
print(f"テストデータの正解率は{test_acc:.2%}です。")

6 正解率のグラフ表示

正解率を学習回数毎にグラフで表示してみます。accuracyが訓練データの正解率、val_accuracyがテストデータの正解率です。

import matplotlib.pyplot as plt
import seaborn as sns
sns.set(font=["Meiryo"])

plt.title("正解率")
plt.xlabel("学習回数")
plt.plot(history.history["accuracy"])
plt.plot(history.history["val_accuracy"])
plt.legend(["訓練","テスト"])
plt.show()

誤差を学習回数毎にグラフで表示してみます。lossが訓練データの正解率、val_lossがテストデータの正解率です。

plt.title("誤差")
plt.xlabel("学習回数")
plt.plot(history.history["loss"])
plt.plot(history.history["val_loss"])
plt.legend(["訓練","テスト"])
plt.show()

7 実際の推論

実際に値を与えて推論してみます。x_test のデータを推論させます。

pre = model.predict(x_test)

その0番目の推論結果を表示してみましょう。 画像を確認してみます。

index = 0  # 何番目を推論するか

plt.axis("off")
plt.imshow(x_test[index].reshape(28,28), cmap="Greys")

推論結果を表示してみましょう。

p = pre[index] # 推論結果をp にいれる

# p の0番目~9番目のデータを表示してみる。
for i in range(10):
    print(f"{i}番目 {p[i]:.8f}")

最も数値が高い番目が推論した数字となります。改めて最大値がいくつなのかを表示し、正解と比較してみましょう。

import numpy as np
n = np.argmax(p) # 最大値が何番目か
print("予測:", n)
print("確率:", p[n], "%")
print("正解:",y_test[index])

最初の15個を判定してみます。

pre = model.predict(x_test)

for i in range(15):
    plt.subplot(3, 5, i+1)
    plt.axis("off")
    plt.imshow( x_test[i].reshape(28,28), cmap="gray" )

    index = np.argmax(pre[i]) # 最大値が何番目か
    pct = pre[i][index] # 確率
    title = f"{y_test[i]} : {index} {pct:.0%}" # 正解 : 解答 確率%
    plt.title(title)