learn-intro-ml-with-phoebe / Builder session 8 of 10
Learn Intro to Machine Learning with Phoebe · Builder session 8 of 10

Regression metrics, and judging a number-predictor in dollars

A classifier gives you a yes or no; a regressor gives you a number - and you have to say how wrong that number is. This session builds the four metrics every regression uses (MAE, MSE, RMSE, R-squared), grounds each one in the Lumen order-value target, and does the thing that actually lands in a business review: it reports the error in dollars, not in an abstract score. You will beat a predict-the-mean baseline step by step, and learn why RMSE and MAE tell you different things about the same model.

🟠 Core Builders · DA / DE / DS Python · scikit-learn 45 min
0-3 · Why dollars 3-16 · The four metrics 16-40 · Build it in Python 40-45 · Wrap
Part 0

A prediction is only useful if you can say how wrong it is

In b7 you judged a classifier - a yes/no machine - with precision, recall, and ROC-AUC. Regression is different: the model predicts order_value, a dollar amount, and "right or wrong" is no longer a clean split. Instead you measure the size of the miss. This session turns that miss into four numbers, then translates the best one back into the language a CFO actually cares about: dollars off, on average.

Live - built in session Self-study - run after ★ The one big idea The case: Lumen Skincare
★ What you walk out with today The four regression metrics and when each earns its keep - MAE (average dollar miss), MSE, RMSE (same units as the target, punishes big misses harder), and R-squared (share of variance explained). You will build up from a predict-the-mean baseline (RMSE ~$41, R-squared 0) to a full model (RMSE ~$28, MAE ~$20, R-squared ~0.52), and learn to report "$28 vs a $41 baseline" instead of an abstract score.
Part 1 · the four numbers

MAE, MSE, RMSE, and R-squared 6 min live

Every regression score starts from one quantity: the residual, the gap between what the model predicted and what actually happened. Each metric summarises those residuals a different way, and the differences are the whole point - they are not interchangeable.

Residuals: the vertical gap from each point to the fit line order_value ($) prior_30d_spend One long orange miss (the big order) counts once in MAE, but squared in MSE - so RMSE > MAE whenever misses vary. RMSE > MAE squaring punishes the big misses harder The metric you pick decides whether one huge mispriced order matters a little (MAE) or a lot (RMSE).
🔍 Click to zoom - residuals, and why RMSE outruns MAE
LiveRMSE vs MAE - two honest answers to "how far off?"3 min▶

Both measure the typical miss in dollars, but they weight the misses differently:

  • MAE (mean absolute error) - the average size of the miss, in dollars, treating a $10 miss and a $100 miss proportionally. It is the number a stakeholder reads as "we are off by about this much".
  • MSE (mean squared error) - the average of the squared misses. Squaring blows up big errors, so MSE reacts strongly to a few large misses. Its units are dollars-squared, which nobody can picture.
  • RMSE (root mean squared error) - the square root of MSE, which pulls the units back to dollars. Because the squaring happened first, RMSE still punishes big misses harder than MAE does.
Why this matters Order value is right-skewed - a handful of big orders sit far above the rest. On that shape RMSE will always be larger than MAE, and the gap between them tells you how much a few large misses are dominating your error. Report both: MAE for the everyday miss, RMSE for the risk from the tail.
Self-studyR-squared and the predict-the-mean baseline3 min▶

R-squared answers a different question: not "how many dollars off" but "how much better than doing nothing?" The do-nothing model is predicting the mean order value for every row - it ignores all features. R-squared is the share of the variance in order value that your model explains on top of that baseline.

  • R-squared = 0 means you are no better than predicting the mean.
  • R-squared = 1 would mean perfect prediction (never happens on real, noisy money).
  • The predict-the-mean baseline has RMSE equal to the standard deviation of the target - on Lumen converters, about $41.

So R-squared and RMSE are two views of the same fit: R-squared is unit-free and comparable across problems, RMSE is in dollars and lands in a room. You will report both, but lead with dollars.

★ everyone builds · beat the baseline

From predict-the-mean to a real model 22 min · everyone builds

We predict order_value for the sessions that converted, and we build the score up in stages so every number has a reference point. Your figures will land near the course canon (a real sklearn fit on seed 42); teach the relationships, not a false fourth decimal.

Reload and set up the regression. Order value only makes sense for buyers, so we model the converter subset. Same train/test discipline as every session.

setup.py
import numpy as np, pandas as pd
from sklearn.model_selection import train_test_split

df = pd.read_parquet("lumen_sessions.parquet")
buyers = df[df.converted == 1].copy()          # regression is on converters only

y = buyers["order_value"]
X = buyers[["prior_30d_spend", "product_category",
            "channel", "new_vs_returning"]]

X_tr, X_te, y_tr, y_te = train_test_split(
    X, y, test_size=0.25, random_state=42)
print(X_tr.shape, X_te.shape, "target SD $%.0f" % y.std())   # SD ~ $41

Set the baseline: predict the mean. Before any model earns praise, know what doing nothing scores. The mean is the honest floor.

baseline.py
from sklearn.dummy import DummyRegressor
from sklearn.metrics import mean_squared_error, r2_score

dummy = DummyRegressor(strategy="mean")
dummy.fit(X_tr[["prior_30d_spend"]], y_tr)
pred0 = dummy.predict(X_te[["prior_30d_spend"]])

rmse0 = mean_squared_error(y_te, pred0) ** 0.5
print("baseline RMSE $%.0f" % rmse0)           # ~ $41 (= the SD)
print("baseline R2  %.2f" % r2_score(y_te, pred0))   # 0.00

