Stacking more convolutional layers should let a network learn richer features — but past a certain depth, plain stacks of convolutions actually get harder to train, not easier: gradients have to flow back through every layer, and if a layer's Jacobian shrinks the gradient even slightly, dozens of layers compound that shrinkage until the earliest layers barely learn at all. A residual (skip) connection sidesteps this by adding a layer's input directly to its output, giving the gradient a direct, unobstructed path backward through the addition, regardless of how the convolution in between behaves.
Theory says: run the input through a convolution that is padded so its output has the exact same shape as the input, add the original input back to that convolution's output, then apply a nonlinearity. The network only has to learn a "residual" correction on top of the identity — if the best thing to do is nothing at all, it just has to learn to output all zeros from the convolution.
Implement residual_block(x, kernel) against that reasoning, reusing the existing multi-filter convolution and ReLU.
x: shape (C, H, W) — a single feature map, no batch dimension.kernel: shape (C, C, 3, 3) — same number of output channels as input channels, since the result must be addable back to x.H and W, so a 3x3 convolution leaves the spatial size unchanged.x.x and kernel are never modified in place.Open one at a time. Each gives away a little more than the last.
np.pad(x, ((0, 0), (1, 1), (1, 1))) pads only the height and width axes by 1, leaving the channel axis untouched — exactly what a "same"-padded 3x3 convolution needs.
Run conv2d_multi_filter on the padded input, but add the result to the original, unpadded x — then pass that sum through relu_forward.
No results yet
Run your code to check it against the examples, or submit to run every test.