論文一覧に戻る 📚 用語集トップ 🗺 概念マップ
📚 用語解説
📚 用語解説
勾配降下法
Gradient Descent
深層学習
別称: GD

🔖 キーワード索引

勾配降下法GDSGD学習率最適化深層学習

🔖 拡張キーワード索引

この用語『勾配降下法』を理解するうえで併せて押さえたい関連キーワード群です。 クリック(ホバー)で関連用語ページに飛べます。

勾配降下法 SGD ミニバッチ 学習率 Adam Momentum RMSProp AdamW ニューラルネット学習 誤差逆伝播 凸最適化 鞍点

💡 30秒で分かる結論

🍰 まずはやさしく

正解に近づくための階段のような方法です。

間違いをできるだけ少なくするために使います。

スマホのアプリが学習する仕組みに似ています。

この章では基本のやり方と注意点を読みます。

勾配降下法 ── 勾配を使って損失を最小化する手法

📍 文脈 ── どこで出会うか

🍰 まずはやさしく

AIの心臓部となる重要な仕組みです。

データの分析で最適な答えを出すために使います。

部活の記録から傾向を出すときなどに役立ちます。

この章では定義や使い方を順番に読みます。

深層学習の心臓部。 線形回帰、 ロジスティック回帰、 ニューラルネットすべてのパラメータ最適化はこれが基本。

本ページでは「gradient descent」を扱う。 統計データ分析コンペティション (2026) の教材で、 SSDSE-B-2026 (47 都道府県 × 複数年 × 100 超列) の実データを使った再現可能な学習を目指す。

「gradient descent」は統計・データサイエンスの体系における重要概念のひとつ。 本ページは「定義・直感・数式・実装・落とし穴・関連手法」の 6 視点で構成され、 各視点は独立して読めるが順序通り読むと体系的な理解が得られる。

🎨 直感で掴む

🍰 まずはやさしく

霧の中での山下りにたとえられる方法です。

一番低い谷底にたどり着くために使います。

買い物で一番安い店をさがす感覚に似ています。

この章では仕組みや歩き方のコツを読みます。

山下りの比喩:

歩幅 = 学習率。 大き過ぎると崖を越えて反対側に行く、 小さ過ぎると永遠に着かない。

🎨 直感を深掘り

勾配降下法は『目的関数 $L(\theta)$ を最小化するため、 $\theta$ を勾配 $\nabla L$ の逆方向に少しずつ動かす』最適化アルゴリズム。 機械学習の学習はほぼすべてこの方法か、 その発展形(SGD, Adam, AdamW 等)で行われる。 一度に全データを使う Batch GD、 ランダム 1 サンプルの SGD、 中間の Mini-batch GD があり、 後者が深層学習の標準。

勾配降下法は SGD → Momentum (Polyak 1964) → AdaGrad (Duchi 2011) → RMSProp (Hinton 2012) → Adam (Kingma 2014) → AdamW (Loshchilov 2017) と進化し、 現代 DNN は AdamW + cosine decay が標準。 LR を 1e-3〜3e-4 で開始し warmup + decay する設定が ResNet/Transformer 系で安定し、 Llama2 学習も基本この組合せ。

本ページでは SSDSE-B-2026 を使った線形回帰を勾配降下法で解く例を示す: L(θ) = (1/2n)Σ(yᵢ - θ₀ - θ₁xᵢ)² を θ₀, θ₁ で偏微分し、 47 都道府県の (総人口, 出生数) で標準化のうえ学習率 η を変えながら損失曲線が収束する様子を観察する。 これにより、 closed-form 解 (X'X)⁻¹X'y と勾配降下解が一致することを実体験できる。

🎮 学習率を変えて降下を見る

損失関数の曲線の上で、 勾配降下法がどう動くかを自分の手で動かして確かめられる。 学習率 η スライダーを動かし、 グラフをクリック(タップ)して開始点を決め、 「▶ ステップ実行」または「⏯ 自動再生」で更新式 xt+1 = xt − η · L′(xt) の 1 歩ずつを観察しよう。 η が小さい=遅い、 適度=収束、 大きすぎ=振動/発散 を目で体感できる。

関数:
💡 グラフをクリック(またはタップ)すると、 その x を開始点にできます。 緑▲ = 大域的最小、 赤● = 現在位置、 橙 = これまでの経路。
反復 txL(x)L′(x)(勾配)
0
開始点をクリックしてください

🔎 動かして分かること
凸関数 f(x)=0.25x²+0.5(勾配 L′=0.5x)は数学的に厳密で、 更新は xt+1 = xt(1 − 0.5η) となる。 よって η<2 で単調に谷へ、 2<η<4 で反対側へ飛び越えつつ収束η=4 で一定振幅の振動η>4 で発散。 これは凸・L-平滑な問題の収束条件 0<η<2/L(ここでは L=0.5)そのもの。
多峰性では、 開始点の位置で落ちる谷が変わる。 浅い谷に捕まると勾配が 0 でも大域的最小には届かない ── これが 局所最適解大域的最適解 の違い。 右側からスタートすると局所解に停留しやすい。

📐 定義/数式

🍰 まずはやさしく

計算式で表した正解へのルートです。

数値をどう書き換えるかを決めるために使います。

テストの点数を上げるための計画表のようなものです。

この章では数式の記号の意味について読みます。

【勾配降下法の更新式】
$$ \theta_{t+1} = \theta_t - \eta \, \nabla L(\theta_t) $$
$\eta$ = 学習率、 $\nabla L$ = 損失の勾配
【SGD(確率的勾配降下法)】
$$ \theta_{t+1} = \theta_t - \eta \, \nabla L_i(\theta_t) $$
1サンプル $i$ の勾配で更新。 ノイジーだが計算が軽い

📐 数式の読み解き ── 勾配降下法 の核心式

$$ \theta_{t+1} = \theta_t - \eta \nabla_\theta L(\theta_t) $$

勾配降下法。 $\eta$ は学習率、 $L$ は損失関数。

数式の各記号が『何の量で、 どの空間に住み、 どんな単位を持つか』を意識すると、 暗記でなく構造として理解できます。 SSDSE-B の都道府県データに当てはめて、 各シンボルが何に対応するかを上の Python 実装で確認しましょう。

❓ FAQ ── 勾配降下法 のよくある質問

Q1. 勾配降下法 を初めて学ぶ場合、 何から始めればよい?

まずは本ページの『💡 30 秒で分かる結論』と『🎨 直感で掴む』で全体像を掴み、 次に『🧮 実値で計算してみる』を 手を動かして追体験するのが最短です。 数式や深い理論はその後で十分。

Q2. 勾配降下法 と似た手法との違いは?

本ページの『🌐 関連手法・派生』『🔗 関連用語』で対比される手法を確認し、 それぞれの適用条件得意・不得意を表で比較するのが効果的です。 SSDSE-B のような共通データセットで両方走らせて結果を見ると違いが体感できます。

Q3. 勾配降下法 の計算量・スケーラビリティは?

最急降下法の 1 反復は $O(nd)$(全サンプル $n$ × 特徴数 $d$ を 1 回なめる)で、 反復 $T$ 回なら $O(nTd)$。 SSDSE の 47 都道府県 × 5 変数なら 1 反復が 235 回の積和で、 1000 反復しても 23.5 万回。一瞬です。 ところが $n=10^6$ になると 1 反復で 500 万回、 1000 反復で 50 億回になり、 「1 反復あたり全データを読む」ことが効いてきます。 ここで効くのが確率的勾配降下法(SGD)で、 1 反復をミニバッチ $b$ 件に絞れば $O(bTd)$。 $b=32$ なら $n$ に依存しなくなるので、 データが 100 万件でも 1 反復のコストは 47 件のときと変わりません。 「$n$ が効く反復法は、バッチ化で $n$ を切り離せないか」を最初に疑うのが定石です。

Q4. 勾配降下法 の結果をどう報告すべき?

『点推定値』だけでなく『不確実性(CI、 SE、 分散)』『前提条件のチェック結果』『代替手法との比較』『データ取得日と seed』をセットで報告するのが標準。 査読・レビューで問われる典型ポイントです。

📐 GD の収束保証:凸性と滑らかさの条件

GD には数学的な収束保証がある。 関数の性質によって「何 iteration で目標精度 ε に到達するか」が定理化されている。

関数クラスGD の収束加速 GD の収束
凸 + L-Lipschitz 勾配O(1/T)O(1/T²)
強凸 + L-LipschitzO((1-μ/L)^T) 線型O((1-√(μ/L))^T)
非凸(一般)局所最適への収束のみ保証同左
非凸 + 良性(深層 NN)経験的にうまく動く理論未解明

深層学習が「理論なしで動く」のは、 非凸であるにも関わらず 1 次法でうまく収束するから。 この理由は active な研究分野(loss landscape, neural tangent kernel, mean field theory)。 SSDSE の線形回帰は強凸なので GD の理論保証が完全に成立する例。

📝 章末まとめ:勾配降下法を 8 つの命題で復習

  1. GD は「損失関数の坂を負勾配方向に下る」反復解法。 凸関数なら大域最適、 非凸なら局所最適に収束。
  2. 更新式 $\theta_{t+1} = \theta_t - \eta \nabla L(\theta_t)$。 学習率 η が最重要パラメータ。
  3. η が大きすぎ → 発散、 小さすぎ → 収束遅延。 「ちょうど良い」は二次曲率の逆数オーダ。
  4. Batch / Mini-batch / SGD の使い分けはデータサイズと計算資源で決まる。 大規模では Mini-batch が標準。
  5. Momentum / Nesterov / Adam などの派生は「振動抑制」「適応的学習率」「先読み」で収束を加速。
  6. 局所最適・鞍点・勾配消失 / 爆発は実装時の三大ハマりどころ。 初期化・正則化・スケーリングで対処。
  7. 特徴量のスケール統一(StandardScaler 等)は必須前処理。 これなしで GD は動かないことも多い。
  8. 線形モデルなら解析解 / L-BFGS、 深層学習なら Adam が現代の標準。 GD は基礎であり万能薬ではない。

🔬 数式を言葉で読み解く

$\theta$
モデルパラメータ(重み・バイアス)
$\eta$
学習率(step size)。 0.001〜0.1が典型
$\nabla L$
損失の勾配ベクトル(各パラメータでの偏微分)
バッチサイズ
1更新あたりのサンプル数
エポック
全データを1回見終わる単位

🧮 実値で計算してみる

1次元の例:$L(\theta) = (\theta - 3)^2$

🧮 SSDSE-B 実値で計算してみる ── 勾配降下法

都道府県データで線形回帰モデル『就業者数 = w × 人口 + b』を勾配降下で学習。 SSDSE-B の人口(A1101)と就業者数(C3301)を使い、 ミニバッチ(10 県ずつ)で w, b を更新していく。

項目 条件 / 入力 結果 / 解釈
学習率 α=0.00110 epochloss 0.85 → 0.41
学習率 α=0.0110 epochloss 0.85 → 0.12
学習率 α=0.110 epochloss 0.85 → 発散
Momentum β=0.9α=0.01より速い収束
Adam α=0.001デフォルトロバスト
SGD + warmup5 epoch warmup学習安定

※ 数値は SSDSE-B-2026.csv から抽出した実値、 もしくは典型的な学習設定での目安値です。 細部の数値は前処理・乱数 seed・実装により変動します。

🧮 補足: vanilla GD / SGD / Momentum / Adam の収束比較 (SSDSE-B-2026)

勾配降下法には多数の派生があるが、 何が違うのかは 同じデータで損失曲線を重ね描き しないと体感できない。 SSDSE-B-2026 の総人口 → 出生数の単回帰を題材に、 4 手法を PyTorch optim で並行訓練し、 反復ごとの損失を比較する。

数式を言葉で読み解く: 4 つの更新則の差分

Vanilla GD は θ ← θ - η∇L。 SGD は同式だがミニバッチで ∇L を近似。 Momentum は v ← βv + ∇Lθ ← θ - ηv で過去勾配を引きずる。 Adam は m, v の 1 次・2 次モーメントを推定し θ ← θ - η m̂/(√v̂+ε) で各次元の学習率を自動調整する。 SSDSE-B-2026 のように特徴量スケールが百万〜千万のとき、 Adam の自動スケーリングが効く。

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) A4101(出生数) 北海道 5,092,000 24,430 東京都 14,086,000 86,348 沖縄県 1,468,000 12,549 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import pandas as pd
import numpy as np
import torch
import torch.nn as nn

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == df['SSDSE-B-2026'].max()]
xr = df['A1101'].values.astype(float)
yr = df['A4101'].values.astype(float)
X = torch.tensor((xr - xr.mean()) / xr.std(), dtype=torch.float32).unsqueeze(1)
y = torch.tensor((yr - yr.mean()) / yr.std(), dtype=torch.float32).unsqueeze(1)

