Skip to content
mlmentorship

Activation checkpointing

Trade compute for memory: drop activations during the forward pass and recompute them during the backward pass. The cheapest way to fit a larger model on the same GPU.

Published · 6 min read ·Role-specific ·Advanced

Visual quick review

Visual first · depth when needed

Explain why checkpointed chain memory contains both L/K saved segment boundaries and K temporary activations during recomputation, and why balancing those terms puts K near the square root of L.

Preparing the visual…

Summary

Activation checkpointing (also called gradient checkpointing) saves only a subset of activations during the forward pass and recomputes the rest from those saved checkpoints during the backward pass. Memory drops at the cost of one extra forward pass per checkpoint segment.

Backprop needs every layer’s input activation to compute that layer’s parameter gradient. For a deep model the activations dominate training memory. Often more than parameters and optimizer state combined. A 7B-parameter transformer with 32 layers, batch 1, sequence 4096 stores tens of GB of activations.

Checkpointing recovers this memory by repeating part of the forward work. Recomputing the full forward graph adds about one forward pass of FLOPs. Wall-time cost depends on kernels, memory traffic, and how much of the graph is recomputed.

The mechanism

Partition a chain of layers into segments of consecutive layers. During forward:

  1. Run the segment.
  2. Save only its input (the checkpoint).
  3. Discard intermediate activations.

During backward:

  1. Recompute the segment’s forward pass starting from the saved input.
  2. Compute gradients normally for that segment.
  3. Discard the recomputed activations.

For a transformer, the natural segment is one transformer block. PyTorch provides torch.utils.checkpoint.checkpoint(...) and checkpoint_sequential(...); modern training stacks expose this as a single flag (e.g., gradient_checkpointing=True in HuggingFace Trainer).

Cost model

  • Memory across a simple chain: if each segment has layers, peak saved boundary state scales like and peak temporary recomputation state like . The total is minimized near .
  • Memory inside a transformer block: checkpointing every block still stores block boundaries, but discards the larger internal matrix and attention intermediates. The reduction is a workload-dependent constant factor, not ×.
  • Compute: full rematerialization adds roughly one forward pass to a training step. Since a forward and backward step is often estimated at three forward-pass units, this is about 33% more FLOPs. Wall-time overhead can differ.

Memory at one backward step

Saved boundaries and one rebuilt segment coexist in memory.

Activation checkpoint memory for a sixteen-layer chain split into four-layer segments Sixteen numbered layer boxes are divided into four segments of four layers. Diamond checkpoints mark the four saved segment inputs. During backward, a dashed enclosure marks the four activations temporarily rebuilt for layers nine through twelve. Below, four checkpoint diamonds plus four rebuilt activation squares total eight illustrative activation-sized units. The diagram concludes that balancing L divided by K against K gives a segment length near the square root of L. EXAMPLE: L = 16 layers, K = 4 layers per segment Forward keeps only each segment input (diamond). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 one segment rebuilt during backward PEAK ACTIVATION MEMORY IN THIS SIMPLE CHAIN persistent boundaries L/K = 4 + temporary rebuilt activations K = 4 = 8 units smaller K → more saved boundaries larger K → larger rebuilt window Balance L/K ≈ K ⇒ K ≈ √L
Read it this way: at one backward step, the four diamond checkpoints remain live while four discarded activations are rebuilt inside the dashed segment. For this illustrative 16-layer chain, memory is 16/4 + 4 = 8 activation-sized units; shortening segments saves fewer temporary activations but keeps more boundaries, so the terms balance near K = √L.

When to use

  • Always when training would OOM otherwise.
  • Selectively for the most memory-intensive blocks (FFN > attention typically). Selective checkpointing recovers most memory at lower compute cost.
  • Less useful when peak memory is dominated by optimizer state (use FSDP / ZeRO instead).
  • Less useful at inference (no backward pass).

Combined with other techniques

  • FSDP: orthogonal. FSDP shards parameters / gradients / optimizer state; checkpointing reduces activation memory. Most large training runs use both.
  • Mixed precision: orthogonal; checkpointing saves activations in whatever precision they were computed.
  • CPU offload: offload activations to CPU memory instead of recomputing. Saves GPU memory at higher communication cost.

Common pitfalls

  • Recomputing through randomness. Forward passes with dropout or other stochastic ops must use the same RNG state at recomputation; PyTorch’s checkpoint utility handles this with preserve_rng_state=True (default).
  • Checkpointing too aggressively. Larger rematerialized regions save more boundary state and repeat more work. Profile selective, per-block, and larger-segment choices under the real memory limit.
  • Forgetting that the recomputation runs inside the backward graph. Custom forward hooks may fire twice; gradients stay correct.
  • Trying to checkpoint inference. Checkpointing only helps when there is a backward pass to run.