Skip to content
AI.info

Training and optimization

Initialization and Signal Propagation

Understand symmetry breaking, variance-preserving initialization, fan-in and fan-out, residual scaling, and the diagnostic value of early activations.

By the end you can

Example

A network can fail before the first optimizer step

Initialization decides what the optimizer is handed: the distribution of activations, and the local derivatives that come with them. The systems that train at scale treat this as a decision about depth, not a default.

GPT-2 states the rule in one sentence: “We scale the weights of residual layers at initialization by a factor of 1/√N where N is the number of residual layers.” OpenAI published that in 2019, calling it a modified initialization “which accounts for the accumulation on the residual path with model depth”.

NVIDIA's Megatron-LM discloses its own recipe for the same accumulation. It does not arrive at the same constant. “We start by initializing our weights W with a simple normal distribution W ∼ N(0, 0.02). We then scale weights immediately before residual layers by 1/√2N…” There N counts the transformer layers of self-attention and MLP blocks.

Two organisations, the same instinct — divide by the square root of the depth. No agreement on whether the divisor is √N or √(2N). Depth is in the formula either way.

  • Identical hidden units: Equal weights receive equal gradients and remain duplicates without another asymmetry source.
  • Excessive variance: Activations and gradients can grow through depth until overflow or saturation appears, which is exactly the accumulation the 1/√N and 1/√(2N) divisors above are inserted to cancel.
  • Insufficient variance: Signals shrink toward zero, leaving later layers nearly constant — under Xavier initialization the gradients were observed diminishing in a model of up to 30 layers, monitored in the experiments.
  • Biased gates: Small random initialization leaves an LSTM forget gate sitting at 0.5. An ICML paper from 2015, built on a search of over ten thousand RNN architectures, put the cost in one line — “This introduces a vanishing gradient with a factor of 0.5 per timestep, which can cause problems whenever the long term dependencies are particularly severe”. Its fix was one number: “We found that adding a bias of 1 to the LSTM’s forget gate closes the gap between the LSTM and the GRU”. Keras encodes the repair as a layer default: “unit_forget_bias: Boolean (default True). If True, add 1 to the bias of the forget gate at initialization”.
  • Unscaled residual branches: Many additions can make variance grow before normalization or learning compensates, which is why N — the count of layers, not a property of any one of them — appears inside the initialization formula of both shipped recipes above.

Key idea

Zero initialization is appropriate for some parameters, not all

Biases, selected residual-branch endpoints, or output heads may benefit from zero or near-zero starts. Initializing every weight identically destroys hidden-unit symmetry.

Rules depend on parameter role. Apply initialization by module semantics rather than one global function over every tensor.

There is one parameter role for which exactly zero is not merely tolerable but the whole method. The ReZero paper, published in 2021, puts a single scalar gate on each residual branch and sets it to zero, so that “gating each residual connection using a single zero-initialized parameter satisfies initial dynamical isometry”. The abstract states what that one zero buys: “We apply this technique to language modeling and find that we can easily train 120-layer Transformers.” The deepest model actually reported, in Table 3, is a 128-layer ReZero Transformer at 1.08 bits per byte, against a 64-layer vanilla Transformer that diverges.

At ordinary depth the same change is a speed result rather than a feasibility one: “When applied to 12 layer Transformers, it converges 56% faster on enwiki8.” That is the character-level benchmark everyone else spells enwik8.

So the role, not the value, decides. One scalar per residual branch at zero trains a 128-layer Transformer. One hidden weight matrix at zero trains nothing at all.

“Never initialize to zero” is too broad; “never erase needed symmetry breaking” is more precise.

Comparison

Two variance-scaling principles

Exact formulas vary by distribution, mode, and framework convention. The two libraries most people use publish their conventions plainly enough to set side by side.

Xavier, or Glorot, balances variance using fan-in and fan-out assumptions, and pairs conventionally with tanh or linear-like activations. Glorot and Bengio proposed it in 2010, in a paper whose abstract closes “we propose a new initialization scheme that brings substantially faster convergence”. PyTorch ships it as torch.nn.init.xavier_uniform_, with bound a = gain × sqrt(6/(fan_in + fan_out)). Keras publishes the identical recipe as two classes: GlorotUniform at limit = sqrt(6 / (fan_in + fan_out)), and GlorotNormal at stddev = sqrt(2 / (fan_in + fan_out)). Goal: stable forward and backward scale. Caution: the assumptions are approximate.

He, or Kaiming, accounts for the variance a rectifying activation discards, and pairs with the ReLU family. Keras states it without an intermediate gain term: “It draws samples from a truncated normal distribution centered on 0 with stddev = sqrt(2 / fan_in) where fan_in is the number of input units in the weight tensor.” PyTorch reaches the same place through a published lookup table instead. torch.nn.init.calculate_gain gives 1 for Linear/Identity, 1 for Conv1D, Conv2D and Conv3D, 1 for Sigmoid, 5/3 for Tanh, √2 for ReLU, √(2/(1+negative_slope²)) for Leaky ReLU and 3/4 for SELU.

