一文要約: Residual Connection は情報がバイパスレーンを通って後段のレイヤーへ直接届くようにし、深いネットワークの勾配消失を解決します。Dropout は訓練中にランダムに活性化をドロップして過学習を防ぎます。この2つの技術こそが、Transformer を安定して訓練するための秘密です。
13.1 Transformer Block を改めて見る
Residual Connection と Dropout に踏み込む前に、Transformer Block の全体構造をもう一度確認しましょう。
Input X
↓
Layer Norm
↓
Masked Multi-Head Attention
↓
Dropout
↓
Residual connection (+ X) ← 1つ目の residual
↓
Layer Norm
↓
Feed Forward Network (FFN)
↓
Dropout
↓
Residual connection (+ previous output) ← 2つ目の residual
↓
Output
各 Block には 2つの Residual Connection と 2つの Dropout レイヤー があります。本章では、それらが何をしていて、なぜ存在するのかを説明します。
13.2 Residual Connection: 情報のバイパスレーン
13.2.1 深いネットワークの問題
ニューラルネットワークが深くなるにつれて、深刻な問題が現れます。それが 勾配消失 (vanishing gradient) です。
レイヤー1からレイヤー12まで情報が流れていく様子を想像してみてください。
Layer 1 → Layer 2 → Layer 3 → ... → Layer 12
各レイヤーは信号を加工していきます。12層を経た後には、
- 元の信号は大きく歪んでしまっている可能性がある
- バックプロパゲーションでは各レイヤーで勾配が縮んでいく
- 入力に近いレイヤーはほぼゼロの勾配しか受け取れず、何も学習できない
これは Residual Connection が登場する以前の深層学習でよく知られた問題でした。
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. 勾配の流れ
バックプロパゲーション時、Residual Connection を通る勾配は次のように書けます。
たとえ が0に近くなっても (勾配消失問題)、+1 の項のおかげで勾配は流れ続けます。バイパスレーンが勾配をそのまま運んでくれるのです。
2. 恒等写像へのフォールバック
あるレイヤーがまだ何を学習すべきか分かっていないとき、出力をほぼゼロにしておけばよい、という選択肢があります。
sublayer(X) ≈ 0
output = 0 + X = X
こうするとそのレイヤーは事実上、何もしないレイヤー (no-op) になります。情報はそのまま通過していきます。これは完全な恒等変換をゼロから学習するよりも、はるかに簡単に達成できます。
3. 情報の保存
元の情報は常に残ります。どれだけレイヤーが積まれていても、入力信号が完全に上書きされることはありません。常に足し戻されているからです。
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 実装
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.dropout = nn.Dropout(dropout)
def forward(self, x, mask=None):
# 1つ目の Residual Connection
attn_output = self.attention(self.norm1(x), mask)
x = x + self.dropout(attn_output) # ここが residual
# 2つ目の Residual Connection
ffn_output = self.ffn(self.norm2(x))
x = x + self.dropout(ffn_output) # ここが residual
return x
肝になるのは x = x + ... の2行です。残りはすべてサブレイヤーの計算です。
13.3 Dropout: ランダムに削ることで過学習を防ぐ
13.3.1 過学習の問題
ニューラルネットワークには過学習しやすいという性質があります。汎化可能なパターンを学ぶ代わりに、訓練データそのものを丸暗記してしまうのです。
これは、根本的な概念を理解せずに練習問題をひたすら覚える生徒のようなものだと思ってください。練習問題では100点を取るのに、見たことのない試験問題では落第してしまいます。
過学習はモデル版のこの現象です。
13.3.2 Dropout: ランダムな無効化
Dropout のアイデアは驚くほどシンプルです。訓練中に一部のニューロンをランダムに無効にする のです。
各 forward pass のたびに、Dropout はランダムなバイナリマスクを生成します。一部の活性化はゼロになり、残りはそのまま通過します。
通常: [0.5, 0.3, 0.8, 0.2, 0.6] ← すべてのニューロンが有効
Dropout: [0.5, 0.3, 0.0, 0.2, 0.6] ← 0.8 がゼロにされた
どのニューロンがドロップされるか? 毎回ランダムに違うニューロンが選ばれます。
13.3.3 直感的な説明
ソフトウェアチームを思い浮かべてください。
Dropout なし: ある優秀なエンジニアが何でも引き受けてしまい、その人がチームを去ると組織が崩壊する。
Dropout あり: 毎日、メンバーの一部がランダムに「お休み」する。誰もが複数の仕事をカバーできるようにならざるを得ない。誰か1人が単一障害点 (single point of failure) にならないので、チームは強くなる。
ニューラルネットワークにおいては、
- Dropout は各ニューロンに、特定の相棒に頼らずとも機能することを強制する
- 各ニューロンは独立して有用であるよう学習する
- ネットワークはより頑健になり、より分散した表現を学ぶようになる
13.3.4 数式
訓練時:
mask = ランダムなバイナリテンソル、確率 (1 - dropout_rate) で 1
output = input * mask / (1 - dropout_rate)
dropout_rate = 0.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%のニューロンがドロップされるなら、残った90%は 1/0.9 ≈ 1.11 倍にスケールされます。推論時にはすべて (100%) のニューロンが有効ですから、スケーリングは不要です。これによって、両モードで出力の期待値の大きさが一致します。
このリスケーリングがないと、訓練時の出力は推論時よりも一貫して小さくなり、分布のズレが生じてモデルの品質が落ちてしまいます。
13.3.6 Transformer の中での Dropout の位置
- Attention の後:
Attention → Dropout → Residual Connection - FFN の後:
FFN → Dropout → Residual Connection
Dropout は常に Residual の足し算より前 に置かれます。
13.3.7 PyTorch 実装
import torch.nn as nn
# Dropout レイヤーを作る
dropout = nn.Dropout(p=0.1) # 10% の活性化をドロップ
# 訓練モード (model.train() で Dropout が有効になる)
output = dropout(input) # ランダムに活性化をドロップ
# 推論モード (model.eval() で Dropout が無効になる)
output = dropout(input) # 何も変えずに通過させる
PyTorch は訓練/推論モードの切り替えを自動でやってくれます。訓練前には model.train() を、推論前には model.eval() を呼びましょう。
13.4 Pre-Norm と Post-Norm
13.4.1 2つのレイアウト
ややマニアックなアーキテクチャ選択として、LayerNorm を Residual Connection に対してどこに置くか という問題があります。
Post-Norm (オリジナル Transformer, 2017):
X → Attention → Add(+X) → LayerNorm → FFN → Add → LayerNorm → output
LayerNorm は Residual の足し算の 後 にきます。
Pre-Norm (GPT-2 以降):
X → LayerNorm → Attention → Add(+X) → LayerNorm → FFN → Add → output
LayerNorm は各サブレイヤーの 前 (Attention の前、FFN の前) にきます。
13.4.2 なぜ Pre-Norm が現代の標準なのか
研究と実装の両面で、現代の LLM は Pre-Norm に収束しています。
- より安定した勾配: 各サブレイヤーの前で入力を正規化することで、病的に大きな活性化が積み重なるのを防ぐ
- クリーンな Residual パス: Residual の足し算は正規化を通らないので、バイパスレーンには元の信号がそのまま流れる
- 深い積み重ねでの収束性が良い: 特に20以上の Block を持つモデルで重要
GPT-2、GPT-3、LLaMA、そして主要な decoder-only LLM はすべて Pre-Norm を採用しています。
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 | 過学習 | ランダムな無効化により頑健性を強制 |
| LayerNorm | 数値的不安定性 | 活性化を安定した範囲に正規化 |
組み合わさることで、
- LayerNorm が計算前に入力を安定化させる
- Attention または FFN が特徴を学習する
- Dropout が正則化を加える
- Residual Connection が元の信号を確実に残す
このどれか1つでも取り除くと、訓練は目に見えて難しくなります。深い積み重ねでは、完全に破綻することすらあります。
13.6 実運用における Dropout 率
13.6.1 よくある設定
| モデル | Dropout 率 | 備考 |
|---|---|---|
| GPT-2 | 0.1 | 標準設定 |
| GPT-3 | 0.0 – 0.1 | 実験により変動 |
| BERT | 0.1 | 標準設定 |
| LLaMA | 0.0 | Dropout を使わない |
トレンドははっきりしています。大きなモデルほど Dropout は少なくなり、まったく使わないこともある のです。
なぜでしょうか? 巨大なパラメータ数を持つ大きなモデルを膨大なデータで訓練する場合、過学習しにくくなります。データ自体の多様性が正則化として働くためです。加えて、大規模な訓練は非常に高コストなので、強い Dropout で収束が悪化するリスクは現場では避けたいのです。
13.6.2 Residual の派生
オリジナルの Transformer はシンプルな足し算を使います。研究の世界ではいくつかのバリエーションも試されてきました。
スケーリング付き residual:
x = x + 0.1 * sublayer(x) # residual の寄与を縮小する
ゲート付き residual:
gate = torch.sigmoid(linear(x))
x = x + gate * sublayer(x) # サブレイヤー出力をどれだけ信用するかを学習する
これらは特定の状況では安定性を改善できますが、標準的な Transformer はシンプルな足し算を採用したままです。スケールが大きくなるほど、シンプルな手法のほうが汎化しやすい傾向があります。
13.7 章のまとめ
13.7.1 重要な概念
| 概念 | 目的 | 数式 / 効果 |
|---|---|---|
| Residual Connection | 勾配消失を防ぎ、情報を保存する | output = sublayer(x) + x |
| Dropout | 過学習を防ぎ、頑健性を強制する | 訓練中にランダムに活性化をゼロにする |
| Pre-Norm | 深い積み重ねでも安定して訓練 | 各サブレイヤーの前に LayerNorm |
13.7.2 Block レイアウト
Input X
↓
LayerNorm → Attention → Dropout → (+X) → output1
↑
ここが residual
output1
↓
LayerNorm → FFN → Dropout → (+output1) → output2
↑
ここが residual
13.7.3 核となる教訓
Residual Connection と Dropout は、深い Transformer の訓練を実用に耐えるものにしている工学的な足場 (scaffolding) です。Residual Connection は勾配にバイパスルートを与え、入力に近いレイヤーまで学習を届けます。Dropout はモデルが訓練データを丸暗記してしまうのを防ぎます。どちらも華やかではありませんが、片方を外すだけで訓練品質ははっきりと低下します。Residual、Dropout、LayerNorm の3つが噛み合って、深い積み重ねの安定性を支えているのです。
章末チェックリスト
この章を読み終えたら、次のことができるようになっているはずです。
- Residual Connection がなぜ勾配消失を防ぐのかを説明できる
- Residual Connection が可能にする「恒等写像へのフォールバック」を説明できる
- Dropout がどのようにして過学習を防ぐのかを説明できる
- Transformer Block の中で Residual Connection と Dropout が置かれている位置を述べられる
- Pre-Norm と Post-Norm を区別し、現代の LLM で Pre-Norm が好まれる理由を説明できる
次章でお会いしましょう
Residual Connection は Attention の出力を 元の入力 に足し戻します。しかし、その「元の入力」自体が、Token Embedding と Positional Encoding という2種類の信号を組み合わせたものでした。
第14章では、一見当たり前に見えて、実は奥の深い問いを取り上げます。なぜ私たちはこの2つの信号を 連結 (concatenate) するのではなく、足し合わせる (add) のでしょうか? 次章でお会いしましょう。