Code

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_matrix loop: for every image, write one tally into the table. The row is the truth t, the column is the model's answer p.
  • HOLE 2 — in class_scores: precision and recall. You divide the same tp by 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 whole i-th row; cm[:, i] is the whole i-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 wrote cm[p, t] instead of cm[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 and 0.3333… != 0.5 in 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 class fox was 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.