今更ながらDropoutを検証してみる

TL;DR;

  • dropoutで適切なパラメータを振ると学習が安定するよ。

  • val_accの最大値は更新しませんでした。

Dropoutの効果は本当にあるのか

今までいろんなモデル(つっても画像の異常判定とか、U-NET、オートエンコーダーくらいだけど)を組んできたが、

Dropoutがあんまり機能した記憶がないので、ホンマかいな、というのを試してみた。

みなさん大好きmnistで。

(というより誰でもいつでも使えるものになるとmnistに)

検証モデル

まずは下記投稿で最高結果を出したモデルで。

kazuhitogo.hateblo.jp

f:id:kazuhitogo:20190905103651p:plain
prelu_model

from keras import datasets
import numpy as np
((train_x,train_y),(test_x,test_y)) = datasets.mnist.load_data()
train_x = (train_x/255).astype("float32").reshape(-1,28,28,1)
test_x  = (test_x /255).astype("float32").reshape(-1,28,28,1)
train_y = np.eye(10)[train_y]
test_y = np.eye(10)[test_y]

from tensorflow.keras.layers import *
from tensorflow.keras.models import *
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.callbacks import ModelCheckpoint
from tensorflow.keras.losses import categorical_crossentropy

prelu_model = Sequential()
prelu_model.add(Conv2D(filters=32,kernel_size=(3,3),input_shape=(28,28,1),padding="same"))
prelu_model.add(PReLU())
prelu_model.add(BatchNormalization())
prelu_model.add(Conv2D(filters=32,kernel_size=(3,3),padding="same"))
prelu_model.add(MaxPool2D(pool_size=(2,2),padding="same")) # 28,28 -> 14,14
prelu_model.add(PReLU())
prelu_model.add(BatchNormalization())
prelu_model.add(Conv2D(filters=64,kernel_size=(3,3),padding="same"))
prelu_model.add(PReLU())
prelu_model.add(BatchNormalization())
prelu_model.add(Conv2D(filters=64,kernel_size=(3,3),padding="same"))
prelu_model.add(PReLU())
prelu_model.add(MaxPool2D(pool_size=(2,2),padding="same")) # 14,14 -> 7,7
prelu_model.add(BatchNormalization())
prelu_model.add(Conv2D(filters=64,kernel_size=(3,3),padding="same"))
prelu_model.add(PReLU())
prelu_model.add(BatchNormalization())
prelu_model.add(Conv2D(filters=64,kernel_size=(3,3),padding="same"))
prelu_model.add(PReLU())
prelu_model.add(MaxPool2D(pool_size=(2,2),padding="same")) # 7,7 -> 4,4
prelu_model.add(BatchNormalization())
prelu_model.add(Conv2D(filters=64,kernel_size=(3,3),padding="same"))
prelu_model.add(PReLU())
prelu_model.add(BatchNormalization())
prelu_model.add(Conv2D(filters=64,kernel_size=(3,3),padding="same"))
prelu_model.add(PReLU())
prelu_model.add(MaxPool2D(pool_size=(2,2),padding="same")) # 4,4 -> 2,2
prelu_model.add(BatchNormalization())
prelu_model.add(Conv2D(filters=128,kernel_size=(2,2),padding="same"))
prelu_model.add(PReLU())
prelu_model.add(BatchNormalization())
prelu_model.add(Conv2D(filters=128,kernel_size=(2,2),padding="same"))
prelu_model.add(PReLU())
prelu_model.add(MaxPool2D(pool_size=(2,2),padding="same")) # 2,2 -> 1,1
prelu_model.add(BatchNormalization())
prelu_model.add(Flatten())
prelu_model.add(Dense(10,activation="softmax"))
prelu_model.summary()
prelu_model.compile(optimizer=Adam(lr=0.0001),metrics=['accuracy'],loss="categorical_crossentropy")
prelu_model_history = prelu_model.fit(train_x,train_y,batch_size=16,epochs=64,validation_data=(test_x,test_y))

