← Назад к вопросам
Почему в рекуррентных сетях и трансформерах используют не Batch Normalization, а Layer Normalization?
2.0 Middle🔥 141 комментариев
#Глубокое обучение
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Почему в RNN и трансформерах используют Layer Normalization вместо Batch Normalization
Это один из ключевых различий между архитектурами. Причины фундаментальные и связаны с особенностями работы этих сетей.
Что такое Batch Normalization (BN)
# Нормализуем по батчу (по другим примерам в батче)
# Input: [batch_size, features]
mean_batch = mean(X, axis=0) # Средняя по всем примерам
std_batch = std(X, axis=0) # Стандартное отклонение по примерам
X_normalized = (X - mean_batch) / std_batch
# Пример батча:
# X = [[1.0, 2.0],
# [2.0, 3.0],
# [3.0, 4.0]]
# Нормализуем для каждого признака (столбца) отдельно
Что такое Layer Normalization (LN)
# Нормализуем по признакам (по всем признакам одного примера)
# Input: [batch_size, sequence_length, features] # или [batch_size, features]
mean_layer = mean(X, axis=-1, keepdims=True) # Средняя по признакам
std_layer = std(X, axis=-1, keepdims=True) # Стандартное отклонение
X_normalized = (X - mean_layer) / std_layer
# Пример:
# X = [[1.0, 2.0, 3.0],
# [2.0, 3.0, 4.0]]
# Нормализуем для каждого примера отдельно (по 3 признакам)
Проблема Batch Normalization в RNN
1. Размер батча меняется
# Проблема: Статистика батча нестабильна
# Обучение: batch_size=32
# Статистика: mean, std вычисляются по 32 примерам
# Inference: batch_size=1 (обработка одного примера)
# Статистика: mean и std почти случайны для одного примера!
# Сильный разрыв между train и inference
# RNN часто работает с переменной длиной последовательностей
# batch_size меняется часто
2. Temporal Consistency
В RNN одна последовательность обрабатывается шаг за шагом:
# Временной шаг 1: hidden_state_1 = RNN(x_1, hidden_0)
# Временной шаг 2: hidden_state_2 = RNN(x_2, hidden_1) ← Зависит от предыдущего
# Временной шаг 3: hidden_state_3 = RNN(x_3, hidden_2) ← Зависит от предыдущего
# Если применяем BN, статистика меняется между шагами!
# На шаге 1 батч = [x_1_1, x_1_2, ..., x_1_32] (первые элементы 32 последовательностей)
# На шаге 2 батч = [x_2_1, x_2_2, ..., x_2_32] (вторые элементы 32 последовательностей)
# Совершенно разные последовательности!
3. Зависимость от батча
# BN зависит от других примеров в батче
# Это создает сложные зависимости
# Пример: есть два батча с одним и тем же примером
# Батч 1: [A, B, C, D] → нормализуем A с учетом B, C, D
# Батч 2: [A, X, Y, Z] → нормализуем A с учетом X, Y, Z
# Один и тот же пример A нормализуется по-разному!
# Нестабильно для RNN
Почему Layer Normalization работает лучше
1. Независимость от батча
# LN нормализует каждый пример независимо
# Для примера A:
ln_A = (A - mean(A)) / std(A)
# Неважно, какие другие примеры в батче
# A всегда нормализуется одинаково
# Стабильно как в train, так и в inference
2. Работает с переменным batch size
from tensorflow.keras import layers
# Layer Normalization
model = layers.LSTM(128)
model.add(layers.LayerNormalization())
# Неважно, какой batch_size:
# batch_size=32: работает
# batch_size=1: работает
# batch_size=512: работает
# Результат одинаков (с точностью до масштабирования)
3. Правильно для последовательностей
В RNN каждый временной шаг имеет свой hidden state:
# LSTM на временном шаге t
hidden_t = LSTM(input_t, hidden_{t-1})
hidden_t = LayerNorm(hidden_t) # Нормализуем по признакам
# Эта нормализация стабильна для всего процесса
# Каждый шаг нормализуется независимо
# vs Batch Normalization:
hidden_t = LSTM(input_t, hidden_{t-1})
hidden_t = BatchNorm(hidden_t) # Зависит от других примеров в батче
# Если на шаге t батч содержит разные временные шаги
# → Нестабильно!
Математическое сравнение
Вход для обработки:
X: [batch_size=3, sequence_length=4, features=5]
Batch Normalization:
- Норма по оси batch и sequence: axis=(0,1)
- Статистика: mean[5], std[5]
- Зависимость: от 3*4=12 примеров
- Проблема: разные последовательности смешиваются
Layer Normalization:
- Норма по оси features: axis=-1
- Статистика: mean[1], std[1] для каждой позиции
- Зависимость: только от текущего примера
- Преимущество: независимость, стабильность
Практический пример
import tensorflow as tf
from tensorflow.keras import layers, models
# RNN с Layer Normalization (правильно)
model_ln = models.Sequential([
layers.LSTM(64, return_sequences=True),
layers.LayerNormalization(),
layers.LSTM(32),
layers.LayerNormalization(),
layers.Dense(10, activation='softmax')
])
# RNN с Batch Normalization (плохо)
model_bn = models.Sequential([
layers.LSTM(64, return_sequences=True),
layers.BatchNormalization(), # ⚠️ Может быть нестабильно
layers.LSTM(32),
layers.BatchNormalization(),
layers.Dense(10, activation='softmax')
])
# Обучение
model_ln.compile(optimizer='adam', loss='categorical_crossentropy')
model_ln.fit(X_train, y_train, epochs=10, batch_size=32)
# При inference с batch_size=1:
y_pred = model_ln.predict(X_test) # Работает стабильно
# Нормализация одинакова как при train, так и при inference
Трансформеры и Layer Normalization
# Архитектура Transformer блока
class TransformerBlock(layers.Layer):
def __init__(self, embed_dim, num_heads, ff_dim):
super().__init__()
self.att = layers.MultiHeadAttention(num_heads=num_heads, key_dim=embed_dim)
self.ffn = models.Sequential([
layers.Dense(ff_dim, activation='relu'),
layers.Dense(embed_dim)
])
self.layernorm1 = layers.LayerNormalization(epsilon=1e-6)
self.layernorm2 = layers.LayerNormalization(epsilon=1e-6)
self.dropout1 = layers.Dropout(0.1)
self.dropout2 = layers.Dropout(0.1)
def call(self, inputs):
# Self-attention с Layer Norm
attn_output = self.att(inputs, inputs)
attn_output = self.dropout1(attn_output)
out1 = self.layernorm1(inputs + attn_output) # Residual + LN
# Feed-forward с Layer Norm
ffn_output = self.ffn(out1)
ffn_output = self.dropout2(ffn_output)
out2 = self.layernorm2(out1 + ffn_output) # Residual + LN
return out2
Важно: Post-LN (LN после residual) более стабилен в трансформерах:
out = LayerNormalization(x + attention(x)) # Post-LN ✓ (Stable)
out = x + LayerNormalization(attention(x)) # Pre-LN (Older approach)
Когда всё же использовать Batch Normalization
# CNN (Convolutional Neural Networks) — BN отлично работает
model = models.Sequential([
layers.Conv2D(32, 3),
layers.BatchNormalization(), # ✓ Хорошо
layers.Activation('relu'),
layers.Conv2D(64, 3),
layers.BatchNormalization(), # ✓ Хорошо
])
# Полносвязные сети (если batch_size большой и стабилен)
model = models.Sequential([
layers.Dense(256),
layers.BatchNormalization(), # ✓ Может быть хорошо
])
Сравнительная таблица
| Параметр | Batch Norm | Layer Norm |
|---|---|---|
| Зависит от батча | Да | Нет |
| Работает при batch_size=1 | Плохо | Отлично |
| Для RNN/LSTM | Нестабильно | Отлично |
| Для Трансформеров | Редко | Стандарт |
| Для CNN | Отлично | Хорошо |
| Обучается быстро | Да | Да |
| Требует больших батчей | Да | Нет |
Итог
Layer Normalization выбирают для RNN/Трансформеров потому что:
- Независимость — не зависит от других примеров в батче
- Стабильность — одинаковое поведение при train и inference
- Гибкость — работает с любым batch_size
- Правильная норма — нормализует по признакам, а не по примерам
- Особенности последовательностей — учитывает природу RNN/Трансформеров
Batch Normalization остается лучшим выбором для CNN благодаря встроенной регуляризации и ускорению обучения.