Part II — Regression · Final Chapter

Chapter 10 · Evaluating & Comparing Regression Models

Part II built six regressors; this chapter builds the judge. Error metrics computed by hand and read in business units, the residual audit that certifies validity, and cross-validation that replaces one lucky split with five honest ones — all concluding in a real benchmark where every model faces the same data, and the results overturn expectations.

📖 Chapter 10 of 33 ⏱ ~90 min read Mostly Core Stable 📝 Quiz · 30 MCQs

🎯 Learning Objectives

1. The Judgement Layer

Two chapters ago a decision tree "won" R² = 1.0 on the salary grid; last chapter a forest scored a humbler 0.97 and we called it healthier. This chapter makes that judgement systematic. Evaluation is not a report at the end of modelling — it is the specification of what "better" means, written before the models compete.

Three instruments, each answering a different question, none sufficient alone:

📝
Note: Everything here evaluates on data the model never trained on — the Chapter 3 discipline made non-negotiable. The benchmark in Section 7 uses Salary_Data.csv (30 employees, experience → salary) with the Chapter 4 split (1/3 test, seed 0), so every number you are about to see connects back to a chapter you have already read.

2. The Metric Toolkit: MAE, MSE, RMSE

Four residuals from a fitted model: e = 3, −4, 2, −1 (salary misses in ₹000s). Every metric below is computed from exactly these four numbers:

MAE  =  Σ|ei| / n   =  (3+4+2+1)/4  =  2.50
MSE  =  Σei² / n   =  (9+16+4+1)/4  =  7.50
RMSE  =  √MSE   =  √7.50  ≈  2.74
  • MAE — the typical miss, in salary units. Every rupee of error counts one rupee.
  • MSE — squared units (₹000s²): mathematically convenient (differentiable — the loss OLS and gradient descent actually minimise) but not speakable to a manager.
  • RMSE — back in salary units, but still carrying the squaring's attitude: large misses count disproportionately.

That attitude is the whole MAE-vs-RMSE story. Watch the same trick with residuals 2, 2, 2, 2, 10 (real arithmetic):

MAE = (2+2+2+2+10)/5 = 3.60   ·   RMSE = √(116/5) = 4.82

One outlier among five ordinary errors: MAE rises meekly, RMSE screams. Neither is "correct" — they are two cost functions. If a ₹20,000 miss is just four times worse than a ₹5,000 miss, your metric is MAE. If it is catastrophically worse — a missed delivery prediction breaks a contract, a missed fraud threshold costs the transaction — your metric is RMSE. The business decides the metric; the metric then decides the model.

💡
Pro Tip — RMSE ≥ MAE, always. And the gap between them is a free outlier detector: RMSE ≈ MAE means errors are evenly sized; RMSE ≫ MAE means a few large misses dominate — stop and find them before trusting any average.

3. RMSLE & When Metrics Disagree

Some errors should be judged relatively. A ₹10 miss on a ₹20 salary prediction is disastrous; the same ₹10 miss on ₹1,00,000 is noise. RMSLE scores errors in log space, making them proportional:

RMSLE  =  √[ Σ(ln(1+ŷi) − ln(1+yi))² / n ]
Real mini-example: predictions off by exactly the same amount — ₹100→₹110 (+10) and ₹20→₹10 (−10) — score RMSLE 0.46, dominated almost entirely by the small-scale error. Under MAE both misses are identical (5.0); under RMSLE the 50% miss swamps the 10% miss.

When to reach for which — a decision table, not a ranking:

MetricUnitsAttitude to outliersReach for it when…
MAEtarget unitslinear — each rupee counts onceyou want the honest "typical miss"; errors are uniformly costly
MSEsquared unitsharshoptimisation and math (it is the loss being minimised); comparing models on the same data
RMSEtarget unitsharsh, but speakablelarge misses cost disproportionately — the default business report
RMSLElog-relativepunishes relative missestargets span orders of magnitude (demand 10→10,000; prices; salaries across bands)
R²share of varianceimplicitcommunicating "how much of the variation is explained" — never alone (Section 4)
🌎
Real World: Forecasting competitions (demand, sales) live on RMSLE because SKU volumes range from tens to millions. Salary data spans ₹38k–₹122k — one order of magnitude — so RMSE is the natural choice for Section 7's benchmark, and this chapter uses it as the headline metric with MAE alongside as the outlier check.

4. R² and Adjusted R², Honestly

Chapter 4 derived R² = 1 − SSE/SST: the share of variation explained. Three chapters of experience now let us state its rules of honest use:

  1. Which set? R² must be computed on held-out data to mean anything about generalisation. In-sample R² mechanically rewards complexity — the memorising tree's 1.0 was this trap in full costume. Test R², by contrast, is a genuine out-of-sample variance share.
  2. Which units? R² carries none. A model can explain 97% of variance and still miss by ₹37,000 (Section 7 shows exactly this). Pair every R² with an RMSE: share and size.
  3. How many features? On training data, adding features never lowers R² (Chapter 5); adjusted R² charges the rent: 1 − (1−R²)(n−1)/(n−p−1). Use it when comparing models of different sizes in-sample; on a test set, plain R² already contains the honesty.
⚠
Warning: Test R² can be negative — and a negative test R² is a legitimate, informative result: the model is worse than predicting the training mean. Section 7's benchmark contains one, and it is not a bug.

5. The Residual Audit Checklist

Metrics score a model; diagnostics certify it. The audit from Chapter 4 generalises to every regressor in Part II (tree steps, kernel bumps and ensemble averages all produce residuals), and it takes four plots:

#PlotAssumption on trialConvicted byStandard response
1Residuals vs fittedLinearity & equal varianceCurve (non-linearity); funnel (heteroscedasticity)Transform (log y); richer model family
2Q–Q plot of residualsNormality of errorsTails bending off the diagonalTransform y; collect more data; robust intervals
3Residuals vs observation orderIndependenceWaves or runs (autocorrelation)Time-series structure; add trend/season features
4Residual magnitude vs leverage / outlier fencesNo single point steering the fitExtreme Cook's distance; |z| > 3 residualsInvestigate the row; correct or justify — never silently delete
# The audit, four lines at a time
residuals = y_test - model.predict(X_test)
plt.scatter(model.predict(X_test), residuals); plt.axhline(0, ls='--', c='grey')
plt.xlabel('Fitted'); plt.ylabel('Residual'); plt.title('1·3 audit: curve? funnel?')
stats.probplot(residuals, plot=plt)                    # 2: normality
plt.plot(residuals, marker='o')                        # 3: order pattern?

Listing 10.1 — The audit runs on test residuals where available; training residuals when data is scarce.

The audit's verdict is qualitative by design — it certifies that the quantitative metrics above are measuring something real. A benchmark winner with a funnel-shaped residual plot has not won; it has merely averaged its pathology politely.

📋 Stable content — Reviewed: August 2026

6. Cross-Validation for Regression

Chapter 4's test set held ten rows; a lucky or unlucky draw moves a small-set score by points. k-fold cross-validation spends the same data five times smarter: shuffle, split into k = 5 folds, train on four, test on one, rotate — every row is tested exactly once, on a model that never saw it:

fold → run 1run 2 run 3run 4run 5 amber = test fold · green = training folds — each row tests once; report mean ± SD of the five scores
Figure 10.1 — Five-fold cross-validation: five small experiments on one dataset.
from sklearn.model_selection import cross_val_score, KFold
from sklearn.pipeline import Pipeline

cv = KFold(n_splits=5, shuffle=True, random_state=42)

# Pipeline = leakage-proof CV: scalers refit inside every fold
model = Pipeline([('sx', StandardScaler()),
                  ('m', SVR(kernel='rbf', C=10))])
