Skip to content
AI360Xpert
Beta

Residual and Dense Networks

Instead of learning a full mapping from scratch, skip connections let layers learn only the residual difference, enabling networks hundreds of layers deep.

ResNet adds the input back to each block so layers learn only the correction, ResNeXt splits the correction into parallel paths, and DenseNet concatenates every past output.
ResNet adds the input back to each block so layers learn only the correction, ResNeXt splits the correction into parallel paths, and DenseNet concatenates every past output.

Why Does This Exist?

Push a plain network past 20 layers and something odd happens: training accuracy gets worse, not just test accuracy. A 56-layer plain network loses to its own 20-layer sibling on the training set itself, so this isn't overfitting. The extra layers should at least be able to copy their inputs through unchanged, but fitting an identity mapping out of stacked nonlinear layers turns out to be genuinely hard for gradient descent.

Residual Networks (He et al., 2015) fixed this with a shortcut: add the block's input back to its output, so each block only has to learn the correction. With that change, 152-layer networks trained cleanly, and the identity path has sat inside nearly every architecture since. ResNeXt (2017) and DenseNet (2017) pushed the same idea further through parallel paths and dense concatenation.

This page covers all three wirings and the gradient math that makes them work. The 1x1 convolution page covers the bottleneck trick they rely on.

Think of It Like This

Editors who only mark corrections

A 150-page manuscript passes through 150 editors in a row. In a plain network each editor retypes the whole thing from memory, so small slips compound into garbage by editor 30. And an editor with nothing to add still has to reproduce every word perfectly.

A residual network clips the original draft to every desk and asks each editor for red-pen corrections only. Nothing to add means a blank overlay, and the draft passes through untouched. DenseNet goes further: every editor gets a folder with all previous drafts, so anyone can quote an early sketch next to a late revision.

The analogy stops at addition. Editors merge comments with judgment; the network adds tensors elementwise, no judgment involved, which is exactly why shape mismatches crash it.

How It Actually Works

The residual block and the gradient highway

A residual block computes y=F(x)+xy = \mathcal{F}(x) + x, where xx is the block input, F\mathcal{F} is the learned correction (usually two or three convolutions), and yy is the output. When channels or resolution change, a 1×11 \times 1 projection WsW_s aligns the shortcut: y=F(x)+Wsxy = \mathcal{F}(x) + W_s x.

The payoff shows in backpropagation. Let E\mathcal{E} be the scalar loss, xlx_l an early layer, and xLx_L a later one. The chain rule gives ∂E/∂xl=(∂E/∂xL)⋅(1+∂∑F/∂xl)\partial \mathcal{E} / \partial x_l = (\partial \mathcal{E} / \partial x_L) \cdot (1 + \partial \sum \mathcal{F} / \partial x_l). That lone 11 is an identity path: even if the correction gradients shrink to nothing, the loss gradient flows straight back. Depth stops punishing training.

ResNeXt: cardinality instead of width

ResNeXt replaces one wide correction with CC narrow parallel paths: y=x+∑i=1CTi(x)y = x + \sum_{i=1}^{C} \mathcal{T}_i(x), where each Ti\mathcal{T}_i is a small bottleneck (1×11 \times 1, then 3×33 \times 3, then 1×11 \times 1) and CC is the cardinality, typically 3232. Widening cardinality buys more accuracy per parameter than widening channels, and the whole thing runs as one grouped convolution.

DenseNet: concatenate instead of add

DenseNet swaps addition for channel concatenation: layer ll receives every earlier output, xl=Hl([x0,x1,…,xl−1])x_l = H_l([x_0, x_1, \ldots, x_{l-1}]), where HlH_l is that layer's convolution and [⋅][\cdot] means stacking channels. If each layer adds kk feature maps (the growth rate, often k=32k = 32), layer ll sees k0+k(l−1)k_0 + k(l-1) channels, with k0k_0 the input width. Transition layers (1×11 \times 1 convolution plus pooling) compress between dense blocks so memory stays livable.

Worked example: a bottleneck block's parameters

Trace a ResNet bottleneck on input XX with 256256 channels at 56×5656 \times 56, stride 11. The correction path is Conv 256→64256 \to 64 (1×11 \times 1), Conv 64→6464 \to 64 (3×33 \times 3), Conv 64→25664 \to 256 (1×11 \times 1), with shapes matching so the shortcut is plain xx:

  • Reduce: 256×64×1×1=16,384256 \times 64 \times 1 \times 1 = 16{,}384 parameters, output (64,56,56)(64, 56, 56)
  • Spatial: 64×64×3×3=36,86464 \times 64 \times 3 \times 3 = 36{,}864 parameters, output (64,56,56)(64, 56, 56)
  • Expand: 64×256×1×1=16,38464 \times 256 \times 1 \times 1 = 16{,}384 parameters, output (256,56,56)(256, 56, 56)
  • Total: 16,384+36,864+16,384=69,63216{,}384 + 36{,}864 + 16{,}384 = 69{,}632, and the block returns F(X)+X\mathcal{F}(X) + X

Two plain 3×33 \times 3 convolutions at 256256 channels would cost 2×(256×256×9)=1,179,6482 \times (256 \times 256 \times 9) = 1{,}179{,}648, over 1616 times more. The bottleneck compresses before the expensive spatial step, and the shortcut carries the rest for free.

Code

# A residual block in pure Python: the correction plus the shortcut.x = 2.0correction = 0.5  # what the layers learned; 0.0 would pass x through untouchedy = correction + xprint(y)# -> 2.5
# Bottleneck vs plain parameter counts from the worked example.plain = 2 * (256 * 256 * 9)bottleneck = 256 * 64 + 64 * 64 * 9 + 64 * 256print(plain, bottleneck, round(plain / bottleneck, 1))# -> 1179648 69632 16.9

Watch Out For

Adding the shortcut to a mismatched shape

The moment stride or channel count changes, xx no longer matches F(x)\mathcal{F}(x) and the addition crashes. The symptom is a shape error at the plus sign, usually right after a downsampling block. Use the projection shortcut WsxW_s x (a 1×11 \times 1 convolution at the new shape) wherever dimensions change, and identity everywhere else.

Concatenation memory blowing up silently

DenseNet reuse is cheap in parameters but not in memory: every layer holds all previous feature maps alive, so activations grow with depth. The symptom is an out-of-memory kill on an otherwise small model. Keep the growth rate kk small, keep transition compression on, and watch activation memory rather than the parameter count.

The Quick Version

  • A residual block outputs F(x)+x\mathcal{F}(x) + x, so layers learn corrections and identity is the default.
  • The chain rule gains a +1+1 identity term, and gradients reach early layers no matter the depth.
  • ResNeXt splits the correction into 3232 parallel paths, buying capacity without a FLOP explosion.
  • DenseNet concatenates all past outputs, maximizing reuse at the cost of activation memory.
  • A 256256-channel bottleneck costs 69,63269{,}632 parameters versus 1,179,6481{,}179{,}648 for plain 3×33 \times 3 layers.