Weight Decay(L2正則化)の強さで過学習はどう変わる?【Keras×CIFAR-10実験】

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

CIFAR-10 CNN Google Colab Keras L2正則化 過学習 画像分類

X f B! P L
Weight Decay(L2正則化)の強さで過学習はどう変わる?【Keras×CIFAR-10実験】 アイキャッチ画像

「Weight DecayはAdamWで使うもの?それともkernel_regularizerで直接設定するもの?そもそも強さはどう決めるの?」

今回はGoogle ColabとCIFAR-10を使い、L2正則化の強度(weight_decay)を0 / 1e-4 / 1e-3 / 1e-2の4パターンで比較しました。過学習の抑制効果と精度のトレードオフを実験で確認します。

📘 この記事でわかること

  • Weight Decay(L2正則化)の仕組みと設定方法(kernel_regularizer vs AdamW)
  • 強度ごとに精度・過学習の度合いがどう変わるか
  • 実務での適切なWeight Decayの目安

Weight Decayとは

Weight Decay(重み減衰)は損失関数に重みの二乗和を加算することで、重みが大きくなりすぎるのを抑制する正則化手法です。L2正則化とも呼ばれます。

正則化後の損失:\( L_{\text{total}} = L_{\text{CE}} + \lambda \sum_{i} w_i^2 \)
(\(\lambda\):正則化の強度=weight_decay、\(w_i\):各重みパラメータ)

\(\lambda\) を大きくするほど重みが小さく保たれ過学習が抑制されますが、大きすぎるとモデルが十分に学習できなくなります(アンダーフィット)。

Kerasでの2つの設定方法

Kerasでは主に2つの方法でWeight Decayを適用できます。今回は各層への直接指定(kernel_regularizer)を使います。

方法 コード例 特徴
kernel_regularizer Conv2D(..., kernel_regularizer=l2(λ)) 層ごとに強度を変えられる。今回使用
AdamW(Optimizer) AdamW(weight_decay=λ) 全パラメータに一括適用。設定が簡単

実験コード

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

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

# ── 環境準備(最初に一度だけ実行)──────────────────────
!apt-get -y install fonts-ipafont-gothic
!rm -rf /root/.cache/matplotlib
!pip install -q japanize_matplotlib
print("環境準備完了")
実行結果をクリックして内容を開く
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following additional packages will be installed:
  fonts-ipafont-mincho
The following NEW packages will be installed:
  fonts-ipafont-gothic fonts-ipafont-mincho
