Dense block with 2D convolutionsMedium

Dense block with 2D convolutions

Background

A dense block is the core of DenseNet. Unlike a plain stack where each layer feeds only the next, every layer in a dense block receives the concatenation of all preceding feature maps and appends its own output. Each layer adds a fixed number of channels — the growth rate kk — so feature reuse is maximal and gradients reach early layers directly. After LL layers starting from C0C_0 channels, the block outputs C0+LkC_0 + L\cdot k channels.

Problem statement

A working conv2d(x, kernel, padding) (NHWC layout, valid convolution after zero-padding) is provided. Implement dense_net_block(input_data, num_layers, growth_rate, kernels, kernel_size=(3,3)):

For each layer l=0L1l = 0 \dots L-1:

a=ReLU(features),c=conv2d(a, kernels[l], p),features=concat([features,c], axis=channels)a = \operatorname{ReLU}(\text{features}), \quad c = \text{conv2d}(a,\ \text{kernels}[l],\ p), \quad \text{features} = \text{concat}([\text{features}, c],\ \text{axis}=\text{channels})

Use same padding p=(kh1)//2p = (k_h - 1)//2 so spatial dims are preserved.

Input

  • input_datanp.ndarray of shape (N, H, W, C0) (batch, height, width, channels).
  • num_layersint, number of layers LL.
  • growth_rateint, channels added per layer (equals each kernel's output channels).
  • kernels — list of L kernels; kernels[l] has shape (kh, kw, C_l, growth_rate), where C_l is the current channel count.
  • kernel_size(kh, kw) tuple.

Output

An np.ndarray of shape (N, H, W, C0 + L*growth_rate): the original features followed by every layer's output, concatenated along the channel axis.

Examples

Example 1

Input:  input_data shape (1, 4, 4, 2), num_layers = 2, growth_rate = 1
Output: shape (1, 4, 4, 4)   # 2 original + 1 + 1

Explanation: each layer applies ReLU, convolves with same padding (spatial stays 4×4), and concatenates one new channel. Two layers turn 2 channels into 4.

Constraints

  • ReLU is applied to the current concatenated features before each convolution.
  • Padding is (kernel_size[0] - 1)//2 (same convolution), preserving H and W.
  • The original input must remain the leading channels of the output (concatenation appends new maps).

Notes

  • Channel count grows additively, not multiplicatively, which keeps DenseNets parameter-efficient compared to their depth.
  • The skip-by-concatenation differs from ResNet's skip-by-addition: DenseNet preserves all earlier features rather than summing them.
Python
Loading...

This problem ships 5 hidden tests. They run in your browser via Pyodide — no backend, no submission queue. Press ▶ Run tests to execute.

  • Provided conv2d sums the window for an all-ones kernel
  • Channel count grows by L * growth_rate
  • Same padding preserves spatial dimensions
  • Original features are preserved as the leading channels
  • ReLU clamps negative features before convolution