Pytorch how to get the gradient of loss function twice
ML System Design practice on Codemia
Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.
Introduction
By default, PyTorch frees the computation graph after calling .backward() once. Calling .backward() a second time raises a RuntimeError because the intermediate values needed for gradient computation are gone. To compute gradients twice — whether for double backpropagation, second-order optimization, or gradient penalty — you must pass retain_graph=True on the first backward call or use torch.autograd.grad with create_graph=True.
The Error
After the first .backward(), PyTorch releases the graph to save memory. The second call fails because there is no graph left to traverse.
Fix 1: retain_graph=True
Pass retain_graph=True to keep the computation graph alive after the first backward pass:
The graph persists until a backward call without retain_graph=True, which frees it. Always zero out gradients between calls to avoid accumulation.
Fix 2: torch.autograd.grad with create_graph
For second-order derivatives (Hessian, gradient penalty), use torch.autograd.grad with create_graph=True. This builds a new graph of the gradient computation itself, enabling differentiation through it:
create_graph=True means the first gradient is itself part of a computation graph, so you can differentiate through it again.
Practical Example: Gradient Penalty (WGAN-GP)
Gradient penalties require computing the gradient of the discriminator output with respect to the input, then computing a loss based on the gradient norm:
Without create_graph=True, the gradient penalty would be treated as a constant and the discriminator would not receive the penalty signal during optimization.
Practical Example: Computing the Hessian
For small models, you can compute the full Hessian matrix:
retain_graph vs create_graph
| Parameter | What It Does | When to Use |
retain_graph=True | Keeps the existing graph alive after backward | Running .backward() multiple times on the same loss |
create_graph=True | Builds a graph of the gradient computation itself | Second-order derivatives, gradient penalty, meta-learning |
create_graph=True implies retain_graph=True — you do not need to set both.
Memory Considerations
Retaining or creating graphs consumes significantly more memory:
For large models, prefer computing second-order information with Hessian-vector products instead of the full Hessian to limit memory usage.
Common Pitfalls
- Forgetting to zero gradients between backward calls: PyTorch accumulates gradients by default. Call
optimizer.zero_grad()orparam.grad.zero_()before each backward pass to avoid adding gradients from the previous call. - Using
retain_graph=Truewhencreate_graph=Trueis needed:retain_graphkeeps the original forward graph but does not make the gradient computation differentiable. For second derivatives, you must usecreate_graph=True. - Memory leaks from always retaining the graph: If you pass
retain_graph=Truein a training loop and never do a final backward without it, the graph accumulates indefinitely. Ensure the last backward call frees the graph. - Detaching when you should not: Calling
.detach()on a tensor removes it from the computation graph. If you detach the gradient before computing the second derivative, the second backward pass returns zero. - In-place operations breaking the graph: Operations like
x.add_(1)orx[0] = 5modify tensors in place and can invalidate the computation graph. Use out-of-place operations (x = x + 1) when you need to backpropagate through the result.
Summary
- PyTorch frees the computation graph after
.backward()by default - Use
retain_graph=Trueto call.backward()multiple times on the same graph - Use
create_graph=Truewithtorch.autograd.gradto compute second-order derivatives create_graph=Trueis required for gradient penalties (WGAN-GP), meta-learning (MAML), and Hessian computation- Always zero gradients between backward calls to prevent accumulation
- Retaining and creating graphs increases memory usage — free the graph when no longer needed
Related reading
- Pytorch Image label
- PyTorch is there a definitive training loop similar to Keras' fit?
- PyTorch is there a definitive training loop similar to Keras' fit?
- PyTorch Learning rate scheduler
- Pytorch lightning logger doesn't work as expected
- Pytorch lightning print accuracy and loss at the end of each epoch
- PyTorch model input shape
- PyTorch multiprocessing error with Hogwild
.png&w=3840&q=75)
Tackling System Design Interview Problems
A short course that equips you with the skills to approach system design interviews methodically.
Start the free courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
ML System Design practice on Codemia
Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.