前回『【Scikit-learn】手書き数字データセットで深層ニューラルネットワーク(DNN)してみる』ここで、8×8の手書き数字を全結合層だけで分類(DNN)してみて、うまいこといきました。それじゃあもう少し難しい画像でも、ということで、【Scikit-learn】Olivetti facesデータセットこちらでDNNをやってみようと思います。
データの確認とか準備とか
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()
で、今回はこちらを使います。どういうデータかというと、
- 40人の方の顔写真(64×64)が各々10枚、計400枚の画像データ
- 1人の方の10枚の画像は、みんな顔写真なんだけど、向きや表情等変えてるよ
- 0から39でラベリングされてるので、誰の画像か判別する分類器作ってみてね
というものです。
print(faces.images.shape)
print(faces.data.shape)
print(faces.target.shape)
(400, 64, 64)
(400, 4096)
(400,)
images/data/targetに各々こんな感じでデータが入ってます。
1人目(名前:0)の方の画像を見てやると、
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])
plt.gray()
plt.tight_layout()
plt.show()
このように、表情やら向きやらを微妙に変えておられるわけです。
で、こちらがどなたか、というと、
faces.target[0]
0
0の方、というわけです。全結合層のみでやっていくので、faces.dataこちらを使います。
1個目のデータを見てやると、
faces.data[0]
array([0.30991736, 0.3677686 , 0.41735536, ..., 0.15289256, 0.16115703,
0.1570248 ], dtype=float32)
先のshapeで見た通り、色の濃淡が0から1までに正規化されて、4096個の数字で構成されています。
前回は64次元ベクトルだったわけですが、こちらは4096次元ベクトルとなります。
さて、全結合だけで戦えるのでしょうか?明らかに難易度上がってますよね。
print(np.unique(faces.target))
[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39]
targetの方も見ておくと、このように0から39のいずれかが、400枚分入ってます。
40人が数字でラベリングされているわけです。
それでは、データを形成していきます。
x=faces.data
t_=faces.target
t_
array([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
・・・・・・・
37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
39, 39, 39, 39, 39, 39, 39, 39, 39, 39])
1人ずつ順番に入ってますね。前回同様、最終の出力は、一個のdataベクトルに対し、0~39各々対する確率を出力させるので、入力もこの形に合わせます。
t_df=pd.get_dummies(t_)
t_df
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | |
0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
… | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … |
398 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
399 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
0の方から順番に格納されているので、こんな並びになります。
で、これを、データフレームからndarrayに戻して、
t=t_df.values
t
array([[1, 0, 0, ..., 0, 0, 0],
[1, 0, 0, ..., 0, 0, 0],
[1, 0, 0, ..., 0, 0, 0],
...,
[0, 0, 0, ..., 0, 0, 1],
[0, 0, 0, ..., 0, 0, 1],
[0, 0, 0, ..., 0, 0, 1]], dtype=uint8)
学習用検証用テスト用にスプリット、
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, 4096) (56, 4096) (120, 4096)
(224, 40) (56, 40) (120, 40)
できました。
DNNモデルの構築と学習
こんなモデルでやってみます。
model=models.Sequential()
model.add(layers.Dense(1024,activation="relu",input_dim=4096))
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(40,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=50,
verbose=1,
callbacks=callbacks,
validation_data=(x_train_val,t_train_val))
Epoch 17/1000
loss: 3.6884 - accuracy: 0.0268 - val_loss: 3.6913 - val_accuracy: 0.0179
Epoch 18/1000
loss: 3.6884 - accuracy: 0.0134 - val_loss: 3.6914 - val_accuracy: 0.0179
Epoch 19/1000
loss: 3.6883 - accuracy: 0.0268 - val_loss: 3.6916 - val_accuracy: 0.0179
Epoch 20/1000
loss: 3.6883 - accuracy: 0.0268 - val_loss: 3.6918 - val_accuracy: 0.0179
長いので、最後の数行の出力結果のみを書いていますが、
見ての通り、まーーったく分類器として成立していません。。。。
いろいろニューロン数を変えてみましたが、ことごとく同じような結果でした。
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))))
0.0
0.0
0.0
です。前回『【Scikit-learn】手書き数字データセットで深層ニューラルネットワーク(DNN)してみる』の手書き数字8×8=64次元では戦えましたが、4096次元では、全結合のみでは全く戦えないようです。
なるほど。
lightGBMでやってみる
このままでは終われないので、みんな大好きlightGBMでもやってみようと思います。
import lightgbm as lgb
lightGBMなので、こちらをそのまま使って、データを用意。
t_
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, 4096) (56, 4096) (120, 4096)
(224,) (56,) (120,)
各種ハイパーパラメータやら決めて、
lgb_train=lgb.Dataset(x_train,t_train)
lgb_eval=lgb.Dataset(x_train_val,t_train_val)
params = {"metric":"multi_logloss",
"objective":"multiclass",
"num_class":40,
"max_depth":10}
lgbm = lgb.train(params,
lgb_train,
valid_sets=lgb_eval,
num_boost_round=1000,
early_stopping_rounds=100,
verbose_eval=100)
Training until validation scores don't improve for 100 rounds.
[100] valid_0's multi_logloss: 0.45537
[200] valid_0's multi_logloss: 0.330648
Early stopping, best iteration is:
[145] valid_0's multi_logloss: 0.330648
学習完了。
さて、正解率は?!
from sklearn import metrics
print(metrics.accuracy_score(t_train_.values,np.round(lgbm.predict(x_train))))
print(metrics.accuracy_score(t_test__.values,np.round(lgbm.predict(x_test))))
1.0
0.825
ということで、さすがのlightGBMといったところでしょうか、4096次元ベクトルだろうが、
それなりの正解率を叩き出してくれました。