# How Ridge Regression Prevents Overfitting with L2 Regularization: A Complete Guide

> Discover how Ridge Regression prevents overfitting using L2 regularization. Learn to shrink coefficients and stabilize models for better performance.

- Repository: [Rohit Ghumare/ai-engineering-from-scratch](https://github.com/rohitg00/ai-engineering-from-scratch)
- Tags: deep-dive
- Published: 2026-07-31

---

**Ridge regression prevents overfitting by adding an L2 penalty term (λ‖w‖²₂) to the loss function, which shrinks coefficient magnitudes toward zero and stabilizes the solution when features are collinear or the data matrix is ill-conditioned.**

This article examines the mechanics of ridge regression as implemented in the `rohitg00/ai-engineering-from-scratch` repository, breaking down how L2 regularization constrains model complexity to improve generalization. We analyze the closed-form mathematical solution, explore its Bayesian interpretation, and provide runnable Python code from the curriculum's linear systems implementation.

## The L2 Regularization Mechanism

Ridge regression augments ordinary least squares (OLS) with a **Tikhonov regularization** term that penalizes the squared magnitude of the weight vector. The optimization objective transforms from minimizing squared error to minimizing:

```

‖Xw - y‖²₂ + λ‖w‖²₂

```

Here, **λ (lambda)** controls the regularization strength. As implemented in [`phases/01-math-foundations/17-linear-systems/code/linear_systems.py`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/phases/01-math-foundations/17-linear-systems/code/linear_systems.py), this modification changes the normal equations from `XᵀXw = Xᵀy` to `(XᵀX + λI)w = Xᵀy`, where **I** is the identity matrix.

## Three Ways L2 Regularization Reduces Overfitting

### 1. Coefficient Shrinkage

The penalty term **λ‖w‖²₂** forces the optimization algorithm to balance fitting the training data against keeping weights small. Large coefficient values incur higher costs, so the solution favors weight vectors with smaller magnitudes distributed across features. This **shrinkage** reduces the model's capacity to fit noise in the training data, directly addressing the high-variance component of overfitting.

### 2. Improved Matrix Conditioning

In [`phases/01-math-foundations/17-linear-systems/code/linear_systems.py`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/phases/01-math-foundations/17-linear-systems/code/linear_systems.py), the implementation explicitly adds `lam * np.eye(A.shape[1])` to the Gram matrix `A.T @ A`. This addition of **λI** along the diagonal:

- Increases the smallest eigenvalues of the matrix
- Makes the matrix symmetric positive-definite
- Stabilizes numerical inversion when features are highly correlated (multicollinearity)

Without regularization (λ = 0), near-singular matrices cause unstable, high-magnitude weights that overfit. The L2 term guarantees invertibility and numerical stability.

### 3. Bias-Variance Trade-off

Ridge regression intentionally introduces a small **bias** to substantially reduce **variance**. While OLS provides unbiased estimates, ridge regression's shrinkage creates slightly biased coefficient estimates. However, in high-dimensional spaces or with correlated features, this bias dramatically lowers prediction variance on unseen data, yielding lower overall generalization error according to the convex optimization theory documented in [`phases/01-math-foundations/18-convex-optimization/docs/en.md`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/phases/01-math-foundations/18-convex-optimization/docs/en.md).

## Bayesian Interpretation of Ridge Regression

As detailed in [`phases/01-math-foundations/07-bayes-theorem/docs/en.md`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/phases/01-math-foundations/07-bayes-theorem/docs/en.md), ridge regression corresponds to **Maximum A Posteriori (MAP)** estimation under a Gaussian prior. When you assume weights follow a zero-mean Gaussian distribution with variance proportional to 1/λ, the MAP estimate exactly matches the ridge regression solution. This probabilistic perspective frames L2 regularization as principled prior knowledge that extreme weight values are unlikely, naturally discouraging overfitting without requiring explicit feature selection.

## Implementation: From-Scratch Ridge Regression

The curriculum provides a pure NumPy implementation in [`phases/01-math-foundations/17-linear-systems/code/linear_systems.py`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/phases/01-math-foundations/17-linear-systems/code/linear_systems.py). The `ridge_regression` function demonstrates the closed-form solution:

```python
import numpy as np
from sklearn.linear_model import Ridge as SklearnRidge
from sklearn.metrics import r2_score

# Synthetic data generation

np.random.seed(0)
X = np.random.randn(100, 3)
true_w = np.array([1.5, -2.0, 0.5])
y = X @ true_w + 0.1 * np.random.randn(100)

def ridge_regression(A, b, lam):
    """
    Solve ridge regression via normal equations.
    Returns w that minimizes ||Aw - b||^2 + lam*||w||^2
    """
    # (AᵀA + λI) w = Aᵀb

    return np.linalg.solve(A.T @ A + lam * np.eye(A.shape[1]), A.T @ b)

# Fit with regularization strength λ = 0.1

lam = 0.1
w_ridge = ridge_regression(X, y, lam)
print("Ridge weights:", np.round(w_ridge, 4))

```

This implementation explicitly constructs the regularized normal equations, solving `(AᵀA + λI)w = Aᵀb` using `np.linalg.solve` for numerical stability.

## Validating with Scikit-Learn

To verify the from-scratch implementation matches production libraries, the code compares against scikit-learn's `Ridge` class:

```python

# Scikit-learn comparison

ridge_sk = SklearnRidge(alpha=lam, fit_intercept=False)
ridge_sk.fit(X, y)
print("sklearn weights:", np.round(ridge_sk.coef_, 4))

# Verify equivalence

diff = np.max(np.abs(w_ridge - ridge_sk.coef_))
print("Maximum difference:", diff)

# Demonstrate overfitting prevention

w_ols = ridge_regression(X, y, 0.0)  # No regularization

print("\nOLS weights (λ=0):", np.round(w_ols, 4))
print("R² (ridge):", r2_score(y, X @ w_ridge))
print("R² (OLS):  ", r2_score(y, X @ w_ols))

```

The comparison demonstrates that the manual implementation produces identical results to scikit-learn while providing transparency into how the L2 penalty `(A.T @ A + lam * np.eye(...))` modifies the optimization landscape.

## Summary

- **Ridge regression** adds λ‖w‖²₂ to the loss function, penalizing large weights through L2 regularization.
- The modification `(XᵀX + λI)` improves matrix conditioning, solving multicollinearity and numerical instability issues present in OLS.
- **Coefficient shrinkage** reduces model variance at the cost of small bias, improving generalization on unseen data.
- The technique corresponds to MAP estimation with a Gaussian prior, providing a Bayesian justification for the penalty term.
- The `rohitg00/ai-engineering-from-scratch` repository implements this in [`phases/01-math-foundations/17-linear-systems/code/linear_systems.py`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/phases/01-math-foundations/17-linear-systems/code/linear_systems.py) using `np.linalg.solve` for the regularized normal equations.

## Frequently Asked Questions

### What is the difference between L1 and L2 regularization?

**L2 regularization** (ridge) penalizes the sum of squared weights (‖w‖²₂), shrinking coefficients smoothly toward zero but rarely exactly to zero. **L1 regularization** (Lasso) penalizes the sum of absolute weights (‖w‖₁), which can drive coefficients exactly to zero and perform feature selection. Ridge regression maintains all features with reduced magnitudes, while Lasso performs implicit feature selection.

### How do I choose the optimal lambda (α) value for ridge regression?

Select **λ** through **cross-validation**, typically k-fold validation. Iterate over a logarithmic grid of λ values (e.g., 10⁻⁴ to 10⁴), fitting ridge regression on training folds and evaluating mean squared error on validation folds. The λ yielding the lowest validation error minimizes the bias-variance trade-off. Scikit-learn's `RidgeCV` automates this grid search.

### Does ridge regression eliminate features like Lasso?

No. Ridge regression shrinks coefficients toward zero but does not set them exactly to zero (unless λ approaches infinity). It performs **coefficient shrinkage** rather than **feature selection**. If you require sparse models with feature elimination, use Lasso (L1) or Elastic Net, which combine L1 and L2 penalties as referenced in the convex optimization documentation.

### Can ridge regression handle multicollinearity?

Yes. Ridge regression specifically addresses **multicollinearity** by adding λ to the diagonal of XᵀX, which increases the smallest eigenvalues and ensures the matrix remains invertible even when features are highly correlated. This stabilization prevents the explosive variance in coefficient estimates that occurs with ordinary least squares when predictor variables are collinear.