Implementing Dropout Regularization
Dropout is a powerful regularization technique for reducing overfitting in neural networks. During training, it randomly disables selected units and their connections, causing the model to work with a different reduced network on each iteration.
The most common implementation is called inverted dropout. It scales the retained activations during training so that evaluation can use the complete network without additional adjustment.
How Dropout Changes a Neural Network
Suppose a neural network is overfitting. Dropout assigns each hidden unit a probability of being retained.
For example, with:\[ \text{keep\_prob}=0.5 \]
each unit has:
- A 50% probability of being retained
- A 50% probability of being removed
After randomly selecting the units, all connections to and from the removed units are effectively disabled for that pass.
The resulting network is smaller than the original network.
Dropout trains a randomly reduced version of the neural network on each pass.
The selection is repeated, so later iterations normally use different subnetworks.
A New Subnetwork on Each Iteration
Dropout does not permanently remove units from the architecture. Instead, it generates a temporary binary mask during training.
For one iteration, the active units might be:\[ \begin{bmatrix} 1 & 0 & 1 & 1 \end{bmatrix} \]
On another iteration, the mask might be:\[ \begin{bmatrix} 0 & 1 & 1 & 0 \end{bmatrix} \]
A value of 1 means that the unit is retained, while 0 means that it is disabled.
Because the mask changes, the complete network learns parameters that remain useful across many different reduced configurations.
The Keep Probability
Let:\[ p=\text{keep\_prob} \]
Then:\[ P(\text{unit retained})=p \]
and:\[ P(\text{unit removed})=1-p \]
For example, if:\[ p=0.8 \]
then each unit has:
- An 80% probability of being retained
- A 20% probability of being removed
A lower keep probability produces stronger regularization.
Creating the Dropout Mask
Suppose dropout is being applied to layer 3. Its activation matrix is:\[ A^{[3]} \]
For vectorized training with \(m\) examples:\[ A^{[3]} \in \mathbb{R}^{n^{[3]}\times m} \]
Create a random matrix with the same shape:
D3 = np.random.rand(*A3.shape)Every element is sampled independently from a uniform distribution between 0 and 1.
Convert this matrix into a Boolean dropout mask:
D3 = D3 < keep_probIf keep_prob is 0.8, each element has an 80% probability of being True and a 20% probability of being False.
The shape remains:
assert D3.shape == A3.shapeApplying the Mask
Apply the mask to the activation matrix using element-wise multiplication:
A3 = A3 * D3The shorter equivalent is:
A3 *= D3NumPy treats:
Trueas 1Falseas 0
Therefore, every activation corresponding to False becomes zero.
Mathematically:\[ A^{[3]} := A^{[3]}\odot D^{[3]} \]
where \(\odot\) denotes element-wise multiplication.
Why the Remaining Activations Must Be Scaled
Suppose layer 3 contains 50 units and:\[ \text{keep\_prob}=0.8 \]
On average:\[ 50(1-0.8)=10 \]
units will be disabled, leaving approximately 40 active units.
Without scaling, the average magnitude of the layer’s output would be reduced to approximately 80% of its original value.
The next layer computes:\[ Z^{[4]}=W^{[4]}A^{[3]}+b^{[4]} \]
If 20% of the elements in \(A^{[3]}\) are set to zero, the expected value of \(Z^{[4]}\) also decreases.
To compensate, divide the retained activations by the keep probability:
A3 = A3 / keep_probCombining the two steps:
A3 = (A3 * D3) / keep_probThis procedure is called inverted dropout.
Why Inverted Dropout Preserves the Expected Activation
Consider one activation \(a\) and a random mask \(d\), where:\[ d= \begin{cases} 1 & \text{with probability }p\\ 0 & \text{with probability }1-p \end{cases} \]
Without scaling, the dropped activation is:\[ \tilde{a}=da \]
Its expected value is:\[ \mathbb{E}[\tilde{a}] = \mathbb{E}[d]a = pa \]
The expected value has been reduced by a factor of \(p\).
With inverted dropout:\[ \tilde{a}=\frac{da}{p} \]
Its expected value becomes:\[ \mathbb{E}[\tilde{a}] = \frac{\mathbb{E}[d]a}{p} = \frac{pa}{p} = a \]
Therefore:\[ \mathbb{E}[\tilde{a}]=a \]
Dividing by
keep_probpreserves the expected value of the activation.
This is the defining scaling step of inverted dropout.
Complete Forward-Propagation Example
A reusable inverted-dropout function can be written as:
import numpy as np
def apply_inverted_dropout(A, keep_prob):
"""
Apply inverted dropout to an activation matrix.
Parameters
----------
A : np.ndarray
Activation matrix with shape (number_of_units, number_of_examples).
keep_prob : float
Probability of retaining each activation.
Returns
-------
A_dropout : np.ndarray
Scaled activation matrix after dropout.
D : np.ndarray
Boolean dropout mask used during this pass.
"""
D = np.random.rand(*A.shape) < keep_prob
A_dropout = (A * D) / keep_prob
return A_dropout, DIt can be applied to layer 3 as follows:
Z3 = np.dot(W3, A2) + b3
A3 = relu(Z3)
A3, D3 = apply_inverted_dropout(A3, keep_prob=0.8)The mask D3 should be stored because the same mask is needed during backpropagation.
Dropout During Backpropagation
If an activation was disabled during forward propagation, it must remain disabled during the corresponding backward pass.
Suppose dA3 is the derivative flowing backward through layer 3. Apply the same mask:
dA3 = dA3 * D3
dA3 = dA3 / keep_probEquivalently:
dA3 = (dA3 * D3) / keep_probMathematically:\[ dA^{[3]} := \frac{dA^{[3]}\odot D^{[3]}}{p} \]
A newly generated mask must not be used during this backward pass. The backward computation must correspond to the same reduced network used during forward propagation.
Reuse the forward-pass mask during the corresponding backward pass.
Different Masks Across Iterations
Even when processing the same data again, dropout should normally generate a new random mask.
For example:
for epoch in range(num_epochs):
Z3 = np.dot(W3, A2) + b3
A3 = relu(Z3)
A3, D3 = apply_inverted_dropout(A3, keep_prob)Each iteration produces a new D3. The model therefore trains across many different reduced subnetworks instead of repeatedly using the same one.
For vectorized data, different activations can be removed for different examples because the mask has the same shape as the activation matrix.
Do Not Use Dropout During Evaluation
During evaluation or prediction, dropout is disabled. The complete neural network is used:\[ Z^{[1]}=W^{[1]}A^{[0]}+b^{[1]} \]\[ A^{[1]}=g^{[1]}(Z^{[1]}) \]\[ Z^{[2]}=W^{[2]}A^{[1]}+b^{[2]} \]\[ A^{[2]}=g^{[2]}(Z^{[2]}) \]
The process continues until the final prediction:\[ \hat{Y}=A^{[L]} \]
No random mask is generated, and no units are removed.
A simple structure is:
def forward_propagation(X, parameters, training, keep_prob):
A = X
for layer in hidden_layers:
Z = np.dot(layer.W, A) + layer.b
A = relu(Z)
if training and keep_prob < 1.0:
A, D = apply_inverted_dropout(A, keep_prob)
return ADuring training:
predictions = forward_propagation(
X_train,
parameters,
training=True,
keep_prob=0.8,
)During evaluation:
predictions = forward_propagation(
X_dev,
parameters,
training=False,
keep_prob=1.0,
)Why Random Evaluation Is Undesirable
If dropout were used during evaluation, identical inputs could produce different predictions on different runs.
This would:
- Add noise to predictions
- Reduce reproducibility
- Make evaluation unstable
- Require repeated computation to estimate an average
In theory, predictions could be generated many times with different masks and then averaged. However, that would be computationally inefficient.
Using the complete network provides a practical approximation to averaging across the many subnetworks encountered during training.
Why No Extra Evaluation-Time Scaling Is Needed
Older versions of dropout sometimes retained activations without dividing by the keep probability during training. Those approaches required additional scaling during evaluation.
Inverted dropout moves the scaling into the training phase:\[ A_{\text{dropout}} = \frac{A\odot D}{p} \]
Because the expected activation is already preserved, evaluation can use the complete network directly.
Inverted dropout simplifies evaluation because no additional activation scaling is required.
A Layer-Level Implementation
The following example applies dropout to selected hidden layers:
def forward_layer(
A_prev,
W,
b,
activation,
training=False,
keep_prob=1.0,
):
Z = np.dot(W, A_prev) + b
A = activation(Z)
D = None
if training and keep_prob < 1.0:
D = np.random.rand(*A.shape) < keep_prob
A = (A * D) / keep_prob
cache = {
"A_prev": A_prev,
"W": W,
"b": b,
"Z": Z,
"D": D,
"keep_prob": keep_prob,
}
return A, cacheThe cached mask can then be used during backpropagation:
def apply_dropout_backward(dA, cache):
D = cache["D"]
keep_prob = cache["keep_prob"]
if D is not None:
dA = (dA * D) / keep_prob
return dACommon Implementation Mistakes
Forgetting to scale the activations
Incorrect:
A3 = A3 * D3Correct inverted dropout:
A3 = (A3 * D3) / keep_probGenerating a new mask during backpropagation
Incorrect:
D3_backward = np.random.rand(*dA3.shape) < keep_prob
dA3 = dA3 * D3_backwardCorrect:
dA3 = (dA3 * D3) / keep_probThe backward pass must use the mask from the forward pass.
Using dropout during evaluation
Incorrect:
A3, D3 = apply_inverted_dropout(A3, keep_prob=0.8)during prediction.
Correct:
keep_prob = 1.0or bypass the dropout operation entirely.
Using inconsistent keep probabilities
The same layer must use the same keep probability during its corresponding forward and backward calculations.
Setting keep_prob outside its valid range
The valid range is:\[ 0<p\leq1 \]
A defensive check can be added:
assert 0 < keep_prob <= 1Training and Evaluation Summary
| Stage | Random mask | Activation scaling | Full network |
|---|---|---|---|
| Training | Yes | Divide by keep_prob | No |
| Evaluation | No | No additional scaling | Yes |
Key Takeaway
Inverted dropout uses three operations during training:
D = np.random.rand(*A.shape) < keep_prob
A = A * D
A = A / keep_probMathematically:\[ D^{[l]}\sim\operatorname{Bernoulli}(p) \]\[ A^{[l]} := \frac{A^{[l]}\odot D^{[l]}}{p} \]
The same mask is reused during backpropagation:\[ dA^{[l]} := \frac{dA^{[l]}\odot D^{[l]}}{p} \]
During evaluation, dropout is disabled and the complete network produces the prediction.
Inverted dropout preserves expected activation values during training, allowing evaluation to use the full network without additional scaling.
