Train 0.11, Test 0.24: The Whiteboard Arithmetic That Diagnoses Your Model
Interviewers write two numbers and wait. Model A: train error 0.11, test error 0.13. Model B: train 0.02, test 0.31. Model C: train 0.18, test 0.47. Thirty seconds of arithmetic separates candidates who memorized slogans from candidates who have trained models.
The decomposition you are approximating: under squared loss, expected test error = bias-squared + variance + noise. Bias-squared is how far the model class sits from the true function even with infinite data; variance is how much the fitted function moves when the training sample changes; noise is irreducible label randomness. Train and test errors are not those terms exactly - training error is measured on data the model has seen, so it flatters both - but the pair gives usable estimates:
| Model | Train | Test | Gap (approx variance) | Level (approx bias + noise) | Verdict |
|---|---|---|---|---|---|
| A | 0.11 | 0.13 | 0.02 | 0.11 | underfit: high bias, low variance |
| B | 0.02 | 0.31 | 0.29 | 0.02 | overfit: low bias, high variance |
| C | 0.18 | 0.47 | 0.29 | 0.18 | both - check the pipeline first |
Model A cannot even fit the data it has seen: more regularization or a fresh split will not rescue it, it is too simple for the signal or the features do not carry the target. Model B has near-perfect training fit and the world disagrees - textbook variance. Model C is bad on both counts, which usually points at the pipeline itself: leaky features, mismatched preprocessing between train and test, or a drifting distribution, all worth auditing before any architecture work.
The first intervention depends on which column is bad:
| Diagnosis | First move | Why it helps |
|---|---|---|
| High bias (small gap, high train error) | bigger model, richer features, longer training, weaker regularization | capacity to fit the signal |
| High variance (large gap, low train error) | more data, weight decay, dropout, early stopping, simpler model, ensembles | less sensitivity to the sample draw |
| Both | fix labels, features, and target definition; check train-test parity | model surgery cannot rescue a mis-specified problem |
A notebook-sized diagnostic you can paste anywhere:
def diagnose(train_err: float, test_err: float) -> str:
gap = test_err - train_err
if gap > max(0.05, 2 * train_err):
return f"variance-led (gap {gap:.2f}): regularize or add data"
if train_err > 0.15:
return f"bias-led (train {train_err:.2f}): add capacity or features"
return f"healthy (gap {gap:.2f}, train {train_err:.2f}): tune and verify"
Two caveats keep you honest. A large gap is not uniquely overfitting - distribution shift produces the same signature, so re-run evaluation on a time-ordered split: if the gap grows toward the present, you are looking at drift, not variance. And error rate hides confidence: a classifier can be right on 92% of rows while assigning 0.51 to the ones it gets right, so track cross-entropy alongside accuracy; the gap is variance, the level is bias, and the split strategy you used decides how much to trust both. The fold standard deviation you produced in Cross-Validation Strategies measures spread across splits, which is a cousin of the spread across samples measured here. And one final discipline: record both numbers, not just the test error. A test error of 0.13 alone tells you nothing about the intervention; with the training error beside it, the next experiment writes itself.
The PM Asks Whether 5x More Labels Will Help: Reading Learning Curves
Roadmap review: we can buy five times more labeled data next quarter - will accuracy go up? The answer you give with a learning curve beats the one you give with a hunch, and the curve takes ten minutes to build.
A learning curve plots train and validation performance as the training set grows: fit the full pipeline on 10%, 25%, 50%, 75%, and 100% of the data, evaluate each on held-out folds, and watch two lines plus the gap between them.
from sklearn.model_selection import learning_curve
sizes, tr, va = learning_curve(
GradientBoostingClassifier(n_estimators=200), X, y,
train_sizes=[0.1, 0.25, 0.5, 0.75, 1.0], cv=5,
scoring="neg_log_loss", n_jobs=-1)
for s, t, v in zip(sizes, tr.mean(1), va.mean(1)):
print(f"{s:5d} train {-t:.3f} val {-v:.3f} gap {v - t:.4f}")
# 600 train 0.214 val 0.331 gap 0.117
# 1500 train 0.221 val 0.298 gap 0.077
# 3000 train 0.226 val 0.271 gap 0.045
# 4500 train 0.229 val 0.259 gap 0.030
# 6000 train 0.231 val 0.252 gap 0.021
Those five rows answer the question: training error barely moves because the model already fits, validation error keeps falling, and the gap is still closing at full size. Data is the binding constraint, and five times more labels should buy roughly another 0.02 to 0.04 on this metric. Run the same experiment on a different problem and the picture inverts:
| Train curve | Val curve | Reading | Next move |
|---|---|---|---|
| High, flat | High, flat, close to train | bias - data is not the problem | richer features, bigger model, weaker regularization |
| Low, flat | High but falling toward train as n grows | variance, data-limited | buy the labels and extrapolate the val curve |
| Low, flat | High, flat, gap not closing | variance that data will not fix | regularization, simpler model, ensembling |
| Low | Low | converged - or leaked features | verify there is no target leakage before celebrating |
Three details make the curve trustworthy. Plot a loss rather than accuracy: accuracy saturates and hides improvement the model is still making, while log loss keeps moving. Compute each point inside the same fold scheme you chose in Cross-Validation Strategies, otherwise every point is a single noisy split and the curve wiggles for reasons that have nothing to do with data size. And extrapolate honestly - the curve assumes new rows come from the same distribution at the same label quality, and five times more data from a different annotator pool can bend it back up.
Carry the curve into the decision as arithmetic: if the validation loss is still dropping about 0.015 per doubling of data and the roadmap needs 0.05, then two more doublings (4x the labels) roughly close the gap, and you can quote that against the price per label. If the curve has flattened by 0.002 per doubling, five times the data buys about 0.005, and the honest recommendation is to spend the budget on features or labels quality rather than label volume.
The same experiment with model size on the x-axis gives the capacity curve: a U shape where the left arm says grow the model and the right arm says shrink it, with double descent as the reminder that past a critical width test error can fall again. Either axis works; the discipline is choosing by validation evidence rather than folklore. The next problem is that your curves are made of runs, and runs are what Experiment Tracking Tools exist to organize.
Sample 100 Errors, Tag Them, Rank the Ceilings: Turning a Gap Into a Work List
Your sentiment model sits at 91.0% and the roadmap asks for 95%. The weak answer is try a larger model, which guesses. The strong answer is an error analysis that produces a ranked table in a week.
Pull a random sample of 100 misclassified validation examples - random, not the most dramatic ones, because cherry-picked errors inflate the exotic categories. Write a taxonomy of six to eight buckets before tagging, tag every example, then have a second reviewer tag 30 of them so you can check that two people using the taxonomy agree.
Suppose the counts come back like this:
| Category | Share of errors | Ceiling gain | Cheap fix? |
|---|---|---|---|
| Gold labels wrong | 24% | 2.2 pts | relabel pass |
| Product names missing from vocab | 19% | 1.7 pts | normalize to canonical ids |
| Negation spans mishandled | 17% | 1.5 pts | negation features or targeted data |
| Genuinely ambiguous, two valid labels | 15% | - | no: agreement ceiling near 92% |
| Sarcasm | 11% | 1.0 pt | hard, needs world knowledge |
| Everything else | 14% | 1.3 pts | investigate individually |
The arithmetic that makes this table actionable: category share times current error rate equals the maximum points available if you solved that category perfectly. At a 9% error rate, a bucket holding 24% of errors is worth 0.24 x 9 about 2.2 points - the entire roadmap target needs two such buckets, not a new architecture. Buckets with no ceiling cap everyone: if 15% of errors are genuinely double-labelable, 100% is unreachable and 95% may already sit near the label-agreement limit.
A confusion matrix routes you to the same list from the class side:
| actual down / predicted right | positive | negative | neutral |
|---|---|---|---|
| positive | 402 | 21 | 55 |
| negative | 18 | 388 | 44 |
| neutral | 31 | 27 | 402 |
Neutral recall is 0.81 versus 0.90 for negative: the mass sits in neutral, so that is where the fixes belong. The matrix also exposes asymmetric costs you may not have chosen deliberately: positive predicted as neutral (55) versus neutral predicted as positive (31). If a missed positive costs five times a false alarm, those two cells deserve different thresholds, which is the same policy question a stakeholder will eventually ask you.
Also check whether some errors are policy rather than learning - sweep the decision threshold before touching the model:
import numpy as np
from sklearn.metrics import f1_score
for t in np.arange(0.30, 0.80, 0.05):
print(f"threshold {t:.2f} f1 {f1_score(y_val, proba >= t):.3f}")
If a threshold move recovers half the gap, that is a one-line change with no retraining and no new data.
Close the loop: apply the top fix, re-run on the same folds so the comparison stays paired, and record the delta as its own run in Experiment Tracking Tools where the config and data version travel with it. Those buckets then become the known-limitations section of the artifact you will assemble next - interviewers remember candidates who answer how would you get to 95%? with sampling, tagging, and ceilings instead of model names.
Quiz
1. A model reports train error 0.02 and test error 0.31. What is the most accurate first diagnosis?
2. Train error 0.11, test error 0.13 after cross-validation. Which action has the highest expected value?
3. Your learning curve shows training loss flat at 0.23 while validation loss falls from 0.33 to 0.25 as data grows and the gap keeps closing. What do you conclude?
4. Your model errors at 9%, and error analysis shows 24% of those errors are mislabeled gold examples. What is the ceiling gain from fixing the labels?
Flashcards
Question
How do you approximate bias and variance from a train/test error pair?
Click to reveal answer
Answer
The gap (test minus train) approximates variance - sensitivity to the training sample - while the training error level approximates bias plus irreducible noise, since a model that cannot fit seen data is limited by capacity or features. Train error is flattering, so treat both as rough estimates, not exact terms.
Question
What pattern in the two error curves signals underfitting, and what is the first move?
Click to reveal answer
Answer
Training error stays high and test error sits close to it - a small gap with a bad level - meaning the model class is too simple or features are weak. First move is more capacity, richer features, longer training, or weaker regularization; collecting more data will not help.
Question
What pattern signals overfitting, and what is the first move?
Click to reveal answer
Answer
Training error is very low while test error is much higher - a large gap - meaning the model memorizes the sample. First moves are more data, weight decay, dropout, early stopping, a simpler model, or ensembling.
Question
How do you read a learning curve to decide whether buying more labels is worth it?
Click to reveal answer
Answer
Plot train and validation loss against training-set size. If training loss is flat and low while validation loss keeps falling toward it as n grows, variance is data-limited and more labels will pay; if validation loss is flat and far from training, more data will not close the gap and regularization or capacity changes will.
Question
What is the ceiling arithmetic in error analysis, and why does it change roadmaps?
Click to reveal answer
Answer
Ceiling gain = share of errors in a category x current error rate. It bounds the points available if that category were solved perfectly, so a category holding 24% of a 9% error rate is worth at most about 2.2 points - enough to prove the target needs two buckets fixed, not a new model.
Revision Notes
Key Takeaways
- 1. The gap between test and train error approximates variance; the training error level approximates bias plus noise - the two columns demand opposite interventions
- 2. High training error with a tiny gap means underfitting: add capacity or features, because more data and more regularization both make it worse
- 3. A large gap can be overfitting or distribution shift - rerun with a time-ordered split to tell them apart before prescribing regularization
- 4. Learning curves decide the data question: flat training loss with validation still descending means more labels pay, while a flat gap means they will not
- 5. Error categories have ceilings: share times error rate bounds the gain, which turns "reach 95%" into a ranked work list instead of a model upgrade
Interview Tips
- • "Train 0.02, test 0.31 - what now?" - name the gap as variance, then list the variance toolkit (data, regularization, early stopping, ensembling) and add the shift caveat with a time-ordered re-split
- • "How would you improve accuracy from 91% to 95%?" - sample 100 errors, tag with a taxonomy, compute ceiling per bucket, fix the top one, and re-measure on the same folds; never open with "try a bigger model"
- • "How do you know whether collecting more data will help?" - describe the learning curve experiment: subsample fractions, fixed folds, a loss metric, and the gap trajectory at full size
- • "What if the curves look healthy but the metric still drops in production?" - raise distribution shift and drift monitoring, mention time-ordered evaluation, and say you would compare feature distributions between the eval window and live traffic
Cheat Sheet
Bias-Variance and Error Analysis
Two-number diagnosis:
| Train | Test | Gap | Level | Verdict | First move |
|---|---|---|---|---|---|
| 0.11 | 0.13 | 0.02 | 0.11 | underfit (bias) | capacity, features, less regularization |
| 0.02 | 0.31 | 0.29 | 0.02 | overfit (variance) | data, weight decay, dropout, early stop, simpler model |
| 0.18 | 0.47 | 0.29 | 0.18 | both | audit labels, features, train-test parity |
Decomposition: expected test error = bias^2 + variance + noise. Gap approximates variance; training level approximates bias + noise.
Learning curves (train and val vs training size):
- both high and close -> bias: more data will not help
- val falling toward train as n grows -> variance, data-limited: buy labels
- val flat and high -> variance data will not fix: regularize
- plot a loss (log loss), not accuracy; compute points inside the same k folds
Error analysis loop:
# ceiling = share_of_errors * error_rate
# 0.24 * 0.09 = 0.022 -> 2.2 points max from fixing mislabeled gold
for t in np.arange(0.30, 0.80, 0.05): # sweep threshold before retraining
print(t, f1_score(y_val, proba >= t))
Sample randomly, tag against a pre-written taxonomy, rank by ceiling, fix the top bucket, re-measure on identical folds.