Machine Learning·Capstone·20 min·0/3 exercises

Capstone: a regression project

Predict the number, diagnose the residuals, report in minutes

RidgeGridSearchCVresidualsMAEreporting in units

Watch it happen

Play it through, or step back and forth yourself.

1
Choose the targetminutes, not late
lesson 14 — keep the information
2
Splitno stratify — it is a number
lesson 3
3
BaselineDummyRegressor → R² 0
lesson 5
4
TuneRidgeCV / GridSearchCV on alpha
lessons 15 and 23
5
Diagnoseresiduals vs predictions
lesson 17
6
Report in unitsMAE 3.09 min · 80% within 5
lesson 17

The same data, the more useful target. Predicting minutes keeps everything the 30-minute threshold discarded — and it lets you report in a unit anybody understands.

The idea

Same data, better target. late was always minutes > 30 — somebody's threshold — so predicting the number keeps what the threshold discarded, and you can apply any cut you like afterwards.

It also lets you report in minutes, which is a unit the stall owner already thinks in. That matters more than it sounds.

1. Split — and note what changes

X_train, X_test, y_train, y_test = train_test_split(
    X, deliveries["minutes"], test_size=0.25, random_state=0)

No stratify: the target is continuous, so there are no classes to balance. (If you needed it — a very skewed target, say — you'd bin the target and stratify on the bins.)

2. Baseline

DummyRegressor(strategy="mean") scores R² of essentially 0 by construction, which is R²'s quiet advantage: the baseline is built into the scale, so you don't have to fit one to read the number. Do it anyway for the MAE, which is 4.91 minutes — that's what "no model" costs you.

3. Pipeline and search

pipe = Pipeline([("pre", pre), ("m", Ridge())])

GridSearchCV(pipe, {"m__alpha": np.logspace(-2, 3, 12)},
             cv=5, scoring="r2").fit(X_train, y_train)

best alpha    5.337
cv R²         0.5871

Ridge rather than plain LinearRegression because the penalty costs nothing when it isn't needed and protects you when it is (lesson 15). Log scale on alpha, because its effect is multiplicative (lesson 23).

Module 4 already established the important negative results here: polynomial features make it worse, and a 200-tree forest scores 0.5198 against linear's 0.5785. This process really is a weighted sum, and it's worth having checked.

4. Diagnose before you report

resid = y_test - model.predict(X_test)

residual mean    −0.0236        centred
worst miss        9.93 minutes
within 5 min      80.0%

Plot residuals against predictions and you get a flat, shapeless band — no curve, no fan, no drift (lesson 17). The straight-line assumption holds, which independently corroborates why the forest couldn't beat it.

Do this before you report the score. A good R² with a curved residual plot means you have a model that works on average and fails systematically somewhere specific, and the average will not tell you where.

5. Report in units people use

TEST R²      0.5967
TEST MAE     3.087 minutes
within 5 min 80.0%

Three numbers doing three different jobs:

  • — for a technical reader, with its baseline built in.
  • MAE — the typical miss, in minutes.
  • "80% within five minutes" — the one for the stall owner. It's a promise they can check against reality tomorrow, and it converts a model score into an operational commitment.

That third form is worth constructing deliberately for every regression project. Pick a tolerance that matters to the business and report the fraction inside it.

6. And connect it back

You can recover the classification decision from the regression at any threshold: predicted_minutes > 30. That's often the better architecture — one model, one number, and the operational cut kept as a separate, adjustable decision rather than baked in at training time.

It also means changing the promise from 30 minutes to 25 requires no retraining at all.

The checklist

  1. Predict the number if you have it; you can always threshold later.
  2. Split (no stratify), baseline, pipeline.
  3. Tune alpha on a log scale by cross-validation.
  4. Plot the residuals before you believe the score.
  5. Report R², an error in real units, and a tolerance fraction.

See it run

The lesson's code, ready to run and to fiddle with.

Putting the kettle on…

Starting up…

Worked example

not graded

Already written and ready to go — press Run to see what it does, then change a number, a column name, anything, and run it again.

tryreporting "within 3 minutes" instead of 5 — a tighter promise, and a smaller fraction.

Press Run — the output appears here.

Your turn

3 exercises. Write the code yourself, then press Check — a nudge and the answer are there if you want them.

Tune ridge over np.logspace(-2, 3, 12) with scoring="r2" and return [best_alpha, test_R2, test_MAE], rounded to 3, 4 and 3 places.

your answer

The number for the stall owner. Return the fraction of test predictions within 5 minutes of the truth, rounded to 4 places.

your answer

Diagnose before reporting. Return [residual_mean, worst_absolute] rounded to 4 and 2 places — the mean should be essentially zero.

your answer