Single-GPU training hits wall-clock limits on large datasets and models. Distributed training splits work across devices—most commonly data parallelism (DDP), where each GPU runs the same model on different batches and synchronizes gradients.
This is the capstone of Module 6.2: every prior lesson (loops, AMP, clipping) still applies inside each rank’s process.
Learning Objectives
By the end of this lesson, students should be able to:
- Explain data parallelism vs model parallelism at a high level.
- Initialize
torch.distributedand wrap models withDDP. - Use
DistributedSamplerso each rank sees unique batches. - Restrict checkpointing and logging to rank 0.
- Launch jobs with
torchrun(multi-process per node). - Estimate near-linear speedup limits from communication overhead.
Data Parallel vs Model Parallel
| Strategy | Idea | Typical use |
|---|---|---|
| Data Parallel (DDP) | Replica per GPU, split batches, sync gradients | Most CV/NLP training |
| Model Parallel | Split layers across devices | Models too large for one GPU |
| Pipeline / FSDP | Shard parameters across ranks | Very large LLMs (advanced) |
DDP Setup Pattern
One process per GPU. Initialize process group, bind device, wrap model with DDP, use distributed sampler.
Launch with torchrun
torchrun spawns one process per GPU and sets RANK, LOCAL_RANK, and WORLD_SIZE environment variables.
set_epoch on the SamplerWithout train_sampler.set_epoch(epoch), shuffling repeats identically every epoch across ranks—hurting convergence. Always call it at the start of each epoch.
DDP vs Deprecated DataParallel
nn.DataParallel (single process, multiple GPUs) is simpler but slower due to GIL and gradient gather on one GPU. Use DDP for multi-GPU training in production.
Accessing the Underlying Model
DDP wraps the module. For validation/saving on rank 0, use model.module.state_dict(), not model.state_dict() (keys include module. prefix).
Document: per-GPU batch × GPUs × accumulation = global batch. Learning rate schedules often depend on global batch, not per-device batch.
Knowledge Check
- Short Answer: What does DDP synchronize? Answer: Gradients across processes after backward.
- True/False: Each DDP rank should save checkpoints independently. Answer: False—usually rank 0 only.
- Multiple Choice:
DistributedSamplerensures: (a) same batches on all GPUs, (b) unique shards per rank, (c) no shuffling. Answer: (b). - Short Answer: Why call
set_epochon the sampler? Answer: Different shuffle seed each epoch per rank. - Short Answer: How to launch 8 processes on one node? Answer:
torchrun --nproc_per_node=8 train.py. - True/False: DDP is slower than DataParallel for training. Answer: False—DDP is generally faster and preferred.
- Multiple Choice: Save weights from DDP model via: (a)
model.module.state_dict(), (b)model.cuda(), (c) rank 3 only. Answer: (a) on rank 0. - Short Answer: Effective batch with 4 GPUs, batch 32 each? Answer: 128 (without gradient accumulation).
- Short Answer: When is model parallelism needed? Answer: Model does not fit on one GPU’s memory.
- Multiple Choice: Backend for NVIDIA GPU training: (a) gloo, (b) nccl, (c) mpi only. Answer: (b).
Key Takeaways
- DDP: one process per GPU,
DistributedSampler, gradient sync, rank-0 logging. - Launch with
torchrun; callset_epocheach epoch. - Scale global batch and LR together; save
model.moduleweights. - Next module: 6.3 CPU vs GPU—hardware foundations.
Hands-on idea: Train MNIST 1 vs 2 GPU; measure speedup and discuss overhead below 2×.
Discussion prompt: What breaks first when scaling to 64 GPUs—compute, communication, or data loading?