コンテンツにスキップ

4 手書き文字認識

4.1 データの読み込み

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

今回は機械学習の練習で使うデータセットの8x8の手書き画像を読み込みます。 ファイル deep_digits.ipynb に以下を記述します。

from sklearn import datasets

digits = datasets.load_digits()
x = digits.images
y = digits.target

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

digits.images[0]

二次元配列になっており、0~15までの数が入っています。これが手書き文字の画像です。データは縦8ドット×横8ドットで、数字が大きいほど色が濃いドットを表しています。

この数字が何を表しているかは、digits.target に答えが入っています。

digits.target[0]

4.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(digits.target[i]) )
    plt.imshow( digits.images[i], cmap="gray" )

4.3 学習と評価

では、これを学習しましょう。

二次元のリストであるxを一次元のリストに変換にし、訓練データと学習データに分けます。

x = x.reshape(-1, 64)

from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x, y,  test_size=0.2, random_state=0)

モデルを構築します。今回は隠れ層を3層配置します。

from sklearn.neural_network import MLPClassifier

model = MLPClassifier(
    hidden_layer_sizes=(100,50,50),  # 隠れ層
    max_iter=100,               # 最大反復回数
    random_state=0
)

今回はまず、最初に8x8で64個あるニューロンを100個の層に結合します。その後、50個、50個と結合し、最後に10個のニューロンになる出力層があります。

この出力層10個が0~9の数値に対応します。0番目の数値が一番大きいなら0と予測されたことになります。

学習し、予測して評価を出してみます。

# 学習
model.fit(x_train, y_train)

# 評価
model.score(x_test, y_test)

決定木、ランダムフォレストよりも高い結果がでます。 このようにニューラルネットワークは画像認識により高い精度を発揮します。

4.4 確率表示

実際に、確率を表示してみます。テストデータの1番目を出してみましょう。

index = 1
proba = model.predict_proba(x_test)  # 確率取得
for x in range(10):
    print(f"{x} の確率: {proba[index][x]:.4f}")

print(f"正解:{y_test[index]}")

4.5 誤差のグラフ表示

誤差を学習回数毎にグラフで表示してみます。model.loss_curve_ に記録されています。

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

plt.plot(model.loss_curve_)
plt.title('学習の損失推移')
plt.xlabel('反復回数')
plt.ylabel('損失')
plt.show()

4.6 パラメータ数

パラメータの総数を計算してみます。

param_sum = 0

for i, coef in enumerate(model.coefs_):
    print(f"Layer {i+1} 重み付け : {coef.shape} パラメータ数:{coef.shape[0] * coef.shape[1]}")
    param_sum += coef.shape[0] * coef.shape[1] 

for i, bias in enumerate(model.intercepts_):
    print(f"Layer {i+1} バイアス : {bias.shape[0]}")
    param_sum += bias.shape[0]

print(f"総パラメータ数: {param_sum}")