Inicialização e Normalização

Visão geral: por que o treinamento estável depende de inicialização e normalização

Treinar redes profundas (deep networks) é, fundamentalmente, um processo numérico iterativo: os parâmetros são atualizados por métodos no estilo Descida do Gradiente (Gradient Descent) usando gradientes (gradients) computados por Retropropagação (Backpropagation). Para que isso funcione, duas coisas precisam permanecer bem-comportadas:

  1. Propagação do sinal no passo direto: as ativações (activations) não devem explodir para valores enormes nem colapsar para constantes à medida que a profundidade aumenta.
  2. Propagação do gradiente no passo reverso: os gradientes não devem explodir nem desaparecer conforme fluem para camadas anteriores.

Comportamentos ruins em qualquer direção levam aos clássicos Problemas de Gradiente (Gradient Issues): aprendizado lento, instabilidade, sensibilidade à taxa de aprendizado (learning rate) ou divergência completa. Inicialização (initialization) e normalização (normalization) são duas das ferramentas mais eficazes e amplamente usadas para manter os sinais do passo direto e do passo reverso em uma faixa “razoável” ao longo do treinamento.

Este artigo explica por que elas importam, como métodos comuns funcionam e como aplicá-los na prática.

Fundamentos teóricos: o que pode dar errado?

Quebra de simetria (symmetry breaking)

Se todos os pesos em uma camada começam idênticos, todos os neurônios computam as mesmas saídas e recebem os mesmos gradientes — então eles permanecem idênticos para sempre. A inicialização aleatória quebra essa simetria para que neurônios diferentes possam se especializar.

Vieses (biases) muitas vezes podem ser inicializados com zero com segurança porque os pesos já quebram a simetria.

Crescimento/decadência da variância com a profundidade (passo direto)

Considere uma camada simples:

[ \mathbf{h} = \phi(\mathbf{W}\mathbf{x}) ]

Se as entradas de W forem grandes demais, a variância de (\mathbf{W}\mathbf{x}) cresce com a profundidade, fazendo as ativações explodirem ou saturarem (por exemplo, tanh saturando). Se os pesos forem pequenos demais, os sinais encolhem, levando a ativações próximas de zero e perda de informação.

Isso interage fortemente com a escolha da função de ativação (activation function) — veja Ativações e Perdas (Activations & Losses).

Crescimento/decadência do gradiente com a profundidade (passo reverso)

A retropropagação multiplica gradientes por jacobianos (Jacobians) camada após camada. Se valores singulares (singular values) típicos desses jacobianos forem > 1, os gradientes tendem a explodir; se < 1, eles tendem a desaparecer. Inicialização e normalização visam manter a rede próxima de um regime em que esses produtos não degenerem rápido demais.

No aprendizado profundo moderno, você pode pensar em “treinamento estável (stable training)” como manter escalas razoáveis de:

  • ativações,
  • gradientes,
  • e atualizações de parâmetros (parameter updates)

ao longo da rede e através dos passos de treinamento.

Inicialização: escolhendo um bom ponto de partida

A inicialização define a distribuição inicial dos pesos (e às vezes dos vieses). O objetivo não é codificar a solução, mas começar em um regime onde a otimização esteja bem condicionada.

Fan-in e fan-out: as quantidades-chave

Para uma camada com matriz de pesos (W \in \mathbb{R}^{\text{fan_out} \times \text{fan_in}}):

  • fan_in: número de unidades de entrada (fan-in) para um neurônio
  • fan_out: número de unidades de saída (fan-out) da camada

Para camadas convolucionais, fan-in/out incorporam o tamanho do kernel (por exemplo, (C_\text{in} \cdot k_h \cdot k_w)).

Esses valores determinam como a variância deve escalar.

Inicialização de Xavier/Glorot (Xavier/Glorot initialization) (bom padrão para tanh / linear)

A inicialização de Glorot tenta manter a variância estável entre camadas para ativações aproximadamente simétricas (como tanh):

  • Uniforme: [ W_{ij} \sim \mathcal{U}\left(-\sqrt{\frac{6}{\text{fan_in}+\text{fan_out}}}, \sqrt{\frac{6}{\text{fan_in}+\text{fan_out}}}\right) ]
  • Normal: [ W_{ij} \sim \mathcal{N}\left(0, \frac{2}{\text{fan_in}+\text{fan_out}}\right) ]

Quando usar: perceptrons multicamadas (MLPs) com tanh ou camadas lineares, ou como uma linha de base razoável.

Inicialização de He/Kaiming (He/Kaiming initialization) (melhor combinação para a família ReLU)

A ReLU descarta, em média, metade do sinal (valores abaixo de 0 viram 0). A inicialização de He compensa usando variância mais alta:

  • Normal: [ W_{ij} \sim \mathcal{N}\left(0, \frac{2}{\text{fan_in}}\right) ]
  • A uniforme usa um limite correspondente.

