Code

Four functions — write them yourself

The gradient and the flattening work. The remaining four functions you write without holes this time: the skeleton stands, the body does not.

You met three of the four in step 2, in the tables; the fourth is the same flattening, only for a whole stack of pictures.

Four functions

Each is a single line, but each teaches one thing about shapes.

affine(a, scale, shift) — a * scale + shift for every element at once. And one condition the test checks: a must not change. Return a new tensor. It is the same rule you will keep throughout the course: never touch the input data.

add_row(a, row) — add a [3] row to every row of a [2, 3]. Broadcasting does all the work here; no copying by hand.

weighted_sum(x, w, b) — x is [n, k], w is [k, m], b is [m]. The result is [n, m]. This is lesson one's neuron definition, only written for many neurons and many inputs at once.

flatten_batch(images) — the whole stack at once: [n, 28, 28] → [n, 784]. Row i must exactly match flatten_image(images[i]). Here is the trap: do not write n by hand. The batch size varies — there will be thousands of training pictures and one test picture.

Your job

tensors_demo.py (right panel) has grown: four functions with raise NotImplementedError were added at the bottom. Above, your loss_and_grads and flatten_image are unchanged.

The panel shows the skeleton, not your file: the HOLE places are still empty in it. In your copy they are filled in — and stay that way.

test_tensors_demo.py grew as well: it now checks all six functions. One of the new tests runs flatten_batch with n = 1 and n = 5: only one of two "correct-looking" versions survives both.

Run python -m unittest. If you run it before writing anything (with your holes filled in), you get:

$ python -m unittest
....EEEEE
ERROR: test_add_row_broadcasts (...)
NotImplementedError
ERROR: test_affine (...)
NotImplementedError
ERROR: test_affine_does_not_change_its_input (...)
NotImplementedError
ERROR: test_flatten_batch (...)
NotImplementedError
ERROR: test_weighted_sum (...)
NotImplementedError
----------------------------------------------------------------------
Ran 9 tests in 0.002s

FAILED (errors=5)

Four dots are your earlier tests, still passing. Five Es are five functions still empty (test_affine and test_affine_does_not_change_its_input both touch affine). Write them one at a time and the Es go away.

When it does not pass

  • affine passes, but does_not_change_its_input fails — you wrote a *= scale. *= on a tensor works in place: it changes the very a the caller owns. Write a * scale, no =.
  • The size of tensor a (2) must match the size of tensor b (3) at non-singleton dimension 0 — add_row with row.reshape(3, 1). Now the row is a column, trying to add 3 to 2 rows. Shapes compare from the right: [2, 3] and [3] — fine, [2, 3] and [3, 1] — not.
  • The size of tensor a (2) must match the size of tensor b (3) at non-singleton dimension 0 with weighted_sum — you wrote x * w + b instead of x @ w + b. * is element by element, not the matrix product.
  • mat1 and mat2 shapes cannot be multiplied (3x1 and 2x3) — w @ x instead of x @ w. The product is not commutative.
  • shape '[5, 784]' is invalid for input of size 784 — flatten_batch with reshape(5, 784). Passes one test, falls over on the other. Use len(images) or -1.