Weight Initialization
Weight initialization is the process of assigning starting values to the learnable parameters of a neural network before training begins. It matters profoundly because poor initialization can cause gradients to vanish or explode across layers, making deep networks effectively untrainable regardless of architecture or learning rate. The counterintuitive fact: initializing all weights to the same value — even a sensible constant like zero — is strictly worse than random noise, because symmetry between neurons means the network can never differentiate what each unit learns.
The goal of weight initialization is to keep the variance of activations and gradients approximately constant as signals propagate forward and backward through the network. If variance grows layer by layer, activations saturate and gradients explode; if variance shrinks, activations collapse and gradients vanish — either way, learning stalls. The "aha moment" is recognizing that a randomly initialized network is already a carefully engineered system: the scale of random weights must be calibrated to the fan-in and fan-out of each layer so that the network begins in a trainable regime, not by accident but by design.
The most common mistake is treating initialization as a one-time default setting — just use `torch.nn.Linear`'s built-in Kaiming uniform and forget about it. What practitioners get wrong is assuming that a single scheme (e.g., Kaiming He) is universally optimal regardless of activation function or architecture. Kaiming initialization is derived specifically for ReLU networks; applying it to networks with Tanh activations violates its derivation and can cause instability. The correct mental model is that every initialization scheme is a solution to a specific variance-propagation equation tied to a particular activation function — change the activation, and you may need to change the initialization.
How It Works
The Variance Propagation Problem
Consider a single linear layer with no activation function. Given an input vector and weight matrix with entries drawn i.i.d. from a distribution with mean 0 and variance , the output has variance per element:
Explanation: Each output neuron accumulates independent products , so variance multiplies by the fan-in, meaning the signal amplitude changes by a factor of at every layer. Here is the number of inputs (fan-in), is the per-weight variance, and is the input variance assumed identical across neurons. To keep , we need , which is exactly LeCun initialization.
Xavier / Glorot Initialization
Xavier Glorot and Yoshua Bengio (2010) jointly constrained both the forward pass (preserve activation variance) and the backward pass (preserve gradient variance). The symmetric requirement gives:
Explanation: By averaging the fan-in and fan-out constraints, Xavier initialization keeps variance stable in both directions simultaneously, which is especially important for symmetric activations like Tanh and Sigmoid. In practice this is often implemented as a uniform distribution:
Explanation: A uniform distribution has variance , so setting recovers the target .
Kaiming / He Initialization
Xavier assumes a linear activation function. ReLU zeroes out half of its inputs, effectively halving the variance at every layer. Kaiming He et al. (2015) corrected for this:
Explanation: The factor of 2 compensates for the approximately 50% of activations that ReLU kills, restoring the forward-pass variance to its pre-activation level. For the backward pass, the analogous correction uses fan-out: . PyTorch's default for `nn.Linear` and `nn.Conv2d` is Kaiming uniform with fan-in mode: ```python import torch import torch.nn as nn import math
Manually applying Kaiming uniform (what PyTorch does by default)
layer = nn.Linear(512, 256) nn.init.kaiming_uniform_(layer.weight, a=0, mode='fan_in', nonlinearity='relu') nn.init.zeros_(layer.bias)
Xavier / Glorot uniform for Tanh networks
layer_tanh = nn.Linear(512, 256) nn.init.xavier_uniform_(layer_tanh.weight, gain=nn.init.calculate_gain('tanh')) nn.init.zeros_(layer_tanh.bias)
LeCun normal (recommended for SELU activations)
layer_selu = nn.Linear(512, 256) nn.init.normal_(layer_selu.weight, mean=0.0, std=1.0 / math.sqrt(512)) nn.init.zeros_(layer_selu.bias) ```
The Orthogonal Initialization Alternative
For very deep recurrent or residual networks, orthogonal initialization draws from the set of orthogonal matrices (via QR decomposition of a random Gaussian matrix), guaranteeing that:
Explanation: An orthogonal weight matrix preserves the L2 norm of any input vector exactly, so neither explosion nor collapse can occur on the forward pass regardless of depth. ```python
Orthogonal initialization for RNNs or deep residual blocks
rnn_layer = nn.Linear(256, 256) nn.init.orthogonal_(rnn_layer.weight, gain=1.0) ```
Kaiming He is a computer vision researcher who completed his PhD at the Chinese University of Hong Kong and later joined Microsoft Research Asia and then Facebook AI Research (FAIR). In his landmark 2015 paper "Delving Deep into Rectifiers," He derived the variance formula that now bears his name, showing empirically that it enabled training of networks with over 30 layers — at a time when training beyond 20 layers was considered practically infeasible. A surprising fact: the same paper also introduced Parametric ReLU (PReLU), but it is the initialization scheme that became the more lasting contribution.
Xavier Glorot conducted his foundational work on initialization as a graduate student under Yoshua Bengio at the Université de Montréal. His 2010 paper "Understanding the difficulty of training deep feedforward neural networks" was among the first to systematically measure and explain gradient vanishing as a function of initialization scale, and it introduced what is now called Xavier or Glorot initialization. Remarkably, the paper's secondary finding — that Tanh consistently outperformed Sigmoid because of better gradient flow — helped shift the field away from Sigmoid activations years before ReLU became dominant.
Intuition
Imagine you're tuning a large pipe organ where every pipe starts at the exact same length (zero initialization). When you play a chord, every pipe produces the identical sound, and every pipe responds identically to the tuner's adjustments — you can tune forever and the organ remains monotone. Random initialization is like starting each pipe at a slightly different, roughly correct length: now each pipe responds uniquely to tuning, and the full harmonic range becomes reachable. The variance calibration part has an equally clean analogy: imagine passing a whisper through a chain of 20 people. If each person amplifies the message even slightly (say, 1.1×), by person 20 the whisper is a shout that overwhelms all nuance — that's gradient explosion. If each person quiets it slightly (0.9×), by person 20 nothing is audible — that's gradient vanishing. Good initialization is the rule that says "each person should pass the message at exactly the same volume they received it," which is precisely what the formula enforces. The practical implication: initialization is not about finding the "right" weights (they'll be trained away quickly). It's about finding a starting point where the gradient signal is coherent enough that the optimizer can take its first meaningful step.
Historical Development
Why It Matters
Prerequisites
What This Leads To
See Also
Batch Normalization · Activation Functions · Vanishing and Exploding Gradients · [[Residual Networks