技術 約26分で読めます

AIの学習を動かしている勾配降下と逆伝播、ここだけ読めれば怖くない

いけさん目次

前回の微分で、損失 LL のパラメータに対する勾配 L\nabla L が「LL が一番急に増える方向」を指すことまでやった。 ということは、その逆方向 L-\nabla L に動けば LL が一番急に減る、ということになる。 AIの学習は、突き詰めるとこれをひたすら繰り返すだけの営みだ。

この記事では、その繰り返しが具体的にどう組み立てられているかを、シリーズ前4本と同じ「解けるようになる必要はなく、読めればよい」のスタンスでまとめる。

勾配降下・SGD・Adam・逆伝播・勾配消失・残差接続・学習率スケジュール のキーワードが何をまとめてやっているかで読めれば、学習ログやモデルカード、論文のtraining detailsはだいぶ読める。

4本目の連鎖律・勾配・ヤコビ行列は既知として話を進める。記号を忘れたら都度4本目を開き直せば足りる。

学習は「損失を下げる方向にパラメータを動かす」だけ

まず大枠のおさらいから。

ニューラルネットの訓練は、損失関数 L(θ)L(\theta) をパラメータ θ\theta について最小化する、という最適化問題だ。 前回までで用意した道具:

  • 損失 L(θ)L(\theta)(3本目の交差エントロピーなど)はスカラーを返す
  • パラメータ θ\theta はベクトル(数百万〜数兆次元)
  • 勾配 L(θ)\nabla L(\theta)θ\theta と同じ次元のベクトルで、「LL が一番急に増える方向」を指す

この3つから、「L\nabla L の逆方向に少し θ\theta を動かせば LL が下がる」が導ける。 あとは、その「少し動かす」を何千万〜何億回も積み重ねるのが学習の骨格になる。

勾配降下の更新式 θ ← θ − η∇L

一番基本の「動かし方」の式がこれだ。

θθηL(θ)\theta \leftarrow \theta - \eta \, \nabla L(\theta)

記号を分解する。

  • θ\theta(シータ): モデルのパラメータをまとめて表す記号。何億〜何兆個の重みが1つのベクトルに入っているイメージ。
  • L(θ)L(\theta): そのパラメータでの損失(スカラー)。
  • L(θ)\nabla L(\theta): 損失の勾配ベクトル(θ\theta と同じ形)。
  • η\eta(イータ): 学習率。1ステップで動く量の大きさを決める、正の小さな数字。実際の動き幅は(η × 勾配の大きさ)になるので、η が大きいほど一気に動き、小さいほどちょこちょこ動く。典型値は 0.0001 〜 0.001 程度。
  • \leftarrow は代入(右辺の値で θ\theta を置き換える)。

式全体は「θ\theta を、勾配の逆向きに η\eta ぶん動かす」と読める。これを 勾配降下 (gradient descent)と呼ぶ。

なぜ「引く」のか

符号に引っかかる人がいる。なぜ +ηL+\eta\nabla L ではなく ηL-\eta\nabla L なのか。

勾配 L\nabla L は「LL を一番急に増やす方向」だった。 それを足すと、LL が増える方向に動くことになる。 損失は減らしたいのだから、勾配の逆向きに動きたい。だから θ\theta から ηL\eta\nabla L を引く。

ミニマムな例で実際に動かす

本物のLLMだとパラメータは何億個もあるが、仕組みを追うだけなら1変数で十分なので、その最小版で試す。 L(w)=w2L(w) = w^2 というお椀型の関数を、パラメータ ww が1個だけのモデルの損失だと思う。 w=0w = 0 のとき損失が最小(0)で、そこから離れるほど損失が大きくなる、一番素朴な地形だ。

初期値を w=4w = 4、学習率を η=0.3\eta = 0.3 に設定して、更新式 wwηL(w)w \leftarrow w - \eta \cdot L'(w) を何ステップか回してみる。 L(w)=w2L(w) = w^2ww で微分すると L(w)=2wL'(w) = 2w なので、この場合の更新式は

ww0.32w=0.4ww \leftarrow w - 0.3 \cdot 2w = 0.4w

毎ステップ ww が 0.4倍 に縮んでいく、という動きになる。

ステップwwL=w2L = w^2
04.0016.00
11.602.56
20.640.41
30.260.07
40.100.01

ww が徐々に0に近づき、損失もそれに伴って下がっていく。これが勾配降下の最小単位だ。

1Dの勾配降下 放物線 L(w)=w² の上で、初期値 w=4 から w=0 に向かってステップごとに下っていく様子 w L step 0 (w=4) step 1 (w=1.6) step 2, 3, ... → 0 最小 w=0
L(w)=w² 上を、w=4 から η=0.3 の勾配降下で下っていく様子。1ステップ目で大きく下り、以降は最小点に向かって細かくなっていく。

等高線の逆方向に歩く

4本目で出したお椀の等高線図を思い出すと、勾配はある点で等高線に垂直で、高いほうを向いていた。 勾配降下は、それを逆向きに辿って、等高線を1つずつ下のほうへ渡り歩いていく動きに相当する。 損失がお椀状の地形なら、谷底(最小点)に向かって進んでいく。

