Python · Course navigation

Regularization: weight decay and dropout

Shrink weights with an L2 penalty, and see why inverted dropout keeps the expected activation unchanged.

On this page
Before you start

Gradient descent and averages.

  • Shrink weights with an L2 penalty, and see why inverted dropout keeps the expected activation unchanged.
  • Predict the sample output and explain the state changes.
  • Complete the practice task and check the boundary cases.

The concept

Large networks have enough capacity to memorize their training data, which leads to overfitting. Regularization is any technique that reduces this by constraining the model. Besides early stopping and data augmentation, two classic methods are the L2 penalty, also called weight decay, and dropout.

L2 regularization adds λ·Σw² to the loss. It pulls the weights toward zero, preferring smaller weights and smoother functions. For one weight with squared error the penalized solution has the closed form w = Σxy / (Σx² + nλ), where n is the number of examples. In the example the data lie exactly on y = 2x. With λ = 0 gradient descent recovers w = 2. With λ = 1 the weight is shrunk to 1.765 and gradient descent agrees with the closed form. The penalty trades a bit of training accuracy for lower variance.

Dropout (Srivastava et al., 2014) randomly sets each unit's output to zero during training, with probability 1 − keep. In the inverted form used by modern libraries, the surviving activations are divided by keep during training so that their expected value stays the same, and no change is needed at test time, when all units are used. The program checks this by averaging over all 16 possible masks of four units, each equally likely when keep = 0.5. The average equals the original activations exactly. Dropout discourages units from relying on specific partners.

Code example

Python
from itertools import product

activations = [0.2, 0.8, 0.5, 1.0]
keep = 0.5
masks = list(product([0, 1], repeat=len(activations)))
mean = [sum(a * mask[i] / keep for mask in masks) / len(masks) for i, a in enumerate(activations)]
print([round(value, 10) for value in mean] == activations, len(masks))

xs = [1, 2, 3, 4]
ys = [2, 4, 6, 8]


def fit(penalty):
    w = 0.0
    n = len(xs)
    for _ in range(5000):
        grad = sum(2 * (w * x - y) * x for x, y in zip(xs, ys)) / n + 2 * penalty * w
        w -= 0.02 * grad
    return w


print(round(fit(0.0), 3), round(fit(1.0), 3))
closed = sum(x * y for x, y in zip(xs, ys)) / (sum(x * x for x in xs) + len(xs) * 1.0)
print(round(closed, 3))
Output
True 16
2.0 1.765
1.765

Follow the code

Open the walkthrough to inspect the example one step at a time.

Show guided walkthrough
Guided walkthroughReady
1from itertools import product
2
3activations = [0.2, 0.8, 0.5, 1.0]
4keep = 0.5
5masks = list(product([0, 1], repeat=len(activations)))
6mean = [sum(a * mask[i] / keep for mask in masks) / len(masks) for i, a in enumerate(activations)]
7print([round(value, 10) for value in mean] == activations, len(masks))
8
9xs = [1, 2, 3, 4]
10ys = [2, 4, 6, 8]
11
12
13def fit(penalty):
14 w = 0.0
15 n = len(xs)
16 for _ in range(5000):
17 grad = sum(2 * (w * x - y) * x for x, y in zip(xs, ys)) / n + 2 * penalty * w
18 w -= 0.02 * grad
19 return w
20
21
22print(round(fit(0.0), 3), round(fit(1.0), 3))
23closed = sum(x * y for x, y in zip(xs, ys)) / (sum(x * x for x in xs) + len(xs) * 1.0)
24print(round(closed, 3))

Follow the example

Start to inspect the source, values, and output. These prepared steps explain the published sample; grouped loop steps describe multiple iterations.

View the complete dry-run table
Prepared trace of the published program. Some rows group repeated iterations.
Step / lineWhat happensState after stepOutput so far
All 2⁴ = 16 ways to keep or drop four units.—
—
Divide each kept activation by keep, then average over every mask: the expected value of the scaled activation.—
—
The average equals the original activations: inverted dropout preserves the expected value.—
True 16
The L2 penalty adds 2·λ·w to the gradient, pulling w toward zero at every step.—
True 16
Without the penalty w converges to 2; with λ = 1 it settles at 1.765.—
True 16
2.0 1.765
The closed-form ridge solution agrees with gradient descent.—
True 16
2.0 1.765
1.765

Behind the scenes

How Python processes this code

Python runs this source through its interpreter. It creates objects for values and resolves names as each statement runs.

Cost and common mistakes

The L2 term adds O(1) work per weight per step. Dropout adds O(units) to create and apply the mask.

  • Too much regularization causes underfitting: the weights are squeezed until the model can no longer fit the signal.
  • Dropout is applied only during training. At test time use every unit, with no random masking.
  • The penalty strength λ is a hyperparameter. Choose it with validation data, not the training set.

Try it yourself

What does the closed form give for λ = 0?

Reveal answer and reasoning

w = 60 / 30 = 2, the unpenalized least-squares slope.

Further reading