第12章 レイヤー正規化:値のばらつきを揃えて学習を安定させる

こんにちは。ゆうせいです。

第11章では、残差接続が深いモデルの学習を可能にすることを確認しました。しかし、残差接続だけでは十分ではありません。もう一つ、学習を安定させる柱があります。レイヤー正規化(Layer Normalization)です。Transformer Explainerの図で、注意機構やMLP層の直前に配置されている、LNまたはLayerNormと書かれた小さなブロックが、これにあたります。第7章のスケーリングも、値の大きさを調整する処理でした。両者は何が違うのでしょうか。また、画像認識でよく使われるバッチ正規化ではなく、なぜレイヤー正規化が選ばれているのでしょうか。第12章では、この二つの疑問に答えます。

レイヤー正規化とは何か

レイヤー正規化とは、あるベクトルの中の値を、平均0、分散1になるように変換する処理です。

処理の対象は、1つのトークンに対応する768個の数値です。この768個の値について、平均と標準偏差を計算し、それを使って標準化します。

\hat{x}_i = \frac{x_i - \mu}{\sqrt{\sigma^2 + \epsilon}}

\mu は、768個の値の平均です。

\sigma^2 は、768個の値の分散です。

\epsilon は、非常に小さな定数です。分散が0に近いときに、ゼロ除算を避けるために加えられます。実装では、1e-5などの値が使われます。

具体的な数値で確認する

分かりやすくするため、4次元のベクトルで計算してみます。

x = [4, 6, 2, 8]

平均を計算します。

\mu = \frac{4 + 6 + 2 + 8}{4} = \frac{20}{4} = 5

各要素から平均を引きます。

[4-5, 6-5, 2-5, 8-5] = [-1, 1, -3, 3]

分散を計算します。

\sigma^2 = \frac{(-1)^2 + 1^2 + (-3)^2 + 3^2}{4} = \frac{1 + 1 + 9 + 9}{4} = \frac{20}{4} = 5

標準偏差は、次のようになります。

\sigma = \sqrt{5} \approx 2.236

各要素を標準偏差で割ります。

\left[\frac{-1}{2.236}, \frac{1}{2.236}, \frac{-3}{2.236}, \frac{3}{2.236}\right] \approx [-0.447, 0.447, -1.342, 1.342]

この結果は、平均が0、分散が1になっています。

スケールとシフトのパラメータ

上の処理だけでは、すべてのベクトルが平均0、分散1に強制されてしまいます。これは制約が強すぎる場合があります。

そこで、正規化の後に、学習可能なパラメータによる調整が加えられます。

y_i = \gamma_i \hat{x}_i + \beta_i

\gamma(ガンマ)は、スケールを調整するパラメータです。

\beta(ベータ)は、シフトを調整するパラメータです。

これらは、次元ごとに1つずつ用意されます。768次元であれば、\gamma が768個、\beta が768個です。

なぜこのパラメータが必要なのか

正規化によって、値のばらつきは揃います。しかし、モデルにとって、平均0、分散1が常に最適とは限りません。

ある次元では、より大きなばらつきを持たせたほうがよいかもしれません。別の次元では、平均を0からずらしたほうがよいかもしれません。

\gamma\beta を学習可能にすることで、モデルが自分で最適なスケールと位置を決められるようになります。

極端な場合、\gamma が標準偏差と同じ値、\beta が平均と同じ値に学習されれば、正規化の効果を打ち消すこともできます。つまり、「正規化しない」という選択肢も、モデルは取れるということです。

パラメータ数を計算する

1つのレイヤー正規化あたりのパラメータ数は、次の通りです。

768 \times 2 = 1536

第11章で確認したとおり、1つのブロックには正規化が二か所あります。したがって、1ブロックあたり3072個です。

ブロックは12個あり、さらに最終出力の前にも1つ正規化があるため、モデル全体では、おおよそ次のようになります。

3072 \times 12 + 1536 = 38400

約3万8千個です。約1億2400万個という全体のパラメータ数から見れば、ごくわずかな割合です。パラメータ数のわりに、学習の安定性への寄与が大きい仕組みだと言えます。

なぜ正規化が必要なのか

理由1:内部の値のばらつきを抑える

深いネットワークでは、層を通るたびに、値の分布が変化していきます。

ある層の出力の値が極端に大きくなると、次の層への入力も大きくなり、さらに次の層ではもっと大きくなる、という連鎖が起こり得ます。

逆に、値が小さくなり続ける場合もあります。

このような分布の変動が起きると、学習が不安定になります。学習率をどう設定しても、うまく収束しなくなるのです。

各層の入り口で正規化を行うことで、値の分布が常に一定の範囲に保たれます。これにより、層の深さにかかわらず、安定した学習が可能になります。

