Code

Choose on validation — write it yourself

You have at least two runs and two .json files. Now comes the one new function of this lesson, the one that carries out the first rule: decide on validation.

It looks trivial: max over one field. But this is exactly where it is easiest to slip, because every summary.json already holds both numbers, and test_accuracy is one word away from val_accuracy.

Pseudocode

choose_on_validation(runs):

if runs is empty: ValueError
best ← the first run
for each other run:
    if its val_accuracy is STRICTLY greater than best.val_accuracy:
        best ← this run
return best

table_lines(runs, chosen):

lines ← [header]
for each run (in the same order):
    line ← name, number of epochs, val_accuracy %, test_accuracy %
    if it is chosen: append "<- chosen on validation" to the line
    lines.append(line)
return lines

Two small things the test checks:

  • A tie. If two runs have the same validation number, the first wins, no matter which one has the better test. Dull, but otherwise test creeps back in — as a "tie-breaker".
  • Both numbers in every line. The table shows all runs with test, not only the winner. If you showed test only for the winner, you could not see whether the other runs were better or worse.

Your job

Create choose.py and test_choose.py (on the right). In choose.py, load_runs and main are ready (they read the files and print); two functions with raise NotImplementedError are missing.

test_choose.py uses the four rows from the table in the last step and a deliberately chosen trap: the row with the highest test (noaug_5ep, 94.0 %) is not the one to choose (noaug_stop2, 95.6 % on validation).

Run before you write

$ python -m unittest test_choose
...
Ran 9 tests in 0.002s
FAILED (errors=9)

(All nine are NotImplementedError.)

When it does not pass

  • AssertionError: 'noaug_5ep' != 'noaug_stop2' in …highest_validation_accuracy_wins and …cannot_change_the_choice — you pick by test_accuracy, not val_accuracy. The highest test is noaug_5ep.
  • AssertionError: 'b' != 'a' in …tie_goes_to_the_first_run… — two equal val, and you took the second: most likely test slipped in as a tie-breaker, e.g. key=(val, test). Keep only val; max itself takes the first of equals.
  • AssertionError: Lists differ: … in …does_not_modify_the_runs — your function changed the list (e.g. runs.sort(…)). Sort only a copy.
  • AssertionError: '89.0%' not found in 'aug_stop2 4 90.6% ' — a line of the table has no test number. A line must show both.
  • ValueError with an empty list needs no separate line: max([]) raises ValueError itself. But now you know why that test exists.

Run it

python choose.py noaug.json aug.json

For our stand-in:

run            epochs     val    test
noaug               6   95.6%   93.8%  <- chosen on validation
aug                 4   90.6%   89.0%

the test folder was evaluated 2 times; report that number

And with all four runs from the previous step:

run            epochs     val    test
aug_stop2           4   90.6%   89.0%
aug_8ep             8   93.1%   92.2%
noaug_stop2         6   95.6%   93.8%  <- chosen on validation
noaug_5ep           5   93.8%   94.0%

Notice the last row: it is better on test (94.0 %), and we did not choose it. The difference from the winner is one image out of 400. That is how it should be: not choosing what looks prettier on test is the whole rule.

One more line: "the test folder was evaluated 4 times". Take that number to your report.