Early Stopping and Learning Curves
In Lesson 3 you saw the validation error of a booster make a U: too few trees and it underfits, too many and it overfits. That U is the single most useful picture in boosting, and this lesson is about reading it.
Sam runs a chain of bike-rental kiosks and wants to predict each day's rentals from the daily high temperature. A booster keeps stacking small trees, fitting the training days a little better with every round, forever. So the real question is not "is more trees better." It is when do you stop? Set the number of trees too high by hand and you overfit; too low and you leave demand unlearned.
By the end of this lesson you will be able to:
- Read a learning curve and say what the training curve and the validation curve each tell you
- Diagnose overfitting, underfitting, or a healthy fit from the curve's shape, and pick the fix
- State the early-stopping rule (the validation minimum, plus a patience window) and why patience beats stopping at the first uptick
- Use early stopping in R to set the number of trees for free, with no grid search
Prerequisites: Lesson 1 (Gradient Boosting from Scratch: residuals, the learning rate, shallow trees in sequence) and Lesson 3 (The Hyperparameters That Matter: the four knobs and the validation U). You can run R and you know what a training/validation split is and what RMSE measures.
The learning curve, read properly
A learning curve plots two numbers against the boosting round: the error on the training days (the days the model fits) and the error on a held-out validation set (days it never trains on). We measure error with RMSE, the root-mean-square of the prediction misses, in the same units as rentals.
Here is the asymmetry that makes the curve so useful. Every new tree is fit to correct what the current model still gets wrong on the training days, so the training curve falls a little more every single round and never turns back up. The validation curve has no such guarantee: it falls while the trees are still learning real, repeatable demand, bottoms out, then climbs once later trees start fitting the random noise in the training days. The lowest point of the validation curve is the model you want.
Write the booster as the additive model from Lesson 3,
\[ F_M(x) = F_0(x) + \nu \sum_{m=1}^{M} h_m(x), \]
where \(F_0(x)\) is the starting guess (the mean rentals), \(h_m\) is the \(m\)-th tree, \(\nu\) is the learning rate, and \(M\) is the number of trees. Reading the curve picks \(M\): the best number of trees is the round where validation error is smallest,
\[ M^\star = \arg\min_{m}\; \mathcal{L}_{\text{val}}(m), \]
where \(\mathcal{L}_{\text{val}}(m)\) is the validation RMSE after \(m\) rounds and \(\arg\min\) means "the round \(m\) that makes it smallest."
Drag the slider below to choose where to stop. Watch the orange training curve slide down without end while the validation curve makes its U, and read the verdict: stopped too early, stopped too late, or right at the sweet spot. That slider, done automatically, is early stopping.
Build the curve on Sam's data
Let us produce that exact picture for real. Each lesson runs in a fresh R session, so we rebuild Sam's data here: one kiosk's daily rentals against temperature (demand humps near a pleasant 22 degrees), then a small booster that records BOTH errors after every round. Run this setup once.
Now boost 120 rounds and look at the first few. Both errors start high and fall together while the model is still learning the temperature-to-demand shape.
The interesting part is where they part ways. Ask R for the round with the lowest validation error, then compare a few rounds across the whole run.
Validation bottoms out at round 64 (RMSE 22.8), then creeps back up to 23.3 by round 120, even as training error keeps dropping to 16.1. Those last 56 trees made the model look better on the training days and worse on new days. That widening gap is overfitting, caught in the act.
Which model do you ship?
You boost Sam's kiosk for 120 rounds. Training RMSE keeps falling every round and is lowest at round 120. Validation RMSE fell to 22.8 at round 64, then drifted up to 23.3 by round 120. Which model would you put into production?
What the curve's shape is telling you
The same plot diagnoses three different problems by its shape. Read the gap between the two curves and where the validation curve is heading.
| Shape you see | Diagnosis | What to do |
|---|---|---|
| Validation makes a clear U; small gap at the bottom | Healthy. The minimum is your number of trees. | Stop at the validation minimum. |
| Training error near zero, validation far above it and rising | Overfitting. The model memorizes training days. | Stop earlier; shallower trees; lower learning rate; more regularization. |
| Both errors high and still falling (or flat and high) together | Underfitting. The model has not learned the signal yet. | More rounds; a touch more depth; a higher learning rate. |
See all three on Sam's data. The same booster, three settings, reporting the final training and validation RMSE for each.
Read the gaps. The healthy row has a small, honest gap (18.2 vs 22.8). The overfit row drives training error to literally zero but validation is the worst of the three (29.0): a giant gap is the signature of memorizing. The underfit row has almost no gap, but both numbers are terrible (about 53): it never learned the temperature-to-demand curve at all. A tiny gap is only good news when both errors are also low.
Early stopping, in one function
Reading which.min off a finished curve works, but it wastes effort: you had to boost a fixed 120 rounds and guess that 120 was enough. Early stopping flips it around. You set the number of trees deliberately high, then let the booster watch validation error as it goes and stop itself once that error has not improved for a while.
"For a while," not "at all," is the important part. A validation curve is a little jagged, so it can tick up for one unlucky round and then fall to a new low. The patience (libraries call it early_stopping_rounds) is how many non-improving rounds you tolerate before giving up. Each time validation hits a new best, the counter resets; when it runs out, you stop and keep the best round, not the last one.
You asked for up to 300 trees. Early stopping ran to round 74, after watching validation fail to beat its round-64 low for 10 straight rounds, then handed back round 64 as the answer. You never had to know in advance that 64 was right. That is the whole trick: early stopping tunes the number of trees for you, in a single fit, with no grid search.
Find the stopping round
lc holds the per-round curve you built earlier with boost_curve(rounds = 120). Find the round with the LOWEST validation error: that is where early stopping would stop. Fill in the blank with the right function.
Show answer
best_round <- which.min(lc$valid)
best_round
#> [1] 64Why patience?
You are running early stopping with patience = 10. Watching validation error, you see it tick UP slightly at round 41, then fall to a brand-new low at round 58. What does the booster do at round 41?
How real boosters do it
You will rarely hand-write the loop above. Every production booster has early stopping built in: you pass a validation set to watch and an early_stopping_rounds (the patience), set nrounds deliberately high, and the library finds the best round for you. Here is the XGBoost idiom in R. (It needs the xgboost package, which does not run in this in-browser R, so run this one in your own R session.)
library(xgboost)
# Wrap the training and validation matrices XGBoost expects.
dtrain <- xgb.DMatrix(x_train, label = y_train)
dvalid <- xgb.DMatrix(x_valid, label = y_valid)
fit <- xgb.train(
params = list(objective = "reg:squarederror", eta = 0.05, max_depth = 4),
data = dtrain,
nrounds = 5000, # set this deliberately HIGH
watchlist = list(train = dtrain, valid = dvalid),
early_stopping_rounds = 50, # stop after 50 rounds with no validation gain
eval_metric = "rmse"
)
fit$best_iteration # the number of trees early stopping chose for you
LightGBM is the same idea with valids and early_stopping_round; the model remembers its best_iteration and uses it when you predict. The recipe from Lesson 3 now closes cleanly: fix a low learning rate, set the trees high, and let early stopping pick the exact number, so the one knob you never have to tune by hand is the number of trees.
References
A few authoritative places to take this further:
- The Elements of Statistical Learning, ch. 10 (free PDF) - boosting, the shrinkage and number-of-trees parameters, and why the test curve turns back up.
- An Introduction to Statistical Learning, ch. 8 (free PDF) - the gentler companion on choosing the number of trees for a boosted model.
- XGBoost docs: Notes on Parameter Tuning - controlling overfitting and the number of rounds, with early stopping in practice.
- LightGBM docs: early_stopping_round - the same patience parameter, and how best_iteration is used at predict time.
- Prechelt (1998), Early Stopping, But When? - the classic treatment of when to stop and why a patience window beats the first uptick.
Lesson 4 complete
You can now read a boosting learning curve, the most useful diagnostic the method gives you. Training error always falls, so it never tells you when to stop; validation error makes a U whose minimum is the right number of trees. The curve's shape names the problem: a small gap at a low minimum is healthy, a wide and growing gap is overfitting, two high curves are underfitting. And early stopping automates the read: set the trees high, watch validation, wait out a patience window of non-improving rounds, and keep the best one, no grid search required.
Next, Lesson 5: Monotonic Constraints for Business Rules. Sometimes accuracy is not enough and the business needs a guarantee, for example that predicted demand never falls as temperature moves toward the ideal. You will force a feature's effect to go one way on purpose, and weigh the small accuracy cost against the trust it buys.