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
- Explain why identical parameter initialization prevents useful specialization
- Relate Xavier and He-style scaling to activation functions and layer width
- Inspect forward and backward signal statistics before long training
- Choose initialization and residual scaling as part of an architecture-specific recipe
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.
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.
- 1
Run a forward pass
Record per-layer mean, variance, saturation, sparsity, and output scale.
- 2
Compute the loss
Confirm the initial value matches target cardinality and reduction expectations.
- 3
Run backward
Record gradient norms, missing gradients, and layerwise attenuation or growth.
- 4
Simulate one update
Measure update-to-parameter ratios and check numerical validity.
- 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.
1. Verify shapes and reductions
Rule out broadcast errors and unexpected loss scale.
2. Plot activation distributions
Look for saturation, dead outputs, exploding variance, and constant channels.
3. Trace gradients by depth
Check attenuation, growth, and missing paths after one backward pass.
4. Compare initialization recipes
Change only the relevant module or scaling rule.
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 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
- Initialization breaks harmful symmetry while placing activations and gradients in a dynamic range the optimizer can use — a 14-layer network barely registers the choice (33.90/13.44 against 33.82/13.34 top-1/top-5 error), a model of up to 30 layers stalls completely on the wrong one.
- Xavier and He-style rules encode approximate variance assumptions tied to width, direction, and activation behavior, and the multiplier is published rather than guessed: PyTorch's calculate_gain lists 1 for Linear/Identity, Conv and Sigmoid, 5/3 for Tanh, √2 for ReLU and 3/4 for SELU.
- Architecture-specific choices scale residual branches, gates, output heads, or final layers differently — GPT-2 by 1/√N, Megatron-LM by 1/√(2N), ReZero by a single zero-initialized scalar per branch.
- Forward statistics, initial loss scale, backward norms, and one-step update ratios reveal many problems before training begins, and reading the framework's actual default (nn.Linear's kaiming_uniform_ with a=math.sqrt(5)) comes before changing the optimizer.
- Zero initialization is valid for selected parameter roles but harmful when it erases needed hidden-unit specialization: one scalar per residual branch at zero yields a 128-layer Transformer at 1.08 bits per byte and 56% faster convergence at 12 layers.
- Initialization, normalization, residual design, activation choice, precision, and learning rate should be validated as one coupled recipe — ten thousand vanilla convolutional layers have been trained with no residual connections and no batch normalization by changing the initialization alone, and the seed's own contribution has been measured over 200 randomizations per source.