ゼロから学ぶAdam — 2つの移動平均と、誰も説明しないバイアス補正
Adamはほぼすべての深層学習コードの既定オプティマイザなのに、あの4行が何をしているか言える人はほとんどいない。この記事はAdamが解決した2つの問題——素のSGDのジグザグと、層ごとにバラバラな勾配スケール——から組み立て、バイアス補正を数値で分解する。補正を外すと初手が3.16倍、ステップ12付近で6.57倍まで膨らむ。最後に落とし穴: 重み減衰はAdamではなくAdamWの仕事であり、εが平方根の外にあるのには理由がある。
要点
- Adamは、勾配の向きを覚える1次モーメントと大きさを覚える2次モーメントを、どちらも指数移動平均で持つ。
- 指数移動平均は、減衰率 β のとき直近およそ 1/(1-β) 個の値を覚えており、β=0.9 は約10ステップ、β=0.999 は約1000ステップである。
- 平均は0から始まるため、補正なしでは1ステップ目の歩幅が3.16倍、ステップ12付近で6.57倍のピークになり、収まるまで約5000ステップかかる。
- 更新則は w_t = w_{t-1} - η m̂_t/(√v̂_t + ε) で、1歩の大きさは勾配の単位に関係なくおよそ η 以下になる。
- 既定値は β1=0.9、β2=0.999、ε=10^{-8} で、勾配に λw を足す重み減衰は適応的な割り算を通るため、AdamWを使う。
Adam: A Method for Stochastic Optimization
一次資料 — この記事の根拠
この解説の公開 2026-09-23
Adam: A Method for Stochastic OptimizationarXiv:1412.6980論文ページ·PDFDecoupled Weight Decay Regularization (AdamW)arXiv:1711.05101論文ページ·PDF
素のSGDが行き止まりになるところ
勾配降下法はたった1行です。パラメータ に立ち、勾配 ——損失がもっとも速く登る方向——を計算し、その反対へ学習率 倍だけ踏み出す。
これでSGDは全部です。ゼロから積み上げたい人はゼロから学ぶ勾配降下法へ。ここで必要なのは、これが失敗する2つのしかただけです。Adamは、そのそれぞれに対する手当てを1つずつ足しただけのものだからです。
失敗その1: ジグザグ。 現実の損失曲面には*峡谷(ravine)*があります——ある方向には急な壁で、それと交わる方向にはほとんど平ら、という地形です。峡谷の壁の上では、勾配はほとんど峡谷を横切る向き(近い壁のほう)を指し、峡谷に沿った向き(出口のほう)はほとんど指しません。だからSGDは壁から壁へ跳ね返り、本当に下りたいなだらかな坂をのろのろ進みます。急な方向は行き過ぎないために小さな を要求し、平らな方向は前進のために大きな を欲しがる。1つの数で両方は満たせません。
失敗その2: スケールの不一致。 深いネットワークでは、ある層の勾配が別の層の勾配より100倍も1000倍も大きい、ということが日常的に起きます——層の幅が違い、活性化関数が違い、損失からの距離が違うからです。大きい勾配の層にとって妥当な1つの は、小さい勾配の層にとっては何も起きていないのと同じ。欲しいのは、パラメータごとの歩幅が自動で決まることです。
Adam(Adaptive Moment estimation、Kingma & Ba 2014)は、失敗1を最近の勾配の向きを覚えることで、失敗2をその大きさを覚えることで解決します。どちらの記憶も、同じ安価な装置——指数移動平均——でできています。
装置: 指数移動平均
Adamのすべての部品は1つの漸化式からできています。数の列 と、0と1のあいだの減衰率 に対して、指数移動平均(EMA)は
文として読むと、こうです。すでに信じていたものを だけ残し、いま見たものを だけ混ぜる。展開すると正体が見えます——過去に向かって幾何級数的に小さくなる重みをもつ、履歴全体の加重平均です。
役に立つ直感は、減衰率 のEMAは直近およそ 個の値を覚えている、というものです。だから は直近10ステップくらい、 は直近1000ステップくらいを覚えている。このたった1つの事実が、Adamの2つの既定の減衰率の両方を説明し、のちに「なぜ数百ステップも続く補正が必要になるのか」も説明します。
初期化に注目してください。 です。履歴がないところから始めるので、ゼロから始める。この選択が、2節あとで登場するバイアス問題の原因そのものです——覚えておいてください。
1次モーメント: 向きを覚える
EMAを勾配そのものに適用します。これが1次モーメント推定 ——勾配の平均の推定です。
1つずつ。 はこのステップの前まで「典型的な勾配の向きはこれだ」と信じていたもの。 はいまのミニバッチで測った勾配。 は古い信念をどれだけ残すかを決めます。 ではなく に沿って踏み出すことがモメンタムで、これがジグザグを直接なくします。峡谷の左右への跳ね返り成分は符号が反転しつづけるので、移動平均のなかで打ち消し合う。峡谷を下る成分は毎ステップ同じ符号なので、積み上がる。最適化は重いボールのように振る舞います——壁のあいだでガタつくのをやめ、下り坂を転がりつづける。
モメンタムはミニバッチのノイズもならします。各 はほんの数個の例から計算した、真の勾配のノイズ入り推定です。その10個を平均すれば、どの1つを信じるよりも安定した向きが得られます。
2次モーメント: 大きさを覚える
こんどは同じEMAを勾配の2乗に適用します。これが2次モーメント推定 です。
2乗は要素ごとです——各パラメータが自分の を持ち、そのパラメータの勾配が最近どれくらい大きかったかを、符号に関係なく追跡します。つまり は、その座標の勾配の典型的な大きさ(二乗平均平方根)の走る推定値です。RMSPropという名前の由来がここで、この1行はまさにRMSProp——Adamがそこから受け継いだ手法——そのものです。
計画は、歩幅を で割ることです。それが何をするか考えてください。勾配が一貫して巨大な座標は が大きくなるので歩幅は小さくなる。勾配が一貫してごく小さい座標は が小さいので歩幅は大きくなる。この割り算は勾配のスケールを打ち消し、残るのは符号と一貫性だけ——それがまさに、失敗2が求めていたパラメータごとの学習率です。勾配の大きさが桁で違う層どうしが、誰も別々にチューニングしなくても同じくらいの歩幅になります。
バイアス補正: 最初の数歩は嘘をついている
2つの平均をゼロで初期化しました。履歴がなかったのだから正直な選択なのですが、そのせいで序盤の推定はわかっている方向にずれます。そしてその手当てが、Adamのなかで気軽な解説からいちばん抜け落ちる部分です。
いちばん最初のステップを見てください。勾配が だったとします。すると
勾配1を測ったのに、典型的な勾配の推定値は0.1です。推定がノイジーなのではありません。種として置いた架空のゼロのほうへ、9割引きずられているのです。同じことが2次モーメントにも起きて、しかも が1にもっと近いのでずっとひどい: 。
ずれの大きさはきちんと計算できます。展開したEMAで、すべての が同じ値 だとしましょう。重みは で、この幾何級数は1ではなく になります。つまり
推定値は、わかっている倍率で縮んでいる。だから割り戻せばずれは消えます。その2つの割り算がバイアス補正済みモーメントです。
最初のステップをもう一度確かめます。——実際に測った勾配そのものです。。補正は でぴったり正しく、 につれて消えていく。序盤で効き、後半で消える——問題の形とちょうど同じです。
では実務で効くのでしょうか。「どちらのモーメントも縮んでいるのだから、比 では誤差が打ち消し合う」と言いたくなります。打ち消しません。縮み方が違うからです——分子は 、分母は を背負っている。補正なしの歩幅と正しい歩幅の比は
標準の 、 のAdamに一定勾配 を食わせると、補正ありの更新はステップ1からちょうど の歩幅で進みます——正解です。補正なしの更新はステップ1でその3.16倍、ステップ12付近で6.57倍というピークに達し、ステップ100でもまだ3.2倍、ステップ693でも1.4倍大きく、正解の1%以内に収まるのはおよそ5000ステップ後です。バイアス補正を省くことの代償は、数歩ぶらつくことではありません。パラメータがどの最小値からも最も遠いまさにその時期に、何千ステップも膨らんだ学習率を渡すことです。初期のAdam民間伝承がウォームアップを強く勧めたのはこのためで、ウォームアップが手当てしていたものの相当部分がこれでした。
この長い尾は から来ています。 が0.5に達するのは です。2次モーメントは1000ステップの記憶を持つので、その立ち上がりの過渡も1000ステップ続きます。
更新則を組み立てる
1ステップあたり4行、この順です。
最後の1行を1つの文として読んでください。 は直近の履歴でならされた向き。 はこの座標の勾配の典型的な大きさ。前者を後者で割ると信号対雑音比になります——最近の勾配の符号が揃っているときは大きく(平均がRMSとほぼ同じ大きさになり、比は1に近づく)、あちこち振れて打ち消し合うときは小さい(平均はほぼ0、RMSは大きいまま、比は0近く)。Adamは勾配が一貫している座標では大胆に、矛盾している座標では臆病に踏み出す——座標ごとに、です。そして は、ある座標の勾配がすべてほぼ0のときに割り算が発散しないようにする小さな定数です。
ここで効く上限があります。素直な場合には比 はおよそ1以下なので、Adamの1歩が各パラメータを動かす量は、勾配の単位が何であれ、だいたい 以下です。Adamの学習率がSGDのそれよりモデル間で使い回しやすいのはこのためです—— は未知の勾配にかける倍率ではなく、パラメータ空間での歩幅そのものだからです。
既定値と、それぞれの役目
論文の既定値は 、、 で、いまあなたのフレームワークに入っている値でもあります。それぞれに読み方があります。
は向きに約 ステップの記憶を与えます。峡谷の揺れを打ち消しミニバッチのノイズを平均するには十分長く、曲面が本当に曲がったときに向き直れるくらい短い。0.95や0.98に上げると(大規模言語モデルでよく使われます。バッチが巨大で各勾配がすでに低ノイズだからです)軌跡は滑らかになりますが、反応は遅くなります。
はスケールに約1000ステップの記憶を与えます。スケールは向きよりずっと長い窓で推定するべきです。それは進む方角ではなく勾配の分布についての統計量であり、分母が跳ねると歩幅そのものがガタつくからです。代償が前節の1000ステップの立ち上がり過渡です。学習が不安定で勾配の大きさが速く動いている現場では、0.95〜0.98まで下げることがあります。
は割り算の数値的な保険です。見た目の小ささよりも効きます。これは歩幅に静かな上限もかけるからです——ある座標の が をかなり下回ると、比はスケール不変ではなく に律速され、その座標は適応的なブーストを受けなくなります。 が極端に小さいと平らな座標が巨大な一歩を踏み、大きめの値(、混合精度や強化学習の一部では 以上)にするとAdamはモメンタム付きSGDに近い挙動になります。飾りの定数ではありません。
そして が依然としていちばん重要なダイヤルです。 が古典的な既定値で、Transformerのファインチューニングはふつう 〜 を欲しがります。Adamが適応させるのはパラメータ間の相対的な歩幅であって、絶対的な大きさを決めてくれるわけではありません。
コードで見る
オプティマイザ全体は十数行です。ここでは2次の椀 (勾配は )を から最小化しています。
import numpy as np
def grad(w): return 2 * w # L = w^2 の dL/dw
w = 5.0
m = v = 0.0 # 両モーメントはゼロから開始
beta1, beta2, eta, eps = 0.9, 0.999, 0.1, 1e-8
for t in range(1, 201): # t は1から——補正がtを必要とする
g = grad(w)
m = beta1 * m + (1 - beta1) * g # 1次モーメント: 向き
v = beta2 * v + (1 - beta2) * g * g # 2次モーメント: 大きさ
m_hat = m / (1 - beta1 ** t) # バイアス補正
v_hat = v / (1 - beta2 ** t)
w -= eta * m_hat / (np.sqrt(v_hat) + eps)
if t <= 5 or t == 200:
print(f"step {t:3d}: w = {w:+.4f}")
走らせると w = +4.9000, +4.8001, +4.7002, +4.6005, +4.5010 と進み、ステップ200で w = -0.0001 になります。序盤の数歩を見てください。そこでの勾配が10であるにもかかわらず、一歩ごとに はほぼちょうど だけ動いています。これがスケール不変という性質で、5つの数字に見えています。
次に補正の2行を消して m と v をそのまま使ってみます。最初のステップは w = +4.6838 になります——動いた量は0.3162、つまり 。バイアス補正の節で予測した膨張倍率そのものです。理論とprint文が小数4桁で一致します。
落とし穴
Adamの重み減衰は重み減衰になっていない——AdamWを使う。 L2正則化を足す教科書的なやり方は、勾配に を足すことです。それをAdamの内側でやると、罰則も他のすべてと一緒に の割り算を通ってしまいます。すると勾配の大きいパラメータは勾配の小さいパラメータより弱く減衰する——正則化の強さが勾配の大きさの偶然で決まってしまうのです。Loshchilov & Hutterの手当て(AdamW、2017)は、これを切り離すことです。適応的な一歩を踏んだあとで、割り算の外側で を別に引く。これは些細な最適化ではなく、論文はAdamの汎化を大きく改善し、画像分類でモメンタム付きSGDと張り合えるようになると報告しています。現代の言語モデルがほぼ例外なくAdamWで学習される理由でもあります。なお PyTorch の Adam(weight_decay=...) は結合した(劣る)版のほうなので、明示的に AdamW を掴んでください。
Adamは速く収束するが汎化は劣ることがある。 多くの画像タスクでは、よくチューニングしたモメンタム付きSGDのほうがAdamより良いテスト精度に到達します——Adamのほうが学習損失を早く下げるにもかかわらず。通説の説明は、適応的な分母が、SGDを鋭い最小値から押し出して平らでよく汎化する最小値へ運ぶまさにその勾配ノイズを抑えてしまう、というものです。実務的な読み方: Transformerや勾配スケールが極端に不均一なものでは Adam/AdamW が既定で、SGDではそもそも学習が進まないことも多い。固定ベンチマーク上のCNNでチューニングの余裕があるなら、モメンタム付きSGDも一度は試す価値があります。
εは平方根の外、その隣に置く——中ではない。 正しい分母は です。 と書くのは別のアルゴリズムです。後者は を2乗した勾配の単位で足すので、効きはじめるしきい値がまったく違います( ではなく )。どちらの形も実装の現場に存在するので、 の値をフレームワーク間で移植するときは、自分がどちらの流儀に着地しているかを確かめてから信じてください。
Adamはメモリを食う。 パラメータあたり追加のfloatバッファが2本、それぞれモデルと同じ大きさです。70億パラメータのモデルをfp32で持つなら、重みと勾配の上にさらに約56 GBのオプティマイザ状態が乗ります。8ビットオプティマイザやAdafactor、状態のシャーディングが存在するのはこのためです。モメンタム付きSGDはこのバッファが1本、素のSGDは0本です。
はグローバルステップで、1から始めること。 では補正が で割ってしまいます。チェックポイントから再開するときに をリセットすると、立ち上がりの過渡を丸ごと呼び戻すことになります——再開のたびに3〜6倍の学習率スパイクが静かに入るわけです。
現場ではこう使う
誰が、いつ触るか。 事前学習済みモデルをファインチューニングするMLエンジニアが、学習設定ファイルを書くとき。研究者が新しいアーキテクチャを一から学習させ、損失が序盤で跳ねたり NaN になったりした原因を追うとき。どちらの場面でも、Adamの4行のどこが効いているかを知っていれば、設定値を「おまじない」ではなく理由を持って選べます。
実際に触る名前。 PyTorch なら torch.optim.AdamW(params, lr=..., betas=(0.9, 0.999), eps=1e-8, weight_decay=...)。Hugging Face の TrainingArguments では同じものが learning_rate・adam_beta1・adam_beta2・adam_epsilon・weight_decay という名前で並びます。メモリが足りないときは bitsandbytes の 8ビット版(AdamW8bit)が候補になります。オプティマイザの状態は optimizer.state_dict() に入っていて、パラメータごとに step・exp_avg()・exp_avg_sq()が見えます。本文の記号がそのままキー名になっているので、デバッグ中に中身を覗いて「 がほぼ0の層」を探す、といった使い方ができます。
重み減衰をかけるパラメータを選ぶ。 実務では params を1つのリストで渡さず、パラメータグループに分けるのが定石です。バイアスや LayerNorm の重みのような1次元のパラメータは weight_decay=0.0 のグループへ、行列の重みだけに減衰をかける。これらは値の大きさ(スケール)や位置(オフセット)を決める係数で、0へ引っ張っても過学習を抑える効果は薄く、表現を縮めるだけになりやすい、というのが通常の理由づけです。
知らないと事故になる落とし穴。
- チェックポイントに重みしか保存していない。 再開時に
exp_avg・exp_avg_sq・stepがゼロから始まるので、「バイアス補正」の節で見た立ち上がりを丸ごともう一度通ります。モデルと一緒にoptimizer.state_dict()(とスケジューラの状態)を保存し、再開時に読み戻すこと。 - fp16 で を使う。 fp16 の最小の正の数(非正規化数)はおよそ なので、 は0に丸められます。オプティマイザの状態を fp16 で持つ実装では が消え、ほぼ0の勾配で割り算が発散しえます。混合精度でも状態は fp32 で持つのが一般的なのはこのためで、自前の実装や低精度オプティマイザを使うときは確認が要ります。
- ファインチューニングで既定の
lr=1e-3のまま回す。 「既定値とそれぞれの役目」で見たとおり、Adamが揃えてくれるのは相対的な歩幅だけです。事前学習済みの重みを大きく壊さないよう、 は自分で下げる必要があります。
面接・レビューで問われる問い。
- 「Adam と AdamW の違いは?」→ 減衰項が の割り算を通るかどうか。通すと勾配の大きいパラメータほど減衰が弱くなる、まで言えると筋が通ります。
- 「バイアス補正を外すと何が起きる?」→ 序盤の実効学習率が 倍に膨らむ。分子と分母で立ち上がりの時間尺度が違うので打ち消し合わない。
- 「学習の途中で損失が急に跳ねた。オプティマイザ側で何を疑う?」→ 再開時の状態の読み戻し漏れ、 が効かないほど小さい 、 が大きすぎて勾配の大きさの急変に分母が追いつかない、の3つが候補です。
まとめ
- Adam = モメンタム(勾配の向きを覚える)+ RMSProp(その大きさを覚える)、どちらも指数移動平均、プラスでバイアス補正
- が峡谷のジグザグを打ち消し、 が各パラメータに自分の実効学習率を与える
- 両方の平均は0から始まるので だけ縮んでいる。それを割り戻したものが 。省くと初手が3.16倍に膨らみ、ステップ12付近で6.57倍のピーク、消えるまで約5000ステップ
- 更新 は信号対雑音比——勾配が揃う座標では大胆に、矛盾する座標では慎重に、1歩の大きさはおよそ で抑えられる
- 既定値 、、 は約10ステップと約1000ステップの記憶に対応する。 は飾りではなく本物のハイパーパラメータ
- 重み減衰にはAdamWを使い、パラメータあたり追加2バッファを見積もり、AdamがチューニングしたSGDと同じだけ汎化すると決めつけないこと
1行の更新則がどこから来たのかはゼロから学ぶ勾配降下法へ。Adamが属するオプティマイザ一族の広がりはSGDの先へ、 の上に乗るスケジュールは学習率スケジュールへ。
確認問題
- で、勾配が毎ステップちょうど である列を考えます。、、 と、補正後の 、、 を計算してください。補正後の列は何を語っていますか。
- 2つのパラメータが一定の勾配を受けます。 が毎ステップ、 が毎ステップ。過渡が過ぎたあと、 のAdamでそれぞれ1ステップにどれだけ動きますか。同じ の素のSGDならどれだけ動きますか。
- 同僚が「バイアス補正を外しても変わらなかった。比のなかで打ち消し合うから」と報告しました。膨張倍率の式を正確に書き、既定のベータで と の値を求め、彼の主張がほぼ正しくなるには を何に変える必要があるか答えてください。
torch.optim.Adamにweight_decay=0.01を足しました。より強く減衰するのはどちらでしょう——勾配がごく小さい埋め込み層か、勾配が大きい出力ヘッドか。更新則から導き、そのうえで一語の解決策を挙げてください。
解答
- 、、。補正は で割ります: 、、。補正後の推定値はどのステップでも真の平均勾配にぴったり一致しますが、生の は2に近づくまでに数十ステップかかります。バイアス補正は、推定をステップ1から不偏にするための仕掛けです。
- 勾配が一定なら 、 なので比は1( は100に対しても0.01に対しても無視できます)。つまりどちらのパラメータも1ステップ だけ動きます。素のSGDなら と —— 倍の差で、片方が発散しているあいだにもう片方は凍りついています。パラメータごとの学習率が、この1つの比較に凝縮されています。
- 倍率は 。: 。: ——100ステップ経ってもまだ3倍以上大きい。彼の主張が成り立つには が必要で、 でそうなるのは のときです。この値なら倍率は でちょうど1.000、最大でも2.13(ピークは 付近)で、ほぼ無害な水準に収まります。 で打ち消しが起きないのは、2つの過渡の時間尺度がまったく違うからです——向きは約10ステップ、スケールは約1000ステップで落ち着く。
Adam(weight_decay=...)は適応的な一歩の前に を勾配へ足すので、減衰項も他と同じく の割り算を通ります。1ステップへの減衰の寄与はおよそ 。出力ヘッドは勾配が大きいので分母が大きく、減衰は抑えられます。埋め込みは勾配が小さいので分母が小さく、減衰は増幅されます。極端な場合、 が勾配を完全に支配すると となり、減衰は の値に関係なく1ステップあたり約 で飽和します。つまりより強く減衰するのは勾配の小さい埋め込み層のほうで、実効的な正則化の強さは自分で設定した ではなく各層の勾配スケールの副産物になっています。解決策は一語、AdamW。割り算の外で を引くので、すべてのパラメータが同じ率で減衰します。
コメント
コメントにはログインが必要です