Normalização por Camada

O que é Normalização por Camada?

Normalização por Camada (Layer Normalization, LayerNorm) é uma técnica de normalização que estabiliza e acelera o treinamento de redes neurais ao normalizar ativações dentro de cada exemplo individual, tipicamente ao longo da dimensão de características (oculta). Diferentemente de métodos baseados em lote, a Normalização por Camada não calcula estatísticas ao longo do lote, o que a torna especialmente eficaz para:

  • Transformers (onde é usada em quase todos os blocos)
  • RNNs / modelos de sequência (onde estatísticas de lote podem ser incômodas ou instáveis)
  • Tamanhos de lote pequenos ou variáveis (onde normalização baseada em lote pode degradar)

A Normalização por Camada foi introduzida para reduzir a instabilidade de treinamento causada por distribuições de ativação que mudam conforme os parâmetros mudam durante a otimização (veja Descida do Gradiente e Retropropagação). Na prática, seu maior impacto é melhorar a estabilidade da otimização e permitir que arquiteturas mais profundas treinem de forma confiável.

Ideia central: normalizar por exemplo, ao longo das características

Considere um vetor oculto (x \in \mathbb{R}^{d}) para um único exemplo de treinamento (por exemplo, o estado oculto de um token em um transformer). A Normalização por Camada calcula:

[ \mu = \frac{1}{d}\sum_{i=1}^{d} x_i,\quad \sigma^2 = \frac{1}{d}\sum_{i=1}^{d} (x_i - \mu)^2 ]

Em seguida, normaliza:

[ \hat{x}_i = \frac{x_i - \mu}{\sqrt{\sigma^2 + \epsilon}} ]

Por fim, aplica uma transformação afim aprendível por característica:

[ y_i = \gamma_i \hat{x}_i + \beta_i ]

Onde:

  • (d) é o número de características sendo normalizadas (frequentemente o tamanho oculto do modelo)
  • (\epsilon) é uma pequena constante para estabilidade numérica
  • (\gamma, \beta \in \mathbb{R}^{d}) são parâmetros aprendidos (escala e deslocamento)

Em qual dimensão ela normaliza?

No aprendizado profundo moderno, a Normalização por Camada é mais comumente aplicada sobre a última dimensão (características). Por exemplo:

  • Representações de tokens em transformers: x tem formato (batch, seq_len, hidden_dim)
    A Normalização por Camada normaliza cada token independentemente ao longo de hidden_dim.
  • Ativações de MLP: x tem formato (batch, hidden_dim)
    A Normalização por Camada normaliza ao longo de hidden_dim.

Essa propriedade de “normalizar dentro de cada exemplo” é a diferença-chave em relação à normalização baseada em lote.

Por que a Normalização por Camada ajuda

A Normalização por Camada tende a ajudar porque:

  • Estabiliza as escalas das ativações à medida que sinais passam por muitas camadas (importante em redes profundas e com conexões residuais).
  • Melhora o condicionamento do problema de otimização (muitas vezes levando a um treinamento mais suave).
  • Desacopla o treinamento do tamanho do lote, tornando-o robusto quando os lotes são pequenos, variáveis, ou até mesmo de tamanho 1.

Ela frequentemente é discutida junto com “deslocamento de covariância interna” (internal covariate shift), mas uma visão mais prática é: a normalização reduz a sensibilidade a mudanças de parâmetros e ajuda a manter os gradientes em uma faixa viável.

A Normalização por Camada também interage favoravelmente com:

  • Conexões Residuais (comuns em transformers; veja Arquitetura Transformer)
  • Dropout (a Normalização por Camada geralmente é aplicada antes ou depois do dropout dependendo do desenho do bloco)

Normalização por Camada vs Normalização por Lote (e Normalização por Grupo)

A Normalização por Camada faz parte de uma família de camadas de normalização. As comparações mais comuns são:

Normalização por Camada vs Normalização por Lote

A Normalização por Lote normaliza ativações usando estatísticas computadas ao longo do lote (e frequentemente dimensões espaciais para redes convolucionais). A Normalização por Camada normaliza usando estatísticas computadas dentro de cada exemplo.

Diferenças-chave:

  • Fonte das estatísticas

    • Normalização por Lote: média/variância ao longo do lote (e possivelmente dimensões espaciais)
    • Normalização por Camada: média/variância ao longo das características para cada exemplo
  • Comportamento em treinamento vs inferência

    • Normalização por Lote: usa estatísticas do lote durante o treinamento, médias móveis durante a inferência
    • Normalização por Camada: mesmo cálculo em treinamento e inferência (sem médias móveis)
  • Sensibilidade ao tamanho do lote

    • Normalização por Lote: pode se tornar instável com lotes pequenos ou lotes não i.i.d.
    • Normalização por Camada: em grande parte independente do tamanho do lote
  • Casos de uso típicos

    • Normalização por Lote: CNNs, treinamento com lotes grandes, backbones de visão
    • Normalização por Camada: transformers, RNNs, modelos de linguagem, cenários com lotes pequenos

