Skip to content

Latest commit

 

History

History
84 lines (53 loc) · 8.14 KB

File metadata and controls

84 lines (53 loc) · 8.14 KB

勾配降下法(Gradient Descent)の数理と探索の可視化

「モデルの学習(パラメータ更新)は、損失関数の等高線図(地形)の上を、パラメータという点が下り坂(勾配の逆方向)を探しながら谷底を目指して下る旅である」という直感は、数学的にもプログラム的にも完全に正しい認識です。

本ドキュメントでは、この探索プロセスの数理的な意味と、最適化アルゴリズムによる軌跡(動き方)の違いについて解説します。


1. 勾配(Gradient)とは何か?

損失関数(エラーの大きさ)を $L$、モデルのパラメータ(重み)を $w = (w_1, w_2)$ とします。このとき、パラメータの各位置における勾配(微分値のベクトル)は以下のように定義されます。

$$\nabla L(w) = \left( \frac{\partial L}{\partial w_1}, \frac{\partial L}{\partial w_2} \right)$$

🧭 勾配が示す「方向」と探索ルール

  • 勾配の方向: 現在のパラメータの位置から**「最も急に上る(損失が最も増える)方向」**を指します。
  • 勾配の逆方向 ($-\nabla L$): したがって、そのマイナス方向(逆方向)は**「最も急に下る(損失が最も減る)方向」**を指します。

この「最も急な下り坂の方向」に向かって、一定の歩幅(学習率 $\eta$)だけパラメータをずらす操作が 勾配降下法(Gradient Descent) です。

$$w \leftarrow w - \eta \nabla L(w)$$


2. 探索の軌跡(等高線プロットの実証)

以下は、PyTorchの自動微分と最適化アルゴリズムを使用して、初期位置 $(-4.0, 3.0)$ から損失関数の谷底(最小値 $0.0$ である赤星の地点)を目指して探索させたシミュレーション結果です。

勾配降下法の探索軌跡

📊 2つの最適化アルゴリズムの動きの違い

この損失関数は、縦方向( $w_2$ 軸)の傾斜が急で、横方向( $w_1$ 軸)の傾斜がなだらかな「細長い谷」の形をしています。今回のシミュレーション画像(学習率 $\eta = 0.4$)の軌跡は以下の通りです。

  1. 標準的な SGD (Standard SGD / 青いライン):
    • 挙動: 勾配の方向(直近の下り坂)を信じて進みますが、急斜面である $w_2$ 方向へ大きく引っ張られ、かつ歩幅(学習率)が大きすぎるために谷の反対側の斜面まで飛び越えてしまい、結果として対向する斜面を何度も激しく往復するジグザグなバウンド動きになります。
    • 弱点: 谷の進行方向( $w_1$ 方向)への進みが非常に遅くなり、無駄なステップが多くなります。
  2. 慣性を加えた Momentum SGD (オレンジのライン):
    • 挙動: 前ステップまでの「進む勢い(慣性 / 速度ベクトル)」を蓄積しながら進みます。
    • 利点: 左右の急斜面へ往復するバウンドのエネルギーが、蓄積された慣性によって前後のステップで綺麗に相殺(打ち消し合い)されます。そのため、ジグザグの跳ね返りが強力に抑制され、谷の底に沿って滑らかに加速しながら突き進みます。これにより、SGDよりも圧倒的に早く谷底(赤星)へと到達できます。
    • ⚠️ 注意:なぜ今回のグラフではオレンジのほうが遠回り(ステップ数が多く)見えるのか? これは慣性による 「オーバーシュート(行き過ぎ)」 現象が発生しているためです。Momentumは「前回の勢い」を引き継ぐため、坂を一気に下りきった後、谷底( $0, 0$ )で急に止まれずに右側へ大きく通り過ぎてしまっています。そこから戻ろうとする際も勢いが余るため、中心をらせん状に回りながら収束することになり、ステップ数が多くなっています。 一方、青いライン(SGD)は慣性が 0 なので、谷底に近づいて勾配が小さくなると、その場でピタッと急ブレーキが効くため、今回の特定の学習率(0.4)では早く止まれたように見えています。実際の深層学習では、このオーバーシュートを防ぐために**「学習率の減衰(Decay)」**などのテクニックを併用します。