scores = cross_val_score(model, X, y, cv=cv, scoring='r2')
print(f'{scores.mean():.3f} ± {scores.std():.3f}')   # report BOTH numbers

Listing 10.2 — The CV loop. Without the pipeline, the scaler fitted on all folds' data would leak — Chapter 3's rule, now per fold.

The report is mean ± SD: the mean estimates performance, the SD estimates how much that estimate wobbles. A model at 0.92 ± 0.05 and one at 0.90 ± 0.02 are not ranked by their means alone — the second is more predictable, which production teams value. Full treatment of tuning with CV is Chapter 31; here CV is the benchmark's honesty device.

7. The Benchmark: Six Models, One Dataset

The rules: Salary_Data.csv (30 employees), the Chapter 4 split (20 train / 10 test, seed 0), one shared metric suite, no per-model tuning beyond the configurations earlier chapters established. Every number below is real.

7.1 Held-out test results

Model (Ch)MAE (₹)RMSE (₹)R² testR² trainGap (train − test)
Linear (4)3,4264,5850.97490.938−0.037
Poly deg-2 (6)3,5634,6430.97430.939−0.035
Poly deg-4 (6)4,2154,7050.97360.950−0.024
SVR default (7)30,72737,087−0.641−0.114+0.53
SVR C=10 (7)30,68937,040−0.637−0.110+0.53
Tree (8)6,8408,6850.9100.996+0.086
Forest, 100 (9)5,3556,6380.9470.986+0.039

Five readings, each one a chapter's lesson made numerical:

7.2 Cross-validated stability (5 folds, all 30 rows)

ModelLinearPoly d2Poly d4SVR C=10TreeForest 100
CV R² (mean ± SD)0.922 ± 0.0500.914 ± 0.0520.924 ± 0.051−0.61 ± 0.550.878 ± 0.0880.917 ± 0.068

CV confirms the test table and adds nuance: the linear and degree-4 models are statistically inseparable (0.92 ± 0.05 both) — a tie broken, correctly, by simplicity. The tree is the wobbliest winner-adjacent model (± 0.088); the forest, as promised, sits between. And SVR's ± 0.55 says it all: not just bad but erratically bad.

7.3 Can SVR be rescued? Yes — at a price

A grid over C, γ and ε (108 configurations) finds C=10, γ=0.05, ε=0.01 with test R² 0.9794 — the best single score in this whole chapter. Two honest caveats attach to that number. First, it took 108 attempts to beat the linear model by 0.005 — effort the linear model spent on nothing. Second — the deeper lesson — the grid was scored on the test set, so 0.9794 is an optimised, optimistic statistic: the test set has now been consulted 108 times, and Chapter 3's "touch it once" rule lies in pieces. The correct protocol — tune inside cross-validation on training data only, then one final test evaluation — is exactly Chapter 31's subject. The benchmark keeps its honest winner.

🌎
Real World: This pattern — baseline embarrasses the sophisticated model until heavy tuning edges it out, at the cost of interpretability and tuning risk — is among the most common findings in applied ML. Strong simple baselines are not beginner training wheels; they are the professional's first move.

8. Choosing a Model: The Decision Guide

The benchmark's deepest result is procedural, not numerical. Models are chosen by the question being asked, under the evidence the metrics and audits provide:

If the priority is…Reach forBecause
Explaining to humans ("which factors, how much")Linear / MLR (Ch 4–5)Coefficients in business units; inference (p-values, VIF); the audit suite applies cleanly
Gentle, visible curvaturePoly deg 2–3 (Ch 6)Transparent equation that bends; keep degree low, ban extrapolation
Nonlinear, noisy, medium-size dataSVR (Ch 7)Tube noise-immunity + kernel flexibility — after systematic tuning
Auditability by non-experts (rules, compliance)Tree (Ch 8)A flowchart you can read aloud; control depth against memorisation
Best out-of-the-box accuracy on tabular dataForest (Ch 9)Variance-cancelling default; OOB gives a free honesty check
Maximum accuracy, tuning budget availableBoosting (Ch 32 preview)Sequential residual-chasing; the other ensemble family

