EasyResNet

Convolutional Block

Deep Residual Learning

Easy

Problem

Implement a matrix-based projection residual block. Matrix multiplication stands in for convolution so the exercise isolates the dimension-changing shortcut used by ResNet.

y = \operatorname{ReLU}\!\left(\operatorname{ReLU}(xW_1)W_2 + xW_s\right)

Here, (W_1) and (W_2) form the main path, while (W_s) projects the shortcut to the output width. Return y as a nested list rounded to four decimal places.

Theory

The convolutional block (also called the projection shortcut block) is the variant of the residual block from He et al. (2015) used whenever the input and output dimensions differ. Unlike the identity block, which passes the input through unchanged on the skip path, the convolutional block applies a learned projection matrix W_s to the shortcut so that the addition F(x) + W_s x is dimensionally valid. This block appears at every stage transition in ResNet architectures, where the number of channels increases.


What It Is

A residual block computes y = F(x) + \text{shortcut}(x), where F(x) is the main path through stacked layers and the shortcut provides a direct connection from input to output. In the identity block, the shortcut is simply x itself, which works only when input and output have the same dimensionality. The convolutional block handles the case where they do not match.

When the channel count changes between stages, the raw input x cannot be added directly to F(x) because their shapes differ. The convolutional block solves this by placing a learned linear projection W_s on the shortcut path. The projection transforms x into the same dimensional space as the main path output, making the element-wise addition possible. The output becomes y = F(x) + W_s \cdot x.

In the original paper, He et al. describe this as "option B": using a 1 \times 1 convolution on the shortcut to match dimensions. In a simplified fully-connected setting, the projection is a matrix multiply that maps from the input dimension to the output dimension. The key principle is the same: a learned linear transformation aligns the shortcut to the main path.


Key Equations

Let x \in \mathbb{R}^{d_{in}} be the input vector. The main path applies two linear layers with ReLU activations, and the shortcut path applies one linear projection.

Main path, layer 1. Apply the first weight matrix and ReLU:

h = \text{ReLU}(x \cdot W_1)

Here W_1 \in \mathbb{R}^{d_{in} \times d_{out}} maps from input dimension to output dimension. ReLU introduces nonlinearity. After this step, h \in \mathbb{R}^{d_{out}}.

Main path, layer 2. Apply the second weight matrix and ReLU:

z = \text{ReLU}(h \cdot W_2)

Here W_2 \in \mathbb{R}^{d_{out} \times d_{out}} maps within the output dimension space. Note that W_1 changes the dimension (from d_{in} to d_{out}) while W_2 preserves it. This is the residual mapping F(x) = z.

Shortcut path. Project the input to match the output dimension:

s = x \cdot W_s

Here W_s \in \mathbb{R}^{d_{in} \times d_{out}} is the projection matrix. It performs a pure linear transformation with no activation function. After this step, s \in \mathbb{R}^{d_{out}}.

Output. Add the main path and shortcut path:

y = z + s = F(x) + W_s \cdot x

The element-wise addition is valid because both z and s are in \mathbb{R}^{d_{out}}. The projection W_s is what makes the skip connection work across dimension changes.


When Projection Is Needed

Deep residual networks are organized into stages. Within a stage, all blocks have the same number of channels. When transitioning from one stage to the next, the channel count typically doubles: 64 to 128, 128 to 256, 256 to 512. These transitions are where the convolutional block appears.

Element-wise addition requires matching shapes. If x \in \mathbb{R}^{64} and F(x) \in \mathbb{R}^{128}, the expression F(x) + x is undefined. Without projection, the network must either pad the shortcut with zeros or abandon the skip connection at stage boundaries.

In a typical ResNet, the majority of blocks are identity blocks. Only the first block of each new stage is a convolutional block. For ResNet-34, there are 16 residual blocks total: 3 identity blocks in stage 1 (all 64 channels), then 1 conv block + 3 identity blocks in stage 2 (64 to 128), 1 conv block + 5 identity blocks in stage 3 (128 to 256), and 1 conv block + 2 identity blocks in stage 4 (256 to 512). Only 3 out of 16 blocks need a projection shortcut.


Option A vs Option B

He et al. (2015) compared two strategies for handling dimension mismatches. The paper states: "When the dimensions increase, we consider two options: (A) zero-padding shortcuts, and (B) projection shortcuts."

Option A: Zero-padding. Pad the shortcut with zeros to match the larger dimension. If the input has 64 channels and the output has 128, append 64 zeros. This adds no parameters but the padded dimensions carry no learned information, wasting half the shortcut capacity.

Option B: Projection shortcut. Apply a learned 1 \times 1 convolution (or linear layer) to project the input into the higher-dimensional space. This adds d_{in} \times d_{out} parameters but allows the network to learn the most useful mapping to the new dimension.

The paper's experiments found that Option B slightly outperforms Option A. The improvement is marginal, suggesting that the primary benefit comes from the identity mapping principle rather than the shortcut mechanism. In practice, nearly all modern implementations use Option B because the parameter cost at stage transitions is negligible.

