Machine Learning Foundations

The Shape of Occam's Razor: Demystifying L1 and L2 Regularization

Why your neural network wants to memorize noise, and how circles, diamonds, and Bayesian priors force it to generalize.

Muhammad Hamza · IIT Kharagpur · May 14, 2026 Fundamentals Mathematics From Scratch

The Disease: Exploding Weights and Memorization

We've all been there. You train a deep model on a small dataset, your training loss plummets to exactly zero, and your test loss explodes into the stratosphere. Your model didn't learn the underlying physics of the data; it just memorized the noise.

If you peek under the hood of a heavily overfitted model, you'll almost always observe a specific symptom: the weights are massive. To perfectly fit every single outlier and noisy data point, the model creates a wildly oscillating decision boundary. Mathematically, wild oscillations require huge coefficients.

⚠️ The Symptom of Overfitting

Consider a simple polynomial regression trying to fit 10 noisy points with a 9th-degree polynomial. To bend the curve through every single point, the weights $w_i$ multiply against features $x^i$. To cancel each other out precisely between points, the weights grow exponentially large. Large weights mean the model is highly sensitive to tiny changes in the input.

We need a mathematical formulation of Occam's Razor: given two models that explain the data equally well, prefer the simpler one. But how do we define "simple"? In machine learning, we define simplicity by restricting the size or the number of the weights.

This is where regularization comes in. Instead of just minimizing the loss, we minimize the loss plus a penalty on the weights.

$$\mathcal{L}_{\text{total}} = \text{Loss}(\text{Data}, \theta) + \lambda \cdot \text{Penalty}(\theta)$$

The two most famous penalties are L1 (Lasso) and L2 (Ridge). But where do they come from, and why do they behave so differently?

Geometry

The Geometric View: Lagrangian Constraints

To understand how L1 and L2 work, we need to view them through the lens of constrained optimization. Imagine you are trying to minimize your mean squared error (MSE), but I put a strict budget on your weights. You can only use a total "weight budget" of $C$.

By the method of Lagrange multipliers, minimizing $\text{Loss} + \lambda \cdot \text{Penalty}$ is mathematically equivalent to minimizing the Loss subject to a hard constraint on the Penalty. The $\lambda$ hyperparameter directly controls the radius $C$ of our constraint region.

Let's visualize how the loss landscape interacts with these constraint regions.

Intersection of Loss Contours with L1 and L2 Constraints

Left: L1 (Lasso). The diamond constraint has sharp corners on the axes. The expanding elliptical loss contours are highly likely to hit a corner first, forcing $w_1 = 0$. Right: L2 (Ridge). The circular constraint is smooth. The contours touch it at a tangent, shrinking both weights but rarely forcing either to exactly zero.

🔍 Why L1 induces sparsity

This geometric intersection is the entire reason L1 is used for feature selection. Because the L1 ball protrudes at the axes (the corners of the diamond), the expanding ellipses of the loss function will almost always hit a corner before they hit an edge. Hitting a corner means one of the parameters becomes exactly zero. L2, being a smooth circle, has no corners to catch the contours, so it just shrinks weights uniformly.

Probability

The Probabilistic View: MAP Estimation

Geometry is beautiful, but probability tells a deeper story. What if I told you that regularizing your weights is mathematically identical to applying a Bayesian prior to your model?

In standard Maximum Likelihood Estimation (MLE), we try to find the weights $\theta$ that maximize the probability of our data given the weights: $P(D | \theta)$. But Bayes' Theorem tells us we should actually maximize the posterior—the probability of the weights given the data:

$$P(\theta | D) = \frac{P(D | \theta) P(\theta)}{P(D)}$$

This is called Maximum A Posteriori (MAP) estimation. $P(\theta)$ is our prior belief about the weights before seeing any data. If we assume our weights should be small and centered around zero, we can assign a probability distribution to them. To maximize this, we minimize the negative log of the numerator:

$$-\log P(\theta | D) \propto -\log P(D | \theta) - \log P(\theta)$$

Notice the structure? It perfectly matches our regularization equation! The negative log-likelihood $-\log P(D | \theta)$ is our Loss function (like MSE or Cross-Entropy). The negative log-prior $-\log P(\theta)$ is our Penalty term.

Deriving L2 from a Gaussian Prior

Assume our prior belief is that weights are drawn from a Gaussian distribution with mean $0$ and variance $\tau^2$:
$$P(w_i) = \frac{1}{\sqrt{2\pi\tau^2}} \exp\left(-\frac{w_i^2}{2\tau^2}\right)$$

Taking the negative log of this prior (and dropping constants):
$$-\log P(w) \propto \sum_{i} \frac{w_i^2}{2\tau^2} = \lambda \sum_i w_i^2$$

