AI Knowledge Tree

Linear regression as maximum likelihood

Show that OLS is the MLE under Gaussian noise, and recover the noise variance from the residuals.

The previous lesson derived OLS from a geometric loss. The same estimator falls out of a likelihood once you say what the noise distribution is. Under Gaussian noise, OLS is the maximum-likelihood estimator, and the residuals deliver an MLE for the noise variance for free.

The probabilistic model

Replace the loss-only model with a generative assumption: targets are noisy linear functions of features,

yi∣xi∼N(xi⊤β, σ2),y_i \mid x_i \sim \mathcal{N}(x_i^\top \beta,\ \sigma^2),

with independent errors across examples. The unknowns are now β\beta and the noise variance σ2\sigma^2.

The likelihood

The Gaussian PDF gives the likelihood of each observation:

p(yi∣xi,β,σ2)=12πσ2exp⁡ ⁣(−(yi−xi⊤β)22σ2).p(y_i \mid x_i, \beta, \sigma^2) = \frac{1}{\sqrt{2\pi\sigma^2}}\exp\!\left(-\frac{(y_i - x_i^\top\beta)^2}{2\sigma^2}\right).

By independence, the joint log-likelihood is

ℓ(β,σ2)=−n2log⁡(2πσ2)−12σ2∑i=1n(yi−xi⊤β)2.\ell(\beta, \sigma^2) = -\frac{n}{2}\log(2\pi\sigma^2) - \frac{1}{2\sigma^2}\sum_{i=1}^n (y_i - x_i^\top\beta)^2.

MLE for β\beta: same as OLS

The only β\beta-dependent term is the sum of squared residuals, with a negative sign and positive scalar prefactor. Maximizing ℓ\ell in β\beta is the same as minimizing the sum of squared residuals:

β^MLE=arg⁡min⁡β∑i(yi−xi⊤β)2=β^OLS.\hat\beta_{\text{MLE}} = \arg\min_\beta \sum_i (y_i - x_i^\top\beta)^2 = \hat\beta_{\text{OLS}}. [1]

The OLS estimator is the MLE under Gaussian noise. This is why squared-error loss is the default for regression: it is the negative log-likelihood of a Gaussian, the noise model nothing else has a reason to disagree with by default.

MLE for σ2\sigma^2

Substitute β^\hat\beta back into ℓ\ell and differentiate with respect to σ2\sigma^2. Setting the derivative to zero,

∂ℓ∂σ2=−n2σ2+12σ4∑iri2=0  ⟹  σ^MLE2=1n∑i=1nri2,\frac{\partial \ell}{\partial \sigma^2} = -\frac{n}{2\sigma^2} + \frac{1}{2\sigma^4}\sum_i r_i^2 = 0 \implies \hat\sigma^2_{\text{MLE}} = \frac{1}{n}\sum_{i=1}^n r_i^2,

where ri=yi−xi⊤β^r_i = y_i - x_i^\top\hat\beta are the residuals. The MLE for noise variance is the mean squared residual. The unbiased estimator divides by n−dn - d instead of nn (the same correction that turned sample-variance into n−1n - 1 for a single sample):

σ^unbiased2=1n−d∑iri2.\hat\sigma^2_{\text{unbiased}} = \frac{1}{n - d}\sum_i r_i^2.