And the reporting template, now complete — reuse it for the rest of this course:

💡
The final report template: (1) metrics on held-out data — RMSE and MAE in target units, R² as the share; (2) stability — CV mean ± SD; (3) diagnostics verdict — the four-plot audit's findings; (4) recommendation with reason — including why the runner-up lost; (5) caveats — data range, extrapolation ban, tuning protocol. Five parts, one page, defensible in any review meeting.

Part II ends here — six builders and one judge. Part III changes the question: not "how much?" but "which one?" — predicting categories instead of numbers, starting with logistic regression, where a regression's skeleton learns to classify.

🛠 Hands-On: Run Your Own BenchmarkIntermediate

A rival team claims forests dominate everything. Run the full benchmark on Salary_Data.csv yourself — metrics, gaps, CV — audit the winner's residuals, and deliver the five-part recommendation.

Steps

  1. Split 1/3 test, seed 0. Fit linear, poly-2, poly-4, SVR (default and C=10), tree, forest-100. One shared metrics function.
  2. Produce the table: MAE, RMSE, R² test, R² train, and the train−test gap per model.
  3. Which model wins on RMSE? Compute the MAE–RMSE ratio for the winner and interpret it (outlier check).
  4. Run 5-fold CV (shuffle, seed 42) for all models — in pipelines where scaling is involved. Report mean ± SD.
  5. Audit the winner's test residuals (four plots). Verdict?
  6. Write the five-part recommendation. Would a tuned SVR change it — and what protocol would that tuning need?
View Solution / Walkthrough
import numpy as np, pandas as pd
from sklearn.model_selection import train_test_split, cross_val_score, KFold
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures, StandardScaler
from sklearn.svm import SVR
from sklearn.tree import DecisionTreeRegressor
from sklearn.ensemble import RandomForestRegressor
from sklearn.pipeline import Pipeline
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score

ds = pd.read_csv('Salary_Data.csv')
X = ds.iloc[:, :-1].values.astype(float)
y = ds.iloc[:, -1].values.astype(float)
X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=1/3, random_state=0)

models = {
    'Linear'  : LinearRegression(),
    'Poly d2' : Pipeline([('p', PolynomialFeatures(2, include_bias=False)), ('m', LinearRegression())]),
    'Poly d4' : Pipeline([('p', PolynomialFeatures(4, include_bias=False)), ('m', LinearRegression())]),
    'SVR'     : Pipeline([('s', StandardScaler()), ('m', SVR(kernel='rbf'))]),
    'SVR C=10': Pipeline([('s', StandardScaler()), ('m', SVR(kernel='rbf', C=10))]),
    'Tree'    : DecisionTreeRegressor(random_state=0),
    'Forest'  : RandomForestRegressor(n_estimators=100, random_state=0),
}

for name, m in models.items():
    m.fit(X_tr, y_tr)
    yp = m.predict(X_te)
    print('%-9s MAE=%6.0f RMSE=%6.0f R2test=%7.4f R2train=%7.4f' %
          (name, mean_absolute_error(y_te, yp),
           np.sqrt(mean_squared_error(y_te, yp)),
           r2_score(y_te, yp), m.score(X_tr, y_tr)))

cv = KFold(n_splits=5, shuffle=True, random_state=42)
for name, m in models.items():
    s = cross_val_score(m, X, y, cv=cv, scoring='r2')
    print('%-9s CV %.3f ± %.3f' % (name, s.mean(), s.std()))