学習率 η の挙動

学習率 η\eta は、1ステップの踏み込み幅を決める値で、大きすぎても小さすぎても困るという性質を持つ。

  • 小さすぎる → 1ステップで動く量が少なすぎて、なかなか最小値に到達しない。方向は正しい。
  • 大きすぎる → 1ステップで飛びすぎて、最小点を通り越して反対側に行ってしまう。振動したり発散したりする。
  • ちょうど良い → 毎ステップ確実に下る。早く収束する。

具体例で確認する。L(w)=w2L(w) = w^2w=4w = 4 スタート、η\eta を変えて4ステップ走らせた結果がこれだ。

η\etastep 0step 1step 2step 3step 4
0.05 (小)4.003.603.242.922.62
0.3 (中)4.001.600.640.260.10
0.9 (大)4.00−3.202.56−2.051.64
1.1 (発散)4.00−4.805.76−6.918.29

η=0.9\eta = 0.9 では反対側に飛びながら徐々に縮むが、η=1.1\eta = 1.1 では発散していく。

LLMの学習では学習率は典型的に 10310^{-3}10510^{-5} の範囲で、後で出てくるAdamというOptimizerを使う場合は 10410^{-4} 前後(lr=1e-4)が定番だ(Adamの中身は後の章で扱う)。

「じゃあ大きくすれば速いのでは」

よくある疑問だが、η\eta を大きくし続けると発散モードに入って学習が壊れる。 「なるべく大きく、でも発散しない程度に」学習率を選ぶのが基本で、後で出てくる学習率スケジュール(warmup、cosine decay)もこの「発散ぎりぎりで賢く動かす」という発想から来ている。

全バッチ・SGD・ミニバッチ

損失 LL は通常「全訓練データに対する平均」で定義されるが、勾配 L\nabla L を毎ステップ全データで計算するのは重い。 そこで、どれだけのデータで勾配を計算するかに選択肢が出てくる。

  • 全バッチ勾配降下(full-batch): 全データで1回の勾配を計算。真の勾配が得られるが、1ステップが遅い
  • 確率的勾配降下(SGD): 1サンプルだけで勾配を計算。高速だがノイズ大
  • ミニバッチ勾配降下: 数十〜数千サンプルの塊で勾配を計算。両者の中間でバランスが良い

実際のLLM訓練はほぼすべてミニバッチだ。LLMでは1ミニバッチが数百万トークンに相当することも多く、batch_size=4M tokens のような表記を見かけたらそれを指している。

「Stochastic」って何

SGDの S は Stochastic で、「確率的な、ランダム性を含む」という意味。 毎ステップどのデータを使うかがランダムに決まるので、勾配も毎回少しずつズレる。そのランダム性が名前の由来だ。

ランダム性は役に立つ

実際の損失地形はお椀のように単純ではなく、小さな谷がたくさん散らばった複雑な形をしている。 高校の微分で習った「最小値」と「極小値」の違いを思い出すと近い。 極小値は「周りよりは低いが、全体の最小とは限らない点」のことで、AIではこれを 局所最小 (local minimum)と呼ぶ。全体の最小(大域最小)とは別物だ。

凸凹した損失地形 複数の谷と山を持つ損失関数のグラフ。局所最小が2つ、大域最小が1つある様子 θ L 局所最小 局所最小 大域最小
実際の損失地形は凸凹していて、複数の局所最小と大域最小が混在する。勾配降下は目の前の谷に向かって進むしかないので、浅い局所最小に落ちるとそこから出られない。

全バッチの純粋な勾配降下だと、一度浅い局所最小に落ちるとそこから出られない。 SGD/ミニバッチの勾配ノイズは、そこから「偶然」飛び出すきっかけを与えるおまけ効果を持っている。 現代の深層学習が上手く動く理由の1つだ。

ちなみにこの「ランダムなゆらぎで障壁を越える」発想、物理や古典的最適化理論では昔からある話で、熱励起で原子が高いエネルギー状態に上がることや、金属を徐々に冷ましながら低エネルギーの配置に落ち着かせる 焼きなまし法 (simulated annealing)が同じ系譜にいる。 SGDのランダム性も、この血統の直系と思えばよい。

逆伝播は「連鎖律の大規模版」

勾配 L\nabla L を計算するには、各パラメータ ww に対する L/w\partial L / \partial w が全部欲しい。 ニューラルネットは層を何層も重ねていて、入力から損失までは非常に深い合成関数になっている。

4本目で出した図を思い出すと、順方向の流れはこうなっている。

flowchart LR
    X[入力 x] --> L1[層1 W1, b1]
    L1 --> L2[層2 W2, b2]
    L2 --> L3[層3 W3, b3]
    L3 --> L[損失 L]

この順伝播(forward)で損失まで計算した後、勾配は逆方向にもう一度流していく。

flowchart RL
    L[損失 L] -->|∂L/∂y3| L3[層3 W3, b3]
    L3 -->|∂L/∂y2| L2[層2 W2, b2]
    L2 -->|∂L/∂y1| L1[層1 W1, b1]
    L1 -->|∂L/∂x| X[入力 x]

