--- id: training-memory-checkpoints title: Training Memory, Accumulation and Checkpoints code: TM subtitle: Fix NaN steps, memory limits, accumulation scale and broken resumes. Clip, AMP, recompute, atomic checkpoints. hue: 20 sources: phases/10-llms-from-scratch/34-gradient-checkpointing, phases/19-capstone-projects/45-gradient-clipping-amp, phases/19-capstone-projects/46-gradient-accumulation, phases/19-capstone-projects/47-checkpoint-save-resume, phases/19-capstone-projects/80-checkpoint-sharded-resume, phases/19-capstone-projects/81-end-to-end-distributed-train source_hash: 0ea0fe65eca26d48 --- ## Training symptoms type: troubleshooting from: phases/19-capstone-projects/45-gradient-clipping-amp/docs/en.md, phases/19-capstone-projects/46-gradient-accumulation/docs/en.md, phases/19-capstone-projects/47-checkpoint-save-resume/docs/en.md, phases/10-llms-from-scratch/34-gradient-checkpointing/docs/en.md | Symptom | Cause | Fix | |---|---|---| | Loss goes vertical after one batch | Gradient norm spike, no clip | `clip_grad_norm_` at `max_norm = 1.0` | | Every weight is NaN after a step | FP16 overflow gives Inf, then NaN | `torch.amp.GradScaler` skips the step | | Scaling factor never changes | No `scaler.update()` on the skip path | Call `scaler.update()` every step | | Clip acts on scaled gradients | Clip runs before unscale | Call `scaler.unscale_(optimizer)` first | | Hundreds of skips per epoch | Model is past the FP16 range | Alert on a skip rate above 5 percent | | Optimizer step 16 times too big | Micro-batch loss not divided | Divide the loss by `accum_steps` | | Resumed loss curve differs | No RNG state in the checkpoint | Save `capture_rng_state()` output | | Activations do not fit in memory | Backprop keeps every activation | `torch.utils.checkpoint.checkpoint(fn, input)` | ## Checkpoint load failures type: troubleshooting from: phases/19-capstone-projects/80-checkpoint-sharded-resume/docs/en.md, phases/19-capstone-projects/80-checkpoint-sharded-resume/code/main.py, phases/19-capstone-projects/47-checkpoint-save-resume/code/main.py, phases/19-capstone-projects/47-checkpoint-save-resume/docs/en.md | Symptom | Cause | Fix | |---|---|---| | `world_size mismatch` | Resume on a different world size | Resume on the same world size | | `sha256 mismatch on rank` | Shard truncated mid-flush | Write to .tmp, fsync, then rename | | `shard file missing` | Fewer rank files than manifest shards | Enumerate shards, verify each exists | | `unknown schema` | Payload schema does not start with ckpt | Branch migrations on the schema string | | Loss goes to NaN 100 steps after resume | Partial shard behind a present manifest | Atomic write plus sha256 on load | | Disk fills mid-run | No rotation | `rotate_checkpoints` keeps the last K | ## Safe AMP step type: annotated-code lang: python from: phases/19-capstone-projects/45-gradient-clipping-amp/code/main.py, phases/19-capstone-projects/45-gradient-clipping-amp/docs/en.md ```python optimizer.zero_grad(set_to_none=True) # grads to None with torch.amp.autocast(device_type="cuda"): # FP16 forward loss = loss_fn(model(inputs), targets) if not torch.isfinite(loss).all().item(): # check before backward return skip("non_finite_loss") # no scaler.update here scaler.scale(loss).backward() # scaled backward scaler.unscale_(optimizer) # unscale before the clip if has_non_finite_grad(model.parameters()): scaler.update() # halves the factor return skip("non_finite_grad") pre, post = clip_global_l2_norm(params, max_norm) scaler.step(optimizer) scaler.update() # doubles or halves ``` ## Accumulate, sync on the last type: annotated-code lang: python from: phases/19-capstone-projects/46-gradient-accumulation/code/main.py, phases/19-capstone-projects/46-gradient-accumulation/docs/en.md ```python accum_steps = len(micro_batches) zero_grads(model) for i, (x, y) in enumerate(micro_batches): is_last = i == accum_steps - 1 if no_sync_until_last and not is_last: with no_sync_context(model): # DDP skips the all-reduce loss = loss_scaled_for_accum(model(x), y, accum_steps, loss_fn) loss.backward() # grads add into param.grad else: loss = loss_scaled_for_accum(model(x), y, accum_steps, loss_fn) loss.backward() # one sync per effective step optimizer.step() # once per effective batch ``` ## Tasks in code type: task-table span: 2 from: phases/19-capstone-projects/45-gradient-clipping-amp/docs/en.md, phases/19-capstone-projects/45-gradient-clipping-amp/code/main.py, phases/19-capstone-projects/46-gradient-accumulation/code/main.py, phases/10-llms-from-scratch/34-gradient-checkpointing/docs/en.md, phases/19-capstone-projects/47-checkpoint-save-resume/code/main.py, phases/19-capstone-projects/80-checkpoint-sharded-resume/code/main.py | Task | Code | |---|---| | Clip to a global L2 norm | `clip_grad_norm_(parameters, max_norm)` | | Log the scaling factor | `scaler.get_scale()` | | Scale a micro-batch loss | `loss_scaled_for_accum(logits, target, accum_steps, loss_fn)` | | Recompute a segment in backward | `checkpoint(fn, input)` | | Save the full state atomically | `save_checkpoint(model, optimizer, scheduler, state, out_path)` | | Load an untrusted file | `torch.load(path, map_location="cpu", weights_only=True)` | | Save and load per-rank shards | `save_sharded(per_rank_state, dest_dir, step)` | | Resume and verify every shard | `load_sharded(src_dir, expected_world_size)` | ## Sharded atomic save type: architecture from: phases/19-capstone-projects/80-checkpoint-sharded-resume/docs/en.md, phases/19-capstone-projects/80-checkpoint-sharded-resume/code/main.py ```arch node | s0 | 1,1 | rank 0 state | param and optimiser shard | gpu node | s1 | 1,2 | rank 1 state | one file per rank | gpu node | t0 | 2,1 | rank0.bin.tmp | write, flush, fsync | store node | t1 | 2,2 | rank1.bin.tmp | write, flush, fsync | store node | man | 3,1 | manifest.json.tmp | world_size, step, shards | store node | ren | 3,2 | os.replace | shards first, manifest last | process node | load | 4,1 | load_sharded | sha256 check per shard | process edge | s0 | t0 | edge | s1 | t1 | edge | t0 | man | sha256 edge | t1 | man | sha256 edge | man | ren | edge | ren | load | resume ``` ## Save and resume mid epoch type: flow from: phases/19-capstone-projects/47-checkpoint-save-resume/docs/en.md, phases/19-capstone-projects/47-checkpoint-save-resume/code/main.py 1. **save_checkpoint**: packs model, optimizer, scheduler, counters and RNG. 2. **atomic_save**: writes a temp file, then calls `os.replace`. 3. **load_checkpoint**: calls `torch.load` with `weights_only=True`. 4. **restore_rng_state**: restores Python, NumPy and torch RNG. 5. **Training loop**: continues from `batch_in_epoch`. Loss diff under 1e-4. ## Run the lessons type: command-table from: phases/19-capstone-projects/45-gradient-clipping-amp/docs/en.md, phases/19-capstone-projects/46-gradient-accumulation/docs/en.md, phases/19-capstone-projects/46-gradient-accumulation/code/main.py, phases/19-capstone-projects/47-checkpoint-save-resume/code/main.py, phases/19-capstone-projects/80-checkpoint-sharded-resume/docs/en.md, phases/19-capstone-projects/81-end-to-end-distributed-train/docs/en.md | Command | What it does | |---|---| | `python3 code/main.py` | Lesson 45: 20 steps, rows tagged STEP or SKIP | | `--num-steps 100` | Lesson 46: longer sweep for the curve | | `--accum-grid 1,2,4,8,16` | Lesson 46: accumulation steps to sweep | | `--total-steps 24 --interrupt-at 10` | Lesson 47: resume demo window | | `python3 code/main.py` | Lesson 80: 4 shards plus manifest, byte-equal reload | | `python3 code/main.py` | Lesson 81: 20 steps, 4 ranks, RESUME VERIFIED | ## Defaults in the code type: defaults from: phases/19-capstone-projects/45-gradient-clipping-amp/code/main.py, phases/10-llms-from-scratch/34-gradient-checkpointing/code/main.py, phases/19-capstone-projects/80-checkpoint-sharded-resume/code/main.py, phases/19-capstone-projects/47-checkpoint-save-resume/code/main.py, phases/19-capstone-projects/81-end-to-end-distributed-train/code/main.py | Setting | Default | Note | |---|---|---| | `DEFAULT_MAX_NORM` | 1.0 | Clip threshold of `AmpTrainState` | | `amp_dtype` on cpu | `torch.bfloat16` | `torch.float16` on cuda | | `window` | 1000 | Steps in `rolling_skip_rate` | | `k` | 4 | Layers per checkpoint segment | | `attention_fraction` | 0.15 | Recompute share, selective mode | | `keep_last` | 5 | Checkpoints that `rotate_checkpoints` keeps | | `CHECKPOINT_SCHEMA` | ckpt.v1 | Schema string in the payload | | `CHECKPOINT_STEP` | 10 | Sharded save step of lesson 81 | ## Memory and cost numbers type: concepts from: phases/10-llms-from-scratch/34-gradient-checkpointing/docs/en.md, phases/19-capstone-projects/46-gradient-accumulation/docs/en.md, phases/19-capstone-projects/81-end-to-end-distributed-train/docs/en.md - **Activations per layer**: about `12 * B * L * d` floats. - **d=8192, L=8192, B=1**: about 1.6 GB per layer in BF16. 64 layers is about 103 GB. - **Full checkpointing**: about 33% more FLOPs. A step is 4 units, not 3. - **Selective checkpointing**: recompute attention only. About 5% overhead. - **sqrt(L) rule**: one checkpoint every sqrt(L) layers. 8 checkpoints for L=64. - **Effective batch**: `micro_batch * accum_steps`, times the data parallel world size. - **ZeRO-1 optimiser memory**: 12P/N bytes per rank. ## Q&A type: qa from: phases/19-capstone-projects/45-gradient-clipping-amp/docs/en.md, phases/19-capstone-projects/46-gradient-accumulation/docs/en.md, phases/19-capstone-projects/47-checkpoint-save-resume/docs/en.md, phases/19-capstone-projects/80-checkpoint-sharded-resume/docs/en.md, phases/10-llms-from-scratch/34-gradient-checkpointing/docs/en.md - **Why does the CPU GradScaler do nothing?** CPU autocast uses BF16. It does not need loss scaling. - **When does the factor change?** It doubles after clean stretches. It halves on every skip. - **Does accumulation save time?** No. Double `accum_steps` and the wall time per step doubles. - **Why `weights_only=True`?** A `.pt` file is a pickle. Unpickling runs code. Use torch 2.6 or newer. - **Why one file per rank?** Write bandwidth scales. 4 hours on one rank is 4 minutes on 64. ## Tips type: tips from: phases/19-capstone-projects/45-gradient-clipping-amp/docs/en.md, phases/19-capstone-projects/47-checkpoint-save-resume/docs/en.md, phases/19-capstone-projects/81-end-to-end-distributed-train/docs/en.md - Match the autocast device to the optimizer device. - Raise `ValueError`, not assert. `python -O` strips asserts. - On a loss jump above 2x, roll back to the previous checkpoint.