KerasでCNNを書くとき、MaxPooling2Dの pool_size はなんとなく (2, 2) にしていませんか?
「pool_sizeを大きくすると空間情報が失われて精度が下がるはず」——今回の実験を始める前、私もそう思っていました。
ところが実際に動かしてみると、pool_sizeが大きいほど精度が高く、学習時間も短いという予想外の結果になりました。その原因を探っていくと、今回のモデルで使っていた GlobalAveragePooling2D(GAP)の挙動が鍵でした。
📘 この記事でわかること
- GAP使用モデルでpool_sizeを変えたときの精度・学習時間の変化
- なぜGAPを使うとpool_sizeの影響が小さくなるのか
- pool_sizeの選び方はGAP/Flattenどちらを使うかで変わること
実験設定
使用環境はGoogle Colab(GPU:T4)、データセットはCIFAR-10(32×32×3のカラー画像、10クラス)です。
pool_size以外の条件(Conv2Dのフィルター数・層数・Optimizer・エポック数・バッチサイズ)は全て同一にして、pool_sizeの影響だけを観察します。モデルの最終集約層には GlobalAveragePooling2D(GAP)を使っています。
pool_sizeによる特徴マップサイズの変化(入力32×32の場合):
| pool_size | 1回目のPooling後 | 2回目のPooling後 | GAP後の次元数 |
|---|---|---|---|
| (2, 2) | 16×16 | 8×8 | 全パターン共通:128次元 |
| (3, 3) | 10×10 | 3×3 | |
| (4, 4) | 8×8 | 2×2 |
ここが重要なポイントです。 pool_sizeが異なっても、GAPを通過した後はすべて 128次元のベクトル になります。この時点でDense層への入力は完全に揃っています。
※ pool_size=(3,3) のstridesはデフォルトでpool_sizeと同じ(3,3)になります。32÷3=10余り2 で端が切り捨てられ10×10になります。
実験コード
環境準備(最初に一度だけ実行)
# ── 環境準備(最初に一度だけ実行)──────────────────────
!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 2s (4,623 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 41.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
# モデル構築関数(pool_sizeを引数で切り替え)
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.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 ━━━━━━━━━━━━━━━━━━━━ 4s 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 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ global_average_pooling2d │ (None, 128) │ 0 │ │ (GlobalAveragePooling2D) │ │ │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dense (Dense) │ (None, 128) │ 16,512 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ 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 ━━━━━━━━━━━━━━━━━━━━ 13s 10ms/step - accuracy: 0.2754 - loss: 1.9032 - val_accuracy: 0.3508 - val_loss: 1.7013 Epoch 2/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.3980 - loss: 1.6292 - val_accuracy: 0.4259 - val_loss: 1.5462 Epoch 3/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.4483 - loss: 1.5058 - val_accuracy: 0.4652 - val_loss: 1.4658 Epoch 4/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.4855 - loss: 1.4145 - val_accuracy: 0.4894 - val_loss: 1.4294 Epoch 5/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5099 - loss: 1.3502 - val_accuracy: 0.5083 - val_loss: 1.3523 Epoch 6/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5282 - loss: 1.2988 - val_accuracy: 0.5261 - val_loss: 1.2898 Epoch 7/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5410 - loss: 1.2653 - val_accuracy: 0.5367 - val_loss: 1.2872 Epoch 8/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5551 - loss: 1.2282 - val_accuracy: 0.5571 - val_loss: 1.2080 Epoch 9/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5679 - loss: 1.1961 - val_accuracy: 0.5669 - val_loss: 1.1924 Epoch 10/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5795 - loss: 1.1688 - val_accuracy: 0.5866 - val_loss: 1.1524 Epoch 11/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5874 - loss: 1.1421 - val_accuracy: 0.5552 - val_loss: 1.2374 Epoch 12/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5995 - loss: 1.1158 - val_accuracy: 0.5925 - val_loss: 1.1216 Epoch 13/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6054 - loss: 1.0980 - val_accuracy: 0.5915 - val_loss: 1.1317 Epoch 14/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.6139 - loss: 1.0720 - val_accuracy: 0.6148 - val_loss: 1.0766 Epoch 15/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6192 - loss: 1.0535 - val_accuracy: 0.6136 - val_loss: 1.0759 Epoch 16/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6285 - loss: 1.0340 - val_accuracy: 0.6123 - val_loss: 1.0878 Epoch 17/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 6ms/step - accuracy: 0.6362 - loss: 1.0158 - val_accuracy: 0.6024 - val_loss: 1.0955 Epoch 18/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6404 - loss: 0.9963 - val_accuracy: 0.6441 - val_loss: 1.0024 Epoch 19/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6485 - loss: 0.9797 - val_accuracy: 0.6285 - val_loss: 1.0236 Epoch 20/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6545 - loss: 0.9674 - val_accuracy: 0.6425 - val_loss: 1.0060 Epoch 21/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6596 - loss: 0.9499 - val_accuracy: 0.6462 - val_loss: 0.9905 Epoch 22/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6642 - loss: 0.9416 - val_accuracy: 0.6478 - val_loss: 0.9850 Epoch 23/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 6ms/step - accuracy: 0.6715 - loss: 0.9189 - val_accuracy: 0.6574 - val_loss: 0.9647 Epoch 24/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6763 - loss: 0.9096 - val_accuracy: 0.6546 - val_loss: 0.9646 Epoch 25/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.6783 - loss: 0.8979 - val_accuracy: 0.6602 - val_loss: 0.9519 Epoch 26/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6836 - loss: 0.8830 - val_accuracy: 0.6646 - val_loss: 0.9471 Epoch 27/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6889 - loss: 0.8701 - val_accuracy: 0.6736 - val_loss: 0.9152 Epoch 28/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6929 - loss: 0.8569 - val_accuracy: 0.6807 - val_loss: 0.8993 Epoch 29/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6989 - loss: 0.8464 - val_accuracy: 0.6743 - val_loss: 0.9143 Epoch 30/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.7001 - loss: 0.8373 - val_accuracy: 0.6800 - val_loss: 0.9080 学習時間:128.4秒 パラメータ数:93,450 === 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 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ global_average_pooling2d_1 │ (None, 128) │ 0 │ │ (GlobalAveragePooling2D) │ │ │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dense_2 (Dense) │ (None, 128) │ 16,512 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dense_3 (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 ━━━━━━━━━━━━━━━━━━━━ 7s 8ms/step - accuracy: 0.3261 - loss: 1.8095 - val_accuracy: 0.4172 - val_loss: 1.5821 Epoch 2/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.4447 - loss: 1.5130 - val_accuracy: 0.4837 - val_loss: 1.4189 Epoch 3/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 5ms/step - accuracy: 0.5027 - loss: 1.3759 - val_accuracy: 0.5142 - val_loss: 1.3455 Epoch 4/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.5368 - loss: 1.2925 - val_accuracy: 0.5312 - val_loss: 1.3112 Epoch 5/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.5600 - loss: 1.2260 - val_accuracy: 0.5748 - val_loss: 1.1974 Epoch 6/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.5814 - loss: 1.1719 - val_accuracy: 0.5769 - val_loss: 1.1897 Epoch 7/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.5966 - loss: 1.1304 - val_accuracy: 0.6067 - val_loss: 1.1148 Epoch 8/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.6092 - loss: 1.0947 - val_accuracy: 0.5689 - val_loss: 1.2110 Epoch 9/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.6237 - loss: 1.0563 - val_accuracy: 0.6085 - val_loss: 1.0860 Epoch 10/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.6330 - loss: 1.0284 - val_accuracy: 0.6252 - val_loss: 1.0613 Epoch 11/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.6471 - loss: 0.9976 - val_accuracy: 0.6226 - val_loss: 1.0480 Epoch 12/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.6570 - loss: 0.9669 - val_accuracy: 0.6459 - val_loss: 1.0024 Epoch 13/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.6649 - loss: 0.9469 - val_accuracy: 0.6568 - val_loss: 0.9692 Epoch 14/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 5ms/step - accuracy: 0.6753 - loss: 0.9180 - val_accuracy: 0.6179 - val_loss: 1.0709 Epoch 15/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.6795 - loss: 0.9024 - val_accuracy: 0.6490 - val_loss: 0.9898 Epoch 16/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.6901 - loss: 0.8813 - val_accuracy: 0.6382 - val_loss: 1.0171 Epoch 17/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.6975 - loss: 0.8595 - val_accuracy: 0.6422 - val_loss: 1.0168 Epoch 18/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.7019 - loss: 0.8471 - val_accuracy: 0.6642 - val_loss: 0.9555 Epoch 19/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 5ms/step - accuracy: 0.7100 - loss: 0.8208 - val_accuracy: 0.6690 - val_loss: 0.9530 Epoch 20/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.7150 - loss: 0.8132 - val_accuracy: 0.6842 - val_loss: 0.9039 Epoch 21/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.7218 - loss: 0.7926 - val_accuracy: 0.6752 - val_loss: 0.9329 Epoch 22/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.7224 - loss: 0.7778 - val_accuracy: 0.6846 - val_loss: 0.9128 Epoch 23/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.7312 - loss: 0.7653 - val_accuracy: 0.6854 - val_loss: 0.9147 Epoch 24/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.7355 - loss: 0.7522 - val_accuracy: 0.6959 - val_loss: 0.8767 Epoch 25/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.7386 - loss: 0.7395 - val_accuracy: 0.6923 - val_loss: 0.8910 Epoch 26/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.7430 - loss: 0.7227 - val_accuracy: 0.6991 - val_loss: 0.8720 Epoch 27/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.7465 - loss: 0.7154 - val_accuracy: 0.6900 - val_loss: 0.9084 Epoch 28/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.7506 - loss: 0.7035 - val_accuracy: 0.6931 - val_loss: 0.8929 Epoch 29/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.7587 - loss: 0.6878 - val_accuracy: 0.7070 - val_loss: 0.8442 Epoch 30/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.7574 - loss: 0.6839 - val_accuracy: 0.7016 - val_loss: 0.8715 学習時間:103.8秒 パラメータ数:93,450 === 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 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ global_average_pooling2d_2 │ (None, 128) │ 0 │ │ (GlobalAveragePooling2D) │ │ │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dense_4 (Dense) │ (None, 128) │ 16,512 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dense_5 (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 ━━━━━━━━━━━━━━━━━━━━ 7s 6ms/step - accuracy: 0.3523 - loss: 1.7521 - val_accuracy: 0.4241 - val_loss: 1.5548 Epoch 2/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.4915 - loss: 1.4009 - val_accuracy: 0.5257 - val_loss: 1.3107 Epoch 3/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.5471 - loss: 1.2546 - val_accuracy: 0.5449 - val_loss: 1.2546 Epoch 4/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.5830 - loss: 1.1639 - val_accuracy: 0.5901 - val_loss: 1.1428 Epoch 5/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.6063 - loss: 1.0992 - val_accuracy: 0.6009 - val_loss: 1.1121 Epoch 6/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.6311 - loss: 1.0404 - val_accuracy: 0.6163 - val_loss: 1.0783 Epoch 7/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.6478 - loss: 0.9915 - val_accuracy: 0.6413 - val_loss: 1.0188 Epoch 8/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.6602 - loss: 0.9581 - val_accuracy: 0.6215 - val_loss: 1.0618 Epoch 9/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.6748 - loss: 0.9202 - val_accuracy: 0.6600 - val_loss: 0.9911 Epoch 10/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.6885 - loss: 0.8822 - val_accuracy: 0.6810 - val_loss: 0.9123 Epoch 11/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.6978 - loss: 0.8594 - val_accuracy: 0.6801 - val_loss: 0.9184 Epoch 12/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.7074 - loss: 0.8302 - val_accuracy: 0.6786 - val_loss: 0.9188 Epoch 13/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.7152 - loss: 0.8083 - val_accuracy: 0.6673 - val_loss: 0.9752 Epoch 14/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.7262 - loss: 0.7846 - val_accuracy: 0.6922 - val_loss: 0.8877 Epoch 15/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.7304 - loss: 0.7620 - val_accuracy: 0.6968 - val_loss: 0.8694 Epoch 16/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.7398 - loss: 0.7409 - val_accuracy: 0.7066 - val_loss: 0.8601 Epoch 17/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.7463 - loss: 0.7199 - val_accuracy: 0.7097 - val_loss: 0.8472 Epoch 18/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.7539 - loss: 0.7035 - val_accuracy: 0.7006 - val_loss: 0.8870 Epoch 19/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.7587 - loss: 0.6869 - val_accuracy: 0.6943 - val_loss: 0.8831 Epoch 20/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.7629 - loss: 0.6741 - val_accuracy: 0.6955 - val_loss: 0.8977 Epoch 21/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.7675 - loss: 0.6550 - val_accuracy: 0.6925 - val_loss: 0.9150 Epoch 22/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.7736 - loss: 0.6425 - val_accuracy: 0.7178 - val_loss: 0.8426 Epoch 23/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.7804 - loss: 0.6234 - val_accuracy: 0.7255 - val_loss: 0.8109 Epoch 24/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.7836 - loss: 0.6126 - val_accuracy: 0.7087 - val_loss: 0.8676 Epoch 25/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.7890 - loss: 0.5983 - val_accuracy: 0.7183 - val_loss: 0.8382 Epoch 26/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.7931 - loss: 0.5900 - val_accuracy: 0.7132 - val_loss: 0.8502 Epoch 27/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.8009 - loss: 0.5692 - val_accuracy: 0.7200 - val_loss: 0.8295 Epoch 28/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.8017 - loss: 0.5607 - val_accuracy: 0.7252 - val_loss: 0.8370 Epoch 29/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.8054 - loss: 0.5535 - val_accuracy: 0.7261 - val_loss: 0.8269 Epoch 30/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.8121 - loss: 0.5278 - val_accuracy: 0.7351 - val_loss: 0.8147 学習時間:91.7秒 パラメータ数:93,450
グラフ(val_accuracy + val_loss 比較)+ サマリー
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=(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('poolsize_gap_comparison.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}
print("\n===== 最終結果サマリー =====")
print(f"{'Pattern':>22} | {'Val Acc':>8} | {'Test Acc':>9} | {'Time(s)':>8} | {'2nd Map Size':>12}")
print("-" * 75)
map_sizes = {'A:pool_size=(2,2)': '8×8', 'B:pool_size=(3,3)': '3×3', 'C:pool_size=(4,4)': '2×2'}
for key in key_order:
val_acc = histories[key].history['val_accuracy'][-1]
test_loss, test_acc = test_results[key]
elapsed = time_list[key]
mapsize = map_sizes[key]
print(f"{key:>22} | {val_acc:>8.4f} | {test_acc:>9.4f} | {elapsed:>8.1f} | {mapsize:>12}")
print("-" * 75)
実行結果
===== 最終結果サマリー =====
Pattern | Val Acc | Test Acc | Time(s) | 2nd Map Size
---------------------------------------------------------------------------
A:pool_size=(2,2) | 0.6800 | 0.6780 | 128.4 | 8×8
B:pool_size=(3,3) | 0.7016 | 0.6952 | 103.8 | 3×3
C:pool_size=(4,4) | 0.7351 | 0.7230 | 91.7 | 2×2
---------------------------------------------------------------------------
実験結果
精度グラフ
損失グラフ
サマリ
| パターン | 最終 val_accuracy | 最終 test_accuracy | 2回目Pooling後のマップ | 学習時間 |
|---|---|---|---|---|
| A:pool_size=(2,2) | 68.00% | 67.80% | 8×8 | 128.4秒 |
| B:pool_size=(3,3) | 70.16% | 69.52% | 3×3 | 103.8秒 |
| C:pool_size=(4,4) | 73.51% | 72.30% | 2×2 | 91.7秒 |
pool_sizeが大きいほど精度が高く、学習時間も短いという、予想に反する結果になりました。
考察:GAPがpool_sizeの影響を吸収する
① GAPを通るとpool_size後のサイズ差が消える
今回の結果を理解する鍵は GlobalAveragePooling2D(GAP)の動作にあります。
GAPは特徴マップ全体をチャンネルあたり1つの平均値に集約します。そのため、2回目Pooling後の特徴マップが 8×8×128 であっても 2×2×128 であっても、GAPを通過した後は同じ 128次元のベクトル になります。
つまり pool_size=(2,2) で8×8の特徴マップを丁寧に残しても、GAPで平均化して潰してしまうため「空間情報を保持するメリット」がDense層には届きません。このモデル構成では、pool_sizeの違いが精度に直結しにくい設計になっていました。
② 強い圧縮が正則化として機能した
pool_sizeを大きくすると特徴マップが強く圧縮されます。この圧縮が一種の正則化効果として働き、過学習を抑えた可能性があります。学習時間が短い(= 1エポックあたりの計算量が少ない)ことからも、モデルがよりシンプルな表現を学習できていたことが示唆されます。
③ この実験はGAP使用モデルでの「挙動観察」として読む
今回の実験は「pool_sizeの純粋な比較」としては設計に限界があります。GAPがpool_sizeの空間サイズ差を吸収するため、pool_sizeの影響を単独で取り出す実験にはなっていないからです。
ただし「GAPを使うモデルではpool_sizeを大きくしても精度が落ちない、むしろ正則化効果で上がる場合もある」という実用的な知見は得られました。
pool_sizeの効果をより純粋に比較したい場合は、GAPの代わりにFlattenを使い、かつパラメータ数が揃うよう設計を工夫した実験が必要です。その実験については別記事で行います。
まとめ
今回はGAP使用CNNモデルでpool_sizeを2×2・3×3・4×4の3パターンで比較しました。
- pool_size=(4,4) が最高精度(72.30%)・最短学習時間(91.7秒) と予想に反する逆転結果に
- 原因は GAPがPooling後の空間サイズの差を吸収する ため。pool_sizeが何であっても、GAP後は同じ128次元ベクトルになる
- さらに強い圧縮による 正則化効果 が(4,4)の精度を後押しした可能性がある
- GAP使用モデルではpool_sizeの選択が精度に与える影響は限定的。それよりもフィルター数・Dropout・学習率を先に調整する方が効果的
- pool_sizeの効果を純粋に比較するには、パラメータ数が揃う設計での実験が必要(続編で検証予定)
関連記事もあわせてどうぞ:
- pool_size純粋比較(続編)→ 近日公開予定
- MaxPooling vs AveragePooling の比較 →
【Keras】最大値プーリング(MaxPooling)vs 平均値プーリング(AveragePooling)の違いを実験で比較 - FlattenとGAPの比較実験 →
Global Average Pooling vs Flatten|CNNの最終層、どっちが精度・速度で有利か?【Keras実験】 - Conv2Dのフィルター数の選び方 →
Conv2DのFilters数(32 vs 64 vs 128)を変えると精度はどう変わる?【CIFAR-10実験】 - model.summary()の読み方 →
Kerasのmodel.summary()の読み方を徹底解説|パラメータ数の計算方法【初心者向け】



0 件のコメント:
コメントを投稿