これが 逆伝播 (backpropagation、略してbackprop)だ。 やっていることは、連鎖律を層の数だけひたすら適用する、というそれだけ。

なぜ逆方向から計算するのか

連鎖律で損失 LL の層1の重み W1W_1 に対する勾配を展開するとこうなる。

LW1=Ly3y3y2y2y1y1W1\frac{\partial L}{\partial W_1} = \frac{\partial L}{\partial y_3} \cdot \frac{\partial y_3}{\partial y_2} \cdot \frac{\partial y_2}{\partial y_1} \cdot \frac{\partial y_1}{\partial W_1}

右から左へ計算していくと、末端の L/y3\partial L / \partial y_3 が出発点になる。 各層でローカルな微分を掛け算しながら遡っていくと、最後に L/W1\partial L / \partial W_1 が出る。

順方向に計算した数値(各層の出力)を保存しておけば、逆方向の計算で使い回せる。 これが逆伝播が計算上効率的な理由だ。

計算グラフとローカル勾配

逆伝播を実装する基盤になるのが 計算グラフ (computation graph)だ。 式の演算1つ1つをノードにして、データの流れを矢印で繋いだグラフで表す。

例: L = (wx − y)²

1サンプルの線形回帰の損失を計算グラフにしてみる。 ww は重み、xx は入力、yy は正解とする。

flowchart LR
    W[w] --> MUL[×]
    X[x] --> MUL
    MUL -->|u = wx| SUB[−]
    Y[y] --> SUB
    SUB -->|v = u-y| SQ[²]
    SQ -->|L = v²| OUT[L]

各ノードが演算で、矢印が値の流れ。 各ノードは「自分の出力が入力に対してどう変化するか」、つまり局所勾配を計算できる。

ノード演算局所勾配
乗算u=wxu = wxuw=x\dfrac{\partial u}{\partial w} = x
ux=w\dfrac{\partial u}{\partial x} = w
減算v=uyv = u - yvu=1\dfrac{\partial v}{\partial u} = 1
vy=1\dfrac{\partial v}{\partial y} = -1
2乗L=v2L = v^2Lv=2v\dfrac{\partial L}{\partial v} = 2v

これらを逆向きに掛け算していくと、ww に対する損失の勾配が出る。

Lw=Lvvuuw=2v1x=2(wxy)x\frac{\partial L}{\partial w} = \frac{\partial L}{\partial v} \cdot \frac{\partial v}{\partial u} \cdot \frac{\partial u}{\partial w} = 2v \cdot 1 \cdot x = 2(wx - y)x

連鎖律を、計算グラフの矢印を逆向きに辿りながら局所勾配を掛けていくだけ、というのが逆伝播の核心になる。

ヤコビ行列で勾配が流れる

入出力がベクトルの場合、局所勾配は「要素ごとの偏微分を並べた行列」、つまり ヤコビ行列 になる(4本目では名前と形だけ紹介した)。

x\vec{x}(長さ nn)を入力、y\vec{y}(長さ mm)を出力とする関数 y=f(x)\vec{y} = f(\vec{x}) のヤコビ行列は、(i,j)(i, j) 要素が yixj\dfrac{\partial y_i}{\partial x_j}m×nm \times n 行列で、

J=[y1x1y1xnymx1ymxn]J = \begin{bmatrix} \dfrac{\partial y_1}{\partial x_1} & \cdots & \dfrac{\partial y_1}{\partial x_n} \\[6pt] \vdots & \ddots & \vdots \\[6pt] \dfrac{\partial y_m}{\partial x_1} & \cdots & \dfrac{\partial y_m}{\partial x_n} \end{bmatrix}

と書ける。「xjx_j が少し動いたら yiy_i がどれだけ動くか」を全通り詰めた表だ。

逆伝播では、スカラー同士の連鎖律が「掛け算」だったのが、ベクトル同士だと 行列積 に置き換わる。連鎖律の行列版と思えばよい。 各層の局所ヤコビ行列を後ろから順に行列積で連結していくと、最終的に全パラメータに対する勾配がベクトル形で出てくる。

実装上は、出力次元 × 入力次元の巨大なヤコビ行列を全部作らずに、「ヤコビ行列とベクトルの積」だけを計算する手法(VJP、Vector-Jacobian Product)が使われている。autogradが裏でやっていることの正体の1つだ。 LLMでは1層で扱うベクトルの長さが数千、パラメータ行列が数百万要素と巨大だが、原理はこのヤコビ行列の連結に帰着する。

autograd は「逆伝播の自動化」

計算グラフと局所勾配の掛け算を手書きで実装するのは、層が深くなると急にしんどくなる。 現代のディープラーニングフレームワーク(PyTorch、JAX、TensorFlowなど)は、自動微分 (autograd、automatic differentiation)でこれを自動化する。

典型的な訓練1ステップ

PyTorchだとこう書く。

# 1. 順伝播
output = model(x)
loss = criterion(output, y)

# 2. 勾配計算(逆伝播)
loss.backward()

# 3. パラメータ更新
optimizer.step()
optimizer.zero_grad()

loss.backward() が呼ばれると、PyTorchは順伝播で作った計算グラフを逆向きに辿って、各パラメータに対する勾配を自動で計算する。 結果は各パラメータテンソルの .grad 属性に書き込まれる。

