Sign in or create a free account to continue.

AI-210 · Lesson 2 of 12 · 28 min

Linear systems, least squares and projections

Lesson 1 described matrices as linear maps and decomposed them with eigenvalues and the SVD. This lesson asks the question behind most of classical machine learning: given data \(\mathbf{X}\) and targets \(\mathbf{y}\), which weights \(\mathbf{w}\) make \(\mathbf{X}\mathbf{w}\) as close as possible to \(\mathbf{y}\)? The answer, least squares, has a beautiful geometric meaning as a projection, and understanding it explains when a regression is well posed, why some fits are numerically unstable, what "leverage" means, and how regularization picks one answer when infinitely many fit perfectly, which is the normal situation for modern over-parameterized models.

Linear systems and rank

A linear system \(\mathbf{A}\mathbf{x} = \mathbf{b}\) with \(\mathbf{A}\) of size \(m \times n\) has three possible outcomes:

  • Exactly one solution, when \(\mathbf{A}\) is square and invertible (full rank).
  • No exact solution, the usual case when there are more equations than unknowns (\(m > n\)), as in fitting a model to more data points than parameters: the system is overdetermined.
  • Infinitely many solutions, when there are more unknowns than independent equations (\(n > m\)): the system is underdetermined.

The rank of \(\mathbf{A}\) is the number of linearly independent columns (equivalently rows): the dimension of its column space, the set of all vectors \(\mathbf{A}\mathbf{x}\) can produce. The null space is the set of \(\mathbf{x}\) with \(\mathbf{A}\mathbf{x} = \mathbf{0}\); by the rank–nullity theorem its dimension is \(n - \text{rank}(\mathbf{A})\). A design matrix with a redundant column, such as a feature duplicated in different units, is rank deficient: its null space contains directions along which the weights can change without changing the predictions, so the weights are not identifiable.

Least squares

When \(\mathbf{y}\) is not in the column space of \(\mathbf{X}\), we choose \(\mathbf{w}\) to minimize the squared error \(\|\mathbf{X}\mathbf{w} - \mathbf{y}\|^{2}\). Lesson 4 derives the solution by setting the gradient to zero; here is the geometric view. The vector \(\mathbf{X}\mathbf{w}\) ranges over the column space of \(\mathbf{X}\), a flat subspace. The closest point of that subspace to \(\mathbf{y}\) is its orthogonal projection, reached when the residual \(\mathbf{r} = \mathbf{y} - \mathbf{X}\mathbf{w}\) is perpendicular to every column of \(\mathbf{X}\):

\[\mathbf{X}^{T}(\mathbf{y} - \mathbf{X}\mathbf{w}) = \mathbf{0} \quad\Longrightarrow\quad \mathbf{X}^{T}\mathbf{X}\,\mathbf{w} = \mathbf{X}^{T}\mathbf{y}\]

These are the normal equations ("normal" meaning perpendicular). If \(\mathbf{X}\) has full column rank, \(\mathbf{X}^{T}\mathbf{X}\) is invertible and \(\mathbf{w} = (\mathbf{X}^{T}\mathbf{X})^{-1}\mathbf{X}^{T}\mathbf{y}\).

Three-dimensional sketch of a plane through the origin representing the column space of X, spanned by two column vectors x1 and x2. A vector y rises above the plane. Its orthogonal projection onto the plane, labelled X w, lies directly below it, and the residual vector r from X w up to y meets the plane at a right angle.
Least squares projects y onto the column space of X; the residual is perpendicular to that space.
Fitting house prices by least squares, checking that residuals are orthogonal to the columns of X, and detecting a redundant column through the rank.
import numpy as np

rng = np.random.default_rng(0)
n = 50
area = rng.uniform(50, 250, n)                 # floor area, m^2
age = rng.uniform(0, 60, n)                    # years
price = 40 + 2.1 * area - 0.8 * age + rng.normal(0, 15, n)   # thousands of dollars

X = np.column_stack([np.ones(n), area, age])   # design matrix with an intercept column
print("Shape of X:", X.shape, "| rank:", np.linalg.matrix_rank(X))

w, residual_ss, rank, sv = np.linalg.lstsq(X, price, rcond=None)
print("Least-squares weights:", np.round(w, 3))
r = price - X @ w
print("Residuals orthogonal to every column of X:", np.allclose(X.T @ r, 0, atol=1e-6))

X_bad = np.column_stack([X, 2 * area])        # a column that duplicates information
print("Rank with a redundant column:", np.linalg.matrix_rank(X_bad), "of", X_bad.shape[1])
Output
Shape of X: (50, 3) | rank: 3
Least-squares weights: [48.39   2.063 -0.89 ]
Residuals orthogonal to every column of X: True
Rank with a redundant column: 3 of 4