💡 学習率 $\eta$ の値と「ジグザグ振動」の数理

Standard SGDが急斜面でジグザグ(振動)するのか、あるいは滑らかに進むのかは、「斜面の急さ(曲率)」と「学習率 $\eta$」の掛け合わせによって数学的に決定されます。

今回の損失関数 $L(w_1, w_2) = 0.5 w_1^2 + 2.0 w_2^2$ の場合、各パラメータに対する更新式は以下のようになります。

  • $w_1 \leftarrow w_1 - \eta \cdot w_1 = (1 - \eta) w_1$ (なだらかな方向:曲率 $k_1 = 1.0$
  • $w_2 \leftarrow w_2 - \eta \cdot 4.0 w_2 = (1 - 4\eta) w_2$ (急な方向:曲率 $k_2 = 4.0$

ここで、括弧の中の係数( $1 - \eta k$ )の正負によって、更新時の挙動が3つのパターンに分かれます。

1. 滑らかに減少するケース($\eta < 0.25$)

もし学習率を $\eta = 0.12$ のように小さく設定すると、 $w_2$ の係数は $1 - 4 \times 0.12 = 0.52 &gt; 0$ となり、符号はプラスのままです。 この場合、パラメータは往復(バウンド)することなく、ジグザグせずに真っ直ぐ滑らかに $0$ に向かって収縮します。一見綺麗に見えますが、なだらかな $w_1$ 方向(係数 $0.88$)の進みが遅いため、全体としては谷底にたどり着くのに時間がかかります。

2. 激しくジグザグ振動するケース($0.25 < \eta < 0.5$)

今回のデモの設定である $\eta = 0.4$ のとき、 $w_2$ の係数は $1 - 4 \times 0.4 = -0.6 &lt; 0$ となり、**符号が更新のたびに反転(正 $\to$$\to$ 正)します。 $w_2$ の値は 3.0 $\to$ -1.8 $\to$ 1.08 $\to$ -0.65 と変化し、これがまさに等高線上で上下に激しく跳ね返る「ジグザグ(振動)運動」**の正体です。このときこそ、Momentumの「慣性による打ち消し」が真価を発揮します。

3. 発散(爆発)するケース($\eta > 0.5$)

もし学習率をさらに大きくし、 $\eta = 0.6$ に設定したとします。 $w_2$ の係数は $1 - 4 \times 0.6 = -1.4 &lt; -1$ となり、符号が反転するだけでなく、絶対値が毎ステップ $1.4$ 倍に拡大していきます。パラメータは谷底を飛び越えて激しく外側へ跳ね返り、無限大へと発散(オーバーフロー)して学習がクラッシュします。


3. なぜ自動微分(Backward)が必要なのか?

パラメータが $w_1, w_2$ の2つだけであれば、高校数学の微分の公式で簡単に勾配を計算できます。

しかし、実際のLLM(GPT-2など)では、パラメータの数は 1億2400万個(あるいは数千億個) に達します。手動で微分の式を解くことは物理的に不可能です。

そこで、PyTorchは**計算グラフ(Computational Graph)**という仕組みを内部で構築しています。

  • パラメータを requires_grad=True に設定しておくと、データが掛け算や足し算を通るたびに、その順序を自動で裏に記録します。
  • loss.backward() を1回呼び出すだけで、微分チェインルール(連鎖律)に従って、数億個のすべてのパラメータに対する勾配を一瞬で自動計算します。

📂 関連ファイルリンク

  • 探索可視化の実行コード: gradient_descent_demo.py (実際に動かしてパラメータの軌跡データをコンソールに表示し、画像を生成するスクリプトです)