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:
- 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.
- 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 é:
- normalizar entradas,
- usar inicialização apropriada à ativação,
- adicionar a camada de normalização certa para o seu cenário,
- verificar posicionamento de residual/normalização,
- então ajustar otimizador e agenda de taxa de aprendizado.