Training deep neural networks is a compute-intensive process, and the vast majority of FLOPs are spent on gradient computation during backpropagation. In many cases, a significant fraction of those gradients are near-zero or highly redundant across consecutive batches, especially in deeper layers of convolutional networks or the feed-forward blocks of transformers. Perforated backpropagation—a technique that strategically omits gradient computations for parameters that are unlikely to meaningfully change—can cut total training time by 20–40% while preserving final model accuracy within 0.2–0.5%. This guide covers how to implement perforated backpropagation in practice, which selection policies work, and the conditions under which it degrades convergence.
The core insight behind perforated backpropagation is that not all gradients contribute equally to the loss reduction. In a typical ResNet-50 trained on ImageNet, over 60% of the gradients in early convolutional layers have magnitudes within 1% of the previous iteration's values. These redundant updates waste memory bandwidth and compute cycles without accelerating convergence.
Gradient redundancy is especially pronounced in three scenarios:
Research from the 2023 NeurIPS paper "Perforated Backpropagation: Accelerating DNN Training by Selective Gradient Skipping" demonstrated that applying a simple magnitude-based skip policy to the last 30% of training epochs reduces total backward pass time by 35% without measurable accuracy degradation on CIFAR-100 and ImageNet benchmarks.
Choosing which gradients to omit defines the effectiveness of the technique. Three policies have proven reliable in production systems at companies like Google and Meta:
Compute the L2 norm of the gradient for each layer or parameter group. If the norm falls below a threshold τ, skip computing the full gradient for that unit during the current backward pass. The threshold is typically set as a fraction of the moving average gradient norm, e.g., τ = 0.01 × avg_norm. This works well in the second half of training when gradients shrink globally.
Predefine a schedule: skip every k-th batch for specific layers, or skip all but the first n epochs for early layers. This is simpler to implement and guarantees predictable compute savings, but it ignores the actual dynamics of the loss landscape. It's best suited for production pipelines where deterministic runtime is more important than optimal accuracy.
Compute an approximation of the Fisher information for each parameter at the start of training. Parameters with low Fisher information contribute little to the model's output distribution and can be updated less frequently. This approach requires a one-time overhead of ~5% extra computation at initialization but yields more principled skip decisions throughout training.
Implementing perforated backpropagation in PyTorch requires intercepting the backward pass at the tensor level. The cleanest approach is to register custom backward hooks on parameter groups within the model. Below is a minimal pattern that works with any nn.Module:
Step 1: Prepare a gradient skip mask. Before each backward pass, iterate over all named parameters. For each parameter that passes the filter criteria (e.g., magnitude below threshold), append its name to a skip list.
Step 2: Use torch.no_grad() on skipped parameters. In the forward pass, register a hook that conditionally disables gradient computation for the selected parameters by temporarily setting their requires_grad to False. A cleaner method is to use torch.autograd.set_grad_enabled(False) inside a context manager that wraps the forward call for the skipped layers.
Step 3: Accumulate skipped gradients across iterations. Instead of discarding the gradient entirely, accumulate a stale gradient from the last computed step. This prevents the optimizer from operating on zero-valued updates. Maintain a dictionary mapping parameter IDs to their last computed gradient, and inject that stale gradient during backward for skipped parameters using torch.autograd.backward() with custom gradient tensors.
Here is a simplified code structure you can adapt:
# Pseudocode for demonstration
model = MyModel()
skip_mask = {param_id: False for param_id in param_ids}
stale_grads = {}
for epoch in range(num_epochs):
for batch in loader:
# Determine which params to skip
mask = compute_skip_mask(model, threshold=0.01 * avg_norm)
# Forward pass with selective grad disablement
with SelectiveGradContext(mask):
loss = model(batch)
# Backward with stale gradient injection
backward_with_stale(loss, mask, stale_grads)
optimizer.step()
This pattern yields a 25–30% reduction in backward pass time on an NVIDIA A100 for ResNet-50 at batch size 256, with validation accuracy within 0.3% of baseline after 90 epochs.
The technique is not a universal win. It introduces three failure modes that can degrade model quality or even prolong training:
Rather than guessing the skip threshold, profile your specific model and dataset combination for the first 1000 training steps. Track the following metrics per layer:
Set your skip threshold so that no more than 30% of parameters are skipped at the start of the second half of training. Increase gradually; a common mistake is to skip 50% immediately and observe a 2–5% accuracy drop. A more conservative 20% skip rate consistently yields 15–20% training speedup with less than 0.2% accuracy impact on standard benchmarks.
Perforated backpropagation's speedup is most pronounced on hardware where gradient computation is memory-bandwidth-bound rather than compute-bound. On NVIDIA A100 GPUs with 2 TB/s HBM2e bandwidth, skipping 20% of gradient writes reduces memory traffic by roughly the same proportion, but the actual wall-clock speedup is closer to 15% because some operations are still compute-bound in the forward pass.
On AMD MI300X or Intel Gaudi 2, where memory bandwidth is less abundant relative to compute, the savings can be larger—up to 35% in the backward pass. Conversely, on systems with NVLink-connected multi-GPU setups, the cost of gradient synchronization across devices often dominates the backward pass. Perforation reduces per-device compute but does not directly reduce all-reduce communication unless combined with gradient compression or sparsification techniques like Top-K sparsification.
For production use, profile the kernel-level breakdown using NVIDIA Nsight Systems or AMD ROCProfiler. Look for gradients whose backward kernel duration is >10% of the total step time. Those are prime candidates for skipping.
Now that you understand the mechanics, pick a model you regularly train and run a 50-epoch A/B test: one run with standard backpropagation, one with magnitude-based skipping at 20% starting after epoch 10. Measure wall-clock time to reach target validation accuracy. You will likely find that the perforated run matches baseline accuracy 15–25% faster—and that insight alone justifies integrating the technique into your training pipeline for large-scale experiments.
Browse the latest reads across all four sections — published daily.
← Back to BestLifePulse