SEワンタンの独学備忘録

IT関連の独学した内容や資格試験に対する取り組みの備忘録

【機械学習】入門21 CNNに対するプーリングとドロップアウトの実装


前回までは作成したニューラルネットワークに対して、正答率を向上させるためにフィルターと畳み込みの概念を追加しました。
今回はさらに正答率を向上させるための工夫を追加していきたいと思います。

↓↓前回
www.wantanblog.com


プーリング

プーリングの概念

前回のフィルターの概念を追加することにより、対象を二次元の画像としてとらえることができるようになりました。
しかし、手書き文字を想定すると以下のように文字を書く場所がずれるということは容易に想定できます。

f:id:wantanBlog:20201108014931p:plain

人間の目で見れば明らかに1を表す画像だと認識できますが、プログラムでは全く別のものとして認識される可能性があります。
この事象の対策に用いられるのがプーリングと言われる手法です。

プーリングの一般的な方法としては最大プーリング平均プーリングなどがあります。
最大プーリングではプーリング領域の中の最大値を出力値とし、平均プーリングでは領域の平均値をとるそうです。

以下の例では粒度が粗いため、1マスずれると大きなずれとなってしまいますが、プーリング後の出力イメージではおおむね同じようなイメージになっています。

f:id:wantanBlog:20201108020959p:plain

領域は「2×2」や「3×3」など任意のサイズが設定でき、スライドサイズは領域サイズに応じた値にするのが一般的なようです。

プーリングの実装(Python)

以前作成したkerasのニューラルネットワークにプーリングの追加してみます。

・実装例

# CNNモデルの実装(プーリングの追加)
npy.random.seed
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
from keras.optimizers import Adam
import time

model = Sequential()
model.add(Conv2D(8,(3,3),padding='same', input_shape=(28,28,1),activation='relu'))
# プーリング層を追加
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Flatten())
model.add(Dense(10, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer=Adam(),metrics=['accuracy'])

startTime = time.time()
history = model.fit(x_train,y_train,batch_size=1000, epochs=20,verbose=1,validation_data=(x_test,y_test))
score = model.evaluate(x_test,y_test,verbose=0)
print('Test loss:', score[0])
print('Test accuracy:', score[1])
print("Computation time:{0:3f}sec".format(time.time() - startTime))

プーリング層の追加は以下の行により実装します。
ここでは「2×2」の領域を設定してみました。

# プーリング層を追加
model.add(MaxPooling2D(pool_size=(2,2)))

・出力結果(抜粋)

Epoch 18/20
60/60 [==============================] - 4s 66ms/step - loss: 0.0968 - accuracy: 0.9725 - val_loss: 0.0970 - val_accuracy: 0.9711
Epoch 19/20
60/60 [==============================] - 4s 64ms/step - loss: 0.0939 - accuracy: 0.9732 - val_loss: 0.0942 - val_accuracy: 0.9715
Epoch 20/20
60/60 [==============================] - 4s 69ms/step - loss: 0.0912 - accuracy: 0.9737 - val_loss: 0.0933 - val_accuracy: 0.9711
Test loss: 0.09325191378593445
Test accuracy: 0.9710999727249146
Computation time:76.905586sec
結果比較

・プーリングなし

Test loss: 0.07226067781448364
Test accuracy: 0.9781000018119812

・プーリングあり

Test loss: 0.09325191378593445
Test accuracy: 0.9710999727249146

正答率は落ちてしまったようです。
この辺の設計はかなり難しい。プーリングの特性から考えてもどんな場合でも確実に正答率が上がるものではないように感じる。

学習対象などを変えずとも畳み込み層の数はニューロンの数などを変えると結果は微妙に変わってきます。
プーリングの導入によって正答率が上がるようなパターンをチューニングしてみようとしましたがなかなか難しかったです。

・プーリングによって正答率向上が見込めるパターン

# CNNモデルの実装(プーリングの追加)
npy.random.seed
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
from keras.optimizers import Adam
import time

model = Sequential()
model.add(Conv2D(16,(3,3), input_shape=(28,28,1),activation='relu'))
model.add(Conv2D(32,(3,3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Conv2D(64,(3,3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dense(num_classes, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer=Adam(),metrics=['accuracy'])

startTime = time.time()
history = model.fit(x_train,y_train,batch_size=1000, epochs=20,verbose=1,validation_data=(x_test,y_test))
score = model.evaluate(x_test,y_test,verbose=0)
print('Test loss:', score[0])
print('Test accuracy:', score[1])
print("Computation time:{0:3f}sec".format(time.time() - startTime))

・プーリングなし

Test loss: 0.05879649519920349
Test accuracy: 0.9879999756813049

・プーリングあり

Test loss: 0.03173435479402542
Test accuracy: 0.991100013256073

ドロップアウト

ドロップアウトの概念

ニューラルネットワークにおけるドロップアウトとは中間層などのニューロンを一定確率でランダムに選択し非活性化することを言います。
非活性化とは、学習時に選択したニューロンは存在しないものとして更新をおこなっていきます。

一般的にはディープラーニングによる過学習を防ぐなどの目的で導入されるようです。

f:id:wantanBlog:20201108185415p:plain

また、基本的な考え方としてドロップアウトを適用した場合、学習時にはいくつかのニューロンを間引いた状態になりますが、学習した結果から予測を行う際には全てのニューロンを使用します。
その際にそのまま実行すると、出力結果が大きくなってしまうため、ドロップアウトした層に対して補正(ニューロン選択に用いた確率p)を行うことによって辻褄を合わせるようです。

ドロップアウトの詳細な理論についてはかなり難解になるようです。

ドロップアウトの実装(Python)

プーリングの実装を行ったCNNに対してドロップアウトの実装を追加します。
Kerasを使用している場合には実装も簡単になります。

# CNNモデルの実装(ドロップアウトの追加)
npy.random.seed
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
from keras.optimizers import Adam
import time

model = Sequential()
model.add(Conv2D(16,(3,3),padding='same', input_shape=(28,28,1),activation='relu'))
model.add(Conv2D(32,(3,3),padding='same', activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Conv2D(64,(3,3),padding='same', activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(128, activation='softmax'))
model.add(Dropout(0.25))
model.add(Dense(num_classes, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer=Adam(),metrics=['accuracy'])

startTime = time.time()
history = model.fit(x_train,y_train,batch_size=1000, epochs=20,verbose=1,validation_data=(x_test,y_test))
score = model.evaluate(x_test,y_test,verbose=0)
print('Test loss:', score[0])
print('Test accuracy:', score[1])
print("Computation time:{0:3f}sec".format(time.time() - startTime))


ドロップアウトの追加は以下の行です。今回は25%を設定しています。

model.add(Dropout(0.25))

・出力結果

Test loss: 0.020667586475610733
Test accuracy: 0.9932000041007996
Computation time:433.481964sec

最終的な正答率は「0.993」まで向上させることができました。

また、最終的なCNNのイメージは以下のようになりました。

f:id:wantanBlog:20201108232113p:plain

正直この辺は最適なモデルを特定するためにまだまだ(私自身の)学習が必要ですが、次からは「教師なし学習」に進みたいと思います。

ーーーーーーーーーーーーー
・今回のソース
python_dev/MNIST_4.ipynb at master · wantanblog/python_dev · GitHub