前回までは作成したニューラルネットワークに対して、正答率を向上させるためにフィルターと畳み込みの概念を追加しました。
今回はさらに正答率を向上させるための工夫を追加していきたいと思います。
↓↓前回
www.wantanblog.com
プーリング
プーリングの概念
前回のフィルターの概念を追加することにより、対象を二次元の画像としてとらえることができるようになりました。
しかし、手書き文字を想定すると以下のように文字を書く場所がずれるということは容易に想定できます。
人間の目で見れば明らかに1を表す画像だと認識できますが、プログラムでは全く別のものとして認識される可能性があります。
この事象の対策に用いられるのがプーリングと言われる手法です。
プーリングの一般的な方法としては最大プーリングと平均プーリングなどがあります。
最大プーリングではプーリング領域の中の最大値を出力値とし、平均プーリングでは領域の平均値をとるそうです。
以下の例では粒度が粗いため、1マスずれると大きなずれとなってしまいますが、プーリング後の出力イメージではおおむね同じようなイメージになっています。
領域は「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
ドロップアウト
ドロップアウトの概念
ニューラルネットワークにおけるドロップアウトとは中間層などのニューロンを一定確率でランダムに選択し非活性化することを言います。
非活性化とは、学習時に選択したニューロンは存在しないものとして更新をおこなっていきます。
一般的にはディープラーニングによる過学習を防ぐなどの目的で導入されるようです。
また、基本的な考え方としてドロップアウトを適用した場合、学習時にはいくつかのニューロンを間引いた状態になりますが、学習した結果から予測を行う際には全てのニューロンを使用します。
その際にそのまま実行すると、出力結果が大きくなってしまうため、ドロップアウトした層に対して補正(ニューロン選択に用いた確率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のイメージは以下のようになりました。
正直この辺は最適なモデルを特定するためにまだまだ(私自身の)学習が必要ですが、次からは「教師なし学習」に進みたいと思います。
ーーーーーーーーーーーーー
・今回のソース
python_dev/MNIST_4.ipynb at master · wantanblog/python_dev · GitHub