このモデルに対して各アクティベーション層後にDropoutを設定する。

この記事によると、

deepage.net

それではDropoutを適用していこう。 まずは、Hinton氏の提案通り入力層は0.2、隠れ層は0.5にしておく。 TFLearnのdropout関数は1-dropout率を指定する。

ヒントン氏が入力層0.2,隠れ層は0.5と言っているので、それに習い、そのように設定。

ただ、なんとなく出力層直前は0.2にしてみました。

ヒントン氏に敗れた人はなんというか知りませんが…

togetter.com

f:id:kazuhitogo:20190905104359p:plain
prelu_model_drop05

prelu_model_drop05 = Sequential()
prelu_model_drop05.add(Conv2D(filters=32,kernel_size=(3,3),input_shape=(28,28,1),padding="same"))
prelu_model_drop05.add(PReLU())
prelu_model_drop05.add(Dropout(0.2))
prelu_model_drop05.add(BatchNormalization())
prelu_model_drop05.add(Conv2D(filters=32,kernel_size=(3,3),padding="same"))
prelu_model_drop05.add(Dropout(0.5))
prelu_model_drop05.add(MaxPool2D(pool_size=(2,2),padding="same")) # 28,28 -> 14,14
prelu_model_drop05.add(PReLU())
prelu_model_drop05.add(Dropout(0.5))
prelu_model_drop05.add(BatchNormalization())
prelu_model_drop05.add(Conv2D(filters=64,kernel_size=(3,3),padding="same"))
prelu_model_drop05.add(PReLU())
prelu_model_drop05.add(Dropout(0.5))
prelu_model_drop05.add(BatchNormalization())
prelu_model_drop05.add(Conv2D(filters=64,kernel_size=(3,3),padding="same"))
prelu_model_drop05.add(PReLU())
prelu_model_drop05.add(Dropout(0.5))
prelu_model_drop05.add(MaxPool2D(pool_size=(2,2),padding="same")) # 14,14 -> 7,7
prelu_model_drop05.add(BatchNormalization())
prelu_model_drop05.add(Conv2D(filters=64,kernel_size=(3,3),padding="same"))
prelu_model_drop05.add(PReLU())
prelu_model_drop05.add(Dropout(0.5))
prelu_model_drop05.add(BatchNormalization())
prelu_model_drop05.add(Conv2D(filters=64,kernel_size=(3,3),padding="same"))
prelu_model_drop05.add(PReLU())
prelu_model_drop05.add(Dropout(0.5))
prelu_model_drop05.add(MaxPool2D(pool_size=(2,2),padding="same")) # 7,7 -> 4,4
prelu_model_drop05.add(BatchNormalization())
prelu_model_drop05.add(Conv2D(filters=64,kernel_size=(3,3),padding="same"))
prelu_model_drop05.add(PReLU())
prelu_model_drop05.add(Dropout(0.5))
prelu_model_drop05.add(BatchNormalization())
prelu_model_drop05.add(Conv2D(filters=64,kernel_size=(3,3),padding="same"))
prelu_model_drop05.add(PReLU())
prelu_model_drop05.add(Dropout(0.5))
prelu_model_drop05.add(MaxPool2D(pool_size=(2,2),padding="same")) # 4,4 -> 2,2
prelu_model_drop05.add(BatchNormalization())
prelu_model_drop05.add(Conv2D(filters=128,kernel_size=(2,2),padding="same"))
prelu_model_drop05.add(PReLU())
prelu_model_drop05.add(Dropout(0.5))
prelu_model_drop05.add(BatchNormalization())
prelu_model_drop05.add(Conv2D(filters=128,kernel_size=(2,2),padding="same"))
prelu_model_drop05.add(PReLU())
prelu_model_drop05.add(Dropout(0.2))
prelu_model_drop05.add(MaxPool2D(pool_size=(2,2),padding="same")) # 2,2 -> 1,1
prelu_model_drop05.add(BatchNormalization())
prelu_model_drop05.add(Flatten())
prelu_model_drop05.add(Dense(10,activation="softmax"))
prelu_model_drop05.summary()