def train(opt_name, lr=0.05, epochs=200):
    torch.manual_seed(0)
    model = nn.Linear(1, 1)
    if opt_name == 'GD':       opt = torch.optim.SGD(model.parameters(), lr=lr)
    if opt_name == 'SGD':      opt = torch.optim.SGD(model.parameters(), lr=lr)
    if opt_name == 'Momentum': opt = torch.optim.SGD(model.parameters(), lr=lr, momentum=0.9)
    if opt_name == 'Adam':     opt = torch.optim.Adam(model.parameters(), lr=lr)
    losses = []
    for _ in range(epochs):
        pred = model(X); loss = ((pred - y) ** 2).mean()
        opt.zero_grad(); loss.backward(); opt.step()
        losses.append(loss.item())
    return losses

for name in ['GD', 'SGD', 'Momentum', 'Adam']:
    hist = train(name)
    print(f'{name:9s}: final loss = {hist[-1]:.6f}, '
          f'epoch_to_loss<0.05 = {next((i for i,v in enumerate(hist) if v<0.05), -1)}')

🎯 このコードでやること: SSDSE-B-2026 の (A1101, A4101) で 4 つの optimizer を 200 epoch 学習し、 最終損失と収束 epoch を比較する。

📥 入力データ (SSDSE-B-2026 の 2023 年 47 都道府県、 標準化済み):

都道府県 A1101_std A4101_std 北海道 0.88 0.53 東京都 4.13 4.18 大阪府 2.21 2.35

📤 実行結果:

GD : final loss = 0.009129, epoch_to_loss<0.05 = 17 SGD : final loss = 0.009129, epoch_to_loss<0.05 = 17 Momentum : final loss = 0.009129, epoch_to_loss<0.05 = 5 Adam : final loss = 0.009129, epoch_to_loss<0.05 = 19

💬 結果の読み方: 47 県の単回帰は線形で凸なので、 どの optimizer も最終損失はほぼ同じ 0.0091 付近($1-r^2$、 相関 $r=0.995$)に収束する。 違いは「収束速度」。 Momentum は 5 epoch で損失 0.05 を切り、 vanilla GD と SGD は 17 epoch、 Adam は 19 epoch とやや慎重に進む。 高次元・非凸(ニューラルネット)になると Adam の優位が顕著になる。

⚠️ 落とし穴: Adam なら何でも勝てるは誤り

🧭 勾配降下法の総合理解 ── 体系的拡張パート

ここでは勾配降下法(Gradient Descent, GD)の挙動を、 単に「重みを更新するアルゴリズム」として暗記するのではなく、 損失曲面の幾何・更新ベクトルの方向と大きさ・学習率の役割・確率的サンプリングの効果という 4 つの軸で再構成する。 SSDSE-B-2026(47 都道府県の社会経済指標)を用いて、 線形回帰・ロジスティック回帰・浅い MLP の 3 タスクに対し、 同一の初期値から GD/SGD/Mini-batch/Momentum/Adam を走らせ、 収束速度・最終損失・パラメータの最終位置を直接比較する。 これにより、 抽象的な「最適化アルゴリズムの選択」が、 具体的な数値挙動として腹落ちすることを目指す。

🔖 拡張キーワード索引

バッチ勾配降下 確率的勾配降下 ミニバッチ Momentum Nesterov AdaGrad RMSProp Adam AdamW 学習率スケジュール 線形回帰 ロジスティック回帰 凸関数 非凸最適化 SSDSE-B-2026

📐 勾配降下法の 4 つの軸を再定義する

勾配降下法を理解する上で押さえるべき軸は次の 4 つである。 教科書では「式を覚えろ」とだけ書かれがちだが、 それぞれの軸が独立に効くので、 まずは分けて整理する。

軸 1:方向。 損失 $L(\theta)$ を局所的に最も急に減らす方向は 負の勾配 $-\nabla L(\theta)$ である。 これは線形近似 $L(\theta+\Delta) \approx L(\theta) + \nabla L(\theta)^\top \Delta$ から、 $\|\Delta\|$ を固定したときに右辺を最小化する $\Delta$ が $-\nabla L(\theta)/\|\nabla L(\theta)\|$ になることから導かれる。 これは「下りる方向の選択」であり、 凸でも非凸でも局所的には成立する。

軸 2:歩幅。 一歩あたりの移動量を決めるのが学習率 $\eta$ である。 大きすぎれば発散し、 小さすぎれば収束が遅い。 凸かつ $L$ が $L$-平滑(リプシッツ勾配定数 $L_{\rm smooth}$)であれば $\eta \le 1/L_{\rm smooth}$ が安全な選択であることが知られる。 実務ではこの理論値を直接使わず、 学習率探索(LR Range Test)や warmup + cosine schedule で代替する。

軸 3:サンプリング。 損失 $L(\theta) = \frac{1}{n} \sum_{i=1}^n \ell(\theta; x_i, y_i)$ の勾配を毎ステップ全データで計算するのがバッチ GD、 1 サンプルだけで近似するのが SGD、 数十〜数百サンプルで近似するのがミニバッチ GD である。 サンプリングはノイズを導入するが、 そのノイズが鞍点脱出やフラットミニマ選好に寄与することが近年わかってきた。

軸 4:履歴。 過去の勾配情報をどう活かすか。 Momentum は速度ベクトルを慣性として保ち、 Adam は各座標ごとに勾配の二乗和で歩幅を正規化する。 履歴を持つことで「過去に何度も同じ方向に押された座標は今回も同じ方向に大きく動く」「ノイズで激しく振動する座標は歩幅を縮める」といった適応的挙動が可能になる。

📊 主要最適化アルゴリズムの比較表(軸ごとの位置づけ)

アルゴリズム サンプル数/step 学習率の扱い 履歴の扱い 代表的用途
バッチ GD$n$ 全件固定 or scheduleなし小規模・凸問題
SGD1 件固定 or decayなし大規模・オンライン
Mini-batch GD32〜512 件固定 or scheduleなし深層学習標準
Momentumミニバッチ固定 or schedule速度ベクトル画像分類 SOTA
Nesterovミニバッチ固定 or schedule先読み速度理論で収束加速
AdaGradミニバッチ座標別に縮小勾配二乗累積疎データ・NLP 初期
RMSPropミニバッチ座標別に正規化勾配二乗 EMARNN 学習
Adamミニバッチ座標別に正規化速度 + 二乗 EMA汎用標準
AdamWミニバッチ座標別に正規化速度 + 二乗 EMA + decoupled wdTransformer 学習

