一文要約: Residual Connection は各サブレイヤーの横に恒等経路を残し、情報と勾配が変換分岐だけを通らずに済むようにします。Dropout は訓練中に一部の活性化をランダムにゼロにする、任意の正則化手法です。
13.1 Transformer Block を改めて見る
Residual Connection と Dropout に踏み込む前に、Pre-Norm decoder block を最も短い形で書いてみましょう。
u = X + Dropout(Attention(LayerNorm(X)))
y = u + Dropout(FFN(LayerNorm(u)))
Attention と FFN にそれぞれ Residual の経路があるので、足し算は2回です。一方、2回の Dropout はよくある訓練レシピであって、Transformer の必須条件ではありません。Attention weight や入力埋め込みにも Dropout を使うモデルもあれば、すべての Dropout 率を 0 にするモデルもあります。GPT-2 型の Pre-Norm モデルでは、Block の積み重ね全体の後にも最終 Norm を置きますが、それは各 Block 内の2回の足し算とは別です。
13.2 Residual Connection: 情報のバイパスレーン
13.2.1 深いネットワークの問題
ネットワークが深くなるほど、最適化は難しくなります。Backpropagation では多数のレイヤーの Jacobian を連続して掛けるため、その積によって勾配が小さくなりすぎることも、大きくなりすぎることもあります。また、層を足したのに訓練誤差まで悪化する「degradation problem」も起こり得ます。Residual Connection は万能薬ではありませんが、積み重ねの中にずっと単純な経路を残します。
レイヤー1からレイヤー12まで情報が流れていく様子を想像してみてください。
Layer 1 → Layer 2 → Layer 3 → ... → Layer 12
すべてのレイヤーに変換分岐しかなければ、情報と勾配は変換を一つずつ通り抜けるしかありません。積み重ねが深くなるほど、この長い積を最適化するのが難しくなります。
13.2.2 解決策: バイパスレーン
Residual Connection の発想はシンプルです。入力にレイヤーをスキップさせて、出力にそのまま足し込む のです。
Input X ──────────────────────┐
↓ │ (バイパスレーン)
Sublayer │
↓ │
Output ←────────────────────┘ + X
数式で書くと、
output = sublayer(X) + X
sublayer(X) だけを出力するのではなく、元の入力をそこに足し戻します。
13.2.3 数値例
図の数値は、要素ごとの足し算を具体的に示すための説明用の値です。公開モデルの実行ログだと主張するものではありません。
Attention の出力 (Dropout 後):
[4, 16, 512] tensor
最初の値: -0.07005, 0.09600, 0.03522, ...
元の入力 X:
[4, 16, 512] tensor
最初の値: 0.50748, -1.96800, 5.14941, ...
Residual Connection 後:
output = Attention_output + X
= [-0.07005 + 0.50748, 0.09600 + (-1.96800), ...]
= [ 0.43743, -1.87200, ...]
要素ごとの足し算 (element-wise addition) です。何も特別なことはありません。
13.2.4 なぜ Residual Connection が効くのか
1. 勾配の流れ
とします。ベクトル表記なら、Backpropagation は次の形になります。
は恒等経路から来る直接項、 は変換分岐の Jacobian です。この直接項は通常、勾配を流しやすくします。ただし、勾配が絶対に消失も爆発もしないという保証ではありません。2つの項が一部打ち消し合うことも、Block をまたぐ積が効くこともあります。
2. 恒等写像へのフォールバック
あるレイヤーがまだ何を学習すべきか分かっていないとき、出力をほぼゼロにしておけばよい、という選択肢があります。
sublayer(X) ≈ 0
output = 0 + X = X
こうするとそのレイヤーは事実上、何もしないレイヤー (no-op) になります。情報はそのまま通過していきます。これは完全な恒等変換をゼロから学習するよりも、はるかに簡単に達成できます。
3. 情報の保存
各 Block は入力を更新量へ直接足すので、ネットワークは毎回ゼロから恒等写像を作り直さずに済みます。これは情報の直通経路を与えますが、最初の Token 情報が任意の深さで完全に復元できる、という意味ではありません。後続の更新は residual stream を変え続けます。
13.2.5 Transformer の中での Residual Connection の位置
1つ目の Residual Connection: Attention の後
X → LayerNorm → Attention → Dropout → (+X) → output1
2つ目の Residual Connection: FFN の後
output1 → LayerNorm → FFN → Dropout → (+output1) → output2
13.2.6 PyTorch 実装
from torch import nn
class TransformerBlock(nn.Module):
def __init__(self, d_model, num_heads, d_ff, dropout=0.1):
super().__init__()
self.attention = MultiHeadAttention(d_model, num_heads)
self.ffn = FeedForward(d_model, d_ff)
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
self.dropout1 = nn.Dropout(dropout)
self.dropout2 = nn.Dropout(dropout)
def forward(self, x, mask=None):
# 1つ目の Residual Connection
attn_output = self.attention(self.norm1(x), mask)
x = x + self.dropout1(attn_output) # ここが residual
# 2つ目の Residual Connection
ffn_output = self.ffn(self.norm2(x))
x = x + self.dropout2(ffn_output) # ここが residual
return x
ここでは前章までの MultiHeadAttention と FeedForward を再利用しています。肝になるのは x = x + ... の2行です。Dropout を2つに分けると、設定とデバッグの対応も明確になります。
13.3 Dropout: ランダムマスクによる正則化
13.3.1 過学習の問題
ニューラルネットワークは過学習することがあります。訓練データでの成績は上がり続けるのに、held-out data での成績が頭打ちになったり悪化したりする状態です。
これは、根本的な概念を理解せずに練習問題をひたすら覚える生徒のようなものだと思ってください。練習問題では100点を取るのに、見たことのない試験問題では落第してしまいます。
過学習はモデル版のこの現象です。
13.3.2 Dropout: ランダムな無効化
Dropout のアイデアは驚くほどシンプルです。訓練中に activation tensor の一部の要素をランダムにゼロにする のです。
各 forward pass のたびに新しいバイナリマスクをサンプリングします。重みを削除するわけでも、特定の unit を永久に止めるわけでもありません。次の pass では、通常は別の activation 位置がゼロになります。
通常: [0.5, 0.3, 0.8, 0.2, 0.6] ← 表示した activation はすべて保持
Dropout: [0.5, 0.3, 0.0, 0.2, 0.6] ← 0.8 がゼロにされた
どの activation 位置がドロップされるか? 毎回ランダムに別の位置が選ばれます。
13.3.3 直感的な説明
能の囃子方を思い浮かべてください。笛・小鼓・大鼓・太鼓のうち、いつも同じ一人だけが合図を担っていたら、稽古はその一人に依存します。稽古のたびに一つの音が時々休むなら、ほかの奏者も曲の構造をつかまなければなりません。
もちろん完全な対応ではありませんが、Dropout が与える圧力は似ています。固定した協調関係を揺さぶり、過度な co-adaptation を抑えます。ただし、各 unit が本当に独立することや、どんなモデルでも汎化が良くなることを保証するわけではありません。
13.3.4 数式
drop probability を 、keep probability を とします。PyTorch が使うのは inverted Dropout です。
dropout_rate = 0.1 の一回のサンプルを見てみます。表示した5要素のうち偶然1つが落ちていますが、小さなサンプルが毎回きっちり10%になるわけではありません。
input = [0.5, 0.3, 0.8, 0.2, 0.6]
mask = [1, 1, 0, 1, 1 ] # 0.8 がドロップされる
output = [0.5, 0.3, 0.0, 0.2, 0.6] / 0.9
= [0.56, 0.33, 0.00, 0.22, 0.67]
推論時:
output = input # Dropout なし、すべてそのまま通過
13.3.5 なぜリスケーリングするのか?
(1 - dropout_rate) で割っているのは、各入力要素の条件付き期待値を保つためです。
平均10%を落とすなら、生き残った値を 1/0.9 ≈ 1.11 倍します。推論時は入力をそのまま通します。保たれるのは要素ごとの期待値であり、毎回サンプルしたベクトルの norm まで同じになるわけではありません。
13.3.6 Transformer の中での Dropout の位置
本章の単純化した residual Dropout レシピでは、
- Attention の後:
Attention → Dropout → Residual の足し算 - FFN の後:
FFN → Dropout → Residual の足し算
サブレイヤーの更新量を揺らしてから residual stream に足します。オリジナル Transformer は embedding と位置表現の和にも Dropout を使い、多くの実装は attention weights にも別の Dropout を使います。一方、Dropout をまったく使わないモデルもあるため、これは訓練レシピであって普遍法則ではありません。
13.3.7 PyTorch 実装
import torch
import torch.nn as nn
x = torch.tensor([0.5, 0.3, 0.8, 0.2, 0.6])
dropout = nn.Dropout(p=0.1)
dropout.train()
train_output = dropout(x) # ランダムマスク。残った値は 0.9 で割る
dropout.eval()
eval_output = dropout(x) # x と同じ
Dropout が model の子 module として登録されていれば、model.train() / model.eval() が子まで再帰的に切り替えます。上のように単独の Dropout を試す場合は、その layer 自体を切り替えます。
13.4 Pre-Norm と Post-Norm
13.4.1 2つのレイアウト
重要なアーキテクチャ選択の一つが、Norm を Residual の足し算のどちら側に置くかです。
Post-Norm (オリジナル Transformer, 2017):
u = LayerNorm(X + Dropout(Attention(X)))
y = LayerNorm(u + Dropout(FFN(u)))
LayerNorm は Residual の足し算の 後 にきます。
Pre-Norm (GPT-2 が使った配置):
u = X + Dropout(Attention(LayerNorm(X)))
y = u + Dropout(FFN(LayerNorm(u)))
LayerNorm は各サブレイヤーの 前 にきます。GPT-2 は積み重ね全体の後にも最終 LayerNorm を置きます。LLaMA は Pre-Norm の位置関係を保ちつつ、LayerNorm の代わりに RMSNorm を使います。
13.4.2 なぜ Pre-Norm がよく使われるのか
Pre-Norm の恒等経路は Norm を通りません。理論と実践の両方で、初期化時の勾配が整いやすく、深い積み重ねを最適化しやすく、繊細な learning-rate warmup への依存を減らせることが示されています。
ただし、あらゆる意味で Pre-Norm が優れているわけではありません。Post-Norm も適切な初期化、warmup、構造変更があれば訓練できますし、Pre-Norm にも residual stream の scale が深さとともに増える問題があります。現代のモデルには RMSNorm、parallel branch、さらに別の Norm 配置もあります。正確には、Pre-Norm は decoder-only LLM のよくある出発点であって、唯一の正解ではないということです。
13.5 Residual Connection と Dropout の連携
13.5.1 Block を流れるデータを追う
Transformer Block を1つ通るデータの流れを完全に追ってみます。
Input X [4, 16, 512]
↓
LayerNorm(X) # 入力を安定化
↓
Attention(LayerNorm(X)) # 文脈を踏まえた更新を計算
↓
Dropout(Attention(...)) # 一部の更新をドロップ (訓練時のみ)
↓
X + Dropout(...) # residual: 元の信号 + 更新
↓
Output1 [4, 16, 512] # 形状は保たれる
2つ目のサブブロック (FFN) もまったく同じパターンで進みます。
13.5.2 なぜこの組み合わせがうまくいくのか
| 技術 | 解決する問題 | 仕組み |
|---|---|---|
| Residual Connection | 深い最適化の難しさ | 情報と勾配の恒等経路 |
| Dropout | 過学習のリスク | 任意のランダムマスク正則化 |
| Norm | 活性化 scale と訓練 dynamics | Token ごとの特徴 scale を制御 |
組み合わさることで、
- LayerNorm が計算前に入力を安定化させる
- Attention または FFN が特徴を学習する
- 設定されていれば、Dropout が更新量を正則化する
- Residual Connection がその更新量を residual stream に足す
Residual path と何らかの安定化方法は、この系列ではほぼ構造上の要件です。一方 Dropout は任意で、効くかどうかはモデル・データ・訓練目的によります。
13.6 実運用における Dropout 率
13.6.1 よくある設定
| 公開された訓練レシピ | Dropout 率 | 備考 |
|---|---|---|
| オリジナル Transformer base | 0.1 | residual と embedding+position |
| BERT | 0.1 | 論文の pretraining / fine-tuning 設定 |
| LLaMA | 0.0 | 参照実装の Block に Dropout なし |
この3行だけから「大きいモデルほど Dropout が低い」という法則は導けません。Rate は訓練レシピの一部であり、dataset の規模と重複、訓練時間、task、ほかの正則化、最適化 dynamics に左右されます。大規模な autoregressive pretraining で 0 を選ぶ例はありますが、fine-tuning や小さな dataset では改めて調整が必要です。
13.6.2 Residual の派生
オリジナルの Transformer はシンプルな足し算を使います。研究の世界ではいくつかのバリエーションも試されてきました。
scale 付き residual branch (概念図):
x = x + alpha * sublayer(x)
ゲート付き residual:
gate = torch.sigmoid(linear(x))
x = x + gate * sublayer(x) # サブレイヤー出力をどれだけ信用するかを学習する
方式によって alpha を固定・初期化・学習し、gate の式も異なります。オリジナル Transformer は単純な足し算ですが、後続アーキテクチャが必ず同じとは限りません。上の断片は設計空間を示すためのものです。
13.7 章のまとめ
13.7.1 重要な概念
| 概念 | 目的 | 数式 / 効果 |
|---|---|---|
| Residual Connection | 深い最適化を改善し、恒等経路を作る | output = x + F(x) |
| Dropout | 任意のランダム正則化 | 訓練中にランダムにゼロ化し、keep rate で scale |
| Pre-Norm | よく使われる最適化しやすい配置 | サブレイヤー前に Norm、通常は積み重ね後にも最終 Norm |
13.7.2 Block レイアウト
Input X
↓
LayerNorm → Attention → Dropout → (+X) → output1
↑
ここが residual
output1
↓
LayerNorm → FFN → Dropout → (+output1) → output2
↑
ここが residual
13.7.3 核となる教訓
Residual block は表現全体を書き直すのではなく、現在の residual stream に何を足すかを学びます。恒等経路は通常、深い最適化を楽にします。Dropout の役割は別で、訓練レシピが必要とするときにランダムな activation mask で正則化します。Residual はこの構造の根幹ですが、Dropout は任意です。
章末チェックリスト
この章を読み終えたら、次のことができるようになっているはずです。
- Residual Connection が通常なぜ勾配伝播を改善し、それでも絶対保証ではないかを説明できる
- Residual Connection が可能にする「恒等写像へのフォールバック」を説明できる
- inverted Dropout の mask、scale、訓練/推論の違いを説明できる
- Transformer Block の中で Residual Connection と Dropout が置かれている位置を述べられる
- Pre-Norm と Post-Norm を区別し、現代の LLM で Pre-Norm が好まれる理由を説明できる
次章でお会いしましょう
各 Residual の足し算が使うのはそのサブレイヤーの現在の入力であり、最初の Token Embedding を永遠に足すわけではありません。ただし最初の Block に入る前、residual stream の出発点は Token Embedding と Positional Encoding の組み合わせです。
第14章では、一見当たり前に見えて、実は奥の深い問いを取り上げます。なぜ私たちはこの2つの信号を 連結 (concatenate) するのではなく、足し合わせる (add) のでしょうか? 次章でお会いしましょう。