Machine learningLoading progress…

Closed-form fitting

Fit a linear predictor by hand, implement its loss and gradient, then compare with a library solution.

Skip the walk: solve for the bottom directly

Gradient descent walks down the bowl. For linear regression the bottom can be written down. Set the gradient to zero and the equations are linear in w and b, so a linear solver finds the answer in one go.

Step 1 of 4
Gradient equal to zero. At the bottom of the bowl the gradient is 0. Writing the model as X·w with a column of x values and a column of ones, that condition is Xᵀ(Xw − y) = 0.
-5-4-3-2-1012345-10-8-6-4-2024681012xy
X^T X = [[ 33.14, -0.60 ], X^T y = [ 67.08 ] [ -0.60, 4 ]] [ 3.90 ] (first column x, second column ones) det = 4 * 33.14 - (-0.60)^2 = 132.200
w = (n*Sxy - Sx*Sy) / det = 2.0474 b = (Sxx*Sy - Sx*Sxy) / det = 1.2821
wb
closed form2.04741.2821
gradient descent after 00.00000.0000
gap-2.0474-1.2821

Gradient descent uses learning rate 0.0603 here (half of the largest stable rate), starting from w = 0, b = 0. Green solid: closed form. Orange dashed: gradient descent.

Choose how many points to use and read the live matrices. The green line is the exact closed-form answer. The orange dashed line is gradient descent after the iterations you choose.

Closed form versus gradient descent

The closed form gives the exact least-squares line in one solve. Gradient descent approaches it, getting closer with each iteration and never quite arriving. With 12 points and a safe learning rate, about 30 iterations get both w and b within 0.01 of the exact answer. With two points the fitted line passes through both: two points determine a line, so the loss is zero. For models with millions of parameters the closed form is too expensive and most models have no closed form at all, which is why gradient descent matters. For small linear problems, the solve wins.

Predict first. Two features are exact copies of each other. Is XᵀX still invertible?

In code: solve, do not invert

# textbook:  w = (X^T X)^-1 X^T y        # forms an inverse: slower, less accurate
w = np.linalg.solve(X.T @ X, X.T @ y)   # solves the system directly
w, *_ = np.linalg.lstsq(X, y, rcond=None)   # QR/SVD based, also handles rank-deficient X
Chapter context

Linear regression fits a weighted sum of features. The model is simple enough to inspect numerically, making it a useful bridge between geometry, optimization and software implementation.

Chapter reference: math, worked example & code

This section belongs to the whole chapter, so it is the same on every lesson in it and may cover a different lesson's topic. The code for the experiment you just ran is inside the experiment itself.

Math and notation

y^i=xiTw+b,L=1n∑i(y^i−yi)2.\hat y_i=x_i^Tw+b,\quad L=\frac1n\sum_i(\hat y_i-y_i)^2. ∇wL=2nXT(Xw+b1−y),∂bL=2n∑i(y^i−yi).\nabla_w L=\frac2nX^T(Xw+b\mathbf1-y),\quad \partial_bL=\frac2n\sum_i(\hat y_i-y_i).

When invertible, a no-intercept least-squares solution is (XTX)−1XTy(X^TX)^{-1}X^Ty. Use a pseudoinverse or suitable solver otherwise.

Worked example

Use x=[1,2], y=[2,4], w=1 and b fixed at zero. Predictions [1,2] give residuals [-1,-2], so MSE=(1+4)/2=2.5. The w gradient is 2/2×(−1×1−2×2)=−5. With eta=0.1, w becomes 1.5 and MSE falls to (0.25+1)/2=0.625.

Reference code

The following example uses the Python standard library and does not call a remote service.

x, y, w = [1,2], [2,4], 1.0
mse = lambda weight: sum((weight*a-b)**2 for a,b in zip(x,y))/len(x)
grad = 2/len(x)*sum((w*a-b)*a for a,b in zip(x,y))
assert mse(w) == 2.5 and grad == -5
w -= .1*grad
assert w == 1.5 and mse(w) == .625

Python examples are provided for study and local execution; this page does not execute Python.

Common mistakes

A low residual does not show that a weight is causal. The closed form is not always a literal matrix inverse in reliable code. Include or exclude bias consistently in derivations.