MaxPoolingのpool_sizeを変えると精度はどう変わる?Flatten版【Keras実験】

投稿日:2026年4月17日金曜日 最終更新日:

CIFAR-10 CNN Flatten Google Colab Keras MaxPooling 画像分類

X f B! P L
MaxPoolingのpool_sizeを変えると精度はどう変わる?Flatten版【Keras実験】 アイキャッチ画像

前回の記事(GAP版)では、GlobalAveragePooling2D(GAP)を使ったモデルでpool_sizeを比較したところ、GAPがpool_size後の空間サイズの差を吸収してしまうため、pool_sizeの影響を純粋に取り出せていないという限界がありました。

今回はGAPの代わりに Flatten を使います。Flattenはpool_size後の特徴マップをそのまま1次元に展開するため、pool_sizeが違えばDense層への入力次元も変わります。つまりpool_sizeの違いがモデルに直接伝わる構成です。

ただし正直に言うと、この設計にも限界があります。pool_sizeによってFlatten後の次元数が変わるため、パラメータ数も3パターンで揃いません。この点も結果とあわせて開示します。

📘 この記事でわかること

  • Flatten使用モデルでpool_sizeを変えたときの精度・学習時間の変化
  • pool_sizeによってパラメータ数がどれだけ変わるか
  • GAP版の結果と何が違うか
  • この実験の解釈上の注意点

実験設定

使用環境はGoogle Colab(GPU:T4)、データセットはCIFAR-10(32×32×3のカラー画像、10クラス)です。

前回のGAP版から GlobalAveragePooling2D()Flatten() に変えた点のみが異なります。それ以外の条件(Conv2Dのフィルター数・層数・Optimizer・エポック数・バッチサイズ)は全て同一です。

Flatten版でのパラメータ数の変化

Flattenを使うと、pool_size後の特徴マップをそのまま1次元に展開してDense層に渡します。そのためpool_sizeが小さいほど特徴マップが大きく残り、Dense層の入力次元・パラメータ数が大きくなります。

pool_size 2回目Pooling後 Flatten後の次元数 Dense(128)のParam # 総パラメータ数(概算)
(2, 2) 8×8×128 8,192次元 約105万 約111万
(3, 3) 3×3×128 1,152次元 約15万 約21万
(4, 4) 2×2×128 512次元 約6.6万 約13万

pool_size=(2,2) と (4,4) ではパラメータ数が約8倍以上違います。これは「pool_sizeだけの差」ではなく「モデルの複雑さの差」も含んでいるため、精度の違いを解釈するときは注意が必要です。この点は考察で詳しく触れます。


実験コード

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

