← Назад к вопросам
Какие обучаемые параметры есть в BatchNorm?
2.2 Middle🔥 161 комментариев
#Глубокое обучение
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Какие обучаемые параметры есть в BatchNorm?
Batch Normalization (BatchNorm) — это техника нормализации, которая имеет обучаемые параметры, критические для эффективности сети. Многие ошибочно думают, что BatchNorm не содержит обучаемых параметров.
Структура BatchNorm
BatchNorm содержит два обучаемых параметра и два статистических параметра:
Четыре параметра в BatchNorm:
1. γ (gamma) - масштаб - ОБУЧАЕМЫЙ
2. β (beta) - смещение - ОБУЧАЕМЫЙ
3. μ (mu) - среднее - СТАТИСТИЧЕСКИЙ (не обучаемый)
4. σ² (sigma²) - дисперсия - СТАТИСТИЧЕСКИЙ (не обучаемый)
Алгоритм BatchNorm
1. Нормализация (по батчу):
x_normalized = (x - mean(x)) / sqrt(var(x) + ε)
2. Масштабирование и смещение (ОБУЧАЕМЫЕ):
y = γ * x_normalized + β
3. Обновление бегущих статистик (для инференса):
running_mean = momentum * running_mean + (1 - momentum) * batch_mean
running_var = momentum * running_var + (1 - momentum) * batch_var
Практическая демонстрация
import torch
import torch.nn as nn
# BatchNorm1d (для полносвязных слоёв)
bn = nn.BatchNorm1d(num_features=64)
print("Параметры BatchNorm1d:")
for name, param in bn.named_parameters():
print(f" {name}: {param.shape}")
# Output:
# weight: torch.Size([64]) ← γ (gamma) - масштаб
# bias: torch.Size([64]) ← β (beta) - смещение
print("\nСтатистические параметры (не обучаемые):")
print(f" running_mean: {bn.running_mean.shape}")
print(f" running_var: {bn.running_var.shape}")
print(f" momentum: {bn.momentum}")
# BatchNorm2d (для сверток)
bn2d = nn.BatchNorm2d(num_features=64)
print("\nПараметры BatchNorm2d (для каждого канала):")
for name, param in bn2d.named_parameters():
print(f" {name}: {param.shape}")
# Output:
# weight: torch.Size([64]) ← γ для каждого канала
# bias: torch.Size([64]) ← β для каждого канала
Обучаемые параметры: γ и β
γ (gamma) - Масштаб (Scale)
# Инициализируется единицами (нет трансформации по умолчанию)
defaults_gamma = torch.ones(64)
print(f"Начальное значение γ: {defaults_gamma[:5]}")
# Во время обучения сеть учится масштабировать нормализованный вывод
# Большое γ → усилить признак
# Малое γ → ослабить признак
# Пример: если нормализованный выход не информативен,
# γ может быть близко к 0 (игнорировать нормализацию)
print("\nВозможные значения γ после обучения:")
print(" γ = 0.5 → половина интенсивности нормализованного выхода")
print(" γ = 2.0 → удвоенная интенсивность")
print(" γ ≈ 0 → почти игнорирует нормализацию (редко)")
β (beta) - Смещение (Shift)
# Инициализируется нулями
defaults_beta = torch.zeros(64)
print(f"Начальное значение β: {defaults_beta[:5]}")
# Во время обучения сеть учится смещать нормализованный выход
# Позитивное β → сдвинуть вправо
# Негативное β → сдвинуть влево
print("\nВозможные значения β после обучения:")
print(" β = 0.5 → сдвинуть на +0.5")
print(" β = -1.0 → сдвинуть на -1.0")
print(" β = 0.0 → нет смещения (редко)")
Полный пример с обучением
import torch
import torch.nn as nn
import torch.optim as optim
class SimpleNetWithBN(nn.Module):
def __init__(self):
super().__init__()
self.fc1 = nn.Linear(100, 64)
self.bn1 = nn.BatchNorm1d(64) # BatchNorm после fc1
self.fc2 = nn.Linear(64, 10)
def forward(self, x):
x = self.fc1(x)
x = self.bn1(x) # Нормализация
x = torch.relu(x)
x = self.fc2(x)
return x
model = SimpleNetWithBN()
optimizer = optim.SGD(model.parameters(), lr=0.01)
print("Обучаемые параметры:")
for name, param in model.named_parameters():
print(f" {name}: {param.shape}, requires_grad={param.requires_grad}")
# Тренировочный цикл
model.train() # ВАЖНО: BatchNorm работает по-другому в train и eval
x = torch.randn(32, 100) # Батч из 32 примеров
y_true = torch.randint(0, 10, (32,))
output = model(x)
loss = nn.CrossEntropyLoss()(output, y_true)
optimizer.zero_grad()
loss.backward() # γ и β получают градиенты
optimizer.step() # γ и β обновляются
print("\nПосле обновления:")
print(f" γ: {model.bn1.weight[:5]}")
print(f" β: {model.bn1.bias[:5]}")
print(f"\nГрадиенты были вычислены и параметры обновлены!")
Статистические параметры (НЕ обучаемые)
bn = nn.BatchNorm1d(64)
print("Статистические параметры (для инференса):")
print(f" running_mean: {bn.running_mean}")
print(f" running_var: {bn.running_var}")
print(f" momentum: {bn.momentum}")
print(f" eps: {bn.eps}")
# Во время ОБУЧЕНИЯ:
# - Используется среднее и дисперсия ТЕКУЩЕГО БАТЧА
# - После каждого батча обновляются running_mean и running_var
# Во время ИНФЕРЕНСА:
# - Используются running_mean и running_var (не батча!)
# - Это позволяет модели работать с одиночными примерами
model.eval() # Переходим в режим инференса
x_single = torch.randn(1, 100) # Один пример
output = model(x_single) # Использует running_mean, running_var
Как работает обновление параметров
# Формула BackProp для γ и β:
# Цепное правило:
# ∂Loss/∂γ = ∂Loss/∂y * ∂y/∂γ
# ∂Loss/∂β = ∂Loss/∂y * ∂y/∂β
# где y = γ * x_norm + β
# Это означает:
# ∂y/∂γ = x_norm (как влияет γ на выход)
# ∂y/∂β = 1 (как влияет β на выход)
# Обновление градиентного спуска:
# γ_new = γ_old - lr * ∂Loss/∂γ
# β_new = β_old - lr * ∂Loss/∂β
print("\nПример вычисления градиентов:")
x_normalized = torch.randn(32, 64) # Нормализованные данные
loss_grad = torch.randn(32, 64) # Градиент от следующего слоя
# Градиент для γ
grad_gamma = (loss_grad * x_normalized).sum(dim=0) # Сумма по батчу
print(f" ∂Loss/∂γ shape: {grad_gamma.shape}")
# Градиент для β
grad_beta = loss_grad.sum(dim=0) # Сумма по батчу
print(f" ∂Loss/∂β shape: {grad_beta.shape}")
Можно ли отключить обучение γ и β?
bn = nn.BatchNorm1d(64)
# Заморозить параметры γ и β
for param in bn.parameters():
param.requires_grad = False
print("Параметры заморожены:")
for name, param in bn.named_parameters():
print(f" {name}: requires_grad={param.requires_grad}")
# Или использовать эту функцию:
from torch.nn.utils import freeze_module
# freeze_module(bn) # Требует PyTorch 1.11+
# Обычно заморозить BatchNorm имеет смысл при:
# - Transfer learning (использование предтренированной модели)
# - Fine-tuning с очень маленькой скоростью обучения
BatchNorm vs LayerNorm vs GroupNorm
# BatchNorm: нормализуется по батчу (разные статистики на разные примеры)
# LayerNorm: нормализуется по признакам (одинаковые статистики)
# GroupNorm: нормализуется по группам каналов
bn = nn.BatchNorm1d(64) # Нормирует по батчу
ln = nn.LayerNorm(64) # Нормирует по всем 64 признакам
gn = nn.GroupNorm(8, 64) # Нормирует по 8 группам из 64 каналов
print("Параметры LayerNorm:")
for name, param in ln.named_parameters():
print(f" {name}: {param.shape}")
# LayerNorm также имеет γ и β!
# GroupNorm также имеет γ и β!
Инициализация и значения по умолчанию
bn = nn.BatchNorm1d(64)
print("Значения после инициализации:")
print(f" γ (weight): min={bn.weight.min():.4f}, max={bn.weight.max():.4f}")
print(f" (все ~1.0)")
print(f" β (bias): min={bn.bias.min():.4f}, max={bn.bias.max():.4f}")
print(f" (все ~0.0)")
print(f"\nБегущие статистики:")
print(f" running_mean: {bn.running_mean}")
print(f" running_var: {bn.running_var}")
print(f" (inicialised to 0 and 1 respectively)")
Практические советы
✓ Проверяй требует ли слой градиентов:
model = SimpleNetWithBN()
for name, param in model.named_parameters():
print(f"{name}: requires_grad={param.requires_grad}")
✓ Правильно переключай train/eval режимы:
model.train() # Во время обучения
# ... обучение ...
model.eval() # Во время валидации/инференса
# ... инференс ...
✓ Учитывай моментум при инференсе:
# Если обучалась с momentum=0.1, бегущие статистики обновлялись медленно
# Может потребоваться больше примеров для хорошей оценки
✗ НЕ используй BatchNorm если:
- Батч очень маленький (< 4 примера) — статистика нестабильна
- Рекуррентные сети (используй LayerNorm)
- Очень большая дисперсия размеров батчей
Вывод: BatchNorm содержит ДВА обучаемых параметра (γ и β), которые критичны для его функциональности!