Quando usar: ReLU, LeakyReLU, configurações do tipo GELU frequentemente funcionam bem com variantes de Kaiming, embora Transformers muitas vezes dependam mais de normalização e dos padrões do framework.

Inicialização de LeCun (LeCun initialization) (frequentemente combinada com SELU)

A normal de LeCun tem como alvo variância (\approx 1/\text{fan_in}). Ela é comumente combinada com SELU em “redes auto-normalizantes (self-normalizing networks)”.

Quando usar: arquiteturas baseadas em SELU (menos comuns hoje do que ReLU/GELU + normalização).

Inicialização ortogonal (Orthogonal initialization)

A inicialização ortogonal define matrizes de pesos (aproximadamente) com colunas/linhas ortonormais, o que pode ajudar a preservar normas com a profundidade em regimes lineares.

Quando usar: redes neurais recorrentes (RNNs) (historicamente), blocos lineares profundos, ou ao experimentar com redes muito profundas.

Redes residuais (residual networks): a inicialização é mais fácil (e ainda importa)

Conexões residuais (residual connections) (por exemplo, (x + f(x))) melhoram muito o fluxo de sinal/gradiente, motivo pelo qual sustentam ResNets e Transformers. Mas ainda assim pode haver instabilidade se os ramos residuais começarem “altos” demais.

Práticas comuns:

  • Inicializar o ramo residual para ser pequeno:
    • Em alguns designs, definir a última camada em um bloco residual com pesos pequenos (ou zero).
  • Em Transformers, é comum ver:
    • padrões de normalização por camada + residual (especialmente pré-normalização) que reduzem a sensibilidade à inicialização.

Inicialização de vieses (bias initialization): detalhes pequenos, mas importantes

Padrões típicos:

  • Vieses: 0
  • Às vezes é benéfico:
    • Em redes ReLU, um viés pequeno e positivo pode reduzir neurônios mortos (menos comum com boa inicialização e ativações modernas).
    • Em cabeças de classificação, inicializar o viés em direção às probabilidades a priori das classes pode acelerar o treinamento inicial em conjuntos de dados desbalanceados.

Exemplos práticos em PyTorch: inicialização explícita

import torch
import torch.nn as nn

class MLP(nn.Module):
    def __init__(self, d_in=512, d_hidden=1024, d_out=10):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(d_in, d_hidden),
            nn.ReLU(),
            nn.Linear(d_hidden, d_out),
        )
        self.apply(self._init)

    def _init(self, m):
        if isinstance(m, nn.Linear):
            # He/Kaiming for ReLU
            nn.init.kaiming_normal_(m.weight, nonlinearity="relu")
            if m.bias is not None:
                nn.init.zeros_(m.bias)

    def forward(self, x):
        return self.net(x)

Para tanh:

if isinstance(m, nn.Linear):
    nn.init.xavier_uniform_(m.weight)
    nn.init.zeros_(m.bias)

Na prática, os padrões do framework frequentemente são razoáveis, mas a inicialização explícita se torna valiosa quando:

  • você constrói arquiteturas incomuns,
  • aprofunda muito a rede,
  • remove normalização,
  • ou vê perda/gradientes instáveis.

Normalização: estabilizando ativações (e a otimização)

Métodos de normalização tipicamente recentralizam e/ou reescalonam ativações (ou pesos) para que sua distribuição permaneça em uma faixa saudável durante o treinamento. Isso frequentemente melhora:

  • estabilidade do treinamento,
  • tolerância à taxa de aprendizado,
  • velocidade de convergência,
  • e às vezes generalização.

Normalização de entrada (input normalization) (não pule)

Antes de discutir camadas internas de normalização, lembre-se do passo mais simples e frequentemente mais importante:

  • Padronize entradas contínuas: (x \leftarrow (x - \mu)/\sigma)
  • Para imagens: normalização comum de média/desvio padrão por canal
  • Para texto: IDs de tokens não são normalizados, mas embeddings e normalização por camada lidam com a escala internamente

Se a escala de entrada varia de forma descontrolada entre atributos, as camadas iniciais precisam aprender a corrigi-la, o que desperdiça capacidade e pode desestabilizar o treinamento.

Normalização em Lote (Batch Normalization, BatchNorm)

A normalização em lote normaliza por atributo usando as estatísticas do minilote (minibatch):

[ \hat{x} = \frac{x - \mu_{\text{batch}}}{\sqrt{\sigma^2_{\text{batch}} + \epsilon}}, \quad y = \gamma \hat{x} + \beta ]

Propriedades-chave:

  • Funciona extremamente bem em redes neurais convolucionais (CNNs).
  • Introduz dependência do tamanho do lote e da composição do lote.
  • Usa médias móveis (running averages) para inferência (inference).