The paper also tested Option C: projection shortcuts on every block, even where dimensions match. Option C performed marginally better, but the extra parameters were not justified. The standard practice became: projection shortcuts only where needed (Option B), identity shortcuts everywhere else.


The Projection Matrix W_s

The projection matrix W_s serves a single purpose: change the dimensionality of the shortcut to match the main path output. In the convolutional setting, this is a 1 \times 1 convolution. In the fully-connected setting, it is a matrix multiplication.

W_s has shape d_{in} \times d_{out}. The multiplication s = x \cdot W_s maps from \mathbb{R}^{d_{in}} to \mathbb{R}^{d_{out}}. This is a linear transformation with no bias and no activation.

The absence of a nonlinearity on the shortcut path is deliberate. The paper's central argument is that identity mappings (or as close to identity as possible) on the shortcut enable clean gradient flow. A purely linear projection is the minimal departure from identity needed to handle the dimension change. Adding ReLU to the shortcut would break the clean gradient path, undermining the core benefit of residual learning.

In the convolutional case, a 1 \times 1 convolution with stride 2 simultaneously halves spatial resolution and changes channels. In the fully-connected case, $$ only handles the feature dimension change. The parameter count is d_{in} \times d_{out}: for a 64-to-128 transition, $$ parameters.


Paper Context

He et al. (2015), "Deep Residual Learning for Image Recognition," introduced residual networks to address the degradation problem: deeper plain networks achieve higher training error than shallower ones, not because of overfitting, but because optimization becomes harder. Adding layers should never hurt in theory (the extra layers could learn identity mappings), but SGD struggles to find these identity solutions.

Residual connections reformulate the learning problem. Instead of learning H(x) directly, each block learns the residual F(x) = H(x) - x, and the output is F(x) + x. If the optimal transformation is close to identity, pushing F(x) toward zero is easier than learning identity from scratch.

The convolutional block with projection shortcut is used at stage boundaries in all ResNet variants: ResNet-18, 34, 50, 101, and 152. In ResNet-18/34, the main path uses two 3 \times 3 convolutions. In ResNet-50/101/152, the main path uses a bottleneck design ($ \times 1$, 3 \times 3, 1 \times 1). Regardless of main path design, the projection shortcut is always a 1 \times 1 convolution with appropriate stride and output channels.

ResNet-152 achieved a 3.57% top-5 error rate on ImageNet, winning ILSVRC 2015. Training networks beyond 100 layers was directly enabled by skip connections; without them, networks deeper than roughly 30 layers suffered severe degradation.


Numerical Example

Consider a convolutional block with d_{in} = 4 and d_{out} = 6. The input vector is:

x = \begin{pmatrix} 1.0 & 0.5 & -0.5 & 2.0 \end{pmatrix}

The first weight matrix W_1 \in \mathbb{R}^{4 \times 6}:

W_1 = \begin{pmatrix} 0.3 & -0.1 & 0.2 & 0.0 & 0.4 & -0.2 \\ 0.1 & 0.5 & -0.3 & 0.2 & 0.0 & 0.1 \\ -0.2 & 0.3 & 0.1 & -0.4 & 0.2 & 0.3 \\ 0.4 & 0.0 & 0.3 & 0.1 & -0.1 & 0.2 \end{pmatrix}

Step 1: Main path, layer 1. Compute x \cdot W_1:

Element 0: 1.0(0.3) + 0.5(0.1) + (-0.5)(-0.2) + 2.0(0.4) = 0.30 + 0.05 + 0.10 + 0.80 = 1.25

Element 1: 1.0(-0.1) + 0.5(0.5) + (-0.5)(0.3) + 2.0(0.0) = -0.10 + 0.25 - 0.15 + 0.00 = 0.00

Element 2: 1.0(0.2) + 0.5(-0.3) + (-0.5)(0.1) + 2.0(0.3) = 0.20 - 0.15 - 0.05 + 0.60 = 0.60

Element 3: 1.0(0.0) + 0.5(0.2) + (-0.5)(-0.4) + 2.0(0.1) = 0.00 + 0.10 + 0.20 + 0.20 = 0.50

Element 4: 1.0(0.4) + 0.5(0.0) + (-0.5)(0.2) + 2.0(-0.1) = 0.40 + 0.00 - 0.10 - 0.20 = 0.10

Element 5: 1.0(-0.2) + 0.5(0.1) + (-0.5)(0.3) + 2.0(0.2) = -0.20 + 0.05 - 0.15 + 0.40 = 0.10

After ReLU: h = [1.25, 0.00, 0.60, 0.50, 0.10, 0.10]. Element 1 was exactly zero and remains zero.

Step 2: Main path, layer 2. Let W_2 = I_6 (the 6 \times 6 identity matrix) for simplicity. Then h \cdot W_2 = h, and after ReLU: z = [1.25, 0.00, 0.60, 0.50, 0.10, 0.10].

Step 3: Shortcut path. The projection matrix W_s \in \mathbb{R}^{4 \times 6}:

