The problem
Before training, every weight is set to a random number, and the size of those random numbers decides whether the network can train at all. A layer with inputs adds up products, and so it multiplies the variance of its input by
where is the variance of each weight (the average squared distance from 0, for weights with mean 0). Stack many layers and the factor compounds: above 1 the numbers explode, below 1 they vanish (the theory page shows both, measured). Initialization picks so that the factor is about 1.
Example. . Weights with variance 1 make the factor 512: after 3 layers the variance has grown million times. Weights with variance make the factor exactly 1.
The weights have shape and are used as x @ W. In the code, is fan_in and is fan_out.
Xavier (Glorot) uniform
Glorot and Bengio (2010) want both the forward activations and the backward gradients to keep their scale. The forward pass suggests , the backward pass , and the compromise is:
The weights are drawn uniformly between and . A uniform distribution on has variance , so to get the variance above:
Example. : , and the variance is .
Kaiming (He) normal
ReLU (rectified linear unit) sets about half of its inputs to 0, which halves the variance at every layer. He et al. (2015) make up for it with a factor 2, using only :
The weights are drawn from a normal (bell-shaped) distribution with mean 0 and this variance, so the standard deviation is .
Example. : standard deviation .
What to implement
xavier_uniform(fan_in, fan_out, rng): weights uniform on , with as above.kaiming_normal(fan_in, fan_out, rng): weights normal with mean 0 and variance .
Both return an array of shape (fan_in, fan_out). rng is a np.random.Generator: use rng.uniform and rng.normal.
Requirements
- Shape
(fan_in, fan_out). - Kaiming uses
fan_inonly.
Hint
rng.normal(mean, scale, size) takes the standard deviation as scale, not the variance: pass the square root.