optimizer.step() は、それらの .grad を見て θθηL\theta \leftarrow \theta - \eta \nabla L 形式のパラメータ更新を実行する。 optimizer.zero_grad() は、次のステップに持ち込まないように .grad を0にリセットする。

autogradで何がラクになるか

autogradは「魔法」と形容されることがあるが、中身は今まで見てきた手順そのままで、計算グラフを順方向に構築 → 末端から局所勾配(ヤコビ行列)を掛けながら遡って各パラメータの勾配を蓄積、という機械的な処理をしているだけだ。 魔法というよりは、手で書くと地獄になる部分を自動化した道具、と見るほうが正しい。

自分で書かずに済むことで得られる恩恵は、

  • L/W\partial L / \partial W を自分で導出しなくても、順伝播を書けば逆伝播は自動で付いてくる
  • 数億〜数兆のパラメータでも、上の3行のコードで勾配計算が終わる
  • 新しい層や損失関数を作っても、微分可能な演算の組み合わせで書けばそのまま逆伝播できる

あたり。 現代のディープラーニングがここまで大規模化できた背景には、このautogradの存在が大きい。

「自分で微分を書かなくていいのか」

AI論文で微分の式を見かけるが、実装では計算機が全部やるので、ユーザーは式を理解するだけで実装は順伝播だけ書けば済む。 このギャップを理解しておくと、論文で微分が議論されていても「裏で autograd が流すやつね」と読める。

Optimizer の系譜: SGD → Momentum → Adam / AdamW

ここまで「勾配が出たら θθηL\theta \leftarrow \theta - \eta \nabla L で動かす」と書いてきたが、「勾配を受けてパラメータを具体的にどう更新するか」の部品を Optimizer と呼ぶ。 実装上は、PyTorchだと torch.optim.SGD(...)torch.optim.AdamW(...) のように差し替え可能なオブジェクトになっていて、モデル・損失関数・学習率などとは別に選ぶ。 1つ前の節で出てきた optimizer.step() は、このOptimizerが勾配を使ってパラメータを更新するメソッドだ。

素の勾配降下(SGD)にも改良の余地が大きく、歴史的に色々なOptimizerが提案されてきた。 LLM学習で使われている Adam / AdamW に至るまでの流れを押さえておく。

SGD(素の勾配降下)

θθηg\theta \leftarrow \theta - \eta \, g

ここで g=Lg = \nabla L。毎ステップ、現在の勾配だけを見て θ\theta を動かす。 シンプルだが、細かい振動が起きやすく、谷が曲がった地形では効率が悪い。

Momentum付きSGD

前ステップの「速度」vv を引き継いで、慣性を付ける。

vβv+g,θθηvv \leftarrow \beta v + g, \quad \theta \leftarrow \theta - \eta \, v

ここで β\beta は「直前の速度をどれだけ引き継ぐか」を決める係数(慣性係数 と呼ぶ)で、典型値は 0.9。 直前までの勾配の慣性を足すことで、細かい振動が打ち消され、大きな谷方向には素早く動ける。 坂道を転がるボールに勢いがつくイメージだ。

Adam(Adaptive Moment Estimation)

Momentumの慣性(一次モーメント)に加えて、勾配の「大きさ」の履歴(二次モーメント)も使う。

mβ1m+(1β1)gm \leftarrow \beta_1 m + (1-\beta_1) g vβ2v+(1β2)g2v \leftarrow \beta_2 v + (1-\beta_2) g^2

mm が勾配の方向を滑らかにした推定、vv が勾配の大きさを推定したもの。 そして更新はこの2つを使って、

θθηmv+ϵ\theta \leftarrow \theta - \eta \cdot \frac{m}{\sqrt{v} + \epsilon}

勾配が小さいところでは分母も小さいので相対的に大きく動き、勾配が大きく暴れているところでは分母も大きくなって慎重に動く、パラメータごとに動き幅を自動で調整する仕組みになっている。 Adamの名前(Adaptive Moment Estimation)の「Adaptive」はこの「勾配の履歴に応じて動きを変える」性質を指している。

β1=0.9,β2=0.999\beta_1 = 0.9, \beta_2 = 0.999 が原著の定番値。LLMでは β2=0.95\beta_2 = 0.95 なども使われる。

AdamW

Adam + weight decay の組み合わせを正しく扱うように修正した改良版。 weight decay はパラメータが大きくなりすぎるのを抑える正則化で、Adam原著の式だと若干不正確な組み合わせになっていた。AdamWはこれを分離して正しく扱う。

LLM学習はほぼAdamWが標準。論文付録で AdamW(betas=(0.9, 0.95), eps=1e-8, weight_decay=0.1) のような設定を見たら、この β1,β2\beta_1, \beta_2 が上の慣性係数で、weight_decay が正則化の強さだ。

Adamの一次/二次モーメントの実装に興味がある人はMegaTrainの100B LLM 1GPU訓練記事あたりに踏み込んだ話がある。

勾配消失と勾配爆発

層が非常に深い(数十〜数百層)と、連鎖律で勾配を掛け算していく過程で新たな問題が出てくる。

