Choosing a model is a systems decision
On this page
The series has now built a linear model, nearest neighbours, a tree, a forest and a boosted model on the same problem. This post puts them in one table and adds the columns that accuracy leaves out, because those columns decide which model gets deployed.
the chosen model or its predictionerror, or a cost
Five models, one table
Next month’s volatility from this month’s prices, fitted on 2019 to 2021, scored on 2022 to 2023, timed on one laptop core:
| Model | Test RMSE | Train | Predict 2,405 rows | One row, median | One row, worst 1 in 100 |
|---|---|---|---|---|---|
| Linear regression | 2 ms | 0.1 ms | 0.02 ms | 0.03 ms | |
| Nearest neighbours, = 100 | 7.50 | 1 ms | 45 ms | 0.16 ms | 0.44 ms |
| One tree, 200 rows per leaf | 8.41 | 5 ms | 0.1 ms | 0.03 ms | 0.03 ms |
| Random forest, 300 trees | 9.28 | 1,503 ms | 28 ms | ms | 5.94 ms |
| Gradient boosting, 200 trees | 9.30 | 695 ms | 1.8 ms | 0.08 ms | 0.08 ms |
| Predict the training mean | 9.84 |
Read it column by column.
Accuracy. The spread is one volatility point, and the two simplest models win. On this problem the signal is weak and nearly linear, so the models with the least room to fit noise did best. That is not a general law; on the stress-month classification in the forests post the ordering ran the other way. It is a reminder that the accuracy column has to be measured, not assumed from the model’s reputation.
Training time. The forest took 1.5 seconds, 750 times the linear model. That only matters against a budget: a 40-minute fit inside a four-hour overnight window is free; the same fit every time a feature changes is not.
Prediction time, and its tail. The linear model answers a question in 20 microseconds; the forest in 5.7 milliseconds, because every prediction walks 300 trees. Nearest neighbours is cheap to train and the only model whose prediction cost grows with the data, because it scans every stored row. The last column is the one a trading deadline cares about: not the average latency but the worst one in a hundred. A mean of 2 ms inside a 10 ms deadline with a 99th percentile of 40 ms means one order in a hundred misses, and the misses cluster in the busy moments.
What it keeps. Nearest neighbours keeps every training row forever, which is a memory cost and sometimes a privacy or licensing one. The others keep parameters: seven numbers for the linear model, 15 leaves for the tree, 27,193 leaves for the forest.
Interpretability. The linear model’s seven coefficients can be read aloud: more 20-day volatility means more next-month volatility, at 573 points per unit. The tree with 15 leaves can be drawn on a page. The forest cannot, and a model that cannot be explained cannot be audited, which in some settings makes it unusable whatever its accuracy. Partial-dependence curves and importance scores recover some of it, at a cost.
Extrapolation: the question a test set never asks
The test months all lie within the range of the training months. Ask each model about a month twice as volatile as any it has seen, with the recent-volatility features doubled from the wildest training row:
| Model | Predicted next-month volatility |
|---|---|
| Linear regression | |
| Nearest neighbours | 61 |
| One tree | 62 |
| Random forest | 63 |
| Gradient boosting | 72 |
| Highest next-month volatility ever seen in training | 68 |
Every tree-based model and the neighbour model answer with roughly the most extreme value they have ever seen, because a tree’s leaf is a constant and a neighbour’s answer is an average of stored rows. The linear model follows its slope off the edge of the map. Neither is right; the point is that they fail differently, and that a model’s behaviour outside its training range is a design choice, not something a test score reveals. Finance meets the outside of the range on the worst day of the decade.
Stability
A linear model refitted on slightly different data gives nearly the same line. A single tree can flip its root split and everything below it; that instability is why the bagging post exists. A forest is stable again by averaging. Stability across random seeds is cheap to measure and worth reporting next to the score; it is not the same as robustness to a change of regime, which only forward testing shows.
The comparison table
| Item | Linear | Nearest neighbours | Tree | Forest or boosting |
|---|---|---|---|---|
| Shape it can capture | straight lines, or curves you add by hand | any local shape | thresholds and interactions | the same, smoother |
| Training cost | low | none | low | high |
| Cost per prediction | , grows with the data | depth | trees × depth | |
| Keeps the data | no | all of it | no | no |
| Needs scaling | for regularisation | yes | no | no |
| Outside the range | extrapolates the slope | the nearest stored rows | a constant | a constant |
| Explainable | coefficients | “like these past cases” | a drawing, if shallow | only with extra tools |
| Stable | yes | fairly | no | yes |
Worked costs: a table of one million rows and twenty features. One nearest-neighbour prediction touches twenty million numbers; one linear prediction touches twenty; a depth-12 tree makes twelve comparisons; a 500-tree forest of them makes six thousand.
How to decide
- Write down the constraints before the accuracy: latency budget and its tail, how often the model refits, whether a human must be able to explain a prediction, how much data there is per feature.
- Measure the baseline, then the simplest model that fits the constraints. Linear first.
- Move up only when the gain is bigger than the noise between seeds and worth its extra cost.
- Check the behaviour outside the training range on purpose.
- Prefer the model whose failures you understand.
There is no best model for every dataset. There is a best model for this dataset under these constraints, and the constraints are most of the decision.
Traps
| Trap | Truth |
|---|---|
| Pick the lowest-error model | Weigh every column |
| Explainable means reliable | It means auditable |
| Mean latency within budget means safe | The tail breaks deadlines |
| More history always helps | Only if it is still relevant |
| A more flexible model extrapolates better | It usually extrapolates a constant |
| An ensemble fixes a bad input | Nothing fixes a bad input |
The code
import time
import numpy as np
for name, model in models.items():
t0 = time.perf_counter(); model.fit(X_train, y_train); fit = time.perf_counter() - t0
singles = []
for row in X_test[:200]:
t0 = time.perf_counter(); model.predict(row[None, :]); singles.append(time.perf_counter() - t0)
p50, p99 = np.percentile(singles, [50, 99]) # the median and the tail
extreme = X_train[np.argmax(X_train[:, 4])].copy(); extreme[3:5] *= 2 # off the edge of the map
model.predict(extreme[None, :])
Summary
| Column | Why it is in the table |
|---|---|
| Accuracy | measured against the baseline, with its spread across seeds |
| Training time | against the refit budget |
| Prediction time, median and tail | against the deadline, and its worst case |
| Keeps the data | memory, privacy, growth with |
| Explainable | audits and regulators |
| Outside the range | a design choice the test set cannot see |
| Stable | across seeds; and separately, across regimes |
References
- James, Witten, Hastie, Tibshirani and Taylor, An Introduction to Statistical Learning, with Applications in Python (Springer, 2023), section 2.1.3: the trade-off between flexibility and interpretability
- Kevin P. Murphy, Probabilistic Machine Learning: An Introduction (MIT Press, 2022), section 1.2.4: no free lunch
- scikit-learn: computational performance, prediction latency and throughput