理由2:勾配の流れを安定させる

値の分布が安定すると、勾配の大きさも安定します。

第7章で扱ったSoftmaxの飽和も、入力の値が大きすぎることが原因でした。正規化によって値の範囲が抑えられていれば、そうした問題が起きにくくなります。

理由3:学習率を大きく設定できる

正規化があると、より大きな学習率を使っても学習が発散しにくくなります。

学習率を大きくできれば、学習が速く進みます。大規模なモデルの学習において、この効果は実用上、非常に重要です。

バッチ正規化との違い

正規化には、複数の種類があります。Transformer以前、画像認識の分野で広く使われていたのは、バッチ正規化(Batch Normalization)でした。

両者の違いは、「何の平均と分散を計算するか」という一点にあります。

計算の対象が異なる

レイヤー正規化は、1つのサンプルの中の、すべての特徴量について平均と分散を計算します。

バッチ正規化は、1つの特徴量について、バッチ内のすべてのサンプルにわたって平均と分散を計算します。

具体例で確認します。10個の文章をまとめて処理し、各トークンが768次元で表現されているとします。

レイヤー正規化では、あるトークンの768個の値について、平均と分散を計算します。他の文章の値は、一切参照しません。

バッチ正規化では、たとえば「1次元目」という特定の次元について、10個の文章すべての値を集めて、平均と分散を計算します。

なぜTransformerではレイヤー正規化なのか

自然言語処理でバッチ正規化が使いにくい理由は、三つあります。

一つ目の理由は、系列の長さが可変であることです。

画像であれば、すべてのサンプルが同じ大きさに揃えられます。しかし、文章の長さはまちまちです。短い文章にはパディング(埋め草)が入りますが、この意味のない値が統計量に混ざると、正規化が正しく機能しません。

二つ目の理由は、バッチサイズへの依存です。

バッチ正規化は、バッチ内のサンプルから統計量を計算するため、バッチサイズが小さいと、統計量が不安定になります。大規模なモデルでは、メモリの制約からバッチサイズを小さくせざるを得ない場合があり、この点が問題になります。

三つ目の理由は、推論時の扱いです。

バッチ正規化では、学習時にバッチ全体の統計量を使いますが、推論時には1件ずつ処理することがあります。この場合、学習中に蓄積した移動平均を使うという、追加の仕組みが必要になります。学習時と推論時で挙動が変わることは、実装上の複雑さと、性能の不一致を招きます。

レイヤー正規化には、これらの問題がありません。1つのトークンの中だけで完結するため、系列の長さにもバッチサイズにも依存せず、学習時と推論時で挙動が同じです。

比較表

項目レイヤー正規化バッチ正規化
統計量の計算対象1サンプル内の全特徴量バッチ内の同一特徴量
バッチサイズへの依存なしあり
系列長の変動への対応問題なしパディングの影響を受ける
学習時と推論時の挙動同じ異なる(移動平均が必要)
主な使用分野自然言語処理、Transformer画像認識、CNN

Pre-LNとPost-LNの違い

第11章で触れた、正規化を置く位置の違いを、ここで詳しく確認します。

Post-LN方式

最初のTransformer論文で採用された方式です。

x' = LayerNorm(x + Attention(x))

残差接続で足し合わせた後に、正規化を適用します。

この方式の問題は、残差接続の経路に正規化が挟まってしまう点です。

第11章で説明した「勾配が通る高速道路」に、正規化という処理が入り込むため、勾配がそのまま伝わりません。結果として、学習の初期段階で不安定になりやすいことが知られています。

Post-LN方式で深いモデルを学習させるには、学習率を最初は小さくして徐々に上げる、ウォームアップという工夫が必要になります。

Pre-LN方式

GPT-2をはじめ、現在の多くのモデルが採用している方式です。

x' = x + Attention(LayerNorm(x))

処理に入る前に正規化を適用し、残差接続はその外側で行われます。

この方式では、残差接続の経路に正規化が挟まりません。入力から出力まで、足し算だけの経路が保たれます。

結果として、勾配が減衰せずに伝わり、学習が安定します。ウォームアップなしでも学習できることが多く、より深いモデルにも対応しやすくなります。

最終層の正規化

Pre-LN方式では、各ブロックの入り口で正規化が行われますが、最後のブロックの出力には正規化が適用されません。

そのため、すべてのブロックを通過した後、出力層に入る前に、もう一つ正規化が置かれます。GPT-2の実装では、ln_f という名前で存在します。

第14章で出力層を扱う際に、この正規化を経た値が使われることになります。

Transformer Explainerで観察できること