Normalização por Camada vs Normalização por Grupo

A Normalização por Grupo é comumente usada em redes convolucionais e normaliza canais em grupos, independentemente do tamanho do lote.

Uma relação útil:

  • Normalização por Camada em um tensor convolucional (normalizando ao longo de todos os canais e posições espaciais por exemplo) às vezes é agressiva demais ou inconveniente dependendo do layout do tensor.
  • Normalização por Grupo com 1 grupo é efetivamente semelhante à Normalização por Camada para tensores convolucionais com canais primeiro (channels-first) (normalizando ao longo de canais e dimensões espaciais por exemplo), e frequentemente é usada como substituta prática.

Onde a Normalização por Camada é aplicada em Transformers: pré-norm vs pós-norm

Em blocos transformer, o posicionamento da Normalização por Camada é uma escolha importante de projeto que afeta a estabilidade do treinamento.

Um bloco transformer tipicamente contém:

  • Autoatenção multi-cabeças
  • Uma rede feed-forward (MLP)
  • Conexões residuais em torno de ambos

Pós-norm (Transformer original)

Na formulação original do transformer, a Normalização por Camada é aplicada após adicionar o residual:

[ \text{out} = \text{LayerNorm}(x + \text{Sublayer}(x)) ]

Isso é frequentemente chamado de pós-norm.

Prós

  • Historicamente padrão; funciona bem em muitos cenários

Contras

  • Pode ser mais difícil de otimizar para transformers muito profundos (o fluxo de gradiente pode ser menos estável)

Pré-norm (comum em LLMs modernos)

Em pré-norm, a Normalização por Camada é aplicada antes da subcamada:

[ \text{out} = x + \text{Sublayer}(\text{LayerNorm}(x)) ]

Isso hoje é muito comum em modelos de linguagem grandes e transformers profundos porque tende a melhorar o fluxo de gradiente através do fluxo residual, tornando a otimização mais estável em profundidade.

Conclusão prática

  • Se você está treinando um transformer profundo e encontra instabilidade, pré-norm costuma ser o padrão mais seguro.

Normalização por Camada em RNNs e modelos de sequência

A Normalização por Camada também é popular em Redes Neurais Recorrentes e modelos de sequência porque:

  • Estados ocultos de RNN evoluem ao longo do tempo; estatísticas de lote podem ser pouco confiáveis ou incômodas.
  • Comprimentos de sequência podem variar; a normalização por exemplo evita acoplar comportamento entre sequências não relacionadas em um lote.
  • Frequentemente melhora a estabilidade ao treinar RNNs profundas ou empilhadas.

Um padrão comum é aplicar a Normalização por Camada a:

  • transformações de entrada-para-oculto,
  • transformações de oculto-para-oculto,
  • ou à pré-ativação combinada antes de uma não linearidade.

Detalhes práticos de implementação

Parâmetros afins aprendíveis (`gamma` e `beta`)

A Normalização por Camada quase sempre inclui parâmetros aprendíveis:

  • weight (γ): escala por característica
  • bias (β): deslocamento por característica

Eles permitem que a rede recupere flexibilidade de representação; sem eles, a normalização pode restringir demais a camada.

Em algumas arquiteturas, o viés pode ser omitido por simplicidade, mas implementações padrão incluem ambos.

O valor de `epsilon` importa

O termo (\epsilon) evita divisão por zero e melhora a estabilidade numérica. Valores comuns:

  • 1e-5 (comum no PyTorch)
  • 1e-6 (comum em algumas implementações de transformers)

Em precisão mista (mixed precision) (FP16/BF16), um epsilon pequeno demais pode levar a problemas numéricos; um epsilon grande demais pode reduzir levemente a efetividade da normalização. Se você observar NaNs ou instabilidade, ajustar epsilon é um primeiro passo razoável.

Quais eixos são normalizados?

As APIs dos frameworks diferem um pouco, mas o comportamento mais comum é:

  • Normalizar sobre as últimas k dimensões especificadas por normalized_shape.

Exemplo: se x é (batch, seq, hidden) e você passa normalized_shape=hidden, então cada vetor (hidden,) é normalizado independentemente.

Exemplo em PyTorch

import torch
import torch.nn as nn

batch, seq, hidden = 2, 4, 8
x = torch.randn(batch, seq, hidden)

ln = nn.LayerNorm(hidden)  # normalize over last dimension
y = ln(x)

print(x.shape, y.shape)  # torch.Size([2, 4, 8]) torch.Size([2, 4, 8])

Cálculo manual (para intuição)

def layer_norm(x, gamma, beta, eps=1e-5):
    # x: (..., hidden)
    mean = x.mean(dim=-1, keepdim=True)
    var = ((x - mean) ** 2).mean(dim=-1, keepdim=True)
    xhat = (x - mean) / torch.sqrt(var + eps)
    return gamma * xhat + beta