読み方:「バッチ GD → Mini-batch GD → Momentum → Adam」は、 軸 3(サンプリング)→ 軸 4(履歴)の順に高度化していく流れになっている。 Adam だけを覚えても理解は深まらない。 各軸ごとに何が追加されたかを意識する。

🎨 直感を絵で掴む ── 3 枚の図で勾配降下法を見る

勾配降下法は「数値が動く軌跡」「分布」「グループ間の違い」の 3 視点で観察すると、 動作原理が腹落ちする。 ここでは SSDSE-B-2026 の都道府県データを使って 3 つの異なる視点を提示する。

散布図:SSDSE-B 都道府県データの 2 変数散布図(勾配降下法が最適化する線形回帰の対象)
図 R642-A:散布図で見る勾配降下法の出発点。 SSDSE-B-2026 の都道府県人口(A1101)と関連指標の散布図。 勾配降下法は、 このデータ点群に対して「最も誤差が小さい直線」のパラメータ(傾き $w$ と切片 $b$)を、 損失関数の勾配を頼りに少しずつ調整して見つける。

上の散布図は、 GD が解くべき「線形回帰タスクそのもの」を視覚化したものである。 各点までの垂直距離の二乗平均を損失 $L(w, b) = \frac{1}{n}\sum_i (y_i - (w x_i + b))^2$ として、 $(w, b)$ 空間で滑り降りるのが GD だ。 損失関数は $(w, b)$ について凸放物面になるので、 学習率さえ間違えなければ必ず大域最小に達する。

ヒストグラム:勾配の大きさ分布(学習率設計の根拠)
図 R642-B:ヒストグラムで見る学習率の決め方。 都道府県データの分布。 勾配降下法では「特徴量の分布スケール」が学習率の妥当性を決める。 例えば人口(A1101)は $10^4 \sim 10^7$ のレンジを持つので、 標準化なしで学習率 0.05 を使うと勾配が爆発する。

ヒストグラムから読み取れるのは「データのスケール」である。 GD の学習率はデータのスケールに依存するため、 標準化(zero mean, unit variance)またはスケーリング(min-max)が事前に必要だ。 47 都道府県のような小規模データでも、 1 特徴量で 10⁶ 倍のスケール差は普通にある。 標準化前のスケールを把握するためにヒストグラムは必須の前処理ツールである。

箱ひげ図:optimizer 別の最終損失分布(10 回平均)
図 R642-C:箱ひげ図で見るアルゴリズム間の比較。 複数グループの箱ひげ図。 勾配降下法の文脈では、 異なる optimizer(SGD / Momentum / Adam)を 10 回ずつ走らせた最終損失の分布を表す。 Adam は中央値が低いが箱が小さい(再現性高い)、 SGD はバラつきが大きい、 という典型パターンが現れる。

箱ひげ図は、 同じ optimizer を複数回走らせたときの「結果の安定性」を可視化するのに最適だ。 SGD のように確率的要素を持つ optimizer は、 ミニバッチサンプルの順序によって最終損失が異なる。 箱の高さが安定性、 中央値が平均的性能、 ひげの長さが最悪ケースを意味する。 「中央値が低くて箱も小さい」optimizer が実務的に最強だ。

🧮 SSDSE-B-2026 で 47 都道府県の線形回帰を GD で解く

SSDSE-B-2026 のうち A1101(総人口)と A4101(出生数)を選び、 標準化したうえで $y = w \cdot x + b$ の最小二乗フィットを GD で解く。 手計算と Python 実装を並べて、 学習率を 0.001 / 0.01 / 0.1 / 0.5 と変えた場合の挙動を比較する。

表 R642-1:学習率と収束挙動の関係(200 epoch 後の最終損失)
学習率 $\eta$ 10 epoch 損失 50 epoch 損失 200 epoch 損失 判定
0.0010.9650.8240.456遅すぎ
0.010.6980.1460.009やや遅
0.050.1580.0090.009適切
0.10.0270.0090.009速いが OK
0.50.0090.0090.009上限付近・高速収束
1.01.0001.0001.000発散(振動)

読み方:47 県の単回帰(特徴量 1 個、 サンプル 47 個、 標準化済み)の場合、 損失 $L=\mathrm{mean}(\mathrm{err}^2)$ のヘッセ行列は $2\cdot\mathrm{Var}(x)=2$ なので、 収束条件は $\eta < 2/2 = 1$。 表でも $\eta = 0.5$ までは高速に収束し(10 epoch で損失 0.009)、 $\eta = 1.0$ で損失が 1.0 のまま下がらず発散していることが見て取れる。 凸かつ平滑な問題は理論値で安全領域を計算できる。

🐍 Python 実装 1 ── 純粋 numpy で GD ループを書く

🎯 このコードでやること:SSDSE-B-2026 の A1101(総人口) と A4101(出生数) を読み込み、 標準化後に GD ループを numpy 手書きで実装する。 ライブラリに頼らず、 勾配計算と更新式が一致するかを確認する。

📥 入力データ (SSDSE-B-2026 の 2023 年 47 都道府県、 標準化前):

SSDSE-B-2026 列: Code, Prefecture, A1101 (総人口), A4101 (出生数), ... 標準化前のスケール: A1101 ≈ 54万〜1409万、 A4101 ≈ 3千〜8.6万 標準化後 (mean=0, std=1): 北海道 +0.88, 東京 +4.13, 大阪 +2.21
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import numpy as np
import pandas as pd

# SSDSE-B-2026 を読み込む(cp932、 単位行をスキップ)
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == df['SSDSE-B-2026'].max()]

x_raw = df['A1101'].values.astype(float)  # 総人口
y_raw = df['A4101'].values.astype(float)  # 出生数

# 標準化(GD には必須の前処理)
x = (x_raw - x_raw.mean()) / x_raw.std()
y = (y_raw - y_raw.mean()) / y_raw.std()

# 初期パラメータ
w, b = 0.0, 0.0
eta = 0.05
n = len(x)

# GD ループ
history = []
for epoch in range(200):
    pred = w * x + b
    err = pred - y
    loss = (err ** 2).mean()
    # 勾配は ∂L/∂w = 2/n Σ err_i * x_i、 ∂L/∂b = 2/n Σ err_i
    grad_w = 2.0 * (err * x).mean()
    grad_b = 2.0 * err.mean()
    w -= eta * grad_w
    b -= eta * grad_b
    history.append(loss)

print(f'最終パラメータ: w = {w:.4f}, b = {b:.4f}')
print(f'最終損失: {history[-1]:.6f}')
print(f'損失推移: epoch 0 → {history[0]:.4f}, '
      f'epoch 50 → {history[50]:.4f}, epoch 200 → {history[-1]:.4f}')

📤 実行結果:

最終パラメータ: w = 0.9954, b = -0.0000 最終損失: 0.009129 損失推移: epoch 0 → 1.0000, epoch 50 → 0.0092, epoch 200 → 0.0091

💬 結果の読み方:標準化済みデータでは正解 $w = \mathrm{Cov}(x,y)/\mathrm{Var}(x) = \rho(x,y) \approx 0.995$、 $b \approx 0$ となる。 GD は学習率 0.05・200 epoch で $w = 0.9954$ に到達し、 最小二乗の解析解($w = 0.995425$、 閉形式で求まる)とほぼ一致する。 この問題は凸なので GD は解析解へ収束するが、 学習率が小さすぎれば(表の $\eta=0.001$ を参照)200 epoch では届かない ── 「学習率と epoch 数のトレードオフ」は常につきまとう。 解析解が存在しない非線形モデルでは GD が唯一の選択肢になる。

🐍 Python 実装 2 ── 学習率を変えて挙動を比較する

🎯 このコードでやること:同じデータに対し $\eta \in \{0.001, 0.01, 0.05, 0.1, 0.5, 1.0\}$ で GD を走らせ、 表 R642-1 の数値を再現する。 学習率が大きすぎると発散する現象を直接観察できる。

📥 入力データ: 直前のコードと同じ標準化済み $(x, y)$。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import numpy as np
import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == df['SSDSE-B-2026'].max()]
x = df['A1101'].values.astype(float)
y = df['A4101'].values.astype(float)
x = (x - x.mean()) / x.std()
y = (y - y.mean()) / y.std()

def run_gd(eta, epochs=200):
    w, b = 0.0, 0.0
    history = []
    for _ in range(epochs):
        pred = w * x + b
        err = pred - y
        loss = (err ** 2).mean()
        if not np.isfinite(loss):
            history.append(float('nan'))
            break
        history.append(loss)
        w -= eta * 2.0 * (err * x).mean()
        b -= eta * 2.0 * err.mean()
    return history

for eta in [0.001, 0.01, 0.05, 0.1, 0.5, 1.0]:
    h = run_gd(eta)
    vals = [h[i] if i < len(h) else float('nan') for i in [9, 49, 199]]
    print(f'eta={eta:5.3f} | 10ep={vals[0]:.4f} | 50ep={vals[1]:.4f} | 200ep={vals[2]:.4f}')

📤 実行結果:

eta=0.001 | 10ep=0.9649 | 50ep=0.8235 | 200ep=0.4558 eta=0.010 | 10ep=0.6979 | 50ep=0.1460 | 200ep=0.0094 eta=0.050 | 10ep=0.1579 | 50ep=0.0092 | 200ep=0.0091 eta=0.100 | 10ep=0.0270 | 50ep=0.0091 | 200ep=0.0091 eta=0.500 | 10ep=0.0091 | 50ep=0.0091 | 200ep=0.0091 eta=1.000 | 10ep=1.0000 | 50ep=1.0000 | 200ep=1.0000

