Weight Decay
ウェイト減衰は、モデルの重みをゼロに近づける正則化手法です。ニューラルネットワークの過学習を抑え、新しいデータへの汎化を促します。
ウェイト減衰は、トレーニング中にモデルの学習済みの重みをゼロに近づける正則化手法です。このように繰り返し重みを縮小することで、不必要に大きなパラメーター値を抑え、ニューラルネットワークが未知のデータ例に汎化しやすくなります。モデルの容量が十分に大きく、トレーニングデータのノイズまで記憶してしまう可能性がある場合に、特に有効です。
Geoffrey Hintonは、1986年の論文概念の分散表現の学習でウェイト減衰を提案しました。家系図を使った実験では、学習した結びつきを解釈しやすくするため、更新のたびに重みの大きさを0.2%減らしました。その後、ウェイト減衰は過学習を制御する手法として広く使われるようになりました。
ウェイト減衰の仕組み#
勾配降下法では、オプティマイザーが勾配を使ってパラメーターを更新します。勾配とは、各パラメーターが損失関数に与える影響を示す導関数です。ウェイト減衰は、パラメーターをゼロに近づける力を加えます。多くのオプティマイザーで使われる学習率を掛ける方式では、モーメンタムを使わない通常のSGDの更新式はnew weight = (1 - learning rate × decay) × old weight - learning rate × gradientです。この更新では、予測誤差からの学習と、それに比例した重みの縮小を組み合わせます。
減衰係数は、ゼロに近づける力の強さを制御します。係数がゼロの場合は無効になり、正の値を大きくすると、一定の学習率のもとで縮小が強まります。予測の改善につながる場合は、タスクの勾配によって重みが増加することもあります。学習率減衰はオプティマイザーのステップ幅を段階的に小さくするのに対し、ウェイト減衰はパラメーター値に作用します。
ウェイト減衰とL2正則化の違い#
L2正則化では、重みの二乗和に係数を掛けた値をトレーニングの目的関数に加えます。通常の確率的勾配降下法では、このペナルティを微分すると、係数を適切に合わせることで、ウェイト減衰と同じ比例的な縮小が生じます。線形回帰では、この二乗重みペナルティを使ったフィッティングをリッジ回帰と呼びます。
AdamオプティマイザーでL2ペナルティを加えると、ペナルティの勾配はAdamの移動平均とパラメーターごとのスケーリングを通じて処理されます。一方、AdamWは、この適応的な勾配計算とは別に重みを縮小します。Ilya LoshchilovとFrank Hutterは、2017年の論文分離型ウェイト減衰正則化でAdamWを提案し、この論文はICLR 2019で発表されました。この分離により、減衰の強さがAdamの勾配履歴によるスケーリングに左右されなくなります。
減衰の強さを選ぶ#
ウェイト減衰は、検証データを使って選択するハイパーパラメーターです。値が小さすぎると、過学習を十分に抑制できません。減衰が強すぎると未学習になり、モデルが有用なパターンを学習できず、トレーニングデータと検証データの両方で誤差が大きくなります。減衰の設定ごとに検証スコアを比較すると、特定のモデルとデータセットに適した強さを判断できます。
累積的な効果は、学習率スケジュールとオプティマイザーの更新回数にも左右されます。バッチサイズやトレーニングのエポック数を変更すると、減衰が適用される回数も変わることがあります。勾配蓄積では、複数のバッチをまとめてから更新します。ドロップアウト層は活性化をランダムにゼロにすることで正則化し、早期終了はトレーニング期間を制限します。
Ultralytics YOLOでは、トレーニング設定weight_decayを使って正則化を制御できます。デフォルト値は0.0005で、YOLO26などのモデルをトレーニングする際に使用します。オプティマイザーのパラメーターグループでは、バイアスと正規化層のパラメーターをウェイト減衰の対象から除外します。
PyTorchでのウェイト減衰#
torch.optim.AdamWオプティマイザーは、PyTorchで分離型ウェイト減衰を提供します。pip install torchでパッケージをインストールします。この例ではタスクの勾配をゼロにし、出力からウェイト減衰だけによる更新を確認します。
import torch
weight = torch.nn.Parameter(torch.tensor([2.0]))
optimizer = torch.optim.AdamW([weight], lr=0.1, weight_decay=0.1)
before = weight.item()
# Create a zero gradient while keeping the parameter in the computation.
optimizer.zero_grad()
loss = (weight * 0).sum()
loss.backward()
optimizer.step()
print(f"Weight: {before:.2f} -> {weight.item():.2f}")出力はWeight: 2.00 -> 1.98です。1回の更新で重みに1 - 0.1 × 0.1を掛けるため、重みの大きさが1%減少し、0.99になります。