ここから出てくる Transformer は、現代のLLMの土台になっている深層ネットのアーキテクチャの名前で、Attention 層と順伝播層のブロックを何十回も積み重ねた構造を指す。 Attention自体は2本目のベクトル記事の末尾で QKTQK^T の形として軽く触れているが、「ある単語が他のどの単語にどれだけ注目するかを内積で測って重み付け和を取る装置」くらいの粗いイメージで足りる。 この記事ではTransformer・Attentionの中身には深く踏み込まず、「層を非常に深く積むタイプのネット」と思って読み進めればよい。

勾配消失

各層の局所勾配が1より小さい値(例: 0.1、0.5)だと、層の数 NN ぶん掛け算すると、

0.1×0.1××0.1=0.1N00.1 \times 0.1 \times \dots \times 0.1 = 0.1^N \to 0

末端に近い層に届く前に勾配が0付近に潰れて、パラメータがほぼ更新されなくなる。 層は動かないので学習が進まない。

sigmoidtanh は勾配の最大値が小さい(sigmoidで0.25、tanhで1)ので、多層にすると消失が起きやすかった。 この問題の緩和策として ReLU (Rectified Linear Unit、max(0,x)\max(0, x))が広まった。数学では ランプ関数 (ramp function)と呼ばれる、x<0x < 0 で0、x0x \ge 0 でそのまま xx を返す単純な形だ。 正の領域で勾配が1のまま(消失しない)なので、深いネットでも末端まで勾配が届きやすい。

勾配爆発

逆に、局所勾配が1より大きいと掛け算で指数的に大きくなる。

2×2××2=2N2 \times 2 \times \dots \times 2 = 2^N \to \infty

勾配が巨大になると θ\theta が一気に飛び、損失がNaNになって学習が壊れる。

対策は 勾配クリッピング (gradient clipping)で、勾配ベクトル全体の大きさ(L2ノルム と呼ばれる、2本目で扱ったベクトルの長さ x\|x\| と同じもの)に上限を設けて、超えたらスケールダウンする。 設定で grad_clip=1.0 のような項目を見たらこれだ。

残差接続・LayerNormが「勾配を通りやすくする」仕組み

勾配消失を根本的に緩和するために、層の作り方自体が工夫されている。 Transformerに標準で入っているのが 残差接続 (residual connection)と LayerNorm だ。

残差接続

ある層の出力 yy を「層の変換 f(x)f(x) に入力 xx をそのまま足したもの」として定義する。

y=f(x)+xy = f(x) + x

この構造だと、逆伝播で勾配を逆向きに流すとき、f(x)f(x) の部分で勾配が潰れても、+x+x のショートカット経路で勾配がそのまま通り抜けてくる。 深い層でも勾配が末端まで届きやすくなる、という効果を持つ。

ResNet(2015)で提案されてから画像認識・Transformer全般に広まった。 Transformerでは各層のAttentionブロックと順伝播層(FFN、Feed-Forward Network)のブロックに、必ず残差接続が付いている。

LayerNorm

各層の出力の値を「平均0、分散1に揃える」正規化。 層ごとに値のスケールが暴れると勾配のスケールも暴れるので、それを抑える役割を持つ。

式で書くと、ベクトル zz に対して、

LN(z)=γzμσ+β\text{LN}(z) = \gamma \cdot \frac{z - \mu}{\sigma} + \beta

ここで μ,σ\mu, \sigmazz 内の平均と標準偏差、γ,β\gamma, \beta は学習可能なパラメータ。 値のスケールが安定することで、勾配のスケールも安定し、学習全体が落ち着く。

Transformer系の記事で LN や「post-norm / pre-norm」のような言葉が出てきたらこの話だ。

2つで1セットの装置

残差接続だけだと、+x+x で値のスケールが層を通るごとに増えていくリスクがある。 LayerNormがそれを揃え直すので、2つで1セットの「勾配を通しつつ値のスケールも保つ」装置になっている。

Transformerが100層以上積めるのはこの構造のおかげで、この発想を発展させた例としてMoonshotAI AttnResのような提案も出ている。

学習率スケジュール: warmup と cosine decay

学習率 η\eta は固定にしがちだが、訓練中に変化させたほうが上手くいくことが多い。 LLMで標準的なのは warmup + cosine decay の組み合わせだ。

Warmup: 最初は小さく始める

学習開始時、パラメータはランダム初期化されていて損失地形の変な場所にいる。 いきなり大きな学習率で動き出すと、勾配が暴れて発散しやすい。 対策として、最初の数%のステップは学習率を0から目標値まで徐々に上げる。これが warmup だ。

例: warmup_steps=500 なら、ステップ500までで学習率が 0 → 目標値に向かってリニアに上昇する。

Cosine decay: だんだん細かく

warmup後は、cos曲線のカーブに沿って学習率をゆっくり下げていく。 最終的には目標値の1%程度まで下がる。

η(t)=ηmax1+cos(πt/T)2\eta(t) = \eta_{\max} \cdot \frac{1 + \cos(\pi t / T)}{2}

ここで tt は warmup 完了後のステップ、TT は総ステップ数。 序盤は大胆に、終盤は細かく、というメリハリが損失の収束を速くする。