Transformer Explainerでは、正規化の前後の値を比較できる場合があります。

次の点を確認してみてください。

一つ目は、正規化の後に、値の範囲が明確に狭まっているという点です。極端に大きな値や小さな値が、中央付近に集められます。

二つ目は、正規化がトークンごとに独立して行われているという点です。あるトークンの正規化に、他のトークンの値は影響しません。

三つ目は、正規化の位置が、注意機構やMLP層の直前であるという点です。Pre-LN方式が採用されていることが、図の上で確認できます。

初学者がつまずきやすい点

つまずき1:スケーリングと正規化を混同する

第7章のスケーリングと、この章の正規化は、どちらも値の大きさを調整する処理ですが、まったく別のものです。

スケーリングは、\sqrt{d_k} という固定の定数で割る処理です。統計量は計算せず、学習されるパラメータもありません。適用箇所は、注意スコアに対してのみです。

正規化は、実際にデータから平均と分散を計算し、それに基づいて変換します。学習されるパラメータ(\gamma\beta)を持ちます。適用箇所は、各層の入り口です。

両者は、目的も仕組みも異なります。

つまずき2:正規化の方向を間違える

レイヤー正規化は、特徴量の方向(768次元の方向)に対して計算します。

トークンの方向やバッチの方向に計算してしまうと、まったく別の処理になります。

PyTorchでは、nn.LayerNorm(768) のように、正規化する次元のサイズを指定します。この指定が、最後の次元に対して正規化することを意味します。

つまずき3:epsilon の役割を軽視する

\epsilon は、分散が0に近い場合のゼロ除算を防ぐための値です。

768個の値がすべて同じであれば、分散は0になります。この場合、\epsilon がなければ、計算が破綻します。

実際にそのような状況が起きることは稀ですが、学習の初期段階や、特殊な入力に対しては起こり得ます。実装時には、必ず含めてください。

演習

演習1:レイヤー正規化を手計算する

次のベクトルに対して、レイヤー正規化を計算してください。\epsilon は無視して構いません。

x = [1, 3, 5, 7]

平均、分散、標準偏差、正規化後の値の順に求めてください。

演習2:正規化の効果を確認する

import torch
import torch.nn as nn

torch.manual_seed(0)

# 値の範囲がばらついたベクトルを用意
x = torch.tensor([[10.0, 50.0, -30.0, 5.0, 100.0, -80.0]])

print("=== 正規化前 ===")
print(f"値: {x.numpy()}")
print(f"平均: {x.mean().item():.4f}")
print(f"標準偏差: {x.std(unbiased=False).item():.4f}")
print(f"最大値: {x.max().item():.4f}, 最小値: {x.min().item():.4f}")

# レイヤー正規化を適用
ln = nn.LayerNorm(6, elementwise_affine=False)
y = ln(x)

print("\n=== 正規化後 ===")
print(f"値: {y.detach().numpy().round(4)}")
print(f"平均: {y.mean().item():.6f}")
print(f"標準偏差: {y.std(unbiased=False).item():.4f}")
print(f"最大値: {y.max().item():.4f}, 最小値: {y.min().item():.4f}")

# 手計算で検証
mean = x.mean()
var = x.var(unbiased=False)
manual = (x - mean) / torch.sqrt(var + 1e-5)
print(f"\n手計算との一致: {torch.allclose(y, manual, atol=1e-4)}")

演習3:トークンごとに独立していることを確認する

import torch
import torch.nn as nn

torch.manual_seed(0)

ln = nn.LayerNorm(4, elementwise_affine=False)

# 3トークン、各4次元
x = torch.tensor([
    [1.0, 2.0, 3.0, 4.0],
    [100.0, 200.0, 300.0, 400.0],
    [-5.0, 0.0, 5.0, 10.0]
])

y = ln(x)

print("=== 入力 ===")
print(x.numpy())
print("\n=== 正規化後 ===")
print(y.detach().numpy().round(4))

print("\n=== 各行の統計量 ===")
for i in range(3):
    print(f"行{i}: 平均 {y[i].mean().item():.6f}, 標準偏差 {y[i].std(unbiased=False).item():.4f}")

# 1行目だけを単独で正規化しても同じ結果になるか確認
x_single = x[0:1]
y_single = ln(x_single)
print(f"\n1行目を単独で処理した結果と一致するか: {torch.allclose(y[0], y_single[0], atol=1e-5)}")

演習の解答例と解説

演習1の解答は、次の通りです。

平均を計算します。

\mu = \frac{1 + 3 + 5 + 7}{4} = \frac{16}{4} = 4

各要素から平均を引きます。

[-3, -1, 1, 3]

分散を計算します。

