Как рассчитывается Layer Normalization?

Ответ

Layer Normalization (LN) стандартизирует активации по измерениям признаков (features) для каждого отдельного примера в батче, в отличие от BatchNorm, который нормализует по батчу для каждого признака.

Формула расчета для входного тензора (x):

[ text{LN}(x) = gamma cdot frac{x - mu}{sqrt{sigma^2 + epsilon}} + beta ]

Где:

  • (mu) — среднее значение по последней оси (признакам).
  • (sigma^2) — дисперсия по последней оси.
  • (gamma) и (beta) — обучаемые параметры масштаба и сдвига.
  • (epsilon) — малая константа для численной стабильности.

Практическая реализация на PyTorch:

import torch
import torch.nn as nn

# Пример с размерностью [batch_size, sequence_length, hidden_size]
batch_size, seq_len, d_model = 32, 10, 512
x = torch.randn(batch_size, seq_len, d_model)

# Инициализация слоя
layer_norm = nn.LayerNorm(d_model, eps=1e-5)

# Прямой проход
output = layer_norm(x)
print(f'Input shape: {x.shape}')
print(f'Output shape: {output.shape}')
print(f'Gamma shape: {layer_norm.weight.shape}')  # [d_model]
print(f'Beta shape: {layer_norm.bias.shape}')     # [d_model]

Ключевые свойства:

  • Независимость от размера батча: Работает корректно даже при batch_size=1, что критично для RNN и инференса.
  • Стабильность обучения: Сглаживает градиенты, особенно в глубоких сетях и трансформерах.
  • Инвариантность к сдвигу и масштабу: Параметры gamma и beta позволяют модели восстановить оптимальное представление.

Ответ 18+ 🔞

Давай разберём эту штуку, а то названия похожи, а внутри — пиздец разница. Layer Normalization — это как будто ты пришёл на вечеринку и решил привести себя в порядок, но не по сравнению с другими гостями (это был бы BatchNorm), а просто сам с собой: «Так, волосы торчат — пригладил, штаны криво сидят — поправил». То есть нормализуешь ты каждый пример в батче по его собственным фичам, а не смотришь, как у соседа дела.

Формула, если кому надо вникнуть:

[ text{LN}(x) = gamma cdot frac{x - mu}{sqrt{sigma^2 + epsilon}} + beta ]

Что тут вообще происходит:

  • (mu) и (sigma^2) — это среднее и дисперсия, которые ты считаешь по последней оси (то есть по фичам). Не по батчу, ёпта, а внутри одного примера!
  • (gamma) и (beta) — это твои обучаемые костыли, масштаб и сдвиг, чтобы модель не охуела от этой стандартизации и могла что-то полезное выучить.
  • (epsilon) — такая мелкая пакость, чтобы на ноль случайно не поделить, а то будет тебе хиросима численная.

А теперь смотри, как это в коде выглядит, чтоб совсем понятно стало:

import torch
import torch.nn as nn

# Допустим, у нас стандартный тензор для трансформера: [батч, длина последовательности, размерность]
batch_size, seq_len, d_model = 32, 10, 512
x = torch.randn(batch_size, seq_len, d_model)

# Создаём слой — передаём размерность, по которой нормализуем (последнюю ось)
layer_norm = nn.LayerNorm(d_model, eps=1e-5)

# Прямой проход — всё как обычно
output = layer_norm(x)
print(f'Input shape: {x.shape}')
print(f'Output shape: {output.shape}')
print(f'Gamma shape: {layer_norm.weight.shape}')  # [d_model]
print(f'Beta shape: {layer_norm.bias.shape}')     # [d_model]

И главные фишки, почему все так на него подсели:

  • Похуй на размер батча. Серьёзно, работает даже если batch_size = 1. Для RNN или когда ты модель на продакшн гонишь — это просто песня. BatchNorm на таком сдохнет, а этот — красавчик.
  • Стабилизирует обучение, особенно в глубоких сетях. Градиенты не так дико скачут, модель не бздит на каждом шагу. В тех же трансформерах без него — просто пизда рулю, ничего не сойдётся.
  • Инвариантность к сдвигу и масштабу. Параметры gamma и beta — это как предохранительный клапан. Модель может сказать: «Знаешь что, я тут посчитала, и мне вот такой масштаб на этом слое больше нравится» — и восстановит оптимальное представление. Умная жопа, короче.

Вот и вся магия. Не BatchNorm, который овердохуища вычислений требует и на батче завязан, а более универсальная и хитрая жопа, которая внутри каждого примера порядок наводит.