学習率スケジュール warmupで0から目標値まで上昇し、その後cosine曲線で減衰する学習率の推移 step lr max warmup終 warmup cosine decay
学習率スケジュールの典型パターン。warmupで0から目標値まで上昇し、その後cos曲線で0近くまで減衰する。LLM学習はほぼこの組み合わせ。

「最初から目標値ではなぜダメか」

初期化したばかりのネットは、出力が訓練データと無関係にブレているため、勾配も方向が定まらず大きな値になりがちだ。 目標学習率で動くと、このデタラメな勾配を全力で反映してしまって、パラメータが発散する。 warmupで勾配が落ち着くまでゆるく動かす期間を作るのが目的になる。

LLM学習ループの全体像

ここまでの部品を組み合わせると、実際の学習ループが見えてくる。

1ステップの流れ

flowchart LR
    A[データ取得] --> B[順伝播]
    B --> C[損失計算]
    C --> D[逆伝播]
    D --> E[勾配クリップ]
    E --> F[optimizer.step]
    F --> G[scheduler.step]
    G --> A
  1. データ取得: 次のミニバッチを取り出す
  2. 順伝播: モデルに入力を流して出力を得る
  3. 損失計算: 交差エントロピーなどで損失を計算(3本目の話)
  4. 逆伝播: loss.backward() で全パラメータの勾配を計算
  5. 勾配クリップ: 勾配のL2ノルムに上限を設ける
  6. optimizer.step(): Adamなどで θθηL\theta \leftarrow \theta - \eta \nabla L の更新
  7. scheduler.step(): warmup/cosineで次のステップの学習率を調整

このループを何百万回〜何十億回回すのがLLMの訓練だ。

エポックとステップ

  • 1エポック: 訓練データを1周
  • 1ステップ: 1ミニバッチの処理
  • 1ラン: 設定されたエポック数の訓練全体

LLMでは1エポックも終わらずに訓練を打ち切ることもよくある(データが巨大で、1周する前に十分学習が進む)。

この「エポック」「ステップ」という単位はLLM特有のものではなく、画像生成AIの LoRA 学習や Stable Diffusion の fine-tuning、音声合成の追加学習など、生成AIの訓練ツール全般で共通して出てくる。 裏でやっているのはすべて勾配降下のループなので、画面に表示される単位も共通する、と理解しておけばよい。 実例を触ったことがある人はSeaArt の LoRA 学習実践のような記事を読み返すと、「この記事でやってる損失曲線の見方、そのまま同じだ」と繋がるはずだ。

いつ学習が終わるのか

ここまで更新式や逆伝播ばかり見てきたので、「で、学習はいつ終わるのか」が気になるところだ。

勾配降下は原理上「損失が下がる方向に動き続ける」だけの動作なので、理論上は損失が0になるまで止まらない。 ただし現実には、モデルの表現力には限界があり、訓練データにもノイズが混じっているので、損失はどこかで 頭打ち になって0には到達しない。 訓練を止めるタイミングは、別のルールで決める。

  • ステップ数 / エポック数: 事前に決めた回数を消化したら終わり。LLMの事前学習はほぼこの方式
  • 早期終了(early stopping): 検証用データでの損失が下がらなくなったら打ち切る。過学習の防止に使う、fine-tuning で定番
  • 人間判断: 学習ログを見て、損失カーブが平坦になったら手動で止める

論文の training details で total_steps=300000 のような表記が出てきたら、「その回数で止める」が学習終了の合図だ。

分散学習は別の話

実際のLLM訓練は複数GPU / 複数ノードで分散実行するが、並列化・通信の話は別層の問題で、1ステップの中身自体はここに書いたとおりだ。 学習ループの実装寄りの話は別記事(例: 非同期RLの学習アーキテクチャ)にある。

そもそも「正解」はどこから来るのか

ここまで損失・勾配・optimizer と機械的な部分ばかり見てきたが、1歩引くと「そもそもその『正解』は誰が用意しているのか?」が気になってくる。 モデルの出力と正解のズレが損失なのだから、正解なしには損失も定義できない。

答えは、学習のタイプによって違う。機械学習は、正解の扱い方で大きく4種類に分けられる。

学習のタイプ正解の出どころ代表例
教師あり学習人間がラベルを付けたデータ画像分類、スパム判定、対話の模範解答
教師なし学習正解は無い。データ自体の構造を見つけるクラスタリング、次元削減、異常検知
自己教師あり学習データ自体から機械的に正解を作るLLMの事前学習、BERTのマスク予測
強化学習環境からの報酬(スコア)を広義の正解として使うゲームAI、ロボット制御、RLHF

それぞれの動き方

  • 教師あり学習 は、画像に「猫」「犬」と人間がタグを付けたような、明示的な正解を使う。一番分かりやすい形
  • 教師なし学習 は「正解を使わない」のが特徴で、データの塊をまとめたり(クラスタリング)、高次元のデータを低次元に圧縮したり(次元削減)する。損失は「データ自体の性質をどれだけ保てているか」(例: 再構築誤差、似たデータ同士を近くに寄せる距離)で定義される
  • 自己教師あり学習 は、正解をデータ自体から機械的に作る。LLMは「次に来るトークン」をテキストそのものから取り出して正解にする
  • 強化学習 は行動の良し悪しを後から評価する形。1つの正解ではなく、行動の累積報酬を最大化する方向に動く

