AI-310 · Lesson 12 of 12 · 27 min
Distributed optimization and a training project
Large models are trained on hundreds or thousands of accelerators at once, and some models are trained on data that cannot leave the devices or organizations that hold it. Both settings turn optimization into a problem of computation, memory and communication as much as mathematics. This final lesson explains data, tensor and pipeline parallelism, the memory accounting of large-model training and how sharding reduces it, the cost of communication and how optimizers cope with very large batches, asynchronous and federated optimization, and finally works through a training project that draws on the whole course, from choosing an optimizer to diagnosing problems.
Data parallelism
In synchronous data parallelism, each of \(N\) workers holds a copy of the model, processes a different minibatch, computes its gradient, and then all workers average their gradients with an all-reduce operation before each applies the same update. Mathematically this is ordinary minibatch SGD (Lesson 3) with an \(N\) times larger batch. Its limits are:
- Communication: every step exchanges a full set of gradients. A ring all-reduce sends about \(2(N-1)/N\) times the gradient size per worker, so communication time is roughly constant in \(N\) but proportional to model size and inversely proportional to network bandwidth.
- Large batches: beyond the critical batch size, more workers stop reducing the number of steps needed (Lesson 3), so adding workers adds cost without speeding training.
- Memory: each worker must hold the whole model, its gradients and its optimizer state.
Implementations overlap communication with computation, sending gradients of later layers while earlier layers are still being backpropagated, and use high-bandwidth interconnects (DCE-320) to keep accelerators busy.
Memory and sharding
Training with Adam in mixed precision (Lessons 5 and 6) stores, for each parameter, 16-bit weights and gradients and 32-bit master weights and two moment estimates: about 16 bytes per parameter, before activations. A 70-billion-parameter model needs over a terabyte for these alone, far beyond any single accelerator.
# Memory per GPU for training a 70-billion-parameter model with Adam in mixed precision
params = 70e9
bytes_weights16, bytes_grads16 = 2, 2
bytes_master32, bytes_m, bytes_v = 4, 4, 4
per_param = bytes_weights16 + bytes_grads16 + bytes_master32 + bytes_m + bytes_v
print(f"bytes per parameter: {per_param}; total {params * per_param / 1e9:,.0f} GB before activations")
gpus = 64
stages = {
"plain data parallel": params * per_param,
"ZeRO-1 (shard optimizer states)": params * (4 + (4 + 4 + 4) / gpus),
"ZeRO-2 (+ shard gradients)": params * (2 + (2 + 4 + 4 + 4) / gpus),
"ZeRO-3 / FSDP (+ shard weights)": params * per_param / gpus,
}
for name, b in stages.items():
print(f"{name:34s}: {b / 1e9:8.1f} GB per GPU")
# Ring all-reduce time for the gradients of one step
grad_bytes = params * 2
bw = 400e9 / 8 # 400 Gb/s per GPU, in bytes per second
t = 2 * (gpus - 1) / gpus * grad_bytes / bw
print(f"ring all-reduce of {grad_bytes / 1e9:.0f} GB of gradients across {gpus} GPUs: {t:.1f} s at 400 Gb/s")bytes per parameter: 16; total 1,120 GB before activations plain data parallel : 1120.0 GB per GPU ZeRO-1 (shard optimizer states) : 293.1 GB per GPU ZeRO-2 (+ shard gradients) : 155.3 GB per GPU ZeRO-3 / FSDP (+ shard weights) : 17.5 GB per GPU ring all-reduce of 140 GB of gradients across 64 GPUs: 5.5 s at 400 Gb/s
Sharding splits these states across workers instead of replicating them. ZeRO (zero redundancy optimizer) stage 1 shards the optimizer states, stage 2 also the gradients, and stage 3 (also known as fully sharded data parallel, FSDP) the weights themselves, gathering each layer's weights just before they are needed and discarding them afterwards. Stage 3 cuts per-device memory by the number of workers, at the cost of more communication.
Model parallelism
When even sharded data parallelism is not enough, the model itself is split:
- Tensor parallelism splits individual matrix multiplications across devices, for example dividing a layer's weight matrix by columns, so that each device computes part of every layer; it needs very fast communication, usually within a server.
- Pipeline parallelism assigns consecutive layers to different devices and streams microbatches through them, like an assembly line. Idle time at the start and end of each batch (the pipeline "bubble") is reduced by using many microbatches.
- Expert parallelism places the experts of mixture-of-experts layers on different devices, routing each token to the devices holding its chosen experts.
- Sequence or context parallelism splits long sequences across devices.
Large training runs combine several: tensor parallelism within servers, pipeline parallelism across a few servers, and sharded data parallelism across the rest, a configuration often called 3D or 4D parallelism. Choosing it is an optimization problem over memory, compute and the network topology (AI-480 covers it in depth).
Optimizers for very large batches
To use more workers effectively, practitioners push batch sizes up, which requires higher learning rates and careful stabilization:
- Learning rate scaling with warm-up: linear scaling for SGD up to the critical batch size, with gradual warm-up to avoid early instability (Lesson 3).
- Layer-wise adaptive rates: LARS (for SGD with momentum) and LAMB (for Adam) scale each layer's update by the ratio of its weight norm to its update norm, so no layer takes a disproportionately large step. They enabled training with batches of tens of thousands of examples.
- Gradient clipping and loss-spike handling (Lesson 5): at scale, rare instabilities can derail weeks of training, so runs monitor gradient norms and loss, skip or roll back bad steps, and restart from checkpoints.
Worked problem
Is more data parallelism worth it?
A model trains in 100,000 steps at batch 512 on 64 GPUs. Its critical batch size is about 4,096. If the team moves to 512 GPUs at batch 4,096, and above the critical batch size the number of steps stops falling, roughly how many steps are needed and what is the speed-up? What about 2,048 GPUs at batch 16,384?
Show solution
- Up to the critical batch, steps fall roughly in proportion: \(100{,}000 \times 512/4{,}096 = 12{,}500\) steps; speed-up about 8 times with 8 times the GPUs.
- Beyond it, steps fall little: perhaps 10,000 to 12,000 steps at batch 16,384, with 32 times the original GPUs.
Answer: About an 8-fold speed-up for 8 times the GPUs up to the critical batch size; beyond it, 4 times more GPUs buy almost nothing. Measuring the critical batch size early saves large amounts of compute.
Asynchronous and decentralized optimization
Synchronous training waits for the slowest worker at every step. Asynchronous methods let workers update a shared model (on parameter servers) without waiting, applying gradients computed on slightly stale weights. They tolerate stragglers but add noise from staleness; with modest delays and step sizes, they converge like SGD. Decentralized methods replace the central average with exchanges between neighbors in a communication graph (gossip averaging). Methods that communicate less often, such as local SGD (several local steps between averages) and DiLoCo-style training across loosely connected data centers, trade some optimization efficiency for far less communication.
Federated learning
In federated learning, data stays on clients (phones, hospitals, banks) and only model updates are shared. Federated averaging (FedAvg) sends the current model to a sample of clients, each runs several local SGD steps on its own data, and the server averages the returned models. Local steps reduce communication rounds, the scarce resource, but client data are usually not identically distributed, so local models drift toward their own optima.
import numpy as np
rng = np.random.default_rng(6)
d, clients = 10, 10
w_true = rng.normal(size=d)
def make_client(shift):
X = rng.normal(size=(200, d)) + shift # each client sees a different input distribution
y = (X @ w_true + rng.normal(0, 0.5, 200) > 0).astype(float)
return X, y
data = [make_client(rng.normal(0, 1.0, d)) for _ in range(clients)]
Xall = np.vstack([X for X, _ in data]); yall = np.concatenate([y for _, y in data])
def loss(w, X, y):
z = X @ w
return np.mean(np.logaddexp(0, z) - y * z)
def grad(w, X, y):
return X.T @ (1 / (1 + np.exp(-X @ w)) - y) / len(y)
def fedavg(local_steps, rounds=30, lr=0.1):
w = np.zeros(d)
for _ in range(rounds):
updates = []
for X, y in data:
wl = w.copy()
for _ in range(local_steps):
idx = rng.choice(len(y), 32)
wl -= lr * grad(wl, X[idx], y[idx])
updates.append(wl)
w = np.mean(updates, axis=0) # the server averages client models
return w
for steps in (1, 5, 25):
w = fedavg(steps)
print(f"{steps:2d} local steps per round, 30 rounds: global loss {loss(w, Xall, yall):.4f}, communication rounds 30")
w = np.zeros(d)
for _ in range(30 * 25):
idx = rng.choice(len(yall), 32 * clients)
w -= 0.1 * grad(w, Xall[idx], yall[idx])
print(f"centralized SGD, same total updates: global loss {loss(w, Xall, yall):.4f}")1 local steps per round, 30 rounds: global loss 0.3477, communication rounds 30 5 local steps per round, 30 rounds: global loss 0.2016, communication rounds 30 25 local steps per round, 30 rounds: global loss 0.1204, communication rounds 30 centralized SGD, same total updates: global loss 0.1182
Here more local computation per round paid off, but with strongly heterogeneous clients, too many local steps make client models drift apart; methods such as FedProx and SCAFFOLD correct this drift. Federated learning is combined with secure aggregation, so the server sees only the sum of updates, and often with differential privacy (AI-220, Lesson 7), adding noise to updates to bound what can be learned about any one client.
Training project: putting the course together
Consider training a transformer language model of about 1 billion parameters on 100 billion tokens, on 64 accelerators. The steps show how the course's ideas combine:
- Objective and parameterization: next-token cross-entropy, a convex loss of the logits (Lesson 2); a parameterization such as μP so that hyperparameters tuned on small proxies transfer (Lesson 10).
- Optimizer: AdamW with \(\beta_1 = 0.9\), \(\beta_2 = 0.95\), weight decay 0.1 on matrices, gradient clipping at norm 1.0 (Lesson 5), in bfloat16 with float32 master weights (Lesson 6).
- Schedule: linear warm-up over about 2,000 steps, then cosine decay to 10% of the peak, or a constant rate with a final cooldown (Lesson 3).
- Batch size: chosen near the estimated critical batch size, perhaps 1 million tokens per step, giving about 100,000 steps (Lesson 3 and this lesson).
- Parallelism: sharded data parallelism (ZeRO-2 or FSDP) across 64 accelerators, with activation checkpointing if memory is tight (Lesson 6 and this lesson).
- Hyperparameters: peak learning rate and weight decay tuned with random search and early stopping on a 100-million-parameter proxy (Lesson 10).
Worked problem
Sizing the run
Training compute is roughly \(6ND\) floating-point operations for \(N\) parameters and \(D\) tokens. With \(N = 10^9\), \(D = 10^{11}\), and 64 accelerators each sustaining \(3 \times 10^{14}\) FLOP/s (about 40% of peak), how long does training take?
Show solution
- Compute: \(6 \times 10^9 \times 10^{11} = 6 \times 10^{20}\) FLOP.
- Throughput: \(64 \times 3 \times 10^{14} = 1.92 \times 10^{16}\) FLOP/s.
- Time: \(6 \times 10^{20}/1.92 \times 10^{16} = 31{,}250\) s, about 8.7 hours.
Answer: About 9 hours, a manageable run. The same arithmetic shows why frontier models, with 100 to 1,000 times more parameters and tokens, need tens of thousands of accelerators for months.
Diagnosing training
During training, the curves tell what is happening:
- Loss spikes: often from learning rates that are too high for current sharpness (Lessons 1 and 9), or from bad data batches. Remedies: lower peak rate, longer warm-up, stronger clipping, skipping or rolling back.
- Divergence to NaN: overflow in low precision or exploding gradients. Check loss scaling (float16), clipping, and layer normalization placement.
- Plateau early in training: learning rate too low, poor initialization, or a bug; a learning-rate range test and a gradient check on a small model help (Lessons 6 and 10).
- Training loss falling, validation loss rising: overfitting, so more data, regularization or earlier stopping (Lesson 8).
- Slow throughput: communication bottlenecks or poor overlap; profile computation and communication separately.
Each diagnosis connects back to a principle: curvature and step size, noise and batch size, adaptivity and stability, regularization and generalization. Those principles, not any single recipe, are what let an engineer train a new model successfully.
Common mistakes
MistakeAdding workers far beyond the critical batch size.
InsteadMeasure the critical batch size; beyond it, more data parallelism wastes compute.
MistakeReplicating optimizer states on every worker for large models.
InsteadShard them with ZeRO or FSDP.
MistakeIgnoring communication time.
InsteadEstimate all-reduce time from model size and bandwidth; overlap it with computation.
MistakeRunning many local steps on very heterogeneous federated clients.
InsteadLimit local steps or use drift-correcting methods such as SCAFFOLD.
MistakeStarting a large run without proxy experiments.
InsteadTune on small proxies with transferable parameterizations and check scaling first.
Summary and key terms
- Data parallelism averages gradients with all-reduce; it is SGD with a larger batch.
- Adam in mixed precision needs about 16 bytes per parameter; ZeRO/FSDP shard it across workers.
- Tensor, pipeline, expert and sequence parallelism split the model itself.
- Large-batch training needs scaled learning rates, warm-up, LARS/LAMB and stability monitoring.
- Asynchronous, local and federated methods trade optimization efficiency for less communication.
- A training project combines objective, optimizer, schedule, batch size, parallelism, tuning and diagnosis.
- Data parallelism
- Replicating the model and splitting the batch across workers.
- All-reduce
- A collective operation that sums or averages data across workers.
- ZeRO
- Zero redundancy optimizer: sharding optimizer states, gradients and weights.
- FSDP
- Fully sharded data parallel training.
- Tensor parallelism
- Splitting individual layers' computations across devices.
- Pipeline parallelism
- Assigning consecutive layers to different devices.
- Pipeline bubble
- Idle time at the start and end of a pipelined batch.
- LAMB
- A layer-wise adaptive large-batch variant of Adam.
- Federated averaging
- Averaging models trained locally on clients' data.
- Client drift
- Divergence of local models trained on non-identical data.
Practice problems
Work each problem on paper first, then reveal the answer to check.
Bytes per parameter for Adam in mixed precision?
Show answer
About 16.
Optimizer and weight memory for a 7B model at 16 bytes per parameter?
Show answer
112 GB.
Per-GPU memory for that model with ZeRO-3 on 16 GPUs?
Show answer
7 GB.
Ring all-reduce data per worker for 10 GB of gradients on 8 workers?
Show answer
2 × 7/8 × 10 = 17.5 GB.
Which parallelism needs the fastest interconnect?
Show answer
Tensor parallelism.
What reduces the pipeline bubble?
Show answer
Using many microbatches.
What does LARS scale per layer?
Show answer
The update by the ratio of weight norm to update norm.
Why do federated methods use several local steps?
Show answer
To reduce the number of communication rounds.
Training FLOPs for N = 3×10⁸ and D = 6×10⁹?
Show answer
6 × 3×10⁸ × 6×10⁹ ≈ 1.1 × 10¹⁹.
First check for a run that diverges to NaN in float16?
Show answer
Loss scaling and gradient clipping.
Lesson quiz · 5 questions
Score 60% or more to complete this lesson.
Create a free account to save your progress, take quizzes and earn a certificate.