Média Móvel Exponencial (Exponential Moving Average, EMA)
Visão geral
Uma média móvel exponencial (EMA, exponential moving average) é um esquema de ponderação simples, mas poderoso, para suavizar uma sequência ruidosa (um sinal) dando mais peso a valores recentes e diminuindo exponencialmente o peso de valores mais antigos. Em aprendizado profundo, a EMA aparece em dois lugares muito comuns:
- Suavização de sinais de treinamento (por exemplo, perda de treinamento, acurácia de validação, normas de gradiente) para tornar as curvas mais fáceis de interpretar e estabilizar heurísticas como parada antecipada ou ajustes da taxa de aprendizado.
- Manutenção de pesos “sombra” (shadow) do modelo via EMA (uma cópia média dos parâmetros) para avaliação mais estável e, com frequência, melhor generalização, especialmente em cenários com alto ruído de treinamento (tamanhos de lote pequenos, aumento de dados pesado, modelos de difusão, etc.).
A EMA é conceitualmente relacionada a “momento” e a otimizadores adaptativos: SGD com Momento usa uma EMA de gradientes, e o Otimizador Adam usa EMAs tanto dos gradientes quanto dos gradientes ao quadrado.
Definição e intuição da EMA
Dada uma sequência de valores (x_1, x_2, \dots) (por exemplo, perda por passo), a EMA (m_t) no tempo (t) é definida recursivamente como:
[ m_t = \beta , m_{t-1} + (1-\beta), x_t ]
- (m_t) é o valor suavizado no passo (t)
- (\beta \in [0, 1)) é o decaimento (às vezes chamado de “momento” ou “suavização”)
- (\beta) maior (por exemplo, 0.99, 0.999) significa mais suavização e mais atraso
- (\beta) menor (por exemplo, 0.9) significa menos suavização e resposta mais rápida
Essa forma é comum em bibliotecas de ML. Você também pode ver uma parametrização equivalente:
[ m_t = (1-\alpha) m_{t-1} + \alpha x_t ] onde (\alpha = 1 - \beta) às vezes é chamado de “taxa de atualização”.
Por que “exponencial”?
Se você expandir a recorrência (desenrolar), obtém:
[ m_t = (1-\beta)\sum_{k=1}^{t} \beta^{t-k} x_k + \beta^t m_0 ]
Assim, o peso em uma observação (x_k) decai como (\beta^{t-k}), isto é, exponencialmente com a idade. Valores recentes dominam, mas valores mais antigos nunca desaparecem completamente.
EMA como um filtro passa-baixas (visão de processamento de sinais)
A EMA se comporta como um filtro passa-baixas de primeira ordem simples: ela suprime flutuações de alta frequência (“ruído”) enquanto acompanha tendências mais lentas. Por isso, é eficaz para suavizar curvas de treinamento com picos ou gradientes ruidosos.
Escolhendo o decaimento: meia-vida e “janela efetiva”
Escolher (\beta) pode parecer arbitrário. Duas formas úteis de interpretá-lo:
Meia-vida (quão rápido informações antigas desaparecem)
A meia-vida (h) é o número de passos após o qual o peso de um valor passado cai pela metade:
[ \beta^h = \tfrac{1}{2} \quad \Rightarrow \quad h = \frac{\ln(1/2)}{\ln(\beta)} ]
Exemplos:
- (\beta=0.9): (h \approx 6.6) passos
- (\beta=0.99): (h \approx 69) passos
- (\beta=0.999): (h \approx 693) passos
Assim, (\beta=0.999) significa “eu me importo com tendências ao longo de centenas de passos”.
Tamanho de janela efetiva (regra prática)
Uma heurística comum é que a EMA age aproximadamente como uma média móvel ao longo de cerca de:
[ N \approx \frac{1}{1-\beta} ]
Exemplos:
- (\beta=0.9 \Rightarrow N \approx 10)
- (\beta=0.99 \Rightarrow N \approx 100)
- (\beta=0.999 \Rightarrow N \approx 1000)
Isso não é exato (a EMA tem janela infinita), mas é um bom modelo mental.
Inicialização e correção de viés
Se você inicializar (m_0 = 0) (comum), então os valores iniciais da EMA ficam enviesados em direção a zero porque a média ainda “não aqueceu”. Você pode corrigir isso de forma semelhante ao Adam:
[ \hat{m}_t = \frac{m_t}{1-\beta^t} ]
Isso importa quando:
- você precisa de uma EMA precisa no início do treinamento, ou
- sua sequência tem média diferente de zero e você registra a EMA imediatamente.
Para muitas aplicações de suavização de curvas de treinamento, a correção de viés é opcional; para estimativas de momento em otimizadores, ela é padrão (veja Otimizador Adam).
EMA para suavizar métricas de treinamento e validação
Métricas de treinamento como perda ou acurácia podem ser extremamente ruidosas devido a:
- amostragem de minibatches (Descida de Gradiente Estocástica)
- aleatoriedade do aumento de dados
- dropout e outras camadas estocásticas
- dinâmicas de treinamento não estacionárias (agendamentos de taxa de aprendizado, currículo, etc.)
Usar EMA para registro (logging) pode tornar tendências visíveis sem alterar o treinamento em si.
Exemplo prático: suavizando uma curva de perda
def ema_update(ema, x, beta=0.98):
if ema is None:
return x # or 0.0 if you plan to bias-correct
return beta * ema + (1 - beta) * x
ema_loss = None
for step in range(num_steps):
loss = compute_loss(...)
ema_loss = ema_update(ema_loss, float(loss), beta=0.98)
if step % 50 == 0:
print(f"step={step} loss={loss:.4f} ema_loss={ema_loss:.4f}")
Decaimentos típicos para logging:
- (\beta \in [0.9, 0.99]) para perda por passo (feedback rápido)
- (\beta \in [0.99, 0.999]) para sinais muito ruidosos (por exemplo, normas de gradiente)
Suavizando métricas de validação
Métricas de validação frequentemente são calculadas com menor frequência, e cada avaliação ainda pode ser ruidosa (conjuntos de validação pequenos, inferência estocástica, etc.). A EMA pode ajudar a decidir:
- se o modelo está realmente melhorando,
- quando reduzir a taxa de aprendizado,
- quando aplicar parada antecipada.
Dito isso, tenha cuidado: uma EMA de acurácia de validação introduz atraso, então pode demorar para reagir a regressões.
Armadilhas comuns para EMA de métricas
- Não misture escalas: se você mudar o tamanho do lote ou o escalonamento da perda no meio da execução, a EMA pode se tornar enganosa.
- O atraso pode esconder instabilidades: sempre mantenha também as métricas brutas; use a EMA como auxílio, não como substituto.
- Use atualizações consistentes por passo: atualize a EMA por passo de treinamento ou por evento de avaliação de maneira consistente.
EMA dentro de otimizadores (momento e Adam)
A EMA não é apenas um truque de logging; ela é um componente central de métodos de otimização populares.
Momento como uma EMA de gradientes
No SGD com momento, um vetor de velocidade (v_t) acompanha uma EMA dos gradientes:
[ v_t = \beta v_{t-1} + (1-\beta) g_t ] [ \theta_t = \theta_{t-1} - \eta v_t ]
Aqui:
- (g_t) é o gradiente no passo (t)
- (\theta_t) são os parâmetros
- (\eta) é a taxa de aprendizado
Isso suaviza gradientes ruidosos e ajuda a acelerar em direções consistentes. Veja Descida de Gradiente e Descida de Gradiente Estocástica.
Adam: EMAs do primeiro e do segundo momentos
O Adam mantém:
- (m_t): EMA dos gradientes (primeiro momento)
- (v_t): EMA dos gradientes ao quadrado (segundo momento)
além de correção de viés. Esse é um dos motivos pelos quais a EMA e a correção de viés são ideias canônicas no aprendizado profundo moderno. Veja Otimizador Adam.
“Pesos sombra” via EMA (média de parâmetros)
Uma técnica amplamente utilizada é manter uma EMA dos parâmetros do modelo durante o treinamento. Você mantém duas versões:
- Pesos online: os parâmetros usuais sendo atualizados por retropropagação (backpropagation)
- Pesos EMA (sombra): uma média exponencial contínua desses parâmetros
Regra de atualização para cada tensor de parâmetro (\theta):
[ \theta^{EMA}t = \beta \theta^{EMA}{t-1} + (1-\beta)\theta_t ]
Por que pesos EMA ajudam
Intuitivamente, o treinamento com SGD não converge para um único ponto; ele oscila em torno de uma região por causa do ruído de minibatches. Pesos EMA:
- fazem a média de oscilações de curto prazo,
- frequentemente caem em uma região “mais plana” da paisagem de perda,
- fornecem métricas de avaliação mais estáveis e, às vezes, melhor generalização.
Essa ideia é intimamente relacionada à média de Polyak/Ruppert e tem semelhança com métodos como SWA (stochastic weight averaging), embora o SWA tipicamente use uma média uniforme sobre checkpoints selecionados, em vez de uma média exponencial.
Pesos EMA são especialmente comuns em:
- modelos de difusão (EMA frequentemente é essencial para as melhores amostras),
- aprendizado auto-supervisionado e métodos professor-aluno (por exemplo, treinamento no estilo “mean teacher”),
- treinamento de visão em larga escala com aumento de dados pesado.
Valores típicos de decaimento para EMA do modelo
Decaimentos de EMA do modelo geralmente ficam muito próximos de 1:
- (\beta = 0.999) ou (\beta = 0.9999) para execuções grandes de treinamento
- (\beta = 0.99) ou (\beta = 0.995) para execuções mais curtas
Uma boa forma de escolher (\beta) é decidir a escala de tempo (em passos) ao longo da qual você quer fazer a média, usando (N \approx 1/(1-\beta)).
Exemplo:
- Se você quiser uma janela efetiva de ~10.000 passos: escolha (\beta \approx 1 - 1/10000 = 0.9999).
Exemplo prático em PyTorch: mantendo pesos EMA
import copy
import torch
class EMA:
def __init__(self, model, beta=0.9999):
self.beta = beta
self.ema_model = copy.deepcopy(model)
self.ema_model.eval()
for p in self.ema_model.parameters():
p.requires_grad_(False)
@torch.no_grad()
def update(self, model):
for ema_p, p in zip(self.ema_model.parameters(), model.parameters()):
ema_p.data.mul_(self.beta).add_(p.data, alpha=1 - self.beta)
def state_dict(self):
return self.ema_model.state_dict()
# Usage in training loop
model = ...
ema = EMA(model, beta=0.999)
for step, batch in enumerate(loader):
loss = training_step(model, batch)
loss.backward()
optimizer.step()
optimizer.zero_grad(set_to_none=True)
ema.update(model)
# Evaluation with EMA weights
eval_model = ema.ema_model
validate(eval_model)
Padrões comuns em código real de treinamento:
- Atualize a EMA após o passo do otimizador.
- Mantenha os pesos EMA no mesmo dispositivo para velocidade, ou na CPU para economizar memória de GPU (atualizações mais lentas).
- Salve pesos EMA em checkpoints para avaliação/inferência de melhor qualidade.
Trocando pesos para avaliação
Algumas bases de código mantêm pesos EMA separadamente e temporariamente os trocam para dentro do modelo de treinamento antes da avaliação:
@torch.no_grad()
def swap_params(model, ema_model):
for p, ema_p in zip(model.parameters(), ema_model.parameters()):
p.data, ema_p.data = ema_p.data, p.data # swap
# validate using EMA weights
swap_params(model, ema.ema_model)
validate(model)
swap_params(model, ema.ema_model) # swap back
Isso evita manter dois modelos para o forward pass na memória, mas tenha cuidado para restaurar exatamente.
E quanto ao BatchNorm e às estatísticas acumuladas (running statistics)?
Se seu modelo usa BatchNorm, há dois mecanismos distintos de “média móvel”:
- EMA dos parâmetros do modelo (o que estamos discutindo)
- Média e variância acumuladas do BatchNorm (buffers internos atualizados durante o treinamento)
Se você avaliar com pesos EMA, mas com buffers de BatchNorm do modelo online, pode haver inconsistências. As opções incluem:
- manter EMA para ambos os parâmetros e buffers relevantes (alguns frameworks fazem isso),
- recomputar estatísticas do BatchNorm para o modelo EMA passando dados de treinamento por ele em modo de treinamento (às vezes chamado de “recalibração do BN”),
- usar camadas de normalização que não dependem de estatísticas acumuladas (por exemplo, GroupNorm) em arquiteturas onde a avaliação com EMA é importante.
O próprio BatchNorm usa uma atualização do tipo EMA para estatísticas acumuladas; veja Normalização em Lote.
Truques de agendamento da EMA: aquecimento e decaimento dinâmico
Um (\beta) fixo é comum, mas há situações em que você quer que a EMA “comece mais rápido” e depois fique mais suave.
Aquecimento / aumento gradual do decaimento
No início do treinamento, os parâmetros mudam rapidamente. Um (\beta) muito alto (por exemplo, 0.9999) pode fazer o modelo EMA ficar atrasado demais. Uma correção simples é usar um decaimento menor inicialmente e então aumentá-lo ao longo do tempo.
Conceitualmente:
- passos iniciais: (\beta) menor → a EMA alcança rapidamente
- passos finais: (\beta) maior → a EMA se torna estável
O agendamento específico varia por base de código; alguns usam uma função do número de passos para que a janela efetiva cresça ao longo do treinamento.
Correção de viés para EMA de parâmetros (opcional)
Você pode corrigir o viés da EMA de parâmetros como o Adam faz, mas muitos praticantes não o fazem — especialmente quando (\beta) é próximo de 1 e a execução de treinamento é longa o suficiente para que o transitório não importe.
Relação com outros métodos de média
Média móvel simples (SMA) vs EMA
- SMA em uma janela de tamanho (N): peso igual aos últimos (N) pontos, peso zero aos mais antigos
- EMA: janela infinita com pesos decrescendo exponencialmente
Vantagens da EMA:
- memória e computação constantes (O(1)) por atualização
- transição mais suave (sem corte abrupto)
- conveniente para treinamento em fluxo (streaming)
Vantagens da SMA:
- “tamanho de janela” mais intuitivo
- sem cauda longa de informação muito antiga
Média de Polyak e SWA
- Média de Polyak: faz a média de parâmetros ao longo do tempo; resultados clássicos em otimização convexa.
- SWA (stochastic weight averaging): tipicamente faz a média dos pesos de múltiplos pontos (frequentemente no fim do treinamento) com pesos uniformes.
A EMA é uma versão ponderada por recência. Ela frequentemente funciona bem sem exigir seleção explícita de checkpoints, mas pode ficar atrasada se o decaimento for alto demais.
Orientações práticas e boas práticas
Para suavizar métricas (logging)
- Comece com (\beta = 0.98) para perda por passo; ajuste conforme o ruído.
- Registre valores brutos e valores via EMA; use a EMA para legibilidade, os brutos para diagnóstico.
- Se você quiser um controle mais interpretável, decida uma janela efetiva (N) e defina (\beta \approx 1 - 1/N).
Para pesos EMA do modelo
- Use (\beta) próximo de 1; pontos de partida comuns:
- execuções curtas: 0.99–0.999
- execuções longas: 0.999–0.9999+
- Atualize a EMA após cada passo do otimizador.
- Avalie usando pesos EMA (e garanta que buffers de normalização sejam tratados de modo sensato).
- Salve checkpoints com pesos online e EMA se você puder retomar o treinamento.
Quando a EMA pode não ajudar (ou exige cuidado)
- Execuções de treinamento muito curtas: a EMA pode atrasar demais; use um (\beta) menor ou aquecimento.
- Regimes não estacionários (por exemplo, mudanças abruptas na taxa de aprendizado): a EMA pode “lembrar” do regime antigo; ajustar decaimento ou usar agendamentos ajuda.
- Modelos sensíveis a estatísticas acumuladas: tenha cuidado com buffers de BatchNorm como observado acima.
Resumo
Uma média móvel exponencial (EMA, exponential moving average) é uma forma leve e em fluxo de suavizar uma sequência ruidosa atribuindo pesos que decaem exponencialmente a observações passadas. Em aprendizado profundo, a EMA é tanto um bloco conceitual (momento, Adam) quanto uma ferramenta prática:
- EMA de métricas melhora a interpretabilidade de sinais ruidosos de treinamento.
- EMA de parâmetros (pesos sombra) frequentemente produz avaliação mais estável e, às vezes, melhor generalização, especialmente em treinamento em larga escala ou com alto ruído.
Entender como o decaimento (\beta) se mapeia para uma escala de tempo (meia-vida ou janela efetiva) torna a EMA mais fácil de ajustar e aplicar com confiabilidade em pipelines reais de treinamento.