NeuronCanvas
Neural Networks
Training a Network

Training-Time Regularization

Training-procedure techniques - dropout, early stopping, batch normalization, and data augmentation - that fight overfitting without changing the loss formula.

Step 01 - The break

94% in training, 71% one flag later

A network that scores 94% during training and 71% the moment you switch it to evaluation mode. Same weights, same data, one flag.

the same hidden layer, twice
during training, dropout active, one random mask
the layer hands on [1.6000, 0.0000, 2.4000, 0.0000]
mean 1.0000 spread 1.0392
at prediction time, dropout off, nothing else changed
the layer hands on [0.8000, -0.3000, 1.2000, 0.5000]
mean 0.5500 spread 0.5500
the next layer spent the whole of training learning to expect the
first row and is now being handed the second

Both rows are correct. Both are what the layer is supposed to produce in its own mode. And the layer downstream of them has been calibrated against numbers it will never see again.

Step 02 - Before this page
Step 03 - The stage

Press ‘Resample mask’ five or six times and watch the four numbers change. Then switch to Inference and press it again - nothing happens, and that is the point.

Preview frame from the "Training-Time Regularization" animation0:14

What this animation shows

A small multi-layer network lights up, and then, over a few rounds, random hidden neurons fade out and shrink ("dropped") before fading back in, repeating with a different random set each round. At the end, every neuron lights up together with the caption "at test time, every neuron is back on." This is dropout: randomly disabling neurons during training so the network can't over-rely on any one of them.

1.60-0.602.401.00inputhidden (dropout here)output
mode: training, p = 0.50
h = [0.8, -0.3, 1.2, 0.5]
h' = [1.6000, -0.6000, 2.4000, 1.0000]
Mode

Greyed, dashed neurons were dropped this draw. Survivors get scaled up by 1/(1−p) so the layer's expected total signal stays the same.

Step 04 - One question first

Dropout at p = 0.5 switches off half a layer’s neurons. What does it do to the survivors?

  • aNothing - the rest carry on as normal
  • bHalves them, so the total stays the same
  • cDoubles them, so the total stays the same
  • dAdds noise to them
Commit to a guess, then open this

Doubles them. The surviving activations are divided by (1 − p) = 0.5, so 0.8 becomes 1.6 and 1.2 becomes 2.4 - which is what the widget’s readout shows. The reason is that the next layer has to see roughly the same total signal whether or not units were dropped, because at prediction time nothing will be dropped at all. Averaged over all sixteen possible masks the layer’s output comes back to exactly [0.8, −0.3, 1.2, 0.5], unchanged. Unbiased on average, and different on every single pass - and that difference is the regularizer.

Step 05 - Plain explanation

The previous page covered regularizers that change the loss formula itself. This page covers a different category: techniques applied to the training procedure or the network’s structure, rather than to the loss.

Dropout. During training, randomly and independently switch off a fraction of neurons on every forward pass, as if training a huge ensemble of thinner sub-networks that all share weights. No single neuron can be relied upon to always be present, so the network is forced to spread information across multiple redundant paths instead of a few neurons over-specializing together. At test time, use the WHOLE network, but since training only ever saw a thinned-out version, surviving activations get scaled up during training so the expected total signal stays the same (“inverted dropout”).

A trap worth stepping around before the numbers. There are two conventions for what the letter p means in dropout, and they are inverses of each other. This page uses p = the probability a unit is dropped, which is PyTorch’s convention and TensorFlow’s current one: nn.Dropout(0.5) drops half the units. Some sources, including the CS231n course notes, use p = the probability a unit is kept, and their scaling factor is therefore 1/p where this page’s is 1/(1−p). Both describe identical behaviour.

Batch normalization. As a network trains, the distribution of values flowing into any given layer keeps shifting, because every earlier layer’s weights are also changing, a moving target that makes each layer’s job harder. Batch Normalization forces every layer’s inputs to have a fixed mean (0) and spread (1) within each mini-batch, then hands back two learnable knobs, a scale and a shift, so the network can undo that forcing if it turns out to need a different mean or spread for that particular layer.

Early stopping depends entirely on having a validation set. Split your data into three parts before training. The training set is what the network learns from and what the loss is computed on. The validation set is held out, never trained on, and scored periodically to see how the network does on data it has not memorised; it is what you use to make decisions, like when to stop or which λ to pick. The test set is held out from that too, scored once at the very end, and never used to make any decision at all, because every decision you make using a set is a way of slowly fitting to it. A typical split is 80/10/10.