The weights recover the coefficients used to generate the data (about 2.1 per square metre and −0.8 per year) up to noise, and the residual is orthogonal to the intercept, area and age columns, exactly as the geometry predicts. Orthogonality to the intercept column means the residuals sum to zero.

The projection matrix and leverage

The fitted values are \(\hat{\mathbf{y}} = \mathbf{X}\mathbf{w} = \mathbf{P}\mathbf{y}\), where

\[\mathbf{P} = \mathbf{X}(\mathbf{X}^{T}\mathbf{X})^{-1}\mathbf{X}^{T}\]

is the projection matrix, often called the hat matrix because it puts the hat on \(\mathbf{y}\). It is symmetric and idempotent (\(\mathbf{P}^{2} = \mathbf{P}\): projecting twice changes nothing), and its trace equals the number of parameters. Its diagonal entries \(P_{ii}\), the leverages, measure how much each observation pulls the fit toward itself. Points far from the others in feature space have high leverage: they can swing the fitted line, and a single error in such a point does disproportionate damage.

The hat matrix of a simple regression: symmetric, idempotent, trace equal to the number of parameters, and high leverage at an isolated point.
import numpy as np

rng = np.random.default_rng(1)
x = np.sort(rng.uniform(0, 10, 8))
x[-1] = 30.0                                   # one point far from the rest
X = np.column_stack([np.ones_like(x), x])
y = 3 + 0.5 * x + rng.normal(0, 0.5, x.size)

P = X @ np.linalg.inv(X.T @ X) @ X.T          # projection ("hat") matrix
print("P symmetric:", np.allclose(P, P.T), "| P idempotent (P @ P = P):", np.allclose(P @ P, P))
print("Trace of P (number of parameters):", round(np.trace(P), 6))
print("Leverage of each point:", np.round(np.diag(P), 3))
y_hat = P @ y
print("Fitted values equal X w:", np.allclose(y_hat, X @ np.linalg.lstsq(X, y, rcond=None)[0]))
Output
P symmetric: True | P idempotent (P @ P = P): True
Trace of P (number of parameters): 2.0
Leverage of each point: [0.203 0.169 0.154 0.152 0.141 0.125 0.128 0.928]
Fitted values equal X w: True

The isolated point at \(x = 30\) has leverage 0.93: the fitted line passes almost through it whatever its \(y\) value. Because the leverages sum to the number of parameters (here 2), their average is \(p/n\), and a common rule flags points with leverage above two or three times that average for inspection.

Worked problem

Least squares by hand

Fit \(y = w_{0} + w_{1}x\) to the points (0, 1), (1, 3) and (2, 4) using the normal equations.

Show solution
  1. \(\mathbf{X} = \begin{bmatrix} 1 & 0 \\ 1 & 1 \\ 1 & 2 \end{bmatrix}\), \(\mathbf{y} = (1, 3, 4)^{T}\).
  2. \(\mathbf{X}^{T}\mathbf{X} = \begin{bmatrix} 3 & 3 \\ 3 & 5 \end{bmatrix}\), \(\mathbf{X}^{T}\mathbf{y} = (8, 11)^{T}\).
  3. Solve \(3w_{0} + 3w_{1} = 8\), \(3w_{0} + 5w_{1} = 11\): subtracting, \(2w_{1} = 3\), so \(w_{1} = 1.5\) and \(w_{0} = (8 - 4.5)/3 = 1.167\).
  4. Residuals: \(1 - 1.167 = -0.167\), \(3 - 2.667 = 0.333\), \(4 - 4.167 = -0.167\); they sum to zero and \(0 \times (-0.167) + 1 \times 0.333 + 2 \times (-0.167) = 0\).

Answer: \(y \approx 1.167 + 1.5x\), with residuals orthogonal to both columns of \(\mathbf{X}\).

Conditioning: when the answer is fragile

Even when \(\mathbf{X}\) has full rank, the least-squares problem can be numerically fragile. The condition number of a matrix, the ratio of its largest to smallest singular value (Lesson 1), \(\kappa(\mathbf{X}) = \sigma_{max}/\sigma_{min}\), measures how much relative errors in the data can be amplified in the solution: roughly, you can lose \(\log_{10}\kappa\) digits of accuracy. Forming the normal equations squares the condition number, \(\kappa(\mathbf{X}^{T}\mathbf{X}) = \kappa(\mathbf{X})^{2}\), which is why numerical libraries solve least squares with the QR decomposition or the SVD instead.

Polynomial fits of increasing degree: the condition number explodes, and the normal equations lose accuracy that the SVD-based solver keeps.
import numpy as np