Expected results (real — Sections 7.1–7.2):

  • Winner: linear — MAE 3,426, RMSE 4,585, R²test 0.9749. The forest is a solid second (RMSE 6,638), but the claim "dominates everything" is refuted by the data's own linearity.
  • Gap column: tree +0.086 (memoriser), forest +0.039 (halved), linear −0.037 (small-sample luck, benign).
  • MAE–RMSE ratio (winner): 3,426/4,585 ≈ 0.75 — no dominant outlier; the miss profile is evenly spread.
  • CV: linear 0.922 ± 0.050 vs poly-4 0.924 ± 0.051 — a statistical tie; SVR −0.61 ± 0.55; tree wobbliest (± 0.088).
  • Audit: the linear winner's residuals show no curve, no funnel (salary data is well-behaved), Q–Q near-diagonal — the win is certified, not just scored.
  • Recommendation: five parts per the template, landing on linear + forest as the deployment pair (champion + challenger); tuned SVR (0.9794 on this test set) noted as optimistic unless re-run under the Chapter 31 protocol.

🎯 Key Takeaways

1

Metrics are cost functions: MAE counts every rupee once, RMSE punishes the big miss. The business's error costs choose the metric before any model runs.

2

RMSE ≥ MAE always — and the gap between them is a free outlier detector for any model's error profile.

3

R² is a share, not a size: pair it with RMSE, compute it on held-out data, and remember negative test R² means "worse than the mean" — informative, not broken.

4

Four plots certify any regression: residuals-vs-fitted, Q–Q, vs-order, leverage. A benchmark winner with a funnel has averaged its pathology, not fixed it.

5

k-fold CV replaces one lucky split with mean ± SD — and pipelines keep the folds leakage-free. Ties between equal scores break toward simplicity.

6

The real benchmark's verdict: untuned linear regression beat five fancier models, and a tuned SVR's win came from consulting the test set 108 times. Simple baselines are the professional's first move.

Test Your Understanding

30 questions across the chapter — metric computations and choices, honest R², the residual audit, cross-validation, and the real benchmark. Choose one answer per question, then check your answers.

Q1.This chapter exists because, left to in-sample scores:

Q2.MAE is defined as:

Q3.(Hand) Residuals 3, −4, 2, −1. The MAE is:

Q4.(Hand) The same residuals give MSE:

Q5.(Hand) The RMSE of those residuals is ≈ 2.74, which is:

Q6.Residuals 2, 2, 2, 2, 10 give (real) MAE = 3.60 and RMSE ≈ 4.82. The divergence shows:

Q7.Your business treats a ₹20,000 miss as merely four times a ₹5,000 miss. The matching metric is:

Q8.MSE, despite its awkward units, survives everywhere because:

Q9.RMSLE exists for problems where errors should be judged:

Q10.(Real mini-example) Predictions 100→110 and 20→10 — equal absolute misses — score RMSLE 0.46. The reading:

Q11.A missed prediction breaks a contract and costs a fortune, while ordinary misses are routine. Choose:

Q12.A test R² of 0.95 states that:

Q13.In-sample R² "lies" because it:

Q14.Adjusted R² exists to:

Q15.The pairing rule for reporting is:

Q16.The first and most information-dense audit plot is:

Q17.A funnel in the residuals-vs-fitted plot convicts the model of:

Q18.In the audit, the Q–Q plot tests:

Q19.Plotting residuals against observation order is the check for:

Q20.A single 10-row test split is fragile because:

Q21.In k-fold cross-validation:

Q22.The professional CV report is:

Q23.Inside CV, scaling must be wrapped in a Pipeline because:

Q24.(Real benchmark) Among the untuned models on Salary_Data's test set, the winner was:

Q25.(Real) The tree scored R² 0.996 on train but 0.910 on test; the forest 0.986 vs 0.947. The diagnosis:

Q26.(Real) SVR scored test R² ≈ −0.64 on this dataset, yet a 108-config grid found one at 0.9794. The honest reading:

Q27.The stakeholder asks "which features matter, and how much each?". The decision guide says:

Q28.Two models tie on CV (0.922 ± 0.050 vs 0.924 ± 0.051). The professional choice:

Q29.The five-part report template ends with:

Q30.Part III, starting next chapter, changes the question from "how much?" to: