Conv2Dのpaddingで精度は変わる?(same vs valid)【Keras×CIFAR-10実験】

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

CIFAR-10 CNN Conv2D Google Colab Keras padding 画像分類

X f B! P L
アイキャッチ画像 Conv2Dのpaddingで精度は変わる?(same vs valid)【Keras×CIFAR-10実験】

KerasでConv2Dを書くとき、padding='same'padding='valid' のどちらを使うか意識していますか?

「とりあえずsameにしている」という方も多いと思いますが、実際に精度・特徴マップサイズ・パラメータ数にどんな違いが出るのか、CIFAR-10で実験して比較しました。

📘 この記事でわかること

  • same と valid でそれぞれ特徴マップサイズがどう変わるか
  • CIFAR-10(低解像度画像)での精度・学習時間の差
  • paddingの選び方の基準

same と valid の違い

padding はConv2Dが入力の端をどう扱うかを決めます。

padding 動作 出力サイズ(kernel=3×3, stride=1)
same 入力の周囲にゼロを追加し、出力サイズを入力と同じに保つ 入力と同じ(例:32×32 → 32×32)
valid パディングなし。カーネルが収まる範囲のみ畳み込む 入力より小さくなる(例:32×32 → 30×30)

valid では畳み込みのたびに特徴マップが小さくなります。CIFAR-10のように元の解像度が32×32と低い場合、層を重ねるごとに情報が急速に失われる可能性があります。

特徴マップサイズの変化(CIFAR-10・kernel=3×3の場合)

same のサイズ valid のサイズ
入力 32×32 32×32
Conv2D(1回目)後 32×32 30×30
MaxPooling後 16×16 15×15
Conv2D(2回目)後 16×16 13×13
MaxPooling後 8×8 6×6

実験コード

使用環境はGoogle Colab(GPU:T4)、データセットはCIFAR-10です。padding以外の条件は全て同一にして、paddingの影響だけを取り出します。

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