💬 結果の読み方:表 R642-1 と一致する。 $\eta = 0.5$ は安定限界 $\eta < 1$ の内側なので 10 epoch で損失 0.009 まで一気に収束する。 一方 $\eta = 1.0$ では更新が対称点へ跳ね返り、 損失が 1.0 のまま 200 epoch を通じて一度も下がらない。 GD 実装では「損失曲線が下がっているか」を全 epoch にわたって確認し、 理論的な安定境界 $\eta < 1$ を超えないことが重要である。

🐍 Python 実装 3 ── ミニバッチ SGD と Adam を比較する

🎯 このコードでやること:47 県を 8 サンプルずつのミニバッチに分け、 SGD / Momentum / Adam の 3 通りで 100 epoch 学習。 各 epoch の最終損失を記録し、 収束パターンを比較する。

📥 入力データ: 標準化済みの (A1101, A4101) 47 県、 ミニバッチサイズ 8。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import numpy as np
import pandas as pd
np.random.seed(0)   # 実行のたびに同じ結果が出るようにする

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == df['SSDSE-B-2026'].max()]
x = df['A1101'].values.astype(float); y = df['A4101'].values.astype(float)
x = (x - x.mean()) / x.std()
y = (y - y.mean()) / y.std()
n = len(x)
batch = 8
eta = 0.05

def sgd_step(w, b, xb, yb):
    err = (w * xb + b) - yb
    return w - eta * 2.0 * (err * xb).mean(), b - eta * 2.0 * err.mean()

def momentum_step(w, b, xb, yb, vw, vb, mu=0.9):
    err = (w * xb + b) - yb
    gw = 2.0 * (err * xb).mean(); gb = 2.0 * err.mean()
    vw = mu * vw + gw; vb = mu * vb + gb
    return w - eta * vw, b - eta * vb, vw, vb

def adam_step(w, b, xb, yb, t, mw, mb, vw, vb, beta1=0.9, beta2=0.999, eps=1e-8):
    err = (w * xb + b) - yb
    gw = 2.0 * (err * xb).mean(); gb = 2.0 * err.mean()
    mw = beta1 * mw + (1 - beta1) * gw; mb = beta1 * mb + (1 - beta1) * gb
    vw = beta2 * vw + (1 - beta2) * gw ** 2; vb = beta2 * vb + (1 - beta2) * gb ** 2
    mhw = mw / (1 - beta1 ** t); mhb = mb / (1 - beta1 ** t)
    vhw = vw / (1 - beta2 ** t); vhb = vb / (1 - beta2 ** t)
    return (w - eta * mhw / (vhw ** 0.5 + eps),
            b - eta * mhb / (vhb ** 0.5 + eps), mw, mb, vw, vb)

results = {}
for name in ['SGD', 'Momentum', 'Adam']:
    w, b, vw, vb, mw, mb = 0.0, 0.0, 0.0, 0.0, 0.0, 0.0
    losses = []
    t = 0
    for epoch in range(100):
        idx = np.random.RandomState(epoch).permutation(n)
        for start in range(0, n, batch):
            t += 1
            ix = idx[start:start + batch]
            xb, yb = x[ix], y[ix]
            if name == 'SGD':
                w, b = sgd_step(w, b, xb, yb)
            elif name == 'Momentum':
                w, b, vw, vb = momentum_step(w, b, xb, yb, vw, vb)
            else:
                w, b, mw, mb, vw, vb = adam_step(w, b, xb, yb, t, mw, mb, vw, vb)
        # epoch 終了時の全体損失
        losses.append(((w * x + b - y) ** 2).mean())
    results[name] = losses

for name, h in results.items():
    print(f'{name:9s}: 1ep={h[0]:.4f}, 10ep={h[9]:.4f}, 50ep={h[49]:.4f}, 100ep={h[-1]:.4f}')

📤 実行結果:

SGD : 1ep=0.2848, 10ep=0.0091, 50ep=0.0091, 100ep=0.0091 Momentum : 1ep=0.0444, 10ep=0.0093, 50ep=0.0094, 100ep=0.0098 Adam : 1ep=0.5135, 10ep=0.0093, 50ep=0.0092, 100ep=0.0092

💬 結果の読み方:3 つの optimizer の最終損失はほぼ同じ(約 0.009)に到達するが、 初期の収束速度が異なる。 Momentum は 1 epoch で既に損失 0.044 まで下がり最速、 SGD は 0.285、 Adam は 0.514 と最初に出遅れる(バイアス補正の初期挙動)。 ここで重要なのは「Adam は万能ではない」点だ。 線形回帰のような単純な凸問題では Momentum の方が速い。 Adam が真価を発揮するのは、 勾配のスケールが座標ごとに大きく異なる非凸問題(深層学習)である。

🐍 Python 実装 4 ── 学習率スケジュールの効果

🎯 このコードでやること:固定学習率 vs cosine annealing スケジュールの比較。 同じデータ、 同じ初期値、 同じアルゴリズムで、 スケジュール導入だけで収束精度がどう変わるかを観察。

📥 入力データ: 標準化済み (A1101, A4101)、 200 epoch、 初期学習率 0.1。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import numpy as np
import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == df['SSDSE-B-2026'].max()]
x = df['A1101'].values.astype(float); y = df['A4101'].values.astype(float)
x = (x - x.mean()) / x.std()
y = (y - y.mean()) / y.std()
eta0 = 0.1
epochs = 200

def cosine_lr(epoch, eta0, T):
    return 0.5 * eta0 * (1.0 + np.cos(np.pi * epoch / T))

results = {}
for sched in ['fixed', 'cosine']:
    w, b = 0.0, 0.0
    losses = []
    for ep in range(epochs):
        eta = eta0 if sched == 'fixed' else cosine_lr(ep, eta0, epochs)
        pred = w * x + b
        err = pred - y
        losses.append((err ** 2).mean())
        w -= eta * 2.0 * (err * x).mean()
        b -= eta * 2.0 * err.mean()
    results[sched] = (w, b, losses)

for sched, (w, b, h) in results.items():
    print(f'{sched:7s}: w={w:.6f}, b={b:.6f}, '
          f'final loss={h[-1]:.6f}')
print(f'最小二乗解析解: w=0.995425, b=0.000000')

📤 実行結果:

fixed : w=0.995425, b=-0.000000, final loss=0.009129 cosine : w=0.995425, b=-0.000000, final loss=0.009129 最小二乗解析解: w=0.995425, b=0.000000

💬 結果の読み方:この凸問題では、 学習率 $\eta = 0.1$ は十分小さく安全な領域なので、 fixed でも 200 epoch あれば解析解に到達する。 cosine スケジュールの真価は「最初は大きな歩幅で広い領域を探索し、 後半は小さな歩幅で精密フィット」という点にあるため、 学習率を 0.5 のような大きな値から開始した場合に最も効く。 深層学習では cosine + warmup が標準だ。

📊 SSDSE-B-2026 都道府県別の勾配の大きさ

SGD では「1 サンプル」を選んで勾配を計算する。 SSDSE-B-2026 のように都道府県をサンプルとする場合、 どの県を選ぶかで勾配の大きさが大きく異なる。 標準化済み線形回帰モデル $L = (wx + b - y)^2$ の勾配は、 県ごとに次のようになる。

表 R642-2:勾配の大きさが大きい上位 5 県と小さい下位 5 県(標準化値、 初期値 $w=0$ で評価)
順位 都道府県 標準化人口 $x$ 標準化出生数 $y$ 勾配 $|2xy|$(初期)
1東京都+4.13+4.1834.52
2神奈川県+2.38+2.2710.80
3大阪府+2.21+2.3510.37
4愛知県+1.75+1.946.77
5埼玉県+1.69+1.575.31
43新潟県-0.19-0.270.10
44宮城県-0.14-0.190.05
45京都府-0.04-0.090.01
46広島県+0.03+0.070.00
47茨城県+0.06-0.030.00

読み方:東京都の勾配は他県の数倍〜数十倍($|2xy|\approx34.5$)。 SGD で東京都を引いた回には大きく動き、 平均に近い茨城・広島を引いた回はほとんど動かない($|2xy|\approx0$)。 ミニバッチ化(複数県を一度に処理)するとこの偏りが平均化され、 学習が安定する。 これがミニバッチ SGD が SGD より好まれる理由の一つ。

📊 損失曲面の幾何 ── 凸 vs 非凸

勾配降下法の挙動を決める最大の要因は「損失曲面の形」である。 凸関数では局所最小 = 大域最小なので、 GD は必ず最良解に到達する。 非凸関数では局所最小・鞍点・プラトーが存在し、 初期値とサンプリング戦略次第で異なる解に収束する。

表 R642-3:損失曲面の典型パターンと GD の挙動
曲面タイプ 代表モデル GD の到達点 対策
凸放物面線形回帰(MSE)大域最小(一意)学習率管理のみ
凸(非放物面)ロジスティック回帰大域最小(一意)学習率管理のみ
細長い谷悪条件回帰大域最小(遅い)Momentum, 標準化
複数の極小深層 MLP初期値依存の局所解SGD ノイズ, 多回試行
鞍点高次元 NN停滞する可能性Momentum, ノイズ
プラトー飽和活性化を持つ NN勾配 ≈ 0 で停滞ReLU, BatchNorm

📊 学習率スケジュール一覧

表 R642-4:主要な学習率スケジュール
スケジュール名 数式 特徴 代表用途
Fixed$\eta_t = \eta_0$最も単純凸問題・短い学習
Step Decay$\eta_t = \eta_0 \cdot \gamma^{\lfloor t/k \rfloor}$階段状に減少ResNet 学習で標準
Exponential$\eta_t = \eta_0 \cdot e^{-kt}$滑らかに減少古典 NN
Cosine$\eta_t = \frac{\eta_0}{2}(1 + \cos(\pi t/T))$滑らかな減衰 → 0画像分類 SOTA
Cosine + Warmup線形 ramp + cosine初期不安定回避Transformer 学習
One Cycle三角波 + 末尾減衰Super-convergencefast.ai 流派
Cyclic三角波を反復鞍点脱出効果非凸モデル探索

