Convolution — fill in two holes
In the last step you saw a convolution in tables. Now the same thing is written as a Python function — and you fill in the two missing pieces.
The algorithm
out ← table of zeros [H - kh + 1, W - kw + 1]
for each row i:
for each column j:
patch ← image[i .. i+kh, j .. j+kw]
out[i, j] ← sum( patch * filter ) # cell by cell
return out
This is exactly what you did to get 27: a patch, the cell-by-cell product, the sum. Two loops just repeat it for every position.
Your job
Create two files in the project folder (see the right panel): conv_by_hand.py
and test_cnn.py.
conv_by_hand.py is complete and runnable, but wrong: the loops are written,
two places are missing. They are marked HOLE 1 and HOLE 2:
- HOLE 1 — the patch. Right now
image[:kh, :kw]always takes the same corner. Rewrite it so the corner is(i, j). - HOLE 2 — one line: multiply the patch by the filter cell by cell and add
it up. Right now it holds
0.0.
Nothing else needs to change.
test_cnn.py is the verdict — you do not write it, you pass it. For now it only
tests the convolution: the output shape, the very numbers you saw in step 2
(27, 36, −27 …), and a comparison with F.conv2d on random numbers.
Run it before filling anything in
Run it exactly as it is. From the project folder:
$ python -m unittest test_cnn
F.F
======================================================================
FAIL: test_matches_torch_on_random_input (test_cnn.ConvByHandTest.test_matches_torch_on_random_input)
----------------------------------------------------------------------
Traceback (most recent call last):
...
AssertionError: Tensor-likes are not close!
Mismatched elements: 63 / 63 (100.0%)
Greatest absolute difference: 10.376058578491211 at index (4, 3) (up to 1e-05 allowed)
Greatest relative difference: 1.0 at index (0, 0) (up to 1.3e-06 allowed)
======================================================================
FAIL: test_the_numbers_from_the_lesson (test_cnn.ConvByHandTest.test_the_numbers_from_the_lesson)
----------------------------------------------------------------------
Traceback (most recent call last):
...
AssertionError: False is not true : got
tensor([[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]])
----------------------------------------------------------------------
Ran 3 tests in 0.590s
FAILED (failures=2)
(Shortened: the Traceback bodies are cut, and your timing and paths will
differ.)
That is not an error — it is the starting point. F.F means: the shape is right
(the middle test passed) and every number is zero. The skeleton never looks at
the picture.
Fill in both holes and run again — you should see OK.
When it does not pass
- Every number in the map is the same (27) — you left
image[:kh, :kw]. The patch does not slide. RuntimeError: The size of tensor a (2) must match the size of tensor b (3)— the patch is too small (e.g.i : i + kh - 1). It must be exactlykh × kw. You also get it when you swapiandjbetween rows and columns.- Numbers different but the right size — HOLE 2 forgot the filter
(
patch.sum()) or uses.mean()(the result is 9 times too small). RuntimeError: expand(torch.FloatTensor{[3, 3]}, size=[])— you wrotepatch @ kernel. That is a matrix product, which returns another 3 × 3 table, and a single cell only holds one number. You need*and.sum().