Chapter 3: Distributed Training Techniques
Introduction
Training LLMs with billions of parameters requires distributing work across many GPUs. This chapter covers the key parallelism strategies.
Data Parallelism
The Simplest Approach
import torch.distributed as dist
from torch.nn.parallel import DistributedDataParallel
# Each GPU processes different data, same model
model = DistributedDataParallel(model)
Limitation: Model must fit on single GPU
Model Parallelism
Tensor Parallelism
Split individual layers across GPUs:
[Layer Weight Matrix]
↓ Split columns
[GPU 0: W[:, :d/2]] [GPU 1: W[:, d/2:]]
Pipeline Parallelism
Different layers on different GPUs:
GPU 0: Layers 0-11
GPU 1: Layers 12-23
GPU 2: Layers 24-35
GPU 3: Layers 36-47
ZeRO: Zero Redundancy Optimizer
Three Stages
| Stage | What's Partitioned | Memory Savings |
|---|---|---|
| ZeRO-1 | Optimizer states | 4x |
| ZeRO-2 | + Gradients | 8x |
| ZeRO-3 | + Parameters | Linear with GPUs |
Implementation with DeepSpeed
import deepspeed
model_engine, optimizer, _, _ = deepspeed.initialize(
model=model,
config={
"zero_optimization": {
"stage": 3,
"offload_optimizer": {"device": "cpu"},
"offload_param": {"device": "cpu"}
},
"bf16": {"enabled": True}
}
)
Training Compute
Chinchilla Scaling
Optimal training: approximately 20 tokens per parameter
# Compute-optimal model size
def optimal_model_size(compute_budget):
# C ≈ 6 * N * D (N=params, D=tokens)
# Optimal: D ≈ 20N
return compute_budget / (6 * 20)
Key Takeaways
Summary
- Data parallelism for small models
- Model parallelism when model doesn't fit on one GPU
- ZeRO dramatically reduces memory requirements
- Mixed precision (bf16) speeds training