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.
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 , where is the block input, is the learned correction (usually two or three convolutions), and is the output. When channels or resolution change, a projection aligns the shortcut: .
The payoff shows in backpropagation. Let be the scalar loss, an early layer, and a later one. The chain rule gives . That lone 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 narrow parallel paths: , where each is a small bottleneck (, then , then ) and is the cardinality, typically . 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 receives every earlier output, , where is that layer's convolution and means stacking channels. If each layer adds feature maps (the growth rate, often ), layer sees channels, with the input width. Transition layers ( convolution plus pooling) compress between dense blocks so memory stays livable.
Worked example: a bottleneck block's parameters
Trace a ResNet bottleneck on input with channels at , stride . The correction path is Conv (), Conv (), Conv (), with shapes matching so the shortcut is plain :
- Reduce: parameters, output
- Spatial: parameters, output
- Expand: parameters, output
- Total: , and the block returns
Two plain convolutions at channels would cost , over 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.9Watch Out For
Adding the shortcut to a mismatched shape
The moment stride or channel count changes, no longer matches and the addition crashes. The symptom is a shape error at the plus sign, usually right after a downsampling block. Use the projection shortcut (a 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 small, keep transition compression on, and watch activation memory rather than the parameter count.
The Quick Version
- A residual block outputs , so layers learn corrections and identity is the default.
- The chain rule gains a identity term, and gradients reach early layers no matter the depth.
- ResNeXt splits the correction into parallel paths, buying capacity without a FLOP explosion.
- DenseNet concatenates all past outputs, maximizing reuse at the cost of activation memory.
- A -channel bottleneck costs parameters versus for plain layers.