📊 ハイパーパラメータの典型値(実務目安)

表 R642-5:optimizer 別ハイパーパラメータの実務目安
タスク 推奨 optimizer 初期 lr weight decay batch size
線形回帰バッチ GD or 解析解0.01〜0.10全件
ロジスティック回帰L-BFGS or SGD0.01〜0.11e-4全件 or 大
ResNet 画像分類SGD + Momentum 0.90.1(warmup)1e-4256
BERT 事前学習AdamW1e-40.01256〜8192
GPT 事前学習AdamW6e-40.1512〜数百万
RL DQNAdam1e-4032〜256

⚠️ 追加の落とし穴 ── GD 実装でハマる 8 パターン

❓ FAQ ── 勾配降下法の素朴な疑問

Q1: 線形回帰には解析解(正規方程式)があるのに、 なぜ GD を使うのか?
A1: サンプル数が 100 万を超えると、 正規方程式の行列演算($O(n d^2 + d^3)$ メモリ・計算量)が現実的でなくなる。 GD は反復ごとに $O(n d)$ で済み、 巨大データに対応できる。 また GD は非線形モデル(NN)でも同じフレームワークで使える汎用性が魅力。
Q2: 学習率が小さければ常に安全か?
A2: 学習率を小さくすれば発散はしないが、 収束が極端に遅くなる。 また、 SGD のノイズが小さくなりすぎて、 鞍点や悪い局所解にハマる可能性も上がる。 「ノイズを残す程度のやや大きめ + 後半で減衰」が定石。
Q3: バッチサイズはいくつが良いか?
A3: 計算効率とノイズのバランスで決まる。 GPU メモリが許す範囲で大きい方が並列化効率は高い。 しかし、 バッチサイズが大きすぎると勾配ノイズが減り、 汎化性能が悪化することが報告されている(Keskar et al. 2017)。 画像分類なら 256、 言語モデルなら数千〜数万が標準。
Q4: Momentum と Adam、 どちらを使えば良い?
A4: 画像分類・CNN なら SGD + Momentum 0.9 + cosine schedule が今でも SOTA。 NLP・Transformer なら AdamW が標準。 「迷ったら AdamW」で大きく間違えることは少ない。
Q5: GD と Newton 法はどう違う?
A5: Newton 法は二次の情報(ヘッセ行列)も使うので 1 ステップあたり精度は高い。 しかし $d \times d$ の行列の逆行列計算 $O(d^3)$ が必要で、 $d$ が 10⁶ を超える深層学習では非現実的。 GD は一次情報だけで $O(d)$。 大規模問題では一次情報のみが現実解。
Q6: SGD の確率性は本当に良いことか?
A6: 凸問題では確率性は単なるノイズだが、 非凸問題では「鞍点から脱出する力」「フラットミニマを好む傾向」として作用する。 LeCun et al. (2015) や Keskar et al. (2017) などで実証的に確認されている。

🗺 勾配降下法の概念マップ

勾配降下法は「最適化」という大樹の幹であり、 そこから多くの枝が伸びている。 上位概念・並列概念・派生概念をツリー状に整理する。

最適化(Optimization) ├─ 一次法(勾配のみを使う) │ ├─ バッチ勾配降下(Batch GD)── 凸問題で理論保証 │ ├─ 確率的勾配降下(SGD)── 大規模・オンライン学習 │ ├─ ミニバッチ GD ── 深層学習の標準 │ ├─ Momentum 系 │ │ ├─ Heavy-ball Momentum(古典) │ │ ├─ Nesterov Accelerated Gradient(理論的に最適) │ │ └─ NAG + 適応学習率 │ └─ 適応的学習率系 │ ├─ AdaGrad(疎データに強い) │ ├─ RMSProp(RNN 用) │ ├─ Adam(汎用標準) │ ├─ AdamW(weight decay 分離) │ └─ Lion(2023, Google) ├─ 二次法(ヘッセ行列も使う) │ ├─ Newton 法 │ ├─ Quasi-Newton(BFGS, L-BFGS) │ ├─ Conjugate Gradient │ └─ Trust Region ├─ 進化計算系(勾配を使わない) │ ├─ 遺伝的アルゴリズム │ ├─ CMA-ES │ └─ 粒子群最適化 └─ ベイズ最適化(高コスト関数向け) ├─ Gaussian Process └─ Tree-structured Parzen Estimator

🎯 理解度チェック ── 勾配降下法

  1. Q1:標準化していない SSDSE-B-2026 の人口(A1101、 値域 10⁴〜10⁷)に GD を直接適用したらどうなるか? 学習率 0.05 で予想される結果を述べよ。
    (答えの方向:勾配が 10⁵〜10⁶ オーダーになり、 1 ステップで重みが巨大な値に飛ぶ。 損失が NaN/Inf に発散。 標準化が必須)
  2. Q2:凸問題で「学習率 0.5 では 10 epoch まで順調に下がっていたのに 50 epoch で発散した」という観察から何が分かるか?
    (答えの方向:振動が成長していた可能性が高い。 損失曲線全体を見るべきで、 序盤だけでは判断できない)
  3. Q3:バッチサイズ 32 を 256 に増やすとき、 学習率はどう調整するべきか?
    (答えの方向:Linear Scaling Rule により、 lr も 8 倍にするのが定石。 ただし warmup を入れて急増を緩和する)
  4. Q4:SGD + Momentum 0.9 と Adam、 どちらが線形回帰に向いているか。 理由とともに述べよ。
    (答えの方向:Momentum の方が速く収束する。 線形回帰の損失曲面は凸放物面で、 座標ごとに勾配スケールが大きく違わないので、 Adam の正規化機構の恩恵が少ない)
  5. Q5:「Adam のデフォルト lr=0.001 を 0.1 にしたら学習が爆発した」という現象を説明せよ。
    (答えの方向:Adam は勾配を二乗 EMA で正規化するので、 実効的な歩幅は lr に比例する。 0.001 と 0.1 は 100 倍違い、 多くの問題では発散する範囲)
  6. Q6:SSDSE-B-2026 で東京都だけを抜いて GD を学習すると、 結果はどう変わるか?
    (答えの方向:東京都は標準化値の絶対値が大きい外れ値的存在で、 勾配への寄与が大きい。 抜くと最小二乗解が変わる。 ただし方向は変わらず、 傾きがやや小さくなる程度)
  7. Q7:「学習率を毎 epoch 半分にする」スケジュールは何と呼ばれるか。 利点・欠点を述べよ。
    (答えの方向:Exponential decay または Step decay の一種。 利点は実装が単純。 欠点は減衰が速すぎて後半は実質ゼロ学習率になり、 微調整できない)
  8. Q8:GD と進化計算系手法(GA, CMA-ES)の最大の違いは何か?
    (答えの方向:GD は勾配(一次情報)を使う、 進化計算は使わない。 GD は非微分可能な目的関数では使えないが進化計算は使える。 一方、 GD は大規模問題(数百万パラメータ)で圧倒的に速い)

🧭 GD 関連の重要論文・歴史

表 R642-6:勾配降下法の歴史的マイルストーン
提唱者 手法 貢献
1847Cauchy最急降下法GD の原型を提唱
1951Robbins & Monro確率近似(SGD 原型)確率的勾配法の理論基盤
1964PolyakHeavy-ball Momentum慣性項の導入
1983NesterovNesterov Accelerated Gradient凸問題で理論最適収束
2011Duchi et al.AdaGrad座標別適応学習率
2012Tieleman & HintonRMSProp指数移動平均で AdaGrad 改良
2014Kingma & BaAdamMomentum + RMSProp の融合
2017Loshchilov & HutterAdamWweight decay の分離
2017SmithOne Cycle Learning RateSuper-convergence の発見
2023Chen et al. (Google)Lion記号関数で軽量化

📚 関連用語と学習の次の一歩

勾配降下法を理解した次に学ぶべきトピックを、 前提・並列・発展の 3 区分で整理する(実在する用語ページはリンク済み、 未整備のものは href="#" プレースホルダ)。

🧪 まとめ ── 勾配降下法を腹落ちさせるための再整理

勾配降下法は「損失の偏微分の符号反転方向に少しだけ動く」という極めてシンプルな原理を持つ。 しかし、 この単純さに次の 4 つの工夫が乗ることで、 GPT・Stable Diffusion・AlphaGo といった現代 AI の学習を可能にしている。

  1. サンプリング:全件 GD → ミニバッチ → SGD と段階的にサンプル数を減らし、 計算効率とノイズ導入を両立。
  2. 履歴の活用:Momentum で速度を保ち、 RMSProp/Adam で座標別歩幅を正規化。 過去情報の二段階利用。
  3. 学習率スケジュール:warmup で安定起動、 cosine で滑らかに終息。 単一の固定 lr では届かない領域に到達。
  4. 標準化と正規化:データ前処理(標準化)、 内部レイヤー正規化(BatchNorm, LayerNorm)、 重み正則化(weight decay)の三位一体。

SSDSE-B-2026 のような 47 サンプル × 数百列の小さなデータセットでも、 GD の挙動・学習率の感度・サンプリングノイズの効果はすべて観察できる。 数値を一度自分の手で動かすことが、 GD という概念を腹落ちさせる最短ルートである。

🧮 数式に値を入れて手で計算する: 勾配降下 3 ステップ

f(x) = (x-3)² の勾配降下で 3 ステップ更新する。

Step 1: 初期値と微分

x₀ = 0, lr = 0.5 f'(x) = 2(x-3)

Step 2: 更新

x₁ = 0 - 0.5·(2·(0-3)) = 0 - (-3) = 3 ※ 一気に到達 やり直し lr=0.3 で: x₁ = 0 - 0.3·(-6) = 1.8 x₂ = 1.8 - 0.3·(-2.4) = 2.52 x₃ = 2.52 - 0.3·(-0.96) = 2.808 収束方向: 3 へ

🐍 Python で再現

1
2
3
4
5
6
7
def f(x): return (x-3)**2
def df(x): return 2*(x-3)
x = 0.0
lr = 0.3
for i in range(3):
    x = x - lr * df(x)
    print(f"x_{i+1} = {x:.3f}, f = {f(x):.4f}")

📤 実行結果

x_1 = 1.800, f = 1.4400 x_2 = 2.520, f = 0.2304 x_3 = 2.808, f = 0.0369

💬 手計算 (Step 2) x₃ = 2.808 と Python 出力が完全一致。

🐍 Python 実装

最小限のスニペットで動作確認できる例。 公的データ(SSDSE 等)を想定しています。

🎯 このコードでやること:$L(\theta)=(\theta-3)^2$ の最小点 θ=3 を勾配降下法で逐次探索。 numpy で手動実装したループと、 PyTorch の SGD を使った同じ計算を比較します。
📥 入力例(SSDSE-B-2026 を念頭に置いた抽象化) # 初期パラメータと学習率 theta_0 = 0.0 # 47 都道府県データの平均推定で言えば「初期値ゼロ」 lr = 0.2 # 学習率(大きすぎると発散、 小さすぎると遅い) n_steps = 20 # 反復回数
📤 実行例(期待出力) 2.9998903... # numpy 実装で θ → 3 に収束 2.9998903... # PyTorch SGD でも同じく θ → 3
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import numpy as np

# シンプルな勾配降下
def gd(theta=0.0, lr=0.2, n_steps=20):
    for _ in range(n_steps):
        grad = 2 * (theta - 3)
        theta -= lr * grad
    return theta

print(gd())  # 約 3.0

# PyTorch の SGD
import torch
theta = torch.zeros(1, requires_grad=True)
optim = torch.optim.SGD([theta], lr=0.2)
for _ in range(20):
    loss = (theta - 3) ** 2
    optim.zero_grad(); loss.backward(); optim.step()
print(theta.item())
💬 読み方:勾配 $\nabla L = 2(\theta-3)$ の方向(θ より大きければ+、 小さければ-)に学習率 lr 倍ステップで降下。 lr=0.2 で 20 ステップで十分収束。 PyTorch では zero_grad → backward → step の3 連動が定形。

🐍 SSDSE-B を使った Python 実装

公的データ SSDSE-B(47 都道府県社会・人口統計)を読み込み、 勾配降下法 を実際に動かす最小コードです。 引数のパスは平易さ優先で直書きしています。

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) A4101(出生数) C3301(着工建築物数) 北海道 5,092,000 24,430 15,872 東京都 14,086,000 86,348 41,817 沖縄県 1,468,000 12,549 5,217 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import pandas as pd
import numpy as np

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', skiprows=[1], encoding='cp932')
df = df[df['SSDSE-B-2026'] == 2023]   # 2023年の47都道府県のみ
X = df[['A1101', 'A4101']].astype(float).values
y = df['C3301'].astype(float).values

# 正規化
X = (X - X.mean(0)) / X.std(0)
y = (y - y.mean()) / y.std()

w = np.zeros(2)
b = 0.0
lr = 0.01

for ep in range(300):
    pred = X @ w + b
    err = pred - y
    grad_w = 2 * X.T @ err / len(y)
    grad_b = 2 * err.mean()
    w -= lr * grad_w
    b -= lr * grad_b
    if ep % 50 == 0:
        print(f'ep {ep}: loss={(err**2).mean():.4f}')
print('最終 w:', w, 'b:', b)

※ 上記スニペットは Python 3.10+ / pandas 2.x / numpy / scikit-learn を想定。 環境構築は『conda create -n ds python=3.11 pandas scikit-learn matplotlib』で十分です。

⚠️ よくある落とし穴

❌ 1. 学習率が大きすぎる
損失が振動・発散。 1/10にしてみる
❌ 2. 学習率が小さすぎる
収束が遅い/止まる。 Warmup や減衰スケジュールを
❌ 3. スケール未調整の特徴量
次元毎にスケール違う → 勾配の方向が偏る。 標準化必須
❌ 4. 鞍点で停滞
勾配ほぼ0で動かない。 Momentum や Adam で抜ける
❌ 5. SGDの分散を見落とす
ミニバッチを大きくすると安定するが、 過学習リスク

⚠️ 追加の落とし穴 ── 実務で踏み抜く罠

❌ 1. 学習率が大きすぎ
loss が振動・発散。 lr=1e-3 から 10 倍ずつ試す。
❌ 2. 学習率が小さすぎ
学習が進まない、 局所解で停滞。 lr scheduler で時々ブースト。
❌ 3. バッチサイズと学習率
大バッチでは lr も比例して上げるのが定石(Goyal 2017)。
❌ 4. 初期値依存
重み初期化を間違えると爆発・消失。 He/Xavier 初期化が標準。
❌ 5. ミニバッチ依存
バッチ内のデータ偏りでノイズ大。 シャッフルとデータ拡張が必須。

⚠️ 落とし穴:GD で実装するときに必ず踏む 8 つの地雷

⚠️ 学習率が大きすぎる
η が大きいと損失が発散し NaN になる。 学習曲線で「上昇に転じた」「振動が大きい」が見えたら下げる。 1/10 ずつ試すのが定石。
⚠️ 学習率が小さすぎる
η が小さいと収束が遅く、 計算資源の無駄。 学習曲線が「ほぼ水平」なら上げる。 warm-up scheduling で序盤に大きく、 終盤に小さくすると効率的。
⚠️ 局所最適解 (local minimum) に捕まる
非凸関数(NN の損失)では複数の谷がある。 異なる初期値で複数回試す、 SGD のノイズで脱出を期待、 アニーリングで最終的に冷却、 などの戦略。
⚠️ 鞍点 (saddle point)
高次元では局所最適より鞍点が圧倒的に多い(次元の呪い)。 すべての方向で勾配 0 でも、 片方は山、 片方は谷。 Momentum で慣性をつけると脱出しやすい。
⚠️ 特徴量のスケール差
x1 が [0, 100], x2 が [0, 0.01] のような不均衡だと、 学習率を統一すると一方しか動かない。 StandardScaler / MinMaxScaler で必ず前処理。
⚠️ ミニバッチサイズの極端
バッチ=1 は超ノイジー、 バッチ=全データは Batch GD と同じ。 32〜256 が経験的にバランス良い。 大きすぎると汎化が悪化する報告(Keskar 2017)。
⚠️ 過学習 (overfitting)
訓練 loss は下がり続けても、 検証 loss が U 字なら過学習。 early stopping、 weight decay、 dropout 等の正則化を併用。
⚠️ 勾配消失・爆発 (NN 深層)
深い NN で勾配が指数的に 0 / ∞ になる。 ReLU、 BatchNorm、 残差結合 (ResNet)、 gradient clipping が標準対処。

🏔 最適化の階層:GD は数ある手法の一つ

「最適化アルゴリズム」の世界で GD はどの位置にいるか。 階層構造で整理する。

最適化問題 min f(θ)
├── 解析解が存在
│    ├── 線形回帰 → 正規方程式
│    ├── ロジスティック回帰(小規模) → IRLS
│    └── 主成分分析 → 固有値分解
└── 反復解法
     ├── 0 次法(勾配を使わない)
     │    ├── グリッドサーチ
     │    ├── ベイズ最適化
     │    └── 進化的アルゴリズム
     ├── 1 次法(勾配を使う) ★ GD はここ
     │    ├── Batch GD / SGD / Mini-batch SGD
     │    ├── Momentum / Nesterov
     │    └── Adam / AdaGrad / RMSProp / LION
     ├── 2 次法(ヘッセ行列を使う)
     │    ├── Newton 法
     │    ├── 準ニュートン法 (BFGS, L-BFGS)
     │    └── 信頼領域法
     └── 制約付き最適化
          ├── 投影 GD(projected GD)
          ├── 交互方向乗数法 (ADMM)
          └── 内点法

深層学習では 1 次法(特に Adam 系)が定番。 2 次法は精度高いが行列計算が O(n²) で大規模 NN に向かない。 L-BFGS は古典的 ML で根強い人気。 ベイズ最適化はハイパーパラメータ探索の主役。

🗺 勾配降下法 の概念マップ

『勾配降下法』は『最適化』カテゴリに属する重要概念で、 以下の関連概念群と密接につながっています。

最適化
  ├── 前提
  │   └── 数学・統計の基礎
  ├── 勾配降下法  ← このページ
  │   ├── 派生 1
  │   ├── 派生 2
  │   └── 応用
  └── 並列・対比される手法
      ├── 別アプローチ A
      └── 別アプローチ B
  

完全な概念マップは 🗺 概念マップ で確認できます。

📋 学習チェックリスト ── 勾配降下法 を使いこなすために

📜 歴史と発展

Cauchy (1847) の最急降下法に起源。 ニューラルネットでは Rosenblatt (1958), Rumelhart (1986) で確立。 SGD (Robbins-Monro 1951), Momentum (Polyak 1964), AdaGrad (Duchi 2011), RMSProp (Tieleman 2012), Adam (Kingma 2014) と派生多数。 近年は LION (2023) など二次情報を擬似的に使う手法も。

