Tensorflow・kerasで構築したCNNの性能改善を行ってみる（その３ BatchNormaliztionとDropoutの組み合わせ）

前回の第2弾はの続きです。
[Tensorflow・kerasで構築したCNNの性能改善を行ってみる（その２ BatchNormaliztion）](https://qiita.com/kazuboy/items/0b4038732275c6b30c81)

BatchNormalizationとDropoutの両方を組み合わせてどうなるか考えてみたいと思います。

## 前提/環境
前提となる環境とバージョンは下記となります。
・Anaconda3
・Python3.7.7
・pip 20.0
・TensorFlow 2.0.0

前回までで、Dropout、BatchNormalizationを層の処理に追加した場合に、効果等を書いてきました。
今回は組み合わせた場合にどうであるか見てみようと思っています。
いろいろ調べてみると、BatchNormalizationでもDropOutと同様にオーバーフィッティングの効果もあるようなので、Dropoutと併用はしなくてもよいなどいろいろな意見があるのですが、組み合わせがどうなるかは自分でも確認しておこうと思いました。


## BatchNomaliztionとDropoutの実装
CNNのベースのモデルに層を追加する方針とします。ベースは下記のモデルです。今までの記事でも利用した単純なものです。
![ベースとなるモデル.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/659138/198efd6b-d4ea-2f09-2dd7-3837ad7704bb.png)

今回は原則、BatchNormalizationを各畳み込み層の直後に追加して⇒プーリング層という流れにします。またDropoutは最後のDense層に追加する形です。追加後の形は下記のようになります。

![モデル.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/659138/7cf2b8ec-1960-d426-a0ae-5828da276986.png)

畳み込み層の後に

```python:code
model.add(BatchNormalization())
```
※BatchNormalizationを追加する際、事前にfrom tensorflow.keras.layers import BatchNormalization　を行ってください

またDense層の部分で

```python:code
model.add(Dropout(0.25))
```

とDropoutを追加します。実行してみます。今回も10エポックとしています。

結果
Train on 40000 samples, validate on 10000 samples
Epoch 1/10
40000/40000 [==============================] - 660s 17ms/sample - loss: 1.4209 - accuracy: 0.4954 - val_loss: 1.1341 - val_accuracy: 0.5987
Epoch 2/10
40000/40000 [==============================] - 65487s 2s/sample - loss: 0.9395 - accuracy: 0.6704 - val_loss: 0.8906 - val_accuracy: 0.6866
Epoch 3/10
40000/40000 [==============================] - 492s 12ms/sample - loss: 0.7699 - accuracy: 0.7314 - val_loss: 0.9478 - val_accuracy: 0.6864
Epoch 4/10
40000/40000 [==============================] - 453s 11ms/sample - loss: 0.6505 - accuracy: 0.7720 - val_loss: 0.7926 - val_accuracy: 0.7233
Epoch 5/10
40000/40000 [==============================] - 416s 10ms/sample - loss: 0.5533 - accuracy: 0.8055 - val_loss: 0.7207 - val_accuracy: 0.7540
Epoch 6/10
40000/40000 [==============================] - 406s 10ms/sample - loss: 0.4742 - accuracy: 0.8343 - val_loss: 0.7959 - val_accuracy: 0.7384
Epoch 7/10
40000/40000 [==============================] - 447s 11ms/sample - loss: 0.3875 - accuracy: 0.8642 - val_loss: 0.7406 - val_accuracy: 0.7725
Epoch 8/10
40000/40000 [==============================] - 507s 13ms/sample - loss: 0.3324 - accuracy: 0.8848 - val_loss: 0.7624 - val_accuracy: 0.7676
Epoch 9/10
40000/40000 [==============================] - 512s 13ms/sample - loss: 0.2745 - accuracy: 0.9039 - val_loss: 0.7895 - val_accuracy: 0.7792
Epoch 10/10
40000/40000 [==============================] - 517s 13ms/sample - loss: 0.2396 - accuracy: 0.9151 - val_loss: 0.8905 - val_accuracy: 0.7759
Test accuracy: 0.741

結果としてさほど効果があるのかわからないですが、Dropoutのみよりは確実にBatchNormalizationの効果がでていて、BatchNormalizationのみの場合との間に優位な点はそれほど見てとれていません。併用はむしろ逆効果という意見もあるのですが、この実験では見受けられませんでした。
これはDropout処理の行うタイミング（位置）とも関係していると思われますので次のモデルで試してみました。

![モデル２.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/659138/58bea29d-b6d8-b806-4cc8-c07d551c17b2.png)

このモデルでは畳み込み層の後にDropout処理を追加し、そのあとBatchNormalizationの処理を行っています。これにより実行した結果は下記です。

結果
Train on 40000 samples, validate on 10000 samples
Epoch 1/10
40000/40000 [==============================] - 575s 14ms/sample - loss: 1.5098 - accuracy: 0.4620 - val_loss: 1.5849 - val_accuracy: 0.4898
Epoch 2/10
40000/40000 [==============================] - 599s 15ms/sample - loss: 1.0474 - accuracy: 0.6301 - val_loss: 1.9410 - val_accuracy: 0.4599
Epoch 3/10
40000/40000 [==============================] - 597s 15ms/sample - loss: 0.8547 - accuracy: 0.7000 - val_loss: 1.1494 - val_accuracy: 0.6313
Epoch 4/10
40000/40000 [==============================] - 613s 15ms/sample - loss: 0.7501 - accuracy: 0.7395 - val_loss: 0.9340 - val_accuracy: 0.6919
Epoch 5/10
40000/40000 [==============================] - 601s 15ms/sample - loss: 0.6664 - accuracy: 0.7663 - val_loss: 1.0509 - val_accuracy: 0.6678
Epoch 6/10
40000/40000 [==============================] - 613s 15ms/sample - loss: 0.5987 - accuracy: 0.7907 - val_loss: 0.7472 - val_accuracy: 0.7413
Epoch 7/10
40000/40000 [==============================] - 646s 16ms/sample - loss: 0.5378 - accuracy: 0.8139 - val_loss: 0.7891 - val_accuracy: 0.7522
Epoch 8/10
40000/40000 [==============================] - 592s 15ms/sample - loss: 0.4822 - accuracy: 0.8320 - val_loss: 0.7769 - val_accuracy: 0.7605
Epoch 9/10
40000/40000 [==============================] - 453s 11ms/sample - loss: 0.4345 - accuracy: 0.8471 - val_loss: 0.9633 - val_accuracy: 0.7257
Epoch 10/10
40000/40000 [==============================] - 420s 10ms/sample - loss: 0.3927 - accuracy: 0.8612 - val_loss: 0.7979 - val_accuracy: 0.7644

この結果を見ると、loss、accuracyの結果が、BatchNormalizationのみや、併用した一つ目の実験用モデルに比べ、良くない結果となっています。エポック数などの少なさなどもあり判断は難しいですが傾向はみられたと思います。

BatchNormalizationの処理とDropout処理の流れ（処理を行う位置）の違いで差がでるか実験しました。
モデルとしては、下記となります。BatchNormalizationの処理の前にDropout処理を追加しています。

![モデル３.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/659138/f85e2417-95fe-9466-3567-068a0a281c2b.png)


結果

Train on 40000 samples, validate on 10000 samples
Epoch 1/10
40000/40000 [==============================] - 419s 10ms/sample - loss: 1.5248 - accuracy: 0.4563 - val_loss: 1.4546 - val_accuracy: 0.5006
Epoch 2/10
40000/40000 [==============================] - 421s 11ms/sample - loss: 1.0825 - accuracy: 0.6180 - val_loss: 1.4129 - val_accuracy: 0.5488
Epoch 3/10
40000/40000 [==============================] - 419s 10ms/sample - loss: 0.8786 - accuracy: 0.6889 - val_loss: 1.3392 - val_accuracy: 0.5767
Epoch 4/10
40000/40000 [==============================] - 420s 10ms/sample - loss: 0.7557 - accuracy: 0.7362 - val_loss: 1.2353 - val_accuracy: 0.6279
Epoch 5/10
40000/40000 [==============================] - 420s 10ms/sample - loss: 0.6680 - accuracy: 0.7671 - val_loss: 0.9067 - val_accuracy: 0.7037
Epoch 6/10
40000/40000 [==============================] - 417s 10ms/sample - loss: 0.6002 - accuracy: 0.7887 - val_loss: 0.9423 - val_accuracy: 0.7254
Epoch 7/10
40000/40000 [==============================] - 428s 11ms/sample - loss: 0.5413 - accuracy: 0.8122 - val_loss: 1.0447 - val_accuracy: 0.6869
Epoch 8/10
40000/40000 [==============================] - 422s 11ms/sample - loss: 0.4867 - accuracy: 0.8272 - val_loss: 0.8378 - val_accuracy: 0.7373
Epoch 9/10
40000/40000 [==============================] - 417s 10ms/sample - loss: 0.4418 - accuracy: 0.8462 - val_loss: 0.9158 - val_accuracy: 0.7297
Epoch 10/10
40000/40000 [==============================] - 420s 10ms/sample - loss: 0.3995 - accuracy: 0.8606 - val_loss: 0.7728 - val_accuracy: 0.7604



結果からいうとDropoutを組み込むならばBatchNormalizationの処理の後に入れるということのほうが精度は高くなっています。
モデルのどの段階で各処理を行うのかは重要なポイントだと思います。