Skip to main content

๐Ÿ“ Weight Initialization

Descriptionโ€‹

< What is it? >โ€‹

Weight initialization is the choice of the values a network's parameters start at, before the first forward pass. Biases are almost always started at zero; the weights cannot be, and the distribution they are drawn from decides whether the very first gradients are usable.

The reason it matters is the same repeated multiplication that drives Vanishing & Exploding Gradients. A signal passing through LL layers is multiplied by LL weight matrices on the way forward and by LL Jacobians on the way back. If each layer shrinks the signal by a factor slightly below 1, the product decays geometrically with depth; if each layer amplifies it slightly, the product blows up. Initialization sets that per-layer factor at step 0.

< The two degenerate cases >โ€‹

StartWhat happens
All zeros (or any constant)Every unit in a layer computes the same output and receives the same gradient, so they stay identical forever. The layer has the capacity of a single unit โ€” this is the symmetry problem, and it is why weights must be random.
Too large / too smallActivation variance grows or decays layer by layer. With sigmoid / tanh the units saturate and their derivatives go to zero; with ReLU the activations either explode or die out.

Random initialization breaks symmetry; the scale of that randomness is what keeps the signal alive through depth.

Key pointsโ€‹

< The variance argument >โ€‹

For a linear layer z=Wxz = Wx with ninn_{\text{in}} inputs, independent zero-mean weights of variance Varโก(W)\operatorname{Var}(W) and inputs of variance Varโก(x)\operatorname{Var}(x):

Varโก(z)=ninโ‹…Varโก(W)โ‹…Varโก(x).\operatorname{Var}(z) = n_{\text{in}} \cdot \operatorname{Var}(W) \cdot \operatorname{Var}(x).

Holding Varโก(z)โ‰ˆVarโก(x)\operatorname{Var}(z) \approx \operatorname{Var}(x) โ€” so that neither the forward activations nor the backward gradients change scale with depth โ€” requires Varโก(W)โ‰ˆ1/nin\operatorname{Var}(W) \approx 1 / n_{\text{in}}. Every standard scheme is a version of this fraction, adjusted for what the activation function does to the variance.

< The standard schemes >โ€‹

SchemeVarianceUse withWhy
Xavier([หˆzรฆvษชr]) / Glorot2nin+nout\dfrac{2}{n_{\text{in}} + n_{\text{out}}}tanh, sigmoid, linearAverages the forward (ninn_{\text{in}}) and backward (noutn_{\text{out}}) constraints, so activations and gradients keep roughly constant variance. Assumes an activation that is near-linear around 0.
He / Kaiming2nin\dfrac{2}{n_{\text{in}}}ReLU, Leaky ReLU, GELUReLU zeroes about half its inputs and so halves the variance; the factor 2 compensates. Using Xavier with ReLU makes activations decay with depth.
LeCun1nin\dfrac{1}{n_{\text{in}}}SELU, self-normalizing netsForward-only constraint; the fixed point SELU's self-normalizing property is derived from.
Orthogonalโ€” (orthogonal matrix, optionally scaled by a gain)deep RNNs, very deep stacksAn orthogonal WW preserves vector norms exactly, so repeated multiplication neither shrinks nor grows the signal.

Each comes in a normal and a uniform flavor: draw from N(0,ฯƒ2)\mathcal{N}(0, \sigma^2) with ฯƒ2\sigma^2 from the table, or from U(โˆ’a,a)\mathcal{U}(-a, a) with a=3ฯƒ2a = \sqrt{3\sigma^2} (which gives the same variance).

  • fan_in is the number of inputs to the layer, fan_out the number of outputs. For a conv layer, fan_in = in_channels ร— kernel_height ร— kernel_width.
  • The choice follows the activation function, not the task. The one question worth asking is "does this layer's nonlinearity kill part of the signal?" โ€” if it does (ReLU family), use He.

< In practice >โ€‹

import torch.nn as nn

layer = nn.Linear(512, 512)