Prós

  • Forte estabilizador; frequentemente permite taxas de aprendizado maiores.
  • Atua como uma forma de regularização (regularization) (ruído vindo das estatísticas do lote).

Contras

  • Lotes pequenos podem tornar as estatísticas ruidosas (ou sem sentido).
  • Treinamento distribuído (distributed training) precisa de normalização em lote sincronizada ou alternativas.
  • Descompasso entre treino/inferência se as estatísticas acumuladas forem ruins.

Onde é usada: redes neurais convolucionais clássicas (estilo ResNet), alguns perceptrons multicamadas, menos comum em Transformers modernos.

Normalização por Camada (Layer Normalization, LayerNorm)

A normalização por camada normaliza ao longo dos atributos dentro de um único exemplo:

[ \hat{x} = \frac{x - \mu_{\text{features}}}{\sqrt{\sigma^2_{\text{features}} + \epsilon}} ]

Ela não depende de estatísticas do lote, o que a torna:

  • robusta a lotes pequenos,
  • natural para modelos de sequência (processamento de linguagem natural (NLP)),
  • estável para inferência autorregressiva (autoregressive inference).

Onde é usada: Transformers quase universalmente; veja Arquitetura Transformer (Transformer Architecture).

Normalização RMS (RMSNorm)

A normalização RMS normaliza pela raiz do valor quadrático médio sem subtrair a média:

[ \hat{x} = \frac{x}{\sqrt{\text{mean}(x^2) + \epsilon}} ]

Frequentemente usada em modelos de linguagem grandes (LLMs) modernos como uma alternativa mais barata à normalização por camada, às vezes com desempenho semelhante.

Normalização por Grupos (GroupNorm)

A normalização por grupos divide canais em grupos e normaliza dentro de cada grupo. É uma forte alternativa à normalização em lote quando os tamanhos de lote são pequenos (comum em detecção/segmentação).

Normalização de pesos e normalização espectral (weight-centric)

  • Normalização de Pesos (Weight Normalization, WeightNorm) reparametriza pesos para desacoplar direção e magnitude; pode acelerar a otimização em alguns cenários.
  • Normalização Espectral (Spectral Normalization, SpectralNorm) restringe a norma espectral (maior valor singular) de matrizes de pesos; usada para estabilidade em redes adversariais generativas (GANs) e para controlar constantes de Lipschitz.

Elas são menos “padrão” do que normalização em lote/normalização por camada, mas importantes em regimes específicos.

Posicionamento da normalização: o exemplo do Transformer (pré-normalização vs pós-normalização)

Em blocos residuais (residual blocks), você normalmente tem um padrão como:

  • Pós-normalização (post-norm) (Transformer antigo): [ x \leftarrow \text{LayerNorm}(x + f(x)) ]
  • Pré-normalização (pre-norm) (comum hoje): [ x \leftarrow x + f(\text{LayerNorm}(x)) ]

A pré-normalização tende a produzir gradientes mais estáveis em Transformers muito profundos, tornando o treinamento menos sensível à inicialização e à taxa de aprendizado. A pós-normalização pode funcionar, mas frequentemente precisa de ajustes mais cuidadosos.

Este é um exemplo concreto de como normalização e inicialização interagem com a arquitetura: conexões residuais + posicionamento da normalização podem dominar a estabilidade de treinamento.

Como inicialização e normalização interagem

A normalização pode “perdoar” uma inicialização imperfeita ao reescalonar ativações durante o treinamento. Essa é uma das razões pelas quais arquiteturas modernas com normalização por camada/normalização RMS e conexões residuais frequentemente são fáceis de treinar com inicializações padrão.

No entanto, a inicialização ainda importa porque:

  • a fase inicial do treinamento depende das escalas iniciais antes de os parâmetros aprendidos das normas (por exemplo, (\gamma, \beta)) se adaptarem,
  • nem todos os caminhos são normalizados (por exemplo, ramos residuais, logits de atenção (attention logits), cabeças de saída),
  • e camadas de normalização podem elas mesmas saturar ou se tornar ineficazes se as entradas estiverem em escalas extremas.

Implicação prática:

  • Com normalização em lote, a escala dos pesos importa menos, mas o ruído das estatísticas do lote e as configurações do otimizador (optimizer) importam mais.
  • Com normalização por camada/normalização RMS, a escala residual estável e a estabilidade da atenção importam muito; inicialização e posicionamento da normalização contribuem.

Checklist prático de estabilidade

1) Combine a inicialização com a ativação

  • ReLU/LeakyReLU: He/Kaiming
  • tanh: Xavier/Glorot
  • SELU: LeCun (mais variantes de dropout compatíveis com SELU)

Se você mudar as ativações, revisite as suposições de inicialização.

2) Observe a última camada (cabeças podem dominar a instabilidade)

