Dense層の数を変えると精度はどう変わる?(1層 vs 2層 vs 3層)【Keras×CIFAR-10実験】

投稿日:2026年6月9日火曜日 最終更新日:

CIFAR-10 CNN Dense Google Colab Keras 過学習 画像分類

X f B! P L
Dense層の数を変えると精度はどう変わる?(1層 vs 2層 vs 3層)【Keras×CIFAR-10実験】 アイキャッチ画像

KerasのCNNでDense層の数はいくつにすべきか、迷ったことはありませんか?

「Deep Learningなんだから層は多いほど良い」——CNNの畳み込み部分ではそれが当てはまる場面もありますが、GAP後のDense層を積み重ねることが本当に有効かどうかは別の話です。今回はGoogle ColabとCIFAR-10を使い、Dense層の数を1層・2層・3層の3パターンで比較しました。

なお、Dense層のユニット数比較は → Dense層のユニット数を変えると精度はどう変わる?(32 vs 128 vs 512) をご覧ください。本記事ではユニット数を128に固定したうえで、層数の影響だけを検証します。

📘 この記事でわかること
  • Dense層を1層・2層・3層に変えたときの精度・過学習の違い
  • GAP構成でDenseを積み重ねることが有効かどうか
  • Dropoutの配置がDense多層化と組み合わさったときの挙動
  • CIFAR-10での実用的なDense層数の目安

Dense層の数を変えると何が起きるか

GAP後のDense層は、畳み込みで抽出した特徴を最終的なクラス予測にマッピングする役割を担います。層を増やすと非線形変換が増え、より複雑な特徴の組み合わせを学習できる可能性がありますが、同時に過学習のリスクと学習の難易度も上がります

パターン 構成 実際のパラメータ数 期待される挙動
A:Dense 1層 Dense(128) → Dense(10) 93,450 シンプル。CIFAR-10では標準的な構成
B:Dense 2層 Dense(128) → Dense(64) → Dense(10) 101,066 中程度の表現力。段階的な次元削減
C:Dense 3層 Dense(128) → Dense(64) → Dense(32) → Dense(10) 102,826 高い表現力だが正則化が複合的に強まる

今回のモデルはGAP後の入力が128次元に固定されているため、Dense層を増やしてもパラメータ増加は最大で約10%(93,450→102,826)に留まります。それでも層を深くすることで過学習や勾配消失が起きやすくなるかどうかを実験で確かめます。


実験コード

使用環境はGoogle Colab(GPU:T4)、データセットはCIFAR-10です。Dense層の数以外の条件は全て同一にして、層数の影響だけを取り出します。各Dense層の後にDropout(0.2)を挿入し、正則化の強さが層数に応じて変わる点も観察します。

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

