Gradient Flow
How gradients travel backward through deep networks — and why they vanish or explode.
For years, deep networks were a great idea that nobody could actually train. Stack more layers and, in theory, the network learns richer things — but in practice the deep ones just refused to learn. The culprit turned out to be something subtle: how the *learning signal* travels backward through the layers.
Here is the picture. To learn, the network sends an error signal from the output back toward the input, layer by layer, telling each layer how to adjust. But at every layer that signal gets *multiplied* by the layer's local sensitivity. Multiply a number by something a bit less than 1 over and over — 0.25 × 0.25 × 0.25 … — and it shrinks toward nothing astonishingly fast. Multiply by something a bit more than 1 over and over and it blows up just as fast. So a signal that starts healthy at the output arrives at the early layers as either a vanished whisper (near zero) or an exploded scream (enormous). Either way, learning breaks.
Vanishing gradients — the whisper that fades to nothing
The old sigmoid activation was the classic offender. Its local sensitivity is at most 0.25, and usually much less. Chain ten sigmoid layers together and the signal reaching the first layer is scaled by at most 0.25^10 — about a millionth of what left the output. The early layers get essentially *no* signal, so their weights barely move; the network learns only in its last few layers and wastes all its depth. This is exactly why deep sigmoid networks stalled.
The first fix was a better activation: ReLU, whose sensitivity is a clean 1 for any active neuron — it passes the signal through unchanged instead of shrinking it by a fraction. (ReLU has its own smaller catch: a neuron whose input stays negative outputs zero forever, a "dead" neuron. But it hugely reduced the vanishing problem.)
Exploding gradients — the whisper that becomes a scream
The opposite failure shows up especially in RNNs, which process a sequence by applying the *same* weights at every time step. Unroll one over 200 steps and the signal gets multiplied by that weight matrix 200 times. If the matrix is even slightly "bigger than 1" in effect, 1.05 to the 200th power is about 17,000 — the signal explodes, the weights leap to absurd values, and the loss turns to NaN. The quick, blunt fix is gradient clipping: if the signal's overall size exceeds some cap, shrink it back to the cap while keeping its *direction* the same. A seatbelt, not a cure.
The real cures were architectural
Clipping and ReLU helped, but the breakthroughs were changes to the architecture itself, all aimed at giving the signal a clean path home.
Residual connections (ResNets) add a shortcut around each block: the block's output is "what the block computes *plus* its own input." On the way back, that shortcut hands the signal a *direct* route to earlier layers that skips the multiplying entirely — so even a 100-layer network keeps a full-strength signal reaching layer 1. This one trick is why we can train networks hundreds of layers deep.
LSTMs did the same for sequences. Instead of *multiplying* the signal through every time step (which explodes or vanishes), an LSTM carries a running memory that mostly gets *added to*. When its "forget gate" says "hold on to this," the signal flows back across hundreds of steps almost untouched — a gradient highway through time — which is what finally let networks learn long-range patterns.
Layer normalization helps too, by rescaling each layer's activations so they stay in the healthy middle range of the activation function, where the local sensitivity is largest and the signal does not get crushed.
The through-line: ReLU, clipping, residual connections, LSTM gates, and layer norm are not fancy representational upgrades. They are all *plumbing* — machinery whose entire job is to make sure the learning signal survives the trip backward through a deep network.
Initialisation: the first line of defence
Before any of those fixes, the *starting* weights already decide whether the signal survives. If they're too large the activations explode; too small and they vanish — from step one. The principled fix is to scale the initial weights so the *variance* of the signal is preserved as it passes through each layer, in both the forward and backward directions. Xavier/Glorot initialisation does this for tanh/sigmoid (variance ≈ 1/fan_in, or 2/(fan_in+fan_out) to balance forward and backward). He/Kaiming initialisation is the ReLU version (variance ≈ 2/fan_in), the extra factor of 2 compensating for ReLU zeroing out half its inputs. Use He with ReLU, Xavier with tanh/sigmoid — the wrong one on a deep plain network reintroduces vanishing/exploding at initialisation.
BatchNorm versus LayerNorm
Both stabilise the signal by normalising activations, but along *different axes*. BatchNorm normalises each feature *across the batch* — it depends on batch statistics, which makes it powerful for CNNs but batch-size-sensitive and awkward for variable-length sequences. LayerNorm normalises *across the features within a single example*, so it's independent of batch size and of other examples — which is exactly why it's the norm of choice in transformers and RNN-like models where sequences vary and batch statistics are unreliable. The axis is the whole distinction: BatchNorm across the batch, LayerNorm across the features.
Fixing dead ReLUs
ReLU's "dead neuron" catch (input stuck negative → output zero forever → no gradient) has a family of fixes. Leaky ReLU gives a small negative slope so the neuron always passes *some* gradient. ELU and GELU (the transformer default) are smooth variants that keep a non-zero gradient for negative inputs. Beyond activations, better initialisation (He) and a lower learning rate reduce the chance a neuron gets pushed into the dead zone in the first place. If a large fraction of your ReLUs are dead (check activation histograms), that's the lever.
Transformers: residuals + norm, and where the norm goes
Everything above is *why* transformers are built the way they are. Each transformer block wraps attention and the feed-forward network in residual connections and layer normalisation — that combination is what keeps gradients flowing through dozens of blocks. And *where* you put the norm matters: post-norm (the original, norm after the residual add) is harder to train deep and needs careful warmup; pre-norm (norm inside the residual branch, before attention/FFN) gives a cleaner gradient path and trains stably at great depth, which is why modern large models use it. "Pre-norm vs post-norm" is a real interview question about gradient stability, not a detail.
Reading gradient flow: the diagnostics
You don't have to guess whether gradients are healthy — measure. Per-layer gradient norms: log the gradient magnitude at each layer; a healthy net keeps them within ~10× across layers, and a steep decay toward the input means vanishing. Activation histograms: watch for saturation (piling at the extremes) or lots of dead zeros. NaN/Inf checks: catch explosions the moment they appear. Weight-update ratios: the size of each step relative to the weight it updates should sit around 1e-3; far smaller means a layer is barely learning. These turn "training is off" into "layer 2 is vanishing."
Symptom map, and what clipping can't fix
Tell the two failures apart by their signatures. Vanishing: early-layer gradients near zero, those layers barely update, loss falls painfully slowly or stalls, and the network behaves shallow. Exploding: gradient norms blow up, loss oscillates wildly or goes NaN, weights leap to extremes. And be clear about clipping's limits: it's a seatbelt against catastrophic single steps, but it does *not* fix poor conditioning, a bad learning rate, bad initialisation, or slow long-term vanishing — if you're clipping on most steps, the real problem is upstream (LR too high, init wrong), and clipping is just masking it.
Key points
- The core mechanism: the backward signal is multiplied by each layer's sensitivity, so small factors vanish it and large ones explode it — exponentially with depth. Backpropagation sends the error from the output to the input one layer at a time, scaling it by each layer's local sensitivity as it goes. A chain of factors below 1 shrinks the signal toward zero (vanishing); a chain above 1 blows it up (exploding). The deeper the network, the more extreme it gets — which is why depth was so hard to train before the fixes below.
- Activations matter: sigmoid caused vanishing, ReLU mostly cured it. Sigmoid's sensitivity peaks at just 0.25, so ten layers can shrink the signal by a factor of a million and the early layers never learn. ReLU passes the signal through at full strength (sensitivity 1) for any active neuron, removing that per-layer shrink. ReLU's own catch is "dead" neurons — ones whose input stays negative and so output zero forever — but it was still the change that made deeper networks trainable.
- Exploding gradients hit RNNs hardest, and clipping is the seatbelt. An RNN applies the same weights at every step, so over a long sequence the signal is multiplied by that matrix again and again; if it is even slightly amplifying, the signal explodes and the weights blow up to NaN. Gradient clipping caps the overall size of the update while keeping its direction, so no single step is catastrophic. Clip by the whole-vector norm (rescale everything together), not per-component, or you distort the direction — norm clipping at 1.0 is the transformer default.
- The durable cures are architectural: give the signal a clean path home. Residual connections add a shortcut around each block, so the signal has a direct route back that skips the multiplying — which is why 100-plus-layer networks train. LSTMs carry a memory that is mostly *added* to rather than multiplied through, so when the forget gate says "keep this," the signal flows back across hundreds of time steps almost intact. Layer normalization keeps activations in the healthy middle range where sensitivity is highest. All of it is plumbing to keep the backward signal alive.
- Initialisation comes first, and the normalisation axis matters. Scale initial weights to preserve signal variance forward and backward: He/Kaiming (variance 2/fan_in) for ReLU, Xavier/Glorot (1/fan_in) for tanh/sigmoid — the wrong one reintroduces vanishing/exploding at step one. BatchNorm normalises each feature across the batch (batch-size-sensitive, great for CNNs); LayerNorm normalises across features within one example (batch-independent, the transformer/RNN choice). Fix dead ReLUs with Leaky ReLU / ELU / GELU, better init, or a lower learning rate.
- Diagnose gradient flow directly, and know clipping's limits. Transformers stack residual connections + layer norm, and pre-norm (norm inside the residual branch) trains deeper and more stably than the original post-norm. Measure health with per-layer gradient norms (a steep decay toward the input = vanishing), activation histograms (saturation/dead zeros), NaN checks, and weight-update ratios (~1e-3). Read the symptoms: vanishing = early layers stall, loss crawls; exploding = NaN, oscillating loss, huge norms. Clipping is a seatbelt against catastrophic steps — it does not fix bad conditioning, LR, or init, so clipping on most steps means the real problem is upstream.
Gradient flow is the reason deep networks were impractical before 2010. Sigmoid saturates, vanishing the gradient. RNNs multiply the same matrix T times, exploding it. The solutions — ReLU, gradient clipping, residual connections, LSTM gates, layer norm — are all gradient infrastructure, not representational improvements. The network architectures we use today were designed around the constraint that gradients must survive the backward pass.
Recap
- Core mechanism: the backward signal is multiplied by each layer's sensitivity as it propagates, so a chain of factors < 1 shrinks it toward zero (vanishing) and a chain > 1 blows it up (exploding) — and both compound *exponentially* with depth.
- Sigmoid caused the vanishing era: its sensitivity is ≤ 0.25, so $0.25^{10}$ ≈ a millionth reaches the first layer of a 10-layer net. ReLU mostly cured it with sensitivity 1 for active neurons, passing the signal through undiminished.
- Exploding gradients hit RNNs hardest because they multiply the *same* weight matrix T times through the sequence — even a factor slightly above 1 detonates to NaN. Gradient clipping caps the update's overall size while keeping its direction: a seatbelt against one catastrophic step.
- The durable cures are architectural, not tuning: residual connections give the gradient a direct identity route home, LSTM gates *add* to the cell state rather than repeatedly multiply, and layer norm keeps activations in the responsive range where sensitivities stay near 1.
- Initialization comes first — it's gradient flow at step zero: He (variance 2/fan_in) for ReLU, Xavier (1/fan_in) for tanh/sigmoid. The wrong recipe reintroduces vanishing or exploding from the very first forward pass, before the optimizer even runs.
- Norm axis is a correctness choice: BatchNorm normalizes a feature *across the batch* (batch-sensitive, used in CNNs); LayerNorm normalizes *across features* within one example (batch-independent, used in Transformers).
- Diagnose gradient flow directly: log per-layer gradient norms (a steep decay toward the input = vanishing), activation histograms (saturation / dead zeros), NaN checks, and weight-update ratios (~1e-3). If clipping fires on most steps, the real problem is upstream — bad conditioning, LR, or init — clipping won't fix those.
Check your understanding
Q1. A 20-layer sigmoid network barely trains, but a 20-layer ReLU network trains fine. What is the mechanistic difference in how the learning signal flows?
- `A) The sigmoid outputs are stuck between 0 and 1, so its predictions collapse toward 0.5 and the loss cannot drop below a floor around ln(2); ReLU's unbounded outputs avoid this ceiling, letting the network make confident, low-loss predictions from the start.`
- `B) Each sigmoid layer scales the backward signal by at most 0.25, so across 20 layers it shrinks up to 0.25^20 (~10⁻¹³) — early layers get essentially no signal and never learn. ReLU passes the signal at full strength (factor 1) through active neurons, so it never shrinks layer by layer.`
- `C) The sigmoid network is actually exploding, not vanishing — its bounded [0,1] outputs push activations toward 1 in every layer, compounding through 20 layers to values near 10^6 and forcing ever-larger weight updates; ReLU avoids this because its activations are unbounded above and never saturate.`
- `D) It is purely an initialization issue — sigmoid networks need weight variance under a strict threshold to converge and fail without it, while ReLU tolerates a much wider range; a correctly-initialised sigmoid network would train exactly as fast as the ReLU one.`
Q2. Why can ResNets train with 100+ layers when a plain network of the same size cannot?
- `A) Because ResNets put batch normalization between every block, which is what actually prevents vanishing gradients — measured gradient norms stay within a factor of 3 across all 100 layers purely from the normalization statistics; the skip connections are secondary, and plain networks fail only because they lack batch norm.`
- `B) Because the skip connections let the gradient bypass all 100 layers straight from output to input in one step, so the optimizer only has to train each shallow residual function independently — while a plain network must propagate error through all 100 layers sequentially, compounding each layer's Jacobian.`
- `C) Because the skip connections start each residual block at zero output, so the network begins as an exact identity map and only learns small deviations from it — easier to optimize than starting from a random 100-layer transformation with no known-good solution nearby.`
- `D) Because the shortcut around each block gives the backward signal a direct route to earlier layers, skipping the layer-by-layer multiplying — a gradient "highway" reaching layer 1 at full strength. A plain 100-layer network has no such path, so its signal must survive 100 multiplications and dies.`
Q3. Clipping the gradient by its norm versus by value: what is the difference, and which should you use for transformers?
- `A) Clipping by value caps each component separately, distorting the update's direction when components differ wildly in size. Clipping by norm rescales the whole vector together, preserving direction. Transformers use norm clipping, typically at 1.0, for this reason.`
- `B) They give identical results for transformers, because Adam's per-parameter second-moment normalization already rescales each component before the clip, undoing any directional distortion value clipping would cause — norm clipping only matters for plain SGD without adaptive scaling.`
- `C) Value clipping is preferred for transformers because their many attention heads act independently; norm clipping would over-shrink the small head gradients whenever one big gradient dominates the norm, while value clipping leaves the small ones alone.`
- `D) They differ mainly in compute cost: norm clipping requires an extra reduction pass across all parameters to compute the vector norm, adding real overhead on huge models, so production code often uses cheaper per-component value clipping with a large threshold instead.`
Q4. An RNN trained on 200-character sequences shows wildly oscillating loss and NaN weights after 500 steps. What is happening, and what fixes it?
- `A) Vanishing gradients — after 200 steps the signal for the early characters has decayed to zero, so the model ignores the start of the sequence entirely; fix by raising the learning rate to amplify the vanished signal and switching to a bidirectional RNN architecture.`
- `B) It is overfitting, not a gradient problem — the model memorised the sequences and flips between memorised and general predictions across batches; fix with heavy dropout, a smaller model capacity, and L2 regularization on the recurrent weight matrix.`
- `C) Exploding gradients. The same recurrent matrix is applied 200 times, so a slightly-amplifying matrix grows the signal exponentially (1.05^200 ≈ 17,000×), sending weights to extremes and outputs to NaN. Fix: clip the gradient by norm, then consider an LSTM/GRU.`
- `D) It is a learning-rate problem unique to RNNs — touching 200 time steps at once effectively multiplies the effective learning rate by a factor of 200, the same way batch size scales it, so dividing the base learning rate by exactly 200 removes the instability entirely.`
Q5. You initialise a deep ReLU network with weights drawn from N(0, 1) and it fails to train — activations either explode or collapse across layers. What initialisation should you use and why?
- `A) Xavier/Glorot initialisation (variance 1/fan_in), because empirical benchmarks show it minimizes activation variance drift across every activation function including ReLU, making it the universal default in most modern frameworks.`
- `B) He/Kaiming initialisation (variance 2/fan_in) — the factor of 2 compensates for ReLU zeroing out roughly half its inputs. Xavier (1/fan_in) is calibrated for tanh/sigmoid and under-scales for ReLU.`
- `C) Just make all initial weights very small (e.g. variance 1e-6), which guarantees the forward signal never explodes regardless of activation function, depth, or fan_in — the standard "safe default" before Xavier and He were derived.`
- `D) Initialisation doesn't matter for ReLU networks because BatchNorm's learned scale and shift parameters fully compensate for any initial variance mismatch within the first few forward passes, so keep N(0,1) and add BatchNorm.`
Q6. Why do transformers use LayerNorm rather than BatchNorm, and what does pre-norm (vs post-norm) placement change? Select the two true statements.
- `A) BatchNorm normalises each feature across the batch, so it depends on batch statistics that are unreliable for variable-length sequences and small batches; LayerNorm normalises across the features within a single example, making it batch-independent — the right fit for transformers.`
- `B) Post-norm (the original placement) is harder to train deep and needs careful warmup, while pre-norm (norm inside the residual branch, before attention/FFN) gives a cleaner gradient path and trains stably at great depth — why modern large models use it.`
- `C) LayerNorm is just a faster approximation of BatchNorm that skips computing running mean and variance buffers, and pre-norm vs post-norm only changes peak memory usage during backpropagation, not training stability.`
- `D) Transformers use LayerNorm only because BatchNorm hadn't been invented yet when the original Transformer paper was written; pre-norm and post-norm placements are mathematically identical and produce the same gradient statistics.`
Try it interactively
ML Systems Lab is a free interview-prep platform for ML engineers — work through the full interactive module, quizzes, and drills.
Open ML Systems Lab →