原典は Cauchy (1847)で、 ニューラルネットどころかコンピュータより 100 年早い手法です。 Cauchy の動機は天体力学の連立方程式を解くことでした。 一方 SGD の原典 Robbins & Monro (1951) は「確率近似」という別分野の論文で、 ノイズのある観測から根を求める逐次法として提案されています。 今日の深層学習の中核 2 つが、どちらも機械学習と無関係な文脈で生まれたのは知っておく価値があります。

🧭 拡張解説:SSDSE-B-2026 で勾配降下法を「動かして」理解する

「坂を下る」の比喩は分かりやすいが、 実装すると 学習率・収束・鞍点・過学習といった現実問題に直面する。 ここでは SSDSE-B-2026 の人口データを使い、 「高齢者人口から総人口を予測する線形回帰モデル」を勾配降下法で学習し、 ① 手作り GD、 ② 学習率の比較、 ③ scikit-learn の SGDRegressor との比較を通して、 実務で起きるすべての問題を体験する。

📌 題材:高齢者人口から総人口を予測する線形回帰

47 都道府県の高齢者人口 A1303 から総人口 A1101 を予測する単純な線形回帰 $y = wx + b$ を、 平均二乗誤差を最小化する勾配降下法で学習する。 解析解(最小二乗法)が存在するため、 GD で同じ答えに到達できるかを比較できる。

📥 入力データ(SSDSE-B-2026 の 2023 年、 47 都道府県) Prefecture A1303(高齢者人口) A1101(総人口) 北海道 1,681,000 5,092,000 青森県 417,000 1,184,000 岩手県 407,000 1,163,000 東京都 3,205,000 14,086,000 ... 鳥取県 179,000 537,000 標準化後(z スコア):x_n = (A1303 - μ_x)/σ_x, y_n = (A1101 - μ_y)/σ_y

⚙️ 数式:MSE 損失とその勾配

$$L(w, b) = \frac{1}{N}\sum_{i=1}^{N} (wx_i + b - y_i)^2$$

$$\frac{\partial L}{\partial w} = \frac{2}{N}\sum_{i=1}^{N} (wx_i + b - y_i)\, x_i, \quad \frac{\partial L}{\partial b} = \frac{2}{N}\sum_{i=1}^{N} (wx_i + b - y_i)$$

$$w_{t+1} = w_t - \eta\, \frac{\partial L}{\partial w}, \quad b_{t+1} = b_t - \eta\, \frac{\partial L}{\partial b}$$

数式を言葉で読み解く:L はモデルの予測 $\hat{y} = wx+b$ と真値 $y$ の誤差を 2 乗して平均したもの(MSE)。 $\partial L/\partial w$ は「w を少し増やすと損失がどう変わるか」の感度。 GD は「損失を最も減らす方向」(負の勾配)に学習率 $\eta$ だけ進む。 学習率が大きすぎると振動・発散、 小さすぎると収束が遅い、 がトレードオフ。

🐍 自前 GD 実装:50 iteration で MSE がどう減るか

このコードでやること:SSDSE-B-2026 の標準化したデータで、 numpy のみで GD を実装。 各ステップの loss を記録し、 学習率 0.1 で収束する様子を観察。 最後に解析解(polyfit)と比較。

📥 入力:標準化済み x_n, y_n(47 点)、 初期値 w=0, b=0、 学習率 η=0.1

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 自前で勾配降下法を実装 (numpy のみ)
import pandas as pd
import numpy as np

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == df['SSDSE-B-2026'].max()]
X = df['A1303'].values.astype(float) / 1e6
y = df['A1101'].values.astype(float) / 1e6
xn = (X - X.mean()) / X.std()
yn = (y - y.mean()) / y.std()

w, b, eta = 0.0, 0.0, 0.1
for t in range(50):
    err = (w*xn + b) - yn
    gw = (2/len(xn)) * (err*xn).sum()
    gb = (2/len(xn)) * err.sum()
    w -= eta*gw; b -= eta*gb
    if t in [0, 4, 9, 49]:
        print(f'iter {t+1:3d}: w={w:.4f}, b={b:.4f}, loss={(err**2).mean():.4f}')

# 解析解 (最小二乗の公式)
m, c = np.polyfit(xn, yn, 1)
print(f'解析解  : w={m:.4f}, b={c:.4f}')

📤 実行結果:

iter 1: w=0.1982, b=-0.0000, loss=1.0000 iter 5: w=0.6663, b=-0.0000, loss=0.1827 iter 10: w=0.8846, b=-0.0000, loss=0.0357 iter 50: w=0.9910, b=0.0000, loss=0.0180 解析解 : w=0.9910, b=0.0000

💬 結果の読み方:GD は 50 反復で w=0.991 に収束し、 解析解(polyfit)と一致。 loss は 1.0 → 0.0180 へ 55 倍減少。 b は標準化のため 0 に張り付く(intercept の自由度が消える)。 「単純な GD でも解析解と同じ答えに到達できる」が確認できる。 ただし学習率 0.1 が適切だったため安定したのであり、 これを 1.5 に上げると発散、 0.001 に下げると 50 反復では遠く及ばない。

⚡ 学習率 η を 4 段階で比較:発散・低速・最適・振動

学習率は GD の最重要ハイパーパラメータ。 同じ問題でも η を変えると収束パターンが劇的に変わる。

🐍 4 種類の学習率で 30 反復の loss を比較

このコードでやること:η = 0.001, 0.1, 1.0, 1.5 の 4 段階で同じデータを学習し、 各 iter の loss を記録。 「小さすぎ → 進まない / 大きすぎ → 発散 / ちょうど良い → 収束」のパターンを実値で確認する。

📥 入力:先の標準化済み xn, yn を再利用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# 学習率を 4 段階で比較 (同じデータ、 同じ初期値)
import pandas as pd, numpy as np

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == df['SSDSE-B-2026'].max()]
X = df['A1303'].values.astype(float) / 1e6
y = df['A1101'].values.astype(float) / 1e6
xn = (X - X.mean()) / X.std(); yn = (y - y.mean()) / y.std()

def gd(eta, T=30):
    w = 0.0; losses = []
    for _ in range(T):
        err = w*xn - yn
        w -= eta * (2/len(xn)) * (err*xn).sum()
        losses.append((err**2).mean())
    return losses

for eta in [0.001, 0.1, 1.0, 1.5]:
    L = gd(eta)
    print(f'η={eta:5.3f}: loss[1]={L[0]:.3f}, loss[15]={L[14]:.3f}, loss[30]={L[29]:.3f}')

📤 実行結果:

η=0.001: loss[1]=1.000, loss[15]=0.946, loss[30]=0.892 η=0.100: loss[1]=1.000, loss[15]=0.020, loss[30]=0.018 η=1.000: loss[1]=1.000, loss[15]=1.000, loss[30]=1.000 η=1.500: loss[1]=1.000, loss[15]=263614068.288, loss[30]=283053450496536896.000

💬 結果の読み方:η=0.001 では 30 反復で loss が 0.892、 約 11% しか減らない(収束遅い)。 η=0.1 はちょうど良く 30 反復で loss 0.018、 98% 削減。 η=1.0 は安定限界(この単変数問題では更新係数 $|1-2\eta|=1$)ちょうどで更新が符号反転を繰り返し、 loss が 1.0 のまま下がらない。 η=1.5 は 発散し loss が 10^17 まで爆発。 「学習率 = 二次関数の曲率の逆数」程度が安全圏で、 ハイパーパラメータサーチが必須。 Adam などの適応的最適化は、 この調整を自動化したもの。

🚀 派生手法:SGD / Momentum / AdaGrad / Adam の使い分け

手法更新式特徴
Batch GD全データで勾配計算安定だが遅い、 大規模データ不向き
SGD1 サンプルで勾配計算速いがノイジー、 学習率調整必須
Mini-batch SGD32〜256 サンプルで勾配バランス良、 深層学習標準
Momentum$v_t = \beta v_{t-1} + g_t, \theta -= \eta v_t$谷で振動を抑制、 慣性で加速
Nesterov先読み付き Momentum理論保証あり、 凸問題で最速
AdaGrad$\eta_t = \eta / \sqrt{\sum g^2}$特徴量ごとに学習率調整、 sparse 向き
RMSProp指数移動平均で勾配 2 乗AdaGrad の改良、 RNN で実績
AdamMomentum + RMSProp深層学習デファクトスタンダード
AdamWAdam + decoupled weight decayTransformer/BERT で標準
LION (2023)符号化された Momentum大規模 LLM で Adam を超える

🐍 SSDSE で SGDRegressor と LinearRegression を比較

このコードでやること:scikit-learn の SGDRegressor(SGD ベース)と LinearRegression(解析解)で同じデータを学習し、 係数と R² を比較する。 GD の実装上のディテール(収束判定、 学習率スケジュール)が sklearn でどう自動化されているかが分かる。

📥 入力:47 県の X=[A1303], y=A1101(オリジナルスケール)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# SGDRegressor と LinearRegression の比較
import pandas as pd
from sklearn.linear_model import SGDRegressor, LinearRegression
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import make_pipeline

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == df['SSDSE-B-2026'].max()]
X = df[['A1303']].values
y = df['A1101'].values

sgd = make_pipeline(StandardScaler(), SGDRegressor(max_iter=1000, tol=1e-5, random_state=0))
ols = LinearRegression()
sgd.fit(X, y); ols.fit(X, y)

print(f'SGD     R² = {sgd.score(X, y):.4f}, coef={sgd[-1].coef_[0]:.3f}')
print(f'OLS     R² = {ols.score(X, y):.4f}, coef={ols.coef_[0]:.3f}')
print(f'SGD intercept = {sgd[-1].intercept_[0]:.0f}')
print(f'OLS intercept = {ols.intercept_:.0f}')

📤 実行結果(例):

SGD R² = 0.9820, coef=2743275.201 OLS R² = 0.9820, coef=3.996 SGD intercept = 2645757 OLS intercept = -434130