# ── 環境準備(最初に一度だけ実行)──────────────────────
!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 53 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 0s (36.0 MB/s)
Selecting previously unselected package fonts-ipafont-gothic.
(Reading database ... 122403 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 54.6 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
import numpy as np
import time

# 再現性のためにシードを固定
tf.random.set_seed(42)
np.random.seed(42)

# データ読み込みと正規化
(x_train, y_train), (x_test, y_test) = keras.datasets.cifar10.load_data()
x_train = x_train.astype('float32') / 255.0
x_test  = x_test.astype('float32')  / 255.0

# ── 共通のCNN特徴抽出部分 ──────────────────────────────
def conv_base():
    return [
        keras.layers.Input(shape=(32, 32, 3)),
        keras.layers.Conv2D(64,  (3, 3), activation='relu', padding='same'),
        keras.layers.MaxPooling2D((2, 2)),
        keras.layers.Conv2D(128, (3, 3), activation='relu', padding='same'),
        keras.layers.MaxPooling2D((2, 2)),
        keras.layers.GlobalAveragePooling2D(),
    ]

# ── パターン別モデル構築 ─────────────────────────────────
def build_model(pattern, name):
    layers = conv_base()
    if pattern == 1:
        # A:Dense 1層
        layers += [
            keras.layers.Dense(128, activation='relu'),
            keras.layers.Dropout(0.2),
        ]
    elif pattern == 2:
        # B:Dense 2層
        layers += [
            keras.layers.Dense(128, activation='relu'),
            keras.layers.Dropout(0.2),
            keras.layers.Dense(64, activation='relu'),
            keras.layers.Dropout(0.2),
        ]
    elif pattern == 3:
        # C:Dense 3層
        layers += [
            keras.layers.Dense(128, activation='relu'),
            keras.layers.Dropout(0.2),
            keras.layers.Dense(64, activation='relu'),
            keras.layers.Dropout(0.2),
            keras.layers.Dense(32, activation='relu'),
            keras.layers.Dropout(0.2),
        ]
    layers.append(keras.layers.Dense(10, activation='softmax'))
    return keras.Sequential(layers, name=name)

def compile_and_fit(model):
    model.compile(optimizer='adam',
                  loss='sparse_categorical_crossentropy',
                  metrics=['accuracy'])
    start = time.time()
    history = model.fit(x_train, y_train, epochs=30, batch_size=64,
                        validation_split=0.2, verbose=1)
    return history, time.time() - start
実行結果をクリックして内容を開く
Downloading data from https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz
170498071/170498071 ━━━━━━━━━━━━━━━━━━━━ 4s 0us/step

③ 3パターンの学習実行

configs = [(1, 'A_dense1'), (2, 'B_dense2'), (3, 'C_dense3')]
histories, times, scores, params = {}, {}, {}, {}

for pattern, name in configs:
    print(f"\n=== {name} ===")
    tf.random.set_seed(42)
    np.random.seed(42)
    model = build_model(pattern, name)
    print(model.summary())
    h, t = compile_and_fit(model)
    s = model.evaluate(x_test, y_test, verbose=0)
    label = name.split('_')[1]
    histories[label] = h
    times[label]     = t
    scores[label]    = s
    params[label]    = model.count_params()
    print(f"学習時間:{t:.1f}秒 パラメータ数:{model.count_params():,} test_accuracy:{s[1]:.4f}")
実行結果をクリックして内容を開く
=== A_dense1 ===
Model: "A_dense1"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Layer (type)                    ┃ Output Shape           ┃       Param # ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ conv2d (Conv2D)                 │ (None, 32, 32, 64)     │         1,792 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ max_pooling2d (MaxPooling2D)    │ (None, 16, 16, 64)     │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ conv2d_1 (Conv2D)               │ (None, 16, 16, 128)    │        73,856 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ max_pooling2d_1 (MaxPooling2D)  │ (None, 8, 8, 128)      │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ global_average_pooling2d        │ (None, 128)            │             0 │
│ (GlobalAveragePooling2D)        │                        │               │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense (Dense)                   │ (None, 128)            │        16,512 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dropout (Dropout)               │ (None, 128)            │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_1 (Dense)                 │ (None, 10)             │         1,290 │
└─────────────────────────────────┴────────────────────────┴───────────────┘
 Total params: 93,450 (365.04 KB)
 Trainable params: 93,450 (365.04 KB)
 Non-trainable params: 0 (0.00 B)
None
Epoch 1/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 10s 9ms/step - accuracy: 0.2652 - loss: 1.9200 - val_accuracy: 0.3645 - val_loss: 1.7004
Epoch 2/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.3656 - loss: 1.6788 - val_accuracy: 0.4144 - val_loss: 1.5916
Epoch 3/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.4243 - loss: 1.5664 - val_accuracy: 0.4659 - val_loss: 1.4694
Epoch 4/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.4642 - loss: 1.4693 - val_accuracy: 0.4900 - val_loss: 1.4059
Epoch 5/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.4931 - loss: 1.3941 - val_accuracy: 0.5170 - val_loss: 1.3334
Epoch 6/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5144 - loss: 1.3353 - val_accuracy: 0.5301 - val_loss: 1.2859
Epoch 7/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.5292 - loss: 1.2948 - val_accuracy: 0.5398 - val_loss: 1.2550
Epoch 8/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 9s 7ms/step - accuracy: 0.5444 - loss: 1.2595 - val_accuracy: 0.5507 - val_loss: 1.2353
Epoch 9/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5547 - loss: 1.2269 - val_accuracy: 0.5642 - val_loss: 1.1970
Epoch 10/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5658 - loss: 1.1976 - val_accuracy: 0.5760 - val_loss: 1.1764
Epoch 11/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 7ms/step - accuracy: 0.5774 - loss: 1.1721 - val_accuracy: 0.5842 - val_loss: 1.1558
Epoch 12/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5857 - loss: 1.1487 - val_accuracy: 0.5950 - val_loss: 1.1295
Epoch 13/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5949 - loss: 1.1236 - val_accuracy: 0.6000 - val_loss: 1.1101
Epoch 14/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.6052 - loss: 1.0981 - val_accuracy: 0.6090 - val_loss: 1.0865
Epoch 15/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6101 - loss: 1.0819 - val_accuracy: 0.6134 - val_loss: 1.0749
Epoch 16/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6183 - loss: 1.0596 - val_accuracy: 0.6260 - val_loss: 1.0411
Epoch 17/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.6268 - loss: 1.0412 - val_accuracy: 0.6297 - val_loss: 1.0346
Epoch 18/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6316 - loss: 1.0222 - val_accuracy: 0.6302 - val_loss: 1.0313
Epoch 19/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6373 - loss: 1.0061 - val_accuracy: 0.6301 - val_loss: 1.0262
Epoch 20/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6453 - loss: 0.9855 - val_accuracy: 0.6414 - val_loss: 0.9973
Epoch 21/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6514 - loss: 0.9715 - val_accuracy: 0.6420 - val_loss: 0.9970
Epoch 22/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6528 - loss: 0.9608 - val_accuracy: 0.6368 - val_loss: 1.0095
Epoch 23/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6586 - loss: 0.9489 - val_accuracy: 0.6576 - val_loss: 0.9547
Epoch 24/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.6658 - loss: 0.9324 - val_accuracy: 0.6578 - val_loss: 0.9527
Epoch 25/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6677 - loss: 0.9211 - val_accuracy: 0.6633 - val_loss: 0.9519
Epoch 26/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6744 - loss: 0.9083 - val_accuracy: 0.6659 - val_loss: 0.9449
Epoch 27/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6771 - loss: 0.8960 - val_accuracy: 0.6660 - val_loss: 0.9395
Epoch 28/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6812 - loss: 0.8848 - val_accuracy: 0.6677 - val_loss: 0.9343
Epoch 29/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6850 - loss: 0.8772 - val_accuracy: 0.6747 - val_loss: 0.9143
Epoch 30/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.6912 - loss: 0.8647 - val_accuracy: 0.6682 - val_loss: 0.9450
学習時間:130.7秒 パラメータ数:93,450 test_accuracy:0.6625

=== B_dense2 ===
Model: "B_dense2"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Layer (type)                    ┃ Output Shape           ┃       Param # ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ conv2d_2 (Conv2D)               │ (None, 32, 32, 64)     │         1,792 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ max_pooling2d_2 (MaxPooling2D)  │ (None, 16, 16, 64)     │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ conv2d_3 (Conv2D)               │ (None, 16, 16, 128)    │        73,856 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ max_pooling2d_3 (MaxPooling2D)  │ (None, 8, 8, 128)      │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ global_average_pooling2d_1      │ (None, 128)            │             0 │
│ (GlobalAveragePooling2D)        │                        │               │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_2 (Dense)                 │ (None, 128)            │        16,512 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dropout_1 (Dropout)             │ (None, 128)            │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_3 (Dense)                 │ (None, 64)             │         8,256 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dropout_2 (Dropout)             │ (None, 64)             │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_4 (Dense)                 │ (None, 10)             │           650 │
└─────────────────────────────────┴────────────────────────┴───────────────┘
 Total params: 101,066 (394.79 KB)
 Trainable params: 101,066 (394.79 KB)
 Non-trainable params: 0 (0.00 B)
None
Epoch 1/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 10s 10ms/step - accuracy: 0.2497 - loss: 1.9506 - val_accuracy: 0.3442 - val_loss: 1.7227
Epoch 2/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.3591 - loss: 1.6850 - val_accuracy: 0.4268 - val_loss: 1.5472
Epoch 3/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.4294 - loss: 1.5350 - val_accuracy: 0.4753 - val_loss: 1.4194
Epoch 4/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.4762 - loss: 1.4262 - val_accuracy: 0.5050 - val_loss: 1.3309
Epoch 5/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5040 - loss: 1.3548 - val_accuracy: 0.5252 - val_loss: 1.2772
Epoch 6/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5285 - loss: 1.2933 - val_accuracy: 0.5436 - val_loss: 1.2381
Epoch 7/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5469 - loss: 1.2419 - val_accuracy: 0.5662 - val_loss: 1.1860
Epoch 8/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5633 - loss: 1.2036 - val_accuracy: 0.5647 - val_loss: 1.2135
Epoch 9/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5798 - loss: 1.1667 - val_accuracy: 0.5815 - val_loss: 1.1695
Epoch 10/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5898 - loss: 1.1357 - val_accuracy: 0.5924 - val_loss: 1.1327
Epoch 11/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.6004 - loss: 1.1034 - val_accuracy: 0.6029 - val_loss: 1.0947
Epoch 12/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6111 - loss: 1.0770 - val_accuracy: 0.6186 - val_loss: 1.0531
Epoch 13/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6244 - loss: 1.0447 - val_accuracy: 0.6213 - val_loss: 1.0606
Epoch 14/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6310 - loss: 1.0257 - val_accuracy: 0.6323 - val_loss: 1.0219
Epoch 15/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6385 - loss: 1.0084 - val_accuracy: 0.6330 - val_loss: 1.0203
Epoch 16/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6466 - loss: 0.9872 - val_accuracy: 0.6464 - val_loss: 0.9898
Epoch 17/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.6531 - loss: 0.9651 - val_accuracy: 0.6424 - val_loss: 0.9943
Epoch 18/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6581 - loss: 0.9516 - val_accuracy: 0.6678 - val_loss: 0.9421
Epoch 19/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6692 - loss: 0.9300 - val_accuracy: 0.6637 - val_loss: 0.9452
Epoch 20/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.6731 - loss: 0.9163 - val_accuracy: 0.6723 - val_loss: 0.9205
Epoch 21/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6764 - loss: 0.9030 - val_accuracy: 0.6707 - val_loss: 0.9279
Epoch 22/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6834 - loss: 0.8897 - val_accuracy: 0.6682 - val_loss: 0.9423
Epoch 23/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.6867 - loss: 0.8767 - val_accuracy: 0.6750 - val_loss: 0.9151
Epoch 24/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6951 - loss: 0.8604 - val_accuracy: 0.6797 - val_loss: 0.9026
Epoch 25/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6982 - loss: 0.8477 - val_accuracy: 0.6661 - val_loss: 0.9400
Epoch 26/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.7038 - loss: 0.8388 - val_accuracy: 0.6833 - val_loss: 0.8944
Epoch 27/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.7079 - loss: 0.8249 - val_accuracy: 0.6861 - val_loss: 0.8884
Epoch 28/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.7118 - loss: 0.8120 - val_accuracy: 0.6823 - val_loss: 0.8974
Epoch 29/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.7149 - loss: 0.8044 - val_accuracy: 0.6818 - val_loss: 0.9027
Epoch 30/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.7211 - loss: 0.7913 - val_accuracy: 0.6826 - val_loss: 0.9083
学習時間:124.6秒 パラメータ数:101,066 test_accuracy:0.6832

=== C_dense3 ===
Model: "C_dense3"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Layer (type)                    ┃ Output Shape           ┃       Param # ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ conv2d_4 (Conv2D)               │ (None, 32, 32, 64)     │         1,792 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ max_pooling2d_4 (MaxPooling2D)  │ (None, 16, 16, 64)     │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ conv2d_5 (Conv2D)               │ (None, 16, 16, 128)    │        73,856 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ max_pooling2d_5 (MaxPooling2D)  │ (None, 8, 8, 128)      │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ global_average_pooling2d_2      │ (None, 128)            │             0 │
│ (GlobalAveragePooling2D)        │                        │               │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_5 (Dense)                 │ (None, 128)            │        16,512 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dropout_3 (Dropout)             │ (None, 128)            │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_6 (Dense)                 │ (None, 64)             │         8,256 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dropout_4 (Dropout)             │ (None, 64)             │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_7 (Dense)                 │ (None, 32)             │         2,080 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dropout_5 (Dropout)             │ (None, 32)             │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_8 (Dense)                 │ (None, 10)             │           330 │
└─────────────────────────────────┴────────────────────────┴───────────────┘
 Total params: 102,826 (401.66 KB)
 Trainable params: 102,826 (401.66 KB)
 Non-trainable params: 0 (0.00 B)
None
Epoch 1/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 10s 8ms/step - accuracy: 0.2245 - loss: 1.9996 - val_accuracy: 0.3206 - val_loss: 1.7664
Epoch 2/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.3141 - loss: 1.7610 - val_accuracy: 0.3748 - val_loss: 1.6147
Epoch 3/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.3776 - loss: 1.6246 - val_accuracy: 0.4453 - val_loss: 1.4762
Epoch 4/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 6ms/step - accuracy: 0.4300 - loss: 1.5068 - val_accuracy: 0.4972 - val_loss: 1.3505
Epoch 5/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.4726 - loss: 1.4250 - val_accuracy: 0.5017 - val_loss: 1.3492
Epoch 6/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.4957 - loss: 1.3746 - val_accuracy: 0.5145 - val_loss: 1.3119
Epoch 7/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5155 - loss: 1.3185 - val_accuracy: 0.5437 - val_loss: 1.2467
Epoch 8/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5318 - loss: 1.2774 - val_accuracy: 0.5488 - val_loss: 1.2289
Epoch 9/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.5473 - loss: 1.2461 - val_accuracy: 0.5651 - val_loss: 1.1776
Epoch 10/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5584 - loss: 1.2150 - val_accuracy: 0.5853 - val_loss: 1.1274
Epoch 11/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5732 - loss: 1.1842 - val_accuracy: 0.5912 - val_loss: 1.1129
Epoch 12/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.5885 - loss: 1.1513 - val_accuracy: 0.6077 - val_loss: 1.0830
Epoch 13/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5989 - loss: 1.1305 - val_accuracy: 0.6043 - val_loss: 1.0914
Epoch 14/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6077 - loss: 1.1008 - val_accuracy: 0.6281 - val_loss: 1.0270
Epoch 15/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.6161 - loss: 1.0869 - val_accuracy: 0.6300 - val_loss: 1.0251
Epoch 16/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6233 - loss: 1.0670 - val_accuracy: 0.6390 - val_loss: 1.0004
Epoch 17/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6277 - loss: 1.0483 - val_accuracy: 0.6333 - val_loss: 1.0217
Epoch 18/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.6376 - loss: 1.0289 - val_accuracy: 0.6432 - val_loss: 0.9971
Epoch 19/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6444 - loss: 1.0089 - val_accuracy: 0.6498 - val_loss: 0.9890
Epoch 20/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6516 - loss: 0.9885 - val_accuracy: 0.6549 - val_loss: 0.9818
Epoch 21/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.6575 - loss: 0.9751 - val_accuracy: 0.6419 - val_loss: 1.0068
Epoch 22/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6619 - loss: 0.9578 - val_accuracy: 0.6624 - val_loss: 0.9679
Epoch 23/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6700 - loss: 0.9433 - val_accuracy: 0.6603 - val_loss: 0.9592
Epoch 24/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6722 - loss: 0.9325 - val_accuracy: 0.6718 - val_loss: 0.9315
Epoch 25/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.6806 - loss: 0.9187 - val_accuracy: 0.6656 - val_loss: 0.9410
Epoch 26/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6848 - loss: 0.9061 - val_accuracy: 0.6756 - val_loss: 0.9204
Epoch 27/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6860 - loss: 0.8945 - val_accuracy: 0.6685 - val_loss: 0.9497
Epoch 28/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.6938 - loss: 0.8756 - val_accuracy: 0.6659 - val_loss: 0.9499
Epoch 29/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6980 - loss: 0.8669 - val_accuracy: 0.6835 - val_loss: 0.9059
Epoch 30/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6995 - loss: 0.8586 - val_accuracy: 0.6816 - val_loss: 0.9195
学習時間:127.0秒 パラメータ数:102,826 test_accuracy:0.6760

④ グラフ+サマリー

# ── val_accuracy / val_loss 比較グラフ ──────────────────
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 の比較(全30エポック)')
axes[1].set_title('val_loss の比較(全30エポック)')
for ax in axes:
    ax.set_xlabel('Epoch'); ax.legend(); ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('dense_layers_comparison.png', dpi=150)
plt.show()

# ── train_loss vs val_loss(過学習の乖離)───────────────
fig2, axes2 = plt.subplots(3, 1, figsize=(7, 14))
for i, (label, h) in enumerate(histories.items()):
    axes2[i].plot(h.history['loss'],     label='train_loss')
    axes2[i].plot(h.history['val_loss'], label='val_loss')
    axes2[i].set_title(f'{label}')
    axes2[i].set_xlabel('Epoch'); axes2[i].legend(); axes2[i].grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('dense_layers_overfit.png', dpi=150)
plt.show()

print("\n===== 最終結果サマリー =====")
print(f"{'Pattern':>8} | {'Val Acc':>8} | {'Test Acc':>9} | {'Time(s)':>8} | {'Params':>10}")
print("-" * 55)
for label in ['dense1', 'dense2', 'dense3']:
    val_acc  = histories[label].history['val_accuracy'][-1]
    test_acc = scores[label][1]
    t        = times[label]
    p        = params[label]
    print(f"{label:>8} | {val_acc:>8.4f} | {test_acc:>9.4f} | {t:>8.1f} | {p:>10,}")
print("-" * 55)

最終結果サマリー

===== 最終結果サマリー =====
 Pattern |  Val Acc |  Test Acc |  Time(s) |     Params
-------------------------------------------------------
  dense1 |   0.6682 |    0.6625 |    130.7 |     93,450
  dense2 |   0.6826 |    0.6832 |    124.6 |    101,066
  dense3 |   0.6816 |    0.6760 |    127.0 |    102,826
-------------------------------------------------------
⚠️ ハマりポイント:Dropoutが複数入ると正則化が強くなりすぎる
Dense層を増やすと、各層後のDropoutも増えます。今回はDropout(0.2)を各Dense層の後に入れているため、3層構成では合計3箇所のDropoutが適用されます。これは事実上の強い正則化として機能します。Dense層を増やすときは、Dropoutの配置や率を層数に合わせて見直すことが重要です。

実験結果

精度グラフ

精度グラフ

損失グラフ

損失グラフ

A:Dense 1層

A:Dense 1層

B:Dense 2層

B:Dense 2層

C:Dense 3層

C:Dense 3層
パターン 最終 val_accuracy 最終 test_accuracy パラメータ数 学習時間
A:Dense 1層 66.82% 66.25% 93,450 130.7秒
B:Dense 2層 68.26% 68.32% 101,066 124.6秒
C:Dense 3層 68.16% 67.60% 102,826 127.0秒

考察

① 2層が最高精度、3層目の追加は効果なし

結果を見ると、Dense 2層(68.32%)が最高精度を記録し、3層目を追加しても test_accuracy は 67.60% と逆に低下しました。val_accuracy でも 2層(68.26%)と 3層(68.16%)の差はわずか 0.10% にとどまり、パラメータ増加(+1,760)に見合った恩恵は得られていません。

比較 test_accuracy の変化 パラメータ増加
1層 → 2層 +2.07%(66.25% → 68.32%) +7,616(約8%増)
2層 → 3層 −0.72%(68.32% → 67.60%) +1,760(約2%増)

1層→2層は明確な精度向上があった一方、2層→3層は精度低下に転じており、Dense層の積み重ねには明確な収益逓減が確認できます。

② 3層目が逆効果になった理由:Dropoutの複合効果

Dense 3層構成では Dropout(0.2) が計3箇所に適用されます。各 Dropout 層は学習時にランダムに 20% のニューロンを無効化するため、3箇所では理論上 1−(0.8)³ ≈ 49% の情報が何らかの形で遮断される計算になります。これが過剰な正則化として働き、Dense 3層の表現力向上を打ち消したと考えられます。

Dropout(0.2) を \(n\) 箇所適用したとき、全てのDropoutをくぐり抜ける確率:
\[ P = (1 - 0.2)^n = 0.8^n \] \( n=1 \) では 80%、\( n=2 \) では 64%、\( n=3 \) では 51.2% となります。

③ GAP構成ではDense層数より「ユニット数」が効く

前回の実験(ユニット数比較)では Dense(512) が 70.43% を記録しました。今回の Dense 2層(68.32%)よりも高い精度です。GAPで128次元に圧縮された特徴には、層を増やして変換を重ねるより、1層でより広い空間に展開する方が有効であることが2つの実験を通じて示されました。

④ 学習時間:層数の差は小さい

学習時間は 1層(130.7秒)・2層(124.6秒)・3層(127.0秒)とほぼ同水準で、Dense層の増加による計算コストはGAP使用モデルでは非常に小さいことが確認できます。コスト面での懸念は不要ですが、それでも精度は改善しないという点がこの実験の核心です。


実務での推奨

ユースケース 推奨するDense構成 理由
CIFAR-10程度の小規模データセット 2層(128→64)が最良、1層でも十分 今回の実験でも2層がベスト。3層は過剰正則化のリスクあり
精度をさらに高めたい場合 ユニット数を増やす(例:Dense(256)の1層) 層数より幅を広げる方が効果的
Dense 3層を使う場合 Dropoutを最終Dense層のみに絞る 複合Dropoutによる過剰正則化を防ぐ
推論速度が重要な場合 1層(128ユニット) GAPで十分に集約済み。分類器はシンプルで良い
まとめ
  • Dense 2層(128→64)が最高精度(68.32%)。1層(66.25%)より+2.07%の向上
  • Dense 3層目の追加は逆効果(−0.72%)。GAP構成では収益逓減が明確
  • 3層が2層を下回った主因はDropout(0.2) ×3の複合正則化(情報遮断率が理論上約49%)
  • 精度向上には層数を増やすよりユニット数を増やす方が有効(前回実験:Dense(512)で70.43%)
  • Dense 3層を試す場合はDropoutを最終Dense層だけに置くなどの工夫が必要

関連記事


▶EN English Summary

How does the number of Dense layers affect accuracy?

This article compares one, two, and three Dense layers after GlobalAveragePooling2D on CIFAR-10 using Keras (T4 GPU, 30 epochs, batch size 64). The CNN backbone is fixed; only the Dense head varies. Each Dense layer is followed by Dropout(0.2).

Pattern Dense Head Test Accuracy Params
A: 1 layer Dense(128) 66.25% 93,450
B: 2 layers Dense(128) → Dense(64) 68.32% 101,066
C: 3 layers Dense(128) → Dense(64) → Dense(32) 67.60% 102,826

Key finding: Two Dense layers gave the best result. Adding a third layer slightly hurt accuracy, likely because three Dropout(0.2) layers compound to block ~49% of information during training. For GAP-based CNNs on small datasets, 1–2 Dense layers is the practical sweet spot. If accuracy matters more, increasing the number of units (e.g. Dense(512)) outperforms adding more layers.