学習率ウォームアップとNoamスケジュール — なぜTransformerは「最初ゆっくり」学ぶ必要があるのか

学習を始めたばかりのモデルこそ、一番たくさん学べるはずです。だったら学習率は最初から大きくして、どんどん更新すればいい——この素朴な直感は、Transformerではに働きます。最初に大きな学習率を使うと、モデルは学習を始める前に壊れてしまうのです。

だからTransformerの学習レシピには、必ずと言っていいほど「ウォームアップ(warmup)」が入っています。最初の数千ステップは学習率をほぼゼロから少しずつ引き上げ、目標値に達してから本格的に減衰させる。準備運動をしてから走り出すイメージです。原論文 “Attention Is All You Need” は、これを1本の式で表現しました。

$$ \mathrm{lrate} = d_{\text{model}}^{-0.5} \cdot \min\left(\mathrm{step}^{-0.5},\ \mathrm{step} \cdot \mathrm{warmup}^{-1.5}\right) $$

提案者の一人 Noam Shazeer にちなんで Noamスケジュール とも呼ばれる式です。初見では暗号のようですが、分解すればすべての項に明確な意味があります。

ウォームアップを理解しておくと、次のような場面で役に立ちます。

  • 自分でTransformerを学習するとき: 「lossが下がらない」「学習が発散した」というトラブルの原因の筆頭がウォームアップ設定。壊れ方と処方箋が分かる
  • アーキテクチャの設計判断: Pre-LN/Post-LNというLayerNormの配置の違いがウォームアップの必要性を左右する。GPT系がPre-LNを採用した理由が腹落ちする

本記事の内容

  • Noam式の完全分解 — 2本の枝の min、交点の連続性、ピーク学習率の計算
  • なぜ最初ゆっくりが必要か①: Adamの適応項は初期ほど暴れる(実測でばらつき1908倍)
  • なぜ最初ゆっくりが必要か②: Post-LNの勾配問題(Xiong et al., ICML 2020)
  • 8層Transformerの実学習で「warmupなしのPost-LNだけが学習不能」を再現
  • cosine減衰など現代LLMのスケジュールとの関係

前提知識

この記事を読む前に、以下の記事を読んでおくと理解が深まります。

Noamスケジュールの全体形状: 線形ウォームアップと逆平方根減衰

上の図がNoamスケジュールの全体像です。学習率は2つの局面を持ちます。①最初の warmup ステップ(図では4000)までは線形にゼロから引き上げ、②それ以降はステップ数の逆平方根でゆっくり減衰させます。赤い点のピークがちょうど step = warmup の位置に来る——この形が1本の min 式から生まれる仕組みを、次節で分解します。

Noam式を完全分解する

もう一度式を眺めます。

$$ \mathrm{lrate} = d_{\text{model}}^{-0.5} \cdot \min\left(\mathrm{step}^{-0.5},\ \mathrm{step} \cdot \mathrm{warmup}^{-1.5}\right) $$

min の中に2本の「枝」が入っています。それぞれの正体を確認しましょう。

枝1: $\mathrm{step} \cdot \mathrm{warmup}^{-1.5}$(線形増加)。step の1次式なので、ステップに比例してまっすぐ増えます。傾きは $\mathrm{warmup}^{-1.5}$ で固定です。

枝2: $\mathrm{step}^{-0.5}$(逆平方根減衰)。ステップが進むほど $1/\sqrt{\mathrm{step}}$ で小さくなる減衰曲線です。

min は「2本のうち低い方を採用する」という意味です。では、どちらが低いかの境目はどこでしょうか。2本の枝が等しくなる step を求めます。

$$ \mathrm{step}^{-0.5} = \mathrm{step} \cdot \mathrm{warmup}^{-1.5} $$

両辺に $\mathrm{step}^{0.5}$ を掛けて整理すると

$$ 1 = \mathrm{step}^{1.5} \cdot \mathrm{warmup}^{-1.5} = \left(\frac{\mathrm{step}}{\mathrm{warmup}}\right)^{1.5} $$

つまり 交点はちょうど step = warmup です。それより前は増加中の枝1が下にいて(線形ウォームアップ)、それより後は減衰する枝2が下に来ます(逆平方根減衰)。しかも交点で両枝の値が一致するので、学習率は折れ曲がりこそすれ、飛びのない連続な曲線になります。

Noam式のminを2本の枝に分解: 交点がstep=warmup

破線の2本が枝1(緑・線形増加)と枝2(茶・逆平方根減衰)、青の実線がその min です。交点 step = warmup を境に「採用される枝」が入れ替わる様子がひと目で分かります。1本の式でウォームアップと減衰を同時に書けるのは、この min のトリックのおかげです。

