【Scikit-learn】手書き数字データセットで深層ニューラルネットワーク(DNN)してみる

機械学習

以前、『Scikit-learnカリフォルニア住宅価格データセットで深層ニューラルネットワークしてみる』『Scikit-learn乳がん診断データセットで深層ニューラルネットワークしてみる』これらの記事で各々回帰と分類をDNNでやりました。今回は、【Scikit-learn】手書き数字データセットでやろうと思います。画像の分類を、全結合層だけでやってみるという内容です。

データの確認とか準備とか

importimport numpy as np
import pandas as pd
import matplotlib.pyplot as plt
plt.style.use("ggplot")

from keras import models
from keras import layers
from keras.callbacks import EarlyStopping
from sklearn.model_selection import train_test_split

いつものように、必要なライブラリをインポート。

from sklearn.datasets import load_digits
digits=load_digits()                          

この、load_digits()でデータを呼び出します。

print(digits.images.shape)
print(digits.data.shape)
print(digits.target.shape)
(1797, 8, 8)
(1797, 64)
(1797,)

images/data/targetに各々こんな型でデータが入ります。

  • images:1797個の8×8の行列
  • data:1797個の64個の値を持つベクトル
  • target:1797個の正解データ

imagesのデータを少し見てやると、

plt.gray()
plt.matshow(digits.images[0])
digits.target[0]
0

ということで、1個目のデータはゼロの手書き数字で、正解もゼロというわけです。これが1797個ありますよと。dataの方も見てやると、

digits.data[0]
array([ 0.,  0.,  5., 13.,  9.,  1.,  0.,  0.,
        0.,  0., 13., 15., 10., 15.,  5.,  0.,
        0.,  3., 15.,  2.,  0., 11.,  8.,  0.,
        0.,  4., 12.,  0.,  0.,  8.,  8.,  0.,
        0.,  5.,  8.,  0.,  0.,  9.,  8.,  0.,
        0.,  4., 11.,  0.,  1., 12.,  7.,  0.,
        0.,  2., 14.,  5., 10., 12.,  0.,  0.,
        0.,  0.,  6., 13., 10.,  0.,  0.,  0.])

8×8の行列が1個のベクトルに変換されています。画像と見比べると、各位置の色の濃淡が一つの数字で表現されています。

print(np.unique(digits.data))
print(np.unique(digits.target))
[ 0.  1.  2.  3.  4.  5.  6.  7.  8.  9. 10. 11. 12. 13. 14. 15. 16.]
[0 1 2 3 4 5 6 7 8 9]

ということで、色の濃淡は17段階で表現され、正解データは0~9までの数字となっています。
中身が概ね分かったところで、学習のためのデータを用意していきます。

x=digits.data/16
t_=digits.target

xは16で割って正規化しておきます。

t_
array([0, 1, 2, ..., 8, 9, 8])

先に見た通り、これは1797個の0~9までの数字です。
最終の出力は、一個のdataベクトルに対し、0~9各々対する確率を出力させるので、
入力もこの形に合わせます。

t=pd.get_dummies(t_)
t=t.values
t
array([[1, 0, 0, ..., 0, 0, 0],
       [0, 1, 0, ..., 0, 0, 0],
       [0, 0, 1, ..., 0, 0, 0],
       ...,
       [0, 0, 0, ..., 0, 1, 0],
       [0, 0, 0, ..., 0, 0, 1],
       [0, 0, 0, ..., 0, 1, 0]], dtype=uint8)

データフレームからndarrayに戻しています。
一個目は、正解0だったので、左端に1他0となっています。

x_train,x_test,t_train,t_test=train_test_split(x,t,test_size=0.3,random_state=0)
x_train,x_train_val,t_train,t_train_val=train_test_split(x_train,t_train,test_size=0.2,random_state=0)
print(x_train.shape,x_train_val.shape,x_test.shape)
print(t_train.shape,t_train_val.shape,t_test.shape)
(1005, 64) (252, 64) (540, 64)
(1005, 10) (252, 10) (540, 10)

データを分けて準備完了です。

モデルの構築と学習

多値分類なので、出力層の活性化関数はソフトマックス関数に指定します。

model=models.Sequential()
model.add(layers.Dense(1024,activation="relu",input_dim=64))
model.add(layers.Dense(512,activation="relu"))
model.add(layers.Dense(256,activation="relu"))
model.add(layers.Dense(128,activation="relu"))
model.add(layers.Dense(64,activation="relu"))
model.add(layers.Dense(32,activation="relu"))
model.add(layers.Dense(16,activation="relu"))
model.add(layers.Dense(10,activation="softmax"))

仰々しいですが、まあ、こんなでやってみます。

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

callbacks=[EarlyStopping(monitor="val_accuracy",patience=10)]

results=model.fit(x_train,
                  t_train,
                  epochs=1000,
                  batch_size=80,
                  verbose=1,
                  callbacks=callbacks,
                  validation_data=(x_train_val,t_train_val))

最適化方法やら、損失関数やら、評価指標を決めて学習です。

サクッと終わって、結果を見ると、

pred_x_test=model.predict(x_test)
np.round(pred_x_test)
array([[0., 0., 1., ..., 0., 0., 0.],
       [0., 0., 0., ..., 0., 1., 0.],
       [0., 0., 1., ..., 0., 0., 0.],
       ...,
       [0., 0., 0., ..., 0., 0., 0.],
       [0., 0., 1., ..., 0., 0., 0.],
       [0., 0., 0., ..., 0., 0., 1.]], dtype=float32)

各々確率で出力されるので、roundで丸めています。
testデータに対して、1個目は2、2個目は8、3個目は2…という予測をしてくれています。
正解率は、

from sklearn import metrics
print(metrics.accuracy_score(t_train,np.round(model.predict(x_train))))
print(metrics.accuracy_score(t_train_val,np.round(model.predict(x_train_val))))
print(metrics.accuracy_score(t_test,np.round(model.predict(x_test))))
1.0
0.9761904761904762
0.9777777777777777

このぐらいのデータだからなのでしょう、全結合だけでも98%近い正解率を得ました。
畳み込みやプーリングを使うと、ここからさらに上がるんでしょうね。