A cabeça de saída (output head) (classificação/regressão) pode produzir logits grandes cedo e desestabilizar perda/gradientes. Práticas comuns:

  • inicializar os pesos da cabeça menores do que os do tronco,
  • ou usar uma taxa de aprendizado conservadora na cabeça em alguns cenários de aprendizado por transferência (transfer learning).

3) Use normalização apropriada ao seu regime de lote

  • Lotes grandes e redes neurais convolucionais: normalização em lote é excelente.
  • Lotes pequenos, sequências de comprimento variável, inferência autorregressiva: normalização por camada/normalização RMS é preferida.
  • Visão com lotes minúsculos: normalização por grupos frequentemente é melhor do que normalização em lote.

4) Redes residuais: mantenha ramos residuais sob controle

Se o treinamento divergir, considere:

  • reduzir a taxa de aprendizado,
  • garantir blocos de pré-normalização (para Transformers),
  • ou escalar explicitamente ramos residuais (algumas arquiteturas usam um parâmetro de escala residual aprendível (learnable residual scaling parameter)).

5) Combine com escolhas de otimizador e agenda

Inicialização e normalização preparam você para gradientes estáveis, mas o otimizador controla o tamanho da atualização. A instabilidade pode vir de passos agressivos demais mesmo com init/norm “corretos”. Veja:

Depuração: sintomas e o que tentar

Sintoma: a perda vira NaN cedo

Causas comuns:

  • taxa de aprendizado alta demais,
  • ativações/gradientes explosivos,
  • overflow em precisão mista (mixed-precision).

Coisas para tentar:

  • reduzir a taxa de aprendizado (ou adicionar aquecimento (warmup)),
  • adicionar/ajustar normalização,
  • usar corte de gradiente (gradient clipping) (veja Problemas de Gradiente),
  • verificar a escala de inicialização (especialmente em camadas customizadas (custom layers)).

Sintoma: o treinamento é extremamente lento, gradientes minúsculos

Causas comuns:

  • gradientes que desaparecem (vanishing gradients) por escalas ruins,
  • ativações saturadas (por exemplo, tanh com pesos grandes demais),
  • instabilidade de pós-normalização em Transformers profundos.

Coisas para tentar:

  • trocar para Xavier/He conforme apropriado,
  • usar ReLU/GELU com inicialização adequada,
  • usar blocos de pré-normalização em Transformers,
  • verificar normalização de entrada.

Sintoma: funciona com tamanho de lote 256, falha com tamanho de lote 8

Frequentemente dependência da normalização em lote. Tente:

  • normalização por grupos/normalização por camada,
  • SyncBatchNorm (distribuído),
  • ou acumular gradientes (accumulating gradients) para simular lotes maiores (observação: isso não corrige diretamente as estatísticas da normalização em lote a menos que estejam sincronizadas).

Tendências modernas e abordagens “sem normalização” (normalization-free approaches) (avançado)

Embora a normalização seja padrão, há trabalho contínuo para reduzi-la ou removê-la para melhorar a eficiência:

  • inicialização Fixup (Fixup initialization): permite treinar redes residuais muito profundas sem normalização ao inicializar e escalar cuidadosamente ramos residuais.
  • residual escalada (scaled residual) / μParam / regras de escalonamento profundo (deep scaling rules): famílias de técnicas para manter a propagação de sinal estável em grandes profundidades/larguras.
  • Algumas variantes de LLMs exploram normalização reduzida (ou normas alternativas) por velocidade, mas tipicamente ainda dependem de mecanismos do tipo normalização RMS/normalização por camada para estabilidade.

Na prática, para a maioria dos modelos do mundo real:

  • a normalização permanece o padrão, e
  • uma inicialização cuidadosa ainda é valiosa para componentes customizados e margens de estabilidade.

Resumo: o que lembrar

  • Inicialização define as escalas iniciais de sinal e gradiente; bons esquemas (Xavier/He) são derivados de ideias de preservação de variância.
  • Normalização estabiliza ativamente as ativações durante o treinamento e melhora o condicionamento da otimização; normalização em lote domina redes neurais convolucionais, normalização por camada/normalização RMS dominam Transformers.
  • Conexões residuais + posicionamento da normalização (especialmente pré-normalização em Transformers) são centrais para treinamento estável em profundidade.
  • Quando o treinamento é instável, trate inicialização e normalização como alavancas de primeira classe — junto com taxa de aprendizado, escolha do otimizador e agendas.

Se você está construindo uma nova arquitetura ou depurando instabilidade, um fluxo de trabalho confiável é:

  1. normalizar entradas,
  2. usar inicialização apropriada à ativação,
  3. adicionar a camada de normalização certa para o seu cenário,
  4. verificar posicionamento de residual/normalização,
  5. então ajustar otimizador e agenda de taxa de aprendizado.