交点での値、つまりピーク学習率も計算できます。step = warmup を代入すると

$$ \mathrm{lrate}_{\text{peak}} = d_{\text{model}}^{-0.5} \cdot \mathrm{warmup}^{-0.5} = \frac{1}{\sqrt{d_{\text{model}}}\sqrt{\mathrm{warmup}}} $$

原論文の設定($d_{\text{model}} = 512$, warmup = 4000)で実際に計算してみましょう。

import numpy as np

def noam(step, d_model, warmup):
    step = np.asarray(step, dtype=float)
    return d_model ** (-0.5) * np.minimum(step ** (-0.5), step * warmup ** (-1.5))

d_model, warmup = 512, 4000
peak = noam(warmup, d_model, warmup)
print(f"ピーク学習率 = {peak:.6e} = 1/{np.sqrt(d_model)*np.sqrt(warmup):.1f}")
# => ピーク学習率 = 6.987712e-04 = 1/1431.1

ピークは約 7.0×10⁻⁴。「Adamの定番 1e-3 よりやや控えめ」という妥当な値が、2つのハイパーパラメータから自動的に決まる設計です。ウォームアップ長と $d_{\text{model}}$ を動かすと形がどう変わるかも見ておきます。

ウォームアップ長を変えるとピークの高さと位置が変わる

warmup を 1000 → 4000 → 8000 と延ばすと、ピークは遅く・低くなります。ピーク学習率が $\mathrm{warmup}^{-0.5}$ に比例するためで、「慎重に立ち上げるほど、到達する学習率も控えめになる」という設計です。

d_modelを変えると曲線全体が上下にスケールする

一方 $d_{\text{model}}$ を大きくすると、曲線全体が $d_{\text{model}}^{-0.5}$ 倍で下にスケールします。大きいモデルほど小さい学習率で学習させる、という経験則が式に織り込まれているわけです。

ピーク学習率はwarmupとd_modelの両方で決まる

ピーク学習率を warmup 長の関数として描くと、どちらのパラメータを増やしてもピークが下がる関係が確認できます。式の分解はこれで完了です。しかし肝心の問いが残っています——そもそも、なぜ最初に学習率を絞る必要があるのでしょうか?

理由①: Adamの適応項は初期ほど暴れる

1つ目の理由は、Transformerの学習に使われる Adam の内部にあります。Adamは勾配の2次モーメント(大きさの2乗の移動平均)$v$ を推定し、$1/\sqrt{\hat v}$ を各パラメータの実効学習率として使います。ここに初期特有の問題があります。学習開始直後は $v$ の推定に使えた勾配のサンプルが数個しかないのです。

サンプル数個で推定した分散は当てになりません。たまたま小さい勾配が続けば $\hat v$ は過小評価され、$1/\sqrt{\hat v}$ ——つまり実効学習率——が異常に大きくなります。この「推定不良による実効学習率の暴れ」を、乱数シミュレーションで測ってみます。

import numpy as np

rng = np.random.default_rng(0)
beta2, T, n_trials = 0.999, 300, 2000
eff_lr = np.zeros((n_trials, T))
for t in range(n_trials):
    g = rng.standard_normal(T)      # 勾配を平均0・分散1のノイズとみなす
    v = 0.0
    for i in range(T):
        v = beta2 * v + (1 - beta2) * g[i] ** 2
        vhat = v / (1 - beta2 ** (i + 1))         # バイアス補正
        eff_lr[t, i] = 1.0 / (np.sqrt(vhat) + 1e-8)

std = eff_lr.std(axis=0)
print(f"適応項のばらつき: step1={std[0]:.3f}, step10={std[9]:.3f}, "
      f"step100={std[99]:.3f}, step300={std[299]:.3f}")
print(f"ばらつき比 step1/step300 = {std[0]/std[299]:.1f}倍")
# => 適応項のばらつき: step1=75.181, step10=0.257, step100=0.073, step300=0.039
# => ばらつき比 step1/step300 = 1908.3倍

Adamの適応項1/√vのばらつきは初期に爆発的に大きい

結果は劇的です。実効学習率のばらつき(標準偏差)は、ステップ1で 75.2、ステップ300では 0.039——約 1908倍の差があります。学習開始直後のAdamは、パラメータごとに実効学習率が数十倍もばらつく「暴れ馬」の状態なのです。

Adamの更新量の軌跡: 個別試行でも初期は大きく振れる

