How to check the output gradient by each layer in pytorch in my code?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
If you want the gradient of each layer's output in PyTorch, checking param.grad is not enough. Parameter gradients tell you how the loss changes with respect to weights and biases, while output gradients tell you what flowed backward through the activations produced by each layer.
The Key Distinction: Parameter Gradient Versus Output Gradient
After loss.backward(), PyTorch stores gradients for leaf parameters such as layer.weight.grad. That is useful for optimization, but it does not directly answer "what gradient hit this layer's output tensor?"
To inspect output gradients, you usually need one of these patterns:
- keep a reference to the intermediate tensor and call
retain_grad() - register hooks on module outputs during the forward pass
For per-layer debugging, output hooks are usually the most practical.
A Reliable Pattern with Forward Hooks and Tensor Hooks
One robust approach is:
- register a forward hook on each layer
- inside that hook, attach a tensor hook to the output
- run a normal forward and backward pass
- inspect the captured gradients
This gives you the gradient that flowed through each captured layer output.
Why This Works Well
PyTorch's autograd system computes gradients on the backward pass only for tensors that matter to the loss. By attaching a tensor hook to the actual layer output, you inspect the activation gradient directly rather than inferring it indirectly from parameter gradients.
This is also more precise than a generic print statement inside the training loop because it lets you record gradients per layer, save them, compare norms, and inspect anomalies systematically.
Using register_full_backward_hook
PyTorch also provides register_full_backward_hook, which receives grad_input and grad_output for a module during backpropagation.
This can be useful for quick debugging, but module backward hooks can be trickier than tensor hooks when modules return tuples, use in-place operations, or participate in more complex graphs. For many real debugging sessions, capturing output tensor hooks is the clearer option.
Checking a Single Saved Intermediate Tensor
If you only care about one specific activation, you can keep the tensor reference and call retain_grad() on it.
This is great for one-off inspection, but it does not scale nicely when you want every layer.
What to Look For
Once you capture output gradients, a few checks are especially useful:
- gradients that are all zeros, which may indicate dead activations or disconnected paths
- extremely tiny norms, which can suggest vanishing gradients
- very large norms, which may indicate exploding gradients
- unexpected
Nonevalues, which often mean the tensor was not part of the backward path
Gradient inspection is most useful when paired with layer names, tensor shapes, and summary statistics such as min, max, and norm.
Common Pitfalls
The first pitfall is reading layer.weight.grad and assuming it represents the output gradient. It does not.
Another pitfall is forgetting retain_grad() on non-leaf tensors when you expect to inspect .grad directly.
A third pitfall is registering hooks and never removing them. That can create confusing repeated output or memory issues in longer-running training processes.
Finally, remember that if a tensor does not affect the chosen loss, it may not receive a gradient at all. In that case, hooks may not fire or gradients may be None.
Summary
- Output gradients are different from parameter gradients
- The most practical per-layer method is a forward hook that attaches a tensor hook to each output
- '
register_full_backward_hookcan help, but tensor hooks are often easier to reason about' - Use
retain_grad()when inspecting a specific intermediate activation - Watch for zero, tiny, huge, or missing gradients when debugging training behavior

