Skip to content
Back to skills

Ema Model Averaging

ASecurity

Tracks an Exponential Moving Average of model weights during training and evaluates both live and EMA models for more stable predictions.

  • 61 stars
  • 0 votes
  • 0 copies
  • 5 views
  • Added September 12, 2026
toolspython

Security analysis

A100/100

Scanned September 12, 2026

npx -y skills add wenmin-wu/ds-skills --skill ema-model-averaging --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Ema Model Averaging?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Ema Model Averaging
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/wenmin-wu-ema-model-averaging/badge)](https://www.skillsdirectory.com/skills/wenmin-wu-ema-model-averaging)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: cv-ema-model-averaging
description: >
  Tracks an Exponential Moving Average of model weights during training and evaluates both live and EMA models for more stable predictions.
---
# EMA Model Averaging

## Overview

Exponential Moving Average (EMA) maintains a shadow copy of model weights as a running average: `ema_w = decay * ema_w + (1 - decay) * w`. The EMA model is smoother than the live model — it averages over many training steps, reducing variance from batch noise and learning rate spikes. At inference, the EMA weights typically outperform any single checkpoint by 0.002–0.01. During training, evaluate both live and EMA models to track both trajectories.

## Quick Start

```python
import torch
from copy import deepcopy

class ModelEMA:
    def __init__(self, model, decay=0.999):
        self.ema = deepcopy(model)
        self.ema.eval()
        self.decay = decay
        for p in self.ema.parameters():
            p.requires_grad_(False)

    @torch.no_grad()
    def update(self, model):
        for ema_p, model_p in zip(self.ema.parameters(), model.parameters()):
            ema_p.data.mul_(self.decay).add_(model_p.data, alpha=1 - self.decay)

    def state_dict(self):
        return self.ema.state_dict()

# Training loop
model = MyModel().cuda()
ema = ModelEMA(model, decay=0.999)

for epoch in range(num_epochs):
    model.train()
    for batch in train_loader:
        loss = criterion(model(batch['image']), batch['target'])
        loss.backward()
        optimizer.step()
        optimizer.zero_grad()
        ema.update(model)  # update EMA after each step

    # Evaluate both
    val_score = evaluate(model, val_loader)
    ema_score = evaluate(ema.ema, val_loader)
    print(f"Live: {val_score:.4f}, EMA: {ema_score:.4f}")

# Save EMA weights for inference
torch.save(ema.state_dict(), 'model_ema.pth')
```

## Workflow

1. Initialize EMA as a deep copy of the model with `requires_grad=False`
2. After each optimizer step, call `ema.update(model)`
3. Evaluate both live and EMA models on validation set
4. Save EMA weights for inference (usually better than last checkpoint)

## Key Decisions

- **Decay**: 0.999 is standard; 0.9999 for long training; 0.99 for short/aggressive
- **Update frequency**: Every step is best; every N steps saves compute but reduces smoothing
- **BN buffers**: Also EMA-average batch norm running stats for consistency
- **Warmup**: Some implementations ramp decay from 0.99 to 0.999 over first 1000 steps

## References

- [VinBigData 2-Class Classifier Complete Pipeline](https://www.kaggle.com/code/corochann/vinbigdata-2-class-classifier-complete-pipeline)

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.