Scaling Laws & Modern Training
- Kaplan et al. (2020) discovered that language model loss follows predictable power laws as compute, data, and parameters scale
- Chinchilla (Hoffmann et al. 2022) showed most LLMs were undertrained - optimal compute splits equally between model size and training tokens
- Scaling laws guide the entire LLM development roadmap: when to scale up, how to allocate compute budgets
Scaling laws describe how model performance improves predictably as you increase model size (parameters), training data (tokens), and compute (FLOPs). Each follows a smooth power law. The laws allow extrapolating how large a model needs to be to reach a target capability before training it.
- Directly informs LLM architecture decisions, training duration, and dataset size requirements
- Emergent abilities (few-shot learning, chain-of-thought reasoning) appear at specific scale thresholds
- Connects to neural scaling, mixture-of-experts (MoE), and the efficiency-performance frontier
- State the Kaplan 2020 power-law L(N, D) and explain why loss is monotone-decreasing in compute on the iso-loss frontier.
- Derive the Chinchilla compute-optimal allocation from C = 6 N D and a loss-power-law, and explain why the Kaplan recipe under-trained models like GPT-3.
- Compare data, tensor (Megatron), and pipeline (GPipe) parallelism on memory, communication volume, and the step at which each pays its cost.
- Walk through the Adam memory budget (about 20 bytes per parameter at ZeRO-0) and how ZeRO-1, ZeRO-2, and ZeRO-3 partition that budget across data-parallel ranks.
- Explain mixed precision (FP16, BF16, FP8) with master FP32 weights and dynamic loss scaling, and the mechanism behind the FP16 training-loss explosion.
TL;DR
Modern LLM training is shaped by two intertwined laws: a scaling law that predicts loss from compute and a systems budget that decides how many GPUs you need. Kaplan et al. [1] showed that loss falls as a clean power law in parameters , data , and compute . Hoffmann et al. [2] later corrected the recipe: at a fixed compute budget , the compute-optimal model has roughly , so most pre-Chinchilla models (GPT-3 included) were several times smaller in data than they should have been. Training trillion-parameter models requires three orthogonal parallelism axes (data, tensor [3], and pipeline [4]) plus ZeRO sharding of optimizer state, gradients, and parameters across data-parallel ranks [5]. Mixed precision (FP16 or BF16 forward and backward, FP32 master weights, dynamic loss scaling) and gradient checkpointing complete the modern training stack. The 2023-2024 open-weights wave (LLaMA-2, Mistral, Mixtral, Qwen, DeepSeek-V2, Phi-3) is a tour through the trade-offs this whole stack opens up.
Intuition
Training a frontier LLM is a three-way trade between compute, data, and parameters. Spend all your compute on a tiny model and you under-fit. Spend all of it on parameters and you over-fit, or run out of data. Spend it on a moderate-size model trained for a long time and you sit on the compute-optimal frontier [14]. The scaling-law literature is the empirical map of that frontier: how loss falls as you spend more, and which axis pays the most.
For most of 2018 to 2022 the consensus from Kaplan 2020 was “make the model bigger; data scales sub-linearly”. Hoffmann et al. ran a broader sweep with the right hyperparameter retuning per model size and found the opposite: data scales as fast as parameters. The whole field shifted in a year. Modern open-weights LLMs (LLaMA-2 [8], Mistral [9], Qwen [11], DeepSeek-V2 [12]) train past the Chinchilla optimum on purpose, because at inference time the cheaper, smaller, longer-trained model wins on tokens-per-dollar.
- 2017Hestness et al.: deep-learning scaling is predictablepaper
A Baidu paper shows that translation, language-modelling, and image-classification loss all fall as smooth power laws in dataset size, holding architecture and compute fixed. Plants the empirical seed for scaling laws.
- 2020Kaplan 2020 scaling lawspaper
Clean joint fit of loss versus parameters, data, and compute on dense decoder-only Transformers. Predicts compute-optimal N grows roughly as C^0.73 and D as C^0.27. Becomes the planning law for GPT-3.
- 2022Hoffmann et al. (Chinchilla)paper
Per-size hyperparameter retuning and a wider sweep find compute-optimal N and D scale at near-equal exponents. Chinchilla 70B beats Gopher 280B at the same compute, validating the new prescription D approx 20 N.
- 2023LLaMA-2: deliberately past Chinchillarelease
LLaMA-2 7B and 70B both train on 2T tokens. The 7B is more than 10x past Chinchilla; the bigger training compute is paid back in cheaper inference at deployment.
- 2024MoE re-emerges at scalearchitecture
Mixtral 8x7B and DeepSeek-V2 ship sparse mixture-of-experts open weights, decoupling active parameters per token from total parameter count. The training stack that supports them is the modern 3D parallelism + ZeRO + mixed-precision recipe end to end.
Once you fix the GPU-hours, scaling laws decide the shape of the model that wins:
- Compute (FLOPs, dollars, GPU-hours) is the budget you bring to the table.
- Parameters are how big a function the model can represent.
- Data are how many tokens you push through that function.
Kaplan said “spend most of on , training data scales slowly”. Chinchilla said “no, and should grow at roughly equal rates”. Both fit the same identity ; they disagreed on where on the iso-compute curve the loss minimum sits. The Chinchilla position has held up since 2022 and is the default planning assumption for any new training run.
Compute, parameters, and data: the FLOP identity
Before any scaling law you need a flat-FLOP accounting. Per training token, a dense Transformer does roughly forward FLOPs and backward FLOPs (one re-evaluate plus one gradient matmul of the same size as forward), giving the textbook approximation
where is total training FLOPs, is parameter count, and is the number of tokens trained on. The factor is a rule of thumb: it ignores the attention term (relevant only at very long context) and small heads like the embedding matrix. Plug in GPT-3: , . Then FLOPs, which matches the FLOPs reported in the GPT-3 paper. Hold this identity in your head; every scaling-law derivation rides on it.
Kaplan 2020 scaling laws
Kaplan 2020: the loss-versus-(N, D, C) power law
Kaplan et al. [1] trained dense decoder-only Transformers across seven orders of magnitude in compute and fit clean power laws to the test loss. Their headline result is
with empirically fit constants , , , , and an irreducible loss (in nats per token on WebText2). Three properties matter:
Power-law in each axis. With one axis held fixed and the other (and compute) sufficient, loss decays as a power of the active axis. Doubling at fixed shaves off roughly on the relative loss; doubling at fixed shaves off . Power-law decay is slow but predictable: a increase in buys on the relevant term.
Iso-loss contours are straight lines in log-log. Setting and rearranging gives a curve versus that is approximately linear. Move along an iso-loss contour and you trade parameters for data at a fixed cost.
Compute-loss is itself a power law. Combine with and minimize over subject to the FLOP constraint. Kaplan’s fit gave compute-optimal and , which translates to a strong recommendation: spend most of any extra compute on parameters, not on more data. This was the basis for the GPT-3 budget choice (175B parameters, 300B tokens, ratio ).
The Kaplan recipe is internally consistent given the data they collected. The trouble, as Hoffmann would show two years later, is that they trained at suboptimal hyperparameters for the largest models in their sweep, and the fit at the bottom of the sweep was unreliable. The corrected fit changes the exponents and flips the prescription.
The Kaplan exponents motivate two practical tricks. First, you can predict loss before training by fitting the law on small models and extrapolating. The GPT-3 budget was set this way: extrapolate the law, see that FLOPs land you near , allocate the cluster. Second, the law gives a sharp diagnostic for “is this run on track”: if at compute your loss is above the predicted , something is wrong (a hyperparameter, a data issue, a numerical bug) and another epoch will not save you.
Chinchilla: compute-optimal training
Chinchilla: deriving the optimal N* / D* allocation
Hoffmann et al. [2] repeated the Kaplan exercise but with a per-model-size hyperparameter sweep, broader range, and Approach-3 parametric fitting: a single joint power law of the form
with , , , , . The constants are not the headline; the prescription is. Constrain by and minimize over :
Form the Lagrangian and set partials to zero. The first-order conditions give
which on substitution of the constraint yields the optimal scaling
Plugging in , gives , . Hoffmann’s three approaches all triangulate to a remarkably clean rule of thumb on real LLM scales:
So a 10B-parameter model is compute-optimal at about 200B tokens. A 70B at about 1.4T tokens, which is exactly the budget of the original Chinchilla 70B run (and roughly LLaMA-2-70B’s recipe). For comparison, Kaplan’s prescription would have suggested under 200B tokens for a 70B model. Run Chinchilla 70B versus Gopher 280B at the same compute, and Chinchilla wins on every benchmark.
Why Kaplan under-trained. Two issues. First, Kaplan held learning-rate cosine-schedule constants fixed across model sizes; the actual optimum needs per-size tuning, and the larger Kaplan models trained at sub-optimal hyperparameters. Second, the original fit was on a narrower model-size range; extrapolating power-law exponents from 100M-1B up to 175B is less reliable than fitting jointly across the whole range. Chinchilla redid the experiment, found the joint-fit exponents were closer to equal, and changed the field’s planning assumption overnight.
The Chinchilla optimum is a guideline for the compute-frontier run. In practice, modern open-weights LLMs train past the optimum on purpose. LLaMA-2 7B sees 2T tokens at , more than past Chinchilla. The reason is inference cost: a 7B model is much cheaper to serve than a 70B, and since serving accounts for the vast majority of total project cost, paying extra training compute to ship a smaller deployable model is a clear win [8]. The Chinchilla rule names the training-loss-optimal point; the inference-economics-optimal point is to the right of it.
Training infrastructure: the three parallelism axes
A 70B-parameter model in BF16 needs GB just for weights, and Adam adds another on top of that during training. No single GPU has that memory. The three orthogonal parallelism axes are how a modern cluster fits a model that does not fit on one chip [14].
Data parallelism (gradient all-reduce)
Replicate the entire model on every GPU, split the batch across GPUs. Each rank computes its forward and backward on its slice of the batch and produces local gradients; the optimizer step needs the average across all ranks, so an all-reduce sums and broadcasts the gradients before the update.
Cost. Memory per GPU is unchanged (every rank has a full copy). Communication per step is in the parameter count: each rank sends and receives roughly bytes (in FP16) per step under ring-all-reduce. The bandwidth is hidden behind compute when batch-per-GPU is large enough; data parallelism is the cheapest way to scale as long as the model fits on one GPU. Data-parallel training is the default when memory permits and is the only axis that does not change the per-step compute pattern. The all-reduce primitive predates the deep-learning era; the connection to gradient descent is just “add the per-shard gradients and divide by the number of shards” [14].
Tensor parallelism (Megatron, splitting one matmul)
When the model itself does not fit, split a single matrix multiply across GPUs. Megatron-LM [3] partitions the per-layer weights along a chosen dimension and synchronizes only at the boundaries.
For a Transformer block, the natural cuts are the projections and the FFN. Take the FFN: with , . Megatron splits column-wise across GPUs (each rank holds columns), runs the GeLU locally without communication, then splits row-wise (each rank holds rows). The output is a partial sum on each rank; one all-reduce at the FFN exit gives the full output. Two all-reduces per Transformer block: one after FFN, one after attention. Communication scales with hidden size per token, not with parameter count.
Tensor parallelism works inside one node (NVLink or NVSwitch, 600+ GB/s). Across nodes the all-reduce becomes the bottleneck; modern recipes cap tensor-parallel size at (one node) and stack other axes on top.
Pipeline parallelism (GPipe, splitting the layer stack)
Split the layer stack across GPUs: ranks 0 hold layers 0-15, rank 1 holds 16-31, and so on. A microbatch flows through the pipeline rank by rank during forward, then gradients flow back during backward. Naive layer-split has a “bubble” where downstream ranks idle until the first microbatch arrives.
GPipe [4] fixes this with microbatching: split the global batch into microbatches and feed them through the pipeline back to back. With stages and microbatches the pipeline bubble is of the step time. With the bubble is below , which is the practical sweet spot.
Cost. Memory per GPU is of the full model (each stage holds one -th of the layers). Communication is point-to-point activation passes between adjacent stages; under interleaved 1F1B schedules the volume is per stage. Pipeline parallelism slots cleanly between nodes when tensor parallelism saturates a single node.
The standard 3D recipe is TP PP DP: tensor-parallel inside a node (), pipeline-parallel across nodes ( stages), data-parallel on top ( replicas). Megatron-Turing-NLG-530B trained at , , on 2240 A100s.
| Axis | What is split | Memory per GPU | Communication per step | Where it slots in |
|---|---|---|---|---|
| Data parallel | Batch dimension; full model replicated | Same as single-GPU model | All-reduce of gradients, O(N) bytes per step | Cheapest axis; default until memory fails |
| Tensor parallel (Megatron) | A single matmul, along one dim | About 1 / T of the model | Two all-reduces per Transformer block, O(seq * d) per step | Inside one node (NVLink), T at most 8 |
| Pipeline parallel (GPipe) | Layer stack across stages | About 1 / P of the model | Point-to-point activations between adjacent stages | Across nodes; needs microbatching to hide the bubble |
| ZeRO sharding | Optimizer / gradients / parameters across DP ranks | Reduced by factor up to N_DP | Adds an extra all-gather per step at ZeRO-3 | On top of data parallel; orthogonal to TP / PP |
ZeRO: sharding the optimizer state
ZeRO stages 1, 2, and 3 with the Adam memory budget
The largest single line item in LLM training memory is not the weights, it is the Adam optimizer state. Walk the budget per parameter under standard mixed-precision Adam:
- FP16 weights (forward and backward use these): bytes per parameter.
- FP16 gradients (accumulated during backward): bytes per parameter.
- FP32 master weights (the high-precision copy that the optimizer updates): bytes per parameter.
- FP32 first moment (Adam momentum): bytes per parameter.
- FP32 second moment (Adam variance): bytes per parameter.
- FP32 gradient buffer (used during the optimizer step): bytes per parameter.
Total: bytes per parameter of “optimizer state”, plus bytes per parameter of weights and gradients in FP16, giving about bytes per parameter at standard ZeRO-0. For a 7B model that is GB per GPU, before activations. No amount of FP16 helps; the FP32 master state dominates.
Rajbhandari et al. [5] noticed that across data-parallel ranks, every rank holds an identical copy of all this state. ZeRO partitions the state across ranks instead of replicating it.
ZeRO-1 (Optimizer state). Partition the bytes per parameter of optimizer state across the DP ranks. Each rank holds bytes per parameter. Weights and gradients still replicated. Per-GPU memory: bytes per parameter. Communication: same all-reduce on gradients, plus a final scatter of the updated weights from each rank’s slice of the optimizer state.
ZeRO-2 (Optimizer state + gradients). Also partition gradients across ranks. Per-GPU memory: bytes per parameter (FP16 weights still replicated; the counts the FP16 gradient plus the FP32 buffer plus the optimizer state). Communication: replace the gradient all-reduce with a reduce-scatter (each rank ends up with its slice of the summed gradient), then the local optimizer step, then an all-gather of weights at the next forward.
ZeRO-3 (Optimizer + gradients + parameters). Partition the FP16 weights too. Per-GPU memory: bytes per parameter. Each layer’s weights are gathered just-in-time from peers before forward and freed afterward, then re-gathered on backward. Communication: doubles the all-gather count compared to ZeRO-2 because forward also needs weight gathering. At infinite , per-GPU memory tends to zero in the parameter axis; in practice ZeRO-3 lets you train a 100B+ dense model on GPUs each with GB.
Worked example: 7B parameters on 8 GPUs. With :
- ZeRO-0: GB per GPU. Does not fit on GB.
- ZeRO-1: GB per GPU.
- ZeRO-2: GB per GPU.
- ZeRO-3: GB per GPU.
ZeRO-2 is the typical sweet spot for 7B-class models on a single 8-GPU node: GB per rank leaves room for activations and a generous batch. ZeRO-3 is required only when the model itself does not fit on one GPU.
The cost of ZeRO is communication. ZeRO-1 adds essentially no extra traffic versus standard data parallelism. ZeRO-2 swaps one all-reduce for a reduce-scatter plus an all-gather of weights at next forward, which is bandwidth-equivalent. ZeRO-3 adds an all-gather of weights at every layer’s forward and backward, doubling the per-step communication. Practitioners pick the smallest stage that fits the model: ZeRO-1 if you can, ZeRO-2 if you cannot, ZeRO-3 only if the parameters themselves do not fit. DeepSpeed and PyTorch FSDP both implement the full ladder.
Mixed precision: FP16, BF16, and FP8
Training in FP32 doubles memory and halves compute throughput on Tensor Cores. Modern training is mixed precision: forward and backward in a low-precision format (FP16 or BF16), optimizer state and master weights in FP32 [6].
FP16. Has exponent bits and mantissa bits, giving to as the representable positive range. Activations and gradients near the small end of this range round to zero (an “underflow”); near the large end they overflow to infinity. The latter is silently catastrophic in deep stacks. The standard fix is dynamic loss scaling: multiply the loss by a large constant before the backward pass, so the gradients are times larger and stay in the FP16 range, then unscale before the optimizer step. If any unscaled gradient is non-finite, halve and retry; otherwise periodically double to track the largest representable gradient.
BF16. Has exponent bits (matching FP32) and mantissa bits. The wider exponent range means no loss scaling needed: gradients that would underflow in FP16 are still representable in BF16. The price is fewer mantissa bits, so individual operations are noisier. In practice BF16 trains modern LLMs with no quality loss versus FP32 and is the default on A100, H100, and TPUs.
FP8. Has either exponent + mantissa (E4M3, for forward) or exponent + mantissa (E5M2, for backward) bits. Halves memory and doubles throughput again versus BF16. Requires per-tensor scaling factors and is currently used selectively (linear layers) rather than across the whole model. The H100 Transformer Engine and recent H200 / B200 launches lean heavily on FP8 for the next throughput jump.
| Format | Bits (sign / exp / mantissa) | Representable range | Loss scaling required? | Throughput on H100 vs FP32 | Where it is used |
|---|---|---|---|---|---|
| FP32 | 1 / 8 / 23 | About 1.4e-45 to 3.4e38 | No | 1x | Master weights and optimizer state always; baseline |
| FP16 | 1 / 5 / 10 | About 6e-5 to 6.5e4 | Yes (dynamic) | About 4x | V100-era forward and backward; legacy inference |
| BF16 | 1 / 8 / 7 | Same exponent range as FP32, lower mantissa precision | No | About 4x | Default for forward and backward on A100, H100, and TPUs |
| FP8 E4M3 | 1 / 4 / 3 | About 1.6e-3 to 4.5e2 | Yes (per-tensor) | About 8x | Forward GEMMs and activations on H100 Transformer Engine |
| FP8 E5M2 | 1 / 5 / 2 | About 1.5e-5 to 5.7e4 | Yes (per-tensor) | About 8x | Backward GEMMs (wider exponent range for gradients) |
| INT8 | 1 / -- / -- | Integer, scale + zero-point per tensor | Per-tensor calibration | About 8x | Post-training and quantization-aware inference; some training research |
The interaction with the optimizer matters. Adam’s variance term accumulates the square of the gradient, which can underflow even in BF16 if you store it there. Standard practice is to keep (and , and the master weights) in FP32 regardless of the forward-pass precision. This is the ” FP32 bytes per parameter” line in the ZeRO budget above.
The single most reproducible failure mode of FP16 training is the loss explosion: training proceeds normally for thousands of steps, then loss spikes to or NaN in one or two steps. The mechanism is almost always the same: a gradient with magnitude near the FP16 max value () overflows to infinity at one rank, the all-reduce contaminates the global gradient with infinity, the optimizer step writes infinity into the master weights, and from there NaN propagates everywhere on the next forward.
Fix. Use BF16 if the hardware supports it (any A100 or newer). If you are on V100 / FP16, ensure the loss-scaling controller is doing its job (logs should show adjusting up and down on a regular cadence) and add gradient clipping with a global L2 norm of to as a hard floor against numerical pathology [6].
Gradient checkpointing
Forward activations are the second-largest memory line item after Adam state. A Transformer block at , , batch stores MB per layer in BF16; for layers that is GB per sample.
Chen et al. [7] showed how to trade compute for memory: keep only a subset of the activations, and recompute the others on the backward pass. The optimal recomputation strategy stores activations every layers, giving total memory at the cost of one extra forward pass per backward (a compute overhead).
In practice the call is tactical: apply checkpointing to the layers you cannot fit otherwise, leave the rest un-checkpointed. Modern frameworks expose this as torch.utils.checkpoint.checkpoint_sequential or torch.utils.checkpoint.checkpoint, which wraps a forward function and re-runs it on demand. Combining ZeRO-3 sharding with selective checkpointing is the standard recipe for training B-parameter models on commodity 80 GB GPUs.
Learning rate, batch size, and warmup at scale
A scaling-law plot tells you what target loss is reachable, not how to actually drive a training run there. The optimization-side recipe has three knobs that interact: peak learning rate, batch size, and the warmup-plus-decay schedule.
Peak learning rate. Scales inversely with model width. The Kaplan and Chinchilla papers both used a peak around for AdamW on 100M-class models and dropped it for the largest sizes. As a rule of thumb the peak learning rate at width scales as in the original Vaswani schedule and as under maximal-update parameterization (muP). Most production runs sit between these and use empirical sweeps at small scale, then transfer to the full run.
Global batch size. Modern LLMs train at very large global batch sizes: GPT-3 used M tokens per step, LLaMA-2 used about M, and Chinchilla used M scaling to M. Big batches reduce the number of optimizer steps to reach a target loss but eventually hit a “critical batch size” past which doubling the batch buys less than half a step of progress. The critical batch size grows during training, so warmup batch-size schedules are common at the very top of the scale.
Warmup and decay. A linear warmup over the first of steps, followed by a cosine decay to of peak, is the modern default. Warmup is required because the optimizer’s variance estimate is noisy at step 0; running at peak learning rate before stabilizes blows up the parameter trajectory. The cosine decay tail is empirical: a flat-then-cosine schedule fits final loss curves better than a constant-then-step decay, with no clear theory [14].
The 3D-parallel + ZeRO + mixed-precision stack changes which of these knobs is easy to vary. Global batch size is set by the data-parallel degree times microbatch times pipeline-microbatches; halving the DP degree halves the global batch but doubles the per-rank microbatch, which shifts the activation memory budget. Practitioners pick global batch first, then choose parallelism degrees that produce that batch at the per-rank memory ceiling.
Wiring it up: PyTorch FSDP and DeepSpeed ZeRO
The two production training frameworks for ZeRO-style sharding are PyTorch FSDP (built into torch.distributed) and DeepSpeed (the original ZeRO implementation from [5]). Both expose roughly the same configuration surface: pick a stage, pick the precision, pick a checkpoint policy.
import torch
import torch.distributed as dist
from torch.distributed.fsdp import (
FullyShardedDataParallel as FSDP,
ShardingStrategy,
MixedPrecision,
CPUOffload,
)
from torch.distributed.fsdp.wrap import transformer_auto_wrap_policy
from transformers.models.llama.modeling_llama import LlamaDecoderLayer
from functools import partial
dist.init_process_group(backend="nccl")
torch.cuda.set_device(dist.get_rank() % torch.cuda.device_count())
model = build_llama_7b() # your nn.Module
# ZeRO-3 equivalent: SHARD_GRAD_OP shards optimizer + gradients;
# FULL_SHARD also shards parameters (true ZeRO-3).
mp = MixedPrecision(
param_dtype=torch.bfloat16,
reduce_dtype=torch.bfloat16,
buffer_dtype=torch.bfloat16,
)
wrap_policy = partial(
transformer_auto_wrap_policy,
transformer_layer_cls={LlamaDecoderLayer},
)
model = FSDP(
model,
sharding_strategy=ShardingStrategy.FULL_SHARD, # ZeRO-3
mixed_precision=mp,
auto_wrap_policy=wrap_policy,
cpu_offload=CPUOffload(offload_params=False),
device_id=torch.cuda.current_device(),
)
opt = torch.optim.AdamW(model.parameters(), lr=3e-4, betas=(0.9, 0.95))
for batch in loader:
out = model(batch.input_ids, labels=batch.labels)
out.loss.backward()
opt.step()
opt.zero_grad(set_to_none=True)The choice between FSDP and DeepSpeed is mostly ergonomic. FSDP integrates more cleanly with vanilla PyTorch training loops and the wider PyTorch ecosystem. DeepSpeed has older, more battle-tested implementations of CPU and NVMe offload (ZeRO-Infinity), MoE layer support, and a broader set of debugging tools. Both produce identical training loss curves on the same configuration; both inherit the trade-offs [5].
The 2023-2024 open-weights landscape
The practical consequences of all this stack show up in the open-weights LLM lineage of 2023 and 2024. Each model below sits at a different point on the (Chinchilla, inference-cost, license) trade-space.
LLaMA-2 7B and 70B trained on T tokens each, the same data budget regardless of model size [8]. The 70B is on the Chinchilla optimum at ; the 7B is way past at , paying extra training compute for a smaller serving model. Mistral 7B keeps the same 7B parameter count but adds grouped-query attention and sliding-window attention of tokens, dropping inference cost while matching or beating LLaMA-2 13B on most benchmarks [9]. Mixtral 8x7B is the same lineage gone MoE: experts of B-FFN each, top- routing, B active parameters per token but B total parameters in memory [10]. Qwen 72B trained on T tokens with a similar architecture to LLaMA-2 [11]. DeepSeek-V2 took the MoE idea further: B total parameters, B active per token, T training tokens, and a novel multi-head latent attention to compress the KV cache [12]. Phi-3-mini is the contrarian point: B parameters trained on T heavily filtered “textbook-quality” tokens, deliberately past Chinchilla but tuned for on-device inference [13].
| Model | Params (active / total) | Training tokens | Compute (approx FLOPs) | Architecture notes | License |
|---|---|---|---|---|---|
| LLaMA-2 7B | 7B | 2.0T | ~8e22 | RMSNorm + SwiGLU + RoPE; multi-head attention | LLaMA-2 community |
| LLaMA-2 70B | 70B | 2.0T | ~8e23 | GQA with 8 KV heads; otherwise LLaMA-2 stack | LLaMA-2 community |
| Mistral 7B | 7B | undisclosed | ~1e23 (estimate) | GQA + 4096-token sliding-window attention | Apache 2.0 |
| Mixtral 8x7B | 13B active / 47B total | undisclosed | ~6e23 (estimate) | MoE with 8 experts, top-2 routing | Apache 2.0 |
| Qwen 72B | 72B | 3.0T+ | ~1.3e24 | LLaMA-style stack; extended context to 32k | Tongyi Qianwen license |
| DeepSeek-V2 | 21B active / 236B total | 8.1T | ~1.0e24 | MoE with multi-head latent attention (compressed KV cache) | DeepSeek License |
| Phi-3-mini | 3.8B | 3.3T | ~7e22 | "Textbook-quality" data + standard decoder; 4k / 128k context variants | MIT |
A few patterns are visible across this table. First, the bigger dense models (LLaMA-2 70B, Qwen 72B) sit close to the Chinchilla optimum at to . Second, the smaller dense models (LLaMA-2 7B, Mistral 7B, Phi-3-mini) train far past the optimum to lower inference cost, accepting more training compute for a cheaper serving footprint. Third, the MoE family (Mixtral, DeepSeek-V2) decouples active from total parameters, which scales total capacity without scaling per-token compute; the price is the routing complexity and the load-balance auxiliary loss. The license column matters as much as any architectural choice for downstream users.
Pitfalls
The single most common scaling-law mistake in 2020-2022 was applying the Kaplan recipe (-heavy, -light) to a fresh training run in 2023. A 30B model trained for B tokens (Kaplan-optimal) loses cleanly to a 13B model trained for T tokens (Chinchilla-optimal) at the same compute. The compute-optimal allocation has , give or take a factor of two. Always plan with the Chinchilla rule and consider going past it for inference economy [2].
The first OOM you hit in any new training setup is almost always Adam state, not weights. Standard mixed-precision Adam uses about bytes per parameter at ZeRO-0, which is the FP16 weight footprint. If a B model OOMs on a GB GPU but the FP16 weight memory is only GB, the missing room went to , , and the FP32 master copy. The fix is ZeRO-1 (or ZeRO-2 if you also want to shard gradients), not a smaller model [5].
FP16 training can run for steps before a single gradient overflow takes the loss to NaN. The mechanism is one rank seeing a gradient near the FP16 max value, the all-reduce contaminating the global gradient with infinity, and the optimizer step writing infinity into every master weight. If you must use FP16 (V100 hardware), keep a dynamic loss-scaling controller and global L2 gradient clipping. If your hardware supports BF16 (any A100 or newer), prefer BF16 and avoid the whole problem [6].
For any non-trivial training run, plot the actual loss curve against the Kaplan and Chinchilla predictions for your trajectory. A run that drifts upward versus the prediction is signalling a problem (data quality, learning rate, an upstream regression in the data pipeline) long before you see it in eval. A run that hugs the Chinchilla curve is on track. The cost of this overlay is essentially zero; the value is catching a \120%$ in.
Pop quiz
- 1. Under the Chinchilla compute-optimal recipe with C = 6 N D and the joint power-law fit, the optimal data-to-parameter ratio is approximately:
- 2. ZeRO-3 partitions which of {optimizer state, gradients, parameters} across the N data-parallel ranks?
- 3. Why does BF16 training avoid the dynamic-loss-scaling controller that FP16 needs?
- 4. In a 3D-parallel recipe (TP x PP x DP), which axis is typically capped at one node (e.g., 8 GPUs over NVLink)?
- 5. For a 7B-parameter model with mixed-precision Adam at ZeRO-0, the per-GPU memory cost (excluding activations) is approximately:
Interview Q&A
Derive the Chinchilla compute-optimal N* and D* from C = 6 N D and a loss power law of the form L = A/N^alpha + B/D^beta + E.phone
Setup. Compute is approximately for a dense Transformer (factor from forward and backward FLOPs per parameter per token). We want to minimize the loss
over subject to .
Lagrangian. Form and set partial derivatives to zero:
Eliminating gives the optimality condition
So the contributions of the parameter and data terms to the loss are tied at the optimum, in a ratio . Substitute into the constraint and solve for the scaling exponents. The result is
Plug in the numbers. Hoffmann’s Approach 3 fit gives , . Then and . So and , which empirically lands at roughly across a wide compute range.
Why this differs from Kaplan. Kaplan’s joint fit had effectively in this notation, which gives exponent close to and exponent close to . The Hoffmann fit uses a per-size hyperparameter sweep and a larger model-size range, and finds the exponents are much closer to equal. The resulting prescription “scale data and parameters together” is the modern default [2][1].
Walk me through the per-parameter memory budget of mixed-precision Adam, and explain how ZeRO-3 reduces it from about 20 bytes to 16 / N bytes per parameter.onsite
ZeRO-0 budget. A single data-parallel rank stores, per parameter:
- bytes for the FP16 weights (used in the forward and backward pass).
- bytes for the FP16 gradients (accumulated during the backward pass).
- bytes for the FP32 master weights (the high-precision copy that the optimizer updates).
- bytes for the FP32 first moment (Adam momentum).
- bytes for the FP32 second moment (Adam variance).
- bytes for the FP32 gradient buffer used during the optimizer step.
Total: bytes per parameter of optimizer state, plus bytes per parameter of low-precision weights and gradients. Roughly bytes per parameter end-to-end. For parameters that is GB per rank, well above any single GB GPU.
ZeRO-1 partitions optimizer state. The bytes per parameter of FP32 state are partitioned across the data-parallel ranks: each rank stores bytes per parameter. The FP16 weights and gradients remain replicated, so per-GPU memory is bytes per parameter. The communication primitive picks up: at the optimizer step, each rank updates only its slice and a final all-gather reconstitutes the FP16 weights for the next forward.
ZeRO-2 also partitions gradients. Add gradients to the partitioned set. Per-GPU memory is now bytes per parameter (FP16 weights still replicated). The gradient all-reduce becomes a reduce-scatter into per-rank slices, then a local optimizer step on each slice, then an all-gather of weights.
ZeRO-3 also partitions parameters. All three components (optimizer, gradients, FP16 weights) are sharded. Per-GPU memory is bytes per parameter, asymptotically scaling to zero with . The cost: each layer’s FP16 weights must be all-gathered before that layer’s forward and freed afterward, and gathered again on backward. Effective communication doubles versus ZeRO-2 in the parameter axis, but the memory unlock is what makes 100B+ dense training on commodity 80 GB GPUs possible.
Worked example, 7B on 8 GPUs. ZeRO-0: GB per rank (does not fit). ZeRO-1: GB per rank. ZeRO-2: GB per rank. ZeRO-3: GB per rank. The choice is “smallest stage that fits”, because each stage adds communication. ZeRO-2 is the typical sweet spot for 7B-class models on a single 8-GPU node [5].
Compare ZeRO stages 1, 2, and 3 along three axes: per-GPU memory, additional communication versus plain data parallelism, and which step in the loop pays the cost.staff
ZeRO-1: optimizer state partitioning.
- Memory. Per-GPU memory drops from bytes per parameter to bytes per parameter. The optimizer state is the largest line item ( of the budget), so this single stage already gives the bulk of the savings on most configurations.
- Communication. The forward and backward passes are unchanged versus plain data parallelism. The gradient all-reduce remains. The new wrinkle is at the optimizer step: each rank updates only its slice of the FP32 master weights, then an all-gather reconstitutes the FP16 weights for the next forward. Net change versus plain DP is essentially zero bandwidth in the steady state because the all-gather cost matches the work the all-reduce was already doing.
- Step that pays. The optimizer step. Each rank pays a small extra coordination cost when assembling its slice and broadcasting the result. Forward and backward are unaffected.
ZeRO-2: optimizer state + gradients.
- Memory. Per-GPU memory drops to bytes per parameter (FP16 weights still replicated). For on a 7B model, that is about GB per rank versus GB at ZeRO-1.
- Communication. The gradient all-reduce becomes a reduce-scatter (each rank ends up with the summed gradient for its slice). After the local optimizer step, an all-gather reconstitutes the FP16 weights. Reduce-scatter plus all-gather is bandwidth-equivalent to a single all-reduce, so total bytes per step are roughly unchanged.
- Step that pays. The backward pass: gradients must be reduced into per-rank slices instead of broadcast everywhere. The optimizer step is otherwise identical to ZeRO-1.
ZeRO-3: optimizer + gradients + parameters.
- Memory. Per-GPU memory drops to bytes per parameter, asymptotically zero with . The FP16 weights themselves are sharded.
- Communication. Adds an all-gather of FP16 weights at every layer’s forward pass and backward pass, on top of the ZeRO-2 reduce-scatter. The doubling of all-gather count is the price for fitting the parameters themselves under the GPU memory ceiling.
- Step that pays. Forward and backward both pay: each layer first all-gathers its weights from the responsible rank, runs the layer, then frees the gathered weights. PyTorch FSDP and DeepSpeed both implement this with prefetch overlap so the gather hides behind compute when the layer is large enough.
Practical guidance. Pick the smallest stage that fits the model and a workable batch size. ZeRO-1 if the optimizer state alone breaks you. ZeRO-2 if you also need the gradient memory back. ZeRO-3 only when the FP16 weights themselves do not fit on one GPU (typically at B parameters on GB hardware). Never run ZeRO-3 when ZeRO-2 fits, because the extra all-gathers cost real wall-clock time that ZeRO-2 does not pay [5].
A weekend-scale project that takes scaling laws from formulas to a working sweep:
- Train a 5M, 10M, 25M, and 50M-parameter decoder-only Transformer on a M-token slice of OpenWebText. Same architecture, scaled and depth. Plot test loss versus compute on a log-log axis.
- Fit the Kaplan power law on the four points and extrapolate to a 100M model. Train a 100M model and check the prediction.
- Repeat the sweep at fixed M but vary from M to M tokens. Fit the data axis. Combine into the joint fit and find your local Chinchilla optimum.
- Implement ZeRO-1 manually using PyTorch’s
torch.distributed.reduce_scatterandtorch.distributed.all_gather. Verify that 4-GPU ZeRO-1 trains the same loss curve as 4-GPU plain DDP, with less optimizer-state memory per rank. - Stretch: swap the FP32 Adam state to BF16 and confirm training breaks; swap to BF16 weights only with FP32 master and confirm training matches.
Interactive Demo
Scaling Laws Explorer
Log-log plot of predicted loss vs scale. Power laws govern how larger models, more data, and more compute improve performance.
What to try: Switch between curves to see how parameters, data, and compute each affect loss on a log-log scale
References
- Kaplan, J., McCandlish, S., Henighan, T., Brown, T. B., Chess, B., Child, R., Gray, S., Radford, A., Wu, J., and Amodei, D. (2020). Scaling Laws for Neural Language Models. arXiv preprint. arXiv:2001.08361
- Hoffmann, J., Borgeaud, S., Mensch, A., Buchatskaya, E., Cai, T., Rutherford, E., de Las Casas, D., Hendricks, L. A., Welbl, J., Clark, A., et al. (2022). Training Compute-Optimal Large Language Models. NeurIPS 2022 (arXiv preprint). arXiv:2203.15556
- Shoeybi, M., Patwary, M., Puri, R., LeGresley, P., Casper, J., and Catanzaro, B. (2019). Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism. arXiv preprint. arXiv:1909.08053
- Huang, Y., Cheng, Y., Bapna, A., Firat, O., Chen, M. X., Chen, D., Lee, H., Ngiam, J., Le, Q. V., Wu, Y., and Chen, Z. (2018). GPipe: Efficient Training of Giant Neural Networks using Pipeline Parallelism. NeurIPS 2019 (arXiv preprint 2018). arXiv:1811.06965
- Rajbhandari, S., Rasley, J., Ruwase, O., and He, Y. (2019). ZeRO: Memory Optimizations Toward Training Trillion Parameter Models. SC 2020 (arXiv preprint 2019). arXiv:1910.02054
- Micikevicius, P., Narang, S., Alben, J., Diamos, G., Elsen, E., Garcia, D., Ginsburg, B., Houston, M., Kuchaiev, O., Venkatesh, G., and Wu, H. (2017). Mixed Precision Training. ICLR 2018 (arXiv preprint 2017). arXiv:1710.03740
- Chen, T., Xu, B., Zhang, C., and Guestrin, C. (2016). Training Deep Nets with Sublinear Memory Cost. arXiv preprint. arXiv:1604.06174
- Touvron, H., Martin, L., Stone, K., Albert, P., Almahairi, A., Babaei, Y., Bashlykov, N., Batra, S., Bhargava, P., Bhosale, S., et al. (2023). Llama 2: Open Foundation and Fine-Tuned Chat Models. arXiv preprint. arXiv:2307.09288
- Jiang, A. Q., Sablayrolles, A., Mensch, A., Bamford, C., Chaplot, D. S., de las Casas, D., Bressand, F., Lengyel, G., Lample, G., Saulnier, L., et al. (2023). Mistral 7B. arXiv preprint. arXiv:2310.06825
- Jiang, A. Q., Sablayrolles, A., Roux, A., Mensch, A., Savary, B., Bamford, C., Chaplot, D. S., de las Casas, D., Hanna, E. B., Bressand, F., et al. (2024). Mixtral of Experts. arXiv preprint. arXiv:2401.04088
- Bai, J., Bai, S., Chu, Y., Cui, Z., Dang, K., Deng, X., Fan, Y., Ge, W., Han, Y., Huang, F., et al. (2023). Qwen Technical Report. arXiv preprint. arXiv:2309.16609
- DeepSeek-AI: Bi, X., Chen, D., Chen, G., Chen, S., Dai, D., Deng, C., Ding, H., Dong, K., Du, Q., et al. (2024). DeepSeek LLM: Scaling Open-Source Language Models with Longtermism. arXiv preprint. arXiv:2401.02954
- Abdin, M., Aneja, J., Awadalla, H., Awadallah, A., Awan, A. A., Bach, N., Bahree, A., Bakhtiari, A., Bao, J., Behl, H., et al. (2024). Phi-3 Technical Report: A Highly Capable Language Model Locally on Your Phone. arXiv preprint. arXiv:2404.14219
- Goodfellow, I., Bengio, Y., and Courville, A. (2016). Deep Learning. MIT Press. https://www.deeplearningbook.org