Boosting
On this page
Boosting is covered in section 8.2.3 of An Introduction to Statistical Learning, and its classic example is AdaBoost. Boosted trees (the XGBoost and LightGBM family) are among the most common models in banking today for tabular data, so it’s worth understanding what they actually do.
Bagging vs boosting in one picture
| Item | Bagging / random forest | Boosting |
|---|---|---|
| How trees are trained | independently, in parallel | one after another |
| What each tree sees | a random resample | the previous trees’ mistakes |
| Typical trees | deep (low bias, high variance) | shallow (high bias, low variance) |
| What it reduces | variance | mainly bias |
| Risk | can’t overfit by adding trees | can overfit if you add too many |
In the bias and variance terms: bagging takes complex, unstable models and steadies them. Boosting takes simple, stable models and makes them smarter.
AdaBoost: reweight the mistakes
AdaBoost (adaptive boosting, Freund and Schapire, 1997) was the first widely used boosting method.
- Give every training example the same weight.
- Train a weak model, often a stump: a tree with a single question.
- Measure its weighted error : the total weight of the examples it got wrong.
- Give the model a vote : the lower its error, the bigger its say.
- Increase the weight of the examples it got wrong, decrease the rest, and repeat from step 2.
The final prediction is a weighted vote of all the models.
The formulas
α, the model's voteε, its weighted errorwrong examplesright examples
The vote of model :
If (a coin flip), : no say. The smaller the error, the larger .
The weight update for each example , depending on whether model got it wrong or right:
Then all the weights are divided by their total, so they add up to 1 again.
Worked example: one round, ten trades
Ten trades, each with weight 0.1. The first stump gets 3 of them wrong:
wrong trades are multiplied by , right ones by :
| Item | Before | × factor | Unnormalised | After normalising (÷ 0.9165) |
|---|---|---|---|---|
| Each wrong trade (3) | 0.1 | 1.5275 | 0.15275 | 0.1667 |
| Each right trade (7) | 0.1 | 0.6547 | 0.06547 | 0.0714 |
After one round, the 3 wrong trades carry half the total weight (3 × 0.1667 = 0.5), and the 7 right ones the other half. That’s always true after an AdaBoost update: the next model is forced to treat the previous model’s mistakes as half of its whole problem.
Notation in other books
Murphy’s Probabilistic Machine Learning: An Introduction (chapter 18, on boosting) writes the same algorithm slightly differently. He calls the vote , and only multiplies the wrong examples, by with , before normalising. That’s the same result: either way, a wrong example ends up times heavier than a right one.
A note on scikit-learn’s numbers
scikit-learn’s AdaBoostClassifier (version 1.9) uses the SAMME form of the algorithm. With two
classes, the weight it reports is
without the . That’s exactly Murphy’s . Every vote is twice as
big, so the final ranking of predictions is the same; only the printed numbers differ. If you
compare a textbook with ada.estimator_weights_, that’s why.
What AdaBoost did on the settlement data
I boosted stumps on the 15,000 made-up OTC trades. Only 5.9% of them failed, so accuracy is
misleading (saying “settles” every time scores 94%); that’s why the scores below are AUC. Boosting
libraries also offer class weights (scale_pos_weight in XGBoost) for imbalance like this. The
first three rounds:
| Round | Stump’s question | Weighted error | scikit-learn weight |
|---|---|---|---|
| 1 | SSI mismatch? | 0.0589 | 2.7706 |
| 2 | SSI mismatch? | 0.3364 | 0.6794 |
| 3 | Hours booked late? | 0.3882 | 0.4548 |
Round 1’s stump predicts “settles” for every trade, so its error equals the fail rate (). AdaBoost then gives the fails half the weight, which forces round 2 to look at them.
Why does the first stump ignore mismatches? Even trades with a mismatch settle more often than they fail (58% versus 42%). Once the fails carry half the weight, round 2’s stump on the same question flags mismatched trades as fails, and round 3 moves on to late booking. Each round fixes a part of what the earlier ones missed.
A single stump scores an AUC of 0.669 on new trades. Boosting 44 of them reaches .
Gradient boosting: fit the leftover error
Gradient boosting (Friedman, 2001) generalises the idea. Instead of reweighting examples, each new tree is trained to predict what the current model still gets wrong.
For regression with squared error, that’s literally the residuals. An Introduction to Statistical Learning gives it as Algorithm 8.2. Start with and residuals . Then, for each round , fit a small tree to the residuals and update:
λ, how much of each tree is addedr, the error left
The final model is the sum of all the shrunken trees:
| Symbol | Meaning |
|---|---|
| the model so far | |
| the residuals: what’s still unexplained for example | |
| the small tree added in round , trained to predict the residuals | |
| the shrinkage or learning rate, a small number like 0.1 (ISLP suggests 0.01 or 0.001) | |
| the number of trees |
Plain meaning: start with a rough guess. Look at the errors. Train a small tree to predict the errors. Add a fraction of it to the guess. Repeat. For other losses, like the log loss used for classification, “residuals” become the negative gradient of the loss (often called pseudo-residuals), which gives the method its name. The idea is the same.
The learning rate matters a lot. Each tree only corrects part of the error, so no single tree can push the model too far:
For this chart the models trained on 12,000 of the trades and kept 3,000 aside as a validation set. The number of trees to use is chosen on that validation set, never on the test set, and the dot on each line marks the round it picked. Test AUC:
| Model | 10 trees | 50 trees | 300 trees | Chosen on validation | Test AUC there |
|---|---|---|---|---|---|
| AdaBoost, stumps | 0.803 | 0.812 | 0.810 | 44 | 0.812 |
| Gradient boosting, depth-3 trees, rate 0.1 | 0.803 | 0.814 | 0.807 | 30 | 0.817 |
| Gradient boosting, depth-3 trees, rate 1.0 | 0.752 | 0.751 | 0.751 | 1 | 0.759 |
| Random forest (last post) | 0.795 |
Two lessons from the chart above:
- Too high a learning rate wrecks the model fast. At rate 1.0 each tree over-corrects so hard that after a few trees the scores saturate and the AUC sticks at 0.751; the validation set cannot do better than a single tree.
- Boosting can overfit by adding trees, unlike a random forest. Even at rate 0.1 the score drifts down after about 100 trees. In practice you use early stopping: watch a validation score and stop adding trees when it stops improving, which is what the dots show.
On this data, well-tuned boosting edged out the random forest. That matches common experience on tabular data, but it isn’t a law: with fewer rows or noisier labels, forests often win because they’re harder to overfit.
The code
from sklearn.ensemble import AdaBoostClassifier, GradientBoostingClassifier
from sklearn.tree import DecisionTreeClassifier
ada = AdaBoostClassifier(DecisionTreeClassifier(max_depth=1), # stumps
n_estimators=300, learning_rate=1.0)
ada.fit(X_train, y_train)
ada.estimator_errors_[:3], ada.estimator_weights_[:3] # epsilon and alpha per round
gb = GradientBoostingClassifier(n_estimators=300, learning_rate=0.1, max_depth=3,
validation_fraction=0.1, n_iter_no_change=20) # early stopping
gb.fit(X_train, y_train)
gb.n_estimators_ # trees actually used
For large datasets, scikit-learn’s HistGradientBoostingClassifier, XGBoost and LightGBM are much
faster versions of the same idea.
Why banks use boosted trees
- Stronger libraries. XGBoost and LightGBM add regularisation, histogram-based splits and built-in handling of missing values.
- Monotonic constraints, for example “risk can only go up as hours booked late rises”, which credit and model-risk teams like because the model can’t contradict common sense.
- Explanations. SHAP values explain an individual prediction, feature by feature.
- The trade-off: more settings to tune than a random forest, and easier to overfit.
Summary
| Idea | Formula / rule | Remember it as |
|---|---|---|
| Boosting | weak models in sequence, each fixing the last | a team where each member covers the gaps |
| AdaBoost vote | lower error, bigger say | |
| AdaBoost update | wrong × , right × , normalise | mistakes end up with half the weight |
| scikit-learn AdaBoost | (SAMME) | twice the textbook value, same ranking |
| Gradient boosting | , fits the residuals | keep fitting what’s left over |
| Main danger | too many trees or too high a rate | use a small rate and early stopping |
That completes the ML basics series: what machine learning is, regression versus classification, linear regression, decision trees, bias and variance, bagging, random forests and boosting.
Next
The next series is coming soon. Until then, the finance stats series covers the probability and risk ideas these models are measured with.
References
- James, Witten, Hastie, Tibshirani and Taylor, An Introduction to Statistical Learning, with Applications in Python (Springer, 2023), section 8.2.3: boosting, Algorithm 8.2 (equations 8.10 to 8.12)
- Kevin P. Murphy, Probabilistic Machine Learning: An Introduction (MIT Press, 2022), chapter 18: AdaBoost (β and α = 2β) and gradient boosting
- Yoav Freund and Robert E. Schapire, “A Decision-Theoretic Generalization of On-Line Learning and an Application to Boosting”, Journal of Computer and System Sciences 55(1), 119–139, 1997
- Jerome H. Friedman, “Greedy Function Approximation: A Gradient Boosting Machine”, The Annals of Statistics 29(5), 1189–1232, 2001
- scikit-learn: AdaBoost and gradient boosting