Checking a Model Before a Long Training Run
A network can fail because its starting values suppress the signal or because its derivative is wrong. Test these separately before tuning the optimiser.
13 sections · 4 questions · 2 coding challenges
The arrow keys move between sections too.
Keeping Signals Alive Through Layers
BOTIE
A finite-difference check addresses a different issue: whether an implemented derivative agrees with the loss. Good initialisation cannot repair a wrong derivative, so our lesson keeps these two checks separate.
Is the Model Learning Slowly, or Is the Calculation Wrong?
Botie starts a training run and the loss hardly changes. Increasing the learning rate might help, but it might also hide a broken derivative or make an unstable calculation worse.
Before changing the learning rate, let's inspect the starting weights. Do they allow useful signals to reach the later layers? Then we can check a derivative independently by nudging one parameter and measuring the change in loss. Agreement gives us a reason to trust the update calculation.
The paired loss values illustrate a central finite-difference check.
Step 1 of 3
Start with a scalar loss
Use a smooth example where the derivative is independently known.
Loss(w) = (w − 3)² Test weight: 1 Loss(1) = (1 − 3)² = 4
Step 2 of 3
Perturb in both directions
Evaluate the same deterministic loss just above and below the weight.
Step h = 0.00001 Loss(1 + h) = 3.9999600001 Loss(1 − h) = 4.0000400001 Central difference = (3.9999600001 − 4.0000400001) / 0.00002 ≈ −4
Step 3 of 3
Compare with the derivative
Agreement checks local calculation, not the usefulness of a model.
Analytical derivative = 2 × (1 − 3) = −4 Numerical derivative ≈ −4
A numerical check can expose a mistake before a long run
Symmetric perturbations estimate the local slope while cancelling some approximation error.
Does a small perturbation agree with the claimed derivative?
The loss has been measured on either side of a parameter. Compare the numerical slope with the proposed gradient and consider how the perturbation size affects that comparison.
BOTIE
Slope estimated from the two loss evaluations
-4
Follow the operations
1. Loss at w+h
3.61
2. Loss at w-h
4.41
3. Subtract
-0.8
4. Set a constant
2
5. Positive perturbation
0.1
6. Multiply
0.2
7. Divide
-4
8. Result
-4
Use deterministic evaluations at a smooth point. Finite differences are approximate.
1. Break Symmetry Without Exploding the Signal
If every hidden unit starts with identical incoming weights and receives identical updates, they can remain copies. Random initialisation breaks that symmetry. But arbitrarily large weights can saturate activations, while tiny scales can shrink signals through many layers.
A variance rule uses the number of inputs and outputs to set a starting scale. Which rule fits depends on the activation and architecture. Biases can often start at zero without making all hidden weights identical.
Use a seeded generator for a reproducible check, not the same identical value for every weight.
2. Understand a Scale Rule
For a layer with fan_in inputs and fan_out outputs, a common Glorot normal scale uses a variance based on both. This is a design starting point, not a claim that one tiny sampled matrix has exactly that variance.
Variance = 2 / (fan_in + fan_out)
fan_in = 4; fan_out = 4
Standard deviation = sqrt(2 / 8) = 0.5ReLU networks often use an incoming-connection-based rule instead; activation assumptions matter.
3. Estimate a Derivative by Perturbing One Parameter
Hold data and all other parameters fixed. Evaluate loss slightly above and below one parameter value, subtract, and divide by twice the perturbation. Compare with the implemented derivative at the unperturbed value.
Choose a smooth point and use floating-point precision appropriate for the test. Too large a perturbation introduces approximation error; too small one can suffer cancellation.
Numerical derivative ≈ [L(w+h) - L(w-h)] / (2h)
L(w) = (w-3)²; w = 1
Analytical derivative = 2 × (1-3) = -4Repeat for several parameters and several perturbation sizes before trusting a complex backward pass.
Estimate a slope from two nearby losses
The loss below is a simple bowl. Evaluate it on both sides of the chosen weight, using a symmetric perturbation so neither side gets special treatment.
import numpy as np
def loss(weight):
return (weight - 2.0) ** 2
weight, h = 4.0, 0.01
above = loss(weight + h)
below = loss(weight - h)
slope = (above - below) / (2 * h)
print(f'{slope:.4f}')4.0000The positive slope says that a small increase in the weight raises this loss. In practice, compare a numerical estimate like this with the derivative produced by the implementation.
4. Remove Sources of False Disagreement
Disable random dropout and freeze sampled data during a gradient check. Do not compare two losses evaluated with different random masks. At a ReLU corner, the derivative is not unique, so use points away from that corner.
Compare with a tolerance that combines absolute and relative error. A derivative check establishes local consistency with the coded loss; it does not show that the loss or labels represent the intended task.
The workshop calculates a central difference from two supplied loss evaluations.
5. Identical Hidden Units Can Remain Identical
If hidden units start with identical incoming and outgoing weights, they receive the same activations and updates under a symmetric architecture. Several units can then behave like copies of one unit instead of learning different features.
Small random differences break that symmetry. Their scale also matters: overly large values can saturate some activations, while overly small values can weaken signals through many layers. Fan-in based initialisation rules address signal scale under assumptions about the layer and activation; they are not universal constants.
6. Check a Derivative along a Chosen Direction
A coordinate check nudges one parameter at a time. A directional check nudges several together and compares the finite difference with the gradient's dot product with that direction. It is an inexpensive additional check for a larger parameter vector.
Use deterministic evaluation during the comparison. Dropout draws, changing batches or stateful updates can change the loss for reasons unrelated to the parameter perturbation.
Check a quadratic derivative independently
The analytic gradient is compared with a symmetric finite difference at the same point.
import numpy as np
w = np.array([1., -2.])
direction = np.array([.6, .8])
def loss(v):
return float((v*v).sum())
eps = 1e-5
numerical = (loss(w+eps*direction)-loss(w-eps*direction))/(2*eps)
analytic = (2*w)@direction
print(round(float(numerical), 4))
print(round(float(analytic), 4))-2.0
-2.0Agreement checks this direction at this point. Test several directions and examples; one successful check does not prove every branch of a network correct.
7. A Smaller Perturbation Is Not Always a Better Check
A large perturbation measures curvature over too wide an interval. A very small one subtracts nearly equal floating-point losses and can amplify rounding error. Try a sensible range and look for agreement over a stable region.
At a nondifferentiable point, such as the corner of ReLU, a symmetric finite difference can disagree with the implementation's selected derivative without revealing a bug. Move away from the corner for the smooth check, and test the chosen boundary convention separately.
Check yourself
Questions on Parts 1 to 7
Question 1 of 4
What is its derivative?
Use the information below.
L(w)=(w-3)^2, w=1.Try it with Botie
Build with Botie
Write and run Python