\sigma^2 = \frac{9 + 1 + 1 + 9}{4} = \frac{20}{4} = 5

標準偏差は、次の通りです。

\sigma = \sqrt{5} \approx 2.236

正規化後の値は、次のようになります。

\left[\frac{-3}{2.236}, \frac{-1}{2.236}, \frac{1}{2.236}, \frac{3}{2.236}\right] \approx [-1.342, -0.447, 0.447, 1.342]

もとの値の間隔は2ずつでしたが、正規化後も等間隔が保たれています。正規化は、値の相対的な関係を維持したまま、スケールと位置だけを変える変換です。

演習2では、マイナス80から100まで広がっていた値が、おおよそマイナス1.5からプラス1.5程度の範囲に収まることが確認できます。

演習3では、3つの行がそれぞれ独立に正規化されることが確認できます。2行目は値が100倍の大きさですが、正規化後は1行目と同じ結果になるはずです。これは、正規化が相対的な関係だけを保存し、絶対的な大きさの情報を捨てていることを示しています。

また、1行目だけを単独で処理しても同じ結果になることから、トークンどうしが影響し合わないことが確認できます。この性質が、バッチサイズや系列長に依存しない理由です。

講師向けの補足

この章では、バッチ正規化との違いを説明する際に、図を使うことを強く推奨します。

行がサンプル、列が特徴量である表を描き、レイヤー正規化では横方向に、バッチ正規化では縦方向に統計量を計算する、という図が最も分かりやすいものです。

言葉だけの説明では、どちらがどちらか分からなくなる受講者が多く出ます。

また、演習3で、値の大きさが100倍違う行が同じ結果になる点は、受講者に驚きを与えます。「正規化は絶対的な大きさの情報を捨てている」という点を、この例で強調してください。この理解は、\gamma\beta が必要な理由の説明にもつながります。

理解度を確認する問い

第12章の内容を、次の問いで確認してみてください。

レイヤー正規化とバッチ正規化の違いを、統計量を計算する対象という観点から説明してみてください。

自然言語処理でバッチ正規化が使いにくい理由を、三つ挙げてください。

\gamma\beta という学習可能なパラメータが必要な理由は何でしょうか。

Pre-LN方式がPost-LN方式より学習が安定する理由を、第11章の残差接続の内容と関連づけて説明してみてください。

まとめ

レイヤー正規化とは、1つのトークンに対応する768個の値について平均と分散を計算し、平均0、分散1になるように変換する処理です。

正規化の後には、学習可能なスケールパラメータ \gamma とシフトパラメータ \beta による調整が加えられます。これにより、モデルが最適なスケールと位置を自分で決められます。

正規化が必要な理由は、層を通るたびに変動する値の分布を一定範囲に保ち、勾配の流れを安定させ、より大きな学習率を使えるようにすることです。

バッチ正規化との違いは、統計量を計算する対象にあります。レイヤー正規化は1サンプル内の全特徴量、バッチ正規化はバッチ内の同一特徴量を対象とします。自然言語処理では、系列長が可変であること、バッチサイズへの依存があること、学習時と推論時で挙動が変わることの三点から、バッチ正規化が使いにくく、レイヤー正規化が選ばれています。

GPT-2はPre-LN方式を採用しており、処理の前に正規化を行い、残差接続はその外側で行われます。この配置により、残差接続による勾配の経路が正規化に妨げられず、学習が安定します。

次の第13章では、MLP層を扱います。自己注意がトークンどうしの関係を扱うのに対し、MLP層は各トークンの内部で情報を加工します。なぜ次元をいったん4倍に拡大するのか、活性化関数にGELUが使われる理由は何かを解説します。

第13章に進む前に、演習3のプログラムを動かし、値の大きさが違う行が正規化後に同じ結果になることを確認しておいてください。正規化が何を保存し、何を捨てているのかを理解しておくと、次章以降の説明が読みやすくなります。

セイ・コンサルティング・グループでは新人エンジニア研修のアシスタント講師を募集しています。

投稿者プロフィール

山崎講師
山崎講師代表取締役
セイ・コンサルティング・グループ株式会社代表取締役。
岐阜県出身。
海外放浪の末、2000年創業、2004年会社設立。
IT企業向け人材育成研修歴業界歴20年以上。
すべての無駄を省いた費用対効果の高い「筋肉質」な研修を提供します!
この記事に間違い等ありましたらぜひお知らせください。

学生時代は趣味と実益を兼ねてリゾートバイトにいそしむ。長野県白馬村に始まり、志賀高原でのスキーインストラクター、沖縄石垣島、北海道トマム。高じてオーストラリアのゴールドコーストでツアーガイドなど。現在は野菜作りにはまっている。