Ensembles and Gradient Boosting

AdaBoost

AdaBoost trains weak models one at a time and makes every example it got wrong count for more in the next round, which was the first proof that weak learners add up to a strong one.

On this page 6
  1. Why this had to be invented
  2. How it works
  3. A real example you have seen
  4. What is honestly hard here
  5. Remember this
  6. What to learn next

One lesson, three depths. Pick the one that fits you today — you can switch any time.

Beginner — No maths. Plain English.

AdaBoost trains simple models one after another, and after each round it makes the examples it got wrong count for more.

Think of revising with flashcards. Cards you answer correctly go to the back of the deck. Cards you get wrong go back in near the front, so they come up again and again. Your practice automatically concentrates on your weak spots.

AdaBoost runs a whole team of models through this drill. Each new model faces a deck where the previous models' mistakes appear more often. The name means adaptive boosting — the deck adapts after every round.

Why this had to be invented

In the early 1990s this was an open theory question. Suppose you can only build weak learners: models barely better than a coin flip. Can you combine them into one strong model? Many suspected no. AdaBoost was the constructive proof of yes.

The usual weak learner is a decision stump: a decision tree with a single question, like "is income above 4 lakh?". One stump is nearly useless. Hundreds of stumps, each drilled on the previous ones' failures, become sharp.

The stitching differs from bagging in two ways. The models train in sequence, not independently. And the final answer is a weighted vote — stumps that performed well on their round speak louder.

How it works

round 1: every example carries equal weight
         stump 1 answers → its mistakes get heavier

round 2: stump 2 trains on the reweighted deck
         (heavy examples matter more) → new mistakes get heavier

round 3: stump 3 trains on the newest deck ...

final answer = weighted vote of all stumps,
               better stumps get louder voices

Two things update every round: the examples' weights (hard cases grow) and each stump's voting strength (accurate stumps grow).

A real example you have seen

The little box that snaps around faces in a phone camera. For years that was the Viola–Jones detector, built directly on AdaBoost. It used thousands of extremely weak brightness checks. Boosted together, they made a face detector fast enough for 2001-era hardware. Descendants of it shipped in cameras for a decade before neural networks took over.

What is honestly hard here

AdaBoost's obsession with hard examples has a dark side. If a label is wrong — a mislabeled photo, a typo in the data — AdaBoost cannot know that. It piles weight onto the impossible example, round after round, and can bend the whole model around garbage. Noisy labels are AdaBoost's known weakness, and one reason its successors won.

Remember this

  • AdaBoost trains weak models in sequence; each round reweights the examples the team got wrong.
  • The final prediction is a weighted vote — better stumps count more.
  • It proved weak learners combine into strong ones, but it is fragile against wrong labels.

What to learn next

Developer — Code and libraries.

Setup

bash
pip install scikit-learn

Outputs verified with scikit-learn 1.7.2 on CPU. Version note: since 1.6 the algorithm parameter is deprecated and only the SAMME variant exists; leave the parameter out entirely. Older tutorials passing algorithm="SAMME.R" now raise an error.

Watching the boost happen

adaboost_demo.py
from sklearn.datasets import make_classification
from sklearn.ensemble import AdaBoostClassifier
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier

X, y = make_classification(n_samples=1000, n_features=10, n_informative=6,
                           flip_y=0.02, random_state=3)
Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.4, random_state=3)

stump = DecisionTreeClassifier(max_depth=1).fit(Xtr, ytr)
print(f"one stump alone: {stump.score(Xte, yte):.3f}")

ada = AdaBoostClassifier(n_estimators=300, random_state=3).fit(Xtr, ytr)
print(f"300 boosted stumps: {ada.score(Xte, yte):.3f}")

for i, acc in enumerate(ada.staged_score(Xte, yte), start=1):
    if i in (1, 5, 25, 100, 300):
        print(f"after {i:3d} stumps: {acc:.3f}")
Output
one stump alone: 0.642
300 boosted stumps: 0.848
after   1 stumps: 0.642
after   5 stumps: 0.698
after  25 stumps: 0.802
after 100 stumps: 0.828
after 300 stumps: 0.848

One stump manages 64% — barely a model. Three hundred of them, boosted, reach 85%. No single stump got smarter; the sequence did.

The walkthrough

The default base model is a stump. AdaBoostClassifier uses DecisionTreeClassifier(max_depth=1) unless told otherwise. That default is a feature, not a limitation — boosting is designed around weak learners.

staged_score replays the ensemble one member at a time, scoring after each addition. It is the cheapest way to see whether more rounds are still paying. Here the curve climbs steeply to 25 stumps, then grinds out the rest slowly.

learning_rate (default 1.0) scales down every stump's voting strength. Lower values need more rounds but often land slightly better — the same slow-cooking trade covered properly in tuning gradient-boosted trees.

flip_y=0.02 plants 2% wrong labels in this data. Push it to 0.15 and watch the boosted score sag — the noisy-label fragility from the beginner section, live.

Common mistakes

Using a strong base model. Set the base to max_depth=10 trees and each member nearly solves the problem alone, mistakes stop being informative, and the ensemble overfits fast. If you want boosted deep-ish trees, use gradient boosting, which is built for them.

Applying AdaBoost to noisy labels. Every mislabeled row becomes a magnet for weight. If your labels are crowd-sourced or scraped, prefer gradient boosting with log loss, which punishes hopeless cases far more gently.

