多層パーセプトロン(MLP)で、MNISTの手書き数字を認識する。
最も単純なニューラルネットワークを使ったケースは、
で実装している。
MLPは、このケースより、中間層の数を増やしただけだ。2層フィードフォワードニューラルネットワークとは、MLPの2層の場合だと言える。
さて、これから実装するのは3層のMLPだ。つまり、中間層が2層と出力層が1層の構成となる。高い正解率を目指すため、ユニット数も多くする。ただし、過学習を防ぐために、何らかの正則化をする必要がある。
今回使う正則化テクニックは、ドロップアウトと呼ばれるものだ。まず、ドロップアウト率、すなわち、ユニット(ニューロン)が一時的に消去される確率pを定める。そして、パラメータの更新ごとに、各ユニットを確率pで一時的に消去する。
このようにすると、トレーニング時に使われるユニット数は、平均してキープ率(1 - p)倍になる。テスト時には全てのユニットを使うから、出力が 1 / (1 - p) 倍になってしまう。その分の調整として、出力に1 - pを掛ける。
調整の仕方は1つではない。個々の入力の重みにキープ率を掛けてもいいし、トレーニング時から出力をキープ率で割っておいてもいい。意味的に異なるが、どれもうまく機能するらしい。ただ、Kerasで実装するなら、自分で調整する必要はない。
なお、ドロップアウト率は0.5に設定されることが多い。
ドロップアウトを使うと、近隣のユニットや、ごく少数のユニットに依存したネットワークが形成されにくい。その結果、高い汎化能力が得られる。
また、ドロップアウトはアンサンブル学習と解釈することもできる。ドロップアウト可能なユニット数をNとすると、ネットワークは全部で2N通りある。N=100としても、2100=(210)10=102410≃(103)10=1030だから、同じネットワークが2回以上使われる確率は、ほとんどゼロだ。パラメータ更新が1万回だとすると、1万種類のニューラルネットワークが訓練される。最終的に得られるニューラルネットワークは、この1万種類のニューラルネットワークを平均化するアンサンブルということになる。
ドロップアウトに加えてearly stoppingも併用したいが、うまくいかない。ドロップアウトを使うと、一時的にニューラルネットワークの性能が下がることがあり、その時点で学習が打ち切られてしまうのかもしれない。ドロップアウトを使うなら、エポック数をある程度多くする必要がある。
以下に、結果を記載しておく。ドロップアウトは、2つの中間層にのみ適用している(上図は2層の場合で、入力層にもドロップアウトを適用していることに注意)。
Multi-Layer Perceptron (MLP)¶
import os
from time import time
from datetime import datetime, timedelta, timezone
import math
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Dropout
from keras.utils import np_utils
from keras.callbacks import EarlyStopping, TensorBoard
(X_train, y_train), (X_test, y_test) = mnist.load_data()
img_rows, img_cols = 28, 28
input_dim = img_rows * img_cols # = 784
X_train = X_train.reshape(-1, input_dim).astype('float32') / 255
X_test = X_test.reshape(-1, input_dim).astype('float32') / 255
X_train.shape
output_dim = 10
Y_train = np_utils.to_categorical(y_train.astype('int32'), output_dim)
Y_test = np_utils.to_categorical(y_test.astype('int32'), output_dim)
Y_train[0]
UNITS_1 = 256
UNITS_2 = 512
ACTIVATION_1 = 'relu'
ACTIVATION_2 = 'relu'
DROPOUT_RATE_1 = 0.5
DROPOUT_RATE_2 = 0.5
OPTIMIZER = 'adam'
BATCH_SIZE = 128
EPOCHS = 50
VALIDATION_SPLIT = 0.1
model = Sequential()
model.add(Dense(UNITS_1, activation=ACTIVATION_1, input_dim=input_dim))
model.add(Dropout(DROPOUT_RATE_1))
model.add(Dense(UNITS_2, activation=ACTIVATION_2))
model.add(Dropout(DROPOUT_RATE_2))
model.add(Dense(output_dim, activation='softmax'))
model.summary()
model.compile(loss='categorical_crossentropy',
optimizer=OPTIMIZER,
metrics=['accuracy'])
VERBOSE = 0
def make_tensorboard(log_dir_base):
jst = timezone(timedelta(hours=+9), 'JST')
jst_now = datetime.now(jst)
log_dir = log_dir_base + '_' + jst_now.strftime('%Y%m%d%H%M%S')
os.makedirs(log_dir, exist_ok=True)
tensorboard = TensorBoard(log_dir=log_dir)
return tensorboard
CALLBACKS = [
# EarlyStopping(patience=0, verbose=1),
make_tensorboard(log_dir_base='log/mlp_keras_mnist')
]
start_time = time()
history = model.fit(X_train,
Y_train,
batch_size=BATCH_SIZE,
epochs=EPOCHS,
verbose=VERBOSE,
validation_split=VALIDATION_SPLIT,
callbacks=CALLBACKS)
print('Executed in {0:.3f}s'.format(time() - start_time))
plt.figure(1, figsize=(10, 4))
plt.subplots_adjust(wspace=0.5)
plt.subplot(1, 2, 1)
plt.plot(history.history['loss'], label='train', color='black')
plt.plot(history.history['val_loss'], label='validation', color='dodgerblue')
plt.legend()
# plt.ylim(0, 10)
plt.grid()
plt.title('Loss')
plt.xlabel('epoch')
plt.ylabel('loss')
plt.subplot(1, 2, 2)
plt.plot(history.history['acc'], label='train', color='black')
plt.plot(history.history['val_acc'], label='validation', color='dodgerblue')
plt.legend(loc='lower right')
# plt.ylim(0, 1)
plt.grid()
plt.title('Accuracy')
plt.xlabel('epoch')
plt.ylabel('acc')
plt.show()
score = model.evaluate(X_test, Y_test, verbose=1)
print('Test loss: ', score[0])
print('Test accuracy: ', score[1])
TEST_COUNT = 50
Y_predict = model.predict(X_test)
plt.figure(1, figsize=(5, 14))
plt.subplots_adjust(hspace=0.4)
for i in range(TEST_COUNT):
plt.subplot(TEST_COUNT / 5, 5, i + 1)
X = X_test[i, :].reshape(img_rows, img_cols)
Y = Y_predict[i, :]
prediction = np.argmax(Y)
y = np.argmax(Y_test[i, :])
if prediction == y:
plt.title(prediction, pad=10)
else:
plt.title('{}({})'.format(prediction, y), pad=10,
backgroundcolor='red')
plt.axis('off')
plt.imshow(X, cmap='gray')
plt.show()
ソースコード