~/blog
Simple Linear Regression
A real estate agent asks you: "I have six recent sales in this neighborhood. If a 1,000 sq ft house comes on the market, what's it worth?" You could take the average — $303k — but you'd ignore the obvious pattern: bigger houses sell for more. You need a formula that turns square footage into a price. That's simple linear regression: one input, one output, find the best line.
The derivation is short enough to do by hand, and doing it by hand is how you internalize why the slope formula involves covariance and variance — not just what the formula is. This is not the same as a lookup table where you check the closest past sale; a linear model gives you a prediction for any square footage, even ones you've never seen.
Anchor dataset:
import numpy as np
from sklearn.linear_model import LinearRegression
X = np.array([650, 850, 1100, 1400, 1600, 1900]).reshape(-1, 1)
y = np.array([180, 220, 280, 340, 370, 430])The Plan — Three Steps to Simple Linear Regression
We'll work through three steps: first measure how bad a naive guess is, then derive the formulas for the optimal slope and intercept, then compute them by hand on our anchor data and verify with code.
Step 1 — The Loss: Sum of Squared Errors
The residual for sample is .
The loss is the sum of squared errors:
Why squared? It penalizes large errors more than small ones, treats over- and under-prediction symmetrically, and is differentiable everywhere — properties that allow a closed-form solution.
As a baseline, try the worst possible model: (always predict zero):
Our goal is to find and that drive SSE far below this.
✓ Step 1 complete. SSE for the naive all-zeros model is 596,600. Every step from here drives this number down.
Step 2 — Deriving the OLS Formulas
We start by setting the partial derivatives of SSE with respect to and to zero — this finds the bottom of the bowl-shaped loss surface:
Solving this system yields the OLS normal equations:
The slope is the covariance between and divided by the variance of — how much moves per unit movement in , adjusted for 's own spread.
✓ Step 2 complete. We have the formulas: and .
Step 3 — Computing OLS by Hand on the Anchor
Step 1: Summary statistics
Step 2: Per-sample products
| 650 | 180 | −600 | −123.33 | 360000 | 74000 |
| 850 | 220 | −400 | −83.33 | 160000 | 33333 |
| 1100 | 280 | −150 | −23.33 | 22500 | 3500 |
| 1400 | 340 | 150 | 36.67 | 22500 | 5500 |
| 1600 | 370 | 350 | 66.67 | 122500 | 23333 |
| 1900 | 430 | 650 | 126.67 | 422500 | 82333 |
| Σ | 1110000 | 222000 |
Step 3: Compute weights
Final model:
✓ Step 3 complete. and . Each additional square foot adds $200 to predicted price.
Step 4 — Predictions and Residuals
| 650 | 180 | 183.33 | −3.33 | 11.09 |
| 850 | 220 | 223.33 | −3.33 | 11.09 |
| 1100 | 280 | 273.33 | 6.67 | 44.49 |
| 1400 | 340 | 333.33 | 6.67 | 44.49 |
| 1600 | 370 | 373.33 | −3.33 | 11.09 |
| 1900 | 430 | 433.33 | −3.33 | 11.09 |
| SSE | 133.33 |
SSE dropped from 596600 (naive model) to 133.33 — a 4500× reduction. No other linear model can achieve lower SSE on this data; OLS is provably optimal among all unbiased linear estimators.
✓ Step 4 complete. SSE at the optimal weights is 133.33, down from 596,600 at .
Step 5 — Code Verification
model = LinearRegression()
model.fit(X, y)
print(f"w₀ (intercept): {model.intercept_:.2f}")
print(f"w₁ (slope): {model.coef_[0]:.4f}")w₀ (intercept): 53.33
w₁ (slope): 0.2000The sklearn default uses ordinary least squares with no regularization — it should match our hand derivation exactly. Verify by predicting a new house:
new_house = np.array([[1000]])
print(f"Predicted price for 1000 sq_ft: ${model.predict(new_house)[0]:.1f}k")Predicted price for 1000 sq_ft: $253.3kManual check: ✓
✓ Step 5 complete. sklearn confirms , , prediction for 1,000 sq ft is $253.3k.
Assumptions
Four conditions are required for OLS to behave well:
- Linearity — the true relationship is .
- Independence — residuals are independent across samples.
- Homoscedasticity — the variance of residuals is constant (doesn't grow with ).
- Normality — residuals follow a normal distribution (needed for inference, not prediction).
Violation check: plot residuals vs fitted values. Random scatter around zero confirms the assumptions. A fan shape means heteroscedasticity. A curved pattern means the linearity assumption is wrong.
Key Formulas
| Formula | Expression |
|---|---|
| Slope | |
| Intercept | |
| Prediction | |
| SSE |
Related Concepts and Honest Limitations
The OLS closed form is unique to linear regression with MSE — it works because the loss is quadratic in the weights, which gives a single global minimum you can solve for directly. For logistic regression or neural networks, the loss has no such algebraic shortcut, which is why gradient descent (post 7) exists. Multiple linear regression (post 8) extends the same formulas to features using the matrix form .
A common mistake here is assuming linearity is a small constraint. It's not — if the true relationship between sq_ft and price curves (prices that plateau at large houses), the best-fit line will have systematic residual patterns regardless of how carefully you tune the weights. You'll see it immediately in a residual-vs-fitted plot: the residuals won't scatter randomly around zero; they'll curve. That's the signal to add polynomial features or switch model class, not to keep tweaking what you have.
Test Your Understanding
-
For our 6-sample anchor, verify that the sum of residuals . Is this always true for OLS? Why?
-
If you doubled all values (prices in $2k increments instead of $k), how would and change? Use the OLS formulas to derive this, not trial and error.
-
The SSE dropped from 596600 to 133.33 with OLS weights. Could you find weights that give SSE = 0? If not, why not? If yes, what would that mean about the data?
-
What is the residual for a house with sq_ft = 1250? Is the model over- or under-predicting?
-
A colleague argues that maximizing is the same as minimizing SSE. Are they correct? Under what conditions would these objectives give different results?