個別の試行を追った上の図でも、最初の20ステップほどは更新量 $\hat m/\sqrt{\hat v}$ が大きく振れ、その後落ち着いていく様子が見えます。この暴れ馬に最初から大きな学習率を与えるのは危険で、$v$ の推定が信頼できるようになるまで学習率側を絞って待つ——これがウォームアップの1つ目の役割です(この問題を修正項で直接解決しようとしたのが RAdam です)。

ただし、話はAdamだけでは終わりません。Transformer特有の、もっと構造的な理由があります。

理由②: Post-LNの勾配はスケールが壊れている

原論文のTransformerは、残差接続のに LayerNorm を置く Post-LN 構成です。Xiong et al. (ICML 2020) は、この配置こそがウォームアップを必須にしている犯人だと理論的に示しました。

Post-LN層とPre-LN層の構造比較(原論文Figure 1)

出典: Xiong et al., “On Layer Normalization in the Transformer Architecture”, ICML 2020, Fig.1

図の (a) が Post-LN で、LayerNorm が residual の合流点のにあります。(b) が Pre-LN で、LayerNorm がサブレイヤーの入口に移動し、residual の通り道($x_l$ から $x_{l+1}$ への矢印)には何も挟まりません。たったこれだけの違いですが、勾配の流れ方が根本的に変わります。

Xiong らの解析の要点はこうです。Post-LN では、初期化直後の勾配のスケールが出力層付近で大きくなり、層を遡るにつれ不均衡になります。LayerNorm が residual 経路上にあるため、勾配が毎層 LayerNorm の微分を通過してスケールが歪むのです。この状態で大きな学習率を使うと出力付近の層が一撃で壊れ、学習が復帰不能になります。ウォームアップは「勾配のスケールが自然に均されるまで、そっとしておく」ための保護期間として機能します。

一方 Pre-LN では residual 経路が素通しなので、勾配は各層に直接届き、スケールは深さに対しておおむね均一です。理論どおりなら、Pre-LN はウォームアップなしでも学習できるはずです。これは実験で確かめられる主張です。

実験: warmupなしで壊れるのはPost-LNだけ

8層のTransformerエンコーダに、ランダムなトークン列をそのまま出力するコピータスクを学習させます。学習率はピーク $10^{-2}$(わざと高め)で、①線形ウォームアップあり(200ステップ)と②最初からピーク値の定数、を Post-LN / Pre-LN それぞれで比較します。勾配クリッピングは「壊れる様子」を隠してしまうので使いません。

import numpy as np
import torch
import torch.nn as nn

class TinyTransformer(nn.Module):
    def __init__(self, vocab=20, d_model=128, nhead=4, nlayers=8, seqlen=12, pre_ln=False):
        super().__init__()
        self.emb = nn.Embedding(vocab, d_model)
        self.pos = nn.Parameter(torch.randn(1, seqlen, d_model) * 0.02)
        layer = nn.TransformerEncoderLayer(
            d_model=d_model, nhead=nhead, dim_feedforward=4 * d_model,
            dropout=0.0, batch_first=True, norm_first=pre_ln)  # norm_first=TrueでPre-LN
        self.enc = nn.TransformerEncoder(layer, num_layers=nlayers)
        self.head = nn.Linear(d_model, vocab)

    def forward(self, x):
        return self.head(self.enc(self.emb(x) + self.pos))

def train_run(pre_ln, use_warmup, peak_lr=1e-2, warmup=200, steps=600, seed=0):
    torch.manual_seed(seed)
    rng = np.random.default_rng(seed)
    model = TinyTransformer(pre_ln=pre_ln)
    opt = torch.optim.Adam(model.parameters(), lr=1.0, betas=(0.9, 0.98), eps=1e-9)
    crit = nn.CrossEntropyLoss()
    losses = []
    for step in range(steps):
        s = step + 1
        lr = peak_lr * min(s / warmup, 1.0) if use_warmup else peak_lr
        for g in opt.param_groups:
            g["lr"] = lr
        x = torch.from_numpy(rng.integers(0, 20, size=(64, 12))).long()
        loss = crit(model(x).reshape(-1, 20), x.reshape(-1))
        opt.zero_grad(); loss.backward(); opt.step()
        losses.append(loss.item())
    return np.array(losses)

seeds = [0, 1, 2]
for name, pre_ln, warm in [("Post-LN + warmupあり", False, True),
                           ("Post-LN + warmupなし", False, False),
                           ("Pre-LN  + warmupなし", True, False)]:
    final = np.mean([train_run(pre_ln, warm, seed=s)[-100:].mean() for s in seeds])
    print(f"{name}: 最終100ステップの平均損失 = {final:.3f}")
