Xavier/He Weight Initialization - Neural Networks
Implement Xavier/He Weight Initialization in Python with NumPy. medium exercise with examples, hints, and instant feedback. No setup or signup required.
Xavier/He Weight Initialization
What you'll build
A function that draws a weight matrix with Xavier (Glorot) normal or He (Kaiming) normal initialization and returns the empirical standard deviation of the sampled weights, so you can check it lands on the theoretical value.
Background
If weights are too small, activations shrink layer after layer and gradients vanish; too large, and they explode. Good initialization picks the variance so that the signal keeps roughly the same scale through the network:
- Xavier (Glorot & Bengio, 2010) balances the forward and backward pass for symmetric activations like tanh:
Var(W) = 2 / (n_in + n_out). - He (He et al., 2015) accounts for ReLU zeroing half of its inputs, which halves the variance, by doubling it:
Var(W) = 2 / n_in.
Formula
Xavier normal: W = randn(n_in, n_out) * sqrt(2 / (n_in + n_out))
He normal: W = randn(n_in, n_out) * sqrt(2 / n_in)
return round(std(W), 4)
Inputs
n_in: number of input units (fan-in), positive intn_out: number of output units (fan-out), positive intmethod:'xavier'or'he'
Expected return format
A float: np.std(W) of the sampled (n_in, n_out) matrix, rounded to 4 decimals. It is close to, but not exactly, the theoretical std (sampling noise).
Edge cases and conventions
- Call
np.random.seed(42)at the start of the function (the starter code does this), then draw exactly onenp.random.randn(n_in, n_out)and scale it. The expected values depend on this exact sequence. - Any other
methodmay raise aValueError(not tested).
Constraints
NumPy only.
Follow-up
- The uniform variants draw from
U(-a, a)witha = sqrt(3) * std. Whysqrt(3)? (The variance ofU(-a, a)isa² / 3.) - PyTorch's
nn.Lineardefault is Kaiming-uniform witha = sqrt(5), which givesU(-1/sqrt(n_in), 1/sqrt(n_in)). Why do transformers often use a small fixed std like 0.02 instead?
Examples
Input: initialize_weights(784, 256, "he") Output: 0.0505
The theoretical std is sqrt(2/784) = 0.0505; with 200k samples the empirical std matches to 4 decimals.
Input: initialize_weights(100, 50, "xavier") Output: 0.1151
Theoretical sqrt(2/150) = 0.1155; with only 5,000 samples the empirical std is slightly off.
Practice in Python
import numpy as np
def initialize_weights(n_in, n_out, method='xavier'):
"""
Sample an (n_in, n_out) weight matrix with Xavier or He normal initialization.
Args:
n_in: Number of input units (fan-in)
n_out: Number of output units (fan-out)
method: 'xavier' (std = sqrt(2 / (n_in + n_out))) or 'he' (std = sqrt(2 / n_in))
Returns:
std: Empirical standard deviation of the sampled weights, rounded to 4 decimals
"""
np.random.seed(42) # For reproducibility: draw one np.random.randn(n_in, n_out) after this
# Your code here
pass
The interactive editor loads with Python and NumPy. Run your implementation against 5 tests, or use the progressive hints.