CIFAR-10にRandomRotationは逆効果?角度を変えて5段階で比較実験した結果【Keras】

投稿日:2026年5月5日火曜日 最終更新日:

CIFAR-10 CNN Data Augmentation Google Colab Keras RandomRotation データ拡張 画像分類

X f B! P L
CIFAR-10にRandomRotationは逆効果?角度を変えて5段階で比較実験した結果【Keras】 アイキャッチ画像

前回の記事では、 RandomRotation(0.15) を加えた途端に精度が約10%急落するという予想外の結果になりました。
原因は 0.15 = 最大±54度という強すぎる回転 がCIFAR-10の32×32画像を歪めていたことでした。
では、どの角度なら精度が上がるのか? 今回は RandomRotation の値だけを5段階に変えて比較実験しました。
結論を先に言うと、0.03という小さい角度でも test accuracy は約3.1ポイント低下し、今回の実験条件ではrotationを使わない flip のみが最も良い結果になりました。

📘 この記事でわかること

  • RandomRotation の数値が実際に何度の回転を意味するか
  • CIFAR-10(32×32)に最適なrotation角度
  • 角度を上げるほど精度が下がる理由と学習時間への影響
  • 解像度別のrotation推奨設定

RandomRotation(0.15)はなぜ強すぎたのか──前回の振り返り

Kerasの RandomRotation(factor)factor は、 「全体(360度)に対する割合」 で指定します。

factor の値最大回転角度イメージ
0.03±10.8度わずかに傾く程度
0.05±18度やや傾いた画像
0.10±36度明らかに斜めになる
0.15±54度ほぼ横倒し(前回の設定)

32×32ピクセルという小さな画像を±54度回転させると、 コーナーに大きな黒い余白が生まれ、被写体の形が大きく失われます。 モデルは「歪んだ画像」を学習することになり、精度が落ちました。
今回は より小さい角度(0.03・0.05)でも効果があるか を確かめます。

📌 前回記事:Data Augmentationを重ねすぎると精度が下がる?flip〜cropの5パターンをCIFAR-10で比較【Keras実験】


実験で比較する5段階

全パターンに RandomFlip('horizontal') を統一適用し、 RandomRotation の値だけを変えます。 その他の条件(モデル構造・エポック数・バッチサイズ・オプティマイザ)はすべて同一です。

パターンrotation値最大回転角度狙い
A:0.00rotationなし0度flipのみのベースライン
B:0.030.03±10.8度最小限の回転
C:0.050.05±18度よく推奨される値
D:0.100.10±36度中程度の回転
E:0.150.15±54度前回と同じ設定(比較用)

実験コード

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