RMSE ~$41 and R-squared 0. That $41 is the standard deviation of order value - the baseline error is literally how much order value bounces around on its own. Every model from here has to beat it.

R² 0.52 clears the do-nothing baseline of 0 0 = mean baseline 0.52 R-squared 0 means no better than guessing the mean; the full model reaches 0.52.
🔍 Click to zoom - R² of 0 is the floor every model has to beat, not a score of failure

One feature: prior spend. The statistics course found prior spend correlates with order value at r ~ 0.6. A single-feature linear model should turn that into real explained variance.

single_feature.py
from sklearn.linear_model import LinearRegression

reg1 = LinearRegression()
reg1.fit(X_tr[["prior_30d_spend"]], y_tr)
p1 = reg1.predict(X_te[["prior_30d_spend"]])

print("R2   %.2f" % r2_score(y_te, p1))            # ~ 0.40
print("RMSE $%.0f" % (mean_squared_error(y_te, p1) ** 0.5))   # ~ $32

R-squared ~0.40, RMSE ~$32 - already well below the $41 baseline. Note r ~ 0.6 means r-squared ~0.36 on the full data, and a touch higher on the fitted converter subset. One feature, one clear win.

The full model. Add product category, channel, and new-vs-returning via one-hot encoding. More honest signal should push the error down further.

full_model.py
from sklearn.metrics import mean_absolute_error

Xtr = pd.get_dummies(X_tr, drop_first=True)
Xte = pd.get_dummies(X_te, drop_first=True).reindex(
    columns=Xtr.columns, fill_value=0)          # align test columns to train

reg = LinearRegression().fit(Xtr, y_tr)
p = reg.predict(Xte)

print("R2   %.2f" % r2_score(y_te, p))              # ~ 0.52
print("RMSE $%.0f" % (mean_squared_error(y_te, p) ** 0.5))   # ~ $28
print("MAE  $%.0f" % mean_absolute_error(y_te, p))  # ~ $20

R-squared ~0.52, RMSE ~$28, MAE ~$20. Notice MAE < RMSE - always true, and the gap is wide here because the right-skewed big orders inflate RMSE. The everyday miss is about $20; the big-order risk is what pushes RMSE to $28.

The error shrinks as real signal replaces the baseline BASELINE (PREDICT MEAN) $41 RMSE PRIOR SPEND ONLY $32 RMSE FULL MODEL - RMSE $28 RMSE FULL MODEL - MAE $20 MAE Error keeps shrinking: $41 baseline to $32 with one feature to $28 RMSE / $20 MAE full model.
🔍 Click to zoom - every feature added should shrink this bar, or it is not earning its place

Look at the residuals. A single number hides where the model struggles. Plot residuals against the prediction to see the skew with your own eyes.

residuals.py
import matplotlib.pyplot as plt

resid = y_te - p
plt.scatter(p, resid, s=6, alpha=0.3)
plt.axhline(0, color="black")
plt.xlabel("predicted order_value ($)")
plt.ylabel("residual  (actual - predicted)")
plt.title("Residuals fan out on the big orders")
plt.show()
Real world

When the Lumen analyst took this to the CFO, she did not say "R-squared 0.52". She said "our order-value model is off by about $28 on average, versus $41 if we just guessed the average order" - and the room understood it instantly. Dollars land; R-squared does not. Keep R-squared for the model card and lead every conversation with the RMSE-versus-baseline dollar figure.

Before Session 9

This week ◐ 40 min total

Check yourself

Three questions before you go 🎯 ◐ 90 seconds

1 · How does RMSE differ from MAE?

RMSE squares the residuals before averaging, so big misses count disproportionately, then the square root returns it to dollars. MAE weights every miss proportionally. On a right-skewed target RMSE is larger than MAE, never smaller.

2 · What does the predict-the-mean baseline score on Lumen converters?

Predicting the mean explains none of the variance, so R-squared is 0 by definition, and its RMSE equals the standard deviation of the target - about $41 here. That is the floor every real model has to beat.

3 · Your full model reaches R-squared ~0.52. What does that mean?

R-squared is the share of variance explained, not an accuracy or an exact-hit rate. ~0.52 means roughly half the variation in order value is captured - a solid, honest lift over the predict-the-mean baseline, matched by the RMSE dropping from $41 to $28.

Source material

What this session covers

This builder session covers regression evaluation - the metrics you use to judge any number-predictor - on the Lumen order-value target, and pairs with the classification metrics of b7.

✓MAE, MSE, RMSE, R-squared; the predict-the-mean baseline; reporting error in dollarsPart 1 + demo
◐scikit-learn - regression metrics (mean_absolute_error, mean_squared_error, r2_score)the metric API
◐365 Data Science - linear regression and its evaluationconcept spine
◐StatQuest - R-squared, clearly explainedintuition

Builder Session 8 cheat sheet · pin this

Residualactual minus predicted. Every regression metric is just a way of summarising the residuals.
MAEaverage absolute miss, in dollars. The everyday "we are off by about this much" number. ~$20 for the full Lumen model.
RMSEsquare-root of the mean squared error - same units as the target, punishes big misses harder. Always > MAE on skewed money. ~$28 full model.
R-squaredshare of variance explained. 0 = no better than the mean, 1 = perfect. ~0.52 for the full model; unit-free, so lead with dollars in a room.
The baselinepredict the mean for every row: R-squared 0, RMSE = the SD (~$41). Beat this or you have nothing.
Report it right"$28 off vs a $41 baseline" beats "R-squared 0.52" in every business review. Dollars land, scores do not.