PyTorch torch.nn.LayerNorm 関数
torch.nn.LayerNormPyTorch のレイヤー正規化モジュールです。
バッチ正規化とは異なり、レイヤー正規化は単一サンプルの特徴次元に対して正規化を行い、batch size に依存しません。
関数定義
torch.nn.LayerNorm(normalized_shape, eps=1e-05, elementwise_affine=True)
パラメータの説明:
normalized_shape(int または list): 正規化する次元。eps(float): 数値安定性のための epsilon。デフォルトは 1e-5。elementwise_affine(bool): 学習可能なスケールとシフトを使用するかどうか。デフォルトは True。
数学的原理
レイヤー正規化の式:
y = (x - E[x]) / sqrt(Var[x] + eps) * gamma + beta
バッチ正規化との違い:レイヤー正規化は特徴の最後の次元で平均と分散を計算します。
使用例
例 1: 基本の使い方
特徴に対してレイヤー正規化を行います:
実例
import torch
import torch.nn as nn
# レイヤー正規化:最後の次元で正規化
ln = nn.LayerNorm(normalized_shape=10)
# 入力:batch=4、特徴次元=10
x = torch.randn(4, 10)
# 順伝播
output = ln(x)
print("入力形状:", x.shape)
print("出力形状:", output.shape)
print("n元の入力の最初の行:", x[0].tolist())
print("正規化後の最初の行:", output[0].tolist())
import torch.nn as nn
# レイヤー正規化:最後の次元で正規化
ln = nn.LayerNorm(normalized_shape=10)
# 入力:batch=4、特徴次元=10
x = torch.randn(4, 10)
# 順伝播
output = ln(x)
print("入力形状:", x.shape)
print("出力形状:", output.shape)
print("n元の入力の最初の行:", x[0].tolist())
print("正規化後の最初の行:", output[0].tolist())
例 2: 多次元入力
3D または 4D 入力を処理します:
実例
import torch
import torch.nn as nn
# シーケンス次元で正規化:(batch, seq, features)
ln_seq = nn.LayerNorm(normalized_shape=64)
# 3D 入力
x_3d = torch.randn(2, 10, 64)
output_3d = ln_seq(x_3d)
print("3D 入力:", x_3d.shape, "-> 出力:", output_3d.shape)
# 4D 入力 (画像など): (batch, height, width, channels)
# LayerNorm は最後の channel 次元で正規化
ln_channel = nn.LayerNorm(normalized_shape=128)
x_4d = torch.randn(2, 8, 8, 128)
output_4d = ln_channel(x_4d)
print("4D 入力:", x_4d.shape, "-> 出力:", output_4d.shape)
import torch.nn as nn
# シーケンス次元で正規化:(batch, seq, features)
ln_seq = nn.LayerNorm(normalized_shape=64)
# 3D 入力
x_3d = torch.randn(2, 10, 64)
output_3d = ln_seq(x_3d)
print("3D 入力:", x_3d.shape, "-> 出力:", output_3d.shape)
# 4D 入力 (画像など): (batch, height, width, channels)
# LayerNorm は最後の channel 次元で正規化
ln_channel = nn.LayerNorm(normalized_shape=128)
x_4d = torch.randn(2, 8, 8, 128)
output_4d = ln_channel(x_4d)
print("4D 入力:", x_4d.shape, "-> 出力:", output_4d.shape)
例 3: Transformer での使用
典型的な LayerNorm の使用:
実例
import torch
import torch.nn as nn
class TransformerBlock(nn.Module):
def __init__(self, d_model, nhead):
super(TransformerBlock, self).__init__()
self.self_attn = nn.MultiheadAttention(d_model, nhead, batch_first=True)
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
self.ffn = nn.Sequential(
nn.Linear(d_model, d_model * 4),
nn.GELU(),
nn.Linear(d_model * 4, d_model)
)
def forward(self, x):
# Self-attention with residual
attn_out, _ = self.self_attn(x, x, x)
x = self.norm1(x + attn_out)
# FFN with residual
ffn_out = self.ffn(x)
x = self.norm2(x + ffn_out)
return x
# テスト
block = TransformerBlock(d_model=512, nhead=8)
x = torch.randn(4, 100, 512) # (batch, seq, d_model)
output = block(x)
print("入力形状:", x.shape)
print("出力形状:", output.shape)
import torch.nn as nn
class TransformerBlock(nn.Module):
def __init__(self, d_model, nhead):
super(TransformerBlock, self).__init__()
self.self_attn = nn.MultiheadAttention(d_model, nhead, batch_first=True)
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
self.ffn = nn.Sequential(
nn.Linear(d_model, d_model * 4),
nn.GELU(),
nn.Linear(d_model * 4, d_model)
)
def forward(self, x):
# Self-attention with residual
attn_out, _ = self.self_attn(x, x, x)
x = self.norm1(x + attn_out)
# FFN with residual
ffn_out = self.ffn(x)
x = self.norm2(x + ffn_out)
return x
# テスト
block = TransformerBlock(d_model=512, nhead=8)
x = torch.randn(4, 100, 512) # (batch, seq, d_model)
output = block(x)
print("入力形状:", x.shape)
print("出力形状:", output.shape)
例 4: 学習可能なパラメータを使用しない
スケールとシフトのない純粋な正規化:
実例
import torch
import torch.nn as nn
# 学習可能なパラメータなし
ln = nn.LayerNorm(16, elementwise_affine=False)
x = torch.randn(4, 16)
output = ln(x)
# weight と bias なし
print("weight があるか:", hasattr(ln, 'weight'))
print("bias があるか:", hasattr(ln, 'bias'))
print("n出力形状:", output.shape)
import torch.nn as nn
# 学習可能なパラメータなし
ln = nn.LayerNorm(16, elementwise_affine=False)
x = torch.randn(4, 16)
output = ln(x)
# weight と bias なし
print("weight があるか:", hasattr(ln, 'weight'))
print("bias があるか:", hasattr(ln, 'bias'))
print("n出力形状:", output.shape)
正規化手法の比較
| 手法 | 正規化次元 | Batch 依存 | 適用シーン |
|---|---|---|---|
BatchNorm |
batch 次元 | である | CNN、batch が安定 |
LayerNorm |
特徴次元 | 否 | Transformer、RNN |
InstanceNorm |
チャンネル+空間 | 否 | スタイル変換 |
GroupNorm |
チャンネルグループ | 否 | 小さい batch のシーン |
よくある質問
Q1: LayerNorm と BatchNorm の違いは?
LayerNorm は batch に依存せず、系列モデルや batch の変化が大きいシーンに適しています。
Q2: normalized_shape はどう選択する?
通常は特徴次元を選択します。BERT では 768 や 1024 です。
Q3: なぜ Transformer は LayerNorm を使うのか?
Transformer の入力シーケンス長は可変であり、LayerNorm の方が安定します。
使用シーン
nn.LayerNorm主な適用シーンは次のとおりです:
- Transformer アーキテクチャ: BERT、GPT など
- リカレントニューラルネットワーク: LSTM、GRU
- 可変長シーケンスの処理: batch サイズが固定されていない
ヒント:LayerNorm は Transformer の標準装備であり、残差接続の後(Post-LN)または前(Pre-LN)に配置します。
その他の拡張