ひとことで言うと
L1ペナルティは重みの絶対値を、L2ペナルティは重みの二乗を損失に加えます。同じ「重みを小さくする」処理でも、L1は小さい重みをゼロへ押し込み、L2は全体を滑らかに縮める点が実装上の分岐です。
L1は細い枝を根元から切る剪定、L2はすべての枝を同じ比率で短くする剪定です。どちらも木を小さくしますが、残る枝の本数は同じになりません。
なぜ必要か
損失を 、パラメータを 、係数を とすると、目的関数はデータ損失にペナルティを足した形になります。L1とL2では勾配の形が違うため、同じ学習率・同じ でも更新の軌跡が変わります。
実装では、L2ペナルティを optimizer の weight_decay として指定することが多く、「損失に足す場合」と「optimizerが勾配に項を足す場合」を同じ更新則で読めることが必要です。
| 見る対象 | L1 | L2 |
|---|---|---|
| 更新を決める量 | 符号(ゼロでは劣勾配) | 重みそのもの |
| 典型的な結果 | ゼロが生じる | 非ゼロのまま縮む |
仕組み
L1とL2を次のように定義します。 はパラメータの第 成分、 はデータ損失、 は罰則の強さです。
L2では微分がそのまま になるため、通常のSGD(学習率を 、データ損失の勾配を とします)は次になります。
データ勾配がゼロなら、毎回 倍です。これが weight decay と呼ばれる理由です。ただし momentum や AdamW では更新の解釈が変わるため、試験ではまず「勾配に を加えるSGD」の条件を確認します。
L1の微分はゼロで連続ではありません。 では符号関数 、ゼロでは劣勾配 を使います。
そのためゼロ付近では、データ損失の勾配がこの範囲に収まると更新をゼロで止められます。L2は非ゼロの重みをゼロにしにくく、L1は疎なパラメータを作りやすい差があります。バイアス項は入力に依存しないオフセットで重みとは役割が違うため、通常は罰則から外します。
試験でどう問われるか
| 問われ方 | 正解に寄る条件 | 引っかけ |
|---|---|---|
| L1とL2の勾配を比較 | L1は符号、L2は重みに比例 | L1の勾配を常に重みそのものとする |
| L2の更新則を読む | を学習率で更新し、勾配ゼロなら一定割合で縮む | 毎回一定量を引くとする |
| ゼロの扱い | L1はゼロで劣勾配を選び、ゼロに止まり得る | ゼロでも通常の微分値が一意にあるとする |
| 罰則の対象を選ぶ | 通例は重みだけで、バイアスを除外 | 全パラメータへ必ず同じ罰則をかける |
実装で確かめる
データ損失の勾配をゼロに固定し、L2の割合縮小とL1の更新をNumPyで確認します。ゼロで選ぶ劣勾配も明示します。
import numpy as np
theta = np.array([2.0, -1.0, 0.0])
eta, lam = 0.1, 0.2
l2 = theta - eta * (lam * theta)
assert np.allclose(l2, (1 - eta * lam) * theta)
subgrad = np.sign(theta)
subgrad[2] = 0.0 # ゼロで選ぶ劣勾配
l1 = theta - eta * lam * subgrad
assert np.allclose(l1, [1.98, -0.98, 0.0])
print(l2, l1)
取り違えやすいもの
| 用語 | 切り分け |
|---|---|
| L1ペナルティ | 符号を使う。ゼロ解・疎性が生じやすい |
| L2ペナルティ | 重みに比例する勾配。SGDでは weight decay と同じ形で読める |
| weight decay | 実装上の呼び名。単純SGDではL2型の勾配項として現れる |
| バイアスの更新 | 入力に依存しないオフセット。通常はL1/L2の対象外 |
想起チェック
L2のデータ勾配がゼロのとき、1回の更新で重みはどうなるか
は 倍になります。一定量を引くのではなく、現在値に比例して縮みます。
L1で重みがゼロに止まり得る理由は何か
ゼロでの劣勾配が の範囲を持つためです。データ損失側の勾配を相殺する値を選べます。
バイアスを通常は正則化しない理由は何か
バイアスは入力に依存しない全体のオフセットで、重みとは役割が違うためです。