Función PyTorch torch.nn.BatchNorm2d

PyTorch torch.nn 参考手册Manual de referencia de PyTorch torch.nn


torch.nn.BatchNorm2dEs el módulo de PyTorch para la normalización por lotes en 2D.

La normalización por lotes acelera el entrenamiento y estabiliza la convergencia al normalizar las entradas de la capa; es una de las técnicas más utilizadas en las redes neuronales profundas modernas.

Definición de la función

torch.nn.BatchNorm2d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)

Descripción de parámetros:

  • num_features(int): Número de canales de entrada C.
  • eps(float): Valor añadido al denominador para la estabilidad numérica. Por defecto es 1e-5.
  • momentum(float): Se utiliza para calcular la media acumulada (running mean) y la varianza acumulada (running variance). Por defecto es 0.1.
  • affine(bool): Si se utilizan parámetros afines aprendibles (gamma y beta). Por defecto es True.
  • track_running_stats(bool): Si se realiza un seguimiento de las estadísticas acumuladas (running statistics). Por defecto es True.

Atributos:

  • weight(Tensor): Parámetro de escala aprendible gamma, con forma (num_features,).
  • bias(Tensor): Parámetro de desplazamiento aprendible beta, con forma (num_features,).
  • running_mean(Tensor): Media acumulada (running mean), no se actualiza durante el entrenamiento.
  • running_var(Tensor): Varianza acumulada (running variance), no se actualiza durante el entrenamiento.

Principio matemático

La normalización por lotes normaliza cada canal de forma independiente:

y = (x - E[x]) / sqrt(Var[x] + eps) * gamma + beta

Donde gamma y beta son parámetros aprendibles que permiten a la red recuperar su capacidad expresiva.


Ejemplo de uso

Ejemplo 1: Uso básico

Usar la normalización por lotes después de la capa convolucional:

Ejemplo

import torch
import torch.nn as nn

# Crear la capa de normalización por lotes: 32 canales
bn = nn.BatchNorm2d(num_features=32)

# Imprimir parámetros
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)

# Crear la entrada: batch=4, canales=32, alto=16, ancho=16
input_tensor = torch.randn(4, 32, 16, 16)

# Propagación hacia adelante
output = bn(input_tensor)

print("nMedia de entrada (por canal):", input_tensor.mean(dim=(0, 2, 3))[:5].tolist())
print("Media de salida (por canal):", output.mean(dim=(0, 2, 3))[:5].tolist())
print("nForma de la entrada:", input_tensor.shape)
print("Forma de la salida:", output.shape)

El resultado de salida es:

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])

Durante el entrenamiento, la salida se normaliza a media 0 y varianza 1 (seguida de la transformación gamma y beta).

Ejemplo 2: Modo entrenamiento vs modo evaluación

La normalización por lotes se comporta de manera diferente durante el entrenamiento y la evaluación:

Ejemplo

import torch
import torch.nn as nn

bn = nn.BatchNorm2d(num_features=16)

# Modo entrenamiento
bn.train()
print("Modo entrenamiento - requiere gradiente:", bn.weight.requires_grad)

# Simular entrenamiento
for _ in range(10):
    x = torch.randn(8, 16, 8, 8)
    output = bn(x)

print("running_mean después del entrenamiento (primeros 5):", bn.running_mean[:5].tolist())

# Modo evaluación
bn.eval()
print("nModo evaluación - requiere gradiente:", bn.weight.requires_grad)

# Usar estadísticas acumuladas (running stats) en la evaluación
x = torch.randn(4, 16, 8, 8)
output = bn(x)
print("Forma de la salida en evaluación:", output.shape)

Ejemplo 3: Ejemplo completo de CNN

La estructura típica de una CNN con normalización por lotes:

Ejemplo

import torch
import torch.nn as nn

class BNConvNet(nn.Module):
    def __init__(self, num_classes=10):
        super(BNConvNet, self).__init__()
        # Convolución + normalización por lotes + activación + agrupación (pooling)
        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
        )
        # Agrupación promedio global (global average pooling)
        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("Forma de la entrada:", input_image.shape)
print("Forma de la salida:", output.shape)

# Imprimir los parámetros de la primera capa BN
print("nGamma de la primera capa BN:", model.block1[1].weight[:5].tolist())
print("Beta de la primera capa BN:", model.block1[1].bias[:5].tolist())

Ejemplo 4: Sin usar el parámetro affine

Deshabilitar los parámetros aprendibles:

Ejemplo

import torch
import torch.nn as nn

# Normalización por lotes sin parámetros aprendibles
bn_no_affine = nn.BatchNorm2d(16, affine=False)

print("¿Tiene weight?:", bn_no_affine.weight is not None)
print("¿Tiene bias?:", bn_no_affine.bias is not None)

# Aún así se realiza la normalización
x = torch.randn(4, 16, 8, 8)
output = bn_no_affine(x)
print("nForma de la salida:", output.shape)

Preguntas frecuentes

P1: ¿La normalización por lotes se coloca antes o después de ReLU?

Ambas formas son válidas. El artículo original la coloca después de la convolución y antes de la activación; en la práctica también se coloca a menudo después de la activación.

P2: ¿Qué hacer si el rendimiento es malo con un tamaño de lote (batch size) pequeño?

  • UsarGroupNormen lugar de
  • UsarLayerNorm
  • Aumentar el tamaño de lote (batch size)
  • Ajustar el parámetro momentum

P3: ¿Por qué es necesario cambiar al modo eval durante la evaluación?

Durante el entrenamiento se utilizan las estadísticas del lote (batch statistics), y durante la evaluación las estadísticas acumuladas (running statistics). Olvidar cambiar de modo produce salidas inconsistentes.


Escenarios de uso

nn.BatchNorm2dLos principales escenarios de aplicación incluyen:

  • Acelerar el entrenamiento: Permite usar una tasa de aprendizaje (learning rate) mayor
  • Estabilizar la convergencia: Reduce el desplazamiento interno de covariables (internal covariate shift)
  • Regularización: Proporciona un ligero efecto de regularización
  • Clasificación de imágenes: Casi todas las CNN modernas la utilizan

Nota: La normalización por lotes requiere un tamaño de lote (batch size) suficiente durante el entrenamiento; un lote pequeño puede provocar estadísticas inestables.


PyTorch torch.nn 参考手册Manual de referencia de PyTorch torch.nn

Otras extensiones