結果

Dropoutはポンコツでした。

f:id:kazuhitogo:20190905104517p:plain
val_acc

Dropout無しが99.47%のval_accだったのに対し(あれ、99.5%超えてない)

Dropout有りが97.47%が限界でした。

検証2

たぶんDropoutの設計がイケてなかったのではないでしょうか…。

Dropoutレイヤが多すぎたか、パラメータの0.2,0.5が行けなかった気がします。

ヒントン氏を信じてパラメータはそのままに、 レイヤを減らしてみましょう。

で、作成したモデルがこちら。 (dropoutしてないヤツは変わらず)

prelu_model_drop05 = Sequential()
prelu_model_drop05.add(Conv2D(filters=32,kernel_size=(3,3),input_shape=(28,28,1),padding="same"))
prelu_model_drop05.add(PReLU())
prelu_model_drop05.add(Dropout(0.2))
prelu_model_drop05.add(BatchNormalization())
prelu_model_drop05.add(Conv2D(filters=32,kernel_size=(3,3),padding="same"))
prelu_model_drop05.add(Dropout(0.5))
prelu_model_drop05.add(MaxPool2D(pool_size=(2,2),padding="same")) # 28,28 -> 14,14
prelu_model_drop05.add(PReLU())
prelu_model_drop05.add(BatchNormalization())<figure class="figure-image figure-image-fotolife" title="dropout減らし">[f:id:kazuhitogo:20190906102602p:plain]<figcaption>dropout減らし</figcaption></figure>
prelu_model_drop05.add(Conv2D(filters=64,kernel_size=(3,3),padding="same"))
prelu_model_drop05.add(PReLU())
prelu_model_drop05.add(BatchNormalization())
prelu_model_drop05.add(Conv2D(filters=64,kernel_size=(3,3),padding="same"))
prelu_model_drop05.add(PReLU())
prelu_model_drop05.add(Dropout(0.5))
prelu_model_drop05.add(MaxPool2D(pool_size=(2,2),padding="same")) # 14,14 -> 7,7
prelu_model_drop05.add(BatchNormalization())
prelu_model_drop05.add(Conv2D(filters=64,kernel_size=(3,3),padding="same"))
prelu_model_drop05.add(PReLU())
prelu_model_drop05.add(BatchNormalization())
prelu_model_drop05.add(Conv2D(filters=64,kernel_size=(3,3),padding="same"))
prelu_model_drop05.add(PReLU())
prelu_model_drop05.add(Dropout(0.5))
prelu_model_drop05.add(MaxPool2D(pool_size=(2,2),padding="same")) # 7,7 -> 4,4
prelu_model_drop05.add(BatchNormalization())
prelu_model_drop05.add(Conv2D(filters=64,kernel_size=(3,3),padding="same"))
prelu_model_drop05.add(PReLU())
prelu_model_drop05.add(BatchNormalization())
prelu_model_drop05.add(Conv2D(filters=64,kernel_size=(3,3),padding="same"))
prelu_model_drop05.add(PReLU())
prelu_model_drop05.add(Dropout(0.5))
prelu_model_drop05.add(MaxPool2D(pool_size=(2,2),padding="same")) # 4,4 -> 2,2
prelu_model_drop05.add(BatchNormalization())
prelu_model_drop05.add(PReLU())
prelu_model_drop05.add(BatchNormalization())
prelu_model_drop05.add(Conv2D(filters=128,kernel_size=(2,2),padding="same"))
prelu_model_drop05.add(PReLU())
prelu_model_drop05.add(Dropout(0.2))
prelu_model_drop05.add(MaxPool2D(pool_size=(2,2),padding="same")) # 2,2 -> 1,1
prelu_model_drop05.add(BatchNormalization())
prelu_model_drop05.add(Flatten())
prelu_model_drop05.add(Dense(10,activation="softmax"))
prelu_model_drop05.summary()

結果2

f:id:kazuhitogo:20190906102602p:plain

