Intermediate
18 min read
#Regularization#Dropout#L1 L2#Weight Decay#Early Stopping#Overfitting

08. Regularization Techniques (Dropout, L1/L2, Early Stopping)

Techniques for model generalization: L1 Lasso, L2 Ridge weight decay, inverted Dropout, early stopping, and data augmentation strategies.

Regularization: Complete Notes (Beginner to Advanced)


Introduction#

Regularization refers to a broad set of techniques used to improve how well a model generalizes to new, unseen data, rather than just memorizing the training data it was shown. Regularization becomes necessary because of a fundamental tension in machine learning between fitting the training data well and performing well on data the model hasn't seen before. This tension is best understood through the two failure modes it addresses: overfitting and underfitting.


1. Overfitting#

Overfitting occurs when a model learns the training data too well, including its noise and random fluctuations, rather than learning the true, underlying pattern that generalizes to new data.

Signs of overfitting:

  • Training loss keeps decreasing and training accuracy keeps increasing.
  • Validation/test loss starts increasing (or accuracy starts decreasing) even while training loss continues to improve.
  • A large, growing gap between training performance and validation performance.

Why it happens: if a model has too much capacity (too many parameters relative to the amount and complexity of the training data), it can find weight configurations that fit even the noise and random quirks specific to the training set, rather than the general pattern that would also apply to new data.

Without addressing overfitting: the model appears to perform very well during training but performs poorly in the real world on new, unseen data, which defeats the entire purpose of building a predictive model in the first place.

With regularization techniques (covered throughout the rest of this note) to address overfitting: the model is encouraged or forced to learn simpler, more general patterns that hold up on new data, even if this means it fits the training data slightly less perfectly.

Visual intuition (loss curves):

Architecture & Data Flow
Loss
  |
  |  Training Loss:    \___________________
  |                                          (keeps decreasing)
  |  Validation Loss:   \______/‾‾‾‾‾‾‾‾‾‾‾‾
  |                            ^
  |                    (starts increasing here = overfitting begins)
  +----------------------------------------------> Epochs

2. Underfitting#

Underfitting is the opposite problem: the model is too simple to capture even the underlying pattern in the training data, resulting in poor performance on both the training data and new, unseen data.

Signs of underfitting:

  • Training loss remains high and doesn't improve much, even after substantial training.
  • Validation loss is also high, and closely tracks the (already poor) training loss.
  • No meaningful gap between training and validation performance, because the model is failing at both equally.

Why it happens: the model may have too few parameters (too small/shallow an architecture) to represent the true complexity of the pattern in the data, or it may not have been trained for long enough, or the learning rate may be poorly tuned, preventing it from finding even a reasonably good fit.

Without addressing underfitting: the model fails to be useful at all, since it can't even accurately capture the patterns present in the data it was trained on, let alone generalize to new data.

With addressing underfitting (increasing model capacity, training longer, tuning the learning rate, or reducing excessive regularization): the model becomes capable of learning the actual underlying pattern well enough to perform reasonably on both training and new data.

Overfitting vs. Underfitting Comparison:

AspectUnderfittingOverfitting
Training performancePoorVery good (sometimes near-perfect)
Validation performancePoorPoor to moderate
Gap between the twoSmall (both are poor)Large (training much better than validation)
Root causeModel too simple, insufficient trainingModel too complex, memorizing noise
Fix directionIncrease capacity/training, reduce regularizationIncrease regularization, reduce capacity, get more data

This overfitting-underfitting trade-off, often called the bias-variance trade-off, is the central motivation for every regularization technique described in the rest of this note: each one is a tool for pulling an overfitting model back toward better generalization, without pushing it so far that it underfits instead.


3. L1 Regularization#

L1 regularization (also called Lasso regularization) adds a penalty term to the loss function equal to the sum of the absolute values of all the model's weights.

Architecture & Data Flow
L1_penalty = lambda * sum(|w_i| for all weights w_i)

Total_Loss = Original_Loss + L1_penalty