That is the whole content of the warning that gain depends on the nonlinearity. It is not a vague caution but a table. Using tanh's 5/3 where ReLU's √2 belongs is a scale error of a fixed, computable size, repeated at every layer. Note also that the two libraries disagree about which fan to divide by — sqrt(2 / fan_in) for He, sqrt(6 / (fan_in + fan_out)) for Glorot. So "the same" initialization can mean two different distributions, depending on which mode a framework selects.

Architecture-specific scaling adjusts residual branches, gates, heads, or depth explicitly, and is where the very deep recipes live: a zero or small final branch scale, or a divisor in the depth itself, as in the 1/√N and 1/√(2N) rules above. Goal: stable composition. Caution: it is recipe-dependent, and the recipes do not agree with one another.

FigureComparison · 3 columns

Xavier or Glorot

Balances variance using fan-in and fan-out assumptions.

  • Common pairing: tanh or linear-like activations
  • Goal: stable forward and backward scale
  • Choice: normal or uniform
  • Caution: assumptions are approximate

He or Kaiming

Accounts for variance loss through rectifying activations.

  • Common pairing: ReLU family
  • Goal: preserve signal after gating
  • Choice: fan-in or fan-out mode
  • Caution: gain depends on nonlinearity

Architecture-specific scaling

Adjust residual branches, gates, heads, or depth explicitly.

  • Common pairing: very deep residual networks
  • Goal: stable composition
  • Choice: zero or small final branch scale
  • Caution: recipe-dependent

Starting an orchestra before the conductor gives direction

Before the downbeat the players sit at distinct, moderate volumes, which is what leaves the conductor an ensemble to shape. Identical or deafening starts leave little that anyone can usefully correct.

An orchestra recovers from a bad first bar. A deep stack multiplies its opening levels layer after layer. Diversity and usable dynamic range at step zero decide what the optimizer is able to correct later. This is why the same two initialization recipes are interchangeable in a 14-layer network — 33.90/13.44 top-1/top-5 error against 33.82/13.34 — and decide whether a 30-layer one learns at all. The recipe did not become more powerful with depth. The multiplication did.

A good start preserves room for the optimizer to create structure.

Visual

What to inspect before learning has time to hide the problem

A single synthetic or real batch can reveal early signal failure. Every check below runs before any long training job.

Run a forward pass, recording per-layer mean, variance, saturation, sparsity, and output scale. Compute the loss, confirming that the initial value matches target cardinality and reduction expectations. Run backward, recording gradient norms, missing gradients, and layerwise attenuation or growth. Simulate one update, measuring update-to-parameter ratios and checking numerical validity.

Then repeat across seeds, to separate a robust regime from one fortunate initialization. Here the size of the effect has been measured rather than assumed. A 2021 MLSys study took five deep-learning case studies — CIFAR10 with VGG11, PascalVOC segmentation with an FCN on a ResNet18 backbone, GLUE SST-2 and RTE with BERT, and peptide-MHC I binding with a shallow MLP. It then worked through the sources of variance one at a time: “we randomized the seeds 200 times, while keeping all other sources fixed to initial values”. Its verdict places the initialization seed exactly: “In contrast, model initialization generally is less than 50% of the variance of bootstrap, on par with the visit order of stochastic gradient descent.”

Read both halves of that sentence. Bounded — under half the variance you would get by resampling the data. But on par with the data-visit order, which nobody would report a single draw of either.

FigureProcess · 5 steps
  1. 1

    Run a forward pass

    Record per-layer mean, variance, saturation, sparsity, and output scale.

  2. 2

    Compute the loss

    Confirm the initial value matches target cardinality and reduction expectations.

  3. 3

    Run backward

    Record gradient norms, missing gradients, and layerwise attenuation or growth.

  4. 4

    Simulate one update

    Measure update-to-parameter ratios and check numerical validity.

  5. 5

    Repeat across seeds

    Separate a robust regime from one fortunate initialization.

Steps

Diagnose initialization before changing the optimizer

A loss that is invalid, flat, or extremely sensitive from step one sends you to the workflow below.

1. Verify shapes and reductions, to rule out broadcast errors and unexpected loss scale. 2. Plot activation distributions, looking for saturation, dead outputs, exploding variance, and constant channels. 3. Trace gradients by depth, checking attenuation, growth, and missing paths after a single backward pass. 4. Compare initialization recipes, changing only the relevant module or scaling rule. Start by reading what the framework was already doing: PyTorch's nn.Linear obtains its default by calling init.kaiming_uniform_(self.weight, a=math.sqrt(5)), a line its own source annotates “Setting a=sqrt(5) in kaiming_uniform is the same as initializing with uniform(-1/sqrt(in_features), 1/sqrt(in_features))”. 5. Retest several seeds, requiring the healthy regime to persist beyond one random draw.

