learn-model-evaluation-with-phoebe / Practitioner session 4 of 10
Learn Model Evaluation with Phoebe · Practitioner track · Session 4 of 10

Regression + ranking metrics

Three sessions of classifiers; tonight the other two families. When the model predicts a NUMBER (Lumen's order values) or a RANKED LIST (which 500 sessions get the outreach budget), the confusion matrix disappears - but the b1 habit survives intact: every metric is still an opinion about which errors hurt. You will learn the regression six (MAE, RMSE, MAPE, R², median absolute error, pinball), sklearn's which-scoring-function doctrine that assigns each one a job, and the ranking metrics - precision@k and NDCG - that turn "how good is the list?" into a number the business can argue with.

🟡 Practitioner track DS · analysts · ML + AI engineers Python · live evaluator on key pages 45 min
0-3 · Welcome 3-18 · Regression metrics with opinions 18-42 · Build-along: Lumen order values + ranking 42-45 · Q&A
Part 0

Where tonight fits

b1-b3 taught you to read a classifier. Tonight is the detour every real project takes: Lumen also forecasts order values (a regression - the average order is ~$74 and the distribution is right-skewed), and marketing does not consume probabilities, it consumes a ranked outreach list with a budget cutoff. Both need metrics, and both inherit the same discipline: name the errors that hurt before you pick the number. Next session (b5) everything funnels into the cost matrix; tonight makes sure the non-classifier metrics arrive there fluent.

Live - presented in session Self-study - read after class Official sources covered
★ What you walk out with today The regression metrics as six answers to one question ("which errors hurt?"), sklearn's which-scoring-function doctrine as a decision rule you can recite, and precision@k as the moment a ranking metric IS the business metric - plus the 30-second chooser to pick the right one under pressure.
Part 1 · covers sklearn regression metrics + the which-scoring-function doctrine

Six regression metrics, one question 15 min live

Every regression metric answers the same question - which errors hurt? - with a different opinion. MAE says all dollars hurt equally. RMSE says big misses hurt disproportionately. MAPE says percentage misses hurt, wherever they happen. Learn the opinion, not just the formula, and the choice becomes obvious from the business.

MetricThe question it answersProsThe trap
mean_absolute_error (MAE)How big is the typical miss, in $?Reads in real dollars; robust to a few wild missesTreats a $10 and a $10,000 miss linearly - no extra alarm for disasters
root_mean_squared_error (RMSE)How bad are the BIG misses?Punishes big misses - right for inventory and capacity problems where one huge error breaks thingsOutlier-sensitive: Lumen's order_value mean is ~$74 and right-skewed, so a few big gift sets dominate RMSE
mean_absolute_percentage_error (MAPE)How far off in %, across scales?Scale-free - compare a $2M store and a $20k product on one numberEXPLODES near zero actuals, and it is asymmetric - it penalizes over-forecast less
r2_score (R²)What share of the variance did we explain?One familiar number; comparable across targetsMisleading alone - high R² with structured residuals is common; always plot residuals too
median_absolute_errorWhat is the typical miss, ignoring disasters?The robust reporter - immune to the tailHides the tail entirely; never use it alone when big misses are the expensive ones
mean_pinball_lossHow good is our quantile (p90) forecast?The only honest score for quantile targetsMeaningless if you actually meant to predict the mean
Which errors hurt? Three metrics land at three spots on the same skewed errors error size in $ median: robust MAE: the typical miss RMSE: dragged by the tail a few big gift-set orders live out here The tail owns RMSE, the crowd owns the median, MAE sits between - pick by which error hurts your business.
🔍 Click to zoom - one skewed error distribution, three metrics with three different opinions
The same MAE, reported two ways ✗ Reported alone MAE $19 $19 of what? Good, bad, or no better than doing nothing at all? ✓ Reported as a pair MAE $19 - the model MAE $31 - predict-the-mean the model beats the dumb baseline, and you can see by how much Always print the metric next to its predict-the-mean baseline, in the same log line. Same lie detector as b1's always-say-no model: the dumb baseline is what makes a score readable.
🔍 Click to zoom - a regression metric only becomes readable next to its baseline
LiveThe which-scoring-function doctrine4 min▶

sklearn's evaluation guide compresses this whole zoo into one decision rule - what are you actually trying to predict?

  • Predicting a mean - score with squared error (RMSE family).
  • Predicting a median - score with absolute error (MAE family).
  • Predicting a quantile (p90 demand for safety stock) - score with pinball loss.
  • Predicting a probability - score with proper scoring rules (Brier, log loss - b3's territory).

The doctrine cuts both ways: a model trained toward the mean but reported on MAE is being graded against a target it never aimed at. Say what the model predicts, then the metric picks itself.

Baseline discipline (intro-ml canon) ALWAYS report regression metrics against the predict-the-mean baseline. "MAE $19" means nothing alone; "MAE $19 vs $31 for predict-the-mean" is a model earning its keep. Same paradox-proofing as b1's always-say-no model - the dumb baseline is the lie detector.
LiveR² ~0.45 and proud of it4 min▶

The intro-ml course's order-value model reached R² ~0.45 with multiple features - and that is honest work on noisy humans. Individual purchase behavior is mostly unexplainable variance; nobody's features capture mood, payday, or a friend's recommendation. Two habits follow:

  • Calibrate expectations by domain. R² 0.45 on human behavior can be excellent; R² 0.98 on a physical sensor can be mediocre. There is no universal "good R²."
  • Never read R² alone. High R² with structured residuals is common - the model can explain the variance and still be systematically wrong for a whole segment (every order above $200, say). Residual plots are part of the metric, not an optional extra.
Real world

The demand model with the beautiful R². A retailer's demand forecast reported R² 0.91 and shipped. The residuals, never plotted, were fine on ordinary weeks and wildly low every promotion week - exactly the weeks the forecast existed for. The aggregate metric averaged over the moments that mattered. RMSE sliced by promotion weeks would have caught it in review.

Self-studyThe regression six in sklearn, 60 seconds2 min read▶

The intro-ml course covers the mechanics hands-on; here is the whole family as a refresher, with the baseline wired in:

import numpy as np
from sklearn.metrics import (
    mean_absolute_error, root_mean_squared_error,
    mean_absolute_percentage_error, r2_score,
    median_absolute_error, mean_pinball_loss,
)

mae  = mean_absolute_error(y_test, pred)
rmse = root_mean_squared_error(y_test, pred)
mape = mean_absolute_percentage_error(y_test, pred)  # guard zeros first
r2   = r2_score(y_test, pred)
med  = median_absolute_error(y_test, pred)
p90  = mean_pinball_loss(y_test, pred_q90, alpha=0.9)

# the lie detector: the predict-the-mean baseline
baseline = np.full_like(y_test, y_test.mean(), dtype=float)
mae_base = mean_absolute_error(y_test, baseline)

One habit worth stealing: print every metric as a pair - model vs baseline - in the same log line. Reviews go faster when the lie detector is already in the output.

Part 2 · covers sklearn ndcg_score + average_precision_score

Ranking metrics - when position matters 14 min live

Search results, recommendations, top-k outreach lists: the model's output is an ORDER, and errors near the top hurt more than errors near the bottom. Classification metrics ignore position entirely; ranking metrics make it the whole point.

The log2 position discount: why only the top of a list counts Bar length is the weight a relevant item earns at that position, before normalization. rank 1 1 / log2(2) rank 2 1 / log2(3) rank 3 1 / log2(4) rank 5 1 / log2(6) rank 10 1 / log2(11) rank 40 1 / log2(41) - almost nothing left The discount encodes the belief that users read from the top, so errors near rank 1 dominate. Read NDCG with its k, and only against rankers on the same data - the normalizer moves with the items.
🔍 Click to zoom - a hit at rank 40 is worth a fraction of a hit at rank 1
LiveThe ranking family in four names6 min▶
  • precision@k - of the top k items, how many are relevant? The metric of every budget cutoff: "we can act on 500, how many are real?"
  • recall@k - of all relevant items, how many made the top k? The metric of coverage: "how many real buyers does the top 500 even contain?"
  • NDCG - handles GRADED relevance (a perfect match beats an okay match), discounts each position by log2 of its rank so the top of the list dominates, and normalizes to 0-1 against the ideal ordering. In sklearn: ndcg_score with k= for a cutoff. Canonical citation: Jarvelin & Kekalainen 2002.
  • average_precision_score - sklearn's AP summarizes the whole precision-recall tradeoff of the ranking. It is deliberately step-wise, not trapezoidal: linear interpolation of PR curves is overly optimistic (the b3 warning, still true here).

Honest note: true IR-style MAP@k - averaged over queries the way search papers report it - is not in sklearn. If a vendor quotes MAP@10, ask exactly how they computed it.

LiveLumen's top-500 list: precision@500 IS the business metric8 min▶

Here is how ranking metrics enter Lumen's story: rank all sessions by the conversion model's score, and marketing's outreach budget covers the top 500. Nobody below position 500 gets touched, so the quality of the whole ranking is irrelevant - precision@500 is not a proxy for the business metric, it IS the business metric: real converters per 500 outreach dollars spent. This is the rare, happy case where the metric and the money are the same number.

import numpy as np
from sklearn.metrics import ndcg_score

# graded relevance (0-3) vs model scores, top-heavy by design
ndcg = ndcg_score([relevance], [scores], k=500)

# precision@k by hand: slice the top k, count the hits
k = 500
top_k = np.argsort(scores)[::-1][:k]
precision_at_k = y_true[top_k].sum() / k
The offline-online gap, previewed Offline ranking metrics correlate WEAKLY with online clicks - a list that scores better on NDCG does not reliably earn more engagement from real users. The A/B test remains the truth; the offline metric is the filter that decides what is worth testing. b9 names this the two-hop proxy chain and builds the governance around it.
Self-studyReading NDCG without fear3 min read▶

NDCG intimidates people because the formula has three moving parts. Read them one at a time and it is friendly:

  • Graded relevance. Items are not just relevant/irrelevant - a 3 (perfect) beats a 1 (marginal). Binary labels still work; grades are the upgrade.
  • The log2 rank discount. Position 1 counts full weight, position 2 divided by log2(3), and so on - relevance buried at rank 40 is worth almost nothing. The discount encodes the belief that users read from the top.
  • The N is normalization. The score is divided by the best achievable ordering of the same items, so 1.0 always means "the ideal ranking" and scores compare across queries.

Two reading rules: an NDCG without a stated k is a whole-list score (rarely what the business does), and an NDCG compared across DIFFERENT datasets means little - the normalizer changes with the items. Compare rankers on the same data, at your real k.

Part 3 · the 30-second chooser

Choosing in 30 seconds self-study

The whole session as a flowchart in prose. Say the target out loud, walk the branches, done.

Self-studyThe flowchart, in prose3 min read▶
  • Money target? Start with MAE and RMSE together, then pick by outlier pain: if one huge miss breaks something (inventory, capacity), lead with RMSE; if you want the honest typical miss, lead with MAE (or median absolute error when the tail is pure noise).
  • Comparing across scales? (stores, products, currencies) - MAPE, with a zero-guard: exclude or floor near-zero actuals first, and remember it penalizes over-forecast less.
  • Ranked list with a budget cutoff? - precision@k at YOUR k. The cutoff is a business fact, not a modeling choice.
  • Graded relevance, order matters everywhere? - NDCG (with k if only the top matters).

And in every branch: quote the number NEXT TO its dumb baseline (predict-the-mean, or a random ranking). A metric without a baseline is a mood.

Homework

Try it yourself - this week ◐ 20-30 min total

Source material

Official sources covered

This session covers the regression and ranking sections of sklearn's evaluation guide, with the doctrine that assigns each metric its job. This page covers:

✓sklearn regression metrics - mean_absolute_error, root_mean_squared_error, mean_absolute_percentage_error, r2_score, median_absolute_error, mean_pinball_lossPart 1 · the full opinion table
✓sklearn ranking metrics - ndcg_score, average_precision_scorePart 2 · step-wise AP noted · MAP@k honestly absent
✓sklearn "which scoring function" doctrine - mean/median/quantile/probabilityPart 1 · probability branch handled in b3
◐Lumen canon (learn-intro-ml) - order_value ~$74, right-skewed; R² ~0.45 modelThe running case · reused, never re-derived
Check yourself

Three questions before you go 🎯 ◐ 90 seconds

1 · Lumen forecasts order values to size warehouse capacity, where one huge miss breaks the packing line. RMSE or MAE?

RMSE's squaring is not a quirk, it is the opinion: disasters count extra. When one big miss hurts more than many small ones (inventory, capacity), that opinion matches the business. When it does not, MAE or the median tells the truer story.

2 · MAPE has two famous traps. Which pair is it?

Divide by an actual near zero and one row can dominate the whole average; and the same $ miss scores differently over- vs under-forecast. MAPE earns its keep on cross-scale comparison - guard the zeros and know the asymmetry.

3 · Marketing's budget covers the top 500 sessions on the list. When does precision@500 beat AUC as the metric to watch?

AUC scores the entire ordering, including the 90% of the list below the budget line. When action stops at k, precision@k is real converters per outreach dollar - the metric and the money are the same number. More information is not better when it is information about sessions you will never contact.

Practitioner session 4 cheat sheet · pin this

MAEThe typical miss in $. Robust, linear - a $10 and a $10,000 miss count proportionally, no extra alarm for disasters.
RMSEPunishes big misses - inventory and capacity problems. Outlier-sensitive: Lumen's right-skewed ~$74 orders let a few gift sets dominate it.
MAPEScale-free % across stores and products. Two traps: explodes near zero actuals, asymmetric (penalizes over-forecast less).
R²Variance explained; misleading alone. ~0.45 multi-feature is honest work on noisy humans. Always check residuals for structure.
The doctrinePredict a mean -> squared error · median -> absolute error · quantile -> pinball · probability -> proper scoring rules. Target first, metric second.
Baseline disciplineALWAYS report against predict-the-mean (or a random ranking). A metric without its dumb baseline is a mood.
precision@kOf the top k, how many are real? At a budget cutoff (Lumen's top 500) it IS the business metric, not a proxy.
NDCGGraded relevance, log2 rank discount, 0-1 vs the ideal order (Jarvelin & Kekalainen 2002). Offline it correlates weakly with clicks - A/B stays the truth (b9).