The n−dn - d correction accounts for the degrees of freedom lost to fitting dd coefficients. The two estimators differ by the factor (n−d)/n(n - d)/n, which is the fraction by which the MLE underestimates on average, so the gap is a story about sample size. Fitting d=3d = 3 coefficients:

  • n=10n = 10: factor 7/10=0.77/10 = 0.7, the MLE runs about 30%30\% low. With ten points and three coefficients, three of the residuals are nearly forced to zero, and dividing by nn ignores that.
  • n=50n = 50: factor 47/50=0.9447/50 = 0.94, a 6%6\% underestimate.
  • n=500n = 500 (the code block's size): factor 497/500≈0.994497/500 \approx 0.994, under 1%1\%. The correction barely moves the answer here, which is why the two printed variances come out close.

The correction matters when dd is a real fraction of nn and is negligible when n≫dn \gg d.

Why this matters

Having the likelihood gives you, in addition to point estimates:

  • Confidence intervals on each β^j\hat\beta_j via the Fisher information, which for linear regression equals σ2(X⊤X)−1\sigma^2 (X^\top X)^{-1}[2].
  • Hypothesis tests (t-tests for individual coefficients, F-tests for groups).
  • Model comparison via likelihood-ratio tests and information criteria (AIC, BIC).
  • The bridge to ridge regression as MAP under a Gaussian prior on β\beta, and to LASSO as MAP under a Laplace prior.
Least squares is Gaussian maximum likelihood: the log-likelihood surface over (slope, noise σ) peaks exactly at the OLS slope and the residual standard deviation.
Least squares is Gaussian maximum likelihood: the log-likelihood surface over (slope, noise σ) peaks exactly at the OLS slope and the residual standard deviation.

Check it on arrays

python · numpy
import numpy as np

rng = np.random.default_rng(1)
n, d = 500, 3
X = np.column_stack([np.ones(n), rng.normal(size=(n, d - 1))])
beta_true = np.array([1.0, 2.5, -0.8])
sigma_true = 0.7
y = X @ beta_true + sigma_true * rng.normal(size=n)

# MLE for beta = OLS
beta_hat = np.linalg.lstsq(X, y, rcond=None)[0]
print("beta_hat:", np.round(beta_hat, 3), " true:", beta_true)

# MLE vs unbiased noise variance
r = y - X @ beta_hat
sigma2_mle = np.mean(r**2)
sigma2_unbiased = np.sum(r**2) / (n - d)
print(f"sigma^2 MLE      = {sigma2_mle:.4f}")
print(f"sigma^2 unbiased = {sigma2_unbiased:.4f}")
print(f"true sigma^2     = {sigma_true**2:.4f}")

# Coefficient standard errors via Fisher info
cov_beta = sigma2_unbiased * np.linalg.inv(X.T @ X)
se = np.sqrt(np.diag(cov_beta))
print("std errors:", np.round(se, 4))
for j, (b, s, bt) in enumerate(zip(beta_hat, se, beta_true)):
    lo, hi = b - 1.96 * s, b + 1.96 * s
    inside = lo <= bt <= hi
    print(f"  beta_{j}: 95% CI [{lo:.3f}, {hi:.3f}] contains truth? {inside}")

The MLE for β\beta recovers the OLS estimate, the MLE noise variance is slightly biased low (the unbiased version with n−dn - d is closer to truth), and the 95% confidence intervals from the Fisher information cover the true coefficients.

Exercises

Exercise 1. A single feature-free model fits a constant: yi∼N(μ,σ2)y_i \sim \mathcal{N}(\mu, \sigma^2), so XX is the all-ones column and β=μ\beta = \mu. Show that the MLE is the sample mean μ^=1n∑iyi\hat\mu = \frac{1}{n}\sum_i y_i by differentiating the log-likelihood.

Solution

Step 1. With xi⊤β=μx_i^\top\beta = \mu for every ii, the log-likelihood from the lesson is

ℓ(μ,σ2)=−n2log⁡(2πσ2)−12σ2∑i=1n(yi−μ)2.\ell(\mu, \sigma^2) = -\frac{n}{2}\log(2\pi\sigma^2) - \frac{1}{2\sigma^2}\sum_{i=1}^n (y_i - \mu)^2.

Step 2. Only the second term depends on μ\mu. Differentiate and set to zero:

∂ℓ∂μ=1σ2∑i=1n(yi−μ)=0.\frac{\partial \ell}{\partial \mu} = \frac{1}{\sigma^2}\sum_{i=1}^n (y_i - \mu) = 0.

Step 3. The positive prefactor 1/σ21/\sigma^2 drops out, leaving ∑iyi−nμ=0\sum_i y_i - n\mu = 0, so μ^=1n∑iyi\hat\mu = \frac{1}{n}\sum_i y_i. The sample mean is the special case of OLS with an intercept-only design, and it is the MLE under Gaussian noise.

Exercise 2. Prove the residuals are orthogonal to every column of XX: X⊤r=0X^\top r = 0 at β^MLE\hat\beta_{\text{MLE}}, where r=y−Xβ^r = \mathbf{y} - X\hat\beta. Use this to explain why fitting dd coefficients forces the residual vector into an (n−d)(n - d)-dimensional subspace, the degrees-of-freedom story behind the n−dn - d divisor.

Solution

Step 1. The MLE for β\beta minimizes ∥y−Xβ∥2\lVert \mathbf{y} - X\beta\rVert^2. The gradient is ∇β∥y−Xβ∥2=−2X⊤(y−Xβ)\nabla_\beta \lVert \mathbf{y} - X\beta\rVert^2 = -2 X^\top(\mathbf{y} - X\beta). Setting it to zero at β^\hat\beta gives X⊤(y−Xβ^)=X⊤r=0X^\top(\mathbf{y} - X\hat\beta) = X^\top r = 0.

Step 2. X⊤r=0X^\top r = 0 is dd independent linear constraints on the nn residual components (one per column of the full-rank XX). The residual vector therefore lives in the orthogonal complement of the dd-dimensional column space, an (n−d)(n - d)-dimensional subspace.

Step 3. Only n−dn - d of the residual coordinates are free to vary; the other dd are pinned by the constraints. The expected squared residual length is E[∥r∥2]=(n−d)σ2\mathbb{E}[\lVert r\rVert^2] = (n - d)\sigma^2, so dividing ∑iri2\sum_i r_i^2 by n−dn - d (not nn) returns an unbiased σ2\sigma^2.

Exercise 3 (code). Confirm the σ^MLE2\hat\sigma^2_{\text{MLE}} bias factor (n−d)/n(n - d)/n empirically. Fit OLS with d=4d = 4 coefficients over many random datasets for several sample sizes, and check that the average of σ^MLE2/σ2\hat\sigma^2_{\text{MLE}} / \sigma^2 matches (n−d)/n(n - d)/n.

Solution
python · numpy
import numpy as np

d = 4
sigma2 = 1.5
print("n      mean(MLE)/true   expected (n-d)/n")
for n in [8, 20, 100, 1000]:
    ratios = []
    for t in range(2000):
        r = np.random.default_rng(1000 + t)
        X = np.column_stack([np.ones(n)] + [r.normal(size=n) for _ in range(d - 1)])
        beta = r.normal(size=d)
        y = X @ beta + np.sqrt(sigma2) * r.normal(size=n)
        bhat = np.linalg.lstsq(X, y, rcond=None)[0]
        res = y - X @ bhat
        ratios.append(np.mean(res**2) / sigma2)
    mean_ratio = float(np.mean(ratios))
    expected = (n - d) / n
    print(f"{n:>5}  {mean_ratio:.4f}          {expected:.4f}")
    assert np.isclose(mean_ratio, expected, atol=0.02)

The MLE runs low by the factor (n−d)/n(n - d)/n: about 0.500.50 at n=8n = 8 (a 50%50\% underestimate when dd is half the sample), and effectively 11 by n=1000n = 1000, matching the lesson's claim that the correction is negligible once n≫dn \gg d.

The next lesson tackles the universal tradeoff every estimator faces: bias and variance.


References

  1. [1]Bishop, C. M. (2006). Pattern Recognition and Machine Learning, §3.1. Springer. Least squares as Gaussian maximum likelihood.↩
  2. [2]Hastie, T., Tibshirani, R., & Friedman, J. (2009). The Elements of Statistical Learning (2nd ed.), §3.2. Springer. doi:10.1007/978-0-387-84858-7. Sampling distribution and inference for the coefficients.↩