Skip to content

Boosting - Introduction to AdaBoost

What you’ll learn

  • boosting as the mirror image of bagging: sequential and aimed at bias, not variance
  • the AdaBoost algorithm in five steps
  • the predictor weight α=12ln1εε\alpha = \frac{1}{2}\ln\frac{1-\varepsilon}{\varepsilon}, derived and interpreted
  • a complete round of AdaBoost computed by hand, weights and all
  • why a learner at exactly 50% gets no vote, and one below 50% gets an inverted one
  • why boosting is sensitive to noise where bagging is not

Intuition

Bagging trains many strong models independently and averages away their variance. Boosting does the opposite in every respect: it trains weak models one at a time, each one focused on the examples the previous ones got wrong, and attacks bias.

The mechanism is reweighting. After each round, misclassified examples get heavier and correctly classified ones get lighter. The next learner, fitted on those weights, is forced to pay attention to precisely the cases the committee currently fails on.

The remarkable claim — proved by Freund and Schapire in 1995 — is that any learner reliably better than chance can be boosted into an arbitrarily accurate one. A decision stump, a single split on a single feature, is enough.

diagram Diagram mermaid

Note the shape: every arrow is sequential. Stump 2 cannot start until stump 1’s errors are known, which is why boosting does not parallelise across members and a random forest does.

The math

The algorithm

  1. Initialise every sample weight equally: wi=1/mw_i = 1/m.
  2. Fit a weak learner on the weighted data.
  3. Measure its weighted error rate:
εj=i:y^iyiwii=1mwi\varepsilon_j = \frac{\sum_{i:\, \hat{y}_i \neq y_i} w_i}{\sum_{i=1}^{m} w_i}
  1. Weight the predictor:
αj=η12ln ⁣(1εjεj)\alpha_j = \eta \cdot \frac{1}{2}\ln\!\left(\frac{1 - \varepsilon_j}{\varepsilon_j}\right)
  1. Reweight the samples, then renormalise so they sum to 1:
wiwi{eαjif correctly classifiede+αjif misclassifiedw_i \leftarrow w_i \cdot \begin{cases} e^{-\alpha_j} & \text{if correctly classified} \\ e^{+\alpha_j} & \text{if misclassified} \end{cases}

Repeat from step 2. The final prediction is the weighted vote:

y^(x)=arg maxkj:y^j(x)=kαj\hat{y}(\mathbf{x}) = \operatorname*{arg\,max}_{k}\sum_{j:\, \hat{y}_j(\mathbf{x}) = k} \alpha_j

Reading the alpha formula

figurePredictor weight against error ratematplotlib
Curve of alpha against the weighted error rate, passing through zero at 0.5, rising steeply toward positive infinity as error approaches zero and falling toward negative infinity as it approaches one.Curve of alpha against the weighted error rate, passing through zero at 0.5, rising steeply toward positive infinity as error approaches zero and falling toward negative infinity as it approaches one.
At 10% error a learner gets weight +1.10; at 50% it gets exactly 0.00 and is ignored; at 70% it gets -0.42 and its vote is deliberately inverted.
ε\varepsilonα\alphaInterpretation
0.10+1.0986Strong learner, loud vote
0.30+0.4236Useful
0.40+0.2027Barely useful
0.500.0000Coin flip — silenced entirely
0.70−0.4236Reliably wrong, so its vote is flipped

Three properties fall out of the logarithm:

  1. ε=0.5\varepsilon = 0.5 gives exactly zero. ln(1)=0\ln(1) = 0. A learner no better than chance contributes nothing, automatically — no special case in the code.
  2. The curve is antisymmetric about 0.5. A learner at 0.3 and one at 0.7 get weights of equal size and opposite sign. Being reliably wrong is exactly as useful as being reliably right, once you flip it.
  3. α\alpha \to \infty as ε0\varepsilon \to 0. A perfect learner would dominate the vote — which is why AdaBoost stops early if a learner reaches zero error, and why weak learners are the point.

Worked example by hand

Five samples, uniform initial weights of 0.20.2. Round 1’s stump misclassifies samples 2 and 4.

Step 1 — the weighted error.

ε1=0.2+0.21.0=0.4\varepsilon_1 = \frac{0.2 + 0.2}{1.0} = 0.4

Step 2 — the predictor weight.

α1=12ln ⁣(10.40.4)=12ln(1.5)=0.2027\alpha_1 = \tfrac{1}{2}\ln\!\left(\frac{1 - 0.4}{0.4}\right) = \tfrac{1}{2}\ln(1.5) = 0.2027

Step 3 — the multipliers.

e+α1=e0.2027=1.2247(mistakes),eα1=e0.2027=0.8165(correct)e^{+\alpha_1} = e^{0.2027} = 1.2247 \quad (\text{mistakes}), \qquad e^{-\alpha_1} = e^{-0.2027} = 0.8165 \quad (\text{correct})

Step 4 — apply and renormalise.

samplecorrect?ww beforemultiplierww rawww after
10.20.81650.163300.16667
20.21.22470.244950.25000
30.20.81650.163300.16667
40.21.22470.244950.25000
50.20.81650.163300.16667
1.00.979801.0

Step 5 — read the result. The two misclassified samples went from 0.200 to 0.250, a 25% increase; the three correct ones fell to 0.1667. Their combined weight is now 0.50, up from 0.40 — the next stump faces a problem where half the mass is the cases the first stump could not handle.

Notice too that the mistakes’ total weight is now exactly 0.50.5. That is not a coincidence: the reweighting is constructed so the previous learner scores exactly 50% — and therefore α=0\alpha = 0 — on the new distribution. Every round starts from a problem its predecessor finds impossible.

figureFour rounds of reweightingmatplotlib
Four panels showing the same forty points over four AdaBoost rounds. Point sizes grow for the points circled in red as misclassified in the previous round.Four panels showing the same forty points over four AdaBoost rounds. Point sizes grow for the points circled in red as misclassified in the previous round.
Point area is its weight. Round 1 misclassifies two points; by round 4 those and their neighbours dominate the mass, and the stumps are fitting almost entirely to the difficult boundary region.

See it move

The same loop, one round per beat. Watch the two mislabelled points in the middle swell until the stumps have no choice but to attend to them.

sketch Reweighting, round by round p5.js
Eighteen points on a line, two of them mislabelled. Each round picks the best weighted stump, prints its error and alpha, then grows the weight of everything it got wrong.

In code

adaboost.py
from sklearn.datasets import load_breast_cancer
from sklearn.ensemble import AdaBoostClassifier
from sklearn.model_selection import cross_val_score
from sklearn.tree import DecisionTreeClassifier
 
X, y = load_breast_cancer(return_X_y=True)
 
for n_estimators in (1, 10, 50, 200):
    model = AdaBoostClassifier(
        DecisionTreeClassifier(max_depth=1),      # a stump: one split, one feature
        n_estimators=n_estimators,
        learning_rate=1.0,
        random_state=0,
    )
    print(f"{n_estimators:>3} stumps  CV {cross_val_score(model, X, y, cv=5).mean():.4f}")
 
#   1 stumps  CV 0.8998
#  10 stumps  CV 0.9385
#  50 stumps  CV 0.9666
# 200 stumps  CV 0.9772
adaboost.py
from sklearn.datasets import load_breast_cancer
from sklearn.ensemble import AdaBoostClassifier
from sklearn.model_selection import cross_val_score
from sklearn.tree import DecisionTreeClassifier
 
X, y = load_breast_cancer(return_X_y=True)
 
for n_estimators in (1, 10, 50, 200):
    model = AdaBoostClassifier(
        DecisionTreeClassifier(max_depth=1),      # a stump: one split, one feature
        n_estimators=n_estimators,
        learning_rate=1.0,
        random_state=0,
    )
    print(f"{n_estimators:>3} stumps  CV {cross_val_score(model, X, y, cv=5).mean():.4f}")
 
#   1 stumps  CV 0.8998
#  10 stumps  CV 0.9385
#  50 stumps  CV 0.9666
# 200 stumps  CV 0.9772

One stump reaches 0.8998 — a single threshold on a single feature. Two hundred of them reach 0.9772, competitive with anything else in this curriculum on this dataset. That is the boosting claim, demonstrated: weak learners, chained, become strong.

Note the shape of the gains: 0.900 → 0.939 → 0.967 → 0.977. Each quadrupling of the budget buys roughly half the remaining error, which is the diminishing return you should expect.

Reading the weights back out

inspect_weights.py
import numpy as np
 
model = AdaBoostClassifier(DecisionTreeClassifier(max_depth=1),
                           n_estimators=50, random_state=0).fit(X, y)
 
print("first five alphas:", model.estimator_weights_[:5].round(4))
print("first five errors:", model.estimator_errors_[:5].round(4))
 
# alpha should equal 0.5 * ln((1 - eps) / eps) for each stump
eps = model.estimator_errors_
manual = 0.5 * np.log((1 - eps) / eps)
print("formula matches:", np.allclose(manual, model.estimator_weights_ / 2, atol=1e-6))
inspect_weights.py
import numpy as np
 
model = AdaBoostClassifier(DecisionTreeClassifier(max_depth=1),
                           n_estimators=50, random_state=0).fit(X, y)
 
print("first five alphas:", model.estimator_weights_[:5].round(4))
print("first five errors:", model.estimator_errors_[:5].round(4))
 
# alpha should equal 0.5 * ln((1 - eps) / eps) for each stump
eps = model.estimator_errors_
manual = 0.5 * np.log((1 - eps) / eps)
print("formula matches:", np.allclose(manual, model.estimator_weights_ / 2, atol=1e-6))

scikit-learn stores 2α2\alpha rather than α\alpha (a harmless convention difference), which is worth knowing before you compare against the formula and conclude something is broken.

learning_rate: the second brake

The η\eta in step 4 scales every α\alpha:

αj=η12ln ⁣(1εjεj)\alpha_j = \eta \cdot \tfrac{1}{2}\ln\!\left(\frac{1-\varepsilon_j}{\varepsilon_j}\right)

A smaller η\eta makes each learner contribute less, so more of them are needed — the same shrinkage trade as in gradient boosting. learning_ratelearning_rate and n_estimatorsn_estimators must be tuned together, and their product is roughly conserved.

Where boosting breaks

Bagging is robust to label noise: a mislabelled point ends up in about 63% of the bootstrap samples and gets outvoted. Boosting hunts it down.

A mislabelled example is misclassified every round by construction — no learner can get it right — so its weight grows exponentially. After enough rounds the ensemble is devoting most of its capacity to fitting a handful of corrupted labels.

Data conditionBaggingBoosting
CleanGoodUsually better
Label noiseRobustDegrades badly
Outliers in featuresRobustSensitive
Class imbalanceHandles it with weightsChases the minority, sometimes usefully
algorithmAdaBoostSupervised · Classification and Regression · Boosting ensemble

APIsklearn.ensemble.AdaBoostClassifier / AdaBoostRegressor

Assumes

  • Each weak learner is at least slightly better than chance on the weighted data
  • The base learner supports sample weights
  • Labels are reasonably clean — mislabelled points attract exponentially growing weight

Cost

train
O(m · n · estimators) — strictly sequential
predict
O(depth · estimators)
memory
O(estimators)

m = samples, n = features; the sequential dependency is why this cannot parallelise across estimators

Hyperparameters that matter

  • n_estimatorsdefault 50Unlike a forest, this CAN overfit. Tune it, and pair it with learning_rate.
  • learning_ratedefault 1.0Scales every alpha. Lower needs more estimators; their product is roughly conserved.
  • estimatordefault stumpA depth-1 tree is the classic choice. Depth 2 or 3 captures interactions; deeper defeats the point.
  • algorithmdefault SAMMESAMME.R used probabilities and converged faster; it is deprecated in recent versions in favour of SAMME.

Reach for it when

  • You want strong accuracy from very simple base learners
  • The dataset is clean and modestly sized
  • You want an ensemble with few hyperparameters to tune
  • Interpretability of individual stumps has some value

Look elsewhere when

  • Labels are noisy — the reweighting will chase the errors
  • You need parallel training; boosting is inherently sequential
  • The dataset is very large — consider histogram gradient boosting instead
  • There are extreme outliers you cannot remove

Pitfalls

Compare

Bagging / Random ForestAdaBoost
MembersStrong, deep treesWeak, stumps
TrainingParallelSequential
AttacksVarianceBias
Members weightedEquallyBy α\alpha
More members overfitNoYes
Label noiseRobustFragile
Typical accuracy on clean tabular dataVery goodOften better
quizCheck yourself
  1. A weak learner has a weighted error of exactly 0.5. What weight does AdaBoost give it?

    Show answer

    C — Exactly 0 — alpha = 0.5 ln((1 - 0.5)/0.5) = 0.5 ln(1) = 0. A coin-flip learner is silenced automatically by the formula, with no special case needed.

  2. A learner has a weighted error of 0.7. What does AdaBoost do with it?

    Show answer

    B — Gives it a negative weight of -0.4236, which inverts its vote — being reliably wrong is as useful as being reliably right — The alpha curve is antisymmetric about 0.5. A learner wrong 70% of the time is right 70% of the time once you flip its output.

  3. After a round with error 0.4, what happens to the total weight of the misclassified samples?

    Show answer

    B — It becomes exactly 0.5, so the previous learner would score 50% on the new distribution and earn zero weight — The reweighting is constructed so each round hands its successor a problem the predecessor finds exactly as hard as guessing. That is what forces genuine specialisation.

  4. Why does AdaBoost degrade on noisy labels where bagging does not?

    Show answer

    B — A mislabelled point is misclassified every round, so its weight grows exponentially and the ensemble spends its capacity fitting corrupted labels — In bagging a bad point appears in 63% of samples and is outvoted. In boosting it is by definition always wrong, so the algorithm concludes it is the most important example.

🧪 Try It Yourself

Exercise 1 – The alpha formula

Exercise 2 – One round of reweighting

Exercise 3 – Weak learners become strong

Exercise 4 – A weighted majority vote

Exercise 5 – Watch boosting chase a bad label

Recap

  • Boosting is bagging’s mirror image: weak learners, trained sequentially, attacking bias.
  • α=12ln1εε\alpha = \frac{1}{2}\ln\frac{1-\varepsilon}{\varepsilon} gives +1.099 at 10% error, exactly 0 at 50%, and −0.424 at 70% — the vote is inverted rather than discarded.
  • Hand-worked round: error 0.4, α=0.2027\alpha = 0.2027, mistake weights rise from 0.200 to 0.250 and their total mass becomes exactly 0.5.
  • Each round hands its successor a problem the previous learner finds no better than chance.
  • One decision stump on breast cancer scores 0.8998; two hundred score 0.9772.
  • n_estimatorsn_estimators can overfit here, unlike a random forest, and learning_ratelearning_rate trades against it.
  • Boosting is fragile to label noise for exactly the reason it is powerful on clean data.

Exercise 6 – Run one AdaBoost weight update by hand

Next

Continue to Gradient Boosting (XGBoost, LightGBM, CatBoost) — the same sequential idea with reweighting replaced by gradient descent in function space, and the family of libraries that dominates tabular machine learning.

If this helped you, consider buying me a coffee ☕

Buy me a coffee

Was this page helpful?

Let us know how we did