What is least squares line fitting?
Least squares fitting finds the straight line that comes closest to a set of points. Every straight-line model in statistics and machine learning is fitted this way.
In this lesson you will measure how far a line is from the data, then use gradient descent to find the best line. You will check it against the exact answer.
By the end of this lesson
Note
I can fit a line to points by minimising the squared error.
Key words
| Word | What it means |
|---|---|
| Residual | the gap between a prediction and the real value |
| Squared error | a residual multiplied by itself, so it is never negative |
| Mean squared error | the average of the squared errors |
| Least squares | choosing the line with the smallest mean squared error |
| Intercept | the height of the line where x is zero |
The cells students fill in are left blank in this preview.
Squared error measures how bad a line is
A line hires = m * temp + c predicts hires from temperature. For each day, the residual is the prediction minus the real hires.
Positive and negative residuals would cancel, so square each one. The average of the squares is the mean squared error. A smaller number means a better line.
Worked example: two lines on five days
| Temp | Hires | Guess 10, 130 predicts | Squared error | Best line predicts | Squared error |
|---|---|---|---|---|---|
| 2 | 150 | 150 | 0 | 150 | 0 |
| 6 | 190 | 190 | 0 | 193 | 9 |
| 10 | 250 | 230 | 400 | 236 | 196 |
| 14 | 260 | 270 | 100 | 279 | 361 |
| 18 | 330 | 310 | 400 | 322 | 64 |
| Mean | 180 | 126 |
The cells students fill in are left blank in this preview.
Gradient descent can fit m and c
The mean squared error is a function of two inputs, m and c. Its gradient says how the error changes when each one moves.
For each day, the slope for m adds 2 times the residual times x. The slope for c adds 2 times the residual. Divide each total by the number of days, then step both numbers downhill.
Fit a line by descent in Python
# Temperature in tens of degrees, hires in hundreds.
xs = [0.2, 0.6, 1.0, 1.4, 1.8]
ys = [1.5, 1.9, 2.5, 2.6, 3.3]
m = 0.0
c = 0.0
rate = 0.1
for step in range(2000):
slope_m = 0.0
slope_c = 0.0
for n in range(len(xs)):
error = m * xs[n] + c - ys[n]
slope_m = slope_m + 2 * error * xs[n] / len(xs)
slope_c = slope_c + 2 * error / len(xs)
m = m - rate * slope_m
c = c - rate * slope_c
print(round(m, 3), round(c, 3))
# Change the 2000 to 20 and run it again.
# Is the line already close, or does it need more steps?
A line also has an exact answer
For a straight line you do not need descent. Find the mean of x and the mean of y. Multiply each pair of gaps and add them up. Add up the squared x gaps too.
The slope is the first total divided by the second. The intercept is the mean of y minus the slope times the mean of x. Descent matters because most models have no such formula.
Worked example: the exact line for five days
| Temp gap | Hires gap | Gaps multiplied | Temp gap squared |
|---|---|---|---|
| -8 | -86 | 688 | 64 |
| -4 | -46 | 184 | 16 |
| 0 | 14 | 0 | 0 |
| 4 | 24 | 96 | 16 |
| 8 | 94 | 752 | 64 |
| Total | 1720 | 160 | |
| Slope | 1720 divided by 160 | 10.75 | |
| Intercept | 236 minus 10.75 times 10 | 128.5 |
The cells students fill in are left blank in this preview.
Check the exact line in the lab
docker exec jupyter python3 -c "import numpy as np; print(*np.polyfit([2, 6, 10, 14, 18], [150, 190, 250, 260, 330], 1).round(2))"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Fit the whole year of hires
docker exec jupyter python3 -c "import numpy as np, pandas as pd; h = pd.read_csv('hires.csv'); print(*np.polyfit(h['temp_c'], h['hires'], 1).round(2))"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
A common mistake
Warning
Many people think the best line must pass through as many points as possible. The best line can miss every point. It is the one whose squared errors, added up, are as small as they can be.
Going further
In your own notebook, fit a line of hires against rain_mm for the whole year with np.polyfit.
Say what the slope tells you about one more millimetre of rain. Then compare its sign with the temperature slope of 11.12.