Tech カテゴリのイメージ

勾配降下法とは?損失を見て重みを更新する最小の実験

「損失が4.667でした」と分かっても、それだけではモデルを改善できません。次に必要なのは、重みを少し増やしたときに損失が増えるのか減るのかという情報です。勾配降下法は、その傾きを使って重みを更新する方法です。

前の記事で扱った損失関数が「何を小さくしたいか」を決めるのに対し、勾配降下法は「どう動かすか」を決めます。この二つを混同すると、損失の値が出ているのに学習が進まない理由が見えにくくなります。

重みが一つだけなら、傾きは計算できる

入力を x、重みを w、正解を y として、予測を wx とします。説明のため損失を (wx − y)^2 と置くと、重み w に関する傾きは次のとおりです。

損失 L(w) = (wx − y)^2
傾き dL/dw = 2x(wx − y)
更新後の重み = w − 学習率 × dL/dw

傾きが正なら重みを小さくし、負なら大きくします。学習率は一度にどの程度動かすかを決める値です。傾きを見つける操作と、更新量を決める操作は別だと考えると整理しやすくなります。

5行の更新で正解へ近づく

入力 x=2 に対して正解が y=6 なら、狙う重みは3です。最初の重みを0、学習率を0.1として実行してみます。標準のPythonだけで動きます。

x, y = 2.0, 6.0
w = 0.0
learning_rate = 0.1
for step in range(5):
prediction = w * x
loss = (prediction - y) ** 2
gradient = 2 * x * (prediction - y)
print(f"{step}: w={w:.4f}, loss={loss:.4f}, gradient={gradient:.4f}")
w -= learning_rate * gradient
print(f"更新後: w={w:.4f}, loss={(w * x - y) ** 2:.4f}")

出力:

0: w=0.0000, loss=36.0000, gradient=-24.0000
1: w=2.4000, loss=1.4400, gradient=-4.8000
2: w=2.8800, loss=0.0576, gradient=-0.9600
3: w=2.9760, loss=0.0023, gradient=-0.1920
4: w=2.9952, loss=0.0001, gradient=-0.0384
更新後: w=2.9990, loss=0.0000

最初の傾きは -24 なので、0 − 0.1 × (-24) = 2.4 と更新されます。次の傾きは -4.8 となり、重みは2.88へ進みます。出力の最後の損失 0.0000 は小数第4位への丸めで、厳密にはゼロではありません。

学習率を大きくすれば速いわけではない

この例で学習率だけを 0.5 に変えると、最初の更新で重みは12になります。予測は24、損失は324へ増えます。目指す値3を大きく通り越したためです。逆に極端に小さい学習率なら、改善が遅くなります。

この一変数の二乗誤差では条件を計算しやすい一方、実際のニューラルネットワークでは多数の重みが互いに影響します。学習率が適切でも、すべての更新で必ず損失が減るとは限りません。特にデータの一部ずつで計算するミニバッチ学習では、各ステップの損失は上下します。

LLMでの役割は同じ、計算規模は違う

LLMでも次のトークン予測に対する損失を計算し、各パラメータの勾配を得て、最適化手法で更新するという骨格は共通です。ただし、ここに載せた1個の重みと二乗誤差をそのままLLMの実装へ広げるわけではありません。多くのパラメータの勾配を効率的に得るために、自動微分と誤差逆伝播が必要になります。

PyTorchの基本チュートリアルでも、予測と損失を計算したあと、loss.backward() で勾配を求め、optimizer.step() でパラメータを更新しています。勾配は標準では蓄積されるため、次の学習ステップに持ち越さないよう optimizer.zero_grad() も使います。更新式を手計算で追えたら、次は「多数の重みの勾配をどう効率よく求めるか」を見ていきます。

講座の前後記事

参照資料