Contents
Deep Breakdown
What it tells me (Situation Awareness): The Normal Equations give the closed-form Maximum Likelihood Estimate (MLE) for the parameters of a linear regression model with Gaussian noise. The derivation proceeds by taking the gradient of the negative log-likelihood (which equals the least-squares objective) with respect to , setting it to zero, and solving the resulting linear system . The formula requires to be invertible — guaranteed when has full column rank (no multicollinear features). The normal equations give the exact OLS solution in one step without iteration, but computing is and numerically unstable for ill-conditioned problems; in practice, solvers use QR decomposition or SVD instead.
Flowchart
Real-world use
Where it is used
Fitting OLS linear regression models with small to medium datasets (, ), analytical derivation of regression coefficients in statistical analysis, solving Bayesian linear regression posterior mean computation (with a Gaussian prior, the posterior mean is a regularised version of the normal equations).
Problem it solves
You need to find the weight vector that minimises the sum of squared residuals. The normal equations give the exact closed-form solution without iterative gradient descent.
How it solves the problem
By differentiating the loss function with respect to and setting to zero: . The solution is when the matrix is invertible.
Example in a business
A DA fits a linear regression to model weekly revenue as a function of 3 marketing variables (, weeks). They compute directly — the dataset is small enough that the matrix inversion is trivial and gives exact coefficients. The coefficients are then reported as the marginal contribution of each channel per unit spend.
The formula, piece by piece
| Symbol | Meaning |
|---|---|
| Feature/design matrix ( samples, features) | |
| Target variable vector | |
| Parameter vector to estimate | |
| Gram matrix: sum of outer products of feature vectors | |
| Inverse of the Gram matrix (exists when has full column rank) | |
| Cross-correlation of features with target | |
| MLE / OLS solution |
Equivalent notation: Pseudoinverse form (always exists): (SVD-based)
The formula in words
"The ML weight vector is obtained by inverting the feature self-correlation matrix and multiplying by the feature-target cross-correlation — it is the unique least-squares solution that makes the gradient of the squared error exactly zero."
Critical Concepts
- must be invertible: this requires and no perfectly collinear features. If violated, use the Moore-Penrose pseudoinverse or add ridge regularisation.
- Numerical instability: directly computing amplifies condition number. Use
np.linalg.lstsqorscipy.linalg.solve(X.T @ X, X.T @ y)in practice. - Computational cost: — fast for small , slow for large . Gradient descent is preferred for large .
- Geometric interpretation: is the orthogonal projection of onto the column space of . is the hat matrix.
- Ridge extension: — always invertible regardless of rank.
Step-by-step calculation (tiny example)
, (simple regression with intercept absorbed in ). ,
Step 1: Compute
Step 2: Compute
Step 3: Compute Verify: predictions . Residuals . Sum of residuals (normal equations satisfied).
Code examples
Beginner example (Python)
import numpy as np # Simple example: predict y from x (with intercept column)
X = np.array([[1, 1], [1, 2], [1, 3], [1, 4]], dtype=float)
y = np.array([2.0, 3.0, 5.0, 6.0]) # Normal equations: theta = (X^T X)^{-1} X^T y
XTX = X.T @ X
XTy = X.T @ y
theta = np.linalg.solve(XTX, XTy) # safer than inv
print(f"Intercept: {theta[0]:.4f}, Slope: {theta[1]:.4f}") # sklearn OLS for comparison
from sklearn.linear_model import LinearRegression
lr = LinearRegression(fit_intercept=False)
lr.fit(X, y)
print(f"sklearn coefficients: {lr.coef_.round(4)}")Beginner example (R)
x <- c(1, 2, 3, 4)
y <- c(2, 3, 5, 6)
X <- cbind(1, x)
XTX <- t(X) %*% X
XTy <- t(X) %*% y
theta <- solve(XTX, XTy)
cat("Intercept:", round(theta[1], 4), "\n")
cat("Slope: ", round(theta[2], 4), "\n")
# Using lm() for verification
fit <- lm(y ~ x)
cat("lm coefficients:", round(coef(fit), 4), "\n")Professional example (Python)
from dataclasses import dataclass
import numpy as np
import pandas as pd
from scipy import linalg
from sklearn.metrics import r2_score, mean_absolute_error
@dataclass
class OLSResult:
coefficients: np.ndarray
intercept: float
r2: float
mae: float
condition_number: float
rank: int
def fit_ols_normal_equations(
X: np.ndarray, y: np.ndarray, add_intercept: bool = True,
) -> OLSResult:
# Fit OLS via normal equations using scipy.linalg.solve (stable over explicit inv).
if add_intercept:
X = np.column_stack([np.ones(len(X)), X])
XTX = X.T @ X
XTy = X.T @ y
cond = float(np.linalg.cond(XTX))
theta = linalg.solve(XTX, XTy) # avoids explicit matrix inverse
y_pred = X @ theta
intercept = float(theta[0]) if add_intercept else 0.0
coefficients = theta[1:] if add_intercept else theta
return OLSResult(
coefficients=coefficients.round(6),
intercept=round(intercept, 6),
r2=round(r2_score(y, y_pred), 6),
mae=round(mean_absolute_error(y, y_pred), 6),
condition_number=round(cond, 2),
rank=int(np.linalg.matrix_rank(XTX)),
)
if __name__ == "__main__":
rng = np.random.default_rng(seed=42)
n = 200
p = 4
X_sim = rng.standard_normal((n, p))
true_theta = np.array([1.5, -0.5, 2.0, 0.3])
y_sim = X_sim @ true_theta + rng.normal(0, 0.5, n)
result = fit_ols_normal_equations(X_sim, y_sim)
print("OLS Normal Equations result:")
print(f" Coefficients: {result.coefficients}")
print(f" R^2: {result.r2}")
print(f" MAE: {result.mae}")
print(f" Condition number of X^T X: {result.condition_number}")MML1 pag 320
Self-check: mathematical verification
Re-derivation from the least-squares objective
Start with the residual sum of squares:
Expand this quadratic form:
Differentiate with respect to :
Set the gradient equal to zero:
Divide by 2:
These are the normal equations. If is invertible, multiply both sides by :
This matches the canonical formula exactly.
Projection interpretation check Define the residual vector
From the normal equations:
so:
This means the residual is orthogonal to every column of . Therefore is the orthogonal projection of onto the column space of . This matches the stated geometric interpretation.
Invertibility condition check
For to exist, must be nonsingular. This occurs when the columns of are linearly independent, meaning has full column rank. So the assumption in the context is correct.
MLE connection check Under the Gaussian-noise model
the log-likelihood, up to constants, is:
For fixed , maximizing the likelihood with respect to is equivalent to minimizing:
Therefore the MLE for is the same as the OLS solution, which is given by the normal equations. This confirms the stated statistical interpretation.
Verbal-to-algebra consistency
The verbal explanation says the normal equations solve least squares exactly, require invertibility of , and produce a projection. The derivation confirms all three claims.
Structural verification
- Opening restatement paragraph is present.
- All required sections are present and non-empty.
- Block equations use `
`.
- Symbol-table entries use math mode such as
$X$,$y$,$\hat{w}$, and$X^\top X$. - Mermaid uses
flowchart TDand node text in["..."]. - Three code blocks are present: Python beginner, R beginner, Python professional.
- Links to least squares, projection, regression, and interpretable coefficients are explicitly included.
- No placeholder text remains.
Common Pitfalls and Edge Cases
- Trying to invert a singular matrix: If predictors are collinear, is singular and the closed-form inverse does not exist.
- Ignoring numerical stability: Even when invertible, can be ill-conditioned, making direct inversion unstable.
- Confusing the normal equations with the only way to solve OLS: QR decomposition and SVD are often better numerically.
- Forgetting the intercept column: If the intercept is part of the model, the design matrix must include a column of ones.
- Assuming MLE equivalence without Gaussian assumptions: The exact MLE interpretation depends on the normal-noise model.
- Overlooking projection geometry: The formula is not just algebraic, it has a geometric meaning in the predictor subspace.
- Assuming coefficients are automatically causal: The solution gives the best linear fit, not necessarily causal effects.
- Using the formula blindly in very large problems: Iterative solvers may be more practical when is large.
References and Standard Results
- Least-squares objective,
- Normal equations,
- Closed-form OLS solution,
- Full-column-rank condition for invertibility of
- Residual orthogonality condition,
- Orthogonal projection of onto the column space of
- Hat matrix,
- Equivalence of OLS and Gaussian-noise MLE