Skip to main content
Training at Scale

Training Strategies at Scale

The Scale Challenge

Here is the central tension of modern deep learning: the best models are enormous, but GPU memory and compute are finite. A 70B-parameter model in FP32 requires 280 GB just for the weights — that is more than 3x the memory of an A100 80GB GPU. And that is just the parameters; you also need memory for activations, gradients, and optimizer states (Adam stores two additional copies of every parameter). Modern models are huge:
  • GPT-3: 175B parameters (~700 GB in FP32, impossible on any single GPU)
  • LLaMA 70B: 70B parameters (~280 GB in FP32)
  • Vision models: trained on billions of images, requiring weeks of compute
How do we train these efficiently? The answer is a toolkit of techniques — mixed precision, gradient accumulation, checkpointing, and various parallelism strategies — that trade off between memory, speed, and implementation complexity. A senior ML engineer needs to know when to reach for each tool.
Think of it like packing for a trip. A single suitcase (one GPU) can only hold so much. Mixed precision is like using vacuum bags to compress your clothes (same content, half the space). Gradient accumulation is like making multiple trips to the car. Model parallelism is like using multiple suitcases. FSDP/ZeRO is like having each family member carry a different part of a modular tent — nobody carries the full thing, but you can assemble it when needed.

Mixed Precision Training (FP16/BF16)

The single highest-impact optimization for most practitioners. The idea: most operations in a neural network do not need 32-bit floating point precision. Matrix multiplications, convolutions, and attention computations work just fine in 16-bit, which is both 2x smaller (less memory) and 2x faster (modern GPUs have dedicated 16-bit tensor cores that run at double the throughput). The “mixed” part means you keep a master copy of the weights in FP32 for the optimizer update (where precision matters for accumulating tiny gradient changes), but run the forward and backward passes in FP16/BF16.

Precision Comparison

BF16 is preferred on modern GPUs (A100, H100) — same range as FP32, no loss scaling needed.

Gradient Accumulation

When you cannot fit the batch size you want into GPU memory, gradient accumulation lets you simulate it by splitting the batch across multiple forward-backward passes and accumulating the gradients before taking a single optimizer step. Mathematically, the result is identical to running the full batch — the gradients are the same, just computed in pieces. Think of it like filling a swimming pool with a garden hose instead of a fire hose. It takes longer per “batch” but uses far less water pressure (memory) at any given moment.
Pitfall — BatchNorm with gradient accumulation: BatchNorm computes statistics per mini-batch, not per accumulated batch. With accumulation_steps=4, BatchNorm sees a batch of 64 even though your effective batch is 256. This means BN statistics are noisier than expected. If you rely on BatchNorm, either use SyncBatchNorm across accumulation steps (complex) or switch to GroupNorm/LayerNorm which are unaffected.

Gradient Checkpointing

During the forward pass, PyTorch saves every intermediate activation for use during backpropagation. For a 100-layer Transformer, this means storing 100 full-size activation tensors — which can dwarf the model parameters in memory usage. Gradient checkpointing (also called activation checkpointing) trades compute for memory: instead of saving activations, it discards them during forward and recomputes them on-the-fly during backward. The analogy: instead of photocopying every page of a book as you read it (so you can refer back later), you just remember the page numbers and re-read the pages when you need them. Slower, but requires almost no shelf space.
Memory savings: ~2-3x at cost of ~30% slower training (one extra forward pass per block).
Selective checkpointing: You do not have to checkpoint every layer. In practice, checkpointing every 2nd or 3rd block gives most of the memory benefit (~60-70%) with less speed penalty (~10-15%). Profile your specific model to find the sweet spot. The layers with the largest activations (usually the attention layers in Transformers) benefit most from checkpointing.

Data Parallel Training

DataParallel (Single Machine, Multi-GPU)

DistributedDataParallel (Preferred)


Model Parallelism

Split model across GPUs when it doesn’t fit on one:

Pipeline Parallelism

Tensor Parallelism

Split individual layers across GPUs (used in LLM training):

FSDP (Fully Sharded Data Parallel)

Shard model parameters, gradients, and optimizer states across GPUs:

DeepSpeed Integration


Efficient Data Loading

A surprisingly common bottleneck: your GPU sits idle waiting for data. If your data loading is slower than your model’s forward-backward pass, you are data-bound — upgrading your GPU will not help at all. The fix is to overlap data loading with computation using multiple worker processes.
Pitfall — num_workers on Windows: On Windows, multiprocessing uses spawn instead of fork, which is significantly slower to start workers. If you see long pauses at epoch boundaries, set persistent_workers=True (as above) and consider reducing num_workers to 4. Also, ensure your dataset code is picklable — lambdas in transforms will crash the data loader with a cryptic pickling error.

Training Recipe Summary


Exercises

Implement mixed precision training for a CNN. Measure memory usage and speed improvement.
Simulate a 256 batch size with 64 actual batch size using 4 accumulation steps.
Set up DistributedDataParallel training on 2 GPUs. Compare throughput with DataParallel.

What’s Next

Module 20: Transfer Learning & Fine-tuning

Leverage pretrained models effectively — feature extraction, fine-tuning, and adaptation.