PyTorch torch.nn.BatchNorm2d 関数
torch.nn.BatchNorm2dPyTorchにおける2次元バッチ正規化用のモジュールです。
バッチ正規化は、層の入力を正規化することでトレーニングを高速化し、収束を安定させます。これは現代の深層ニューラルネットワークで最もよく使われる技術の1つです。
関数定義
torch.nn.BatchNorm2d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
パラメータの説明:
num_features(int): 入力チャネル数 C。eps(float): 数値安定性のために分母に加える値。デフォルトは1e-5。momentum(float): 移動平均と移動分散の計算に使用される値。デフォルトは0.1。affine(bool): 学習可能なアフィンパラメータ(gammaとbeta)を使用するかどうか。デフォルトはTrue。track_running_stats(bool): 移動統計量を追跡するかどうか。デフォルトはTrue。
属性:
weight(Tensor): 学習可能なスケーリングパラメータgamma。形状は(num_features,)。bias(Tensor): 学習可能なオフセットパラメータbeta。形状は(num_features,)。running_mean(Tensor): 移動平均。トレーニング中は更新されません。running_var(Tensor): 移動分散。トレーニング中は更新されません。
数学的原理
バッチ正規化は各チャネルを独立に標準化します:
y = (x - E[x]) / sqrt(Var[x] + eps) * gamma + beta
ここで、gammaとbetaは学習可能なパラメータであり、ネットワークが表現力を回復できるようにします。
使用例
例1: 基本的な使い方
畳み込み層の後にバッチ正規化を使用します:
例
import torch
import torch.nn as nn
# バッチ正規化層を作成:32チャネル
bn = nn.BatchNorm2d(num_features=32)
# パラメータを出力
print("gamma (weight):", bn.weight.shape)
print("beta (bias):", bn.bias.shape)
print("running_mean:", bn.running_mean.shape)
print("running_var:", bn.running_var.shape)
# 入力を作成:batch=4, チャネル=32, 高さ=16, 幅=16
input_tensor = torch.randn(4, 32, 16, 16)
# 順伝播
output = bn(input_tensor)
print("n入力平均 (チャネルごと):", input_tensor.mean(dim=(0, 2, 3))[:5].tolist())
print("出力平均 (チャネルごと):", output.mean(dim=(0, 2, 3))[:5].tolist())
print("n入力形状:", input_tensor.shape)
print("出力形状:", output.shape)
import torch.nn as nn
# バッチ正規化層を作成:32チャネル
bn = nn.BatchNorm2d(num_features=32)
# パラメータを出力
print("gamma (weight):", bn.weight.shape)
print("beta (bias):", bn.bias.shape)
print("running_mean:", bn.running_mean.shape)
print("running_var:", bn.running_var.shape)
# 入力を作成:batch=4, チャネル=32, 高さ=16, 幅=16
input_tensor = torch.randn(4, 32, 16, 16)
# 順伝播
output = bn(input_tensor)
print("n入力平均 (チャネルごと):", input_tensor.mean(dim=(0, 2, 3))[:5].tolist())
print("出力平均 (チャネルごと):", output.mean(dim=(0, 2, 3))[:5].tolist())
print("n入力形状:", input_tensor.shape)
print("出力形状:", output.shape)
出力結果は次のとおりです:
gamma (weight): torch.Size([32]) beta (bias): torch.Size([32]) running_mean: torch.Size([32]) running_var: torch.Size([32]) 输入均值 (按通道): tensor([ 0.0234, 0.0456, -0.0123, -0.0345, 0.0567]) 输出均值 (按通道): tensor([ 0., 0., 0., 0., 0.]) 输入形状: torch.Size([4, 32, 16, 16]) 输出形状: torch.Size([4, 32, 16, 16])
トレーニング時、出力は平均0、分散1に標準化されます(その後にgammaとbetaの変換が続きます)。
例2: 訓練と評価モード
バッチ正規化は訓練時と評価時で動作が異なります:
例
import torch
import torch.nn as nn
bn = nn.BatchNorm2d(num_features=16)
# 訓練モード
bn.train()
print("訓練モード - 勾配が必要:", bn.weight.requires_grad)
# 模擬トレーニング
for _ in range(10):
x = torch.randn(8, 16, 8, 8)
output = bn(x)
print("訓練後のrunning_mean 最初の5つ:", bn.running_mean[:5].tolist())
# 評価モード
bn.eval()
print("n評価モード - 勾配が必要:", bn.weight.requires_grad)
# 評価時は移動統計量を使用
x = torch.randn(4, 16, 8, 8)
output = bn(x)
print("評価時の出力形状:", output.shape)
import torch.nn as nn
bn = nn.BatchNorm2d(num_features=16)
# 訓練モード
bn.train()
print("訓練モード - 勾配が必要:", bn.weight.requires_grad)
# 模擬トレーニング
for _ in range(10):
x = torch.randn(8, 16, 8, 8)
output = bn(x)
print("訓練後のrunning_mean 最初の5つ:", bn.running_mean[:5].tolist())
# 評価モード
bn.eval()
print("n評価モード - 勾配が必要:", bn.weight.requires_grad)
# 評価時は移動統計量を使用
x = torch.randn(4, 16, 8, 8)
output = bn(x)
print("評価時の出力形状:", output.shape)
例3: 完全なCNNの例
典型的なバッチ正規化CNNの構造:
例
import torch
import torch.nn as nn
class BNConvNet(nn.Module):
def __init__(self, num_classes=10):
super(BNConvNet, self).__init__()
# 畳み込み + バッチ正規化 + 活性化 + プーリング
self.block1 = nn.Sequential(
nn.Conv2d(3, 32, kernel_size=3, padding=1),
nn.BatchNorm2d(32),
nn.ReLU(),
nn.MaxPool2d(2, 2) # 32 -> 16
)
self.block2 = nn.Sequential(
nn.Conv2d(32, 64, kernel_size=3, padding=1),
nn.BatchNorm2d(64),
nn.ReLU(),
nn.MaxPool2d(2, 2) # 16 -> 8
)
# グローバル平均プーリング
self.gap = nn.AdaptiveAvgPool2d(1)
self.classifier = nn.Linear(64, num_classes)
def forward(self, x):
x = self.block1(x)
x = self.block2(x)
x = self.gap(x)
x = x.view(x.size(0), -1)
x = self.classifier(x)
return x
model = BNConvNet()
input_image = torch.randn(2, 3, 32, 32)
output = model(input_image)
print("入力形状:", input_image.shape)
print("出力形状:", output.shape)
# 最初のBN層のパラメータを出力
print("n最初のBN層のgamma:", model.block1[1].weight[:5].tolist())
print("最初のBN層のbeta:", model.block1[1].bias[:5].tolist())
import torch.nn as nn
class BNConvNet(nn.Module):
def __init__(self, num_classes=10):
super(BNConvNet, self).__init__()
# 畳み込み + バッチ正規化 + 活性化 + プーリング
self.block1 = nn.Sequential(
nn.Conv2d(3, 32, kernel_size=3, padding=1),
nn.BatchNorm2d(32),
nn.ReLU(),
nn.MaxPool2d(2, 2) # 32 -> 16
)
self.block2 = nn.Sequential(
nn.Conv2d(32, 64, kernel_size=3, padding=1),
nn.BatchNorm2d(64),
nn.ReLU(),
nn.MaxPool2d(2, 2) # 16 -> 8
)
# グローバル平均プーリング
self.gap = nn.AdaptiveAvgPool2d(1)
self.classifier = nn.Linear(64, num_classes)
def forward(self, x):
x = self.block1(x)
x = self.block2(x)
x = self.gap(x)
x = x.view(x.size(0), -1)
x = self.classifier(x)
return x
model = BNConvNet()
input_image = torch.randn(2, 3, 32, 32)
output = model(input_image)
print("入力形状:", input_image.shape)
print("出力形状:", output.shape)
# 最初のBN層のパラメータを出力
print("n最初のBN層のgamma:", model.block1[1].weight[:5].tolist())
print("最初のBN層のbeta:", model.block1[1].bias[:5].tolist())
例4: affineパラメータを使用しない
学習可能なパラメータを無効にする:
例
import torch
import torch.nn as nn
# 学習可能なパラメータなしのバッチ正規化
bn_no_affine = nn.BatchNorm2d(16, affine=False)
print("weightはありますか:", bn_no_affine.weight is not None)
print("biasはありますか:", bn_no_affine.bias is not None)
# それでも標準化は行われます
x = torch.randn(4, 16, 8, 8)
output = bn_no_affine(x)
print("n出力形状:", output.shape)
import torch.nn as nn
# 学習可能なパラメータなしのバッチ正規化
bn_no_affine = nn.BatchNorm2d(16, affine=False)
print("weightはありますか:", bn_no_affine.weight is not None)
print("biasはありますか:", bn_no_affine.bias is not None)
# それでも標準化は行われます
x = torch.randn(4, 16, 8, 8)
output = bn_no_affine(x)
print("n出力形状:", output.shape)
よくある質問
Q1: バッチ正規化はReLUの前と後ろのどちらに置くべきですか?
どちらの方法も有効です。元の論文では畳み込みの後、活性化の前に置かれています。実際の実践では活性化の後に置くこともよくあります。
Q2: バッチサイズが小さいときにうまく機能しない場合はどうすればよいですか?
- 使用
GroupNorm代替 - 使用
LayerNorm - バッチサイズを大きくする
- momentumパラメータを調整する
Q3: 評価時にevalモードに切り替える必要があるのはなぜですか?
トレーニング時はバッチ統計量を使用し、評価時は移動統計量を使用します。切り替えを忘れると出力が不一致になります。
使用シーン
nn.BatchNorm2d主な使用シーンは次のとおりです:
- トレーニングの高速化: より大きな学習率を使用できる
- 収束の安定化: 内部共変量シフトを減らす
- 正則化: 軽い正則化効果を提供する
- 画像分類: ほぼすべての現代のCNNが使用している
注意:バッチ正規化はトレーニング時に十分なバッチサイズが必要です。バッチが小さいと統計が不安定になる可能性があります。
その他の拡張
PyTorch torch.nn リファレンスマニュアル