16. Gradient of a sum of squares
What does this PyTorch code print?
import torch
x = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)
y = (x ** 2).sum()
y.backward()
print(x.grad)
- A.tensor([1., 4., 9.])
- B.tensor([2., 4., 6.])
- C.tensor([1., 2., 3.])
- D.None
Show answer
Answer: B. tensor([2., 4., 6.])
The derivative of the sum of x squared with respect to each element is 2x. Autograd stores that in x.grad, giving [2., 4., 6.]. x.grad would be None only if x did not require gradients or backward was never called.
17. backward() on a vector
What happens when this code runs?
x = torch.randn(3, requires_grad=True)
y = x * 2
y.backward()
- A.x.grad becomes tensor([2., 2., 2.]) automatically
- B.RuntimeError: grad can only be implicitly created for scalars
- C.x.grad becomes the single value tensor(6.)
- D.Nothing happens, because y is not called a loss
Show answer
Answer: B. RuntimeError: grad can only be implicitly created for scalars
backward() with no argument needs a single-number output. For a vector y, call y.sum().backward() or pass a gradient tensor such as y.backward(torch.ones_like(y)), which then gives x.grad = [2, 2, 2].
18. What detach() does
In a training loop, z = model_a(x).detach() is fed into model_b. What happens when you call loss.backward() on model_b's loss?
- A.Only model_b gets gradients
- B.Both models get gradients
- C.Only model_a gets gradients
- D.backward() raises an error
Show answer
Answer: A. Only model_b gets gradients
detach() returns a tensor that shares data but is cut off from the autograd graph. Gradients stop there, so model_a's parameters get no .grad from this loss. This is used, for example, for target networks or frozen feature extractors.
19. torch.cat or torch.stack
a and b both have shape (4, 8). What are the shapes of torch.cat([a, b], dim=0) and torch.stack([a, b], dim=0)?
- A.(2, 4, 8) and (8, 8)
- B.(4, 16) and (2, 4, 8)
- C.(8, 8) and (2, 4, 8)
- D.(8, 8) and (4, 8, 2)
Show answer
Answer: C. (8, 8) and (2, 4, 8)
cat joins tensors along an existing dimension, so two (4, 8) tensors on dim 0 give (8, 8). stack adds a new dimension, giving (2, 4, 8). Use stack to build a batch from same-shaped samples.
20. Double versus float error
Inputs come from a NumPy float64 array: x = torch.from_numpy(arr). model(x) fails with "expected scalar type Float but found Double". What is the fix?
- A.Convert with x = x.float()
- B.Call model.double() on every batch
- C.Convert with x = x.long()
- D.Wrap the call in torch.no_grad()
Show answer
Answer: A. Convert with x = x.float()
NumPy defaults to float64, while nn layers use float32 weights. Converting inputs with .float() (or creating the array as float32) matches them. model.double() would also work but doubles memory and is much slower on GPUs.
21. Loss for a single-logit classifier
Your binary classifier ends with nn.Linear(64, 1) and no activation. Which loss is the right choice?
- A.nn.BCELoss
- B.nn.CrossEntropyLoss with 1 class
- C.nn.BCEWithLogitsLoss
- D.nn.MSELoss on raw logits
Show answer
Answer: C. nn.BCEWithLogitsLoss
BCEWithLogitsLoss combines the sigmoid and binary cross-entropy in one numerically stable step and expects raw logits. BCELoss expects probabilities, so you would need a separate sigmoid, which is less stable. Its pos_weight argument also handles imbalance.
22. Target format for CrossEntropyLoss
Logits have shape (32, 5). For nn.CrossEntropyLoss with hard labels, what should the target tensor be?
- A.Shape (32,), dtype long, values 0–4
- B.Shape (32, 5) one-hot, dtype long
- C.Shape (32, 1), dtype float
- D.Shape (5,), dtype long
Show answer
Answer: A. Shape (32,), dtype long, values 0–4
With hard labels, CrossEntropyLoss expects class indices: one integer per example in a long tensor. Since PyTorch 1.10 it also accepts float class probabilities of shape (32, 5) for soft labels, but not a long one-hot tensor.
23. Freezing a pretrained backbone
You want to train only the new classifier head on a pretrained ResNet. What is the standard approach?
- A.Call model.eval() on the backbone only
- B.Set requires_grad=False on backbone parameters
- C.Wrap the head in torch.no_grad()
- D.Use a learning rate of zero for the head
Show answer
Answer: B. Set requires_grad=False on backbone parameters
Parameters with requires_grad=False get no gradients, so the optimiser will not change them and backward is cheaper. eval() only changes dropout and batch norm behaviour; no_grad on the head would stop it training.
24. Slow first step after torch.compile
After model = torch.compile(model), the first training step takes 90 seconds and later steps are faster than before. What explains the slow first step?
- A.torch.compile reinitialises all of the weights
- B.Graph capture and kernel compilation run on first call
- C.The first batch is always ten times larger
- D.Compiled models must re-download their weights
Show answer
Answer: B. Graph capture and kernel compilation run on first call
torch.compile traces the Python code into graphs and generates optimised kernels the first time it sees each input shape. That one-time cost pays off over many steps. Frequent shape changes can trigger recompiles, so dynamic or padded shapes help.
25. Loss jumps at each epoch start
Training loss jumps sharply at the start of each epoch, then falls again. The DataLoader has shuffle=False and the data is sorted by class. What is happening?
- A.The learning rate is reset to its peak at every epoch
- B.Batches hold one class at a time, so the model keeps shifting
- C.Dropout is switched off at the start of each epoch
- D.The model reloads its initial weights at each epoch
Show answer
Answer: B. Batches hold one class at a time, so the model keeps shifting
With class-sorted data and no shuffling, the model sees long runs of one class, adapts to it, then is surprised by the next. Each epoch repeats the pattern. Shuffling gives mixed batches and a smooth loss curve.
26. Using argmax inside a loss
A custom loss uses torch.argmax(logits, dim=1) to pick classes and compares them with labels. The model does not learn. Why?
- A.argmax gives no useful gradient
- B.argmax only works on the CPU
- C.argmax changes the batch size
- D.argmax makes all logits negative
Show answer
Answer: A. argmax gives no useful gradient
argmax outputs integer indices, which change in jumps, so no useful gradient flows back to the logits. Train with a differentiable loss like cross-entropy on probabilities, and use argmax only for reporting predictions.
27. DDP error about unused parameters
A model with an optional auxiliary head (used only on some steps) fails under DDP with "Expected to have finished reduction in the prior iteration". What is the usual fix?
- A.Lower the learning rate by 10×
- B.Use a much smaller batch size
- C.Pass find_unused_parameters=True
- D.Call zero_grad() twice per step
Show answer
Answer: C. Pass find_unused_parameters=True
DDP waits for gradients from every parameter before finishing the all-reduce. Parameters that got no gradient on a step leave it waiting. find_unused_parameters=True makes DDP detect them, at some cost; always computing every branch (scaled to zero if needed) avoids that cost.
28. Memory grows each training step
GPU memory rises every step until OOM. Which line is the likely cause?
total = 0
for x, y in loader:
loss = loss_fn(model(x), y)
loss.backward()
opt.step(); opt.zero_grad()
total += loss- A.opt.zero_grad() is called after opt.step()
- B.loss.backward() is called inside the loop
- C.loss_fn is created outside the loop
- D.total += loss keeps each step's graph alive
Show answer
Answer: D. total += loss keeps each step's graph alive
Adding the loss tensor makes total part of the autograd graph, so every step's graph and its saved tensors stay referenced. Use total += loss.item() or loss.detach() to keep only the number.
29. Memory format for CNNs
For mixed-precision CNN training on tensor-core GPUs, why does converting the model and inputs to torch.channels_last often help?
- A.It halves the number of channels
- B.It stores images as grayscale
- C.It turns convolutions into matmuls on the CPU
- D.Conv kernels on tensor cores prefer the NHWC layout
Show answer
Answer: D. Conv kernels on tensor cores prefer the NHWC layout
channels_last stores tensors as NHWC, which matches what cuDNN's fastest tensor-core convolution kernels expect. With NCHW, extra transposes may be needed. Values and shapes are unchanged; only the memory layout differs.
30. GradScaler skipped a step
With FP16 and torch.cuda.amp.GradScaler, a few steps are skipped early in training. What is happening?
- A.The optimizer ran out of memory and paused
- B.The data loader returned an empty batch
- C.Skipped steps mean the model has converged
- D.Gradients overflowed, so the step was skipped
Show answer
Answer: D. Gradients overflowed, so the step was skipped
GradScaler multiplies the loss by a large factor so small gradients do not underflow in FP16. If any gradient becomes inf or NaN, it skips that update and halves the scale; after a run of good steps it raises the scale again. Occasional skips are normal.