ディープラーニングはどれにも使える

ついでに整理しておくと、ディープラーニング (深層学習) は「層の深いニューラルネットを使う学習手法」の総称で、上の4分類とは切り口が違う。 教師あり / 教師なし / 自己教師あり / 強化 のどの学習タイプでも、内部で深いネットを使えばディープラーニングと呼ばれる。 どの場合でも勾配降下と逆伝播が走っていて、この記事の話がそのまま当てはまる。

LLMが特別なのは「大量の自己教師あり学習データ(= Webのテキスト)が存在する」という条件が揃っている点で、ディープラーニング全体としては4分類のうちの1つを大規模に回している状態に過ぎない。

LLMの事前学習が特殊な理由

とくにLLMの事前学習が特殊なのは、正解が訓練データの中にすでに埋め込まれている点だ。 Webや書籍から集めた大量のテキストに対して、「この文脈の次に来るトークンは?」というクイズを延々と解かせる。正解はテキストを1つ後ろにずらすだけで自動的に出てくるので、人間のラベル付けは不要になる。 これを 自己教師あり学習 (self-supervised learning)と呼び、LLMが兆単位のトークンで学習できる理由の1つになっている。

ファインチューニングやRLHFで人間が関わるのは、この自己教師あり学習で得た大雑把な言語知識を「人間の望む形」に調整する後工程の話だ。

ハルシネーションと自己教師ありの関係

ちなみに「自己教師あり」は、文字通り自分を自分で教える構造なので、データ側の内容がそのまま「正解」として受け継がれる。 Webや書籍のテキストに間違いや創作、古い情報、偏見が混ざっていれば、それも含めて学習される。事前学習の段階では「嘘だよ」と訂正してくれる外部の存在がいない。

LLMの ハルシネーション (もっともらしい嘘を言う現象)の根っこには、この「先生がデータ自身だ」という事情が絡んでいる。 ファインチューニングやRLHFは、この「データが正解になっている状態」に人間の評価で上書きをかけて、事実と嘘の扱いを少しでも揃える作業、とも読める。

実際のファインチューニング作業がどういう雰囲気なのか知りたい場合は、このブログでもいくつか実践記録がある。

対象のモデルや手法は違うが、裏でやっていることは勾配降下のループなので、この記事の内容がそのまま当てはまる。

読みながらすれ違いそうな基本の疑問

学習の「動き方」を追ってきたので、そもそも論で置いてけぼりになりがちな点をまとめて補足する。

ニューラルネットって何だっけ

ニューラルネットは、「数値を受け取って、何度も行列積と簡単な変形(活性化関数)を通して、最後に目的の数値を吐くだけの関数」だ。

flowchart LR
    X["入力 x (数値の並び)"] --> L1["層1: Wx+b → 活性化"]
    L1 --> L2[層2]
    L2 --> L3[層3]
    L3 --> Y["出力 (確率分布・数値)"]

各層は重み行列 WW とバイアス bb を持っていて、入力に対して Wx+bWx + b を計算し、その結果を非線形関数(ReLUなど)に通す。 層を何段も重ねることで、複雑な関係を表現できる関数になる。

今まで θ\theta と書いてきた パラメータ とは、この全層の全 WW・全 bb をまとめた呼び名で、学習で調整する対象はこの θ\theta になる。

ディープラーニングってこれが積み重なってるだけ?

ほぼその通り。 ディープラーニング (深層学習) は、この層を 深く (= たくさん)積んだニューラルネット全般を指す呼び方で、2層や3層の浅いネットと区別するために使われる。 Transformer のような現代のLLMは、数十〜数百層を積み上げた典型例になる。

層を増やせば表現できる関数が複雑になり、簡単なネットでは学習できなかった難しい対応関係(画像から物体名、テキストから意味、音声から文字、など)も扱えるようになる。 ただし単純に深くするだけだと勾配消失・爆発が起きて学習が止まってしまうので、残差接続LayerNorm といった「深くても勾配が通る工夫」がセットで入っている、という話がちょうど前の章で出てきたやつだ。

脳の模倣ってよく聞くけど、実際そうなの?

「ニューラルネット」という名前や、層状に繋がったユニットの見た目は、確かに生物のニューロンを参考にしたところから始まっている。 1940〜50年代の McCulloch-Pitts モデルや Rosenblatt のパーセプトロンが出発点で、「脳が神経細胞の発火で情報処理している」という観察に触発された設計だった。

ただ、現代のディープラーニングは名前と見た目を引き継いだだけで、中身は生物学的な脳からかなり離れている。

  • 脳のニューロンは発火のタイミング(スパイク)で情報を表すが、ニューラルネットは連続値のベクトルを渡す
  • 脳の学習は部分的で非同期な化学反応だが、ディープラーニングは全体を微分して勾配で一斉に更新する
  • 脳が逆伝播(backprop)をやっている証拠は見つかっていない

出発点は脳のメタファだったが、今は「脳のやり方を真似ている」というより「機能として似た結果を出す別物」になっている、と読むのが現状に近い。