That is what makes early stopping possible. Training loss cannot tell you when to stop, because it falls essentially forever; a network that has memorised the training set has a beautiful training loss. Validation loss is measured on data the network has never adjusted itself to fit, so when it starts rising, the network has stopped learning the pattern and started learning the sample.

Training with players missing

A football side that always practises eleven-a-side builds moves that depend on one particular winger being in one particular place. Practise with three players pulled out at random every session and nobody can be relied on, so everyone learns to cover. That is dropout, and the ensemble claim is literal: with four players there are sixteen possible line-ups and training visits them at random. The catch is that on match day you field the whole eleven, a team that never actually trained together, which is exactly why the survivors get scaled up during training.

Where it breaks downFootballers know who is missing. A neuron has no idea; it just receives a smaller number and gets no gradient at all.

Step 06 - The depth ladder
WordsFour techniques, none of which touch the lossWhat each technique does, and the one vocabulary trap that will otherwise cost you an afternoon.Rung 01

The previous page covered regularizers that change the loss formula itself. These four are applied to the training procedure or the network’s structure instead.

Dropout switches off a fraction of neurons at random on every forward pass, as if training a huge ensemble of thinner sub-networks that all share weights. No single neuron can be relied on, so the network spreads information across redundant paths. At test time the whole network is used, so survivors are scaled up during training to keep the expected total signal the same.

The p trap: this page uses p = probability of being dropped, which is PyTorch’s convention. CS231n and others use p = probability of being kept, with 1/p scaling rather than 1/(1−p). Identical behaviour, opposite letters.

Batch normalization forces every layer’s inputs to a fixed mean of 0 and spread of 1 within each mini-batch, then hands back a scale and a shift so the network can undo it where it needs to.

Early stopping needs a validation set: training data to learn from, validation data to make decisions with, test data scored once at the end and never used to decide anything. A typical split is 80/10/10. Training loss cannot tell you when to stop because it falls essentially forever; validation loss can, because the network never adjusted itself to fit it.

One mechanism used below and worth naming now: a running average blends each new measurement into a stored value instead of replacing it - keep 90% of what you had, add 10% of what you just saw. You do it this way because any single mini-batch’s mean and variance are noisy, and inference needs one fixed pair of numbers rather than whichever batch happened to arrive last.

NumbersEvery technique, workedDropout twice over, the patience counter traced, and batch norm in both of its two modes.Rung 02

Dropout. p=0.5, layer activations h=[0.8, −0.3, 1.2, 0.5], and this particular random draw happens to keep units 1 and 3:

Worked example
h' = [0.8/0.5, 0, 1.2/0.5, 0] = [1.6000, 0, 2.4000, 0]
Expectation check for unit 1 across many such draws:
0.5 * 0 + 0.5 * 1.6 = 0.8, exactly matching the original h = 0.8

One random draw does not show an ensemble. Here are two draws over the same layer, plus the average over every possible draw:

Worked example - the same layer, different masks. p = 0.5
draw A, mask [1, 0, 1, 0]
h' = [0.8/0.5, 0, 1.2/0.5, 0] = [1.6000, 0.0000, 2.4000, 0.0000]
draw B, mask [0, 1, 1, 1]
h' = [0, -0.3/0.5, 1.2/0.5, 0.5/0.5] = [0.0000, -0.6000, 2.4000, 1.0000]
With 4 units there are 2^4 = 16 possible masks, and training visits
them at random. Averaged over all 16 masks, unit by unit:
[0.800000, -0.300000, 1.200000, 0.500000]
= exactly h. The 1/(1-p) scaling makes the average unbiased.

Unbiased on average is not the same as unchanged. Each individual forward pass is noisy, and the scaling makes that noise larger, not smaller. For a unit with activation h, the variance introduced is h²·p/(1−p):

p = 0.2 scale 1.2500 variance added to a unit with h = 0.8: 0.160000
p = 0.5 scale 2.0000 variance added: 0.640000
p = 0.8 scale 5.0000 variance added: 2.560000

That noise is the regularizer. It is also why p = 0.8 is rarely a good idea: past a point, the layer’s output is more noise than signal. Common values are 0.5 for fully connected hidden layers and 0.1 to 0.2 for convolutional ones, which have far fewer parameters per unit and need less help.

The backward pass needs no special rule, which is worth seeing rather than being told. The mask is just a multiplication by a constant, so the gradient gets multiplied by the same constant on the way back:

Worked example - gradient through the same mask [1, 0, 1, 0], p = 0.5
gradient arriving at h': [ +0.1000, -0.2000, +0.3000, +0.4000 ]
dL/dh_i = dL/dh'_i * mask_i / (1 - p)
unit 1 kept: +0.1000 * 1 / 0.5 = +0.2000
unit 2 dropped: -0.2000 * 0 / 0.5 = 0.0000
unit 3 kept: +0.3000 * 1 / 0.5 = +0.6000
unit 4 dropped: +0.4000 * 0 / 0.5 = 0.0000

A unit that was dropped receives exactly zero gradient, so its incoming weights are not updated on that pass at all. That is the second half of dropout’s regularizing effect: not only does the network have to work without any given unit, no unit gets to learn from every example.

That is dropout in full. Next, a technique that regularizes not by changing what a layer computes but by changing when training stops.

Early stopping. Track validation loss per epoch, stop when it hasn’t improved for some number of consecutive epochs (“patience”), and keep the weights from the BEST epoch, not the last one.

EpochTrain lossValidation loss
10.900.95
20.740.68
30.610.48
40.520.39
50.450.34
6← keep this one0.400.30
70.360.33
80.340.38
90.320.41
100.310.44

Train loss falls steadily through all 10 epochs. Validation loss bottoms out at epoch 6 (0.30), then climbs for the rest of training. With patience = 3, here is exactly when the stop fires and which weights survive:

Worked example - the patience counter, epoch by epoch
epoch 1 val 0.95 new best (0.95) save weights counter -> 0
epoch 2 val 0.68 new best (0.68) save weights counter -> 0
epoch 3 val 0.48 new best (0.48) save weights counter -> 0
epoch 4 val 0.39 new best (0.39) save weights counter -> 0
epoch 5 val 0.34 new best (0.34) save weights counter -> 0
epoch 6 val 0.30 new best (0.30) save weightscounter -> 0
epoch 7 val 0.33 no improvement counter -> 1
epoch 8 val 0.38 no improvement counter -> 2
epoch 9 val 0.41 no improvement counter -> 3 = patience, STOP
training halts during epoch 9, and the weights restored are the ones
saved at epoch 6, three epochs before the run ended
the generalization gap tells the same story:
epoch 6 train 0.40 val 0.30 gap -0.10 (nothing wrong yet)
epoch 10 train 0.31 val 0.44 gap +0.13 (memorising)

Two things that trip people up. Patience exists because validation loss is noisy: a single bad epoch is not evidence of overfitting, and a patience of 1 would stop most runs far too early. And the epochs after the best one are not wasted; you had to run them to find out that epoch 6 was the best. Early stopping always costs you patience epochs of hindsight.

That is the second technique in full. The third, batch normalization, is the longest block here - it has more moving parts, so take it in stages: first what it computes, then why the tiny constant in its formula matters, then how its two learned knobs actually get updated.

Batch normalization. Mini-batch of 4 pre-activation values x=[2.0, 4.0, 4.0, 6.0]:

Worked example
mu_B = 4.0; var_B = ((2-4)^2 + 0 + 0 + (6-4)^2)/4 = 2.0
sqrt(var_B + eps) ~ 1.414214
xhat = [-1.4142, 0, 0, 1.4142]
with learned gamma = 1.5, beta = 0.5:
y = [-1.6213, 0.5000, 0.5000, 2.6213]

Before the next block, what ε is doing in that denominator. It is a tiny constant, usually 1e-5, added to the variance purely so the division cannot blow up. If every value in a mini-batch happens to be identical, the variance is exactly 0, and without ε you would divide by zero. With ε you divide by √0.00001 = 0.003162 instead and every normalized value comes out as 0, which is the sensible answer: values that were all identical carry no information to spread out.

Worked example - what epsilon prevents. batch [3.0, 3.0, 3.0, 3.001]
mu = 3.00025 variance = 0.000000187500 almost degenerate
without epsilon: sqrt(0.000000187500) = 0.000433
xhat = [ -0.577350, -0.577350, -0.577350, +1.732051 ]
a 0.001 difference has been amplified into a value of 1.73
with epsilon 1e-5: sqrt(0.000000187500 + 0.00001) = 0.003192
xhat = [ -0.078326, -0.078326, -0.078326, +0.234978 ]
the same difference stays small, which is the honest answer
and on the normal batch above, [2.0, 4.0, 4.0, 6.0], epsilon changes
the divisor from 1.4142136 to 1.4142171, a difference of 0.0000035

ε is invisible in normal operation and decisive in the one case that would otherwise crash. That is what a numerical-stability constant is for. The demo in rung 3 uses the same ε = 1e-5, so you can watch it happen: drag the four points close together and the normalized values stop growing the way the top row of that block does.

Three words in the next block mean something different here from what they meant earlier in this module. “Momentum” here is not the Momentum optimizer. On the optimizers page, momentum is accumulated velocity and its coefficient β = 0.9 says how much of the past you keep. Here, “momentum” is the weight given to the new value when updating a running average, so a momentum of 0.1 means the stored value keeps 0.9 of itself and takes only 0.1 of the incoming batch. The two numbers look like complements of each other and the roles are reversed: 0.9 there means long memory, and 0.1 here also means long memory. Second, β in y = γx̂ + β is batch norm’s shift parameter, unrelated to the optimizer’s momentum coefficient and to Adam’s β₁ and β₂. Third, “biased” below is a property of a formula: the version that divides by m systematically under-estimates the true spread of the population the sample came from, and dividing by m−1 instead corrects it - a third unrelated sense of a word this module has now used three ways.

Worked example - updating the running statistics, then predicting with them
from this batch: mu_B = 4.000000
var_B = 2.000000 (biased, 1/m; normalizes the batch)
var_B unbiased = 4/3 * 2.000000 = 2.666667 (this gets stored)
running_mean <- 0.9 * 3.800000 + 0.1 * 4.000000 = 3.820000
running_var <- 0.9 * 2.500000 + 0.1 * 2.666667 = 2.516667
now switch to inference. One example, x = 5.0, no batch to average:
xhat = (5.000000 - 3.820000) / sqrt(2.516667 + 0.00001)
= 1.180000 / 1.586404 = 0.743821
y = 1.5 * 0.743821 + 0.5 = 1.615731
compare: had x = 5.0 arrived inside the training batch above, it would
have been normalized to (5.0 - 4.0) / 1.414217 = 0.707105

Two details in there are easy to miss and both are real. The variance used to normalize the batch is the biased one, dividing by m; the variance stored into the running average is the unbiased one, dividing by m−1, which for a batch of 4 is 4/3 times larger. PyTorch does exactly this and documents it. And the running statistics are a slow-moving average, so at momentum 0.1 they take dozens of batches to forget an old value, which is why a network switched to evaluation mode too early in training can produce noticeably worse predictions than it does in training mode.

Pause and recap before the last piece: so far this block has covered what batch norm computes on a forward pass, and why ε is there. What is left is the one thing that makes γ and β genuine trainable parameters rather than fixed constants - seeing them actually move in response to a gradient, worked by hand once below.

Calling γ and β “learnable” is only meaningful if you can see them learn. They are ordinary parameters with ordinary gradients. The block below runs one step of the machinery that the next page is entirely about, one page early. You need one idea: a “gradient arriving at y” is just the answer to if this output number changed by a little, how much would the loss change?, one such answer per output value.

Worked example - one update to gamma and beta. lr = 0.1
xhat from the batch above = [ -1.41421, 0.00000, 0.00000, +1.41421 ]
gradient arriving at y = [ +0.10, -0.05, +0.20, -0.30 ]
dL/dgamma = sum( dL/dy_i * xhat_i )
= (+0.10)(-1.41421) + (-0.05)(0) + (+0.20)(0) + (-0.30)(+1.41421)
= -0.141421 - 0.424263 = -0.565684
dL/dbeta = sum( dL/dy_i ) = 0.10 - 0.05 + 0.20 - 0.30 = -0.050000
gamma -> 1.500000 - 0.1 * (-0.565684) = 1.556568
beta -> 0.500000 - 0.1 * (-0.050000) = 0.505000

γ went up, meaning the network decided this layer wanted slightly more spread than the forced spread of 1. That is the escape hatch working: normalization is imposed, then the network is handed two knobs and allowed to undo as much of it as it turns out to need. If the optimal γ for a layer were √(σ²_B+ε) and the optimal β were μ_B, the layer would learn to cancel itself out entirely.

One practical warning, since this page covers both techniques. Dropout and batch normalization directly interfere with each other, and the reason is in the numbers above: dropout deliberately adds variance to a layer’s output, and batch norm’s running statistics are estimated during training, when dropout is active, but used at inference, when it is not. The statistics therefore describe a distribution the network never actually sees at test time. The usual practice is to use one or the other in a given block, or to place dropout only after the last normalized layer. If a network is unexpectedly worse in evaluation mode than in training mode, this is the first thing to check.

PictureMasks that change, and points you can drag togetherThe ensemble, resampled, and epsilon caught in the act.Rung 03

On the dropout demo in Figure 02: press ‘Resample mask’ repeatedly at p = 0.5 and watch the four hidden values. Every draw is a different sub-network, and the greyed dashed circles are the units contributing nothing on this pass - not a small contribution, exactly zero, in both directions. Now switch to Inference: all four are live, at their original values with no scaling at all, and the resample button is disabled because there is nothing left to randomise.

On the batch-norm demo below: drag the four points until they are almost on top of each other, around [3.0, 3.0, 3.0, 3.1]. σ²_B collapses to 0.0019 and the divisor to 0.0434, and x̂ still only reaches +1.7275 rather than running away, because ε is holding the floor under the division. Drag them back apart and ε becomes invisible again - on the default batch it changes the divisor by 0.0000035. That is what a numerical-stability constant is: nothing at all, until the one case where it is everything.

2.04.04.06.0

raw pre-activation values x - drag any point

-1.410.000.001.41x̂ (normalized)-1.620.500.502.62y = γx̂+β (scaled & shifted)
μ_B = 4.0000
σ²_B = 2.0000
√(σ²_B+ε) ≈ 1.4142
x̂ = [-1.4142, 0.0000, 0.0000, 1.4142]
y = [-1.6213, 0.5000, 0.5000, 2.6213]

Drag the points on the top line to change the raw values - μ_B, σ²_B, and x̂ recompute live. γ and β then let the network undo the forced mean-0/variance-1 shape if a different one works better for this layer.

EquationThe four formulasFour techniques, four rules, and the one that is not a formula at all.Rung 04
Dropout
training: h'_i = 0 with probability p
h'_i = h_i / (1 - p) otherwise, guaranteeing E[h'_i] = h_i
inference: h'_i = h_i unchanged, no masking, no scaling
backward: dL/dh_i = dL/dh'_i * mask_i / (1 - p)
Early stopping - a rule, not a formula
1. score the validation set once per epoch
2. if it improved, save the weights and reset the counter to 0
3. else increment the counter; stop when counter == patience,
and restore the saved weights
Batch normalization
mu_B = (1/|B|) * sum(x)
var_B = (1/|B|) * sum((x - mu_B)^2)
xhat = (x - mu_B) / sqrt(var_B + eps)
y = gamma * xhat + beta, with gamma and beta learned per channel
running <- (1 - momentum) * running + momentum * batch
PyTorch's momentum default is 0.1
General caseData augmentation, and the failure modes of all of itThe one technique that adds data instead of constraining the model, and every way it goes wrong.Rung 05

Every other technique on this page constrains the model. Data augmentation attacks the problem from the other end: overfitting happens when a model has more capacity than it has data, and if you cannot reduce the capacity, you can manufacture more data. The rule is that the transformation must be label-preserving. A photo of a cat rotated 15 degrees is still a photo of a cat, so the network can be shown it with the same label and learns that “cat” does not depend on exact orientation.

images flips, small rotations, random crops, brightness and
contrast shifts, cutout (blanking a random patch), mixup
(blending two images and their labels in proportion)
text synonym substitution, back-translation (translate out and
back so the phrasing changes but the meaning does not),
random word deletion
audio time stretching, pitch shifting, adding background noise,
masking a band of frequencies or a span of time
tabular adding small amounts of noise, or synthesising
minority-class rows (SMOTE) when classes are unbalanced

The label-preserving requirement is stricter than it first sounds, and it is where augmentation goes wrong. Flipping a photo of a cat horizontally is fine. Flipping a photo of the digit 2 horizontally produces something that is not a 2, and training on it as one actively teaches the network something false. Rotating a 6 by 180 degrees produces a 9. Whether a transformation is safe depends entirely on the task, and there is no general answer, only a question you have to ask about each one.

Two mechanical notes. Augmentation is applied to the training set only, never to validation or test, because those exist to measure performance on real data. And it is normally applied on the fly, freshly randomised each epoch, rather than generated once and saved, so the network sees a different variant of each example every time it comes around, for the same reason dropout resamples its mask every forward pass.

It’s the same idea as dropout’s noise-based regularization, just injected into the inputs instead of the activations.

Step 07 - Why this and not that

Why this and not that

Why scale during training rather than at inference?

Both work and both are called dropout, but scaling at inference means every forward pass in production carries an extra multiply that exists only because of something you did during training. Inverted dropout pushes the cost into training, where it is free, and leaves the deployed model with no dropout code in it at all. That is why every framework does it this way.

Why not just use a smaller network instead of dropping neurons from a big one?

Because dropout is not equivalent to a smaller network; it is equivalent to an average over exponentially many smaller networks that share weights. Four units give sixteen sub-networks; a five-hundred-unit layer gives 2⁵⁰⁰. Training all of them separately is impossible and averaging their predictions is exactly what the scaling approximates.

If batch norm helps so much, why does anyone use anything else?

Because it depends on the batch. Batch size 1 has no variance to normalize by, batches of 2 or 4 give noisy statistics, and any setting where examples in a batch should not influence each other rules it out. Layer normalization computes the same statistics across the features of a single example instead, which is why every transformer uses it.

Is early stopping really regularization? It just stops.

It restricts how far the weights are allowed to travel from their initialisation, and for a linear model with gradient descent it can be shown to be closely related to an L2 penalty with a particular λ. Stopping at epoch 6 rather than 10 is choosing a smaller-weight model, by a different route.

Should I use dropout and batch norm together?

Usually not in the same block, and the interaction paragraph in rung 2 is the reason: dropout deliberately adds variance, batch norm’s running statistics are estimated while that variance is present and then used when it is not, so they describe a distribution the network never sees at test time. The common practice is one or the other per block, or dropout only after the last normalized layer.

Name origins
Dropout
Literal: units drop out of the network for that pass. The 2014 paper’s own framing is that it prevents units from “co-adapting”, which is the ensemble argument stated from the other side.
Inverted dropout
The scaling was originally applied at test time, multiplying by (1−p). Moving it to training and dividing instead inverts where the correction lives. Everything ships inverted now, and the “inverted” in the name is a historical artefact.
Batch
From batch processing: a group of items handled in one go. It is the same batch as the optimizers page’s mini-batch, which is why batch normalization’s statistics change when you change your batch size.
γ and β
Ioffe and Szegedy’s own letters, chosen for scale and shift. They collide with the optimizers page’s momentum β and with Adam’s β₁ and β₂, and the field has no intention of fixing that. The collision paragraph in rung 2 is the only defence.
Momentum (BN)
The same word as the optimizer, a completely different quantity, and the roles are reversed: 0.9 there means long memory, 0.1 here also means long memory. Read “momentum 0.1” as “take 10% of the new value”, never as “barely remembers”.
Patience
Literal, and it is measured in epochs of no improvement, not in epochs total.
Augmentation
From Latin augere, to increase. You are increasing the dataset, not improving the model, which is why it is the only technique on this page that would help a model with no capacity problem at all.
Step 08 - Where people go wrong
1.60-0.602.401.00inputhidden (dropout here)output
mode: training, p = 0.50
h = [0.8, -0.3, 1.2, 0.5]
h' = [1.6000, -0.6000, 2.4000, 1.0000]
Mode

Greyed, dashed neurons were dropped this draw. Survivors get scaled up by 1/(1−p) so the layer's expected total signal stays the same.

2.04.04.06.0

raw pre-activation values x - drag any point

-1.410.000.001.41x̂ (normalized)-1.620.500.502.62y = γx̂+β (scaled & shifted)
μ_B = 4.0000
σ²_B = 2.0000
√(σ²_B+ε) ≈ 1.4142
x̂ = [-1.4142, 0.0000, 0.0000, 1.4142]
y = [-1.6213, 0.5000, 0.5000, 2.6213]

Drag the points on the top line to change the raw values - μ_B, σ²_B, and x̂ recompute live. γ and β then let the network undo the forced mean-0/variance-1 shape if a different one works better for this layer.

Step 09 - Practice
  1. 01
    At p = 0.5, resample until you get a mask where every unit survives. What is the layer handing on, and is that a bug?
    Hint

    The scaling applies to survivors regardless of how many there are.

    Answer

    [1.6000, −0.6000, 2.4000, 1.0000] - every value doubled and nothing dropped. It is not a bug: the mask is drawn independently per unit, so all four surviving has probability 0.5⁴ = 1/16, the same as all four being dropped. The scaling is applied unconditionally, which is exactly what keeps the average over all sixteen masks equal to the original [0.8, −0.3, 1.2, 0.5]. If the scaling were conditional on how many survived, that guarantee would break.

  2. 02
    Find the dropout rate at which the surviving units are handed on exactly quadrupled.
    Hint

    The scale is 1/(1−p).

    Answer

    p = 0.75, giving 1/0.25 = 4. The readout shows 0.8 becoming 3.2000. At the slider’s maximum of 0.80 the scale is 5.0 and 0.8 becomes 4.0000. Note how fast the scale climbs at the top end: from p = 0.5 to p = 0.8 the rate rises by 0.3 and the scale by 3.0. That non-linearity is why the noise table in rung 2 goes 0.16, 0.64, 2.56 rather than anything gentle.

  3. 03
    Drag the batch-norm points until ε visibly changes the answer, then say roughly how close they have to be.
    Hint

    ε is 1e-5. Compare it against σ²_B in the readout.

    Answer

    It becomes visible once σ²_B is within a couple of orders of magnitude of 1e-5, which needs the points within about 0.1 of each other - roughly [3.0, 3.0, 3.0, 3.1], where σ²_B is 0.0019 and x̂ reaches 1.7275 rather than the 1.7321 it would reach without ε. On the default batch, σ²_B is 2.0 and ε is a hundred thousand times smaller, so it changes the fourth decimal place of the divisor and nothing you can see. The drag granularity is 0.1, so [3.0, 3.0, 3.0, 3.1] is the closest the widget lets you get, and it is close enough.

  4. 04
    Set γ and β so the batch-norm layer’s output has mean 4 and the same spread as γ. Explain what you have just proved.
    Hint

    x̂ always has mean 0 and spread 1. What does y = γx̂ + β do to that?

    Answer

    β = 4.0 sets the mean and γ = 3.0 sets the spread, exactly, giving y = [−0.2426, 4.0000, 4.0000, 8.2426], because normalization guarantees x̂’s mean is 0 and its spread is 1 before γ and β touch it. What that proves is that batch norm can be completely undone: if the optimal γ for a layer were √(σ²_B+ε) and the optimal β were μ_B, the layer would learn to reproduce its own input and the normalization would have achieved nothing. Those two knobs are an escape hatch by design, and the fact that trained networks generally do not fully undo the normalization is the evidence that it was worth imposing.

  5. 05
    Using the widget, show that the mean of the dropped layer over many draws really is the original layer.
    Hint

    Track one unit across ten resamples. It is either 0 or h/0.5.

    Answer

    Take unit 1, h = 0.8. Across resamples it reads either 0.0000 or 1.6000, and nothing else. Over ten draws you should see roughly five of each, averaging near 0.8. The exact statement is in rung 2: averaged over all sixteen possible masks the layer is exactly [0.800000, −0.300000, 1.200000, 0.500000]. The important word is averaged. Any single forward pass is off by a lot, and that error is not a defect being tolerated - it is the regularizer doing its job.

Step 10 - Seen in the wild
  • AlexNet, 2012Used dropout at p = 0.5 on its two large fully connected layers, and the paper states plainly that without it the network overfitted substantially. It is one of the handful of changes credited with the result that restarted the field.
  • Every ResNetHas a batch-norm layer between every convolution and its activation, which is why the running-statistics arithmetic in rung 2 runs millions of times in any image model you have used, and why model.eval() matters as much as it does.
Step 11 - Memory anchor

Practise with players missing, measure everything in the same units, and stop revising before you start memorising.

None of the three changes what the network is scored on. All three change what it lives through.

Step 12 - The next break

Two things on this page were handed to you rather than computed. The γ and β update took a list of numbers labelled “the gradient arriving at y” as given, and never said where such a list comes from. So did the dropout backward pass. Every page in this module since the gradients page has been quietly borrowing the same thing: the claim that there exists a procedure which, given one loss at the end of a network, produces one gradient for every parameter in it, all the way back to the first layer. That procedure has not been shown. It is one page long, it is entirely arithmetic, and everything in this module rests on it.

Backpropagation