モデル学習ノート · 第3章
残差接続:変化を学び、直接の経路を残す
x + F(x) と勾配の近道を確認し、深いネットワークで必要な条件を考えます。
Muxuan Liu · 自分のモデル学習ノートを整理し、式と実装例を確認した版です。
置き換える代わりに更新を学ぶ
現在の表現に有用な情報があるなら、それを残して更新だけ加えられます。y = x + F(x) という形です。原稿を毎回白紙から書くのではなく、元を残しながら修正するイメージです。
残差接続は直接加算する経路、残差ブロックはその経路と学習する分岐を含む単位です。分岐には畳み込みや注意・前向きネットワークなどを使えます。ResNet は残差ブロックを用いる具体的な系列で、接続の考え方自体は他の構造にも使われます。
近道が最適化を助ける理由
恒等写像が有用なら、分岐をゼロに近づけて入力を保てます。既存の表現の周囲で変化を学ぶ経路が得られるため、深いモデルの最適化がしやすくなります。残差学習が扱うのは、層を増やしたときの劣化を含む最適化の難しさであり、勾配消失だけではありません。
「残差」は、常に分岐の出力が小さいことや、常に入力をコピーしたいことを意味しません。必要な更新は課題と構造によって変わります。
展開の前に漸化式を書く
各ブロックを xₗ₊₁ = xₗ + Fₗ(xₗ) と書きます。L 個の後は xL = x0 + Σ Fₗ(xₗ) で、各分岐はその時点の中間状態を入力に取ります。長い入れ子を直接書くよりも誤りを見つけやすくなります。
一つのブロックでは、列ベクトルの勾配を使うと、入力勾配は出力勾配に (I + JF(x))ᵀ を掛けたものです。I は単位行列、JF は分岐のヤコビ行列です。直接の項は経路を増やしますが、全体の勾配が最低 1 になる保証ではありません。
小さな例で確かめる
スカラーの分岐を F(x)=a·x として L 個重ねると、毎回 1+a 倍になるため、全体の導関数は (1+a)^L です。この構造の導関数を 1+a+a²+…+a^L とするのは誤りです。
a=−0.5、L=3 なら 0.125、a=1 なら 8 です。残差接続があっても、勾配の減衰や増大は起こり得ます。初期化、正規化、分岐のスケール、学習された関数が重要になります。
活性値の分散が増える場合
スカラー確率変数では Var(x+F) = Var(x)+Var(F)+2Cov(x,F) です。学習した分岐は入力に依存するため、共分散を無条件に省けません。独立なノイズを毎層加える例は直感を得る助けですが、実際の Transformer をすべて表すわけではありません。
平均ゼロ・分散 1 の独立な更新を L 回足すと、分散は L、標準偏差は √L になります。各更新を 1/√L 倍すれば、更新の和の分散は 1 です。残差スケーリングの動機の一例であり、あらゆる構造に共通する処方ではありません。
実装で確認したいこと
加算する両側は互換のある形状である必要があります。幅が変わるなら、近道側にも投影が必要な場合があります。Transformer では正規化の位置で実際の漸化式も変わるため、単純な y=x+F(x) と完全なブロックを区別します。
下の例は自動微分でスカラーの導関数を確認します。前向きの値と後ろ向きの勾配を両方追うのが、私には実用的な読み方です。近道は深いモデルの経路を改善しますが、安定性は各部の組み合わせにも依存します。
PyTorch の例を開く
import torch
# Minimal residual-gradient example: three blocks, F(x) = a*x.
def residual_gradient(a, depth=3):
x = torch.tensor(1.0, requires_grad=True)
y = x
for _ in range(depth):
y = y + a * y
y.backward()
return x.grad.item() # (1+a)**depth
for a in [-0.5, 0.1, 1.0]:
print(a, residual_gradient(a), (1 + a) ** 3)