The confusion matrix — fill in two holes
In the last step the matrix appeared "out of thin air". Now we write the same table as code — and you fill in the two missing pieces.
Your job
Create evaluate.py and test_evaluate.py (see the panel on the right).
evaluate.py is complete and runs — it does not crash — but it gives wrong results. Two places are missing, marked HOLE 1 and HOLE 2:
- HOLE 1 — in the
confusion_matrixloop: for every image, write one tally into the table. The row is the trutht, the column is the model's answerp. - HOLE 2 — in
class_scores: precision and recall. You divide the sametpby two different sums. From the last step: recall is read along the row, precision down the column.
Nothing else needs changing. print_report is already written and only prints what your functions return.
A few PyTorch things you may need:
cm[i]is the wholei-th row;cm[:, i]is the wholei-th column;.sum()adds up;.item()turns a one-number tensor into an ordinary Python number.
test_evaluate.py is the verdict — you do not write it, you pass it. It holds the same six-image example from the last step, so you can check the answers by hand, too.
Run the test before you fill anything in
Run it exactly as it is:
$ python -m unittest
unittest prints long tracebacks; only the essentials are kept here:
FAIL: test_f1_combines_both
AssertionError: 0.0 != 0.4 within 4 places (0.4 difference)
FAIL: test_precision_is_a_column
AssertionError: 0.0 != 0.3333333333333333 within 4 places (0.3333333333333333 difference)
FAIL: test_recall_is_a_row
AssertionError: 0.0 != 0.5 within 4 places (0.5 difference)
FAIL: test_every_image_is_counted_once
AssertionError: 0 != 6
FAIL: test_rows_are_the_truth_columns_the_prediction
AssertionError: Lists differ: [[0, 0, 0], [0, 0, 0], [0, 0, 0]] != [[2, 1, 0], [1, 1, 0], [0, 1, 0]]
FAIL: test_report_prints_the_scores_per_class
AssertionError: '0.33' not found in 'dog 0.00 0.00 0.00 0'
Ran 7 tests in 0.002s
FAILED (failures=6)
That is not an error — it is the starting point. The skeleton creates the table but never fills it (all zeros), so every score is zero. Six tests fail; the seventh (never_predicted) passes by accident, because zero is exactly the answer it expects. Fill in both holes and run again — you should see OK.
When it does not pass
[[2, 1, 0], [1, 1, 1], [0, 0, 0]] != [[2, 1, 0], [1, 1, 0], [0, 1, 0]]— you wrotecm[p, t]instead ofcm[t, p]. The table is mirrored: rows became columns. Accuracy (the diagonal) does not change, so this mistake stays hidden for a long time — while precision and recall silently trade places.0.5 != 0.3333…in the precision test and0.3333… != 0.5in the recall test — you swapped row and column. Precision divides by the column sum (cm[:, i].sum()), recall by the row sum (cm[i].sum()).0.1666… != 0.3333…— you divided by the number of all images (cm.sum()), not by a row or a column.ZeroDivisionError: division by zero— the classfoxwas never predicted, so its column sum is 0, and Python does not let a zero division pass quietly. Make sure the divisor is never below 1:max(total, 1). The numerator is 0 too, so the result is 0.0.