This is exactly the L2 penalty! So, L2 regularization is equivalent to saying: "I believe my weights are normally distributed around zero."

Deriving L1 from a Laplace Prior

Now, assume a Laplace distribution, which has a sharper peak at zero and thicker tails:
$$P(w_i) = \frac{1}{2b} \exp\left(-\frac{|w_i|}{b}\right)$$

Taking the negative log (dropping constants):
$$-\log P(w) \propto \sum_{i} \frac{|w_i|}{b} = \lambda \sum_i |w_i|$$

This is exactly the L1 penalty! L1 regularization is equivalent to saying: "I believe my weights follow a Laplace distribution." Because Laplace is pointy at zero, MAP estimation naturally drives many weights to exactly zero.

Beyond L1 & L2

When L1 and L2 Fail: The Elastic Net

So, we use L2 to prevent explosion, and L1 when we want sparsity (feature selection). But the real world is messy, and both methods have fatal flaws.

Lasso's Flaw
When features are highly correlated, L1 randomly picks one and zeroes out the rest. It is unstable.
Ridge's Flaw
L2 shrinks everything but never zeroes anything out. Terrible for high-dimensional feature selection.
Elastic Net
Combines both penalties. Gives sparsity of L1 with the stability of L2.

If you have a dataset where "Feature A" and "Feature B" are practically identical (collinear), Lasso (L1) will arbitrarily push the weight of one to zero and keep the other. This makes your model highly sensitive to the specific training sample. Ridge (L2) will smoothly divide the weight equally between both.

To get the best of both worlds, Zou and Hastie introduced the Elastic Net in 2005. It's elegantly simple—just add both penalties together:

$$\mathcal{L} = \text{Loss} + \lambda_1 \sum |w_i| + \lambda_2 \sum w_i^2$$

Geometrically, the constraint region looks like a diamond with rounded edges. It retains the corners needed to drive weights to zero, but the slight curve stabilizes the selection of correlated features.

1

Ordinary Least Squares (OLS)

Minimize MSE. Often leads to massive weights and catastrophic overfitting in high dimensions.

2

Ridge Regression (L2) - 1970s

Added the squared penalty. Fixed the explosion problem, but kept all features.

3

Lasso (L1) - 1996

Robert Tibshirani introduced the absolute value penalty, enabling automatic feature selection.

4

Elastic Net - 2005

A hybrid approach solving Lasso's instability with collinear features.

From Scratch

Writing Custom Regularizers From Scratch

In modern deep learning libraries like PyTorch, L2 regularization is often hidden behind the weight_decay parameter in optimizers like Adam or SGD. But what if you want to implement L1, or an Elastic Net, or a custom penalty for your PDE solver?

To really understand it, you should implement the loss penalty directly during your forward pass. Here is how you write a custom Elastic Net regularization hook in pure PyTorch.

PyTorch: Custom Elastic Net Regularization

import torch
import torch.nn as nn

def elastic_net_penalty(model: nn.Module, l1_ratio: float, alpha: float):
    """
    Computes Elastic Net penalty for all weights in the model.
    alpha: Overall strength of the regularization
    l1_ratio: Mix between L1 (1.0) and L2 (0.0)
    """
    l1_loss = torch.tensor(0.0, requires_grad=True).to(next(model.parameters()).device)
    l2_loss = torch.tensor(0.0, requires_grad=True).to(next(model.parameters()).device)
    
    for name, param in model.named_parameters():
        # We typically only regularize weights, not biases
        if 'bias' not in name:
            l1_loss = l1_loss + torch.sum(torch.abs(param))
            l2_loss = l2_loss + torch.sum(torch.square(param))
            
    # Elastic Net formula
    penalty = alpha * (l1_ratio * l1_loss + (1 - l1_ratio) * l2_loss)
    return penalty

# Training Loop Example
for data, target in dataloader:
    optimizer.zero_grad()
    
    output = model(data)
    base_loss = criterion(output, target)
    
    # Compute our custom penalty
    reg_loss = elastic_net_penalty(model, l1_ratio=0.5, alpha=1e-4)
    
    # Total loss is Data Loss + Prior (Penalty)
    total_loss = base_loss + reg_loss
    
    total_loss.backward()
    optimizer.step()
      

By decoupling the penalty from the optimizer, you gain full mathematical control. You can track exactly how much of your loss is coming from the data vs. the prior, and you can easily experiment with custom penalties (like smoothly regularizing spatial derivatives in Physics-Informed Neural Networks).

✓ Wrapping Up

Next time you casually set `weight_decay=1e-4` in Adam, remember what's happening mathematically. You aren't just tweaking a dial; you are declaring a Gaussian prior over your parameters. You are altering the geometric constraints of your loss landscape. By mastering these foundations, from the shape of the constraints to their probabilistic origins, you stop treating ML as a black box and start wielding it like an engineer.