前回の記事(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版(パラメータ数不均一)を並べることで、集約方法の選択がモデル設計に与える影響の大きさが改めて浮かび上がる
関連記事もあわせてどうぞ:
- 前回(GAP版)の実験 → GAPモデルでMaxPoolingのpool_sizeを変えると精度はどう変わる?【Keras実験・予想外の結果】
- FlattenとGAPの比較実験 → Global Average Pooling vs Flatten|CNNの最終層、どっちが精度・速度で有利か?【Keras実験】
- MaxPooling vs AveragePooling の比較 → 【Keras】最大値プーリング(MaxPooling)vs 平均値プーリング(AveragePooling)の違いを実験で比較
- Conv2Dのフィルター数の選び方 → Conv2DのFilters数(32 vs 64 vs 128)を変えると精度はどう変わる?【CIFAR-10実験】
- model.summary()の読み方 → Kerasのmodel.summary()の読み方を徹底解説|パラメータ数の計算方法【初心者向け】






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