x = torch.randn(2, 4, 8)
gamma = torch.ones(8)
beta = torch.zeros(8)
y = layer_norm(x, gamma, beta)

Isso deixa explícito que a normalização é por exemplo (e por token/passo de tempo), não ao longo do lote.

Observações comuns e armadilhas

1) A Normalização por Camada não é “gratuita”: custo de computação e memória

A Normalização por Camada exige calcular média e variância e fazer normalização por elemento. Em transformers grandes, ela pode representar uma fração perceptível do tempo de execução.

Na prática, treinamento de alto desempenho usa kernels de Normalização por Camada fundidos (fused LayerNorm kernels) (frequentemente de bibliotecas especializadas) para reduzir o overhead.

2) Cuidado com layouts de tensor (especialmente em CNNs)

A Normalização por Camada é mais natural para tensores canais por último (channels-last) ou características por último (por exemplo, (batch, seq, hidden)).

Para tensores convolucionais (N, C, H, W):

  • Aplicar nn.LayerNorm((C, H, W)) normaliza ao longo dos canais e das posições espaciais, o que pode não corresponder à sua intenção.
  • Se você quer normalização principalmente ao longo dos canais, alternativas como Normalização por Grupo costumam ser mais adequadas.
  • Um truque comum é usar GroupNorm(num_groups=1, num_channels=C), que se comporta de forma semelhante a uma normalização “estilo Normalização por Camada” para tensores convolucionais com canais primeiro.

3) Interação com conexões residuais e estabilidade

O posicionamento da Normalização por Camada importa:

  • Transformers pré-norm geralmente são mais fáceis de treinar em profundidade.
  • Pós-norm pode funcionar bem, mas pode exigir ajuste cuidadoso (taxa de aprendizado, warmup, inicialização).

Isso interage com Inicialização de Pesos e cronogramas de taxa de aprendizado; se o treinamento divergir, a correção pode ser arquitetural (posicionamento da normalização) e não apenas ajuste do otimizador.

4) A Normalização por Camada não impõe ativações limitadas

A Normalização por Camada normaliza média e variância, mas as saídas ainda podem ter valores grandes dependendo de γ (escala) aprendido e das ativações a montante. Ela estabiliza distribuições, mas não “recorta” (clip) estritamente.

5) Tamanhos ocultos pequenos podem reduzir a efetividade

Se a dimensão de características (d) for muito pequena (por exemplo, 8 ou 16), a média/variância estimadas podem ser ruidosas e os benefícios podem diminuir. Isso é menos comum em transformers modernos (onde tamanhos ocultos tipicamente são centenas ou milhares).

Orientação prática: quando você deve usar Normalização por Camada?

A Normalização por Camada geralmente é um forte padrão quando:

  • Você está construindo um transformer ou uma arquitetura do tipo transformer.
  • Você treina com tamanhos de lote pequenos ou variáveis.
  • Seu modelo processa sequências, e estatísticas em nível de lote são inconvenientes ou prejudiciais.
  • Você quer um comportamento de normalização idêntico em treinamento e inferência.

A Normalização por Lote frequentemente é preferida quando:

  • Você está treinando uma CNN em imagens com tamanhos de lote razoavelmente grandes e quer as características de desempenho típicas da Normalização por Lote (mas veja Normalização por Lote para detalhes e ressalvas).

A Normalização por Grupo frequentemente é preferida quando:

  • Você está treinando CNNs com tamanhos de lote pequenos ou fazendo detecção/segmentação onde os tamanhos de lote são limitados (veja Normalização por Grupo).

Variantes relacionadas e notas modernas (breve)

Você pode encontrar variantes de normalização na literatura de transformers:

  • RMSNorm: normaliza pela raiz do valor quadrático médio (root-mean-square) sem subtrair a média. Pode ser um pouco mais rápida e é usada em alguns LLMs modernos, mas a Normalização por Camada permanece uma linha de base padrão.
  • ScaleNorm, NoNorm, e outras: geralmente trocam força de normalização por velocidade ou propriedades específicas de estabilidade.

Essas variantes existem em grande parte porque a normalização afeta tanto a otimização quanto o throughput em escala.

Resumo

  • A Normalização por Camada normaliza ativações dentro de cada exemplo ao longo das dimensões de características, e então aplica uma transformação afim aprendível.
  • Ela não usa estatísticas de lote, então se comporta da mesma forma durante treinamento e inferência e é robusta a tamanhos de lote pequenos/variáveis.
  • Ela é um pilar dos transformers modernos, com posicionamento pré-norm amplamente usado por estabilidade em modelos profundos.
  • Considerações práticas incluem escolher os eixos corretos para normalização, lidar com layouts de tensor, ajustar epsilon para precisão mista e entender o overhead de desempenho.

Para comparações e técnicas complementares, veja Normalização por Lote, Normalização por Grupo e Inicialização de Pesos.