Data Augmentationと正規化(/255)、どちらを先に書くか——意識したことはありましたか?
「なんとなく正規化を先に書いている」「Augmentationが先のサンプルを見た気がする」。コードを書く中でこんな疑問を抱えたことがある方も多いはずです。今回はGoogle ColabとCIFAR-10を使い、処理の順番(Augmentation→正規化 vs 正規化→Augmentation)が精度に影響するかどうかを実験で確認します。
なお、正規化方法の違い(/255 vs BatchNormalization vs LayerNormalization)については → 正規化方法の比較(/255 vs BatchNormalization vs LayerNormalization)【Keras×CIFAR-10実験】 をご覧ください。本記事は「処理順番」に絞ってピンポイントで検証します。
- AugmentationをRescalingの前後どちらに置くかで精度が変わるかどうか
- 処理順番の違いがモデルに与える影響の考え方
- Kerasで前処理パイプラインを組む際の正しい書き方
なぜ順番が気になるのか
Kerasでデータ拡張を行う場合、よく使われる構成が2通りあります。
パターンA:Augmentation → 正規化
augmentation = keras.Sequential([
keras.layers.RandomFlip("horizontal"),
keras.layers.RandomRotation(0.1),
keras.layers.RandomZoom(0.1),
keras.layers.Rescaling(1./255), # 最後に正規化
])
パターンB:正規化 → Augmentation
augmentation = keras.Sequential([
keras.layers.Rescaling(1./255), # 最初に正規化
keras.layers.RandomFlip("horizontal"),
keras.layers.RandomRotation(0.1),
keras.layers.RandomZoom(0.1),
])
「どちらが正しいか」は検索してもはっきりした答えが見つかりにくいポイントです。数学的には「Augmentation(アフィン変換)と線形スケーリングは可換なはずでは?」という議論もあります。実際に実験して確かめましょう。
ランダム系のAugmentation(RandomRotation, RandomZoomなど)はピクセル値の補間処理を行います。補間後の値が 0〜255 のまま整数として扱われると、Rescaling(1./255)後の結果と、先に正規化してから補間した場合とでは数値的に微妙に異なる可能性があります。また、RandomZoomなどは境界を「0埋め」(fill_mode='reflect' or 'constant')しますが、正規化前は0が「黒」、正規化後は0が「平均から大きく離れた値」として機能する場合があります。このあたりが順番の議論の核心です。
実験コード
使用環境は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 100 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 (22.8 MB/s)
Selecting previously unselected package fonts-ipafont-gothic.
(Reading database ... 122402 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 56.6 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 numpy as np
import matplotlib.pyplot as plt
import japanize_matplotlib
import time
# ── データ読み込み(正規化はAugmentationレイヤーで行うためここでは整数のまま)
(x_train_raw, y_train), (x_test_raw, y_test) = keras.datasets.cifar10.load_data()
x_train_raw = x_train_raw.astype('float32') # float32に変換するだけ(/255はしない)
x_test_raw = x_test_raw.astype('float32')
# ── パターンA:Augmentation → 正規化
aug_A = keras.Sequential([
keras.layers.RandomFlip("horizontal"),
keras.layers.RandomRotation(0.1),
keras.layers.RandomZoom(0.1),
keras.layers.Rescaling(1./255), # 最後に正規化
], name='aug_A')
# ── パターンB:正規化 → Augmentation
aug_B = keras.Sequential([
keras.layers.Rescaling(1./255), # 最初に正規化
keras.layers.RandomFlip("horizontal"),
keras.layers.RandomRotation(0.1),
keras.layers.RandomZoom(0.1),
], name='aug_B')
# ── パターンC:Augmentationなし・正規化のみ(ベースライン)
aug_C = keras.Sequential([
keras.layers.Rescaling(1./255),
], name='aug_C_baseline')
def build_model(aug_layer, name):
inputs = keras.Input(shape=(32, 32, 3))
x = aug_layer(inputs)
x = keras.layers.Conv2D(64, (3, 3), activation='relu', padding='same')(x)
x = keras.layers.MaxPooling2D((2, 2))(x)
x = keras.layers.Conv2D(128, (3, 3), activation='relu', padding='same')(x)
x = keras.layers.MaxPooling2D((2, 2))(x)
x = keras.layers.GlobalAveragePooling2D()(x)
x = keras.layers.Dense(128, activation='relu')(x)
x = keras.layers.Dropout(0.2)(x)
outputs = keras.layers.Dense(10, activation='softmax')(x)
return keras.Model(inputs, outputs, name=name)
def compile_and_fit(model, x_train, y_train):
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
3パターンの学習実行
configs = [
(aug_A, 'A_aug_then_norm'),
(aug_B, 'B_norm_then_aug'),
(aug_C, 'C_baseline_noaug'),
]
histories, times, scores = {}, {}, {}
for aug_layer, name in configs:
print(f"\n=== {name} ===")
model = build_model(aug_layer, name)
print(model.summary())
h, t = compile_and_fit(model, x_train_raw, y_train)
s = model.evaluate(x_test_raw, y_test, verbose=0)
label = name.split('_')[0]
histories[label] = h
times[label] = t
scores[label] = s
print(f"学習時間:{t:.1f}秒 test_accuracy:{s[1]:.4f}")
実行結果をクリックして内容を開く
=== A_aug_then_norm === Model: "A_aug_then_norm" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Layer (type) ┃ Output Shape ┃ Param # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ input_layer (InputLayer) │ (None, 32, 32, 3) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ aug_A (Sequential) │ (None, 32, 32, 3) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ 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 ━━━━━━━━━━━━━━━━━━━━ 12s 9ms/step - accuracy: 0.2464 - loss: 1.9902 - val_accuracy: 0.2760 - val_loss: 1.9800 Epoch 2/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.3379 - loss: 1.7670 - val_accuracy: 0.3873 - val_loss: 1.6749 Epoch 3/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.3912 - loss: 1.6571 - val_accuracy: 0.4278 - val_loss: 1.5600 Epoch 4/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.4245 - loss: 1.5706 - val_accuracy: 0.4556 - val_loss: 1.4792 Epoch 5/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.4494 - loss: 1.5139 - val_accuracy: 0.4529 - val_loss: 1.5148 Epoch 6/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 13s 13ms/step - accuracy: 0.4654 - loss: 1.4671 - val_accuracy: 0.4697 - val_loss: 1.4714 Epoch 7/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 7s 8ms/step - accuracy: 0.4760 - loss: 1.4446 - val_accuracy: 0.4880 - val_loss: 1.4128 Epoch 8/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.4848 - loss: 1.4201 - val_accuracy: 0.4919 - val_loss: 1.3839 Epoch 9/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.4961 - loss: 1.3870 - val_accuracy: 0.4924 - val_loss: 1.3892 Epoch 10/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.5011 - loss: 1.3686 - val_accuracy: 0.5101 - val_loss: 1.3582 Epoch 11/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5094 - loss: 1.3494 - val_accuracy: 0.4778 - val_loss: 1.4770 Epoch 12/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.5160 - loss: 1.3377 - val_accuracy: 0.5037 - val_loss: 1.3710 Epoch 13/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5231 - loss: 1.3176 - val_accuracy: 0.5378 - val_loss: 1.2772 Epoch 14/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5252 - loss: 1.3033 - val_accuracy: 0.5086 - val_loss: 1.3922 Epoch 15/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.5328 - loss: 1.2902 - val_accuracy: 0.5386 - val_loss: 1.2801 Epoch 16/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 10s 9ms/step - accuracy: 0.5362 - loss: 1.2818 - val_accuracy: 0.5580 - val_loss: 1.2278 Epoch 17/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5383 - loss: 1.2739 - val_accuracy: 0.5725 - val_loss: 1.1890 Epoch 18/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.5477 - loss: 1.2505 - val_accuracy: 0.5697 - val_loss: 1.2109 Epoch 19/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5475 - loss: 1.2469 - val_accuracy: 0.5526 - val_loss: 1.2464 Epoch 20/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5552 - loss: 1.2316 - val_accuracy: 0.5592 - val_loss: 1.2104 Epoch 21/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.5562 - loss: 1.2265 - val_accuracy: 0.5744 - val_loss: 1.1960 Epoch 22/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5601 - loss: 1.2124 - val_accuracy: 0.5818 - val_loss: 1.1637 Epoch 23/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.5641 - loss: 1.2050 - val_accuracy: 0.5584 - val_loss: 1.2694 Epoch 24/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 10s 9ms/step - accuracy: 0.5684 - loss: 1.1998 - val_accuracy: 0.5812 - val_loss: 1.1798 Epoch 25/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.5728 - loss: 1.1837 - val_accuracy: 0.5860 - val_loss: 1.1742 Epoch 26/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.5751 - loss: 1.1769 - val_accuracy: 0.5703 - val_loss: 1.2252 Epoch 27/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 10s 8ms/step - accuracy: 0.5791 - loss: 1.1701 - val_accuracy: 0.5767 - val_loss: 1.1934 Epoch 28/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.5822 - loss: 1.1589 - val_accuracy: 0.6047 - val_loss: 1.1148 Epoch 29/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5869 - loss: 1.1492 - val_accuracy: 0.5857 - val_loss: 1.1830 Epoch 30/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.5891 - loss: 1.1471 - val_accuracy: 0.5956 - val_loss: 1.1381 学習時間:192.1秒 test_accuracy:0.5956 === B_norm_then_aug === Model: "B_norm_then_aug" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Layer (type) ┃ Output Shape ┃ Param # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ input_layer_2 (InputLayer) │ (None, 32, 32, 3) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ aug_B (Sequential) │ (None, 32, 32, 3) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ conv2d_2 (Conv2D) │ (None, 32, 32, 64) │ 1,792 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d_2 (MaxPooling2D) │ (None, 16, 16, 64) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ conv2d_3 (Conv2D) │ (None, 16, 16, 128) │ 73,856 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d_3 (MaxPooling2D) │ (None, 8, 8, 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 ━━━━━━━━━━━━━━━━━━━━ 7s 9ms/step - accuracy: 0.2490 - loss: 1.9841 - val_accuracy: 0.3232 - val_loss: 1.8137 Epoch 2/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.3307 - loss: 1.7771 - val_accuracy: 0.3765 - val_loss: 1.6628 Epoch 3/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.3768 - loss: 1.6820 - val_accuracy: 0.4004 - val_loss: 1.6306 Epoch 4/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.4058 - loss: 1.6191 - val_accuracy: 0.4223 - val_loss: 1.6089 Epoch 5/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.4283 - loss: 1.5554 - val_accuracy: 0.4627 - val_loss: 1.4820 Epoch 6/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.4482 - loss: 1.5103 - val_accuracy: 0.4775 - val_loss: 1.4507 Epoch 7/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 10s 8ms/step - accuracy: 0.4626 - loss: 1.4637 - val_accuracy: 0.4988 - val_loss: 1.3749 Epoch 8/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.4767 - loss: 1.4410 - val_accuracy: 0.4925 - val_loss: 1.3916 Epoch 9/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.4834 - loss: 1.4136 - val_accuracy: 0.4931 - val_loss: 1.4117 Epoch 10/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.4944 - loss: 1.3913 - val_accuracy: 0.4958 - val_loss: 1.3867 Epoch 11/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5037 - loss: 1.3678 - val_accuracy: 0.5318 - val_loss: 1.2893 Epoch 12/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.5047 - loss: 1.3606 - val_accuracy: 0.5193 - val_loss: 1.3419 Epoch 13/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5118 - loss: 1.3452 - val_accuracy: 0.5399 - val_loss: 1.2864 Epoch 14/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5181 - loss: 1.3226 - val_accuracy: 0.5305 - val_loss: 1.2952 Epoch 15/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.5229 - loss: 1.3115 - val_accuracy: 0.5442 - val_loss: 1.2521 Epoch 16/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5276 - loss: 1.2979 - val_accuracy: 0.5328 - val_loss: 1.2959 Epoch 17/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.5332 - loss: 1.2851 - val_accuracy: 0.5485 - val_loss: 1.2694 Epoch 18/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5394 - loss: 1.2730 - val_accuracy: 0.5415 - val_loss: 1.2694 Epoch 19/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.5412 - loss: 1.2637 - val_accuracy: 0.5635 - val_loss: 1.2198 Epoch 20/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5500 - loss: 1.2496 - val_accuracy: 0.5640 - val_loss: 1.2405 Epoch 21/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5523 - loss: 1.2393 - val_accuracy: 0.5705 - val_loss: 1.2021 Epoch 22/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.5581 - loss: 1.2245 - val_accuracy: 0.5842 - val_loss: 1.1659 Epoch 23/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 10s 9ms/step - accuracy: 0.5603 - loss: 1.2200 - val_accuracy: 0.5902 - val_loss: 1.1400 Epoch 24/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5613 - loss: 1.2123 - val_accuracy: 0.5877 - val_loss: 1.1749 Epoch 25/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.5653 - loss: 1.2022 - val_accuracy: 0.5720 - val_loss: 1.1969 Epoch 26/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5669 - loss: 1.1968 - val_accuracy: 0.5888 - val_loss: 1.1552 Epoch 27/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5739 - loss: 1.1848 - val_accuracy: 0.5843 - val_loss: 1.1631 Epoch 28/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.5754 - loss: 1.1803 - val_accuracy: 0.5869 - val_loss: 1.1413 Epoch 29/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5795 - loss: 1.1692 - val_accuracy: 0.5940 - val_loss: 1.1378 Epoch 30/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.5839 - loss: 1.1616 - val_accuracy: 0.5729 - val_loss: 1.2397 学習時間:173.1秒 test_accuracy:0.5636 === C_baseline_noaug === Model: "C_baseline_noaug" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Layer (type) ┃ Output Shape ┃ Param # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ input_layer_4 (InputLayer) │ (None, 32, 32, 3) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ aug_C_baseline (Sequential) │ (None, 32, 32, 3) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ conv2d_4 (Conv2D) │ (None, 32, 32, 64) │ 1,792 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d_4 (MaxPooling2D) │ (None, 16, 16, 64) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ conv2d_5 (Conv2D) │ (None, 16, 16, 128) │ 73,856 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d_5 (MaxPooling2D) │ (None, 8, 8, 128) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ 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) None Epoch 1/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 9s 9ms/step - accuracy: 0.2624 - loss: 1.9279 - val_accuracy: 0.3365 - val_loss: 1.7242 Epoch 2/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.3669 - loss: 1.6726 - val_accuracy: 0.4034 - val_loss: 1.5930 Epoch 3/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.4295 - loss: 1.5445 - val_accuracy: 0.4671 - val_loss: 1.4731 Epoch 4/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.4681 - loss: 1.4491 - val_accuracy: 0.4985 - val_loss: 1.3751 Epoch 5/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.4952 - loss: 1.3758 - val_accuracy: 0.5089 - val_loss: 1.3290 Epoch 6/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5122 - loss: 1.3356 - val_accuracy: 0.5291 - val_loss: 1.2856 Epoch 7/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.5239 - loss: 1.3035 - val_accuracy: 0.5270 - val_loss: 1.2950 Epoch 8/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5394 - loss: 1.2609 - val_accuracy: 0.5457 - val_loss: 1.2333 Epoch 9/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5525 - loss: 1.2340 - val_accuracy: 0.5669 - val_loss: 1.1978 Epoch 10/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5597 - loss: 1.2106 - val_accuracy: 0.5650 - val_loss: 1.1856 Epoch 11/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5690 - loss: 1.1839 - val_accuracy: 0.5828 - val_loss: 1.1456 Epoch 12/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5744 - loss: 1.1682 - val_accuracy: 0.5736 - val_loss: 1.1848 Epoch 13/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5845 - loss: 1.1458 - val_accuracy: 0.5884 - val_loss: 1.1284 Epoch 14/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5932 - loss: 1.1240 - val_accuracy: 0.6012 - val_loss: 1.0955 Epoch 15/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6015 - loss: 1.1075 - val_accuracy: 0.5955 - val_loss: 1.1195 Epoch 16/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6056 - loss: 1.0906 - val_accuracy: 0.6081 - val_loss: 1.0828 Epoch 17/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.6140 - loss: 1.0732 - val_accuracy: 0.6109 - val_loss: 1.0823 Epoch 18/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6203 - loss: 1.0535 - val_accuracy: 0.6078 - val_loss: 1.0726 Epoch 19/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6279 - loss: 1.0375 - val_accuracy: 0.6327 - val_loss: 1.0153 Epoch 20/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.6339 - loss: 1.0194 - val_accuracy: 0.6437 - val_loss: 1.0029 Epoch 21/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6356 - loss: 1.0122 - val_accuracy: 0.6349 - val_loss: 1.0111 Epoch 22/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6422 - loss: 0.9959 - val_accuracy: 0.6490 - val_loss: 0.9840 Epoch 23/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.6461 - loss: 0.9818 - val_accuracy: 0.6459 - val_loss: 0.9869 Epoch 24/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6510 - loss: 0.9697 - val_accuracy: 0.6577 - val_loss: 0.9631 Epoch 25/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6564 - loss: 0.9626 - val_accuracy: 0.6579 - val_loss: 0.9617 Epoch 26/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6598 - loss: 0.9484 - val_accuracy: 0.6547 - val_loss: 0.9611 Epoch 27/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6645 - loss: 0.9339 - val_accuracy: 0.6683 - val_loss: 0.9353 Epoch 28/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6668 - loss: 0.9255 - val_accuracy: 0.6627 - val_loss: 0.9452 Epoch 29/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6695 - loss: 0.9172 - val_accuracy: 0.6777 - val_loss: 0.9160 Epoch 30/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6798 - loss: 0.9032 - val_accuracy: 0.6575 - val_loss: 0.9561 学習時間:122.5秒 test_accuracy:0.6538
グラフ+サマリー
# ── val_accuracy / val_loss 比較グラフ ───────────────
label_map = {'A': 'A: Aug→正規化', 'B': 'B: 正規化→Aug', 'C': 'C: ベースライン'}
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
for key, h in histories.items():
axes[0].plot(h.history['val_accuracy'], label=label_map[key])
axes[1].plot(h.history['val_loss'], label=label_map[key])
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('aug_order_comparison.png', dpi=150)
plt.show()
# ── train vs val loss(過学習の乖離)────────────
fig2, axes2 = plt.subplots(3, 1, figsize=(7, 14))
for i, (key, 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_map[key]}')
axes2[i].set_xlabel('Epoch'); axes2[i].legend(); axes2[i].grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('aug_order_overfit.png', dpi=150)
plt.show()
print("\n===== 最終結果サマリー =====")
print(f"{'Pattern':>3} | {'Val Acc':>8} | {'Test Acc':>9} | {'Time(s)':>8}")
print("-" * 42)
for key in ['A', 'B', 'C']:
val_acc = histories[key].history['val_accuracy'][-1]
test_acc = scores[key][1]
t = times[key]
print(f"{key:>3} | {val_acc:>8.4f} | {test_acc:>9.4f} | {t:>8.1f}")
print("-" * 42)
最終結果サマリー
===== 最終結果サマリー ===== Pattern | Val Acc | Test Acc | Time(s) ------------------------------------------ A | 0.5956 | 0.5956 | 192.1 B | 0.5729 | 0.5636 | 173.1 C | 0.6575 | 0.6538 | 122.5 ------------------------------------------
実験結果
精度グラフ
損失グラフ
A: Aug→正規化
B: 正規化→Aug
C: ベースライン
| パターン | 最終 val_accuracy | 最終 test_accuracy | 学習時間 |
|---|---|---|---|
| A:Augmentation → 正規化 | 59.56% | 59.56% | 192.1秒 |
| B:正規化 → Augmentation | 57.29% | 56.36% | 173.1秒 |
| C:ベースライン(Augなし) | 65.75% | 65.38% | 122.5秒 |
考察
① 予想外の結果:Augmentationありが最下位だった
今回の実験で最も驚くべき結果は、Augmentationなしのベースライン(C)が最高精度65.38%を記録し、Augmentationありの A(59.56%)・B(56.36%)を大きく上回ったことです。
| 比較 | test_accuracy の差 |
|---|---|
| C(Augなし)vs A(Aug→正規化) | +5.82% ベースラインが上 |
| C(Augなし)vs B(正規化→Aug) | +9.02% ベースラインが上 |
「Augmentationは汎化性能を上げる」という一般的な知識とは逆の結果です。これはAugmentationが悪いのではなく、今回の設定(エポック数30・強度設定)が不適切だった可能性が高いです。
② Augmentationが逆効果になる理由
Augmentationは学習データを「難しくする」処理です。30エポックという短い学習では、モデルが変換後の多様な画像に十分適応しきれず、精度が伸び悩んだと考えられます。Augmentationの効果を引き出すには、一般的により多くのエポック数(50〜100以上)が必要です。
また、RandomRotation(0.1)・RandomZoom(0.1)の組み合わせが CIFAR-10(32×32の小さな画像)に対してやや強すぎた可能性もあります。32×32の画像をズームすると重要な特徴が失われやすく、ノイズとして機能してしまいます。
③ A vs B:順番の影響は約3%の差
本来の実験テーマである「AとBの比較」では、A(59.56%)がB(56.36%)を約3.2%上回りました。「どちらでも同じはず」という事前予測に反して、無視できない差が出ています。
原因として考えられるのは fill_mode の挙動です。RandomRotation・RandomZoomは画像外にはみ出た領域を 0 で埋めます(fill_mode='reflect' がデフォルトですが実装依存あり)。正規化前(0〜255スケール)に0埋めされた場合、0は「真っ黒」として自然な境界になります。一方、正規化後(0〜1スケール)に幾何学変換が走る場合も同様ですが、内部の補間精度や実装の微差が積み重なった結果、Aの方がわずかに有利に働いたと推測されます。
fill_mode='constant'(0埋め)で標準化(平均\(\mu\)・標準偏差\(\sigma\))を使う場合:
正規化後の境界値 \(= \frac{0 - \mu}{\sigma} \neq 0\) ← 0が「黒」ではなくなる
④ 学習時間の差:Augmentationはコストがかかる
AはCより約70秒(57%増)、BはCより約50秒(41%増)の学習時間がかかっています。Augmentationの変換処理はGPUのデータパイプライン上でエポックごとに実行されるため、学習時間への影響は無視できません。精度が下がって時間も増えるという結果は、Augmentationの設定見直しが必要であることを示しています。
- 「Augmentation → 正規化(A)」と「正規化 → Augmentation(B)」では、Aが約3%高い精度となった。完全に等価ではないことが実験で示された
- 一方で最大の発見はAugmentationなし(C)が最高精度だったこと。エポック数30ではAugmentationの効果が出きらず逆効果になった
- CIFAR-10のような小さな画像にはAugmentationの強度を抑えるか、エポック数を増やすことが必要
- 処理順番の安全な書き方としては「Augmentation → 正規化(パターンA)」が推奨。生の画像(0〜255)に幾何学変換をかけ、最後にスケールを揃える流れが直感的で数値的にも安定しやすい
- fill_mode='constant' + 標準化(StandardScaler等)を使う場合は順番が特に重要になる
関連記事もあわせてどうぞ:
- 正規化方法の比較 → 正規化方法の比較(/255 vs BatchNormalization vs LayerNormalization)【Keras×CIFAR-10実験】
- MixUpの効果検証 → MixUp の効果検証(あり vs なし)【Keras×CIFAR-10実験】
- CutOut/Random Erasingの効果 → CutOut / Random Erasing の効果検証【Keras×CIFAR-10実験】
- BatchNorm vs Dropout → BatchNormalization vs Dropout|どちらが過学習を抑えるか?【Keras実験】






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