Chapter 02
Autograd
How PyTorch computes gradients for you: it records operations on tensors that need gradients, then walks that record backwards.
requires_grad
- What
- A flag on a tensor that tells autograd to track operations on it. Model parameters have it set; ordinary data tensors do not.
- Why
- Only tracked tensors get a
.grad, and tracking costs memory, so it is on for what you train and off for everything else.
w = torch.randn(3, requires_grad=True)
x = torch.tensor([1.0, 2.0, 3.0])
y = (w * x).sum()
y.requires_gradWatch out
torch.randn(3, requires_grad=True).cuda() is the result of an operation, not a leaf, so it gets no .grad; create it on the device instead.
The computation graph
Also called dynamic graph, grad_fn
- What
- As tracked operations run, autograd builds a graph linking each result to its inputs through a
grad_fn. It is rebuilt on every forward pass. - Why
- Because the graph is dynamic, ordinary Python control flow (loops, ifs) works inside models.
Watch out
Keeping tensors like the loss in a Python list across steps keeps their whole graph alive; store loss.item() instead.
backward and .grad
- What
- Calling
.backward()on a scalar computes the gradient of it with respect to every leaf tensor that requires grad, and adds the result into each tensor's.grad. - Why
- This is how the loss tells each weight which way to move.
w = torch.tensor([2.0, -1.0], requires_grad=True)
x = torch.tensor([3.0, 4.0])
loss = ((w * x).sum() - 1) ** 2
loss.backward()
w.gradWatch out
Gradients accumulate: without optimizer.zero_grad() each step adds to the previous gradients.
no_grad and inference_mode
- What
torch.no_grad()turns off graph recording inside its block.torch.inference_mode()does the same with extra optimisations for code that never needs gradients.- Why
- Evaluation and prediction use less memory and run faster without the graph.
model.eval()
with torch.inference_mode():
preds = model(batch).argmax(dim=1)
with torch.no_grad():
for p in model.parameters():
p.clamp_(-1, 1)Watch out
Tensors created under inference_mode cannot later be used in autograd; use no_grad if they might be.
detach
- What
tensor.detach()returns a tensor that shares the data but is cut off from the graph, so no gradient flows through it.- Why
- Logging values, stopping gradients into a target network, or converting to NumPy with
.detach().cpu().numpy().
features = encoder(x)
frozen = features.detach()
value = loss.detach().item()
arr = preds.detach().cpu().numpy()Watch out
Changing a detached tensor in place also changes the original, because the memory is shared.
In-place operations
Also called add_, trailing underscore
- What
- Methods ending in an underscore, such as
add_andzero_, change a tensor in place instead of returning a new one. - Why
- They save memory, which is why optimisers use them to update weights.
Watch out
An in-place change to a value autograd saved for backward raises "modified by an inplace operation"; use the out-of-place version.