MNISTでハイパーパラメータをいじってloss/accuracyグラフを見てみる

1 Star2 Stars3 Stars4 Stars5 Stars (まだ評価されていません)
Loading...

はじめに

ハイパーパラメータの調整は至難の業で、最終的にはランダムサーチしろよという結論になるのですが、グラフから「あっ、このパラメータはちょっとやばいな」とか「データの分配がおかしいな」というのがどの程度わかるか気になったので、(極端な)パラメータ設定をして損失関数と精度のグラフを出してみました。

残念ながら、「どのパラメータがおかしいか」を1対1で見つけることはできなかったのですが、ハイパーパラメータを決める一番初期の段階で役に立てばと、自分用にまとめることにしました。

isogu_patoka-.png

動作環境

MacOS Sierra, Keras 2, Tensorflow v1.2.1

注意

  • あくまで自分用のメモです。
  • MNISTが元ネタ

いろんな場合

パラメータ設定が正しいときのソースコード

import numpy as np
import matplotlib.pyplot as plt
import os
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation
from keras.optimizers import Adam
from keras.utils import np_utils, to_categorical
# ----------------------------------------------
# Data
# ----------------------------------------------
(X_train, Y_train), (X_test, Y_test) = mnist.load_data()
# convert integer RGB values (0-255) to float values (0-1)
X_train = X_train.astype('float32') / 255
X_test = X_test.astype('float32') / 255
# reshape the input data
X_train = X_train.reshape(60000, 28*28)
X_test = X_test.reshape(10000, 28*28)
# convert class labels to one-hot encodings
Y_train = to_categorical(Y_train, 10)
Y_test = to_categorical(Y_test, 10)
# split off last 5000 training samples for validation
X_train, X_valid = np.split(X_train, [55000])
Y_train, Y_valid = np.split(Y_train, [55000])
# ----------------------------------------------
# Model and training
# ----------------------------------------------
model = Sequential([
Dense(512, input_shape=(784,)),
Activation('relu'),
Dropout(0.5),
Dense(10),
Activation('softmax')])
model.compile(
loss='categorical_crossentropy',
optimizer=Adam(lr=1e-3),
metrics=['accuracy'])
print(model.summary())
fit = model.fit(
X_train, Y_train,
batch_size=128,
epochs=50,
verbose=2,
validation_data=(X_valid, Y_valid),
callbacks=[])
score = model.evaluate(X_test, Y_test,
verbose=0
)
print('Test score:', score[0])
print('Test accuracy:', score[1])
# ----------------------------------------------
# Some plots
# ----------------------------------------------
fig, (axL, axR) = plt.subplots(ncols=2, figsize=(10,4))
# loss
def plot_history_loss(fit):
# Plot the loss in the history
axL.plot(fit.history['loss'],label="loss for training")
axL.plot(fit.history['val_loss'],label="loss for validation")
axL.set_title('model loss')
axL.set_xlabel('epoch')
axL.set_ylabel('loss')
axL.legend(loc='upper right')
# acc
def plot_history_acc(fit):
# Plot the loss in the history
axR.plot(fit.history['acc'],label="loss for training")
axR.plot(fit.history['val_acc'],label="loss for validation")
axR.set_title('model accuracy')
axR.set_xlabel('epoch')
axR.set_ylabel('accuracy')
axR.legend(loc='upper right')
plot_history_loss(fit)
plot_history_acc(fit)
fig.savefig('./mnist-tutorial.png')
plt.close()

mnist-tutorial.png

Test score: 0.0724496891232
Test accuracy: 0.9855

コメント
– ちょっと過学習起きしてるけど、この設定だと一番よい精度が得られる。(CNNを使えばもっと高い精度が得られます.)

以降はソースコードは差文のみ書きます。

Dropout(0.5)を省いたら…

model = Sequential([
Dense(512, input_shape=(784,)),
Activation('relu'),
#Dropout(0.5), 
Dense(10),
Activation('softmax')])

Test score: 0.0737036298519
Test accuracy: 0.984

mnist-tutorial_2.png

コメント
– 典型的な過学習を起こしているグラフ。過学習を防ぐには、データ量を増やすとか、Dropoutを入れるとか、layer構造を深く広くしないといけない。(Kerasの場合はEarlystoppingが有効)

学習率をめちゃくちゃ小さくしてみたら…

model.compile(
loss='categorical_crossentropy',
optimizer=Adam(lr=1e-5), # here
metrics=['accuracy'])
print(model.summary())

mnist-tutorial_4_1.png

Test score: 0.180903082219
Test accuracy: 0.9493

コメント
– 学習の進行が遅いため、データ量の少ないvalidation dataのほうが先に学習が進んでしまっている. このまま学習続ければ逆転していい精度が得られるかも。

学習率を小さくすると、時間だけかかります。最小の計算時間を追求するのも大事!

学習率を大きくしてみると…

model.compile(
loss='categorical_crossentropy',
optimizer=Adam(lr=1e-2), # here
metrics=['accuracy'])
print(model.summary())

mnist-tutorial_4_2.png

Test score: 0.201800413718
Test accuracy: 0.975

コメント
– 最適な値の周りで振動してしまっている。その分損失関数が増加していっている。
– 個人的な経験として、val_loss or val_accの振動(fluctuation)が大きいときは、たいてい学習率が大きすぎることが原因だと思う。この場合もまさにそう。(decayとか使うともっとよい精度で得られたりするんだろうけど) SDGの問題点もそういう(最適な値の周りでの振動)ものだったので、この場合は学習率を下げるのが最初の手。

validation dataの量をめちゃくちゃ少なくすると…

X_train, X_valid = np.split(X_train, [59000]) # here
Y_train, Y_valid = np.split(Y_train, [59000]) # here

mnist-tutorial_6_1.png

Test score: 0.0803674175286
Test accuracy: 0.9845

  • 損失関数が減らない.
  • 確率もval_accの変化がない.

validation dataの量をめちゃくちゃ多くすると…

X_train, X_valid = np.split(X_train, [10000]) # here
Y_train, Y_valid = np.split(Y_train, [10000]) # here

mnist-tutorial_6_2.png

Test score: 0.150466875415
Test accuracy: 0.9628

  • 損失関数が減らない.
  • 確率もval_accの変化がない.

層の数が多すぎると…

model = Sequential([
Dense(512, input_shape=(784,)),
Activation('relu'),
Dense(512),
Activation('relu'),
Dense(512),
Activation('relu'),
Dense(512),
Activation('relu'),
Dense(512),
Activation('relu'),
Dropout(0.5),
Dense(10),
Activation('softmax')])

mnist-tutorial_7.png

Test score: 0.326702955916
Test accuracy: 0.9526

  • 損失関数が増加していく.

まとめ

こうやってみると、どれも決定的な違いというのは見受けられなかった。つまり、グラフから特定のパラメータが適切でないのを知るのはなかなか難しいことがわかったが、以下のように整理できた。

  • 損失関数が増加に転じる -> 過学習
  • validation accuracyがtraining accuracyよりも精度が高い -> validation dataが少なくて学習率が適切なとき, もしくはvalidation dataが適切で学習率が小さいとき(or 大きいとき) に起こりうる。
  • グラフが激しく振動するとき -> 学習率が高すぎるか、データ量(validation data / training data)が少ない.

もう少し上手いやり方や、ご指摘等あれば頂きたいです。


1 Star2 Stars3 Stars4 Stars5 Stars (まだ評価されていません)
Loading...
      この投稿は審査処理中  | 元のサイトへ