# ── 環境準備(最初に一度だけ実行)──────────────────────
!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,006 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 32.1 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(padding, name):
    return keras.Sequential([
        keras.layers.Input(shape=(32, 32, 3)),
        keras.layers.Conv2D(64, (3, 3), activation='relu', padding=padding),
        keras.layers.MaxPooling2D((2, 2)),
        keras.layers.Conv2D(128, (3, 3), activation='relu', padding=padding),
        keras.layers.MaxPooling2D((2, 2)),
        keras.layers.GlobalAveragePooling2D(),
        keras.layers.Dense(128, activation='relu'),
        keras.layers.Dropout(0.2),
        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)
    return history, time.time() - start
実行結果をクリックして内容を開く
Downloading data from https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz
170498071/170498071 ━━━━━━━━━━━━━━━━━━━━ 6s 0us/step

2パターンの学習実行

results = {}

for padding, name in [('same', 'A_same'), ('valid', 'B_valid')]:
    print(f"\n=== {name} ===")
    model = build_model(padding, name)
    print(model.summary())
    h, t = compile_and_fit(model)
    s = model.evaluate(x_test, y_test, verbose=0)
    results[name] = {'history': h, 'time': t, 'score': s,
                     'params': model.count_params()}
    print(f"学習時間:{t:.1f}秒 パラメータ数:{model.count_params():,} test_accuracy:{s[1]:.4f}")
実行結果をクリックして内容を開く
=== A_same ===
Model: "A_same"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ 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 ━━━━━━━━━━━━━━━━━━━━ 18s 14ms/step - accuracy: 0.2695 - loss: 1.9178 - val_accuracy: 0.3521 - val_loss: 1.7118
Epoch 2/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 10s 6ms/step - accuracy: 0.3769 - loss: 1.6657 - val_accuracy: 0.4342 - val_loss: 1.5519
Epoch 3/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 6ms/step - accuracy: 0.4240 - loss: 1.5560 - val_accuracy: 0.4476 - val_loss: 1.4884
Epoch 4/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.4620 - loss: 1.4637 - val_accuracy: 0.4884 - val_loss: 1.3945
Epoch 5/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.4814 - loss: 1.4095 - val_accuracy: 0.5116 - val_loss: 1.3415
Epoch 6/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5031 - loss: 1.3565 - val_accuracy: 0.5245 - val_loss: 1.3035
Epoch 7/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5195 - loss: 1.3182 - val_accuracy: 0.5316 - val_loss: 1.2758
Epoch 8/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5345 - loss: 1.2805 - val_accuracy: 0.5524 - val_loss: 1.2337
Epoch 9/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5429 - loss: 1.2487 - val_accuracy: 0.5589 - val_loss: 1.1953
Epoch 10/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5602 - loss: 1.2165 - val_accuracy: 0.5547 - val_loss: 1.2026
Epoch 11/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5671 - loss: 1.1938 - val_accuracy: 0.5649 - val_loss: 1.1798
Epoch 12/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5781 - loss: 1.1668 - val_accuracy: 0.5808 - val_loss: 1.1386
Epoch 13/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5850 - loss: 1.1449 - val_accuracy: 0.5993 - val_loss: 1.1053
Epoch 14/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5939 - loss: 1.1216 - val_accuracy: 0.6048 - val_loss: 1.0837
Epoch 15/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5996 - loss: 1.1098 - val_accuracy: 0.5965 - val_loss: 1.0972
Epoch 16/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6097 - loss: 1.0864 - val_accuracy: 0.6123 - val_loss: 1.0742
Epoch 17/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6160 - loss: 1.0662 - val_accuracy: 0.6242 - val_loss: 1.0392
Epoch 18/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6198 - loss: 1.0501 - val_accuracy: 0.6066 - val_loss: 1.0792
Epoch 19/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.6276 - loss: 1.0398 - val_accuracy: 0.6269 - val_loss: 1.0237
Epoch 20/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6335 - loss: 1.0225 - val_accuracy: 0.6326 - val_loss: 1.0104
Epoch 21/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 6ms/step - accuracy: 0.6373 - loss: 1.0087 - val_accuracy: 0.6361 - val_loss: 1.0012
Epoch 22/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6425 - loss: 0.9930 - val_accuracy: 0.6437 - val_loss: 0.9895
Epoch 23/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 6ms/step - accuracy: 0.6519 - loss: 0.9747 - val_accuracy: 0.6470 - val_loss: 0.9772
Epoch 24/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.6537 - loss: 0.9635 - val_accuracy: 0.6482 - val_loss: 0.9712
Epoch 25/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 8s 13ms/step - accuracy: 0.6586 - loss: 0.9531 - val_accuracy: 0.6232 - val_loss: 1.0330
Epoch 26/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 7ms/step - accuracy: 0.6617 - loss: 0.9421 - val_accuracy: 0.6659 - val_loss: 0.9301
Epoch 27/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.6685 - loss: 0.9288 - val_accuracy: 0.6527 - val_loss: 0.9631
Epoch 28/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 7ms/step - accuracy: 0.6700 - loss: 0.9199 - val_accuracy: 0.6580 - val_loss: 0.9451
Epoch 29/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 7ms/step - accuracy: 0.6774 - loss: 0.9035 - val_accuracy: 0.6555 - val_loss: 0.9610
Epoch 30/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6798 - loss: 0.8930 - val_accuracy: 0.6717 - val_loss: 0.9190
学習時間:145.3秒 パラメータ数:93,450 test_accuracy:0.6691

=== B_valid ===
Model: "B_valid"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Layer (type)                    ┃ Output Shape           ┃       Param # ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ conv2d_2 (Conv2D)               │ (None, 30, 30, 64)     │         1,792 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ max_pooling2d_2 (MaxPooling2D)  │ (None, 15, 15, 64)     │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ conv2d_3 (Conv2D)               │ (None, 13, 13, 128)    │        73,856 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ max_pooling2d_3 (MaxPooling2D)  │ (None, 6, 6, 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, 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 ━━━━━━━━━━━━━━━━━━━━ 8s 8ms/step - accuracy: 0.2736 - loss: 1.8819 - val_accuracy: 0.3384 - val_loss: 1.7135
Epoch 2/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 7s 5ms/step - accuracy: 0.3766 - loss: 1.6477 - val_accuracy: 0.4285 - val_loss: 1.5407
Epoch 3/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.4405 - loss: 1.5176 - val_accuracy: 0.4800 - val_loss: 1.4326
Epoch 4/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.4749 - loss: 1.4378 - val_accuracy: 0.4787 - val_loss: 1.4253
Epoch 5/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.4975 - loss: 1.3874 - val_accuracy: 0.5253 - val_loss: 1.3249
Epoch 6/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.5142 - loss: 1.3434 - val_accuracy: 0.5296 - val_loss: 1.2950
Epoch 7/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5270 - loss: 1.3081 - val_accuracy: 0.5389 - val_loss: 1.2688
Epoch 8/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.5405 - loss: 1.2785 - val_accuracy: 0.5578 - val_loss: 1.2266
Epoch 9/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.5517 - loss: 1.2510 - val_accuracy: 0.5667 - val_loss: 1.2163
Epoch 10/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5603 - loss: 1.2250 - val_accuracy: 0.5630 - val_loss: 1.2181
Epoch 11/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5654 - loss: 1.2054 - val_accuracy: 0.5747 - val_loss: 1.1794
Epoch 12/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.5758 - loss: 1.1880 - val_accuracy: 0.5760 - val_loss: 1.1611
Epoch 13/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.5832 - loss: 1.1614 - val_accuracy: 0.5661 - val_loss: 1.1896
Epoch 14/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5906 - loss: 1.1388 - val_accuracy: 0.5932 - val_loss: 1.1172
Epoch 15/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 5ms/step - accuracy: 0.6024 - loss: 1.1139 - val_accuracy: 0.6072 - val_loss: 1.0928
Epoch 16/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.6067 - loss: 1.1021 - val_accuracy: 0.6060 - val_loss: 1.0882
Epoch 17/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6096 - loss: 1.0909 - val_accuracy: 0.6095 - val_loss: 1.0838
Epoch 18/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6173 - loss: 1.0716 - val_accuracy: 0.6276 - val_loss: 1.0414
Epoch 19/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.6201 - loss: 1.0575 - val_accuracy: 0.6284 - val_loss: 1.0342
Epoch 20/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 8s 6ms/step - accuracy: 0.6321 - loss: 1.0361 - val_accuracy: 0.6378 - val_loss: 1.0223
Epoch 21/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.6362 - loss: 1.0224 - val_accuracy: 0.6277 - val_loss: 1.0359
Epoch 22/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6401 - loss: 1.0068 - val_accuracy: 0.6420 - val_loss: 1.0092
Epoch 23/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6447 - loss: 0.9933 - val_accuracy: 0.6301 - val_loss: 1.0310
Epoch 24/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 6ms/step - accuracy: 0.6511 - loss: 0.9817 - val_accuracy: 0.6481 - val_loss: 0.9782
Epoch 25/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6567 - loss: 0.9636 - val_accuracy: 0.6592 - val_loss: 0.9605
Epoch 26/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6596 - loss: 0.9524 - val_accuracy: 0.6542 - val_loss: 0.9592
Epoch 27/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 6ms/step - accuracy: 0.6691 - loss: 0.9376 - val_accuracy: 0.6625 - val_loss: 0.9503
Epoch 28/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6697 - loss: 0.9272 - val_accuracy: 0.6494 - val_loss: 0.9691
Epoch 29/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6772 - loss: 0.9162 - val_accuracy: 0.6615 - val_loss: 0.9561
Epoch 30/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6792 - loss: 0.9060 - val_accuracy: 0.6728 - val_loss: 0.9307
学習時間:124.4秒 パラメータ数:93,450 test_accuracy:0.6626

グラフ+サマリー

fig, axes = plt.subplots(1, 2, figsize=(14, 5))
for name, r in results.items():
    axes[0].plot(r['history'].history['val_accuracy'], label=name)
    axes[1].plot(r['history'].history['val_loss'],     label=name)
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('padding_comparison.png', dpi=150)
plt.show()

print("\n===== 最終結果サマリー =====")
print(f"{'Pattern':>8} | {'Val Acc':>8} | {'Test Acc':>9} | {'Time(s)':>8} | {'Params':>12}")
print("-" * 58)
for name, r in results.items():
    val_acc  = r['history'].history['val_accuracy'][-1]
    test_acc = r['score'][1]
    print(f"{name:>8} | {val_acc:>8.4f} | {test_acc:>9.4f} | {r['time']:>8.1f} | {r['params']:>12,}")
print("-" * 58)

最終結果サマリー

===== 最終結果サマリー =====
 Pattern |  Val Acc |  Test Acc |  Time(s) |       Params
----------------------------------------------------------
  A_same |   0.6717 |    0.6691 |    145.3 |       93,450
 B_valid |   0.6728 |    0.6626 |    124.4 |       93,450
----------------------------------------------------------

実験結果

精度グラフ

精度グラフ

損失グラフ

損失グラフ
パターン 最終 val_accuracy 最終 test_accuracy パラメータ数 学習時間
A:padding='same' 67.17% 66.91% 93,450 145.3秒
B:padding='valid' 67.28% 66.26% 93,450 124.4秒

考察

① パラメータ数は同じ——GAPがサイズ差を吸収した

model.summary()で確認すると、AとBのパラメータ数は両方とも93,450で完全に同じです。

理由はGlobalAveragePooling2D(GAP)にあります。GAPはその時点の特徴マップをチャンネルあたり1つの平均値に集約するため、same(8×8×128)でも valid(6×6×128)でも、GAP後は同じ128次元ベクトルになります。そのためDense層への入力次元が変わらず、パラメータ数に差が出ませんでした。

なお、FlattenをGAPの代わりに使った場合はこの限りではありません。Flattenは特徴マップをそのまま展開するため、valid(6×6×128=4,608次元)と same(8×8×128=8,192次元)でDense層のパラメータ数が大きく変わります。

② 精度差は誤差の範囲——どちらが「勝った」とは言いにくい

精度の差を整理すると以下の通りです。

指標 A:same B:valid
val_accuracy 67.17% 67.28% 0.11%(valid優位)
test_accuracy 66.91% 66.26% 0.65%(same優位)
学習時間 145.3秒 124.4秒 20.9秒(valid優位)

val_accuracyではBが0.11%上、test_accuracyではAが0.65%上という指標によって勝敗が逆転する結果になりました。差も最大0.65%と非常に小さく、実質的に誤差の範囲内です。

この結果は「paddingの違いがGAP使用モデルでは精度にほぼ影響しない」ことを示しています。①で述べたようにGAPがサイズ差を吸収するため、same で8×8の特徴マップを丁寧に保持しても、valid で6×6に縮小しても、Dense層への入力の質に大きな差が生じなかったと考えられます。

③ 学習時間の差はvalidが有利

valid はパディングの計算が不要で特徴マップも小さいため、1エポックあたりの計算量が少なくなります。今回は約21秒(約14%)の短縮となりました。精度がほぼ同じであれば、計算コストの観点からは valid に一定のメリットがあります。

ただしこれはGAP使用モデル限定の話です。Flattenを使う場合、valid では特徴マップが小さくなることで情報損失が精度に直接影響するため、same を選ぶ理由が強くなります。


まとめ

  • GAP使用モデルでは samevalidパラメータ数は同じ(GAPがサイズ差を吸収するため)
  • 精度差は最大0.65%と誤差の範囲内。GAP使用モデルではpaddingが精度にほぼ影響しないことが確認できた
  • 学習時間は valid が約21秒(約14%)短い。精度が同等なら計算コスト面で valid に一定のメリットがある
  • Flattenを使う場合は話が変わるvalid では特徴マップ縮小がDense層のパラメータ数と精度に直接影響するため same が有利になりやすい
  • 迷ったら padding='same' を基本にするのが安全。GAP使用モデルでは大きな差は出ないが、後でFlattenに変えたときのリスクを避けられる

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