CNNで「なんとなくMaxPoolingを使っている」という方は多いのではないでしょうか。
MaxPoolingはエッジや輝点などの「強い反応」を残し、AveragePoolingは領域全体の「平均的な特徴」を保持します。この違いが精度や過学習にどう影響するか、実験で確かめます。
なお、最終層のGlobal Average Pooling vs Global Max Poolingの比較は → GlobalAveragePooling vs GlobalMaxPooling の比較【Keras×CIFAR-10実験】 をご覧ください。本記事は中間層のPoolingに絞った比較です。
また、MNISTを使った基礎的な比較は → 【Keras】最大値プーリング vs 平均値プーリングの違いを実験で比較 で解説しています。本記事ではCIFAR-10(カラー自然画像)を使い、MNISTとは逆の結果になった理由まで掘り下げます。
- MaxPooling2DとAveragePooling2Dの動作の違い(何を残し何を捨てるか)
- 中間層でのPooling種類による精度・過学習耐性の差(CIFAR-10実験)
- MaxPooling・AveragePooling・両者の組み合わせ(Mix)の3パターン比較
- MNISTではAvgが有利だったのに、CIFAR-10ではMaxが約4ポイント優位になった理由
MaxPoolingとAveragePoolingの違い
どちらも特徴マップを空間的に縮小(ダウンサンプリング)するレイヤーですが、縮小の方法が異なります。
| 種類 | 操作 | 残す情報 | 向いているタスク |
|---|---|---|---|
| MaxPooling2D | ウィンドウ内の最大値を取る | 最も強い反応(エッジ・輝点) | 物体検出・テクスチャ識別 |
| AveragePooling2D | ウィンドウ内の平均値を取る | 領域全体の平均的な特徴 | 滑らかな特徴・背景含む分類 |
直感的には「MaxPoolingの方が強い特徴を残すから精度が高い」と思われがちですが、タスクによっては平均情報の方が有効なこともあります。今回はCIFAR-10で3パターンを比較します。
実験設計
比較するのは以下の3パターンです。Pool種別以外(Conv2Dフィルター数、Dropout、最適化関数、エポック数)はすべて統一します。
| パターン | 1回目Pooling | 2回目Pooling |
|---|---|---|
| A: Max | MaxPooling2D | MaxPooling2D |
| B: Avg | AveragePooling2D | AveragePooling2D |
| C: Mix(Max→Avg) | MaxPooling2D | AveragePooling2D |
MixパターンはEfficientNetなど実際のアーキテクチャで採用されている設計で、前段で鋭い特徴を抽出し、後段で滑らかにまとめるという考え方です。
実験コード
使用環境はGoogle Colab(GPU:T4)、データセットはCIFAR-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 51 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 (24.6 MB/s)
Selecting previously unselected package fonts-ipafont-gothic.
(Reading database ... 122363 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 77.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
(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
def build_model(pool1_type, pool2_type, name):
"""
pool_type: 'max' or 'avg'
中間層のPooling種別だけを変え、他の条件は統一する
"""
def pool_layer(pool_type):
if pool_type == 'max':
return keras.layers.MaxPooling2D((2, 2))
else:
return keras.layers.AveragePooling2D((2, 2))
model = keras.Sequential([
keras.layers.Input(shape=(32, 32, 3)),
keras.layers.Conv2D(64, (3, 3), activation='relu', padding='same'),
pool_layer(pool1_type), # ← ここを変える(1回目)
keras.layers.Conv2D(128, (3, 3), activation='relu', padding='same'),
pool_layer(pool2_type), # ← ここを変える(2回目)
keras.layers.GlobalAveragePooling2D(),
keras.layers.Dense(128, activation='relu'),
keras.layers.Dropout(0.2),
keras.layers.Dense(10, activation='softmax'),
], name=name)
return model
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 ━━━━━━━━━━━━━━━━━━━━ 20s 0us/step
3パターンの学習実行
configs = [
('max', 'max', 'A_MaxMax'),
('avg', 'avg', 'B_AvgAvg'),
('max', 'avg', 'C_MaxAvg'),
]
histories, times, scores, params = {}, {}, {}, {}
for pool1, pool2, name in configs:
print(f"\n=== {name} ===")
model = build_model(pool1, pool2, name)
model.summary()
h, t = compile_and_fit(model)
s = model.evaluate(x_test, y_test, verbose=0)
label = name
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_MaxMax === Model: "A_MaxMax" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ 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) Epoch 1/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 10s 8ms/step - accuracy: 0.2726 - loss: 1.9051 - val_accuracy: 0.3662 - val_loss: 1.6983 Epoch 2/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.3800 - loss: 1.6635 - val_accuracy: 0.4248 - val_loss: 1.5717 Epoch 3/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.4341 - loss: 1.5407 - val_accuracy: 0.4618 - val_loss: 1.4751 Epoch 4/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.4680 - loss: 1.4511 - val_accuracy: 0.4791 - val_loss: 1.4270 Epoch 5/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 7ms/step - accuracy: 0.4876 - loss: 1.3934 - val_accuracy: 0.5108 - val_loss: 1.3464 Epoch 6/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5119 - loss: 1.3340 - val_accuracy: 0.5171 - val_loss: 1.3153 Epoch 7/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5237 - loss: 1.2979 - val_accuracy: 0.5446 - val_loss: 1.2484 Epoch 8/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5356 - loss: 1.2689 - val_accuracy: 0.5445 - val_loss: 1.2518 Epoch 9/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5494 - loss: 1.2349 - val_accuracy: 0.5646 - val_loss: 1.2026 Epoch 10/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5624 - loss: 1.2112 - val_accuracy: 0.5738 - val_loss: 1.1862 Epoch 11/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5699 - loss: 1.1868 - val_accuracy: 0.5700 - val_loss: 1.1919 Epoch 12/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.5799 - loss: 1.1586 - val_accuracy: 0.5842 - val_loss: 1.1342 Epoch 13/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5885 - loss: 1.1406 - val_accuracy: 0.5867 - val_loss: 1.1250 Epoch 14/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5930 - loss: 1.1222 - val_accuracy: 0.5819 - val_loss: 1.1634 Epoch 15/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6036 - loss: 1.0975 - val_accuracy: 0.6046 - val_loss: 1.0980 Epoch 16/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6122 - loss: 1.0802 - val_accuracy: 0.6247 - val_loss: 1.0477 Epoch 17/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6181 - loss: 1.0601 - val_accuracy: 0.6282 - val_loss: 1.0349 Epoch 18/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6255 - loss: 1.0424 - val_accuracy: 0.6242 - val_loss: 1.0430 Epoch 19/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6312 - loss: 1.0297 - val_accuracy: 0.6372 - val_loss: 1.0050 Epoch 20/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6342 - loss: 1.0114 - val_accuracy: 0.6500 - val_loss: 0.9880 Epoch 21/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6414 - loss: 0.9968 - val_accuracy: 0.6393 - val_loss: 0.9957 Epoch 22/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.6462 - loss: 0.9866 - val_accuracy: 0.6417 - val_loss: 0.9924 Epoch 23/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6517 - loss: 0.9684 - val_accuracy: 0.6442 - val_loss: 1.0061 Epoch 24/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6558 - loss: 0.9586 - val_accuracy: 0.6614 - val_loss: 0.9427 Epoch 25/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.6646 - loss: 0.9407 - val_accuracy: 0.6647 - val_loss: 0.9461 Epoch 26/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6663 - loss: 0.9321 - val_accuracy: 0.6707 - val_loss: 0.9273 Epoch 27/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6676 - loss: 0.9258 - val_accuracy: 0.6644 - val_loss: 0.9334 Epoch 28/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6715 - loss: 0.9124 - val_accuracy: 0.6680 - val_loss: 0.9390 Epoch 29/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.6780 - loss: 0.9003 - val_accuracy: 0.6717 - val_loss: 0.9251 Epoch 30/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6836 - loss: 0.8849 - val_accuracy: 0.6798 - val_loss: 0.9012 学習時間:124.7秒 パラメータ数:93,450 test_accuracy:0.6747 === B_AvgAvg === Model: "B_AvgAvg" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Layer (type) ┃ Output Shape ┃ Param # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ conv2d_2 (Conv2D) │ (None, 32, 32, 64) │ 1,792 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ average_pooling2d │ (None, 16, 16, 64) │ 0 │ │ (AveragePooling2D) │ │ │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ conv2d_3 (Conv2D) │ (None, 16, 16, 128) │ 73,856 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ average_pooling2d_1 │ (None, 8, 8, 128) │ 0 │ │ (AveragePooling2D) │ │ │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ 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, 10) │ 1,290 │ └─────────────────────────────────┴────────────────────────┴───────────────┘ Total params: 93,450 (365.04 KB) Trainable params: 93,450 (365.04 KB) Non-trainable params: 0 (0.00 B) Epoch 1/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 7s 7ms/step - accuracy: 0.2435 - loss: 1.9774 - val_accuracy: 0.3170 - val_loss: 1.7838 Epoch 2/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.3367 - loss: 1.7450 - val_accuracy: 0.3732 - val_loss: 1.6669 Epoch 3/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.3787 - loss: 1.6539 - val_accuracy: 0.4102 - val_loss: 1.5744 Epoch 4/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.4081 - loss: 1.5814 - val_accuracy: 0.4313 - val_loss: 1.5285 Epoch 5/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.4382 - loss: 1.5227 - val_accuracy: 0.4603 - val_loss: 1.4761 Epoch 6/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.4600 - loss: 1.4707 - val_accuracy: 0.4809 - val_loss: 1.4121 Epoch 7/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.4740 - loss: 1.4247 - val_accuracy: 0.4609 - val_loss: 1.4670 Epoch 8/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.4904 - loss: 1.3980 - val_accuracy: 0.5049 - val_loss: 1.3402 Epoch 9/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.4968 - loss: 1.3678 - val_accuracy: 0.5161 - val_loss: 1.3212 Epoch 10/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.5099 - loss: 1.3433 - val_accuracy: 0.5119 - val_loss: 1.3140 Epoch 11/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.5197 - loss: 1.3149 - val_accuracy: 0.5289 - val_loss: 1.2927 Epoch 12/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 5ms/step - accuracy: 0.5264 - loss: 1.3002 - val_accuracy: 0.5225 - val_loss: 1.2914 Epoch 13/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.5329 - loss: 1.2828 - val_accuracy: 0.5382 - val_loss: 1.2622 Epoch 14/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.5371 - loss: 1.2650 - val_accuracy: 0.5441 - val_loss: 1.2346 Epoch 15/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.5479 - loss: 1.2483 - val_accuracy: 0.5515 - val_loss: 1.2215 Epoch 16/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.5549 - loss: 1.2270 - val_accuracy: 0.5539 - val_loss: 1.2066 Epoch 17/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.5619 - loss: 1.2119 - val_accuracy: 0.5740 - val_loss: 1.1687 Epoch 18/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.5656 - loss: 1.2009 - val_accuracy: 0.5795 - val_loss: 1.1539 Epoch 19/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 6ms/step - accuracy: 0.5726 - loss: 1.1799 - val_accuracy: 0.5706 - val_loss: 1.1774 Epoch 20/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.5753 - loss: 1.1669 - val_accuracy: 0.5816 - val_loss: 1.1557 Epoch 21/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.5816 - loss: 1.1548 - val_accuracy: 0.5835 - val_loss: 1.1323 Epoch 22/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.5904 - loss: 1.1364 - val_accuracy: 0.6055 - val_loss: 1.0913 Epoch 23/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 6ms/step - accuracy: 0.5953 - loss: 1.1247 - val_accuracy: 0.6011 - val_loss: 1.1000 Epoch 24/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.5994 - loss: 1.1115 - val_accuracy: 0.5875 - val_loss: 1.1192 Epoch 25/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.6041 - loss: 1.1004 - val_accuracy: 0.5963 - val_loss: 1.1039 Epoch 26/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.6071 - loss: 1.0882 - val_accuracy: 0.6112 - val_loss: 1.0679 Epoch 27/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6079 - loss: 1.0771 - val_accuracy: 0.6086 - val_loss: 1.0625 Epoch 28/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.6173 - loss: 1.0645 - val_accuracy: 0.6193 - val_loss: 1.0454 Epoch 29/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.6217 - loss: 1.0538 - val_accuracy: 0.6103 - val_loss: 1.0718 Epoch 30/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.6243 - loss: 1.0398 - val_accuracy: 0.6265 - val_loss: 1.0340 学習時間:100.0秒 パラメータ数:93,450 test_accuracy:0.6330 === C_MaxAvg === Model: "C_MaxAvg" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Layer (type) ┃ Output Shape ┃ Param # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ conv2d_4 (Conv2D) │ (None, 32, 32, 64) │ 1,792 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d_2 (MaxPooling2D) │ (None, 16, 16, 64) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ conv2d_5 (Conv2D) │ (None, 16, 16, 128) │ 73,856 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ average_pooling2d_2 │ (None, 8, 8, 128) │ 0 │ │ (AveragePooling2D) │ │ │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ global_average_pooling2d_2 │ (None, 128) │ 0 │ │ (GlobalAveragePooling2D) │ │ │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dense_4 (Dense) │ (None, 128) │ 16,512 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dropout_2 (Dropout) │ (None, 128) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ 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) Epoch 1/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 7s 7ms/step - accuracy: 0.2544 - loss: 1.9429 - val_accuracy: 0.3267 - val_loss: 1.7442 Epoch 2/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.3417 - loss: 1.7235 - val_accuracy: 0.3870 - val_loss: 1.6365 Epoch 3/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.3922 - loss: 1.6201 - val_accuracy: 0.4345 - val_loss: 1.5325 Epoch 4/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.4304 - loss: 1.5400 - val_accuracy: 0.4644 - val_loss: 1.4632 Epoch 5/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.4663 - loss: 1.4594 - val_accuracy: 0.4883 - val_loss: 1.3876 Epoch 6/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.4874 - loss: 1.4001 - val_accuracy: 0.4829 - val_loss: 1.3786 Epoch 7/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 6ms/step - accuracy: 0.5002 - loss: 1.3643 - val_accuracy: 0.5159 - val_loss: 1.3176 Epoch 8/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.5153 - loss: 1.3302 - val_accuracy: 0.5403 - val_loss: 1.2553 Epoch 9/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5253 - loss: 1.3028 - val_accuracy: 0.5288 - val_loss: 1.2509 Epoch 10/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.5409 - loss: 1.2659 - val_accuracy: 0.5637 - val_loss: 1.2059 Epoch 11/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5491 - loss: 1.2430 - val_accuracy: 0.5621 - val_loss: 1.2135 Epoch 12/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5580 - loss: 1.2195 - val_accuracy: 0.5530 - val_loss: 1.2092 Epoch 13/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5631 - loss: 1.2003 - val_accuracy: 0.5751 - val_loss: 1.1738 Epoch 14/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5735 - loss: 1.1762 - val_accuracy: 0.5920 - val_loss: 1.1235 Epoch 15/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.5782 - loss: 1.1614 - val_accuracy: 0.5955 - val_loss: 1.1134 Epoch 16/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5860 - loss: 1.1397 - val_accuracy: 0.6032 - val_loss: 1.1147 Epoch 17/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 6ms/step - accuracy: 0.5905 - loss: 1.1292 - val_accuracy: 0.6047 - val_loss: 1.0877 Epoch 18/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.5982 - loss: 1.1119 - val_accuracy: 0.6067 - val_loss: 1.0863 Epoch 19/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6058 - loss: 1.0929 - val_accuracy: 0.6188 - val_loss: 1.0446 Epoch 20/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6101 - loss: 1.0809 - val_accuracy: 0.6197 - val_loss: 1.0496 Epoch 21/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.6170 - loss: 1.0636 - val_accuracy: 0.5958 - val_loss: 1.0900 Epoch 22/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6207 - loss: 1.0499 - val_accuracy: 0.6386 - val_loss: 1.0064 Epoch 23/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6245 - loss: 1.0452 - val_accuracy: 0.6266 - val_loss: 1.0249 Epoch 24/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.6273 - loss: 1.0304 - val_accuracy: 0.6325 - val_loss: 1.0235 Epoch 25/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6341 - loss: 1.0182 - val_accuracy: 0.6435 - val_loss: 0.9884 Epoch 26/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6388 - loss: 1.0043 - val_accuracy: 0.6322 - val_loss: 0.9977 Epoch 27/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.6415 - loss: 0.9960 - val_accuracy: 0.6487 - val_loss: 0.9749 Epoch 28/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 6ms/step - accuracy: 0.6455 - loss: 0.9874 - val_accuracy: 0.6391 - val_loss: 1.0003 Epoch 29/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6478 - loss: 0.9746 - val_accuracy: 0.6495 - val_loss: 0.9700 Epoch 30/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6520 - loss: 0.9632 - val_accuracy: 0.6486 - val_loss: 0.9743 学習時間:114.1秒 パラメータ数:93,450 test_accuracy:0.6436
グラフ+サマリー
# ── 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('pooling_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(label)
axes2[i].set_xlabel('Epoch'); axes2[i].legend(); axes2[i].grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('pooling_overfit.png', dpi=150)
plt.show()
print("\n===== 最終結果サマリー =====")
print(f"{'Pattern':>12} | {'Val Acc':>8} | {'Test Acc':>9} | {'Time(s)':>8} | {'Params':>12}")
print("-" * 62)
for label in ['A_MaxMax', 'B_AvgAvg', 'C_MaxAvg']:
val_acc = histories[label].history['val_accuracy'][-1]
test_acc = scores[label][1]
t = times[label]
p = params[label]
print(f"{label:>12} | {val_acc:>8.4f} | {test_acc:>9.4f} | {t:>8.1f} | {p:>12,}")
print("-" * 62)
最終結果サマリー
===== 最終結果サマリー =====
Pattern | Val Acc | Test Acc | Time(s) | Params
--------------------------------------------------------------
A_MaxMax | 0.6798 | 0.6747 | 124.7 | 93,450
B_AvgAvg | 0.6265 | 0.6330 | 100.0 | 93,450
C_MaxAvg | 0.6486 | 0.6436 | 114.1 | 93,450
--------------------------------------------------------------
実験結果
精度グラフ
損失グラフ
MaxPooling2D x MaxPooling2D
AveragePooling2D x AveragePooling2D
MaxPooling2D x AveragePooling2D
| パターン | 最終 val_accuracy | 最終 test_accuracy | パラメータ数 | 学習時間 |
|---|---|---|---|---|
| A: MaxMax | 67.98% | 67.47% | 93,450 | 124.7秒 |
| B: AvgAvg | 62.65% | 63.30% | 93,450 | 100.0秒 |
| C: MaxAvg(Mix) | 64.86% | 64.36% | 93,450 | 114.1秒 |
3パターンのパラメータ数はすべて同一(93,450)です。Poolingレイヤー自体に学習パラメータはなく、違いは特徴の取捨選択だけです。
考察
① MaxMaxが明確に優位——精度差は約4ポイント
MaxMax(test_accuracy 67.47%)はAvgAvg(63.30%)を約4.2ポイント上回りました。MixパターンはMaxMaxには届かず(64.36%)、MaxMaxが最も高い精度となっています。
CIFAR-10は「飛行機・犬・馬・自動車」など物体の輪郭やテクスチャが分類の決め手になる自然画像データセットです。MaxPoolingがエッジや局所的な強い反応を保持することで、後段のConv層により豊かな特徴が渡され、分類に有利に働いたと考えられます。
② MNISTとは逆の結果になった理由
以前の実験(MNIST版)ではAveragePoolingがわずかにMaxPoolingを上回っていました(val_accuracy: Avg 98.90% vs Max 98.65%)。今回のCIFAR-10では逆転し、Maxが約4ポイント優位という結果になっています。この違いはデータの性質から説明できます。
| データセット | 画像の特性 | 有利なPooling | 理由 |
|---|---|---|---|
| MNIST | 白背景に黒い線画・単純な輪郭 | AveragePooling | 線画は背景との境界が明確で、輪郭の保持より全体の形状把握が重要。平均化により余分なノイズも抑制される |
| CIFAR-10 | 複雑な自然画像・テクスチャ・背景あり | MaxPooling | 物体の輪郭・毛並み・タイヤのエッジなど「局所的に強い反応」が分類の決め手。最大値保持が有効 |
つまり「MaxとAvgどちらが良いか」の答えはデータの複雑さと特徴の性質に依存します。シンプルな線画はAvg、自然画像はMaxが傾向として優位です。
③ Mix(Max→Avg)はなぜMaxMaxに届かなかったか
MixパターンはMaxMaxとAvgAvgの中間(64.36%)に落ち着きました。前段でMaxPoolingにより強い特徴を保持しても、後段のAveragePoolingでその強い反応が平滑化されてしまうためと考えられます。CIFAR-10のような自然画像では、強い特徴をできるだけ最後まで保持する方が精度に有利なようです。
④ 学習時間の差に注目
AvgAvgの学習時間(100.0秒)はMaxMaxより約25秒短くなっています。AveragePoolingは加算と除算という単純な演算のみで、MaxPoolingの「比較+最大値選択」より計算コストが低いためです。精度を多少犠牲にしてでも学習速度を優先したい場面では、AveragePoolingも選択肢になります。
- CIFAR-10(自然画像)ではMaxMaxがAvgAvgを約4.2ポイント上回った(test_accuracy: 67.47% vs 63.30%)
- MNISTではAvgが有利だったが、CIFAR-10では逆転——データの複雑さと特徴の性質がPooling選択に影響する
- Mix(Max→Avg)はMaxMaxに届かず中間の結果(64.36%)——自然画像では強い特徴を最後まで保持する方が有利
- 3パターンのパラメータ数はすべて同一(93,450)——違いは学習パラメータではなく「何の情報を渡すか」だけ
- AvgAvgはMaxMaxより約25秒学習が速い——精度 vs 速度のトレードオフとして覚えておく価値がある
- 判断基準:線画・単純な形状 → Avg、自然画像・テクスチャが重要 → Max が目安
関連記事
- MNIST版(基礎編) → 【Keras】最大値プーリング vs 平均値プーリングの違いを実験で比較
- 最終層のGAP vs GMP → GlobalAveragePooling vs GlobalMaxPooling の比較【Keras×CIFAR-10実験】
- GAP vs Flatten の比較 → Global Average Pooling vs Flatten|CNNの最終層、どっちが精度・速度で有利か?【Keras実験】
- Residual接続あり vs なし → Residual接続(スキップ接続)あり vs なし の比較【Keras×CIFAR-10実験】






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