x = np.linspace(0, 1, 40)
y = np.sin(2 * np.pi * x)
for degree in [3, 8, 14]:
    V = np.vander(x, degree + 1, increasing=True)        # columns 1, x, x^2, ...
    w_qr = np.linalg.lstsq(V, y, rcond=None)[0]           # SVD-based solver
    w_ne = np.linalg.solve(V.T @ V, V.T @ y)              # normal equations
    err_qr = np.linalg.norm(V @ w_qr - y)
    err_ne = np.linalg.norm(V @ w_ne - y)
    print(f"degree {degree:>2}: cond(V) = {np.linalg.cond(V):9.2e}, cond(VtV) = {np.linalg.cond(V.T @ V):9.2e}, "
          f"residual lstsq {err_qr:.2e}, normal eq. {err_ne:.2e}")
Output
degree  3: cond(V) =  1.16e+02, cond(VtV) =  1.35e+04, residual lstsq 4.60e-01, normal eq. 4.60e-01
degree  8: cond(V) =  6.16e+05, cond(VtV) =  3.80e+11, residual lstsq 1.13e-03, normal eq. 1.13e-03
degree 14: cond(V) =  2.46e+10, cond(VtV) =  5.90e+17, residual lstsq 4.41e-09, normal eq. 5.85e-07

Polynomial features \(1, x, x^{2}, \ldots\) on \([0, 1]\) become nearly parallel as the degree rises, and the condition number grows enormously. At degree 14, \(\kappa(\mathbf{V}^{T}\mathbf{V})\) is around \(10^{17}\), beyond what double precision (about 16 significant digits) can represent, and the normal-equation residual is a hundred times worse than the SVD solution. The cures are to use a stable solver, to standardize features (AI-110, Lesson 6), to use better-conditioned bases such as orthogonal polynomials, or to regularize.

Worked problem

Digits lost

A regression's design matrix has condition number \(10^{5}\). Roughly how many significant digits may be lost (a) solving with QR, (b) solving the normal equations, in double precision with about 16 digits?

Show solution
  1. (a) About \(\log_{10}10^{5} = 5\) digits lost, leaving about 11.
  2. (b) The normal equations have condition number \(10^{10}\): about 10 digits lost, leaving about 6.

Answer: About 11 digits remain with QR but only about 6 with the normal equations. In single precision (about 7 digits), as used on many accelerators, the normal equations would leave almost nothing, which is one reason careful numerical methods matter for machine learning on GPUs (Lesson 5).

Underdetermined systems and the minimum-norm solution

Modern machine learning models often have far more parameters than training examples, so many weight vectors fit the training data exactly. Which one is chosen matters, because they behave differently on new data. The pseudo-inverse \(\mathbf{A}^{+}\), computed from the SVD by inverting the nonzero singular values, gives the solution of smallest Euclidean norm:

\[\mathbf{x}^{+} = \mathbf{A}^{+}\mathbf{b} = \mathbf{V}\boldsymbol{\Sigma}^{+}\mathbf{U}^{T}\mathbf{b}\]

Every other solution is \(\mathbf{x}^{+}\) plus a vector from the null space, and is longer. Ridge regression, which adds a penalty \(\lambda\|\mathbf{x}\|^{2}\), approaches the minimum-norm solution as \(\lambda \to 0\).

An underdetermined system: the pseudo-inverse gives the minimum-norm solution, adding a null-space vector gives a longer one, and ridge regression converges to the minimum-norm solution as its penalty shrinks.
import numpy as np

rng = np.random.default_rng(2)
A = rng.normal(size=(3, 6))            # 3 equations, 6 unknowns: infinitely many solutions
b = np.array([1.0, -2.0, 0.5])

x_min = np.linalg.pinv(A) @ b          # minimum-norm solution
print("A x = b:", np.allclose(A @ x_min, b), "| norm of x:", round(np.linalg.norm(x_min), 4))

null_basis = np.linalg.svd(A)[2][3:]  # rows spanning the null space of A
x_other = x_min + null_basis.T @ np.array([1.0, -0.5, 2.0])
print("Another solution also works:", np.allclose(A @ x_other, b), "| its norm:", round(np.linalg.norm(x_other), 4))

for lam in [1.0, 0.01, 1e-6]:
    x_ridge = A.T @ np.linalg.solve(A @ A.T + lam * np.eye(3), b)
    print(f"ridge lambda = {lam:g}: distance to minimum-norm solution {np.linalg.norm(x_ridge - x_min):.2e}")
Output
A x = b: True | norm of x: 1.8168
Another solution also works: True | its norm: 2.9241
ridge lambda = 1: distance to minimum-norm solution 7.28e-01
ridge lambda = 0.01: distance to minimum-norm solution 1.22e-02
ridge lambda = 1e-06: distance to minimum-norm solution 1.23e-06