0 upgraded, 2 newly installed, 0 to remove and 51 not upgraded.
Need to get 8,237 kB of archives.
After this operation, 28.7 MB of additional disk space will be used.
Get:1 http://archive.ubuntu.com/ubuntu jammy/universe amd64 fonts-ipafont-gothic all 00303-21ubuntu1 [3,513 kB]
Get:2 http://archive.ubuntu.com/ubuntu jammy/universe amd64 fonts-ipafont-mincho all 00303-21ubuntu1 [4,724 kB]
Fetched 8,237 kB in 0s (28.5 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 33.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
from tensorflow.keras.regularizers import l2
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

# ── モデル構築(weight_decay以外は固定)──────────────────
def build_model(wd, name):
    reg = l2(wd) if wd > 0 else None
    return keras.Sequential([
        keras.layers.Input(shape=(32, 32, 3)),
        keras.layers.Conv2D(64,  (3, 3), activation='relu', padding='same',
                            kernel_regularizer=reg),
        keras.layers.MaxPooling2D((2, 2)),
        keras.layers.Conv2D(128, (3, 3), activation='relu', padding='same',
                            kernel_regularizer=reg),
        keras.layers.MaxPooling2D((2, 2)),
        keras.layers.GlobalAveragePooling2D(),
        keras.layers.Dense(128, activation='relu', kernel_regularizer=reg),
        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

⚠️ ハマりポイント:L2正則化の損失はtrain_lossに含まれる

kernel_regularizer=l2(λ)を設定すると、正則化項が train_loss に加算されます。そのため、Weight Decayが大きいパターンほど train_loss が高く見えますが、これは正則化ペナルティを含んだ値です。純粋な予測誤差の比較には val_loss(検証データには正則化ペナルティが加算されない)や test_accuracy を使って判断してください。

4パターンの学習実行

configs = [
    (0,    'A_wd0'),
    (1e-4, 'B_wd1e-4'),
    (1e-3, 'C_wd1e-3'),
    (1e-2, 'D_wd1e-2'),
]
histories, times, scores = {}, {}, {}

for wd, name in configs:
    print(f"\n=== {name} (weight_decay={wd}) ===")
    model = build_model(wd, name)
    h, t = compile_and_fit(model)
    s = model.evaluate(x_test, y_test, verbose=0)
    label = name.split('_')[1]
    histories[label] = h
    times[label]     = t
    scores[label]    = s
    print(f"学習時間:{t:.1f}秒 test_accuracy:{s[1]:.4f}")
実行結果をクリックして内容を開く
=== A_wd0 (weight_decay=0) ===
Epoch 1/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 10s 8ms/step - accuracy: 0.2623 - loss: 1.9361 - val_accuracy: 0.3333 - val_loss: 1.7672
Epoch 2/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.3755 - loss: 1.6759 - val_accuracy: 0.4272 - val_loss: 1.5708
Epoch 3/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.4320 - loss: 1.5476 - val_accuracy: 0.4682 - val_loss: 1.4698
Epoch 4/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.4677 - loss: 1.4643 - val_accuracy: 0.4933 - val_loss: 1.4000
Epoch 5/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.4919 - loss: 1.3938 - val_accuracy: 0.4999 - val_loss: 1.3636
Epoch 6/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.5108 - loss: 1.3393 - val_accuracy: 0.5379 - val_loss: 1.2701
Epoch 7/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5259 - loss: 1.2995 - val_accuracy: 0.5251 - val_loss: 1.2935
Epoch 8/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5397 - loss: 1.2681 - val_accuracy: 0.5519 - val_loss: 1.2339
Epoch 9/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5524 - loss: 1.2278 - val_accuracy: 0.5529 - val_loss: 1.2083
Epoch 10/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5582 - loss: 1.2147 - val_accuracy: 0.5431 - val_loss: 1.2560
Epoch 11/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5710 - loss: 1.1786 - val_accuracy: 0.5860 - val_loss: 1.1463
Epoch 12/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5828 - loss: 1.1513 - val_accuracy: 0.5827 - val_loss: 1.1441
Epoch 13/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5871 - loss: 1.1376 - val_accuracy: 0.5927 - val_loss: 1.1066
Epoch 14/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5957 - loss: 1.1142 - val_accuracy: 0.6009 - val_loss: 1.1109
Epoch 15/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6020 - loss: 1.1017 - val_accuracy: 0.5952 - val_loss: 1.1115
Epoch 16/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6109 - loss: 1.0769 - val_accuracy: 0.6105 - val_loss: 1.0642
Epoch 17/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6197 - loss: 1.0537 - val_accuracy: 0.6165 - val_loss: 1.0479
Epoch 18/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6194 - loss: 1.0499 - val_accuracy: 0.6099 - val_loss: 1.0836
Epoch 19/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.6302 - loss: 1.0281 - val_accuracy: 0.6100 - val_loss: 1.0687
Epoch 20/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6323 - loss: 1.0154 - val_accuracy: 0.6423 - val_loss: 0.9844
Epoch 21/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6424 - loss: 0.9960 - val_accuracy: 0.6423 - val_loss: 1.0051
Epoch 22/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6468 - loss: 0.9778 - val_accuracy: 0.6473 - val_loss: 0.9742
Epoch 23/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6520 - loss: 0.9694 - val_accuracy: 0.6395 - val_loss: 0.9975
Epoch 24/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6567 - loss: 0.9554 - val_accuracy: 0.6449 - val_loss: 0.9776
Epoch 25/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6616 - loss: 0.9425 - val_accuracy: 0.6599 - val_loss: 0.9396
Epoch 26/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.6660 - loss: 0.9296 - val_accuracy: 0.6564 - val_loss: 0.9412
Epoch 27/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6689 - loss: 0.9233 - val_accuracy: 0.6526 - val_loss: 0.9675
Epoch 28/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6712 - loss: 0.9090 - val_accuracy: 0.6688 - val_loss: 0.9207
Epoch 29/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6773 - loss: 0.8958 - val_accuracy: 0.6702 - val_loss: 0.9018
Epoch 30/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6813 - loss: 0.8887 - val_accuracy: 0.6704 - val_loss: 0.9129
学習時間:120.9秒 test_accuracy:0.6680

=== B_wd1e-4 (weight_decay=0.0001) ===
Epoch 1/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 9s 9ms/step - accuracy: 0.2639 - loss: 1.9522 - val_accuracy: 0.3261 - val_loss: 1.8123
Epoch 2/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.3649 - loss: 1.7162 - val_accuracy: 0.3904 - val_loss: 1.6788
Epoch 3/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.4167 - loss: 1.6151 - val_accuracy: 0.4476 - val_loss: 1.5256
Epoch 4/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.4505 - loss: 1.5325 - val_accuracy: 0.4679 - val_loss: 1.5163
Epoch 5/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.4809 - loss: 1.4670 - val_accuracy: 0.5020 - val_loss: 1.4224
Epoch 6/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.4994 - loss: 1.4187 - val_accuracy: 0.5234 - val_loss: 1.3579
Epoch 7/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5125 - loss: 1.3898 - val_accuracy: 0.5157 - val_loss: 1.3720
Epoch 8/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5262 - loss: 1.3513 - val_accuracy: 0.5478 - val_loss: 1.2992
Epoch 9/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5346 - loss: 1.3268 - val_accuracy: 0.5393 - val_loss: 1.2997
Epoch 10/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.5507 - loss: 1.2978 - val_accuracy: 0.5378 - val_loss: 1.3062
Epoch 11/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5569 - loss: 1.2815 - val_accuracy: 0.5388 - val_loss: 1.3144
Epoch 12/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5614 - loss: 1.2604 - val_accuracy: 0.5793 - val_loss: 1.2204
Epoch 13/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.5727 - loss: 1.2385 - val_accuracy: 0.5768 - val_loss: 1.2416
Epoch 14/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5782 - loss: 1.2197 - val_accuracy: 0.5808 - val_loss: 1.2206
Epoch 15/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5874 - loss: 1.2077 - val_accuracy: 0.5936 - val_loss: 1.1949
Epoch 16/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.5951 - loss: 1.1904 - val_accuracy: 0.5811 - val_loss: 1.2480
Epoch 17/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6007 - loss: 1.1718 - val_accuracy: 0.6023 - val_loss: 1.1748
Epoch 18/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6036 - loss: 1.1655 - val_accuracy: 0.6217 - val_loss: 1.1267
Epoch 19/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6139 - loss: 1.1445 - val_accuracy: 0.6215 - val_loss: 1.1331
Epoch 20/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6201 - loss: 1.1333 - val_accuracy: 0.6222 - val_loss: 1.1262
Epoch 21/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6213 - loss: 1.1236 - val_accuracy: 0.6224 - val_loss: 1.1164
Epoch 22/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.6274 - loss: 1.1072 - val_accuracy: 0.6246 - val_loss: 1.1099
Epoch 23/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6338 - loss: 1.0964 - val_accuracy: 0.6383 - val_loss: 1.0805
Epoch 24/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6409 - loss: 1.0849 - val_accuracy: 0.6132 - val_loss: 1.1471
Epoch 25/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6424 - loss: 1.0769 - val_accuracy: 0.6266 - val_loss: 1.1011
Epoch 26/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 7ms/step - accuracy: 0.6499 - loss: 1.0595 - val_accuracy: 0.6346 - val_loss: 1.0821
Epoch 27/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6532 - loss: 1.0542 - val_accuracy: 0.6469 - val_loss: 1.0671
Epoch 28/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6523 - loss: 1.0493 - val_accuracy: 0.6338 - val_loss: 1.1205
Epoch 29/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.6632 - loss: 1.0313 - val_accuracy: 0.6585 - val_loss: 1.0373
Epoch 30/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6623 - loss: 1.0283 - val_accuracy: 0.6509 - val_loss: 1.0510
学習時間:122.5秒 test_accuracy:0.6535

=== C_wd1e-3 (weight_decay=0.001) ===
Epoch 1/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 9s 9ms/step - accuracy: 0.2421 - loss: 2.0579 - val_accuracy: 0.3068 - val_loss: 1.8853
Epoch 2/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.3088 - loss: 1.8616 - val_accuracy: 0.3184 - val_loss: 1.8588
Epoch 3/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.3349 - loss: 1.8006 - val_accuracy: 0.3630 - val_loss: 1.7474
Epoch 4/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.3575 - loss: 1.7627 - val_accuracy: 0.3494 - val_loss: 1.7873
Epoch 5/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.3825 - loss: 1.7282 - val_accuracy: 0.3858 - val_loss: 1.7315
Epoch 6/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.4024 - loss: 1.6917 - val_accuracy: 0.4121 - val_loss: 1.6606
Epoch 7/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.4202 - loss: 1.6480 - val_accuracy: 0.4364 - val_loss: 1.6268
Epoch 8/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.4365 - loss: 1.6189 - val_accuracy: 0.4545 - val_loss: 1.5905
Epoch 9/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.4510 - loss: 1.5907 - val_accuracy: 0.4788 - val_loss: 1.5637
Epoch 10/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.4611 - loss: 1.5694 - val_accuracy: 0.4848 - val_loss: 1.5251
Epoch 11/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 6ms/step - accuracy: 0.4761 - loss: 1.5438 - val_accuracy: 0.4889 - val_loss: 1.5173
Epoch 12/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.4847 - loss: 1.5297 - val_accuracy: 0.4824 - val_loss: 1.5221
Epoch 13/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.4901 - loss: 1.5104 - val_accuracy: 0.5043 - val_loss: 1.4936
Epoch 14/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.4974 - loss: 1.4981 - val_accuracy: 0.5129 - val_loss: 1.4555
Epoch 15/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5050 - loss: 1.4806 - val_accuracy: 0.5202 - val_loss: 1.4465
Epoch 16/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.5113 - loss: 1.4685 - val_accuracy: 0.5303 - val_loss: 1.4427
Epoch 17/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5159 - loss: 1.4609 - val_accuracy: 0.5373 - val_loss: 1.4099
Epoch 18/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5183 - loss: 1.4537 - val_accuracy: 0.5339 - val_loss: 1.4161
Epoch 19/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.5300 - loss: 1.4265 - val_accuracy: 0.5492 - val_loss: 1.3901
Epoch 20/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5373 - loss: 1.4190 - val_accuracy: 0.5415 - val_loss: 1.3995
Epoch 21/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5415 - loss: 1.4057 - val_accuracy: 0.5525 - val_loss: 1.3883
Epoch 22/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5452 - loss: 1.3944 - val_accuracy: 0.5707 - val_loss: 1.3388
Epoch 23/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5508 - loss: 1.3848 - val_accuracy: 0.5614 - val_loss: 1.3506
Epoch 24/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5515 - loss: 1.3857 - val_accuracy: 0.5717 - val_loss: 1.3376
Epoch 25/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5590 - loss: 1.3685 - val_accuracy: 0.5475 - val_loss: 1.3870
Epoch 26/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.5606 - loss: 1.3688 - val_accuracy: 0.5706 - val_loss: 1.3303
Epoch 27/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5641 - loss: 1.3539 - val_accuracy: 0.5409 - val_loss: 1.3808
Epoch 28/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5660 - loss: 1.3468 - val_accuracy: 0.5841 - val_loss: 1.3164
Epoch 29/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.5659 - loss: 1.3432 - val_accuracy: 0.5753 - val_loss: 1.3314
Epoch 30/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5733 - loss: 1.3344 - val_accuracy: 0.5716 - val_loss: 1.3295
学習時間:121.8秒 test_accuracy:0.5755

=== D_wd1e-2 (weight_decay=0.01) ===
Epoch 1/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 8s 9ms/step - accuracy: 0.1861 - loss: 2.3113 - val_accuracy: 0.1918 - val_loss: 2.1563
Epoch 2/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.2079 - loss: 2.1218 - val_accuracy: 0.2156 - val_loss: 2.1036
Epoch 3/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.2095 - loss: 2.1013 - val_accuracy: 0.2032 - val_loss: 2.0975
Epoch 4/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.2178 - loss: 2.0827 - val_accuracy: 0.2230 - val_loss: 2.0803
Epoch 5/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.2437 - loss: 2.0470 - val_accuracy: 0.2745 - val_loss: 1.9924
Epoch 6/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.2697 - loss: 1.9908 - val_accuracy: 0.2747 - val_loss: 1.9616
Epoch 7/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.2768 - loss: 1.9578 - val_accuracy: 0.2946 - val_loss: 1.9210
Epoch 8/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.2824 - loss: 1.9389 - val_accuracy: 0.2979 - val_loss: 1.9194
Epoch 9/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.2900 - loss: 1.9331 - val_accuracy: 0.3007 - val_loss: 1.9005
Epoch 10/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.2981 - loss: 1.9202 - val_accuracy: 0.3108 - val_loss: 1.8863
Epoch 11/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.3042 - loss: 1.9103 - val_accuracy: 0.3073 - val_loss: 1.8822
Epoch 12/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.3099 - loss: 1.8996 - val_accuracy: 0.3116 - val_loss: 1.8949
Epoch 13/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.3133 - loss: 1.8948 - val_accuracy: 0.3274 - val_loss: 1.8674
Epoch 14/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.3176 - loss: 1.8864 - val_accuracy: 0.3297 - val_loss: 1.8580
Epoch 15/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.3184 - loss: 1.8803 - val_accuracy: 0.3400 - val_loss: 1.8477
Epoch 16/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.3187 - loss: 1.8781 - val_accuracy: 0.3303 - val_loss: 1.8583
Epoch 17/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.3222 - loss: 1.8684 - val_accuracy: 0.3244 - val_loss: 1.8631
Epoch 18/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.3248 - loss: 1.8646 - val_accuracy: 0.3319 - val_loss: 1.8389
Epoch 19/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.3246 - loss: 1.8615 - val_accuracy: 0.3337 - val_loss: 1.8364
Epoch 20/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.3279 - loss: 1.8618 - val_accuracy: 0.3338 - val_loss: 1.8651
Epoch 21/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.3264 - loss: 1.8599 - val_accuracy: 0.3365 - val_loss: 1.8320
Epoch 22/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.3260 - loss: 1.8567 - val_accuracy: 0.3368 - val_loss: 1.8271
Epoch 23/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.3280 - loss: 1.8497 - val_accuracy: 0.3239 - val_loss: 1.8623
Epoch 24/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.3317 - loss: 1.8448 - val_accuracy: 0.3376 - val_loss: 1.8213
Epoch 25/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.3299 - loss: 1.8411 - val_accuracy: 0.3405 - val_loss: 1.8181
Epoch 26/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.3315 - loss: 1.8375 - val_accuracy: 0.3228 - val_loss: 1.8653
Epoch 27/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.3351 - loss: 1.8412 - val_accuracy: 0.3462 - val_loss: 1.8189
Epoch 28/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.3293 - loss: 1.8403 - val_accuracy: 0.3459 - val_loss: 1.8089
Epoch 29/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 7ms/step - accuracy: 0.3323 - loss: 1.8332 - val_accuracy: 0.3345 - val_loss: 1.8228
Epoch 30/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.3293 - loss: 1.8328 - val_accuracy: 0.3473 - val_loss: 1.8071
学習時間:125.0秒 test_accuracy:0.3508

グラフ+サマリー

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

# ── train_loss vs val_loss(過学習の乖離)─────────────
fig2, axes2 = plt.subplots(2, 2, figsize=(14, 10))
axes2 = axes2.flatten()
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('weight_decay_overfit.png', dpi=150)
plt.show()

# ── サマリー ──────────────────────────────────────────
print("\n===== 最終結果サマリー =====")
print(f"{'Pattern':>10} | {'Val Acc':>8} | {'Test Acc':>9} | {'Time(s)':>8}")
print("-" * 46)
for label in ['wd0', 'wd1e-4', 'wd1e-3', 'wd1e-2']:
    val_acc  = histories[label].history['val_accuracy'][-1]
    test_acc = scores[label][1]
    t        = times[label]
    print(f"{label:>10} | {val_acc:>8.4f} | {test_acc:>9.4f} | {t:>8.1f}")
print("-" * 46)

最終結果サマリー

===== 最終結果サマリー =====
   Pattern |  Val Acc |  Test Acc |  Time(s)
----------------------------------------------
       wd0 |   0.6704 |    0.6680 |    120.9
    wd1e-4 |   0.6509 |    0.6535 |    122.5
    wd1e-3 |   0.5716 |    0.5755 |    121.8
    wd1e-2 |   0.3473 |    0.3508 |    125.0
----------------------------------------------

実験結果

精度グラフ

精度グラフ

損失グラフ

損失グラフ

wd0

wd0

wd1e-4

wd1e-4

wd1e-3

wd1e-3

wd1e-2

wd1e-2
パターン 最終 val_accuracy 最終 test_accuracy 学習時間
A:weight_decay=0(なし) ⭐ 最高精度0.67040.6680120.9秒
B:weight_decay=1e-40.65090.6535122.5秒
C:weight_decay=1e-30.57160.5755121.8秒
D:weight_decay=1e-20.34730.3508125.0秒

考察

Weight Decayを強くするほど単調に精度が下がった

今回の実験では wd=0(test: 0.6680)→ wd=1e-4(0.6535)→ wd=1e-3(0.5755)→ wd=1e-2(0.3508)と、Weight Decayを強くするほど精度が単調に悪化しました。wd=1e-2 に至ってはランダム予測(10クラスで約10%)の3倍程度まで落ちており、完全なアンダーフィットです。この結果は「Weight Decayをかければ過学習が防げて精度が上がる」という期待とは正反対で、使い方を間違えると性能を大きく損なうことを実験で示しています。

なぜ wd=0 が最高精度になったのか——Dropoutがすでに十分効いている

今回のモデルにはすでに Dropout(0.2) が入っています。Dropoutが過学習を十分に抑制しているため、そこにさらに Weight Decay を上乗せすると正則化の二重がかけになり、モデルが正解を学習しきれなくなるのです。wd=1e-4 ですら val_accuracy が -1.5pt 悪化していることから、今回の構成では「正則化なし+Dropout」が最適解という結果になりました。

wd=1e-3 と wd=1e-2 の急激な崩壊

wd=1e-3 で val_accuracy が 0.6704→0.5716 と約10pt 落ち、wd=1e-2 では 0.3508 まで崩壊しています。L2正則化はすべての Conv 層・Dense 層の重みに一括適用されるため、層数が多いほどペナルティが累積して効きすぎる傾向があります。今回は Conv2D×2+Dense×1 の計3層に適用しているため、1e-3 以上では累積ペナルティが損失を支配してしまいます。

Weight Decayを有効活用するための条件

Weight Decay が効果を発揮するのは主に以下の条件が揃ったときです。まず Dropout を使わない構成(または弱くした構成)で試すこと。次に 1e-5 程度の非常に弱い値 から段階的に強めること。また AdamW を使う場合は weight_decay の適用メカニズムが通常の L2 正則化とは異なるため(更新ステップへの直接適用)、kernel_regularizer=l2() とは別物として扱う必要があります。今回の実験条件(Dropout あり、浅い CNN)では wd=0 のままにするか、試すなら 1e-5 以下 が現実的な出発点です。

train_lossの比較には注意

L2正則化項は train_loss に加算されますが val_loss には含まれません。wd が大きいほど train_loss が高く見えますが、これはペナルティを含んだ値です。モデルの汎化性能の比較は必ず val_accuracy または test_accuracy で行ってください。


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

まとめ

  • Weight Decay(L2正則化)は損失に重みの二乗和を加えて重みの肥大化を防ぐ正則化手法
  • 今回の実験では wd=0(なし)が最高精度。wd を強くするほど単調に精度が低下した
  • 原因は Dropout=0.2 がすでに十分な正則化を提供しており、Weight Decay が過剰な二重正則化になったため
  • wd=1e-2 では test_accuracy が 0.35 まで崩壊——設定を誤ると性能を大きく損なうことを実験で確認
  • Weight Decay を使うなら Dropout なし(または弱め)の構成で 1e-5 以下から 試すのが安全
  • 過学習の確認は val_accuracy / test_accuracy で。train_loss は正則化ペナルティを含むため比較に不向き