If tensors are the building blocks of PyTorch, then Autograd is the engine that enables neural networks to learn.
Autograd is PyTorch’s automatic differentiation system. It records every mathematical operation performed on tensors and automatically computes gradients using backpropagation.
Without Autograd, training modern deep learning models would require manually deriving and implementing derivatives for millions of parameters an impossible task for most real-world neural networks.
Why Do We Need Autograd? #
Training a neural network means adjusting parameters (weights and biases) so that the prediction error becomes smaller.
This adjustment depends on gradients.
Suppose we have
The derivative isdxdy=2x
Finding this derivative manually is trivial.
But a neural network is more likeLoss=f(g(h(k(x))))
where each layer depends on the previous one.
To compute
we repeatedly apply the Chain Rule.
Doing this manually for millions of parameters is infeasible.
PyTorch solves this automatically.
How Autograd Works #
Autograd builds a Dynamic Computation Graph while your program executes.
Input Tensor
│
▼
Linear Layer
│
▼
Activation
│
▼
Loss
Forward Pass #
PyTorch records every operation.
x → y=x² → z=sin(y)
Instead of storing only values, PyTorch also remembers:
- operation performed
- parent tensors
- gradient functions
Backward Pass #
Calling
loss.backward()
makes PyTorch traverse the graph in reverse.
Loss
↑
Activation
↑
Linear Layer
↑
Input
The chain rule is applied automatically.
requires_grad=True #
Gradient tracking is disabled by default.
Enable it using
x = torch.tensor(3.0, requires_grad=True)
Now every operation involving x becomes part of the computation graph.
Example 1 — Computing a Simple Derivative #
import torch
x = torch.tensor(3.0, requires_grad=True)
y = x ** 2
y.backward()
print(x.grad)
Output
tensor(6.)
Why? #
Sincedxdy=2x
Atx=3
Autograd computed the derivative automatically.
Example 2 — Understanding the Chain Rule #
import torch
x = torch.tensor(4.0, requires_grad=True)
y = x ** 2
z = torch.sin(y)
z.backward()
print(x.grad)
Mathematically,
Using the chain rule,dxdz=cos(x2)×2x
PyTorch produces exactly this result without requiring us to derive the equation manually.
This illustrates why Autograd is so powerful for deep neural networks composed of many nested operations.
Example 3 — Training a Single Neuron #
Suppose we want to predict whether a student gets placed based on CGPA.
Forward computation:
z = w*x + b
y_pred = torch.sigmoid(z)
loss = binary_cross_entropy_loss(y_pred, y)
Instead of deriving
- ∂Loss/∂Prediction
- ∂Prediction/∂z
- ∂z/∂w
- ∂z/∂b
manually,
we simply write
loss.backward()
print(w.grad)
print(b.grad)
PyTorch automatically computes
- gradient of weight
- gradient of bias
Working with Multiple Values #
Autograd also supports vectors and matrices.
Example from the notebook:
x = torch.tensor([1.0,2.0,3.0], requires_grad=True)
y = (x**2).mean()
y.backward()
print(x.grad)
Each element receives its own gradient.
tensor([...])
This is exactly how gradients are computed for batches of data during training.
Accessing Gradients #
Gradients are stored inside
tensor.grad
Example
print(x.grad)
print(w.grad)
print(b.grad)
The .grad attribute becomes available only after calling
backward()
Gradient Accumulation #
An important PyTorch behavior is that gradients accumulate.
Example:
y.backward()
print(x.grad)
x.grad.zero_()
Without clearing gradients, every subsequent call to backward() adds new gradients to the existing ones.
During training, optimizers therefore perform
optimizer.zero_grad()
loss.backward()
optimizer.step()
every iteration.
Disabling Gradient Tracking #
Gradients are needed only during training.
During inference, gradient computation wastes memory.
Your notebook demonstrates three common approaches.
1. requires_grad_(False) #
x.requires_grad_(False)
2. detach() #
z = x.detach()
The detached tensor shares data but no longer belongs to the computation graph.
3. torch.no_grad() #
with torch.no_grad():
prediction = model(x)
This is the preferred approach during inference because it reduces memory usage and improves execution speed.
Complete Training Workflow #
A simplified training iteration looks like this:
# Forward Pass
prediction = model(x)
# Compute Loss
loss = criterion(prediction, target)
# Clear Previous Gradients
optimizer.zero_grad()
# Backpropagation
loss.backward()
# Update Parameters
optimizer.step()
This sequence is repeated for every batch throughout training.
Best Practices #
✔ Enable requires_grad=True only for trainable parameters.
✔ Always clear gradients before backpropagation.
✔ Use torch.no_grad() during inference.
✔ Inspect .grad for debugging and understanding optimization behavior.
✔ Remember that PyTorch builds computation graphs dynamically during execution.
Key Takeaways #
- Autograd is PyTorch’s automatic differentiation engine.
- It constructs a dynamic computation graph during the forward pass.
- Calling
backward()automatically applies the chain rule to compute gradients. - Computed gradients are stored in the
.gradattribute. - Gradients accumulate unless they are explicitly cleared.
- Disable gradient tracking during inference using
torch.no_grad()or.detach(). - The examples in this notebook—from simple derivatives to training a perceptron—demonstrate the exact workflow used in real-world deep learning models.
PyTorch Autograd Quiz #
Q.1 What is the primary function of the PyTorch Autograd module?
To automatically optimize hyperparameters like learning rate.
To visualize the neural network architecture during training.
To convert Python code into low-level C++ for faster execution.
To provide automatic differentiation for all operations on tensors.
Explanation
PyTorch’s Autograd module provides automatic differentiation by tracking tensor operations and computing gradients automatically during backpropagation. This enables efficient optimization of neural network parameters.
Q.2 Which attribute must be set to True when defining a tensor to track its operations for gradient calculation?
track_history
compute_derivative
requires_grad
enable_grad
Explanation
The requires_grad=True attribute tells PyTorch to record all operations performed on the tensor so that gradients can be computed automatically during the backward pass.
Q.3 In the context of Autograd, what does the 'Backward' pass achieve?
It calculates the final loss value based on predictions and targets.
It resets the weights of the neural network to their initial values.
It computes the gradients of a scalar value with respect to all leaf tensors.
It reverses the order of the input data to prevent overfitting.
Explanation
The backward pass applies the chain rule to compute gradients of the output (typically the loss) with respect to all leaf tensors that have requires_grad=True. These gradients are stored in each tensor’s .grad attribute.
Q.4 If you have a tensor x=3 with requires_grad=True and y=x², what will x.grad be after calling y.backward()?
2.0
3.0
9.0
6.0
Explanation
For y = x², the derivative is dy/dx = 2x. Substituting x = 3 gives 2 × 3 = 6. Therefore, after calling y.backward(), x.grad will be 6.0.
Q.5 PyTorch represents the computation history as a Directed Acyclic Graph (DAG). In this graph, what do the 'Leaf' nodes represent?
The optimized gradients after the weights have been updated.
The final loss value calculated at the end of the network.
Input tensors for which the user has requested gradient tracking.
Intermediate activation functions like Sigmoid or ReLU.
Explanation
Leaf nodes are the original input tensors created by the user with requires_grad=True. These tensors do not result from other operations, and Autograd stores their computed gradients in the .grad attribute after backpropagation.
Q.6 Why is it necessary to call x.grad.zero_() when performing multiple gradient calculations in a loop?
To prevent the model from overfitting by regularizing the gradients.
To release the GPU memory used by the tensor during the backward pass.
Because Autograd only works if the initial gradient value is zero.
Because PyTorch accumulates gradients by adding them to the existing values in .grad.
Explanation
PyTorch accumulates gradients by default. Calling x.grad.zero_() (or optimizer.zero_grad()) clears previously stored gradients so that each backward pass computes gradients only for the current iteration.
Q.7 Which method would you use to temporarily disable gradient tracking during model evaluation to save memory?
tensor.stop_tracking()
x.requires_grad_(True)
with torch.no_grad():
y.backward(track=False)
Explanation
The with torch.no_grad(): context manager disables gradient tracking during inference. This reduces memory usage and improves execution speed because PyTorch does not build a computation graph.
Q.8 What is the result of applying .detach() to a tensor that is currently part of a computation graph?
It calculates the second-order derivative (Hessian) of the tensor.
It forces the tensor to only accept integer values.
It removes the tensor from the computer's RAM entirely.
It returns a new tensor that shares the same data but does not require gradients.
Explanation
The detach() method returns a new tensor that shares the same underlying data as the original tensor but is disconnected from the computation graph, so Autograd no longer tracks operations performed on it.
Q.9 Which function is commonly used to compute gradients for a scalar loss value in PyTorch?
loss.forward()
loss.backward()
loss.compute_grad()
torch.gradient(loss)
Explanation
The backward() method computes gradients of the loss with respect to all tensors that have requires_grad=True. The computed gradients are stored in each tensor’s .grad attribute.
Q.10 What is the primary purpose of the computation graph built by Autograd during the forward pass?
To permanently store the neural network architecture on disk.
To record operations so gradients can be computed efficiently during backpropagation.
To convert tensors into NumPy arrays automatically.
To optimize GPU memory allocation before training starts.
Explanation
During the forward pass, Autograd constructs a computation graph that records all tensor operations. This graph is later traversed in reverse during backpropagation to efficiently compute gradients using the chain rule.