【Scikit-learn】Olivetti facesデータセットで畳み込みニューラルネットワーク(CNN)してみる

機械学習

前回『【Scikit-learn】Olivetti facesデータセットで深層ニューラルネットワーク(DNN)してみる 』これをやって、64×64の画像分類はDNNではまったく歯が立たないことがわかりました。それじゃあ、CNNならどうなるの?をやらないわけにはいきません。ということで、今回は【Scikit-learn】Olivetti facesデータセットこちらで、CNNをやってみようと思います。

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

import 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 fetch_olivetti_faces
faces=fetch_olivetti_faces()                        

前回と同じですね。
前回1人目(名前:0)の方を見たので、今回は2人目(名前:1)の方を見てみましょう。

fig=plt.figure(figsize=(30,15))

def ax(x):
  return fig.add_subplot(2,5,x+1)

for i in range(10):
  ax(i)
  ax(i).matshow(faces.images[i+10])

plt.gray()
plt.tight_layout() 
plt.show()

この方も、表情やら向きやらを微妙に変えておられます。
同じように、データを準備していきます。が、今回はCNNなので、faces.imagesこちらを使います。

x=faces.images
t_=faces.target

t_df=pd.get_dummies(t_)
t=t_df.values

x_train,x_test,t_train,t_test=train_test_split(x,t,test_size=0.3,stratify=t,random_state=0)
x_train,x_train_val,t_train,t_train_val=train_test_split(x_train,t_train,test_size=0.2,stratify=t_train,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)
(224, 64, 64) (56, 64, 64) (120, 64, 64)
(224, 40) (56, 40) (120, 40)

これでやっていきます。

CNNモデルの構築と学習

書き方は非常に簡単で、見た目はDNNとそんな変わらないですが、やることは一変します。

model=models.Sequential()
model.add(layers.Conv2D(32,(3,3),activation="relu",input_shape=(64,64,1)))
model.add(layers.MaxPooling2D(2,2))
model.add(layers.Conv2D(64,(3,3),activation="relu"))

model.add(layers.Flatten())
model.add(layers.Dense(256, activation="relu"))
model.add(layers.Dense(40, activation="softmax"))
  • 64×64×1(64×64、1色)のデータを入力
  • 次に、3×3×1のカーネルを32枚をあて、reluを通して、62×62×32の特徴量マップをつくる
  • 62×62×32これを、2×2でマックスプーリングして、31×31×32に変換
  • 今度は、3×3×32のカーネルを64セット用意、各々先の31×31×32にあてて、reluを通して、
    29×29×64の特徴量マップをつくる
  • 29×29×64=53,824次元のベクトル化、256個のニューロンと全結合、reluで出力
  • 40個のニューロンに全結合、softmaxで各々の確立を出力

こんなことを書いたと理解しています。
以下は、DNNと同じように、

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

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

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

こうして、学習です。

Epoch 16/100
loss: 0.0046 - accuracy: 1.0000 - val_loss: 0.1576 - val_accuracy: 0.9821
Epoch 17/100
loss: 0.0035 - accuracy: 1.0000 - val_loss: 0.1541 - val_accuracy: 0.9643
Epoch 18/100
loss: 0.0032 - accuracy: 1.0000 - val_loss: 0.1536 - val_accuracy: 0.9821
Epoch 19/100
loss: 0.0028 - accuracy: 1.0000 - val_loss: 0.1532 - val_accuracy: 0.9821

エポック19で止まって、ご覧の通りの正解率です。CNNすげえ。

print(model.evaluate(x_train,t_train))
print(model.evaluate(x_train_val,t_train_val))
print(model.evaluate(x_test,t_test))
loss: 0.0026 - accuracy: 1.0000
loss: 0.1532 - accuracy: 0.9821
loss: 0.3027 - accuracy: 0.9250

テストデータに対して、92.5%の正解率となり、
前回『【Scikit-learn】Olivetti facesデータセットで深層ニューラルネットワーク(DNN)してみる 』のlightGBMの結果を大幅に上回りました。