The training loop — fill in two holes
In the last step you saw the parts one at a time. Now they are put together into a training loop — and you fill in the two missing pieces.
The loop in words
For each epoch, for each batch:
1. loss ← ask the model how wrong it was on this batch
2. clear the gradients left over from the previous batch
3. backward loss.backward() — fills in the gradients
4. step move every weight against its gradient
After each epoch the loop measures and prints the loss and the accuracy — the
evaluate function for that is already written.
Notice the order: gradients are cleared before backward(), not after it.
After backward() they are needed — the step is made from them.
Your job
Create train.py and test_train.py (see the panel on the right).
train.py compiles and runs, but learns nothing. The helpers make_loader and
evaluate are already written (nothing new in them), and fit has two gaps marked
HOLE 1 and HOLE 2:
- HOLE 1 — clearing the gradients. The optimizer is already built in the line
optimizer = torch.optim.SGD(...); you need one command of it. - HOLE 2 — the step itself. Again one optimizer command.
Replace each pass with a real line. Nothing else needs to change.
What does
fitdo when the model is not aLinearClassifierbut a plainnn.Linear? Exactly the same.fitdoes not care what it is given as long as it is annn.Module. That is why the test usesnn.Linear(2, 2)and two clouds of points — you write the model class in the next step.
Run the test before filling anything in
First as it is. In the project folder:
$ python -m unittest
.FF..
======================================================================
FAIL: test_accuracy_rises_on_separable_data (test_train.TestFit...)
----------------------------------------------------------------------
AssertionError: 0.5 not greater than 0.9 : accuracy 50.0% -> 50.0%
======================================================================
FAIL: test_one_epoch_is_exactly_the_manual_update_of_lesson_4 (test_train.TestFit...)
----------------------------------------------------------------------
AssertionError: False is not true : weight differs from the manual loop by up to 0.1795
----------------------------------------------------------------------
Ran 5 tests in 1.2s
FAILED (failures=2)
(The real output has a Traceback with file paths before each AssertionError —
I kept only the essence.)
This is not an error but a starting point. The skeleton learns nothing: the
accuracy stays at exactly 50.0 % (two classes, a coin toss), and the weights never
move — so they differ from the manual loop. The make_loader and evaluate tests
pass: those functions are already correct.
Fill both holes and run again — you should see OK.
How does the test know? The second test replays lesson 4's manual loop
(p -= 0.1 * p.grad, p.grad.zero_()) from the same starting model over the same
4 batches, then compares the weights with what your fit produced. So it is not
just "does accuracy rise" — it is "is this the same computation".
When it does not pass
weight differs from the manual loop by up to 0.2776, but the accuracy test passes — you skippedzero_grad()(or wrote justoptimizer.zero_gradwithout parentheses, which does nothing and raises no error). It still learns, but the gradients pile up: every batch pushes the weights by the leftovers of old batches too.accuracy 50.0% -> 50.0%andup to 0.1795— no step was made. The usual cause:optimizer.stepwithout()— Python raises nothing, it just does not run it.- both tests fail although
step()andzero_grad()are both there — most likely swapped. A step beforebackward()would use gradients that are not filled in yet.