# ── 環境準備(最初に一度だけ実行)──────────────────────
!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 0s (27.2 MB/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 21.8 MB/s eta 0:00:00
  Preparing metadata (setup.py) ... done
  Building wheel for japanize_matplotlib (setup.py) ... done
環境準備完了

import・データ準備・Augmentationレイヤーの定義

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

# ── rotation値ごとのAugmentation定義 ───────────────────
def get_augmentation(rotation_factor):
    layers = [keras.layers.RandomFlip('horizontal')]
    if rotation_factor > 0:
        layers.append(keras.layers.RandomRotation(rotation_factor))
    return keras.Sequential(layers, name=f'aug_rot{rotation_factor}')
実行結果をクリックして内容を開く
Downloading data from https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz
170498071/170498071 ━━━━━━━━━━━━━━━━━━━━ 536s 3us/step

モデル構築関数

# ── モデル構築関数 ─────────────────────────────────────
def build_model(rotation_factor, name):
    augmentation = get_augmentation(rotation_factor)
    model = keras.Sequential([
        keras.layers.Input(shape=(32, 32, 3)),
        augmentation,
        keras.layers.Conv2D(64, (3, 3), activation='relu', padding='same'),
        keras.layers.MaxPooling2D((2, 2)),
        keras.layers.Conv2D(128, (3, 3), activation='relu', padding='same'),
        keras.layers.MaxPooling2D((2, 2)),
        keras.layers.GlobalAveragePooling2D(),
        keras.layers.Dense(256, activation='relu'),
        keras.layers.Dropout(0.2),
        keras.layers.Dense(10, activation='softmax'),
    ], name=name)
    model.compile(
        optimizer='adam',
        loss='sparse_categorical_crossentropy',
        metrics=['accuracy']
    )
    return model

5パターンの学習実行

EPOCHS = 30
BATCH  = 64

configs = [
    (0.00, 'A_rot0.00'),
    (0.03, 'B_rot0.03'),
    (0.05, 'C_rot0.05'),
    (0.10, 'D_rot0.10'),
    (0.15, 'E_rot0.15'),
]

histories, scores, times = {}, {}, {}

for rot, name in configs:
    print(f"\n=== {name} ===")
    model = build_model(rot, name)
    start = time.time()
    h = model.fit(
        x_train, y_train,
        epochs=EPOCHS,
        batch_size=BATCH,
        validation_split=0.2,
        verbose=1
    )
    elapsed = time.time() - start
    score = model.evaluate(x_test, y_test, verbose=0)
    label = name.split('_')[1]   # 'rot0.00' / 'rot0.03' / ...
    histories[label] = h
    scores[label]    = score
    times[label]     = elapsed
    print(f"学習時間:{elapsed:.1f}秒 test_accuracy:{score[1]:.4f}")
実行結果をクリックして内容を開く
=== A_rot0.00 ===
Epoch 1/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 19s 14ms/step - accuracy: 0.2744 - loss: 1.9079 - val_accuracy: 0.3617 - val_loss: 1.7098
Epoch 2/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 8s 6ms/step - accuracy: 0.4021 - loss: 1.6187 - val_accuracy: 0.4338 - val_loss: 1.5376
Epoch 3/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.4571 - loss: 1.4856 - val_accuracy: 0.4934 - val_loss: 1.4091
Epoch 4/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 6ms/step - accuracy: 0.4902 - loss: 1.4010 - val_accuracy: 0.4905 - val_loss: 1.3708
Epoch 5/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5098 - loss: 1.3428 - val_accuracy: 0.5190 - val_loss: 1.3100
Epoch 6/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.5270 - loss: 1.2990 - val_accuracy: 0.5465 - val_loss: 1.2341
Epoch 7/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 6ms/step - accuracy: 0.5391 - loss: 1.2603 - val_accuracy: 0.5382 - val_loss: 1.2495
Epoch 8/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5529 - loss: 1.2269 - val_accuracy: 0.5605 - val_loss: 1.1948
Epoch 9/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.5678 - loss: 1.1929 - val_accuracy: 0.5812 - val_loss: 1.1545
Epoch 10/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5721 - loss: 1.1656 - val_accuracy: 0.5826 - val_loss: 1.1441
Epoch 11/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5885 - loss: 1.1327 - val_accuracy: 0.6023 - val_loss: 1.0893
Epoch 12/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.5991 - loss: 1.1047 - val_accuracy: 0.6116 - val_loss: 1.0676
Epoch 13/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6103 - loss: 1.0805 - val_accuracy: 0.6005 - val_loss: 1.0794
Epoch 14/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6130 - loss: 1.0692 - val_accuracy: 0.6223 - val_loss: 1.0356
Epoch 15/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.6238 - loss: 1.0389 - val_accuracy: 0.6192 - val_loss: 1.0320
Epoch 16/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6342 - loss: 1.0200 - val_accuracy: 0.6356 - val_loss: 1.0027
Epoch 17/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6385 - loss: 1.0043 - val_accuracy: 0.6376 - val_loss: 0.9984
Epoch 18/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.6439 - loss: 0.9818 - val_accuracy: 0.6316 - val_loss: 1.0195
Epoch 19/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6507 - loss: 0.9690 - val_accuracy: 0.6420 - val_loss: 0.9700
Epoch 20/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6617 - loss: 0.9397 - val_accuracy: 0.6580 - val_loss: 0.9522
Epoch 21/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6664 - loss: 0.9264 - val_accuracy: 0.6637 - val_loss: 0.9418
Epoch 22/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6716 - loss: 0.9198 - val_accuracy: 0.6613 - val_loss: 0.9461
Epoch 23/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6806 - loss: 0.8975 - val_accuracy: 0.6723 - val_loss: 0.9311
Epoch 24/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6833 - loss: 0.8905 - val_accuracy: 0.6760 - val_loss: 0.9005
Epoch 25/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.6849 - loss: 0.8835 - val_accuracy: 0.6824 - val_loss: 0.8971
Epoch 26/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6902 - loss: 0.8705 - val_accuracy: 0.6852 - val_loss: 0.8812
Epoch 27/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6943 - loss: 0.8567 - val_accuracy: 0.6910 - val_loss: 0.8645
Epoch 28/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.7011 - loss: 0.8397 - val_accuracy: 0.6940 - val_loss: 0.8572
Epoch 29/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.7043 - loss: 0.8326 - val_accuracy: 0.6795 - val_loss: 0.8997
Epoch 30/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.7073 - loss: 0.8221 - val_accuracy: 0.6773 - val_loss: 0.9078
学習時間:140.9秒 test_accuracy:0.6842

=== B_rot0.03 ===
Epoch 1/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 10s 9ms/step - accuracy: 0.2691 - loss: 1.9205 - val_accuracy: 0.3570 - val_loss: 1.7302
Epoch 2/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.3818 - loss: 1.6529 - val_accuracy: 0.4122 - val_loss: 1.5883
Epoch 3/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.4353 - loss: 1.5366 - val_accuracy: 0.4683 - val_loss: 1.4702
Epoch 4/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.4673 - loss: 1.4528 - val_accuracy: 0.4665 - val_loss: 1.4456
Epoch 5/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.4894 - loss: 1.3949 - val_accuracy: 0.4969 - val_loss: 1.3778
Epoch 6/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5095 - loss: 1.3437 - val_accuracy: 0.5096 - val_loss: 1.3448
Epoch 7/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.5211 - loss: 1.3109 - val_accuracy: 0.5449 - val_loss: 1.2387
Epoch 8/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5338 - loss: 1.2772 - val_accuracy: 0.5555 - val_loss: 1.2306
Epoch 9/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.5483 - loss: 1.2459 - val_accuracy: 0.5556 - val_loss: 1.2326
Epoch 10/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5586 - loss: 1.2179 - val_accuracy: 0.5499 - val_loss: 1.2403
Epoch 11/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.5637 - loss: 1.2000 - val_accuracy: 0.5770 - val_loss: 1.1418
Epoch 12/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5782 - loss: 1.1648 - val_accuracy: 0.5767 - val_loss: 1.1719
Epoch 13/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5828 - loss: 1.1492 - val_accuracy: 0.5976 - val_loss: 1.1033
Epoch 14/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.5896 - loss: 1.1317 - val_accuracy: 0.6014 - val_loss: 1.1091
Epoch 15/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 10s 9ms/step - accuracy: 0.5986 - loss: 1.1128 - val_accuracy: 0.6041 - val_loss: 1.1063
Epoch 16/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.6050 - loss: 1.0955 - val_accuracy: 0.6271 - val_loss: 1.0472
Epoch 17/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.6083 - loss: 1.0818 - val_accuracy: 0.6181 - val_loss: 1.0575
Epoch 18/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.6151 - loss: 1.0693 - val_accuracy: 0.6360 - val_loss: 1.0292
Epoch 19/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 10s 9ms/step - accuracy: 0.6219 - loss: 1.0490 - val_accuracy: 0.6303 - val_loss: 1.0247
Epoch 20/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.6238 - loss: 1.0386 - val_accuracy: 0.6269 - val_loss: 1.0362
Epoch 21/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.6327 - loss: 1.0212 - val_accuracy: 0.6342 - val_loss: 1.0085
Epoch 22/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.6352 - loss: 1.0114 - val_accuracy: 0.6433 - val_loss: 0.9872
Epoch 23/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.6404 - loss: 0.9975 - val_accuracy: 0.6443 - val_loss: 0.9870
Epoch 24/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.6445 - loss: 0.9895 - val_accuracy: 0.6472 - val_loss: 0.9808
Epoch 25/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 10s 9ms/step - accuracy: 0.6490 - loss: 0.9789 - val_accuracy: 0.6573 - val_loss: 0.9667
Epoch 26/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.6499 - loss: 0.9675 - val_accuracy: 0.6446 - val_loss: 0.9946
Epoch 27/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.6542 - loss: 0.9638 - val_accuracy: 0.6365 - val_loss: 1.0170
Epoch 28/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.6600 - loss: 0.9490 - val_accuracy: 0.6634 - val_loss: 0.9427
Epoch 29/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.6658 - loss: 0.9330 - val_accuracy: 0.6590 - val_loss: 0.9613
Epoch 30/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.6679 - loss: 0.9278 - val_accuracy: 0.6538 - val_loss: 0.9898
学習時間:176.8秒 test_accuracy:0.6531

=== C_rot0.05 ===
Epoch 1/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 7s 9ms/step - accuracy: 0.2677 - loss: 1.9208 - val_accuracy: 0.3310 - val_loss: 1.7778
Epoch 2/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.3799 - loss: 1.6664 - val_accuracy: 0.4334 - val_loss: 1.5437
Epoch 3/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.4322 - loss: 1.5520 - val_accuracy: 0.4630 - val_loss: 1.4581
Epoch 4/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.4645 - loss: 1.4677 - val_accuracy: 0.4893 - val_loss: 1.3979
Epoch 5/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 10s 8ms/step - accuracy: 0.4832 - loss: 1.4170 - val_accuracy: 0.4751 - val_loss: 1.4209
Epoch 6/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5034 - loss: 1.3643 - val_accuracy: 0.5127 - val_loss: 1.3192
Epoch 7/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5210 - loss: 1.3255 - val_accuracy: 0.5387 - val_loss: 1.2721
Epoch 8/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.5320 - loss: 1.2919 - val_accuracy: 0.5323 - val_loss: 1.2855
Epoch 9/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 10s 9ms/step - accuracy: 0.5396 - loss: 1.2718 - val_accuracy: 0.5511 - val_loss: 1.2196
Epoch 10/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5510 - loss: 1.2420 - val_accuracy: 0.5424 - val_loss: 1.2480
Epoch 11/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5562 - loss: 1.2181 - val_accuracy: 0.5796 - val_loss: 1.1670
Epoch 12/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.5649 - loss: 1.1999 - val_accuracy: 0.5796 - val_loss: 1.1815
Epoch 13/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5744 - loss: 1.1751 - val_accuracy: 0.5808 - val_loss: 1.1512
Epoch 14/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.5850 - loss: 1.1550 - val_accuracy: 0.5895 - val_loss: 1.1338
Epoch 15/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5882 - loss: 1.1425 - val_accuracy: 0.5925 - val_loss: 1.1208
Epoch 16/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.5927 - loss: 1.1311 - val_accuracy: 0.5958 - val_loss: 1.1140
Epoch 17/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.6001 - loss: 1.1067 - val_accuracy: 0.6116 - val_loss: 1.0762
Epoch 18/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 11s 9ms/step - accuracy: 0.6068 - loss: 1.0893 - val_accuracy: 0.5896 - val_loss: 1.1248
Epoch 19/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 10s 9ms/step - accuracy: 0.6114 - loss: 1.0780 - val_accuracy: 0.6106 - val_loss: 1.0794
Epoch 20/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.6182 - loss: 1.0626 - val_accuracy: 0.6171 - val_loss: 1.0768
Epoch 21/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.6208 - loss: 1.0540 - val_accuracy: 0.6232 - val_loss: 1.0446
Epoch 22/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.6226 - loss: 1.0462 - val_accuracy: 0.6322 - val_loss: 1.0266
Epoch 23/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.6295 - loss: 1.0353 - val_accuracy: 0.6226 - val_loss: 1.0633
Epoch 24/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.6320 - loss: 1.0231 - val_accuracy: 0.6467 - val_loss: 0.9822
Epoch 25/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.6388 - loss: 1.0080 - val_accuracy: 0.6328 - val_loss: 1.0222
Epoch 26/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.6452 - loss: 0.9926 - val_accuracy: 0.6418 - val_loss: 0.9965
Epoch 27/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 10s 8ms/step - accuracy: 0.6465 - loss: 0.9897 - val_accuracy: 0.6455 - val_loss: 0.9811
Epoch 28/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.6508 - loss: 0.9829 - val_accuracy: 0.6490 - val_loss: 0.9945
Epoch 29/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.6502 - loss: 0.9787 - val_accuracy: 0.6460 - val_loss: 0.9834
Epoch 30/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.6552 - loss: 0.9647 - val_accuracy: 0.6494 - val_loss: 0.9847
学習時間:184.9秒 test_accuracy:0.6540

=== D_rot0.10 ===
Epoch 1/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 7s 9ms/step - accuracy: 0.2546 - loss: 1.9605 - val_accuracy: 0.3124 - val_loss: 1.8404
Epoch 2/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.3609 - loss: 1.7128 - val_accuracy: 0.4269 - val_loss: 1.5755
Epoch 3/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.4117 - loss: 1.6005 - val_accuracy: 0.4477 - val_loss: 1.5301
Epoch 4/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.4523 - loss: 1.5092 - val_accuracy: 0.4901 - val_loss: 1.4127
Epoch 5/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.4706 - loss: 1.4570 - val_accuracy: 0.4978 - val_loss: 1.3762
Epoch 6/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.4839 - loss: 1.4155 - val_accuracy: 0.5068 - val_loss: 1.3634
Epoch 7/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.4991 - loss: 1.3806 - val_accuracy: 0.5100 - val_loss: 1.3643
Epoch 8/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5113 - loss: 1.3471 - val_accuracy: 0.4884 - val_loss: 1.4069
Epoch 9/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.5212 - loss: 1.3242 - val_accuracy: 0.5429 - val_loss: 1.2499
Epoch 10/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 10s 9ms/step - accuracy: 0.5275 - loss: 1.3025 - val_accuracy: 0.5519 - val_loss: 1.2414
Epoch 11/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5363 - loss: 1.2785 - val_accuracy: 0.5644 - val_loss: 1.2161
Epoch 12/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5411 - loss: 1.2646 - val_accuracy: 0.5737 - val_loss: 1.1769
Epoch 13/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.5503 - loss: 1.2397 - val_accuracy: 0.5393 - val_loss: 1.2892
Epoch 14/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 10s 9ms/step - accuracy: 0.5569 - loss: 1.2269 - val_accuracy: 0.5725 - val_loss: 1.1877
Epoch 15/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5630 - loss: 1.2117 - val_accuracy: 0.5756 - val_loss: 1.1833
Epoch 16/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.5709 - loss: 1.1929 - val_accuracy: 0.5941 - val_loss: 1.1171
Epoch 17/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5735 - loss: 1.1804 - val_accuracy: 0.6028 - val_loss: 1.0990
Epoch 18/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5803 - loss: 1.1691 - val_accuracy: 0.6077 - val_loss: 1.0925
Epoch 19/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.5878 - loss: 1.1500 - val_accuracy: 0.6057 - val_loss: 1.1135
Epoch 20/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5928 - loss: 1.1389 - val_accuracy: 0.6103 - val_loss: 1.1030
Epoch 21/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.5964 - loss: 1.1219 - val_accuracy: 0.6215 - val_loss: 1.0613
Epoch 22/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 9s 8ms/step - accuracy: 0.6038 - loss: 1.1097 - val_accuracy: 0.6167 - val_loss: 1.0877
Epoch 23/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.6050 - loss: 1.1048 - val_accuracy: 0.6262 - val_loss: 1.0578
Epoch 24/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.6080 - loss: 1.0905 - val_accuracy: 0.6319 - val_loss: 1.0309
Epoch 25/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.6136 - loss: 1.0810 - val_accuracy: 0.6193 - val_loss: 1.0652
Epoch 26/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 10s 9ms/step - accuracy: 0.6152 - loss: 1.0710 - val_accuracy: 0.6332 - val_loss: 1.0384
Epoch 27/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.6179 - loss: 1.0602 - val_accuracy: 0.6300 - val_loss: 1.0470
Epoch 28/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.6227 - loss: 1.0602 - val_accuracy: 0.6502 - val_loss: 0.9847
Epoch 29/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.6272 - loss: 1.0453 - val_accuracy: 0.6451 - val_loss: 1.0119
Epoch 30/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.6309 - loss: 1.0393 - val_accuracy: 0.6276 - val_loss: 1.0680
学習時間:180.9秒 test_accuracy:0.6310

=== E_rot0.15 ===
Epoch 1/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 8s 9ms/step - accuracy: 0.2444 - loss: 1.9918 - val_accuracy: 0.3297 - val_loss: 1.7916
Epoch 2/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.3325 - loss: 1.7941 - val_accuracy: 0.3883 - val_loss: 1.6463
Epoch 3/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.3862 - loss: 1.6784 - val_accuracy: 0.4449 - val_loss: 1.5338
Epoch 4/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.4192 - loss: 1.6011 - val_accuracy: 0.4748 - val_loss: 1.4583
Epoch 5/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.4425 - loss: 1.5390 - val_accuracy: 0.4814 - val_loss: 1.4213
Epoch 6/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.4581 - loss: 1.4973 - val_accuracy: 0.4884 - val_loss: 1.4191
Epoch 7/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.4681 - loss: 1.4641 - val_accuracy: 0.4969 - val_loss: 1.3823
Epoch 8/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 10s 8ms/step - accuracy: 0.4833 - loss: 1.4305 - val_accuracy: 0.5127 - val_loss: 1.3456
Epoch 9/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.4915 - loss: 1.4057 - val_accuracy: 0.5221 - val_loss: 1.3229
Epoch 10/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 10s 9ms/step - accuracy: 0.5015 - loss: 1.3781 - val_accuracy: 0.4918 - val_loss: 1.3633
Epoch 11/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5127 - loss: 1.3566 - val_accuracy: 0.5417 - val_loss: 1.2713
Epoch 12/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.5152 - loss: 1.3346 - val_accuracy: 0.5226 - val_loss: 1.3361
Epoch 13/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.5222 - loss: 1.3206 - val_accuracy: 0.5441 - val_loss: 1.2608
Epoch 14/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5296 - loss: 1.3036 - val_accuracy: 0.5617 - val_loss: 1.2032
Epoch 15/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.5353 - loss: 1.2858 - val_accuracy: 0.5645 - val_loss: 1.1977
Epoch 16/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5409 - loss: 1.2734 - val_accuracy: 0.5571 - val_loss: 1.2262
Epoch 17/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.5425 - loss: 1.2566 - val_accuracy: 0.5649 - val_loss: 1.1971
Epoch 18/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5524 - loss: 1.2461 - val_accuracy: 0.5583 - val_loss: 1.2249
Epoch 19/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.5566 - loss: 1.2284 - val_accuracy: 0.5637 - val_loss: 1.2113
Epoch 20/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 9ms/step - accuracy: 0.5596 - loss: 1.2216 - val_accuracy: 0.5888 - val_loss: 1.1548
Epoch 21/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5698 - loss: 1.2053 - val_accuracy: 0.5671 - val_loss: 1.2066
Epoch 22/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.5652 - loss: 1.2014 - val_accuracy: 0.5887 - val_loss: 1.1532
Epoch 23/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5719 - loss: 1.1900 - val_accuracy: 0.5887 - val_loss: 1.1494
Epoch 24/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.5753 - loss: 1.1824 - val_accuracy: 0.6012 - val_loss: 1.1156
Epoch 25/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5807 - loss: 1.1654 - val_accuracy: 0.5567 - val_loss: 1.2562
Epoch 26/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5818 - loss: 1.1645 - val_accuracy: 0.5981 - val_loss: 1.1244
Epoch 27/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 10s 8ms/step - accuracy: 0.5872 - loss: 1.1485 - val_accuracy: 0.6051 - val_loss: 1.1288
Epoch 28/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.5894 - loss: 1.1471 - val_accuracy: 0.6061 - val_loss: 1.1161
Epoch 29/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5919 - loss: 1.1407 - val_accuracy: 0.6161 - val_loss: 1.0930
Epoch 30/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 9ms/step - accuracy: 0.5972 - loss: 1.1232 - val_accuracy: 0.5818 - val_loss: 1.1949
学習時間:182.7秒 test_accuracy:0.5826

グラフ描画・サマリー

# ── 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('rotation_val.png', dpi=150)
plt.show()

# ── train vs val(過学習の乖離)────────────────────────
fig2, axes2 = plt.subplots(1, 2, figsize=(14, 5))
for ax, label in zip(axes2, ['rot0.00', 'rot0.05']):
    h = histories[label]
    ax.plot(h.history['loss'],     label='train_loss')
    ax.plot(h.history['val_loss'], label='val_loss')
    ax.set_title(f'{label}:train vs val loss')
    ax.set_xlabel('Epoch'); ax.legend(); ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('rotation_overfit.png', dpi=150)
plt.show()

# ── サマリー ─────────────────────────────────────────────
print("\n===== 最終結果サマリー =====")
print(f"{'Pattern':>10} | {'Max Angle':>10} | {'Val Acc':>8} | {'Test Acc':>9} | {'Time(s)':>8}")
print("-" * 58)
angle_map = {'rot0.00': '0度', 'rot0.03': '±10.8度', 'rot0.05': '±18度',
             'rot0.10': '±36度', 'rot0.15': '±54度'}
for label, h in histories.items():
    val_acc  = h.history['val_accuracy'][-1]
    test_acc = scores[label][1]
    t        = times[label]
    angle    = angle_map[label]
    print(f"{label:>10} | {angle:>10} | {val_acc:>8.4f} | {test_acc:>9.4f} | {t:>8.1f}")
print("-" * 58)

最終結果サマリー

===== 最終結果サマリー =====
   Pattern |  Max Angle |  Val Acc |  Test Acc |  Time(s)
----------------------------------------------------------
   rot0.00 |         0度 |   0.6773 |    0.6842 |    140.9
   rot0.03 |     ±10.8度 |   0.6538 |    0.6531 |    176.8
   rot0.05 |       ±18度 |   0.6494 |    0.6540 |    184.9
   rot0.10 |       ±36度 |   0.6276 |    0.6310 |    180.9
   rot0.15 |       ±54度 |   0.5818 |    0.5826 |    182.7
----------------------------------------------------------

実験結果

精度グラフ

精度グラフ

損失グラフ

損失グラフ

rot0.00

rot0.00

rot0.05

rot0.05
パターン 最大回転角度 最終 val_accuracy 最終 test_accuracy 学習時間
A:rot0.000度(flipのみ)67.73%68.42%140.9秒
B:rot0.03±10.8度65.38%65.31%176.8秒
C:rot0.05±18度64.94%65.40%184.9秒
D:rot0.10±36度62.76%63.10%180.9秒
E:rot0.15±54度(前回と同じ)58.18%58.26%182.7秒

予想に反して、最も良かったのはrotationなし(flipのみ)のパターンAでした。
rotation 0.03という最小限の角度でさえ約3.1ポイント低下、 角度を上げるほど単調に精度が低下しています。
さらに学習時間はrotationを加えた途端に約40秒増加し、精度が下がりながら時間だけが増えるという結果になりました。


考察

① なぜ0.03という小さい角度でも精度が下がったのか

±10.8度という回転は「わずかに傾く程度」に見えますが、 32×32ピクセルという極小解像度では1〜2ピクセルのずれが特徴量全体に大きく影響します。 高解像度画像では「少し傾いた犬」も犬に見えますが、 32×32ではわずかな回転でエッジや輪郭の情報が失われてしまいます。

② 角度を大きくするほど概ね低下した理由

今回の結果では、rot0.03(-3.1%)→ rot0.05(-3.5%)→ rot0.10(-5.3%)→ rot0.15(-10.2%)と、 角度に対してほぼ単調に精度が低下しています。 これはCIFAR-10の32×32においては、回転という変換が百害あって一利なしであることを示しています。 回転によって生まれる余白の補完処理(nearest/reflect)が、 低解像度ではノイズとしてモデルの学習を妨げています。

③ 学習時間が急増した理由

rot0.00(140.9秒)に対してrot0.03以降は176〜184秒と、 今回の実行では rotation を加えた条件で学習時間が長くなる傾向が見られました。 角度の大きさによる差はほぼなく、「rotationレイヤーが存在するかどうか」が 学習時間の主な分岐点です。精度が下がりながら時間が増えるという、 コストパフォーマンスが最悪な結果です。

④ 2回の実験を通じてわかったこと

前回の実験(flip〜cropの5パターン比較)では「rotationが強すぎる」という結論でした。 今回は「では弱くすれば?」という問いへの答えとして、 CIFAR-10においてはrotationの強度に関わらずflipのみが最良という結果になりました。
2回の実験を合わせると、次の結論が導けます。

  • CIFAR-10(32×32)では RandomFlip のみ が最もシンプルかつ効果的(低解像度では幾何変換の情報損失コストが大きい)
  • RandomRotation は 解像度が高いデータセットで有効 な手法
  • 「定番の組み合わせ」はデータセットの解像度を前提としており、そのままコピーしてはいけない

まとめ:CIFAR-10でのRandomRotation結論と解像度別推奨設定

今回の実験結果を踏まえた結論です。
今回の実験条件では、RandomRotation(0.03) のような小さい回転でも test accuracy は低下し、CIFAR-10(32×32)では flipのみが最も良い結果 になりました。

データセット・解像度RandomRotation の推奨理由
CIFAR-10(32×32)使わないどの角度でも精度が下がる
中解像度(64×64〜128×128)0.03〜0.05 で検証小さい角度から様子を見る
高解像度(224×224以上)0.10〜0.15被写体の特徴が十分残る

CIFAR-10では RandomFlip のみを使うのが最善です。
Augmentationは「多く使うほど良い」でも「弱くすれば良い」でもなく、 データセットの解像度と特性に合った手法を選ぶことが重要です。


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