The Adam optimizer and build_model — write it yourself
The model is ready. Now it has to be trained — and here plain SGD starts to fail for the first time.
Why we change the optimizer
The same MLP(128), the same slice of data, the same seed, 10 epochs, batch 64.
Only the optimizer and lr change. All of this is my experiment, nothing from a
"textbook":
sgd lr 0.01 loss 0.6689 train acc 76.2%
sgd lr 0.1 loss 0.9140 train acc 68.5%
sgd lr 0.5 loss 0.7285 train acc 74.1%
adam lr 0.001 loss 0.3622 train acc 86.3%
adam lr 0.01 loss 0.3219 train acc 86.8%
adam lr 0.1 loss 1.2670 train acc 45.6%
Three SGD lr values — and none comes close to what Adam gives on the first try.
I am not saying SGD is "bad": I tried only three values and 10 epochs; with more
epochs or momentum it would catch up. I am saying that here, with these
settings, Adam hits first, and that its lr matters differently: 0.1 is already
too much for it.
What Adam does: for every number separately it remembers how large and how consistent its latest gradients were, and picks that number's step accordingly. So:
- the first step moves every number by roughly
lr, whatever its gradient — the test checks exactly that; - numbers with noisy gradients move more carefully.
In your code this is one word: SGD → Adam. optimizer.zero_grad(),
backward() and step() stay the same.
Your job
1. train.py: change torch.optim.SGD to torch.optim.Adam. Do not change
anything else in fit. The file also grew in other ways: argparse (command-line
parameters) and main() are provided — they just wire the parts together.
2. build_model(name, hidden) — an empty skeleton. The contract is in the
docstring: "linear" returns LinearClassifier(), anything else MLP(hidden=hidden).
3. test_train.py: TestBuildModel tests were added and one test was
replaced. Lesson 5's test that checked "exactly SGD's arithmetic" no longer applies
— fit is no longer SGD. It was replaced by another: "after one step every number
moved by about lr". That is not forgotten; it is a deliberate retirement.
Does
fitin the panel have Adam? Yes — the change is already visible there.build_modelstill hasNotImplementedError.
Run it
$ python -m unittest
While build_model is unwritten: two NotImplementedError errors. While fit still
uses SGD, only one test fails:
FAIL: test_first_step_moves_every_number_by_about_lr_as_adam_does
AssertionError: False is not true : weight moved by [0.005079716444015503, 0.004353374242782593, 0.005079731345176697, 0.004353374242782593], want about 0.01 each
(SGD with lr=0.01 moves numbers not by 0.01 but by lr · gradient — here ~0.005.)
When both are done: OK, 23 tests.
When it does not pass
AssertionError: 101770 != 109386—build_modelignoreshidden(you wroteMLP()instead ofMLP(hidden=hidden)).weight moved by [...], numbers not ~0.01 — the optimizer is not Adam (you probably leftSGD).