Six things every serious training loop has
- Config — a dataclass or YAML / TOML file that captures every hyperparameter. Saved into every checkpoint.
- Seed — set at the top, applied to everything.
- Mixed precision — autocast + GradScaler (or BF16, no scaler).
- Gradient clipping — between backward and optimizer step.
- Learning-rate schedule — warmup + cosine for transformers, OneCycle for fine-tuning, step decay for old-school CNNs.
- Validation + best-checkpoint saving — every epoch (or every N steps), eval on a held-out set, save if val metric improved.
Tip: If you start every project from a 'blank training loop' template that already includes these six pieces, you save yourself the same week of debugging on every project.
The gradient accumulation pattern
For models too big to fit a desired batch size in VRAM: skip opt.zero_grad() for several forward/backward passes, then step + zero. The result is mathematically equivalent to a single bigger batch. Adjust scaler.scale(loss / accum_steps).backward() to keep the loss magnitude consistent.
Where frameworks help
PyTorch Lightning, Hugging Face Trainer, and Accelerate handle the boilerplate (mixed precision, distributed, checkpointing, logging) so you can focus on the model and data. Trade-off: less code, less visibility into what's happening. Write your own loop at least once before adopting a framework.
Principle: Your first training loop should be hand-written. Your tenth should probably use a framework. The middle ground is where you learn the most.