# => Post-LN + warmupあり: 最終100ステップの平均損失 = 0.000
# => Post-LN + warmupなし: 最終100ステップの平均損失 = 2.997
# => Pre-LN  + warmupなし: 最終100ステップの平均損失 = 0.128

3シード平均の結果は理論の予言どおりでした。

Post-LNはウォームアップの有無で学習が激変する

Post-LN + ウォームアップあり(緑)は損失がすみやかにゼロへ落ちます。ところが同じモデル・同じ学習率でウォームアップを外すと(赤)、損失は 2.997 に張り付いたまま二度と下がりません。2.997 という値には意味があります。語彙数20の一様分布の交差エントロピーは $\ln 20 \approx 3.0$——つまりモデルは開始直後に壊れて「全トークン等確率」を出す状態に退化し、そこから回復できなかったのです。

Pre-LNは同じ高学習率でもウォームアップなしで学習できる

同じ「warmupなし・高学習率」でも、Pre-LN(青)は最初に一瞬スパイクを出しつつ、すぐ立て直して学習に成功します(最終損失 0.128。ウォームアップを付けるとスパイクも消えてさらに滑らかになります)。ウォームアップ抜きで壊れるのは Post-LN だけ——LayerNorm の位置がウォームアップの必要性を決めるという Xiong らの主張が、このサイズのモデルでも綺麗に再現されました。

この実験は、現代のLLMの設計選択も説明します。GPT系・LLaMA系など最近のモデルがほぼ例外なく Pre-LN(またはその変種)を採用するのは、深いモデルでも学習を安定させやすいからです。それでも実務ではウォームアップを完全には捨てず、短めに残すのが定石です。Adamの初期分散(理由①)は Pre-LN にしても残るためです。

現代のスケジュール: warmup + cosine減衰

Noamスケジュールの「逆平方根減衰」の部分は、現在では別の減衰に置き換えられることが多くなりました。現代LLMの事実上の標準は線形ウォームアップ + cosine減衰です。

代表的な学習率スケジュールの比較: 共通点は最初の線形ウォームアップ

図には定数・Noam(逆平方根)・cosine減衰・線形ウォームアップ+定数の4種を重ねています。減衰部分の形はさまざまですが、すべてに共通するのが「最初の線形ウォームアップ」です。GPT-3 も LLaMA も、総ステップの数%をウォームアップに充ててから cosine で減衰させます。減衰形状は性能チューニングの領域ですが、ウォームアップは安定性の生命線——この非対称な扱いに、本記事で見た2つの理由(Adamの初期分散・深いネットの初期勾配の歪み)が反映されています。

まとめ

本記事では、Transformerの学習率ウォームアップを式・理由・実験の3面から解説しました。

  • Noam式は2本の枝の min: step=warmup を境に線形増加(枝1)から逆平方根減衰(枝2)へ連続的に切り替わる。ピーク学習率は $1/(\sqrt{d_{\text{model}}}\sqrt{\mathrm{warmup}})$ で、原論文設定では約 7.0×10⁻⁴
  • 理由①はAdamの初期分散: 2次モーメントの標本が少ない初期は実効学習率が暴れる(実測でばらつき約1908倍)。落ち着くまで学習率を絞って待つのがウォームアップ
  • 理由②はPost-LNの勾配スケール: LayerNormがresidual経路上にあるPost-LNは初期勾配が不均衡で、高学習率の一撃で壊れる。実験でも warmupなしのPost-LNだけが損失 $\ln 20$ の「全トークン等確率」状態に退化した(2.997 vs warmupあり 0.000)
  • Pre-LNはウォームアップ不要側に倒れる: residual経路が素通しなので同条件でも学習成功(0.128)。現代LLMがPre-LN系を選ぶ理由
  • 現代の標準は warmup + cosine減衰: 減衰形は変わってもウォームアップだけは残り続けている

「最初ゆっくり」は精神論ではなく、最適化器の統計と勾配の幾何に根ざした必然でした。

次のステップとして、以下の記事も参考にしてください。

画像なし
Attention Is All You Need(Transformer原論文)を徹底解読
Scaled Dot-Product Attentionから位置エンコーディングまで、原論文を数式レベルで解読
画像なし
Layer Normalizationの理論と実装
バッチサイズに依存しない正規化。Pre-LN/Post-LNの違いの前提知識
Label Smoothingの理論と実装
Transformer原論文のもう1つの訓練テクニック。正解ラベルを少し崩すと汎化が上がる理由