前回の記事では、CIFAR-10を題材にMLPのModelの構築と、パラメーターチューニングを行い、0.53049〜というスコア(Accuracy)を得ることができました。
今回はCNNを使いさらなる精度向上を目指してみたいと思います。
Model作成その③ CNN
データを準備するコードはこちら。
前回からreshapeを少し変えています。
# データのインポート
import numpy as np
from tensorflow.keras.datasets import cifar10
from tensorflow.keras.utils import to_categorical
(X_train, y_train), (X_test, y_test) = cifar10.load_data()
X_train, X_test = X_train.reshape(-1, 32, 32, 3), X_test.reshape(-1, 32, 32, 3)
X_train, X_test = X_train.astype('float32'), X_test.astype('float32')
X_train, X_test = X_train/255.0, X_test/255.0
y_train, y_test = to_categorical(y_train), to_categorical(y_test)
Modelのコードです。
チューニングはこの後行いますので、シンプルに畳み込み層とプーリング層を1層ずつだけ実装しました。
# ニューラルネットワーク構築(CNN)
import time
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, Conv2D, MaxPooling2D, Flatten
from tensorflow.keras import optimizers
# Sequentialオブジェクト
model = Sequential()
# 畳み込み層
model.add(Conv2D(filters=64, kernel_size=(5,5), padding='same', input_shape=(32, 32, 3), activation='relu'))
# プーリング層
model.add(MaxPooling2D(pool_size=(2,2)))
# Flatten層
model.add(Flatten())
# 出力層
model.add(Dense(10, activation='softmax'))
# コンパイル
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
# 学習を行う(GPU:オン)
start = time.time()
history = model.fit(
X_train,
y_train,
epochs=20,
batch_size=128,
validation_data=(X_test, y_test)
)
end = time.time()
# 結果
score = model.evaluate(X_test, y_test, verbose=1)
print('accuracy:', score[1])
print('loss :', score[0])
print('所要時間 :',end-start,'sec')
accuracy: 0.6527000069618225
loss : 1.1159632205963135
所要時間 : 43.79876708984375 sec
チューニング後のMLPよりも10%以上精度を上げられていますね!
CNNの強力さをシンプルに実感します。
Model作成その④ CNNパラメーターチューニング
前回同様hyperoptを使ってパラメーターチューニングを行います。
チューニング対象は畳み込み層のfilter、kernel_size、プーリング層/ドロップアウト層の配置の有無と、以降追加する層の数、そしてそのユニット数ならびに活性化関数……うん、ほぼ全部ですね。
データ作成/Model作成の関数です。
from hyperopt import hp
from hyperopt import Trials, tpe
from hyperas import optim
from hyperas.distributions import choice, uniform
def create_data():
import numpy as np
import time
from tensorflow.keras.datasets import cifar10
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, Conv2D, MaxPooling2D, Flatten
from tensorflow.keras import optimizers
from tensorflow.keras.utils import to_categorical
(X_train, y_train), (X_test, y_test) = cifar10.load_data()
X_train, X_test = X_train.reshape(-1, 32, 32, 3), X_test.reshape(-1, 32, 32, 3)
X_train, X_test = X_train.astype('float32'), X_test.astype('float32')
X_train, X_test = X_train/255.0, X_test/255.0
y_train, y_test = to_categorical(y_train), to_categorical(y_test)
return X_train, X_test, y_train, y_test
def create_model(X_train, X_test):
# Sequentialオブジェクト
model = Sequential()
# 畳み込み層(第一層)
model.add(
Conv2D(
filters={{choice([64, 128])}},
kernel_size={{choice([(3,3), (5,5), (7,7)])}},
padding='same',
input_shape=(32, 32, 3),
activation={{choice(['tanh', 'relu'])}}
)
)
# プーリング層
if {{choice([0, 1])}} == 0:
pass
else:
model.add(MaxPooling2D(pool_size=(2,2)))
# ドロップアウト
if {{choice([0, 1])}} == 0:
pass
else:
model.add(Dropout({{quniform(0.2, 0.6, 0.1)}}))
# 畳み込み層(第二層)
model.add(
Conv2D(
filters={{choice([32, 64])}},
kernel_size={{choice([(3,3), (5,5), (7,7)])}},
padding='same',
input_shape=(32, 32, 3),
activation={{choice(['tanh', 'relu'])}}
)
)
# プーリング層
if {{choice([0, 1])}} == 0:
pass
else:
model.add(MaxPooling2D(pool_size=(2,2)))
# ドロップアウト
if {{choice([0, 1])}} == 0:
pass
else:
model.add(Dropout({{quniform(0.2, 0.6, 0.1)}}))
# 畳み込み層(第三層)
model.add(
Conv2D(
filters={{choice([32, 64])}},
kernel_size={{choice([(3,3), (5,5), (7,7)])}},
padding='same',
input_shape=(32, 32, 3),
activation={{choice(['tanh', 'relu'])}}
)
)
# プーリング層
if {{choice([0, 1])}} == 0:
pass
else:
model.add(MaxPooling2D(pool_size=(2,2)))
# ドロップアウト
if {{choice([0, 1])}} == 0:
pass
else:
model.add(Dropout({{quniform(0.2, 0.6, 0.1)}}))
# Flatten層
model.add(Flatten())
# 以降
if {{choice([0, 1, 2])}} == 0:
pass
elif {{choice([0, 1, 2])}} == 1:
model.add(Dense({{choice([400, 500, 600])}},activation={{choice(['tanh', 'relu'])}}))
model.add(Dropout({{quniform(0.2, 0.6, 0.1)}}))
elif {{choice([0, 1, 2])}} == 2:
model.add(Dense({{choice([400, 500, 600])}},activation={{choice(['tanh', 'relu'])}}))
model.add(Dropout({{quniform(0.2, 0.6, 0.1)}}))
model.add(Dense({{choice([100, 200, 300])}},activation={{choice(['tanh', 'relu'])}}))
model.add(Dropout({{quniform(0.2, 0.6, 0.1)}}))
# 出力層
model.add(Dense(10, activation='softmax'))
model.compile(loss='categorical_crossentropy',optimizer={{choice(['adam', 'rmsprop'])}},metrics=['accuracy'])
# 学習を行う(GPU:オン)
history = model.fit(
X_train,
y_train,
epochs=30,
batch_size={{choice([128, 256])}},
validation_data=(X_test, y_test),
verbose=0
)
val_acc = np.amax(history.history['val_accuracy'])
return {'loss': -val_acc, 'status':STATUS_OK, 'model':model}
探索を実行するコードは基本前回と同じですが、検証回数を増やしています。
# 探索実行
best_run, best_model = optim.minimize(
model=create_model,
data=create_data,
algo=tpe.suggest,
max_evals=50,
eval_space=True,
notebook_name='__notebook_source__',
trials=Trials()
)
# 結果の確認
X_train, X_test, y_train, y_test = create_data()
score = best_model.evaluate(X_test, y_test)
print('accuracy:', score[1])
print('loss :', score[0])
100%|██████████| 50/50 [2:27:28<00:00, 176.98s/trial, best loss: -0.805400013923645]
accuracy: 0.8014000058174133
loss : 0.6208286881446838
無事、これまでで一番高いスコアが得られました。
参考までにチューニング後のModelの構造も見てみることとします。
# Model構造の表示
best_model.summary()
Model: "sequential_42"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d_126 (Conv2D) (None, 32, 32, 64) 1792
_________________________________________________________________
conv2d_127 (Conv2D) (None, 32, 32, 64) 102464
_________________________________________________________________
max_pooling2d_71 (MaxPooling (None, 16, 16, 64) 0
_________________________________________________________________
conv2d_128 (Conv2D) (None, 16, 16, 64) 200768
_________________________________________________________________
max_pooling2d_72 (MaxPooling (None, 8, 8, 64) 0
_________________________________________________________________
dropout_94 (Dropout) (None, 8, 8, 64) 0
_________________________________________________________________
flatten_42 (Flatten) (None, 4096) 0
_________________________________________________________________
dense_75 (Dense) (None, 500) 2048500
_________________________________________________________________
dropout_95 (Dropout) (None, 500) 0
_________________________________________________________________
dense_76 (Dense) (None, 10) 5010
=================================================================
Total params: 2,358,534
Trainable params: 2,358,534
Non-trainable params: 0
_________________________________________________________________
まとめ
各Modelでのスコアは以下の通りです。
Model | スコア |
---|---|
MLP(チューニングなし) | 0.4948999881744385 |
MLP(チューニングあり) | 0.5304999947547913 |
CNN(チューニングなし) | 0.6527000069618225 |
CNN(チューニングあり) | 0.8014000058174133 |
良いですね、工夫をしていくにつれてしっかりスコアも伸びています。
記事としてもまとめやすいね!
ちなみにこのスコアでSubmitすると、Leaderboard上は50位+くらいの成績です。
全体でも230名強しかいないのでアレですが、上位20%までもう一押し、ってところでしょうか。
分かりきっていたことですが、この程度の規模感のデータでもパラメーターチューニングを頑張ろうとするとそれなりに時間を喰いますね。
CNN(チューニングあり)では2時間半以上掛かっているので、その間に貯まってる家事でも済ませちゃいましょう。
ここから更にスコアを伸ばすとすれば、まだパラメーターのチューニングや層の配置に工夫の余地はあるかと思います。
割と適当に決めてしまったので、時間があるのであればグリッドサーチ等も試して見るべきでしょうか。
あとはやはりアンサンブルか、学習率の制御も一つのアプローチかと思います。
ただこの辺りは自分自身も勉強不足なところも多いので、他のコンペに参加しつつ知見を深めていければと考えています。