BatchNormalizationはDropoutと 併用すべきか?Keras×MNISTで4パターン比較

投稿日:2026年3月1日日曜日 最終更新日:

BatchNormalization Dropout Google Colab Keras MNIST 過学習

X f B! P L
BatchNormalizationはDropoutと 併用すべきか?Keras×MNISTで4パターン比較 アイキャッチ画像

BatchNormalizationとDropout、どちらも過学習対策としてよく使われる手法です。

しかし「両方使っていいのか?」「どっちが効果的なのか?」は意外と実験した記事が少なく、私自身も迷った経験があります。

そこで今回は、Google ColabとMNISTを使って4パターンを実際に動かし、学習曲線で比較しました。

この記事を読むとわかること:

  • BatchNormalizationだけ・Dropoutだけ・両方・なしで精度はどう変わるか
  • 過学習の起きやすさはパターンによってどう違うか
  • 自分のモデルにどちらを入れるべきかの判断基準

BatchNormalizationとDropoutの違いを1分で理解する

まず、2つの手法の役割を簡単に整理しておきます。

BatchNormalization(バッチ正規化)は、各層の出力を正規化して学習を安定させる手法です。学習が速くなり、初期値の影響を受けにくくなります。

Dropoutは、学習中にランダムにニューロンを無効化することで、特定のパターンへの依存を防ぐ手法です。過学習を直接抑制する効果があります。

役割が違うので「どちらかが正しい」という話ではありませんが、一緒に使うと干渉することがあるという報告もあります。実際のところどうなのか、実験で確かめます。

※仕組みの詳しい解説は → 【Keras】BatchNormalizationとは?仕組み・メリット・使い方を徹底解説

実験設定:4パターンのモデルコード

使用環境はGoogle Colab(GPU:T4)、データセットはMNISTです。

モデルはDense層3層のシンプルな全結合ネットワークを使い、BNとDropoutの有無だけを変えた4パターンで比較しました。

実験コード

環境準備(最初に一度だけ実行)

# ── 環境準備(最初に一度だけ実行)──────────────────────
!apt-get -y install fonts-ipafont-gothic
!rm -rf /root/.cache/matplotlib
!pip install -q japanize_matplotlib
print("環境準備完了")
実行結果をクリックして内容を開く
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following additional packages will be installed:
  fonts-ipafont-mincho
The following NEW packages will be installed:
  fonts-ipafont-gothic fonts-ipafont-mincho
0 upgraded, 2 newly installed, 0 to remove and 2 not upgraded.
Need to get 8,237 kB of archives.
After this operation, 28.7 MB of additional disk space will be used.
Get:1 http://archive.ubuntu.com/ubuntu jammy/universe amd64 fonts-ipafont-gothic all 00303-21ubuntu1 [3,513 kB]
Get:2 http://archive.ubuntu.com/ubuntu jammy/universe amd64 fonts-ipafont-mincho all 00303-21ubuntu1 [4,724 kB]
Fetched 8,237 kB in 4s (1,957 kB/s)
Selecting previously unselected package fonts-ipafont-gothic.
(Reading database ... 117540 files and directories currently installed.)
Preparing to unpack .../fonts-ipafont-gothic_00303-21ubuntu1_all.deb ...
Unpacking fonts-ipafont-gothic (00303-21ubuntu1) ...
Selecting previously unselected package fonts-ipafont-mincho.
Preparing to unpack .../fonts-ipafont-mincho_00303-21ubuntu1_all.deb ...
Unpacking fonts-ipafont-mincho (00303-21ubuntu1) ...
Setting up fonts-ipafont-mincho (00303-21ubuntu1) ...
update-alternatives: using /usr/share/fonts/opentype/ipafont-mincho/ipam.ttf to provide /usr/share/fonts/truetype/fonts-japanese-mincho.ttf (fonts-japanese-mincho.ttf) in auto mode
Setting up fonts-ipafont-gothic (00303-21ubuntu1) ...
update-alternatives: using /usr/share/fonts/opentype/ipafont-gothic/ipag.ttf to provide /usr/share/fonts/truetype/fonts-japanese-gothic.ttf (fonts-japanese-gothic.ttf) in auto mode
Processing triggers for fontconfig (2.13.1-4.2ubuntu5) ...
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.1/4.1 MB 36.9 MB/s eta 0:00:00
  Preparing metadata (setup.py) ... done
  Building wheel for japanize_matplotlib (setup.py) ... done