行列積を重ねるだけでなんで動くのか

1層だけだと線形変換(直線的な変換)しかできない。ただ非線形関数(ReLUなど)を挟みながら層を重ねると、理論上ほぼどんな連続関数でも近似できることが知られている(普遍近似定理 と呼ばれる)。 「猫か犬かを判定する」「次のトークンの確率を返す」「画像を生成する」など、入力と出力の対応関係が関数として書ければ、ニューラルネットで近似できる、という構図だ。

学習はその近似をデータに合わせる作業なので、訓練後のネットは「データが教えてくれた入出力対応を覚え込んだ巨大な関数」になる、と思えばよい。

学習は逐次? 一括? どうパラメータが動くのか

結論: 1ミニバッチごとに、全パラメータを同時に少しずつ動かす、を繰り返す。

  • 1ミニバッチぶんの順伝播・逆伝播で、全パラメータ θ\theta(何億〜何兆個)に対する勾配 L\nabla L が一度に計算される
  • optimizer.step() で、θθηL\theta \leftarrow \theta - \eta \nabla L の更新が全パラメータに対して同時に適用される
  • 学習率 η\eta が小さいので、1回の更新で動く量は微小
  • これを何百万〜何十億ステップ繰り返す

つまり「一括」(全パラメータが同時に動く)かつ「逐次」(少しずつ何度も繰り返す)の両方の性質を持つ。 大きな建物の全てのネジを、毎ステップほんの少しずつ同時に回し続ける、というイメージが近い。

ここまで読めると何が読めるか

LLMの論文・モデルカード・学習ログで見かける設定項目が、「意味のある数値」として読めるようになる。

よく見る設定読み方
lr = 1e-4学習率の目標値。Adam系の定番
AdamW(betas=(0.9, 0.95))一次モーメントの慣性0.9、二次モーメントの慣性0.95
weight_decay = 0.1パラメータが大きくなりすぎるのを抑える正則化
warmup_steps = 500最初の500ステップで学習率を0から目標値まで上げる
cosine schedulewarmup後、cos曲線で学習率を下げる
grad_clip = 1.0勾配のL2ノルムが1を超えたらスケールダウン
batch_size = 4M tokens1ミニバッチが400万トークン
loss curve1ステップごとの損失の推移
gradient norm勾配のL2ノルム、爆発の兆候を見る指標

論文のtraining details節や、W&B / TensorBoardの学習ログ画面の意味がだいたい追えるようになっているはずだ。

飛ばして問題ない話

入門の段階では、以下は無視してOKだ。

用語中身なぜ飛ばしていいか
2階の最適化(ニュートン法、L-BFGS)勾配の微分(ヘッセ行列)も使う手法LLMではコスト的に使われない
Natural Gradientパラメータ空間の幾何を使った勾配修正実装が重く、標準的には使われない
正則化(L1/L2、Dropout)過学習を抑える追加の項・手続き重要だが別トピック。必要になったら個別で読む
Gradient Accumulation複数ステップの勾配を足してから更新ミニバッチを実質的に大きくするトリック
Mixed Precision (fp16/bf16)計算精度を下げて高速化実装・ハードウェア寄りの話
分散学習(DDP、FSDP、ZeRO)複数GPUでパラメータを分割して学習学習ループの上位レイヤの最適化
RL系のポリシー勾配強化学習特有の勾配計算RLHFなどで出てくるが別枠

最低限押さえるべきは、θθηL\theta \leftarrow \theta - \eta \nabla L の更新式、逆伝播の連鎖律展開、Adamの一次/二次モーメント、残差接続+LayerNormが勾配を通す仕組み、の4点だ。


次に読むと繋がりやすい記事

気になった人向けの用語メモ

この節は読み飛ばしてOK。

用語意味
勾配降下θθηL\theta \leftarrow \theta - \eta \nabla L で損失を下げる更新の総称
学習率 η\eta1ステップの踏み込み幅
SGD確率的勾配降下。ミニバッチで勾配を計算する手法
逆伝播(backprop)連鎖律を大規模に適用して勾配を計算する手続き
計算グラフ順伝播の演算を辺で繋いだ図。逆伝播の骨格
autogradPyTorchなどフレームワークの自動微分機能
Optimizer勾配を受けて θ\theta を更新する部品(SGD、Adam、AdamW)
Momentum前の勾配を慣性として足す改良
Adam一次モーメント(方向)と二次モーメント(スケール)を使う Optimizer
AdamWAdam + weight decay の正しい組み合わせ
勾配消失連鎖律で勾配が0に潰れる現象
勾配爆発連鎖律で勾配が\inftyに発散する現象
勾配クリッピング勾配の大きさに上限を設けるテクニック
残差接続y=f(x)+xy = f(x) + x の構造。勾配がショートカットで流れる
LayerNorm各層で値を平均0、分散1に揃える正規化
warmup学習率を0から目標値まで徐々に上げる期間
cosine decaywarmup後、cos曲線で学習率を下げるスケジュール

これでシリーズはいったん区切り。1本目から5本目までで、AI記事の「数学記号まわり」はかなり広く読めるようになっているはずだ。