# ── 環境準備(最初に一度だけ実行)──────────────────────
!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 42 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 1s (7,133 kB/s)
Selecting previously unselected package fonts-ipafont-gothic.
(Reading database ... 122354 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 27.2 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 time

# CIFAR-10データの読み込み・前処理
(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

# モデル構築関数(前回GAP版からFlattenに変更)
def build_model(pool_size, name):
    return keras.Sequential([
        keras.layers.Input(shape=(32, 32, 3)),
        keras.layers.Conv2D(64, (3, 3), activation='relu', padding='same'),
        keras.layers.MaxPooling2D(pool_size=pool_size),
        keras.layers.Conv2D(128, (3, 3), activation='relu', padding='same'),
        keras.layers.MaxPooling2D(pool_size=pool_size),
        keras.layers.Flatten(),              # ← GAP版では GlobalAveragePooling2D()
        keras.layers.Dense(128, activation='relu'),
        keras.layers.Dense(10, activation='softmax'),
    ], 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
    )
    elapsed = time.time() - start
    return history, elapsed
実行結果をクリックして内容を開く
Downloading data from https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz
170498071/170498071 ━━━━━━━━━━━━━━━━━━━━ 6s 0us/step

3パターンの学習実行

print("\n=== Pattern A:pool_size=(2,2) ===")
model_A = build_model((2, 2), 'A_pool2x2')
print(model_A.summary())
history_A, time_A = compile_and_fit(model_A)
print(f"学習時間:{time_A:.1f}秒 パラメータ数:{model_A.count_params():,}")

print("\n=== Pattern B:pool_size=(3,3) ===")
model_B = build_model((3, 3), 'B_pool3x3')
print(model_B.summary())
history_B, time_B = compile_and_fit(model_B)
print(f"学習時間:{time_B:.1f}秒 パラメータ数:{model_B.count_params():,}")

print("\n=== Pattern C:pool_size=(4,4) ===")
model_C = build_model((4, 4), 'C_pool4x4')
print(model_C.summary())
history_C, time_C = compile_and_fit(model_C)
print(f"学習時間:{time_C:.1f}秒 パラメータ数:{model_C.count_params():,}")

# テストデータで最終精度を評価
test_results = {
    'A:pool_size=(2,2)': model_A.evaluate(x_test, y_test, verbose=0),
    'B:pool_size=(3,3)': model_B.evaluate(x_test, y_test, verbose=0),
    'C:pool_size=(4,4)': model_C.evaluate(x_test, y_test, verbose=0),
}
実行結果をクリックして内容を開く
=== Pattern A:pool_size=(2,2) ===
Model: "A_pool2x2"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ 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 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ flatten (Flatten)               │ (None, 8192)           │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense (Dense)                   │ (None, 128)            │     1,048,704 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_1 (Dense)                 │ (None, 10)             │         1,290 │
└─────────────────────────────────┴────────────────────────┴───────────────┘
 Total params: 1,125,642 (4.29 MB)
 Trainable params: 1,125,642 (4.29 MB)
 Non-trainable params: 0 (0.00 B)
None
Epoch 1/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 15s 12ms/step - accuracy: 0.4771 - loss: 1.4596 - val_accuracy: 0.5775 - val_loss: 1.2064
Epoch 2/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 13s 7ms/step - accuracy: 0.6316 - loss: 1.0524 - val_accuracy: 0.6434 - val_loss: 1.0347
Epoch 3/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6833 - loss: 0.9027 - val_accuracy: 0.6742 - val_loss: 0.9517
Epoch 4/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.7220 - loss: 0.7959 - val_accuracy: 0.6857 - val_loss: 0.9166
Epoch 5/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.7588 - loss: 0.6927 - val_accuracy: 0.7159 - val_loss: 0.8459
Epoch 6/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.7870 - loss: 0.6062 - val_accuracy: 0.6901 - val_loss: 0.9354
Epoch 7/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.8168 - loss: 0.5291 - val_accuracy: 0.7082 - val_loss: 0.8993
Epoch 8/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.8430 - loss: 0.4469 - val_accuracy: 0.6997 - val_loss: 0.9713
Epoch 9/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.8697 - loss: 0.3738 - val_accuracy: 0.7117 - val_loss: 0.9771
Epoch 10/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.8918 - loss: 0.3127 - val_accuracy: 0.7046 - val_loss: 1.0478
Epoch 11/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.9110 - loss: 0.2515 - val_accuracy: 0.7004 - val_loss: 1.1724
Epoch 12/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.9302 - loss: 0.2031 - val_accuracy: 0.7083 - val_loss: 1.2363
Epoch 13/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.9456 - loss: 0.1615 - val_accuracy: 0.6926 - val_loss: 1.3793
Epoch 14/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.9538 - loss: 0.1363 - val_accuracy: 0.6961 - val_loss: 1.5411
Epoch 15/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.9593 - loss: 0.1198 - val_accuracy: 0.6882 - val_loss: 1.6788
Epoch 16/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.9664 - loss: 0.0995 - val_accuracy: 0.7017 - val_loss: 1.6912
Epoch 17/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.9670 - loss: 0.0945 - val_accuracy: 0.6972 - val_loss: 1.7012
Epoch 18/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.9678 - loss: 0.0918 - val_accuracy: 0.6830 - val_loss: 1.8846
Epoch 19/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.9702 - loss: 0.0864 - val_accuracy: 0.6978 - val_loss: 1.9688
Epoch 20/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.9789 - loss: 0.0636 - val_accuracy: 0.6941 - val_loss: 1.9146
Epoch 21/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.9757 - loss: 0.0685 - val_accuracy: 0.6897 - val_loss: 2.1915
Epoch 22/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.9772 - loss: 0.0699 - val_accuracy: 0.6879 - val_loss: 2.1279
Epoch 23/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.9800 - loss: 0.0583 - val_accuracy: 0.6986 - val_loss: 2.2176
Epoch 24/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.9774 - loss: 0.0684 - val_accuracy: 0.6942 - val_loss: 2.2281
Epoch 25/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.9858 - loss: 0.0413 - val_accuracy: 0.6894 - val_loss: 2.3346
Epoch 26/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.9789 - loss: 0.0636 - val_accuracy: 0.6893 - val_loss: 2.3062
Epoch 27/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.9808 - loss: 0.0561 - val_accuracy: 0.6945 - val_loss: 2.3821
Epoch 28/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.9804 - loss: 0.0582 - val_accuracy: 0.6882 - val_loss: 2.4359
Epoch 29/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.9807 - loss: 0.0579 - val_accuracy: 0.6922 - val_loss: 2.5573
Epoch 30/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.9842 - loss: 0.0476 - val_accuracy: 0.6962 - val_loss: 2.5927
学習時間:147.8秒 パラメータ数:1,125,642

=== Pattern B:pool_size=(3,3) ===
Model: "B_pool3x3"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Layer (type)                    ┃ Output Shape           ┃       Param # ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ conv2d_2 (Conv2D)               │ (None, 32, 32, 64)     │         1,792 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ max_pooling2d_2 (MaxPooling2D)  │ (None, 10, 10, 64)     │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ conv2d_3 (Conv2D)               │ (None, 10, 10, 128)    │        73,856 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ max_pooling2d_3 (MaxPooling2D)  │ (None, 3, 3, 128)      │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ flatten_1 (Flatten)             │ (None, 1152)           │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_2 (Dense)                 │ (None, 128)            │       147,584 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_3 (Dense)                 │ (None, 10)             │         1,290 │
└─────────────────────────────────┴────────────────────────┴───────────────┘
 Total params: 224,522 (877.04 KB)
 Trainable params: 224,522 (877.04 KB)
 Non-trainable params: 0 (0.00 B)
None
Epoch 1/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 8s 9ms/step - accuracy: 0.4331 - loss: 1.5735 - val_accuracy: 0.5519 - val_loss: 1.2732
Epoch 2/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.5859 - loss: 1.1794 - val_accuracy: 0.6102 - val_loss: 1.1321
Epoch 3/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.6370 - loss: 1.0352 - val_accuracy: 0.6576 - val_loss: 1.0038
Epoch 4/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.6742 - loss: 0.9358 - val_accuracy: 0.6629 - val_loss: 0.9585
Epoch 5/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.7014 - loss: 0.8660 - val_accuracy: 0.6879 - val_loss: 0.9095
Epoch 6/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 7s 5ms/step - accuracy: 0.7224 - loss: 0.8036 - val_accuracy: 0.6796 - val_loss: 0.9426
Epoch 7/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.7376 - loss: 0.7523 - val_accuracy: 0.6929 - val_loss: 0.9069
Epoch 8/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.7543 - loss: 0.7007 - val_accuracy: 0.7008 - val_loss: 0.8754
Epoch 9/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.7699 - loss: 0.6610 - val_accuracy: 0.7252 - val_loss: 0.8295
Epoch 10/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 6ms/step - accuracy: 0.7821 - loss: 0.6294 - val_accuracy: 0.7104 - val_loss: 0.8634
Epoch 11/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.7927 - loss: 0.5891 - val_accuracy: 0.7242 - val_loss: 0.8188
Epoch 12/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.8066 - loss: 0.5587 - val_accuracy: 0.7207 - val_loss: 0.8465
Epoch 13/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.8162 - loss: 0.5239 - val_accuracy: 0.7226 - val_loss: 0.8561
Epoch 14/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 5ms/step - accuracy: 0.8259 - loss: 0.4958 - val_accuracy: 0.7318 - val_loss: 0.8417
Epoch 15/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.8351 - loss: 0.4705 - val_accuracy: 0.7272 - val_loss: 0.8544
Epoch 16/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.8474 - loss: 0.4356 - val_accuracy: 0.7237 - val_loss: 0.9064
Epoch 17/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.8531 - loss: 0.4131 - val_accuracy: 0.7289 - val_loss: 0.8667
Epoch 18/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.8605 - loss: 0.3954 - val_accuracy: 0.7245 - val_loss: 0.9286
Epoch 19/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.8718 - loss: 0.3623 - val_accuracy: 0.7222 - val_loss: 0.9760
Epoch 20/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.8784 - loss: 0.3463 - val_accuracy: 0.7208 - val_loss: 0.9572
Epoch 21/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.8891 - loss: 0.3180 - val_accuracy: 0.7168 - val_loss: 1.0241
Epoch 22/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.8949 - loss: 0.2954 - val_accuracy: 0.7213 - val_loss: 1.0664
Epoch 23/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.9042 - loss: 0.2768 - val_accuracy: 0.7191 - val_loss: 1.0957
Epoch 24/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.9046 - loss: 0.2658 - val_accuracy: 0.7205 - val_loss: 1.0809
Epoch 25/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 6ms/step - accuracy: 0.9166 - loss: 0.2411 - val_accuracy: 0.7147 - val_loss: 1.1201
Epoch 26/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.9198 - loss: 0.2263 - val_accuracy: 0.7169 - val_loss: 1.1803
Epoch 27/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.9249 - loss: 0.2106 - val_accuracy: 0.7231 - val_loss: 1.2194
Epoch 28/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.9330 - loss: 0.1905 - val_accuracy: 0.7150 - val_loss: 1.2605
Epoch 29/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.9330 - loss: 0.1893 - val_accuracy: 0.7157 - val_loss: 1.3603
Epoch 30/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.9416 - loss: 0.1689 - val_accuracy: 0.7211 - val_loss: 1.3325
学習時間:112.3秒 パラメータ数:224,522

=== Pattern C:pool_size=(4,4) ===
Model: "C_pool4x4"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Layer (type)                    ┃ Output Shape           ┃       Param # ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ conv2d_4 (Conv2D)               │ (None, 32, 32, 64)     │         1,792 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ max_pooling2d_4 (MaxPooling2D)  │ (None, 8, 8, 64)       │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ conv2d_5 (Conv2D)               │ (None, 8, 8, 128)      │        73,856 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ max_pooling2d_5 (MaxPooling2D)  │ (None, 2, 2, 128)      │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ flatten_2 (Flatten)             │ (None, 512)            │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_4 (Dense)                 │ (None, 128)            │        65,664 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_5 (Dense)                 │ (None, 10)             │         1,290 │
└─────────────────────────────────┴────────────────────────┴───────────────┘
 Total params: 142,602 (557.04 KB)
 Trainable params: 142,602 (557.04 KB)
 Non-trainable params: 0 (0.00 B)
None
Epoch 1/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 7s 7ms/step - accuracy: 0.4211 - loss: 1.5909 - val_accuracy: 0.5488 - val_loss: 1.2788
Epoch 2/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 7s 5ms/step - accuracy: 0.5778 - loss: 1.1896 - val_accuracy: 0.6028 - val_loss: 1.1183
Epoch 3/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.6330 - loss: 1.0477 - val_accuracy: 0.6273 - val_loss: 1.0520
Epoch 4/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.6677 - loss: 0.9458 - val_accuracy: 0.6597 - val_loss: 0.9922
Epoch 5/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 5ms/step - accuracy: 0.6932 - loss: 0.8778 - val_accuracy: 0.6789 - val_loss: 0.9276
Epoch 6/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.7172 - loss: 0.8146 - val_accuracy: 0.6903 - val_loss: 0.9132
Epoch 7/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.7285 - loss: 0.7709 - val_accuracy: 0.7023 - val_loss: 0.8635
Epoch 8/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.7437 - loss: 0.7313 - val_accuracy: 0.7036 - val_loss: 0.8683
Epoch 9/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.7574 - loss: 0.6951 - val_accuracy: 0.7062 - val_loss: 0.8642
Epoch 10/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.7682 - loss: 0.6600 - val_accuracy: 0.7149 - val_loss: 0.8502
Epoch 11/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.7791 - loss: 0.6289 - val_accuracy: 0.7192 - val_loss: 0.8495
Epoch 12/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.7887 - loss: 0.5984 - val_accuracy: 0.7151 - val_loss: 0.8628
Epoch 13/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.7965 - loss: 0.5776 - val_accuracy: 0.7242 - val_loss: 0.8296
Epoch 14/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.8061 - loss: 0.5465 - val_accuracy: 0.7122 - val_loss: 0.8666
Epoch 15/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.8175 - loss: 0.5175 - val_accuracy: 0.7293 - val_loss: 0.8314
Epoch 16/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.8266 - loss: 0.4912 - val_accuracy: 0.7243 - val_loss: 0.8542
Epoch 17/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.8289 - loss: 0.4782 - val_accuracy: 0.7070 - val_loss: 0.9276
Epoch 18/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.8386 - loss: 0.4509 - val_accuracy: 0.7237 - val_loss: 0.8675
Epoch 19/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.8482 - loss: 0.4288 - val_accuracy: 0.7227 - val_loss: 0.8870
Epoch 20/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.8544 - loss: 0.4114 - val_accuracy: 0.7305 - val_loss: 0.8985
Epoch 21/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.8631 - loss: 0.3881 - val_accuracy: 0.7192 - val_loss: 0.9428
Epoch 22/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.8705 - loss: 0.3703 - val_accuracy: 0.7270 - val_loss: 0.9368
Epoch 23/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.8803 - loss: 0.3457 - val_accuracy: 0.7271 - val_loss: 0.9908
Epoch 24/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.8823 - loss: 0.3297 - val_accuracy: 0.7364 - val_loss: 0.9448
Epoch 25/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.8843 - loss: 0.3196 - val_accuracy: 0.7198 - val_loss: 1.0415
Epoch 26/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.8933 - loss: 0.2987 - val_accuracy: 0.7236 - val_loss: 1.0470
Epoch 27/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 5ms/step - accuracy: 0.9005 - loss: 0.2820 - val_accuracy: 0.7151 - val_loss: 1.1043
Epoch 28/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.9035 - loss: 0.2717 - val_accuracy: 0.7319 - val_loss: 1.0829
Epoch 29/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.9102 - loss: 0.2507 - val_accuracy: 0.7314 - val_loss: 1.0652
Epoch 30/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.9153 - loss: 0.2403 - val_accuracy: 0.7249 - val_loss: 1.1518
学習時間:101.3秒 パラメータ数:142,602

グラフ+サマリー

histories = {
    'A:pool_size=(2,2)': history_A,
    'B:pool_size=(3,3)': history_B,
    'C:pool_size=(4,4)': history_C,
}

# ── val_accuracy / val_loss 比較グラフ ───────────────
fig, axes = plt.subplots(1, 2, figsize=(12, 4))
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('poolsize_flatten_comparison.png', dpi=150)
plt.show()

# ── train_loss vs val_loss(過学習の乖離を見る)────────
fig2, axes2 = plt.subplots(3, 1, figsize=(6, 12))
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('poolsize_flatten_overfit.png', dpi=150)
plt.show()

# ── 最終結果サマリー ─────────────────────────────────
key_order   = ['A:pool_size=(2,2)', 'B:pool_size=(3,3)', 'C:pool_size=(4,4)']
time_list   = {'A:pool_size=(2,2)': time_A, 'B:pool_size=(3,3)': time_B, 'C:pool_size=(4,4)': time_C}
params_list = {
    'A:pool_size=(2,2)': model_A.count_params(),
    'B:pool_size=(3,3)': model_B.count_params(),
    'C:pool_size=(4,4)': model_C.count_params(),
}

print("\n===== 最終結果サマリー =====")
print(f"{'Pattern':>22} | {'Val Acc':>8} | {'Test Acc':>9} | {'Time(s)':>8} | {'Params':>12}")
print("-" * 72)
for key in key_order:
    val_acc = histories[key].history['val_accuracy'][-1]
    test_loss, test_acc = test_results[key]
    elapsed = time_list[key]
    params  = params_list[key]
    print(f"{key:>22} | {val_acc:>8.4f} | {test_acc:>9.4f} | {elapsed:>8.1f} | {params:>12,}")
print("-" * 72)

実行結果

===== 最終結果サマリー =====
               Pattern |  Val Acc |  Test Acc |  Time(s) |       Params
------------------------------------------------------------------------
     A:pool_size=(2,2) |   0.6962 |    0.6874 |    147.8 |    1,125,642
     B:pool_size=(3,3) |   0.7211 |    0.7133 |    112.3 |      224,522
     C:pool_size=(4,4) |   0.7249 |    0.7118 |    101.3 |      142,602
------------------------------------------------------------------------

実験結果

精度グラフ

精度グラフ

損失グラフ

損失グラフ

pool_size=(2,2)

pool_size=(3,3)

pool_size=(4,4)

結果サマリ

パターン 最終 val_accuracy 最終 test_accuracy 総パラメータ数 学習時間
A:pool_size=(2,2) 69.62% 68.74% 約111万 147.8秒
B:pool_size=(3,3) 72.11% 71.33% 約21万 112.3秒
C:pool_size=(4,4) 72.49% 71.18% 約13万 101.3秒

考察

① Flatten版でpool_sizeの違いはどう現れたか

GAP版では pool_size=(4,4) が最高精度(72.30%)でしたが、Flatten版では (3,3) と (4,4) がほぼ同精度(71.33% vs 71.18%)で並び、(2,2) が最下位(68.74%) という結果になりました。

パラメータ数が最も多い(2,2)が最下位というのは直感に反しますが、これは後述の過学習の影響が大きく効いています。Flattenを使う構成でpool_sizeを小さくすると、Dense層のパラメータが爆発的に増えてかえって精度が下がるという、実用上重要な知見です。

② パラメータ数の差をどう読むか

Flatten版では pool_size=(2,2) のパラメータ数が約111万と、(4,4) の約13万の8倍以上あります。それにもかかわらず精度は(2,2)が最も低い結果になりました。これは「パラメータ数が多ければ精度が上がる」という単純な関係が成り立たないことを示しています。

一方 pool_size=(4,4) はパラメータ数が最も少ない(約13万)にもかかわらず、(3,3)(約21万)とほぼ同精度を達成しています。パラメータ数が少ない分だけ過学習が抑えられ、効率よく学習できたと考えられます。

精度の差を「pool_sizeだけの効果」と断言するためには、パラメータ数を揃えた別の実験が本来は必要です。ただし今回はその制約を明示した上で結果を観察する実験として位置づけています。

③ train_loss vs val_loss から過学習を確認する

学習ログから各パターンの過学習の状態をまとめると以下の通りです。

パターン val_loss が底を打ったエポック Ep30 train_loss Ep30 val_loss 状態
A:pool_size=(2,2) Ep5(0.846) 0.048 2.593 深刻な過学習
B:pool_size=(3,3) Ep11(0.819) 0.169 1.333 過学習進行中
C:pool_size=(4,4) まだ底打ちしていない 0.240 1.152 収束途中

pool_size=(2,2) はEp5の時点でval_lossが底(0.846)を打ち、その後はひたすら上昇しています。train_lossが0.048まで下がっているのにval_lossが2.593まで膨らむという深刻な過学習状態です。111万パラメータが訓練データに過剰適合した結果です。

pool_size=(4,4) はEp30時点でもtrain_lossが0.240とまだ下降余地があります。val_lossも底打ちしておらず、エポック数を50〜100に増やせばさらに精度が上がる可能性があります。パラメータ数が少ない分だけ収束が遅い一方、過学習が起きにくい構成になっています。

GAP版との比較まとめ

pool_size GAP版 Test Acc GAP版パラメータ数 Flatten版 Test Acc Flatten版パラメータ数
(2,2) 67.80% 93,450(共通) 68.74% 約111万
(3,3) 69.52% 93,450(共通) 71.33% 約21万
(4,4) 72.30% 93,450(共通) 71.18% 約13万

GAP版は3パターン全てパラメータ数が93,450で揃っているのに対し、Flatten版はpool_sizeによって大きく異なります。どちらの実験がより「フェア」かは解釈次第ですが、2つの実験を並べることで集約方法の違いがpool_sizeの効果にどう影響するかが見えてきます。

まとめ

  • Flatten版では pool_size=(3,3) と (4,4) がほぼ同精度で並び、パラメータ数が最多の(2,2)が最下位 という予想外の結果に
  • pool_size=(2,2) はEp5時点でval_lossが底を打ち、その後深刻な過学習に陥った(train_loss: 0.048 vs val_loss: 2.593)
  • pool_size=(4,4) はEp30時点でもまだ収束途中(train_loss: 0.240)。エポック数を増やせばさらに改善余地あり
  • Flattenを使う構成ではpool_sizeを小さくするとDense層のパラメータが爆発的に増え、過学習が精度向上を打ち消すことがある
  • GAP版(パラメータ数均一)とFlatten版(パラメータ数不均一)を並べることで、集約方法の選択がモデル設計に与える影響の大きさが改めて浮かび上がる

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