Leaf Nodes and the Computation Graph
Calling .backward on the loss computes gradients for the leaf nodes of the graph, so you do not need to derive them by hand.
Why You Rarely Compute Gradients by Hand
Up to this point, gradients have been computed by calling the grad function directly. That approach is useful for experimenting, for debugging, and for showing the underlying ideas. In practice, though, PyTorch offers a higher-level tool that does the work for you. This article starts with the code that uses that tool and then explains what happens when the code runs.
Imagine a loss value that has already been computed from a model's parameters, where the parameters w1 and b are tensors. A short program calls backward on the loss and then prints the gradients that PyTorch stored for two of those parameters. Notice that the program never writes a derivative formula. The only calculus step in it is the single call to backward. The calculus is still happening underneath; you simply no longer write it out yourself.
What do you think happens?
Before backward() has been called on the loss, what does the .grad attribute of the parameter w1 hold?
Reveal answer
No gradient has been computed for w1 yet, so there is no gradient value to read from its .grad attribute.
Gradients are stored in each leaf tensor's .grad attribute when backward runs. Until that call happens, the gradient has not been computed, so there is nothing to read.
Following the Gradient Back from the Loss
The forward pass builds a computation graph: the loss was produced by a sequence of operations, and each operation links its inputs to its output. Calling backward on the loss starts at the loss and traverses that graph in reverse. The traversal reaches the leaf nodes, which are the tensors the graph was built from, such as the parameters w1 and b. Gradients are computed for all of these leaf nodes at once.
Read the diagram from top to bottom. The path starts at the loss, and the backward call moves through the graph until it reaches the leaf nodes. Each leaf node receives its gradient, and that gradient becomes available through the matching .grad attribute. The parameters w1 and b are the places where the values finally land. You do not have to follow the chain of calculus steps yourself; the traversal handles it.
Leaf Nodes Versus Intermediate Results
A leaf node is a tensor that the graph was built from, such as a parameter like w1 or b, rather than a value produced by an intermediate operation. Intermediate results are the values created along the way during the forward pass.
The distinction matters because the important detail is which tensors receive gradients. Calling backward on the loss computes gradients for the leaf nodes. The intermediate results sit between the leaves and the loss, and the gradients you read come from the leaves. This is why you read gradients from parameters like w1 and b and not from every value that appeared during the computation.
| Tensor role | Where it comes from | Does it receive a .grad from backward on the loss? |
|---|---|---|
| Leaf node (for example w1 or b) | A tensor the graph was built from | Yes, gradients are computed for it |
| Intermediate result | A value produced by an intermediate operation in the forward pass | Not the target of the stored gradients described here |
Leaf nodes versus intermediate results in the computation graph
The .grad Attribute Before and After backward
Each leaf tensor has its own .grad attribute, and that is where its gradient is stored. The gradient for a given parameter is looked up directly from that parameter. Before backward runs, the attribute holds no computed gradient. After backward finishes, the attribute holds the gradient that PyTorch computed for that leaf.
The source output shows one gradient value for w1 and one for b. Those are the values you read from the two .grad attributes after the backward call. Reading them is the only step you perform; the computation was done when backward ran.
Layers of a Multilayer Perceptron
The source moves from gradients to a concrete network that PyTorch is often used to build: the multilayer perceptron. A multilayer perceptron is a fully connected neural network with multiple hidden layers between its input and output layers. Fully connected means that the units in one layer connect to the units in the next layer.
The source illustrates this structure with two hidden layers. Each node in its drawing stands for one unit in its layer, and the drawing uses very few nodes per layer so the structure is easy to see. The input layer is where data enters, the hidden layers sit between input and output, and the output layer is where the network produces its result. The source does not specify exactly which layer each named parameter such as w1 or b belongs to, so this article does not assign them to specific layers.
Checking Gradients When One Is Missing
When a gradient is missing or unexpected, the source suggests checking three things: the call order, whether the tensor is a leaf node, and which loss was differentiated.
Common mistake Reading .grad before calling backward
Why it failsThe gradient is stored in .grad only when backward runs, so reading earlier gives you no computed value.
Do this insteadCall backward on the loss first, then read the .grad attribute.
Common mistake Expecting a gradient from a tensor that is not a leaf node
Why it failsCalling backward computes gradients for the leaf nodes of the graph, not for every intermediate result.
Do this insteadCheck whether the tensor is a leaf node such as a parameter like w1 or b before looking for its gradient.
Common mistake Differentiating a different loss than the one you intended
Why it failsThe gradients stored in .grad come from the loss that backward was called on.
Do this insteadConfirm that backward was called on the loss you meant to optimize.
Predicting Which Gradients Appear
A loss is computed from parameters w1 and b, and an intermediate result is created along the way during the forward pass. After calling backward on the loss, which tensors should have a gradient value in their .grad attribute, and where would you look to read each one?
Hints
- Ask which tensors are leaf nodes of the graph.
- Remember where gradients are stored: in each leaf tensor's .grad attribute.
Key Takeaways
- Calling backward on the loss computes gradients for the leaf nodes of the computation graph, so you do not need to derive them by hand.
- A leaf node is a tensor the graph was built from, such as w1 or b; gradients are stored in each leaf tensor's .grad attribute, which is where you read them.
- Before backward runs, the .grad attributes hold no computed gradient; after it finishes, the leaf tensors' .grad attributes hold their gradients.
- A multilayer perceptron is a fully connected network with hidden layers between its input and output layers.
- When a gradient is missing, check the call order, whether the tensor is a leaf node, and which loss was differentiated.