Assuming more rounds always help. The staged scores flatten — and on noisier data they bend downward. Watch staged_score on held-out data and stop where it stalls.

Copying algorithm="SAMME.R" from old tutorials. Removed. In scikit-learn 1.7 it raises InvalidParameterError; omit the parameter.

Try it yourself

Rerun adaboost_demo.py with flip_y=0.15. Compare the staged scores against the clean run — find where the curve stops climbing. Then swap the base for DecisionTreeClassifier(max_depth=3) via AdaBoostClassifier(estimator=...) and see whether a slightly-less-weak learner helps or hurts here.

What to learn next

  • XGBoost — boosting's modern form, where residuals replace reweighting.
  • LightGBM — the fast engine that industrialised gradient boosting.
  • Why ensembles work — the theory both families share.

Researcher — Mathematics and papers.

The algorithm

Given $(x_i, y_i)_{i=1}^{n}$ with $y_i \in {-1, +1}$, initialise weights $w_i = 1/n$. For rounds $t = 1 \dots T$:

  1. Fit weak learner $h_t$ to the weighted data.
  2. Compute the weighted error $\varepsilon_t = \sum_i w_i \, \mathbb{1}[h_t(x_i) \neq y_i]$.
  3. Set the member's vote strength $\alpha_t = \tfrac{1}{2} \ln \frac{1 - \varepsilon_t}{\varepsilon_t}$.
  4. Update $w_i \leftarrow w_i \exp(-\alpha_t y_i h_t(x_i))$ and renormalise.

Final classifier: $H(x) = \operatorname{sign}\left(\sum_{t=1}^{T} \alpha_t h_t(x)\right)$.

Where:

  • $w_i$ — the current weight of example $i$ (grows when the ensemble errs on it).
  • $\varepsilon_t$ — round $t$'s weighted error; $\varepsilon_t < 0.5$ makes $\alpha_t > 0$.
  • $\alpha_t$ — how loudly $h_t$ votes; approaches $\infty$ as $\varepsilon_t \to 0$.

Source: Freund and Schapire (1997), A decision-theoretic generalization of on-line learning and an application to boosting, JCSS — building on Schapire (1990), The strength of weak learnability, which answered Kearns and Valiant's question in the affirmative.

Training error bound

With edge $\gamma_t = \tfrac{1}{2} - \varepsilon_t$, the training error satisfies

$$ \frac{1}{n} \sum_i \mathbb{1}[H(x_i) \neq y_i] \;\leq\; \prod_{t=1}^{T} \sqrt{1 - 4\gamma_t^2} \;\leq\; \exp\left(-2 \sum_{t=1}^{T} \gamma_t^2\right) $$

Any persistent edge $\gamma_t \geq \gamma > 0$ drives training error down exponentially in $T$. The surprise, historically, was that test error often kept improving after training error hit zero. The margin theory of Schapire, Freund, Bartlett and Lee (1998), Boosting the margin, explains this: further rounds keep enlarging the classification margins, and generalisation bounds depend on margins rather than on $T$.

The statistical view

Friedman, Hastie and Tibshirani (2000), Additive logistic regression: a statistical view of boosting, showed AdaBoost performs forward stagewise additive modelling that minimises the exponential loss

$$ L(y, F) = \exp(-yF(x)) $$

Where $F$ is the accumulating ensemble score. This reframing did three things: it explained the weight update (the exponential loss's gradient), it exposed the noise fragility (exponential penalty on large negative margins, i.e. confident mistakes), and it opened the door to swapping in other losses — which is precisely gradient boosting (Friedman, 2001).

Noise sensitivity is not only an empirical finding: Long and Servedio (2010), Random classification noise defeats all convex potential boosters, prove any convex-loss booster of this family can be driven to chance accuracy by symmetric label noise.

Complexity and variants

Per round: one weak-learner fit on $n$ weighted rows — for stumps, $O(n d)$ with $d$ features after an $O(n d \log n)$ presort. Memory is the ensemble itself, $O(T)$ stumps.

  • SAMME (Zhu et al., 2009) — the multiclass generalisation; the only variant remaining in scikit-learn.
  • LogitBoost (Friedman et al., 2000) — same skeleton, logistic loss.
  • Gradient boosting (Friedman, 2001) — replaces reweighting with fitting the loss gradient; with regularisation and clever engineering it becomes XGBoost and LightGBM, which have displaced AdaBoost in practice.

AdaBoost's lasting importance is conceptual: weak learnability equals strong learnability, and hard examples deserve more attention — an idea that resurfaces in hard-negative mining and focal loss.

What to learn next

  • XGBoost — boosting's modern form, where residuals replace reweighting.
  • LightGBM — the fast engine that industrialised gradient boosting.
  • Why ensembles work — the theory both families share.

What to learn next

These follow on from what you just read.

  • Ensembles and Gradient Boosting

    LightGBM

    LightGBM makes gradient boosting fast by sorting feature values into coarse bins and growing trees leaf by leaf, which is why it dominates on large tables.

  • Ensembles and Gradient Boosting

    CatBoost

    CatBoost feeds text-like category columns straight into gradient boosting, and its ordered trick stops a model from secretly grading its own answers.

  • Ensembles and Gradient Boosting

    Tuning gradient-boosted trees

    Almost all gradient boosting tuning reduces to one trade — a smaller learning rate with more trees, cut off by early stopping — plus a short list of knobs in priority order.