ML Systems Lab Open interactive version →
Intermediate 40 min read initializationxavierheglorotsymmetry-breaking

Weight Initialization

Symmetry breaking, Xavier and He initialization, and the connection to gradient flow.

Before a network takes a single training step, the *starting* values of its weights already decide whether it can learn at all. Two things can go wrong at the very beginning, and both are worth understanding.

The first is a surprise: do not set all the weights to zero. It sounds harmless, but it is fatal. If every neuron in a layer starts with identical weights, they all compute the same output, all receive the same gradient, and all take the same update — so they stay identical forever. A 512-neuron layer initialised to zero behaves exactly like a *single* neuron; the other 511 are wasted. The fix is random initialisation: the randomness is what makes neurons different from one another so they can learn different features. This is called breaking symmetry.


The second problem: getting the scale right

So the weights must be random — but *how big* should those random numbers be? This turns out to matter enormously, and it connects straight to the gradient-flow story from the last lesson.

Picture a signal passing forward through the layers, multiplied by the weights at each one. If the weights are too small, the signal shrinks a little at every layer, and after 20 layers it has faded to essentially zero — the network cannot tell its inputs apart, and since a dead forward signal means a dead backward signal too, nothing learns. If the weights are too large, the opposite happens: the signal grows until it blows up, or it slams activations like sigmoid and tanh into their flat "saturated" zones where their sensitivity drops to zero — and again the gradient dies. The weights need to be *just* the right size to keep the signal steady as it travels through the network.


The recipes: Xavier and He

Happily, the right size can be worked out exactly, and it depends on how many inputs feed into a layer. The idea is simple: pick the random scale so the signal comes out of each layer at about the same size it went in — no shrinking, no growing.

Xavier (Glorot) initialisation computes that scale for symmetric activations like tanh, and it was the fix that first let deep tanh networks train reliably.

He (Kaiming) initialisation adjusts it for ReLU. Because ReLU throws away all the negative values, it roughly *halves* the signal at every layer — so He simply doubles the variance to make up for the half that ReLU discards. Use He for ReLU networks and Xavier for tanh; use the wrong one on a deep network and it will silently fail to learn.


One small but famous exception: biases can safely start at zero (the random weights already break symmetry), *except* the LSTM's "forget gate" bias, which is usually set to 1. That nudges the gate to *remember* by default at the start, keeping the memory highway open long enough for the network to learn when it actually should forget.


The actual formulas, and fan-in vs fan-out

Worth carrying the numbers. Let fan_in be the number of inputs to a layer and fan_out the number of outputs. He/Kaiming (for ReLU) uses variance 2/fan_in. Xavier/Glorot (for tanh/sigmoid) uses either 1/fan_in or the symmetric 2/(fan_in + fan_out). Why the two versions of Xavier? Preserving the signal's variance on the forward pass wants 1/fan_in; preserving the gradient's variance on the backward pass wants 1/fan_out; averaging the two (2/(fan_in+fan_out)) is the compromise that keeps both roughly stable. He fixes on fan_in because for ReLU the forward-pass halving is the dominant effect to correct.


Uniform or normal?

Both Xavier and He come in a normal and a uniform flavour, and they're near-equivalent in practice. The normal version draws from N(0, variance). The uniform version draws from U(−limit, +limit) with the limit chosen to give the *same* variance (for Xavier uniform, limit = √(6/(fan_in+fan_out))). Frameworks default to one or the other; the difference rarely matters, but know that "Xavier uniform" and "Xavier normal" are the same idea with different sampling shapes.


Orthogonal initialisation for recurrence

For RNNs and very deep near-linear stacks, there's a better choice than random Gaussian: orthogonal initialisation, where the weight matrix is initialised to be orthogonal (its rows/columns are unit vectors at right angles). An orthogonal matrix has the property that it preserves vector norms under multiplication — so applying it repeatedly (as an RNN does across time steps) neither grows nor shrinks the signal. That's exactly the property you want when the *same* matrix is multiplied hundreds of times, which is why orthogonal init helps recurrent and deep-linear networks specifically.


Modern architectures soften the sensitivity — but don't remove it

Here's the honest caveat: "use the wrong init and the network silently fails" is true for a deep plain network, but much less so once you add residual connections and BatchNorm/LayerNorm. Normalisation re-centres and re-scales activations at every layer, which repairs a lot of a bad initial scale, and residual shortcuts keep gradients flowing regardless. So a ResNet or a normalised transformer is far more *forgiving* of initialisation than an old-style plain net. Init still matters — it affects early-training stability and final quality — but it's a smaller cliff than the unqualified claim suggests.


Transformer-specific initialisation

Very deep transformers need extra care beyond He/Xavier. Because each layer adds to the residual stream, naive init lets the residual-stream variance grow with depth, destabilising training — so large models scale the residual-branch weights down by a factor related to depth (e.g. 1/√(2N) schemes) to keep the stream stable. The embedding and output layers often get their own scaling, and the LayerNorm gain/bias start at 1/0. This is why deep transformers historically needed careful warmup — and why good residual-scaling schemes let them train more stably.


Diagnosing an init problem

You can catch a bad initialisation before wasting a training run. Check the loss at step zero: for a K-class classifier it should be ≈ ln(K) (e.g. ~2.3 for 10 classes) — a wildly different value means the output scale is off. Log the activation variance per layer on the first forward pass: healthy init keeps it roughly constant across layers; a steady decay or explosion means the scale is wrong. Also check per-layer gradient norms, the dead-ReLU count, and run NaN/Inf checks. These five-minute checks tell you the init is sane before you commit GPU hours.

Key points

Takeaway

Initialization is gradient flow at step zero. Before the optimizer runs, the parameters must already be at a scale where signals neither vanish nor explode in the forward pass — because if they vanish in the forward pass, they also vanish in the backward pass. The correct variance formula depends on the activation function, and using the wrong formula (Xavier for ReLU, or He for tanh) produces a deep network that silently fails to learn.

Recap

Check your understanding

Q1. A 30-layer tanh network is initialised with weights from N(0, 1). Training loss barely moves. What is happening, and what is the fix?

Q2. Why is He initialisation made specifically for ReLU, and what goes wrong if you use Xavier on a deep ReLU network?

Q3. Why can biases be initialised to zero when weights cannot, and what is the LSTM exception? Select the two true statements.

Q4. Xavier initialisation has two common variance formulas: 1/fan_in and 2/(fan_in + fan_out). Why do both exist?

Q5. You're training a plain (non-normalised) RNN and want the recurrent weight matrix to neither vanish nor explode the signal as it's applied across hundreds of time steps. Which initialisation is especially suited, and why?

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 →