うーん、いまいちすぎる。 が、dropout無し版がepoch 61にしてval_acc99.6%超えましたね。笑

検証3

ヒントン教授を疑うことにして、 dropoutを全て(0.2)で検証します。

prelu_model_drop02 = Sequential()
prelu_model_drop02.add(Conv2D(filters=32,kernel_size=(3,3),input_shape=(28,28,1),padding="same"))
prelu_model_drop02.add(PReLU())
prelu_model_drop02.add(Dropout(0.2))
prelu_model_drop02.add(BatchNormalization())
prelu_model_drop02.add(Conv2D(filters=32,kernel_size=(3,3),padding="same"))
prelu_model_drop02.add(Dropout(0.2))
prelu_model_drop02.add(MaxPool2D(pool_size=(2,2),padding="same")) # 28,28 -> 14,14
prelu_model_drop02.add(PReLU())
prelu_model_drop02.add(BatchNormalization())
prelu_model_drop02.add(Conv2D(filters=64,kernel_size=(3,3),padding="same"))
prelu_model_drop02.add(PReLU())
prelu_model_drop02.add(BatchNormalization())
prelu_model_drop02.add(Conv2D(filters=64,kernel_size=(3,3),padding="same"))
prelu_model_drop02.add(PReLU())
prelu_model_drop02.add(Dropout(0.2))
prelu_model_drop02.add(MaxPool2D(pool_size=(2,2),padding="same")) # 14,14 -> 7,7
prelu_model_drop02.add(BatchNormalization())
prelu_model_drop02.add(Conv2D(filters=64,kernel_size=(3,3),padding="same"))
prelu_model_drop02.add(PReLU())
prelu_model_drop02.add(BatchNormalization())
prelu_model_drop02.add(Conv2D(filters=64,kernel_size=(3,3),padding="same"))
prelu_model_drop02.add(PReLU())
prelu_model_drop02.add(Dropout(0.2))
prelu_model_drop02.add(MaxPool2D(pool_size=(2,2),padding="same")) # 7,7 -> 4,4
prelu_model_drop02.add(BatchNormalization())
prelu_model_drop02.add(Conv2D(filters=64,kernel_size=(3,3),padding="same"))
prelu_model_drop02.add(PReLU())
prelu_model_drop02.add(BatchNormalization())
prelu_model_drop02.add(Conv2D(filters=64,kernel_size=(3,3),padding="same"))
prelu_model_drop02.add(PReLU())
prelu_model_drop02.add(Dropout(0.2))
prelu_model_drop02.add(MaxPool2D(pool_size=(2,2),padding="same")) # 4,4 -> 2,2
prelu_model_drop02.add(BatchNormalization())
prelu_model_drop02.add(PReLU())
prelu_model_drop02.add(BatchNormalization())
prelu_model_drop02.add(Conv2D(filters=128,kernel_size=(2,2),padding="same"))
prelu_model_drop02.add(PReLU())
prelu_model_drop02.add(Dropout(0.2))
prelu_model_drop02.add(MaxPool2D(pool_size=(2,2),padding="same")) # 2,2 -> 1,1
prelu_model_drop02.add(BatchNormalization())
prelu_model_drop02.add(Flatten())
prelu_model_drop02.add(Dense(10,activation="softmax"))
prelu_model_drop02.summary()

prelu_model_drop02.compile(optimizer=Adam(lr=0.0001),metrics=['accuracy'],loss="categorical_crossentropy")
prelu_model_drop02_history = prelu_model_drop05.fit(train_x,train_y,batch_size=16,epochs=64,validation_data=(test_x,test_y))

はてさて。

結果3

かなり改善されました。

f:id:kazuhitogo:20190906135018p:plain
drop0.2

最高のval_accこそ負けるものの(dropout0.2有り99.58%,無し99.6%) 学習がかなり安定しております。

もう少しまんべんなくdropoutしてパラメータが0.1とかだとさらなる改善が期待できそうです。

あとは学習率のスケジュールとかをすると更によいかもしれません。

今回はふざけた広告を笑