# ReLU-family activations -> He / Kaiming
nn.init.kaiming_normal_(layer.weight, nonlinearity='relu')

# tanh / sigmoid -> Xavier / Glorot
nn.init.xavier_uniform_(layer.weight, gain=nn.init.calculate_gain('tanh'))

nn.init.zeros_(layer.bias)
  • Framework defaults are not always the right scheme. PyTorch's nn.Linear and nn.Conv2d default to a Kaiming-uniform variant with a=โˆš5, which is a historical choice rather than a match for your activation; setting the initializer explicitly is cheap insurance for a deep model.
  • Biases start at zero. Exceptions: LSTM forget-gate biases are often set to 1 so the cell remembers by default, and a final-layer bias can be set to the log-odds of the base rate to save the model from learning the class prior.
  • Residual branches are often started at zero (a zero-init final BatchNorm ฮณ\gamma, or a learnable scalar at zero), so a deep network starts as the identity and gains depth as training proceeds.
  • Normalization reduces the sensitivity but does not remove it. BatchNorm/LayerNorm rescale activations after the fact, so a badly scaled init hurts less โ€” but the backward pass at step 0, and networks without normalization, still depend on it.
  • Transformers typically use a small fixed standard deviation (e.g. 0.020.02) and scale the residual-projection weights by 1/2L1/\sqrt{2L} so the residual stream's variance does not grow with layer count.

< How to tell it is wrong >โ€‹

Run one forward and one backward pass on a single batch, and print per-layer statistics:

  • Activation standard deviation shrinking steadily with depth โ†’ scale too small (or Xavier used with ReLU).
  • Activation standard deviation growing with depth, or inf / NaN at step 0 โ†’ scale too large.
  • tanh / sigmoid outputs clustered at ยฑ1\pm 1 / 00 and 11 โ†’ saturated from the start.
  • A large share of ReLU units at exactly 0 across all batches โ†’ dead units.
  • Early-layer gradient norms orders of magnitude below later ones โ†’ the backward signal is decaying.

Implementationโ€‹

< Notebook >โ€‹

Open the weight-initialization notebook on GitHub

< Weight-matrix dimensions >โ€‹

Q: Why is Wโ‚ shaped (2, 3) and Wโ‚‚ shaped (1, 2)?

layers_dims = [3, 2, 1] describes this network:

3ย inputย featuresโŸถ2ย hiddenย neuronsโŸถ1ย outputย neuron.3\ \text{input features} \quad \longrightarrow \quad 2\ \text{hidden neurons} \quad \longrightarrow \quad 1\ \text{output neuron}.

Each row of a weight matrix belongs to one neuron in the current layer; each column corresponds to one input from the previous layer.

MatrixConnectsShape
W1W_13 input features to 2 hidden neurons(2,3)(2, 3)
W2W_22 hidden activations to 1 output neuron(1,2)(1, 2)

For the first layer, xโˆˆR3ร—1x \in \mathbb{R}^{3 \times 1} and b1โˆˆR2ร—1b_1 \in \mathbb{R}^{2 \times 1}:

Z1=W1x+b1โ‡’(2ร—3)(3ร—1)+(2ร—1)=(2ร—1).Z_1 = W_1x + b_1 \qquad\Rightarrow\qquad (2 \times 3)(3 \times 1) + (2 \times 1) = (2 \times 1).

The result has one value for each hidden neuron. The second layer receives those two activations:

Z2=W2A1+b2โ‡’(1ร—2)(2ร—1)+(1ร—1)=(1ร—1).Z_2 = W_2A_1 + b_2 \qquad\Rightarrow\qquad (1 \times 2)(2 \times 1) + (1 \times 1) = (1 \times 1).

In general:

Wl.shape=(neuronsย inย layerย l,ย neuronsย inย layerย lโˆ’1)\boxed{ W_l.\text{shape} = (\text{neurons in layer } l,\ \text{neurons in layer } l - 1) }
W_l.shape = (layers_dims[l], layers_dims[l - 1])

Crash courseโ€‹

Referenceโ€‹