Step 4 is the one most often skipped in the wrong direction. The optimizer gets swapped, the learning rate gets halved, and nobody has yet established which distribution the weights were drawn from.

FigureProcess · 5 steps
  1. 1. Verify shapes and reductions

    Rule out broadcast errors and unexpected loss scale.

  2. 2. Plot activation distributions

    Look for saturation, dead outputs, exploding variance, and constant channels.

  3. 3. Trace gradients by depth

    Check attenuation, growth, and missing paths after one backward pass.

  4. 4. Compare initialization recipes

    Change only the relevant module or scaling rule.

  5. 5. Retest several seeds

    Require the healthy regime to persist beyond one random draw.

Initialization interacts with normalization and residual design

Normalization can widen the stable initialization range, while residual connections shorten gradient paths. Neither makes arbitrary scale harmless. At the limit, neither turns out to be strictly required either.

An ICML paper from 2018 removed both. It trained vanilla convolutional networks ten thousand layers deep, with no residual connections and no batch normalization, using only a delta-orthogonal initialization derived from a mean field theory of signal propagation and the condition of dynamical isometry. The abstract says it plainly: “In this work, we demonstrate that it is possible to train vanilla CNNs with ten thousand layers or more simply by using an appropriate initialization scheme.”

Take the direction of that result carefully. It is not advice to delete your normalization layers. It shows that the work those layers do at step zero — keeping signal norms from collapsing or exploding through composition — is work the initialization can, in principle, do by itself. That is why the three choices have to be designed together, rather than defended one at a time.

Randomness is used to create distinct learning roles

If hidden units begin with identical incoming and outgoing weights, they compute the same value and receive the same gradient. They cannot spontaneously divide responsibilities.

Random initialization breaks that symmetry. Its scale must still preserve useful signal rather than injecting arbitrary large noise. The randomness is there to make units different; the scale is there to keep them informative. Two requirements, set by different considerations.

Initialization should create diversity without destroying information flow.

Case

Where the initialization scale comes from, and what it bought on ImageNet

Glorot and Bengio set the initialization scale in 2010, from fan-in and fan-out. PyTorch implements it as a uniform bound of gain times the square root of six over fan_in plus fan_out, documented as xavier_uniform_ and labelled “Also known as Glorot initialization”.

Five years later four authors redid the derivation for rectifiers, in Delving Deep into Rectifiers. Their networks “achieve 4.94% top-5 test error on the ImageNet 2012 classification dataset”, which they call “a 26% relative improvement over the ILSVRC 2014 winner (GoogLeNet, 6.66%)”. That baseline is independently checkable rather than taken on the authors' word. The official ILSVRC 2014 results page at image-net.org records a classification error of 0.06656, annotated “Top5 val score is 6.66% error”.

The instructive part is where the two derivations actually differ, because through most of the useful range they do not. On the 14-layer model of their Table 1 and Table 2, Xavier initialization reached 33.90/13.44 top-1/top-5 error against 33.82/13.34 for the rectifier-aware scheme. The authors explicitly declined to call that superiority. At 22 layers both schemes still converged; theirs merely began reducing error earlier.

Then the same comparison was run on a model of up to 30 layers, 27 convolutional and 3 fully connected. There the two recipes stopped being variants of each other: “Our initialization is able to make the extremely deep model converge. On the contrary, the “Xavier” method completely stalls the learning, and the gradients are diminishing as monitored in the experiments.”

That is the shape of the whole subject in one experiment. At 14 layers the choice of derivation is worth a tenth of a point and you could reasonably not care. At 30 it is worth the entire run. And on the benchmark that made the paper famous, the gap between the two derivations was worth 1.72 points of top-5 error.

Figure

A named initialization scheme and the default a framework actually ships are not the same distribution, and the gap between two derivations was worth 1.72 points of top-5 error.

A healthy first batch is only the beginning

The most reliable recipe treats architecture, activation, initialization, normalization, and learning rate as a coupled system, and validates their early statistics together.

Most layers, meanwhile, never meet either named recipe at all. The two major libraries do not even agree on the default. PyTorch documents nn.Linear's weights as “are initialized from U(−√k, √k), where k = 1/in_features”, a range the implementation obtains by calling init.kaiming_uniform_(self.weight, a=math.sqrt(5)) — which is neither the Glorot bound nor the plain He one. Keras 3's Dense layer ships kernel_initializer="glorot_uniform", the Glorot recipe that PyTorch's nn.Linear does not use. Same layer, two ecosystems, two distributions, no announcement in either case.

Read the defaults before claiming that a network uses He initialization.

Stable training recipes are combinations, not isolated magic constants.

Key takeaways