一文要約: 学習率は各パラメータ更新でどれだけステップを踏むかを決めます。大きすぎるとモデルが振動し、小さすぎると進みが遅く、適切に選ばれた値と良いスケジュールがあって初めて、安定して効率的な訓練が実現します。
17.1 学習率とは何か
17.1.1 基本のパラメータ更新式
ニューラルネットワークの訓練は勾配降下法です。損失が各パラメータに対してどう変化するかを計算し、損失を減らす方向にパラメータを動かします。
基本的な更新ルール:
new_weight = old_weight - learning_rate × gradient
数式で書くと:
17.1.2 具体的な例
learning_rate = 0.1
old_weight = 0.90
gradient = -0.4
new_weight = 0.90 - 0.1 × (-0.4)
= 0.90 + 0.04
= 0.94
勾配は負なので、この重みを大きくすれば損失が減ることを意味します。したがって重みは0.90から0.94へ上がります。学習率は、勾配のうちどれだけを実際に適用するかを制御します。
17.1.3 学習率が制御するもの
学習率(lr)は各更新のステップサイズを決めます:
- lr = 0.1: 各ステップで勾配の10%を取る
- lr = 0.01: 勾配の1%を取る
- lr = 0.001: 勾配の0.1%を取る
訓練のすべての反復で、これがモデル内のすべてのパラメータに適用されます。
17.2 三つのケース
17.2.1 損失ランドスケープの可視化
小さすぎる場合(左):
- 各ステップが極めて小さい
- 最小値に到達するまで非常に長い時間がかかる
- 浅い局所最小値で停滞することがある
- トレーニング曲線がほとんど動かないように見える
大きすぎる場合(中央):
- 各ステップが大きすぎる
- オプティマイザがオーバーシュートして最小値の周りを飛び跳ねる
- 損失が減少せず振動する
- 最悪の場合、損失は完全に発散する
ちょうど良い場合(右):
- ステップは適度
- 損失が安定して最小値へ降りていく
- トレーニング曲線が滑らかに下向きに傾く
17.2.2 Andrej Karpathyの観察
Andrej Karpathyの有名なツイートがあります:
"3e-4 is the best learning rate for Adam, hands down." "(i just wanted to make sure that people understand that this is a joke...)"
このジョークが効くのは、3 × 10⁻⁴(0.0003)が多くのタスクにおいて Adam 系オプティマイザの妥当な出発点として実際に機能するからです。普遍的な答えに見える理由は、多くの標準的なアーキテクチャやバッチサイズが似たような領域で収束するからです。しかし、本当に最適な学習率はモデルサイズ、バッチサイズ、データ、スケジュールに依存します。普遍的な定数は存在しません。
17.2.3 損失ランドスケープ
損失曲面は、すべてのモデルパラメータの高次元関数です。3Dで可視化すると:
- X軸、Y軸: 二つのパラメータ値
- Z軸: 損失値
曲面には谷と尾根があります。目標は谷へ降りていくことです。学習率は降りる速さを決めます。そして、谷を完全に飛び越えてしまうかどうかも決めます。
17.3 どのパラメータが更新されるのか
17.3.1 Transformer の訓練可能なすべてのパラメータ
完全な訓練の間、訓練可能なすべてのパラメータが更新されます:
1. Word Embedding
- 各トークンのベクトル表現
- パラメータ:
vocab_size × d_model
2. Attention の重み
- Wq, Wk, Wv: 入力から Q, K, V を生成
- Wo: Attention 後の出力射影
- レイヤごとのパラメータ:
4 × d_model²
3. FFN の重み
- 間に ReLU を挟む二つの線形層
- レイヤごとのパラメータ:
2 × d_model × d_ff
4. 出力射影 Wp
- 隠れ状態を語彙ロジットに写像
- パラメータ:
d_model × vocab_size - 多くの場合トークン埋め込みと重みを共有(weight-tied)
17.3.2 すべてのパラメータが同時に更新される
逆伝播はすべてのパラメータの勾配を同時に計算します。そして単一のオプティマイザステップが、それぞれの勾配と共通の学習率を使って、すべてを一度に更新します。
損失を計算 (予測とターゲットを比較)
|
バックプロパゲーション
|
すべてのパラメータの勾配を計算
|
すべてのパラメータを同時に更新
17.4 PyTorch 実装
17.4.1 コード例
import torch
import torch.nn.functional as F
# 1. 損失を計算
loss = F.cross_entropy(input=logits_reshaped, target=targets_reshaped)
print(loss.item()) # 出力: 11.515044212341309
# 2. 更新前の重みを確認
for name, param in Wq.named_parameters():
print(name, param)
# weight Parameter containing:
# tensor([[ 0.0474, -0.0321, -0.0234, ...
# 3. オプティマイザを作成し1ステップ実行
optimizer = torch.optim.AdamW(Wq.parameters(), lr=0.0001)
optimizer.zero_grad() # 蓄積された勾配をクリア
loss.backward() # 勾配を計算
optimizer.step() # パラメータを更新
# 4. 勾配を確認
for name, param in Wq.named_parameters():
print(name, param.grad)
# weight tensor([[-1.7941e-09, -2.7664e-10, -2.7543e-09, ...
# 5. 更新後の重みを確認
for name, param in Wq.named_parameters():
print(name, param)
# weight Parameter containing:
# tensor([[ 0.0474, -0.0320, -0.0233, ...
17.4.2 三つの重要な行
loss.backward(): 自動微分によりすべてのパラメータの勾配を計算optimizer.zero_grad(): 勾配をクリア(PyTorch はデフォルトで勾配を蓄積するため、各ステップ前にクリアが必要)optimizer.step(): 現在の学習率で更新ルールを適用
17.4.3 パラメータ変化の観察
この例でのパラメータの変化:
Before: 0.0474, -0.0321, -0.0234, ...
After: 0.0474, -0.0320, -0.0233, ...
変化は非常に小さく、小数点以下4桁目です。理由は:
- 学習率が小さい(0.0001)
- 勾配が小さい(~10⁻⁹)
しかし、何千ステップも続けるうちに、これら微小な調整が積み重なって本物の学習になります。これが勾配降下法の核心です。
17.5 よく使われるオプティマイザ
17.5.1 SGD (Stochastic Gradient Descent)
ベースラインです:
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
更新ルール:
θ = θ - lr × gradient
シンプルで理論的にもクリーンです。大規模な Transformer 訓練では、たいてい収束が遅すぎます。それでも基礎の理解には有用です。
17.5.2 Adam
ディープラーニングで最も広く使われているオプティマイザ:
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
主な特徴:
- 勾配の一次モーメント(平均)と二次モーメント(分散)を保持
- パラメータごとに実効学習率を適応
- スパースな勾配にもうまく対応
17.5.3 AdamW
重み減衰を修正した Adam:
optimizer = torch.optim.AdamW(model.parameters(), lr=0.0001, weight_decay=0.01)
通常の Adam では重み減衰がやや壊れていて、減衰と勾配適応が混ざってしまいます。AdamW はこれを修正します。AdamW は大規模 Transformer モデル訓練のデフォルト選択肢です。
17.5.4 一般的な設定
| モデル | オプティマイザ | 初期 LR | Weight Decay |
|---|---|---|---|
| GPT-2 | Adam | 2.5e-4 | 0.01 |
| GPT-3 | Adam | 6e-5 ~ 2e-4 | 0.1 |
| LLaMA | AdamW | 3e-4 | 0.1 |
17.6 学習率スケジューラ
17.6.1 なぜスケジューラが必要か
訓練全体を通じて単一の固定学習率が最適であることはまれです:
- 訓練初期: 大きなステップは良い領域を素早く見つけるのに役立つ
- 訓練後期: 小さなステップはその領域内で微調整するのに役立つ
17.6.2 Warmup + Cosine Decay
現代の LLM 訓練で最も一般的なスケジューラ:
Learning Rate
| /\
| / \__
| / \___
| / \____
+--------------------------> Training Steps
| |
warmup cosine decay
- Warmup: 学習率を小さな値から目標値へ線形に増加
- Cosine Decay: 学習率が cosine 曲線に従い、最小値まで減少
17.6.3 PyTorch 実装
from torch.optim.lr_scheduler import CosineAnnealingLR, LinearLR, SequentialLR
optimizer = torch.optim.AdamW(model.parameters(), lr=3e-4)
# warmup: 最初の1000ステップで線形に増加
warmup_scheduler = LinearLR(
optimizer,
start_factor=0.1, # lr × 0.1 から開始
total_iters=1000
)
# cosine decay: 残りのステップで減少
cosine_scheduler = CosineAnnealingLR(
optimizer,
T_max=100000, # 総訓練ステップ数
eta_min=1e-5 # 最小学習率
)
# 結合
scheduler = SequentialLR(
optimizer,
schedulers=[warmup_scheduler, cosine_scheduler],
milestones=[1000]
)
# トレーニングループ内
for step in range(total_steps):
loss = train_step(...)
optimizer.step()
scheduler.step() # 学習率を更新
17.6.4 なぜ Warmup なのか
訓練の最初では:
- パラメータはランダムに初期化されており、スケールが任意
- 勾配方向の推定は最小限の履歴に基づくため信頼性が低い
- 大きなステップはモデルの初期表現に取り返しのつかない損傷を与える可能性がある
Warmup は、フル学習率を適用する前に、モデルが安定した内部スケールを見つける時間を与えます。Warmup なしでは、大規模モデルは最初の数百ステップで発散することがよくあります。
17.7 学習率と他のハイパーパラメータ
17.7.1 バッチサイズ
大きなバッチ → より大きな学習率を使える。
経験則(Linear Scaling Rule):
バッチサイズが2倍になれば、学習率も2倍にできる
理由: 大きなバッチはより正確な勾配推定を与えます。より正確な勾配は、より大きなステップが安全であることを意味します。
17.7.2 モデルサイズ
大きなモデル → 通常、より小さな学習率が必要。
| モデル規模 | パラメータ数 | 典型的な学習率 |
|---|---|---|
| Small | ~100M | 3e-4 ~ 1e-3 |
| Medium | ~1B | 1e-4 ~ 3e-4 |
| Large | ~10B | 3e-5 ~ 1e-4 |
| XL | ~100B+ | 1e-5 ~ 3e-5 |
大きなモデルはより多くのパラメータを持ち、損失曲面もより複雑です。小さなステップは不安定化のリスクを減らします。
17.7.3 Weight Decay
Weight decay は各更新に加わる L2 正則化項です:
θ = θ - lr × (gradient + weight_decay × θ)
これは重みをゼロに向けて押し、個々のパラメータが極端に大きくなるのを防ぎ、過学習の予防に役立ちます。
一般的な値: 0.01 から 0.1。多くの LLM 訓練設定で AdamW のデフォルトは 0.1 です。
17.8 実践的なガイダンス
17.8.1 学習率の選び方
-
デフォルトから始める:
- Transformer 上の Adam/AdamW: 3e-4 が妥当な初期推測
- 大規模モデル: 1e-4 以下
-
トレーニング曲線を読む:
- 損失が安定して減少 → 学習率は良い
- 損失が振動 → 学習率が大きすぎる
- 損失がほとんど動かない → 学習率が小さすぎる
-
学習率ファインダーを使う:
- 非常に小さい値から始め、数百ステップで指数的に増加
- 損失が最も速く減少する点を見つける
- その少し下の値を使う
17.8.2 一般的な診断
| 症状 | 可能性のある原因 | 対処 |
|---|---|---|
| 損失が減少しない | LR が小さすぎる | LR を上げる |
| 損失が激しく振動 | LR が大きすぎる | LR を下げる |
| 損失が NaN になる | LR が大きすぎる、または数値オーバーフロー | LR を下げる、データを確認 |
| 損失が下がってから上がる | 過学習、または LR が減衰していない | 減衰を加える、正則化を加える |
17.8.3 推奨設定
optimizer = torch.optim.AdamW(
model.parameters(),
lr=3e-4, # 初期学習率
betas=(0.9, 0.95), # Adam モメンタムパラメータ
weight_decay=0.1 # weight decay
)
total_steps = 100000
warmup_steps = 2000
scheduler = get_cosine_schedule_with_warmup(
optimizer,
num_warmup_steps=warmup_steps,
num_training_steps=total_steps
)
betas を Adam のデフォルト (0.9, 0.999) ではなく (0.9, 0.95) にするのは、言語モデル訓練に対する Karpathy の推奨です。二次モーメントの減衰を低くすることで、オプティマイザが最近の勾配情報により速く反応するようになります。
17.9 章のまとめ
17.9.1 中核となる式
new_weight = old_weight - learning_rate × gradient
θ_new = θ_old - lr × ∂Loss/∂θ
17.9.2 学習率の効果
| 学習率 | 効果 |
|---|---|
| 大きすぎる | 振動、発散 |
| 小さすぎる | 収束が遅い、停滞することも |
| 適切に選ばれた値 | 安定して効率的に収束 |
17.9.3 推奨設定
| コンポーネント | 推奨値 |
|---|---|
| オプティマイザ | AdamW |
| 初期 LR | 1e-4 ~ 3e-4 |
| Weight Decay | 0.01 ~ 0.1 |
| スケジューラ | Warmup + Cosine Decay |
| Warmup ステップ | 総ステップの 1-5% |
17.9.4 核となる洞察
学習率はおそらく最も重要な訓練ハイパーパラメータです。各パラメータ更新のサイズを制御します。大きすぎると振動を引き起こし、小さすぎると収束が遅くなります。安定して効率的な実行のためには、warmup と cosine decay と組み合わせましょう。大規模 Transformer モデルでは、AdamW を lr ≈ 3e-4、weight_decay ≈ 0.1 で使うのが堅実なデフォルトです。
章末チェックリスト
この章を終えた後、あなたは次のことができるはずです:
- 重み更新の式を述べ、各項が何をするかを説明できる。
- 学習率が大きすぎる場合と小さすぎる場合の症状を説明できる。
- Transformer 訓練で AdamW が通常の Adam より好まれる理由を説明できる。
- Warmup と cosine decay を説明し、それぞれがいつ適用されるかを述べられる。
第4部のまとめ
これで 第4部: 完全なアーキテクチャ を完了しました。
| 章 | テーマ | 中核となる考え方 |
|---|---|---|
| 第13章 | 残差接続と Dropout | 勾配ハイウェイ、過学習防止 |
| 第14章 | 埋め込み + 位置エンコーディング | 連結ではなく、和をとる |
| 第15章 | 完全な順伝播 | テキストから確率へ、形状追跡 |
| 第16章 | 訓練 vs 推論 | 並列訓練、逐次推論 |
| 第17章 | 学習率 | ステップサイズの制御とスケジューリング |
これで Transformer がエンドツーエンドでどう動作するかを理解しました。すべての部品とそれらがどうつながっているかが分かったはずです。
次の章でお会いしましょう
理論はここまでで十分です。Warmup + cosine decay を図を見ずに説明できるなら、第5部の準備はできています。
第5部はコードを書くパートです。第18章では PyTorch でモデルをゼロから一クラスずつ定義します。第19章ではそれを訓練します。第20章では推論を実行します。終わるころには、400行以下のコードで動く GPT が手に入っています。
それでは、コードの世界でまたお会いしましょう。お疲れさまでした。