This has a surprising consequence. Gradient descent started from zero weights on a least-squares problem never moves in null-space directions (the gradient \(\mathbf{A}^{T}(\mathbf{A}\mathbf{x} - \mathbf{b})\) always lies in the row space of \(\mathbf{A}\)), so it converges to the minimum-norm solution without any explicit regularization. This implicit regularization by the optimizer is part of why heavily over-parameterized models can still generalize, a topic of active research (Lesson 8 and AI-310).

Orthogonality as a tool

Orthogonal matrices (\(\mathbf{Q}^{T}\mathbf{Q} = \mathbf{I}\)) preserve lengths and angles and have condition number 1, so computations with them never amplify errors. The QR decomposition \(\mathbf{X} = \mathbf{Q}\mathbf{R}\), with \(\mathbf{Q}\) having orthonormal columns and \(\mathbf{R}\) upper triangular, turns least squares into the well-conditioned triangular system \(\mathbf{R}\mathbf{w} = \mathbf{Q}^{T}\mathbf{y}\). Gram–Schmidt orthogonalization, which builds \(\mathbf{Q}\) column by column, is the classic way to understand QR; Householder reflections are the stable way to compute it. Orthogonality also appears in deep learning, where orthogonal weight initialization and orthogonality constraints help keep signals from exploding or vanishing as they pass through many layers.

Common mistakes

  • MistakeIncluding redundant features and trusting the individual weights.

    InsteadCheck rank; with collinear features, individual weights are not identifiable.

  • MistakeSolving least squares with an explicit inverse of XᵀX.

    InsteadUse QR or SVD-based solvers such as numpy.linalg.lstsq.

  • MistakeIgnoring high-leverage points.

    InsteadInspect points with leverage well above p/n.

  • MistakeFitting high-degree raw polynomials on unscaled inputs.

    InsteadStandardize, use orthogonal bases or regularize.

  • MistakeAssuming a perfect training fit identifies a unique model.

    InsteadUnderdetermined problems have infinitely many solutions; the method decides which one you get.

Summary and key terms

  • Linear systems may have one, no or infinitely many solutions depending on rank and shape.
  • Least squares projects y onto the column space of X; the normal equations make the residual orthogonal to it.
  • The hat matrix P is symmetric and idempotent with trace p; its diagonal gives leverages.
  • The condition number measures fragility; normal equations square it.
  • The pseudo-inverse gives the minimum-norm solution of underdetermined systems; ridge approaches it as λ → 0.
  • Gradient descent from zero implicitly finds the minimum-norm solution.
  • Orthogonal matrices and QR give stable computations.
Rank
The number of linearly independent columns of a matrix.
Column space
The set of all vectors Ax.
Null space
The set of x with Ax = 0.
Normal equations
XᵀXw = Xᵀy, the conditions for a least-squares solution.
Projection matrix
A symmetric idempotent matrix that projects onto a subspace.
Leverage
The diagonal entry of the hat matrix: an observation's pull on its own fit.
Condition number
The ratio of largest to smallest singular value.
Pseudo-inverse
The generalized inverse computed from the SVD.
Minimum-norm solution
The shortest vector that solves an underdetermined system.
QR decomposition
Factorization into an orthonormal and an upper-triangular matrix.

Practice problems

Work each problem on paper first, then reveal the answer to check.

  1. A 100 × 5 design matrix has rank 4. What is the dimension of its null space?

    Show answer

    1.

  2. What does it mean if XᵀX is singular?

    Show answer

    X lacks full column rank: some features are linear combinations of others.

  3. Fit y = w₀ + w₁x to (0, 0), (1, 1), (2, 1).

    Show answer

    w₀ = 1/6, w₁ = 1/2.

  4. What are P² and Pᵀ for a projection matrix?

    Show answer

    Both equal P.

  5. Trace of the hat matrix for a model with 7 parameters?

    Show answer

    7.

  6. n = 200, p = 5. Average leverage? Rule-of-thumb threshold?

    Show answer

    0.025; about 0.05 to 0.075.

  7. cond(X) = 10³. cond(XᵀX)?

    Show answer

    10⁶.

  8. Why are raw polynomial features badly conditioned?

    Show answer

    Their columns become nearly parallel.

  9. What does the pseudo-inverse return for an underdetermined system?

    Show answer

    The minimum-norm solution.

  10. Why does gradient descent from zero find the minimum-norm least-squares solution?

    Show answer

    Its updates lie in the row space and never add null-space components.

Lesson quiz · 5 questions

Score 60% or more to complete this lesson.

Take the quiz