環境準備完了

前準備

# ── import ──────────────────────────────────────────────
import tensorflow as tf
from tensorflow import keras
import matplotlib.pyplot as plt
import japanize_matplotlib

# ── データ準備 ──────────────────────────────────────────
(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
x_train = x_train.reshape(-1, 784).astype('float32') / 255.0
x_test  = x_test.reshape(-1, 784).astype('float32') / 255.0
実行結果をクリックして内容を開く
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
11490434/11490434 ━━━━━━━━━━━━━━━━━━━━ 1s 0us/step

学習

# ── 共通コンパイル&学習関数 ───────────────────────────
def compile_and_fit(model):
    model.compile(
        optimizer='adam',
        loss='sparse_categorical_crossentropy',
        metrics=['accuracy']
    )
    return model.fit(
        x_train, y_train,
        epochs=30,
        batch_size=128,
        validation_split=0.2,
        verbose=1
    )

# ── 4パターンの学習 ────────────────────────────────────

print("\n=== Pattern A:なし(ベースライン)===")
model_A = keras.Sequential([
    keras.layers.Input(shape=(784,)),
    keras.layers.Dense(256, activation='relu'),
    keras.layers.Dense(128, activation='relu'),
    keras.layers.Dense(10,  activation='softmax')
], name='A_none')
history_A = compile_and_fit(model_A)

print("\n=== Pattern B:BatchNormalizationのみ ===")
model_B = keras.Sequential([
    keras.layers.Input(shape=(784,)),
    keras.layers.Dense(256, activation='relu'),
    keras.layers.BatchNormalization(),
    keras.layers.Dense(128, activation='relu'),
    keras.layers.BatchNormalization(),
    keras.layers.Dense(10,  activation='softmax')
], name='B_bn_only')
history_B = compile_and_fit(model_B)

print("\n=== Pattern C:Dropoutのみ ===")
model_C = keras.Sequential([
    keras.layers.Input(shape=(784,)),
    keras.layers.Dense(256, activation='relu'),
    keras.layers.Dropout(0.3),
    keras.layers.Dense(128, activation='relu'),
    keras.layers.Dropout(0.3),
    keras.layers.Dense(10,  activation='softmax')
], name='C_dropout_only')
history_C = compile_and_fit(model_C)

print("\n=== Pattern D:BN+Dropout ===")
model_D = keras.Sequential([
    keras.layers.Input(shape=(784,)),
    keras.layers.Dense(256, activation='relu'),
    keras.layers.BatchNormalization(),   # BNを先(推奨順序)
    keras.layers.Dropout(0.3),
    keras.layers.Dense(128, activation='relu'),
    keras.layers.BatchNormalization(),
    keras.layers.Dropout(0.3),
    keras.layers.Dense(10,  activation='softmax')
], name='D_bn_dropout')
history_D = compile_and_fit(model_D)

# ── テストデータで最終精度を評価 ───────────────────────
test_results = {
    'A:なし':        model_A.evaluate(x_test, y_test, verbose=0),
    'B:BNのみ':      model_B.evaluate(x_test, y_test, verbose=0),
    'C:Dropoutのみ': model_C.evaluate(x_test, y_test, verbose=0),
    'D:BN+Dropout': model_D.evaluate(x_test, y_test, verbose=0),
}
実行結果をクリックして内容を開く
=== Pattern A:なし(ベースライン)===
Epoch 1/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 9s 16ms/step - accuracy: 0.8446 - loss: 0.5566 - val_accuracy: 0.9570 - val_loss: 0.1474
Epoch 2/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 10s 16ms/step - accuracy: 0.9636 - loss: 0.1228 - val_accuracy: 0.9663 - val_loss: 0.1089
Epoch 3/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 5s 13ms/step - accuracy: 0.9779 - loss: 0.0744 - val_accuracy: 0.9713 - val_loss: 0.0965
Epoch 4/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 6s 16ms/step - accuracy: 0.9846 - loss: 0.0528 - val_accuracy: 0.9738 - val_loss: 0.0843
Epoch 5/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 9s 24ms/step - accuracy: 0.9900 - loss: 0.0364 - val_accuracy: 0.9740 - val_loss: 0.0902
Epoch 6/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 6s 14ms/step - accuracy: 0.9928 - loss: 0.0265 - val_accuracy: 0.9763 - val_loss: 0.0834
Epoch 7/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 8s 9ms/step - accuracy: 0.9929 - loss: 0.0234 - val_accuracy: 0.9743 - val_loss: 0.0888
Epoch 8/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 8ms/step - accuracy: 0.9946 - loss: 0.0180 - val_accuracy: 0.9764 - val_loss: 0.0970
Epoch 9/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9955 - loss: 0.0150 - val_accuracy: 0.9752 - val_loss: 0.1003
Epoch 10/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9973 - loss: 0.0101 - val_accuracy: 0.9737 - val_loss: 0.1105
Epoch 11/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 8ms/step - accuracy: 0.9963 - loss: 0.0122 - val_accuracy: 0.9745 - val_loss: 0.1095
Epoch 12/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9974 - loss: 0.0083 - val_accuracy: 0.9749 - val_loss: 0.1089
Epoch 13/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9986 - loss: 0.0052 - val_accuracy: 0.9762 - val_loss: 0.1194
Epoch 14/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 8ms/step - accuracy: 0.9963 - loss: 0.0108 - val_accuracy: 0.9768 - val_loss: 0.1092
Epoch 15/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9972 - loss: 0.0084 - val_accuracy: 0.9766 - val_loss: 0.1088
Epoch 16/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 8ms/step - accuracy: 0.9978 - loss: 0.0073 - val_accuracy: 0.9743 - val_loss: 0.1259
Epoch 17/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9958 - loss: 0.0127 - val_accuracy: 0.9781 - val_loss: 0.1096
Epoch 18/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9983 - loss: 0.0047 - val_accuracy: 0.9756 - val_loss: 0.1244
Epoch 19/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 7ms/step - accuracy: 0.9978 - loss: 0.0074 - val_accuracy: 0.9758 - val_loss: 0.1245
Epoch 20/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9987 - loss: 0.0045 - val_accuracy: 0.9772 - val_loss: 0.1265
Epoch 21/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.9994 - loss: 0.0025 - val_accuracy: 0.9729 - val_loss: 0.1543
Epoch 22/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 8ms/step - accuracy: 0.9970 - loss: 0.0094 - val_accuracy: 0.9753 - val_loss: 0.1241
Epoch 23/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9965 - loss: 0.0105 - val_accuracy: 0.9783 - val_loss: 0.1227
Epoch 24/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9990 - loss: 0.0032 - val_accuracy: 0.9800 - val_loss: 0.1129
Epoch 25/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 8ms/step - accuracy: 0.9993 - loss: 0.0023 - val_accuracy: 0.9799 - val_loss: 0.1215
Epoch 26/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 8ms/step - accuracy: 0.9994 - loss: 0.0020 - val_accuracy: 0.9790 - val_loss: 0.1286
Epoch 27/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9981 - loss: 0.0054 - val_accuracy: 0.9763 - val_loss: 0.1370
Epoch 28/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 8ms/step - accuracy: 0.9982 - loss: 0.0055 - val_accuracy: 0.9779 - val_loss: 0.1326
Epoch 29/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 8ms/step - accuracy: 0.9989 - loss: 0.0035 - val_accuracy: 0.9781 - val_loss: 0.1354
Epoch 30/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 8ms/step - accuracy: 0.9986 - loss: 0.0050 - val_accuracy: 0.9778 - val_loss: 0.1362

=== Pattern B:BatchNormalizationのみ ===
Epoch 1/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 6s 11ms/step - accuracy: 0.8729 - loss: 0.4201 - val_accuracy: 0.9653 - val_loss: 0.1253
Epoch 2/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9763 - loss: 0.0809 - val_accuracy: 0.9696 - val_loss: 0.0968
Epoch 3/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9852 - loss: 0.0503 - val_accuracy: 0.9706 - val_loss: 0.0981
Epoch 4/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 8s 22ms/step - accuracy: 0.9884 - loss: 0.0383 - val_accuracy: 0.9732 - val_loss: 0.0911
Epoch 5/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.9917 - loss: 0.0279 - val_accuracy: 0.9759 - val_loss: 0.0800
Epoch 6/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9933 - loss: 0.0219 - val_accuracy: 0.9759 - val_loss: 0.0831
Epoch 7/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 8ms/step - accuracy: 0.9952 - loss: 0.0167 - val_accuracy: 0.9748 - val_loss: 0.0879
Epoch 8/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9953 - loss: 0.0156 - val_accuracy: 0.9754 - val_loss: 0.0909
Epoch 9/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9955 - loss: 0.0133 - val_accuracy: 0.9735 - val_loss: 0.1042
Epoch 10/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 8ms/step - accuracy: 0.9957 - loss: 0.0130 - val_accuracy: 0.9740 - val_loss: 0.1011
Epoch 11/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9956 - loss: 0.0139 - val_accuracy: 0.9697 - val_loss: 0.1150
Epoch 12/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9957 - loss: 0.0129 - val_accuracy: 0.9759 - val_loss: 0.1016
Epoch 13/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9969 - loss: 0.0097 - val_accuracy: 0.9791 - val_loss: 0.0891
Epoch 14/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9983 - loss: 0.0063 - val_accuracy: 0.9770 - val_loss: 0.1076
Epoch 15/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9963 - loss: 0.0107 - val_accuracy: 0.9750 - val_loss: 0.1070
Epoch 16/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9972 - loss: 0.0087 - val_accuracy: 0.9752 - val_loss: 0.1176
Epoch 17/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9963 - loss: 0.0114 - val_accuracy: 0.9712 - val_loss: 0.1180
Epoch 18/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9952 - loss: 0.0130 - val_accuracy: 0.9763 - val_loss: 0.0992
Epoch 19/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9980 - loss: 0.0068 - val_accuracy: 0.9762 - val_loss: 0.1054
Epoch 20/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 8ms/step - accuracy: 0.9978 - loss: 0.0065 - val_accuracy: 0.9793 - val_loss: 0.0937
Epoch 21/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 8ms/step - accuracy: 0.9991 - loss: 0.0031 - val_accuracy: 0.9780 - val_loss: 0.1073
Epoch 22/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.9986 - loss: 0.0052 - val_accuracy: 0.9770 - val_loss: 0.1081
Epoch 23/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9983 - loss: 0.0057 - val_accuracy: 0.9778 - val_loss: 0.0972
Epoch 24/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.9982 - loss: 0.0052 - val_accuracy: 0.9766 - val_loss: 0.1138
Epoch 25/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9982 - loss: 0.0055 - val_accuracy: 0.9742 - val_loss: 0.1177
Epoch 26/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9971 - loss: 0.0099 - val_accuracy: 0.9765 - val_loss: 0.1156
Epoch 27/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9977 - loss: 0.0068 - val_accuracy: 0.9775 - val_loss: 0.1113
Epoch 28/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9983 - loss: 0.0056 - val_accuracy: 0.9796 - val_loss: 0.0989
Epoch 29/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9991 - loss: 0.0025 - val_accuracy: 0.9803 - val_loss: 0.0954
Epoch 30/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9990 - loss: 0.0030 - val_accuracy: 0.9787 - val_loss: 0.1086

=== Pattern C:Dropoutのみ ===
Epoch 1/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.7729 - loss: 0.7322 - val_accuracy: 0.9563 - val_loss: 0.1482
Epoch 2/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 7s 19ms/step - accuracy: 0.9436 - loss: 0.1836 - val_accuracy: 0.9672 - val_loss: 0.1106
Epoch 3/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9585 - loss: 0.1367 - val_accuracy: 0.9722 - val_loss: 0.0963
Epoch 4/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.9659 - loss: 0.1113 - val_accuracy: 0.9735 - val_loss: 0.0879
Epoch 5/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 8ms/step - accuracy: 0.9701 - loss: 0.0941 - val_accuracy: 0.9759 - val_loss: 0.0829
Epoch 6/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9749 - loss: 0.0816 - val_accuracy: 0.9762 - val_loss: 0.0798
Epoch 7/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 8ms/step - accuracy: 0.9773 - loss: 0.0704 - val_accuracy: 0.9762 - val_loss: 0.0803
Epoch 8/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 8ms/step - accuracy: 0.9789 - loss: 0.0633 - val_accuracy: 0.9783 - val_loss: 0.0790
Epoch 9/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9809 - loss: 0.0571 - val_accuracy: 0.9787 - val_loss: 0.0749
Epoch 10/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 8ms/step - accuracy: 0.9829 - loss: 0.0538 - val_accuracy: 0.9775 - val_loss: 0.0795
Epoch 11/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 8ms/step - accuracy: 0.9844 - loss: 0.0483 - val_accuracy: 0.9798 - val_loss: 0.0736
Epoch 12/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 8ms/step - accuracy: 0.9856 - loss: 0.0447 - val_accuracy: 0.9791 - val_loss: 0.0758
Epoch 13/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 4s 9ms/step - accuracy: 0.9866 - loss: 0.0414 - val_accuracy: 0.9786 - val_loss: 0.0743
Epoch 14/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 8ms/step - accuracy: 0.9881 - loss: 0.0374 - val_accuracy: 0.9788 - val_loss: 0.0749
Epoch 15/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9874 - loss: 0.0380 - val_accuracy: 0.9801 - val_loss: 0.0778
Epoch 16/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9879 - loss: 0.0375 - val_accuracy: 0.9789 - val_loss: 0.0805
Epoch 17/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9892 - loss: 0.0331 - val_accuracy: 0.9806 - val_loss: 0.0768
Epoch 18/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 8ms/step - accuracy: 0.9886 - loss: 0.0343 - val_accuracy: 0.9811 - val_loss: 0.0842
Epoch 19/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 8ms/step - accuracy: 0.9893 - loss: 0.0311 - val_accuracy: 0.9803 - val_loss: 0.0833
Epoch 20/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9909 - loss: 0.0279 - val_accuracy: 0.9808 - val_loss: 0.0769
Epoch 21/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 8ms/step - accuracy: 0.9895 - loss: 0.0302 - val_accuracy: 0.9827 - val_loss: 0.0747
Epoch 22/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 8ms/step - accuracy: 0.9907 - loss: 0.0291 - val_accuracy: 0.9824 - val_loss: 0.0810
Epoch 23/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 8ms/step - accuracy: 0.9912 - loss: 0.0255 - val_accuracy: 0.9813 - val_loss: 0.0830
Epoch 24/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9928 - loss: 0.0241 - val_accuracy: 0.9803 - val_loss: 0.0825
Epoch 25/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 8ms/step - accuracy: 0.9912 - loss: 0.0255 - val_accuracy: 0.9812 - val_loss: 0.0799
Epoch 26/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 8ms/step - accuracy: 0.9907 - loss: 0.0266 - val_accuracy: 0.9816 - val_loss: 0.0860
Epoch 27/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9926 - loss: 0.0221 - val_accuracy: 0.9802 - val_loss: 0.0864
Epoch 28/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9911 - loss: 0.0275 - val_accuracy: 0.9807 - val_loss: 0.0827
Epoch 29/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9915 - loss: 0.0259 - val_accuracy: 0.9792 - val_loss: 0.0906
Epoch 30/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.9929 - loss: 0.0227 - val_accuracy: 0.9797 - val_loss: 0.0910

=== Pattern D:BN+Dropout ===
Epoch 1/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.8030 - loss: 0.6462 - val_accuracy: 0.9535 - val_loss: 0.1690
Epoch 2/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 8ms/step - accuracy: 0.9468 - loss: 0.1793 - val_accuracy: 0.9637 - val_loss: 0.1175
Epoch 3/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 8s 20ms/step - accuracy: 0.9577 - loss: 0.1391 - val_accuracy: 0.9672 - val_loss: 0.1071
Epoch 4/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9677 - loss: 0.1066 - val_accuracy: 0.9737 - val_loss: 0.0851
Epoch 5/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 8ms/step - accuracy: 0.9705 - loss: 0.0937 - val_accuracy: 0.9753 - val_loss: 0.0840
Epoch 6/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 4s 11ms/step - accuracy: 0.9749 - loss: 0.0803 - val_accuracy: 0.9768 - val_loss: 0.0797
Epoch 7/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9775 - loss: 0.0712 - val_accuracy: 0.9753 - val_loss: 0.0822
Epoch 8/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9791 - loss: 0.0667 - val_accuracy: 0.9760 - val_loss: 0.0836
Epoch 9/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 4s 12ms/step - accuracy: 0.9808 - loss: 0.0591 - val_accuracy: 0.9763 - val_loss: 0.0756
Epoch 10/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9821 - loss: 0.0555 - val_accuracy: 0.9780 - val_loss: 0.0759
Epoch 11/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 8ms/step - accuracy: 0.9828 - loss: 0.0516 - val_accuracy: 0.9765 - val_loss: 0.0818
Epoch 12/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9823 - loss: 0.0533 - val_accuracy: 0.9783 - val_loss: 0.0734
Epoch 13/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 4s 12ms/step - accuracy: 0.9828 - loss: 0.0492 - val_accuracy: 0.9778 - val_loss: 0.0738
Epoch 14/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 7s 19ms/step - accuracy: 0.9857 - loss: 0.0413 - val_accuracy: 0.9800 - val_loss: 0.0694
Epoch 15/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 7s 18ms/step - accuracy: 0.9863 - loss: 0.0420 - val_accuracy: 0.9792 - val_loss: 0.0769
Epoch 16/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 7s 19ms/step - accuracy: 0.9846 - loss: 0.0442 - val_accuracy: 0.9788 - val_loss: 0.0752
Epoch 17/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 8s 21ms/step - accuracy: 0.9875 - loss: 0.0373 - val_accuracy: 0.9809 - val_loss: 0.0786
Epoch 18/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 6s 16ms/step - accuracy: 0.9865 - loss: 0.0380 - val_accuracy: 0.9822 - val_loss: 0.0724
Epoch 19/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 6s 17ms/step - accuracy: 0.9879 - loss: 0.0366 - val_accuracy: 0.9816 - val_loss: 0.0715
Epoch 20/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 5s 12ms/step - accuracy: 0.9890 - loss: 0.0338 - val_accuracy: 0.9796 - val_loss: 0.0742
Epoch 21/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9886 - loss: 0.0341 - val_accuracy: 0.9822 - val_loss: 0.0683
Epoch 22/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9899 - loss: 0.0313 - val_accuracy: 0.9811 - val_loss: 0.0758
Epoch 23/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 4s 11ms/step - accuracy: 0.9896 - loss: 0.0306 - val_accuracy: 0.9802 - val_loss: 0.0739
Epoch 24/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9905 - loss: 0.0293 - val_accuracy: 0.9805 - val_loss: 0.0777
Epoch 25/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9897 - loss: 0.0341 - val_accuracy: 0.9814 - val_loss: 0.0732
Epoch 26/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 4s 10ms/step - accuracy: 0.9905 - loss: 0.0301 - val_accuracy: 0.9817 - val_loss: 0.0744
Epoch 27/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9900 - loss: 0.0280 - val_accuracy: 0.9804 - val_loss: 0.0770
Epoch 28/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.9906 - loss: 0.0266 - val_accuracy: 0.9835 - val_loss: 0.0702
Epoch 29/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 4s 11ms/step - accuracy: 0.9918 - loss: 0.0242 - val_accuracy: 0.9813 - val_loss: 0.0749
Epoch 30/30
375/375 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.9906 - loss: 0.0261 - val_accuracy: 0.9821 - val_loss: 0.0710

学習曲線の可視化

# ── 学習曲線グラフ(val_accuracy / val_loss 比較)──────
histories = {
    'A:なし':        history_A,
    'B:BNのみ':      history_B,
    'C:Dropoutのみ': history_C,
    'D:BN+Dropout': history_D,
}

fig, axes = plt.subplots(1, 2, figsize=(14, 5))

for label, h in histories.items():
    axes[0].plot(h.history['val_accuracy'], label=label)
    axes[1].plot(h.history['val_loss'],     label=label)

axes[0].set_title('val_accuracy の比較')
axes[1].set_title('val_loss の比較')

for ax in axes:
    ax.set_xlabel('Epoch')
    ax.legend()
    ax.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

最終結果サマリー

# ── 最終結果サマリー ────────────────────────────────────
# ※ val/test の使い分け:
#   val_acc  → 学習中の汎化性能の推移(グラフ比較に使用)
#   test_acc → 学習後の最終確認用(未使用データで1回だけ評価)

key_order = ['A:なし', 'B:BNのみ', 'C:Dropoutのみ', 'D:BN+Dropout']

print("\n===== 最終結果サマリー =====")
print(f"{'Pattern':>16} | {'Train Acc':>10} | {'Val Acc':>8} | {'Test Acc':>9}")
print("-" * 55)
for key in key_order:
    h = histories[key]
    test_loss, test_acc = test_results[key]
    train_acc = h.history['accuracy'][-1]
    val_acc   = h.history['val_accuracy'][-1]
    print(f"{key:>16} | {train_acc:>10.4f} | {val_acc:>8.4f} | {test_acc:>9.4f}")
print("-" * 55)

最終結果

===== 最終結果サマリー =====
         Pattern |  Train Acc |  Val Acc |  Test Acc
-------------------------------------------------------
            A:なし |     0.9983 |   0.9778 |    0.9783
          B:BNのみ |     0.9988 |   0.9787 |    0.9812
     C:Dropoutのみ |     0.9923 |   0.9797 |    0.9813
    D:BN+Dropout |     0.9913 |   0.9821 |    0.9843
-------------------------------------------------------

実験結果:学習曲線グラフを4本並べて比較

val_accuracy の比較

精度グラフ

損失グラフ

グラフから読み取れること:

  • パターンB(BNのみ)は収束が最も速く、初期段階から高い精度に到達しました。最適化の安定化効果が強く表れています。
  • パターンC(Dropoutのみ)は学習初期の精度は低めですが、過学習が抑制され、val_lossが安定する傾向が見られました。
  • パターンD(BN+Dropout)は最適化の安定性と正則化効果の両方が働き、最終的なval_accuracyが最も高く、汎化性能の観点で最良の結果となりました。

過学習の起きやすさ(train vs val のギャップ)

train_lossとval_lossの乖離を見ると:

  • パターンA(なし)がもっとも乖離が大きく、後半でval_lossが上昇していることから明確な過学習が確認できます。
  • パターンC(Dropoutのみ)およびパターンD(BN+Dropout)は乖離が小さく、過学習が効果的に抑制されています。
  • 特にパターンDはval_lossが安定しており、汎化性能が最も高い結果となりました。

結論:どのパターンを選ぶべきか

実験結果をもとに、状況別の結論をまとめます。

「とりあえず精度を上げたい・収束を速くしたい」→ BNのみ(パターンB)
BNは収束速度の改善効果が明確で、MNISTのようなシンプルなタスクでは過学習もそれほど問題になりません。

「過学習が起きていてなんとかしたい」→ Dropoutのみ(パターンC)
Dropoutは直接的に過学習を抑制します。ただし収束が遅くなるため、EarlyStoppingと組み合わせるのがおすすめです。

「より安定した汎化性能が欲しい・大きめのモデルを使っている」→ BN+Dropout(パターンD)
2つを組み合わせると干渉が起きるという説もありますが、今回の実験では問題は見られませんでした。ただしBNをDropoutより先に置くのが安定する順序でした。

一点注意:MNISTは比較的シンプルなデータセットです。CIFAR-10などより複雑なタスクでは結果が変わる可能性があります。

まとめ

今回はKerasとMNISTを使い、BatchNormalization・Dropoutの組み合わせ4パターンを比較しました。

  • 収束速度を重視するなら → BatchNormalizationのみ
  • 過学習を抑えたいなら → Dropout(rate=0.3)のみ
  • 安定性を最優先するなら → BN → Dropout の順で両方入れる

「どちらを入れるか」と悩んでいた方の参考になれば幸いです。

関連記事もあわせてどうぞ: