Dropout
Randomly switch off neurons to prevent overfitting.
Create a free account Try part of it here
Why it matters
A big network can memorize a small dataset instead of learning from it. Dropout is one of the simplest strong defenses. During training, it switches off a random share of the neurons at every step, so the network can't lean on any single one. It takes one line of code. You'll find it in the original transformer and in many fine-tuning recipes. Its rate is often one of the few settings you're invited to change.
What you'll be able to do
- Explain dropout: randomly zeroing activations during training, and scaling the rest
- Show why inverted dropout keeps the expected activation unchanged
- Measure dropout's effect on an overfitting network
- Place dropout correctly and switch it off for evaluation
What it covers
- Practice with players missing
- Why scale the survivors up?
- Worked example: dropout with p = 0.25
- Why it works
- Using it well
Practice with players missing
Picture a soccer coach who benches a few random players at every practice. Nobody knows who will sit out next. So the team can't build every play around one star. Each player learns to cover for the others. On match day the whole squad plays, and the team is stronger for it.
Dropout does the same thing to a neural network. A neuron's output is called its activation. During each training step, dropout picks some activations at random and sets them to zero. Those neurons sit out that step. The next step, a different random set sits out.
How dropout works
Dropout has one setting, , the dropout rate. It's the chance that each activation gets switched off. With , each one has a 50% chance of becoming zero on a given step.
Dropout does two things during training:
- It sets each activation to zero with probability .
- It multiplies the activations that survive by . With , that's ×2.
At evaluation time, when you test the model or use it for real, dropout does nothing at all.
The code below makes a dropout layer with . It feeds in rows of ones. Then it runs the same layer again in evaluation mode.
import torch import torch.nn as nn torch.manual_seed(0) drop = nn.Dropout(p=0.5) x = torch.ones(2, 8) drop.train() print("training:\n", drop(x)) print("mean of 10,000 dropped ones:", drop(torch.ones(10000)).mean().item()) drop.eval() print("evaluation:\n", drop(x))
In training mode, roughly half of the ones become 0. The rest become 2. The average of 10,000 dropped ones comes out close to 1, the value you started with. In evaluation mode, the ones pass through unchanged. (The torch here is the course's in-browser PyTorch subset, as in the last few lessons.)
The calls drop.train() and drop.eval() flip the layer between its two modes. On a whole model you call model.train() and model.eval(), and every dropout layer inside follows.
Press the button to see the next batch's thinned network. The survivors get scaled up so the total signal stays the same on average.
x = [2.0, 4.0, 6.0, 8.0] mask = [1, 0, 1, 0] p = 0.5 print([v * m / (1 - p) for v, m in zip(x, mask)])
Show the answer
[4.0, 0.0, 12.0, 0.0]
The switched-off values become 0.0. The survivors are divided by 1 − 0.5 = 0.5, which doubles them: 2.0 becomes 4.0 and 6.0 becomes 12.0.
Why scale the survivors up?
Think of a bakery that usually has 4 bakers. One day, 1 of them calls in sick. If the other 3 work at their normal pace, the shop sells fewer loaves. To keep the shelves as full as usual, each baker who shows up has to work a bit harder.
Dropout faces the same problem. Switching some neurons off makes the total signal smaller. The next layer would then see weaker inputs in training than it sees at test time. So dropout boosts each survivor to make up the difference. Here is one activation before dropout:
In plain words: a neuron that survives gets divided by its chance of surviving, . If each neuron survives 3 times out of 4, each survivor is divided by 0.75. That makes it a third bigger.
Here's a small example. Take and an activation of 3. Half the time it becomes 0. The other half it becomes 3 ÷ 0.5 = 6. Averaged over many steps, that's (0 + 6) ÷ 2 = 3, the same as the value you started with.
This trick is called inverted dropout. Because of the scaling, the average value of each activation is the same in training and in evaluation. So nothing needs adjusting at test time.
Go deeper Why the average stays the same
The average over many steps is called the expected value. Each activation has two possible outcomes. With probability it becomes 0. With probability it becomes . Weight each outcome by its chance and add them up:
The on top cancels the underneath, leaving .
The first version of dropout skipped the scaling during training. It shrank the weights at test time instead. The result is the same on average. Scaling during training is tidier, because the model you train is the model you use, with no extra step.
Worked example: dropout with p = 0.25
A layer has four activations: 2, 4, 1 and 3. The dropout rate is p = 0.25. On this step, the second neuron is switched off. What comes out? And what does each neuron give on average over many steps?
p = 0.25 scale = 1 / (1 - p) x = [2.0, 4.0, 1.0, 3.0] mask = [1, 0, 1, 1] # the second neuron is switched off this step out = [v * m * scale for v, m in zip(x, mask)] print("scale:", round(scale, 3)) print("this step:", [round(v, 3) for v in out]) average = [(1 - p) * v * scale + p * 0 for v in x] print("average over many steps:", [round(v, 3) for v in average])
The scale is 1 ÷ (1 − 0.25) = 1 ÷ 0.75 ≈ 1.333. The three survivors are multiplied by it: 2 becomes about 2.667, 1 becomes about 1.333, and 3 becomes 4. The switched-off neuron gives 0.
On this one step, the outputs look quite different from the inputs. But each neuron survives 75% of the time. So its average is 0.75 × (its boosted value), plus 0.25 × 0. For the first neuron that's 0.75 × 2.667 ≈ 2. The averages come back to 2, 4, 1 and 3, exactly the inputs.
Show the answer
1.25
1 / (1 − 0.2) = 1 / 0.8 = 1.25. That boost keeps each activation's average unchanged.
Why it works
There are two ways to see it.
- No single neuron can be relied on. Any neuron might be missing on the next step. So the network spreads what it learns across many neurons. It builds backup features that work on new data. Without dropout, neurons can form fragile teams that only work together. Those teams tend to memorize the training set.
- A crowd of smaller networks. Each step switches off a different random set of neurons. So each step trains a slightly different smaller network, and they all share the same weights. At test time, the full network behaves roughly like the average of all of them. You met this idea in random forests. Many models voting together, called an ensemble, usually beat one model alone.
Seeing the effect
The next example uses scikit-learn's digits dataset. Each example is a small 8×8 image of a handwritten digit, so 64 pixel values. The task is to say which digit, 0 to 9, it shows.
The network has two hidden layers of 192 neurons each. That's far too big for only 150 training images. It reaches 100% training accuracy with or without dropout. The real test is the validation set: images the model never trained on. The code trains the network twice, once with (no dropout) and once with .
A few names in the code: nn.Dropout(p) goes right after each ReLU. Adam is an optimizer, the rule that updates the weights; it gets its own lesson. The loss is cross-entropy, from Phase 2.
import torch import torch.nn as nn from sklearn.datasets import load_digits from sklearn.model_selection import train_test_split digits = load_digits() X = torch.tensor(digits.data / 16.0, dtype=torch.float32) y = torch.tensor(digits.target) X_train, X_val, y_train, y_val = train_test_split(X, y, train_size=150, random_state=0, stratify=y) loss_fn = nn.CrossEntropyLoss() for p in [0.0, 0.5]: torch.manual_seed(0) model = nn.Sequential(nn.Linear(64, 192), nn.ReLU(), nn.Dropout(p), nn.Linear(192, 192), nn.ReLU(), nn.Dropout(p), nn.Linear(192, 10)) opt = torch.optim.Adam(model.parameters(), lr=3e-3) for epoch in range(120): model.train() opt.zero_grad() loss_fn(model(X_train), y_train).backward() opt.step() model.eval() with torch.no_grad(): train_acc = (model(X_train).argmax(1) == y_train).float().mean().item() val_acc = (model(X_val).argmax(1) == y_val).float().mean().item() val_loss = loss_fn(model(X_val), y_val).item() print(f"dropout {p}: train accuracy {train_acc:.3f} val accuracy {val_acc:.3f} val loss {val_loss:.3f}")
With dropout, the validation loss is much lower and the accuracy is a little higher.
Why does the loss change so much more than the accuracy? Cross-entropy loss punishes a wrong answer given with high confidence very hard. The model without dropout is overconfident: when it gets a digit wrong, it's very sure of its wrong answer. Dropout keeps its predictions more cautious. Its probabilities are closer to how often it's actually right, which is called being well calibrated.
Accuracy gains from dropout are often modest, as here. The more honest probabilities and steadier training matter too.
Show the answer
Nothing yet: dropout raises training loss by design, so compare validation results
Switching neurons off makes training harder and noisier on purpose. Dropout's job is to help on new data, so judge it by validation loss and accuracy.
Using it well
- Where to put it: after the activation function in fully connected layers. In transformers, it goes on the attention weights. It also goes on each sub-layer's output, before that output is added back to the running total called the residual stream. You'll meet both in later lessons.
- How much: a rate of 0.1 to 0.5. Large models trained on huge datasets often use little or none, because they rarely see the same example twice. Fine-tuning on a small dataset is where dropout earns its keep.
- Always call
model.eval()before you evaluate or generate. If you forget, dropout keeps switching neurons off, and predictions are randomly damaged. This is the most common dropout bug. - Dropout makes training loss noisier and a bit higher. Judge it by validation results.
For marketers: it's like making sure a campaign doesn't depend on one channel or one star ad. Imagine you randomly remove pieces while planning. You end up with a plan that still works when any one piece underperforms.
Show the answer
Fine-tuning a model on a few thousand examples
Dropout fights overfitting, which is a risk when the data is small and the model sees it many times. A model that sees each example once rarely overfits, and a model that can't fit its training data needs more capacity, not dropout.
Practice what you just read
- 3 code examples you can edit and run
- a practice exercise that checks your code
- a 5-question quiz
- 4 review cards for spaced repetition
Try a quiz question
What does nn.Dropout do in model.eval() mode?