💬 結果の読み方:R² は両者ほぼ同じ 0.982(GD でも解析解とほぼ同精度)。 ただし係数は標準化の有無で異なる:SGD は内部で StandardScaler により X を標準化しているため coef は「X の 1 標準偏差あたり」の値(約 274 万)、 OLS は「1 人あたり」の傾き 3.996。 「線形回帰なら解析解が速くて正確、 GD は標準化スケールでの解釈」 — この区別が重要。 大規模データ(n > 10^6)では解析解が遅すぎるため SGD 一択になる。

🗺 多変数の GD:等高線地図と勾配ベクトル

1 変数の損失は単純な放物線、 多変数は「お椀の底に向かう斜面」。 視覚化には「等高線(等しい loss の線)」とその上の「勾配ベクトル(最急上昇方向)」を描くのが定番。 GD はその真逆方向に進む。

🔬 言葉で読み解く:等高線と勾配の関係

数式を言葉で読み解く:等高線(level set)は loss が同じ値を持つ点の集合。 勾配 $\nabla L$ は等高線に垂直な方向(最急上昇)を指す。 GD はその逆 $-\nabla L$ に進むので、 等高線を直角に横切る軌跡を描く。 鋭く伸びた楕円(細長い谷)では、 谷を直進せず壁を交互に蹴る「ジグザグ運動」になり収束が遅れる。 これが Momentum や Adam が必要な理由。

🐍 2 変数 GD を SSDSE で動かす(w と b を同時学習)

このコードでやること:先ほどは intercept b を 0 固定にしたが、 今度はオリジナルスケールで w と b を同時に学習する。 軌跡 (w_t, b_t) と loss を記録し、 2 次元最適化のジグザグ性を観察する。

📥 入力:47 県の x = A1303 (百万人), y = A1101 (百万人)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# 2 変数 GD: w と b を同時最適化、 軌跡を記録
import pandas as pd, numpy as np

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == df['SSDSE-B-2026'].max()]
x = df['A1303'].values.astype(float) / 1e6
y = df['A1101'].values.astype(float) / 1e6

w, b, eta = 0.0, 0.0, 0.01
for t in range(500):
    yhat = w*x + b
    err = yhat - y
    gw = (2/len(x)) * (err*x).sum()
    gb = (2/len(x)) * err.sum()
    w -= eta*gw; b -= eta*gb
    if t in [0, 9, 99, 499]:
        print(f'iter {t+1:4d}: w={w:.4f}, b={b:.4f}, loss={(err**2).mean():.4f}')

m, c = np.polyfit(x, y, 1)
print(f'解析解        : w={m:.4f}, b={c:.4f}')

📤 実行結果(例):

iter 1: w=0.0784, b=0.0529, loss=14.6601 iter 10: w=0.6824, b=0.4366, loss=8.6071 iter 100: w=2.6866, b=0.8369, loss=1.0233 iter 500: w=3.8403, b=-0.2721, loss=0.1509 解析解 : w=3.9956, b=-0.4341

💬 結果の読み方:500 反復でも解析解(w=3.9956, b=-0.4341)には完全に届かず、 w=3.8403, b=-0.2721(loss 0.1509)で足踏みしている。 w と b は別々のスケールを持つため、 b の収束が遅い(最初に大きく+方向に振れて戻ってくる)。 これが「特徴量の標準化なしで GD を回すと収束が遅い」典型例。 学習率 η=0.01 は b にとっては大きく、 w にとっては小さい — 両者を満たす η を選ぶのが難しい。 StandardScaler を入れれば数十反復で収束する。

勾配降下法 Momentum Adam RMSProp AdamW Newton法 SGD / ミニバッチ

🔗 隣接手法への橋渡し

「勾配降下法」は単独で完結する手法ではなく、 隣接領域と連携することで真価を発揮する。

上流の偏微分・チェインルールで勾配を計算し、 並列のニュートン法・準ニュートン法 (BFGS) と収束速度を比較し、 下流の SGD・モメンタム・Adam で深層学習の実装に接続する。 勾配降下は単一アルゴリズムではなく、 学習率設計・バッチサイズ・最適化アルゴリズム選択の体系の核として位置づける。

🌳 手法選択フロー

「勾配降下法」を実際の課題に当てはめるとき、 状況別に何を選ぶかを 3 段階で判定する。

  1. バッチサイズは? 小規模 → 完全勾配 (バッチ GD)、 大規模 → ミニバッチ SGD、 オンライン → SGD (1 サンプル)
  2. 学習率はどう決めるか? 固定 → グリッドで探索、 適応 → Adam・RMSprop、 大規模事前学習 → ウォームアップ + コサイン減衰
  3. 収束が遅い場合は? モメンタム追加 → Nesterov、 損失振動 → 学習率減衰、 鞍点 → ノイズ注入・SGD の確率性を活用

勾配降下は「学習率 × バッチサイズ × モメンタム」の三軸で挙動が変わる。 学習曲線を必ず可視化し、 振動・停滞・発散を見分けてからハイパーパラメータを調整する。

🔬 解説深化 ── スケール条件数の視点で勾配降下を捉え直す

このページの他パートでは「学習率・バッチ・モメンタム」の三軸で勾配降下法を整理した。ここでは角度を変え、「勾配降下がなぜOLSの閉形式解と一致するのか」「なぜ特徴量のスケールが収束の生死を分けるのか」を、SSDSE-B-2026 の実測値だけで検証する。学習率そのものの設計論は 学習率、連続最適化の一般論は 連続最適化 に譲り、ここではスケーリングと解の一致という一点に絞る。

🎨 直感 ── 勾配降下は「閉形式解を数値的に再発見する装置」

線形回帰 y = b₀ + b₁x の二乗誤差は完全な凸放物面なので、最小点は連立方程式(正規方程式)で一発で解ける(=閉形式解)。勾配降下は同じ谷を、勾配の逆向きに一歩ずつ下って近づいていくだけ。だから両者は理屈上「同じ底」に到達する。これを SSDSE-B-2026 で実際に突き合わせてみる。

使用データ(実測・2023年・47都道府県): 説明変数 x = A1303(65歳以上人口)、目的変数 y = A4200(死亡数)。両者の相関は r = 0.99903 とほぼ直線。高齢人口が多い県ほど死亡数が多いという、当たり前だが極めて強い関係だ。

実測サマリー(df[df['SSDSE-B-2026']==2023]、n=47) x=A1303(65歳以上人口): min 179,000 mean 770,829 max 3,205,000 y=A4200(死亡数) : min 8,290 max 137,241 ① 正規方程式(閉形式・np.linalg.lstsq) b0 = 1234.1267754769 b1 = 0.0418747423763 ② 標準化して勾配降下(lr=0.1, 3000回)→ 元スケールへ逆変換 b0 = 1234.1267754769 b1 = 0.0418747423763 → 小数第10位以上まで完全一致(勾配降下がOLSを再発見) 傾き0.0419の意味: 高齢人口が1人増えるごとに年間死亡数が約0.042人増える

②は勾配降下という反復アルゴリズムなのに、①の閉形式解と 12 桁一致した。凸問題では「解き方(代数 vs 反復)」が変わっても行き着く底は同じ、という事実を実データで確かめられる。

⚠️ 落とし穴(重要)── 未標準化だと「使える学習率」が消滅する

上の一致は標準化した後の話である。生の特徴量(総人口・高齢人口は 10⁶ オーダー)のまま勾配降下を回すと、同じアルゴリズム・同じデータでも破綻する。実測で確かめた学習率の挙動が下表だ。

特徴量学習率3000回後の結果
標準化
(平均0・分散1)
0.3✅ わずか 4回で収束(MSE<0.02)
0.03✅ 34回で収束(遅い)
0.9✅ 収束(10回、上限付近)
1.05❌ 発散(overflow)
未標準化
(生の実数値)
1×10⁻¹²❌ 発散(inf)
5×10⁻¹³△ 発散はしないが最適解に届かず(MSE 2,276,513 > OLS最適 1,602,908)

核心: 標準化すると学習率 0.3〜0.9 が問題なく使えるのに、生スケールでは安定して回せる上限が 約 5×10⁻¹³ まで押し下がる。同じデータ・同じ式なのに、標準化の有無だけで「安全な学習率」が約 12 桁ずれる。しかも生スケールでは唯一ギリギリ動く学習率でも最適解(OLS残差MSE 1,602,908)に届かない。これは特徴量スケールが損失曲面の条件数を悪化させ、全方向で同時に安定する歩幅が存在しなくなるためだ。教訓は単純で、勾配降下の前に標準化は「推奨」ではなく「前提」。詳しい変換手順は 標準化 を参照。

🚀 発展 ── 「なぜ 5×10⁻¹³ で頭打ちか」を条件数で説明する

二乗損失の勾配降下が安定する学習率の上限は理論上 η < 2/L(L はヘッセ行列の最大固有値=損失曲面の最急な曲率)で与えられる。特徴量を c 倍すると設計行列 XᵀX の成分は概ね c² 倍になり、L も跳ね上がる。高齢人口は 10⁶ オーダーなので c² ≈ 10¹² 前後、これが標準化時の安定上限 約 0.9約 5×10⁻¹³ へ押し下げた実測倍率(およそ 10¹²)とほぼ一致する。つまり上表の「12 桁ずれ」は偶然ではなく、スケールの二乗が条件数を通じて学習率上限に効くという理論の実データ確認になっている。

この視点は次に自然につながる:

※ 本節の数値は SSDSE-B-2026(data/raw、2023年・47都道府県、A1303・A4200 列)を pd.read_csv(encoding='cp932', skiprows=[1]) で読み込み、Python で実際に勾配降下・正規方程式を回して得た実測値。合成データは使用していない。