Gradient Checkpointing - PyTorch Basics
Implement Gradient Checkpointing in Python with NumPy. hard exercise with examples, hints, and instant feedback. No setup or signup required.
Gradient Checkpointing
What you'll build: the two halves of activation checkpointing: a forward pass that keeps only selected activations, and a recompute function that rebuilds any activation from the nearest saved one.
Background
Backpropagation needs the activations of the forward pass, so memory grows linearly with depth: O(n) for n layers. Checkpointing stores only some of them and recomputes the rest during the backward pass. With sqrt(n) evenly spaced checkpoints, memory falls to O(sqrt(n)) at the cost of roughly one extra forward pass. It is how large transformers fit long sequences on a GPU (torch.utils.checkpoint).
Conventions
layersis a list of callables; activationiis the output oflayers[i].- The input
xis always saved, under the key-1. checkpoint_indiceslists which layer outputs to keep.
Algorithm
checkpoint_forward(layers, x, checkpoint_indices)
saved = {-1: copy of x}.- Run the layers in order. After layer
i, ifiis incheckpoint_indices, store a copy of its output under keyi. - Return
(final_output, saved).
checkpoint_recompute(layers, saved_activations, checkpoint_indices, layer_idx)
- Find the nearest saved key strictly below
layer_idx:start = max(k for k in saved if k < layer_idx)(-1always qualifies). - Starting from
saved[start], runlayers[start + 1], ..., layers[layer_idx]. - Return the result, the output of layer
layer_idx.
Inputs
layers: list of functions mapping an array to an array.x: input array;checkpoint_indices: list of layer indices (may be empty).layer_idx: index of the activation to rebuild,0 <= layer_idx < len(layers).
Expected Return Format
checkpoint_forwardreturns a tuple(output, saved): the final activation and a dict{index: activation}containing exactly-1and the checkpoint indices.checkpoint_recomputereturns the activation of layerlayer_idx.
Edge Cases and Conventions
- Store copies, so later in-place changes can't corrupt the checkpoints.
- Recompute runs only the layers after the nearest checkpoint, never the whole network.
Constraints
- PyTorch track:
torch.utils.checkpointisn't available in the browser; run the forward undertorch.no_grad()(no graph, so no stored intermediates) and recompute with autograd enabled.
Follow-up
- Why is
sqrt(n)the best number of evenly spaced checkpoints? - Which layers are cheapest to recompute and most expensive to store in a transformer block?
Examples
Input: layers = [x*2, x+1, x*3, x-1], checkpoint_forward(layers, np.array([1.0]), [1, 3])
Output: (array([8.]), {-1: array([1.]), 1: array([3.]), 3: array([8.])})Activations are 2, 3, 9, 8; only the input and the outputs of layers 1 and 3 are kept.
Input: checkpoint_recompute(layers, saved, [1, 3], 2) Output: array([9.])
The nearest saved activation below 2 is index 1 (value 3); only layer 2 (x*3) is re-run.
Practice in Python
import numpy as np
def checkpoint_forward(layers, x, checkpoint_indices):
"""
Forward pass that saves only the input and the checkpoint activations.
Args:
layers: List of layer functions
x: Input array
checkpoint_indices: Indices of the layers whose outputs to save
Returns:
(output, saved): the final output and a dict index -> activation
(key -1 holds the input)
"""
# Your code here
pass
def checkpoint_recompute(layers, saved_activations, checkpoint_indices, layer_idx):
"""
Recompute the output of layer layer_idx from the nearest saved activation below it.
Args:
layers: List of layer functions
saved_activations: Dict index -> activation from checkpoint_forward
checkpoint_indices: Indices that were saved
layer_idx: Layer whose output to rebuild
Returns:
Activation (output) of layer layer_idx
"""
# Your code here
pass
The interactive editor loads with Python and NumPy. Run your implementation against 6 tests, or use the progressive hints.