Where lambda (also written as alpha in some contexts) is a hyperparameter controlling how strongly the penalty is applied.

Effect on the weights: L1 regularization tends to push many weights all the way to exactly zero, effectively performing automatic feature selection by "turning off" the influence of certain inputs entirely. This happens because the gradient of the L1 penalty with respect to a weight is a constant (+lambda or -lambda, depending on the weight's sign), regardless of how close that weight already is to zero, which keeps pushing small weights firmly toward exactly zero rather than just shrinking them slightly.

Without L1 regularization (or any regularization): a model is free to assign large, unconstrained weight values to every single input feature, including features that are irrelevant or only weakly related to the actual target, which contributes to overfitting.

With L1 regularization: the model is encouraged to rely on only the most important, informative features, since it will drive the weights of less useful features to exactly zero, resulting in a sparse model (a model where many weights are literally zero).

Code example (from scratch):

🐍 Python
import numpy as np def l1_penalty(weights, lambda_reg=0.01): return lambda_reg * np.sum(np.abs(weights)) weights = np.array([0.5, -0.2, 0.0, 1.3, -0.05]) print("L1 penalty:", l1_penalty(weights))
🐍 Python
# Adding L1 regularization manually in PyTorch (PyTorch has no built-in L1 argument # on optimizers, unlike L2/weight decay, so it's typically added to the loss manually) import torch import torch.nn as nn model = nn.Linear(10, 1) loss_fn = nn.MSELoss() optimizer = torch.optim.SGD(model.parameters(), lr=0.01) X = torch.randn(5, 10) y_true = torch.randn(5, 1) y_pred = model(X) loss = loss_fn(y_pred, y_true) l1_lambda = 0.01 l1_norm = sum(p.abs().sum() for p in model.parameters()) total_loss = loss + l1_lambda * l1_norm optimizer.zero_grad() total_loss.backward() optimizer.step()

4. L2 Regularization#

L2 regularization (also called Ridge regularization) adds a penalty term to the loss function equal to the sum of the squared values of all the model's weights.

code
L2_penalty = lambda * sum(w_i^2 for all weights w_i) Total_Loss = Original_Loss + L2_penalty

Effect on the weights: unlike L1, L2 regularization tends to shrink weights toward small values that are close to zero, but rarely exactly zero. This happens because the gradient of the L2 penalty with respect to a weight is proportional to the weight's own current value (2 * lambda * w), meaning the penalty's pull becomes progressively weaker as a weight gets closer to zero, so it rarely reaches exactly zero (unlike L1's constant pull).

Without L2 regularization: a model can develop a small number of very large weights that make it overly sensitive to specific input features or specific training examples, contributing to overfitting and poor generalization.

With L2 regularization: the model is encouraged to distribute its "reliance" more evenly across many weights, keeping every individual weight relatively small, rather than letting any single weight become extremely large and dominate the model's predictions.

L1 vs. L2 Comparison:

AspectL1 RegularizationL2 Regularization
Penalty formulaSum of absolute values (|w|)Sum of squared values (w^2)
Effect on weightsPushes many weights to exactly zeroShrinks weights toward small values, rarely exactly zero
Resulting modelSparse (many zero weights)Dense (all weights small but nonzero)
Common use caseFeature selection, interpretabilityGeneral-purpose weight shrinkage, most common in deep learning

Code example (from scratch):

🐍 Python
import numpy as np def l2_penalty(weights, lambda_reg=0.01): return lambda_reg * np.sum(weights ** 2) weights = np.array([0.5, -0.2, 0.0, 1.3, -0.05]) print("L2 penalty:", l2_penalty(weights))
🐍 Python
import torch import torch.nn as nn model = nn.Linear(10, 1) loss_fn = nn.MSELoss() optimizer = torch.optim.SGD(model.parameters(), lr=0.01) X = torch.randn(5, 10) y_true = torch.randn(5, 1) y_pred = model(X) loss = loss_fn(y_pred, y_true) l2_lambda = 0.01 l2_norm = sum(p.pow(2.0).sum() for p in model.parameters()) total_loss = loss + l2_lambda * l2_norm optimizer.zero_grad() total_loss.backward() optimizer.step()

5. Weight Decay#

Weight decay is closely related to L2 regularization, and in the context of plain SGD, the two are mathematically equivalent. However, the term "weight decay" specifically refers to a particular implementation approach: rather than adding a penalty term to the loss function and letting it flow through the normal gradient computation, weight decay is applied as a direct modification to the weight update rule itself.

Mathematical Formulation
Standard SGD update with weight decay:
theta_new = theta_old - learning_rate * (gradient + weight_decay * theta_old)

Which simplifies to:
theta_new = theta_old * (1 - learning_rate * weight_decay) - learning_rate * gradient

This shows why it's called "decay": at every step, the weight is multiplied by a factor slightly less than 1 (1 - learning_rate * weight_decay), causing it to shrink ("decay") toward zero a little bit at every single update, independent of whatever the gradient says.

Why weight decay and L2 regularization are equivalent for plain SGD: if you take the L2 penalty (lambda * sum(w^2)) and compute its gradient with respect to a weight w, you get 2 * lambda * w. Adding this directly to the gradient before applying the standard SGD update produces exactly the same update rule as the weight decay formula shown above (with weight_decay = 2 * lambda). This is precisely why, for a long time, "L2 regularization" and "weight decay" were used interchangeably.

Where the equivalence breaks down (important, and directly connects to the AdamW notes covered previously): for adaptive optimizers like Adam, adding the L2 penalty to the gradient (the traditional approach) causes the weight decay effect to get entangled with Adam's adaptive per-parameter learning rate scaling (division by sqrt(v_hat)), distorting its intended, uniform shrinking effect. This is exactly the problem AdamW was designed to fix, by implementing weight decay as a separate, decoupled step in the update rule rather than folding it into the gradient, restoring the clean, direct "decay toward zero" behavior that the term implies.

Without decoupling weight decay from the gradient (in an adaptive optimizer like Adam): parameters with a large accumulated squared-gradient history (v_hat) have their effective weight decay strength weakened by that same adaptive scaling division, making the actual regularization applied to each parameter inconsistent and unpredictable.

With properly decoupled weight decay (as in AdamW): every parameter is shrunk toward zero by a consistent, predictable amount based purely on the weight_decay hyperparameter and the learning rate, independent of that parameter's individual gradient history.

Code example (weight decay in PyTorch optimizers, available as a direct hyperparameter):

🐍 Python
import torch import torch.nn as nn model = nn.Linear(10, 1) # For plain SGD, weight_decay behaves identically to L2 regularization optimizer_sgd = torch.optim.SGD(model.parameters(), lr=0.01, weight_decay=0.0001) # For AdamW, weight_decay is properly decoupled from the adaptive gradient scaling optimizer_adamw = torch.optim.AdamW(model.parameters(), lr=0.001, weight_decay=0.01)

6. Dropout#

Dropout is a regularization technique specific to neural networks that works by randomly "dropping" (setting to zero) a fraction of neurons' outputs during each training step, forcing the network to not rely too heavily on any single neuron.

How it works during training:

  1. For each training step (each forward pass), every neuron in a dropout-enabled layer is independently kept active with probability p (or dropped with probability 1 - p), based on a random coin flip.
  2. The outputs of the surviving (kept) neurons are then scaled up by a factor of 1/p, so that the expected total signal passed to the next layer remains roughly the same whether or not dropout is applied. This specific approach (scaling during training rather than at test time) is called inverted dropout, and it is the standard implementation used in virtually all modern deep learning frameworks.
  3. During inference (evaluation/prediction on new data), dropout is turned off entirely: all neurons are used, and no scaling is needed (since the scaling was already handled during training).
Mathematical Formulation
During training:
mask = random binary values (1 with probability p, 0 with probability 1-p), same shape as the layer's output
output = (original_output * mask) / p

During inference:
output = original_output   (no dropout, no scaling needed)

Why this reduces overfitting: since any given neuron might be randomly dropped out at any training step, no single neuron can become overly specialized or overly relied upon by the other neurons around it (a phenomenon called "co-adaptation," where neurons learn to depend on very specific combinations of other neurons rather than learning independently useful features). Dropout forces the network to learn features that are useful in many different random sub-networks (since a different random subset of neurons is active on every training step), which acts similarly to training an ensemble of many different smaller networks and averaging their behavior.

Without dropout: neurons in a network can become tightly co-dependent on very specific combinations of other neurons being active, which can make the network's learned features overly specific to the training data's particular patterns and less robust to slight variations in new data.

With dropout: the network is forced to develop more robust, redundant, and independently useful features, since it can never fully rely on any specific neuron or combination of neurons being present, which generally improves generalization to new data.

Code example (dropout from scratch, inverted dropout style):

🐍 Python
import numpy as np def dropout_forward(x, keep_prob, training=True): if not training: return x # no dropout applied during inference mask = (np.random.rand(*x.shape) < keep_prob).astype(float) return (x * mask) / keep_prob # inverted dropout scaling x = np.array([1.0, 2.0, 3.0, 4.0, 5.0]) np.random.seed(0) output_train = dropout_forward(x, keep_prob=0.8, training=True) output_test = dropout_forward(x, keep_prob=0.8, training=False) print("Training output (dropout applied):", output_train) print("Inference output (no dropout):", output_test)
🐍 Python
# Using PyTorch's built-in Dropout layer import torch import torch.nn as nn model = nn.Sequential( nn.Linear(10, 20), nn.ReLU(), nn.Dropout(p=0.5), # 50% of neurons dropped during training nn.Linear(20, 1) ) model.train() # dropout is ACTIVE in this mode X = torch.randn(4, 10) output_train = model(X) model.eval() # dropout is DISABLED in this mode (standard neurons used, no scaling needed) output_eval = model(X)

Important practical note: it's essential to correctly switch between model.train() and model.eval() modes in PyTorch (or the equivalent training=True/False argument in TensorFlow/Keras), since forgetting to switch to evaluation mode before testing/inference would leave dropout active, causing inconsistent, randomly-varying predictions on the exact same input.


7. DropConnect#

DropConnect is a generalization of Dropout. Instead of randomly dropping entire neuron outputs (activations), DropConnect randomly drops individual weights (connections) between neurons.

Mathematical Formulation
During training:
mask = random binary values (1 with probability p, 0 with probability 1-p), same shape as the WEIGHT MATRIX
W_dropped = W * mask

output = X . W_dropped + b

Key distinction from Dropout:

AspectDropoutDropConnect
What gets droppedEntire neuron outputs (activations)Individual weights (connections)
GranularityCoarser (affects all outgoing connections of a dropped neuron at once)Finer (each individual connection independently)
Number of possible random masks per layer2^(number of neurons)2^(number of weights), which is far larger since there are many more weights than neurons

Without DropConnect's finer granularity (relying only on standard Dropout): the regularization effect is applied at the level of whole neurons; when a neuron is dropped, all of its outgoing connections are dropped together as a group, rather than each connection being treated as independently as possible.

With DropConnect: the regularization is applied more granularly at the level of individual connections, which theoretically creates an even larger and more diverse space of possible "thinned" sub-networks during training, though in practice DropConnect is used far less frequently than standard Dropout, partly because it is more computationally expensive (requiring a full random mask the size of the entire weight matrix, rather than just the size of the neuron output vector) and doesn't reliably outperform standard Dropout enough to justify that extra cost for most practical use cases.

Code example (DropConnect from scratch):

🐍 Python
import numpy as np def dropconnect_forward(x, W, b, keep_prob, training=True): if not training: return np.dot(x, W) + b mask = (np.random.rand(*W.shape) < keep_prob).astype(float) W_dropped = (W * mask) / keep_prob # inverted scaling, same principle as dropout return np.dot(x, W_dropped) + b np.random.seed(0) X = np.array([[1.0, 2.0, 3.0]]) W = np.random.randn(3, 4) * 0.1 b = np.zeros(4) output_train = dropconnect_forward(X, W, b, keep_prob=0.8, training=True) output_test = dropconnect_forward(X, W, b, keep_prob=0.8, training=False) print("Training output (DropConnect applied):", output_train) print("Inference output (no DropConnect):", output_test)

8. Early Stopping#

Early stopping is a simple but effective regularization technique that monitors the model's performance on a validation set during training, and stops training as soon as validation performance stops improving, even if training performance is still getting better.

How it works:

  1. After each epoch, evaluate the model on a held-out validation set (data not used for weight updates).
  2. Keep track of the best validation performance seen so far, and save a copy of the model's weights at that point.
  3. If validation performance fails to improve for a specified number of consecutive epochs (called "patience"), stop training and restore the saved weights from the best-performing epoch.

Why this works as regularization: as covered in the Overfitting section, training loss typically keeps decreasing throughout training, but validation loss starts increasing again once the model begins overfitting to the training data. Early stopping directly targets this exact moment, stopping training right around the point where the model has learned the general pattern well but hasn't yet started memorizing training-set-specific noise.

Without early stopping: you would need to guess in advance exactly how many epochs to train for. Training for too many epochs risks overfitting (as validation performance degrades past its peak), while training for too few epochs risks underfitting (stopping before the model has learned the pattern well). You also cannot use the natural, informative signal of "when did validation performance actually stop improving" to inform this all-important stopping decision.

With early stopping: you no longer need to guess the ideal number of epochs in advance, since the technique automatically detects the point of best generalization based on real, ongoing validation feedback during training itself, and preserves the model from exactly that point.

Code example (early stopping from scratch):

🐍 Python
import numpy as np def train_with_early_stopping(model_train_step, model_validate_step, max_epochs=100, patience=5): best_val_loss = float('inf') epochs_without_improvement = 0 best_weights = None for epoch in range(max_epochs): model_train_step() # one epoch of training (forward, backward, update) val_loss = model_validate_step() # evaluate on validation set if val_loss < best_val_loss: best_val_loss = val_loss epochs_without_improvement = 0 best_weights = "snapshot_of_current_weights" # in practice: copy the actual weights else: epochs_without_improvement += 1 if epochs_without_improvement >= patience: print(f"Early stopping triggered at epoch {epoch}") break return best_weights
🐍 Python
# Using PyTorch: a common manual pattern for early stopping (no single built-in class) import torch import copy best_val_loss = float('inf') patience = 5 epochs_without_improvement = 0 best_model_state = None for epoch in range(100): # ... training loop for this epoch ... val_loss = 0.3 # placeholder: actual validation loss computed here if val_loss < best_val_loss: best_val_loss = val_loss epochs_without_improvement = 0 best_model_state = copy.deepcopy(model.state_dict()) else: epochs_without_improvement += 1 if epochs_without_improvement >= patience: print(f"Stopping early at epoch {epoch}") break model.load_state_dict(best_model_state) # restore the best-performing weights

9. Label Smoothing#

Label smoothing is a regularization technique applied to the target labels used in classification tasks, rather than to the weights or architecture. Instead of using "hard" one-hot labels (100% probability on the correct class, 0% on all others), label smoothing replaces these hard targets with slightly "softened" ones.

Formula:

code
For K total classes and a smoothing parameter epsilon (commonly a small value like 0.1): smoothed_label_for_correct_class = 1 - epsilon + (epsilon / K) smoothed_label_for_each_other_class = epsilon / K

Example: for a 4-class problem where the true class is class index 2, with epsilon = 0.1:

Mathematical Formulation
Hard label (no smoothing):     [0,     0,     1,     0    ]
Smoothed label (epsilon=0.1):  [0.025, 0.025, 0.925, 0.025]

Why this helps: when trained with hard, one-hot labels and cross-entropy loss, a model is mathematically encouraged to push the predicted probability for the correct class as close to 1.0 (and all others as close to 0.0) as possible. This can push the model's logits toward extremely large or extremely negative values, since there's no upper bound stopping the model from becoming more and more confident. This excessive confidence can hurt generalization, since the model becomes overly certain even on data it has not truly learned the pattern for well, and provides poor-quality probability estimates (a model that always outputs 99.99% confidence isn't giving you useful information about its actual uncertainty).

Without label smoothing: models trained on hard labels tend to become overconfident, producing extreme probability outputs that don't necessarily reflect genuine uncertainty, and this overconfidence can also somewhat encourage overfitting, since the model is being pushed toward an unachievable, extreme target.

With label smoothing: the target itself no longer allows (or rewards) extreme, maximal confidence, since even the correct class's target probability is capped below 1.0. This acts as a regularizer, discouraging the model from becoming overconfident, generally leading to better-calibrated probability outputs and, in many cases, improved generalization performance, especially in large classification models such as image classifiers and certain transformer-based models.

Code example (label smoothing from scratch):

🐍 Python
import numpy as np def smooth_labels(true_class_index, num_classes, epsilon=0.1): smoothed = np.full(num_classes, epsilon / num_classes) smoothed[true_class_index] = 1 - epsilon + (epsilon / num_classes) return smoothed label = smooth_labels(true_class_index=2, num_classes=4, epsilon=0.1) print("Smoothed label:", label) print("Sum (should be 1.0):", np.sum(label))
🐍 Python
# Using PyTorch's built-in label smoothing (available directly in CrossEntropyLoss) import torch import torch.nn as nn loss_fn = nn.CrossEntropyLoss(label_smoothing=0.1) logits = torch.tensor([[2.0, 0.5, 3.0, 0.2]]) true_class = torch.tensor([2]) # integer class index; smoothing is applied internally loss = loss_fn(logits, true_class) print("Loss with label smoothing:", loss.item())

10. Data Augmentation#

Data augmentation is a regularization technique that works by artificially expanding the training dataset, creating modified (but still label-preserving) versions of existing training examples, rather than modifying the model, loss function, or training procedure directly.

Common data augmentation techniques by data type:

For images:

  • Random rotation, flipping (horizontal/vertical), and cropping
  • Random changes to brightness, contrast, or color saturation
  • Random zoom or slight scaling
  • Adding random noise

For text (NLP):

  • Synonym replacement
  • Random word insertion, deletion, or swapping
  • Back-translation (translating to another language and back)

For audio:

  • Time-stretching or pitch-shifting
  • Adding background noise
  • Time-shifting the audio clip

Why this helps with generalization: by exposing the model to many slightly varied versions of the same underlying example (e.g., the same photo of a cat, but rotated, slightly zoomed, or with adjusted brightness), the model is discouraged from memorizing exact pixel values or very specific, superficial characteristics of individual training images. Instead, it's encouraged to learn the more robust, invariant features that define the actual class (e.g., "cats have certain shapes and textures," regardless of the exact rotation or lighting in a specific photo).

Without data augmentation: the model only ever sees the exact, fixed set of training examples provided. If the dataset is limited in size or variety, the model has a higher chance of overfitting to the exact specific examples and their incidental characteristics (specific lighting, specific angles, specific backgrounds) rather than the true underlying pattern that defines each class.

With data augmentation: the effective size and diversity of the training dataset is artificially increased, without needing to collect and label any genuinely new data, exposing the model to a much wider range of variation and helping it learn more robust, generalizable features.

Code example (image augmentation using torchvision):

🐍 Python
import torchvision.transforms as transforms augmentation_pipeline = transforms.Compose([ transforms.RandomHorizontalFlip(p=0.5), transforms.RandomRotation(degrees=15), transforms.ColorJitter(brightness=0.2, contrast=0.2), transforms.RandomResizedCrop(size=224, scale=(0.8, 1.0)), transforms.ToTensor() ]) # This pipeline would typically be applied when loading each image during training: # augmented_image = augmentation_pipeline(original_image)
🐍 Python
# A minimal from-scratch example: random horizontal flip for an image represented as a NumPy array import numpy as np def random_horizontal_flip(image, p=0.5): if np.random.rand() < p: return np.fliplr(image) # flip along the horizontal axis return image image = np.random.rand(64, 64, 3) # a dummy image, height x width x channels flipped_image = random_horizontal_flip(image, p=0.5)

Important note: data augmentation is applied only to the training set, never to the validation or test sets, since the goal of validation/test evaluation is to measure performance on realistic, unmodified data the model would actually encounter, not on artificially altered versions of it.


Summary Table: Regularization Techniques#

TechniqueWhat It RegularizesCore Mechanism
L1 RegularizationWeight magnitudesPenalizes sum of absolute weight values; encourages sparsity
L2 RegularizationWeight magnitudesPenalizes sum of squared weight values; shrinks weights toward small values
Weight DecayWeight magnitudesDirectly shrinks weights each update step; equivalent to L2 for SGD, decoupled in AdamW
DropoutNeuron co-adaptationRandomly zeroes neuron outputs during training
DropConnectConnection co-adaptationRandomly zeroes individual weights during training
Early StoppingTraining durationStops training once validation performance stops improving
Label SmoothingTarget label confidenceSoftens hard one-hot labels to discourage overconfidence
Data AugmentationTraining data diversityArtificially creates varied versions of training examples

Quick Recap (Beginner to Advanced Flow)#

  1. Overfitting (model too complex, memorizes training data) and underfitting (model too simple, fails to learn the pattern) are the two failure modes that all regularization techniques aim to balance.
  2. L1 regularization penalizes the sum of absolute weight values, encouraging sparsity (many weights become exactly zero).
  3. L2 regularization penalizes the sum of squared weight values, shrinking weights toward small values without necessarily reaching zero.
  4. Weight decay is mathematically equivalent to L2 regularization for plain SGD, but is implemented as a direct multiplicative shrinkage on the weights; this equivalence breaks for adaptive optimizers like Adam, which is why AdamW decouples it.
  5. Dropout randomly zeroes out entire neuron outputs during training to prevent neurons from co-adapting too closely.
  6. DropConnect generalizes Dropout by randomly zeroing out individual weights (connections) rather than whole neuron outputs.
  7. Early stopping halts training once validation performance stops improving, avoiding the need to guess the ideal number of training epochs in advance.
  8. Label smoothing softens one-hot classification targets to prevent the model from becoming overconfident in its predictions.
  9. Data augmentation artificially expands the training dataset with varied, label-preserving transformations, helping the model learn more robust, generalizable features rather than memorizing specific training examples.
Knowledge Checkpoint

08. Regularization Checkpoint

Q1.How does Dropout prevent co-adaptation among hidden units during training?
ABy randomly setting a subset of neuron activations to zero (probability p) during each training step, forcing each neuron to learn robust, self-reliant representations.
BBy deleting model layers from memory permanently.
CBy skipping training on odd-numbered epochs.
DBy randomly permuting dataset labels.
Q2.What is 'Inverted Dropout', and why is it standard in modern frameworks like PyTorch (`nn.Dropout`)?
AIt scales surviving activations by 1 / (1 - p) during training so that no scaling or modification is needed at inference time (`model.eval()`).
BIt activates dropped neurons only on the backward pass.
CIt drops weights instead of activations.
DIt applies dropout only to output logits.
Q3.How do L1 and L2 weight decay regularizations differ in their effect on network parameter distributions?
AL1 regularization drives weights to exact zeros (sparse feature selection), while L2 regularization smoothly shrinks all weights towards zero without forcing exact sparsity.
BL1 only works on CNNs, while L2 only works on RNNs.
CL2 increases weight magnitudes during gradient descent.
DL1 regularization eliminates the bias term.
Track Your Learning

Finished studying this notebook?

Mark this guide as completed to update your course progress roadmap.