W_s = \begin{pmatrix} 0.5 & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 \\ 0.0 & 0.5 & 0.0 & 0.0 & 0.0 & 0.0 \\ 0.0 & 0.0 & 0.5 & 0.0 & 0.0 & 0.0 \\ 0.0 & 0.0 & 0.0 & 0.5 & 0.0 & 0.0 \end{pmatrix}

s = x \cdot W_s = [1.0(0.5), \; 0.5(0.5), \; (-0.5)(0.5), \; 2.0(0.5), \; 0.0, \; 0.0] = [0.50, \; 0.25, \; -0.25, \; 1.00, \; 0.00, \; 0.00]

The projection scaled the 4 input values by 0.5 into the first 4 output positions, with zeros in positions 4 and 5.

Step 4: Addition. Combine main path and shortcut:

y = z + s = [1.75, \; 0.25, \; 0.35, \; 1.50, \; 0.10, \; 0.10]

The shortcut contributed meaningful information. Element 3 went from 0.50 (main path alone) to 1.50 (with shortcut), preserving the strong signal from input value 2.0. The output carries both the learned transformation and a direct trace of the input.


Identity Block vs Convolutional Block

Identity block. Used when d_{in} = d_{out}. The shortcut is x itself with no transformation. All weight matrices are square: W_1, W_2 \in \mathbb{R}^{d \times d}. The output is y = F(x) + x. No extra parameters on the shortcut. This appears for all layers within a stage where the channel count is constant.

Convolutional block. Used when d_{in} \neq d_{out}. The shortcut applies W_s \in \mathbb{R}^{d_{in} \times d_{out}}. The first main path matrix W_1 \in \mathbb{R}^{d_{in} \times d_{out}} changes dimension, and W_2 \in \mathbb{R}^{d_{out} \times d_{out}} preserves it. The output is y = F(x) + W_s \cdot x. This appears at the first layer of each new stage.

Gradient flow differs between the two. In the identity block, \frac{\partial y}{\partial x} = \frac{\partial F}{\partial x} + I. The identity matrix I ensures the gradient magnitude stays at least 1 through the skip path. In the convolutional block, \frac{\partial y}{\partial x} = \frac{\partial F}{\partial x} + W_s^T. The gradient through the shortcut is scaled by W_s^T rather than passing through unchanged. This is why projection shortcuts are used only where necessary: they provide a weaker gradient highway than identity shortcuts.


Pitfalls


Examples

Example 1

Input
x = [[-0.7255, 0.9555, 0.3559], [-0.1239, 0.1807, -0.0165]], W1 = [[-0.1107, 0.2386, -0.346, 0.396], [0.0366, 0.6516, 0.1067, 0.5087], [0.9559, -0.2648, 0.9211, -0.5286]], W2 = [[-0.4315, 0.1188, -0.5771, 0.6075], [-0.6469, 0.4114, -0.1661, -0.6407], [0.1093, 1.0417, -0.1241, 0.1337], [0.3563, -0.3268, 0.0811, 1.1067]], Ws = [[-0.3328, -0.5045, 1.174, 0.3022], [0.3219, -0.1659, 0.0349, -0.0074], [0.8555, -0.1782, 0.3216, -0.1204]]
Output
[[0.8535, 1.05, -0.7039, -0.1173], [0.0853, 0.1061, -0.1445, -0.03]]
Explanation
Main path: relu(relu(x @ W1) @ W2). Shortcut: x @ Ws. Output = main + shortcut. The projection Ws maps input channels to output channels so the addition is valid.

Example 2

Input
x = [[-1.279, -0.9778]], W1 = [[0.0129, 0.3866, 1.093], [0.8893, 0.1701, -0.1068]], W2 = [[0.5589, 0.4735, 0.1315], [-0.934, -0.1958, -0.5757], [0.0487, 0.791, -0.2414]], Ws = [[0.1, -0.1473, 0.4891], [-0.015, -0.3433, 0.0249]]
Output
[[-0.1132, 0.5241, -0.6499]]

Example 3

Input
x = [[0.6688, -0.1164, 0.2769, -0.76], [0.5964, 0.2626, -0.4232, -0.0984]], W1 = [[-0.4315, 0.4927], [-0.0157, -0.4923], [1.1235, -0.0268], [0.2782, -1.0033]], W2 = [[-0.3416, 0.2316], [-0.282, -0.202]], Ws = [[-0.819, -0.0756], [0.4537, 0.0337], [0.3452, -0.035], [0.3737, -0.1532]]
Output
[[-0.789, 0.0523], [-0.5522, -0.0064]]

Hints

  1. Compute shortcut = x @ Ws separately.
  2. Use np.maximum(0, value) for ReLU.
  3. Add the paths before the final activation.

Requirements

Constraints

Starter Code

import numpy as np

def conv_block(x, W1, W2, Ws):
    """
    Returns the projection residual-block output as a nested list.
    """
    pass

Test Cases

CaseMatches
3-to-4 channel projectionpublic
2-to-3 single samplepublic
4-to-2 downsamplepublic