AI Knowledge Tree

AdaBoost

Reweight examples each round to focus on mistakes, and derive the exponential-loss view of AdaBoost.

⏱ 28 minPrerequisites:Impurity criteria and splits

Bagging averages many trees fit to perturbed copies of the data, reducing variance. Boosting takes the opposite approach: fit weak learners sequentially, each one trained to correct the previous ensemble's mistakes. AdaBoost (adaptive boosting) is the original[1], and its analysis as exponential-loss minimization seeds the modern gradient-boosting view[2].

The algorithm

For binary labels yi∈{−1,+1}y_i \in \{-1, +1\}:

  1. Initialize weights wi(1)=1/nw_i^{(1)} = 1/n.
  2. For t=1,…,Tt = 1, \dots, T:
    • Fit a weak learner ht:X→{−1,+1}h_t: \mathcal{X} \to \{-1, +1\} minimizing the weighted error εt=∑iwi(t) 1[ht(xi)≠yi]\varepsilon_t = \sum_i w_i^{(t)}\,\mathbf{1}[h_t(x_i) \ne y_i].
    • Compute its weight in the ensemble: αt=12log⁡ ⁣1−εtεt.\alpha_t = \tfrac{1}{2}\log\!\frac{1 - \varepsilon_t}{\varepsilon_t}.
    • Update example weights: wi(t+1)∝wi(t)exp⁡(−αt yi ht(xi)).w_i^{(t+1)} \propto w_i^{(t)} \exp(-\alpha_t\, y_i\, h_t(x_i)).
  3. Return the additive predictor H(x)=sign⁡ ⁣(∑tαtht(x))H(x) = \operatorname{sign}\!\left(\sum_t \alpha_t h_t(x)\right).

Each round upweights misclassified points (the exponent is positive when yi ht(xi)=−1y_i\, h_t(x_i) = -1), pulling the next learner's attention toward them. Correctly classified points are downweighted.

Weak learners

A weak learner does only marginally better than random guessing (ε<1/2\varepsilon < 1/2). For tabular problems the usual choice is a decision stump (a one-split tree). Stumps are deliberately underpowered, so each one captures a tiny piece of structure, and boosting builds the full classifier from their sum.

When εt=1/2\varepsilon_t = 1/2 exactly, αt=0\alpha_t = 0 and the round contributes nothing. When εt→0\varepsilon_t \to 0, αt→∞\alpha_t \to \infty: a perfect weak learner gets full weight.

AdaBoost as exponential-loss minimization

The remarkable connection: AdaBoost is stagewise additive modeling with the exponential loss ℓ(y,F)=exp⁡(−yF)\ell(y, F) = \exp(-y F).

Theorem. Define FT(x)=∑t=1Tαtht(x)F_T(x) = \sum_{t=1}^T \alpha_t h_t(x). AdaBoost's choice of (ht,αt)(h_t, \alpha_t) at round tt is exactly the pair that minimizes

∑iexp⁡ ⁣(−yi (Ft−1(xi)+α h(xi))),\sum_i \exp\!\big(-y_i\,(F_{t-1}(x_i) + \alpha\, h(x_i))\big),

over all (h,α)(h, \alpha), with Ft−1F_{t-1} fixed.

Sketch. Set wi(t)=exp⁡(−yiFt−1(xi))/Ztw_i^{(t)} = \exp(-y_i F_{t-1}(x_i))/Z_t. The objective factors as ∑iwi(t)exp⁡(−yiαh(xi))\sum_i w_i^{(t)}\exp(-y_i \alpha h(x_i)). For a ±1\pm 1-valued hh, split the sum into correctly and incorrectly classified groups; the inner minimization in α\alpha for fixed hh gives the AdaBoost formula for αt\alpha_t and the outer one in hh becomes the weighted-error minimization.

This recasting matters because it generalizes: swap the exponential loss for any differentiable loss and the algorithm becomes gradient boosting (next lesson).

Why AdaBoost works

  • Bias reduction: each weak learner is high-bias, but the sum can fit arbitrarily complex functions. The training error decays exponentially in TT for weak learners with edge.
  • Margin maximization: even after training error hits zero, more rounds keep increasing the margin yiF(xi)/∑t∣αt∣y_i F(x_i)/\sum_t |\alpha_t|, which generally improves test performance. This is why AdaBoost often does not overfit even with many rounds.

Failure modes

  • Label noise wrecks AdaBoost. Mislabeled points get exponentially upweighted, and the ensemble chases them. Regularized variants (LogitBoost, GentleBoost) help.
  • Computational: each round refits on reweighted data, and the rounds are sequential. Random forests can train each tree in parallel; boosting cannot.
AdaBoost reweights the hard examples each round, so its decision boundary grows from a single stump into a flexible nonlinear separator.
AdaBoost reweights the hard examples each round, so its decision boundary grows from a single stump into a flexible nonlinear separator.

Check it on arrays

python · numpy
import numpy as np

rng = np.random.default_rng(0)

n, d = 400, 2
X = rng.normal(size=(n, d))
y = np.sign(X[:, 0] * X[:, 1] - 0.3 + 0.4 * rng.normal(size=n))

def weighted_stump(X, y, w):
    """Best 1-split stump: feature, threshold, sign, weighted error."""
    n, d = X.shape
    best = (None, None, 1, 1.0)
    for j in range(d):
        order = np.argsort(X[:, j])
        for k in range(1, n):
            t = (X[order[k - 1], j] + X[order[k], j]) / 2
            for sign in (+1, -1):
                pred = sign * np.where(X[:, j] <= t, +1, -1)
                err = np.sum(w * (pred != y))
                if err < best[3]:
                    best = (j, t, sign, err)
    return best

def adaboost(X, y, T=30):
    n = len(y)
    w = np.full(n, 1 / n)
    stumps, alphas = [], []
    for _ in range(T):
        j, t, sign, eps = weighted_stump(X, y, w)
        if eps >= 0.5 - 1e-9: break
        alpha = 0.5 * np.log((1 - eps) / max(eps, 1e-12))
        pred = sign * np.where(X[:, j] <= t, +1, -1)
        w = w * np.exp(-alpha * y * pred); w /= w.sum()
        stumps.append((j, t, sign)); alphas.append(alpha)
    return stumps, alphas

def ada_predict(X, stumps, alphas):
    F = np.zeros(len(X))
    for (j, t, sign), a in zip(stumps, alphas):
        F += a * sign * np.where(X[:, j] <= t, +1, -1)
    return np.sign(F)

# Try increasing T and watch train + held-out
idx = rng.permutation(n); tr, te = idx[:300], idx[300:]
for T in [1, 5, 20, 50]:
    stumps, alphas = adaboost(X[tr], y[tr], T=T)
    tr_acc = np.mean(ada_predict(X[tr], stumps, alphas) == y[tr])
    te_acc = np.mean(ada_predict(X[te], stumps, alphas) == y[te])
    print(f"T={T:>2}: train acc={tr_acc:.3f}  test acc={te_acc:.3f}  (stumps used: {len(stumps)})")

Training accuracy climbs as more stumps are added; test accuracy also improves and then plateaus.

Exercises

Exercise 1. A weak learner at round tt achieves weighted error εt=0.25\varepsilon_t = 0.25. Compute its ensemble weight αt\alpha_t, and the unnormalized weight-update multiplier exp⁡(−αtyiht(xi))\exp(-\alpha_t y_i h_t(x_i)) for a correctly classified example and for a misclassified one.

Solution

Step 1. Apply the ensemble-weight formula:

αt=12log⁡1−0.250.25=12log⁡0.750.25=12log⁡3≈12(1.0986)≈0.549.\alpha_t = \tfrac12 \log\frac{1 - 0.25}{0.25} = \tfrac12 \log\frac{0.75}{0.25} = \tfrac12 \log 3 \approx \tfrac12 (1.0986) \approx 0.549.

Step 2. For a correctly classified example, yiht(xi)=+1y_i h_t(x_i) = +1, so the multiplier is exp⁡(−αt)=e−0.549≈0.577\exp(-\alpha_t) = e^{-0.549} \approx 0.577. The weight shrinks.

Step 3. For a misclassified example, yiht(xi)=−1y_i h_t(x_i) = -1, so the multiplier is exp⁡(+αt)=e0.549≈1.732\exp(+\alpha_t) = e^{0.549} \approx 1.732. The weight grows. The ratio of the two multipliers is e2αt=3=(1−εt)/εte^{2\alpha_t} = 3 = (1-\varepsilon_t)/\varepsilon_t, so a miss gets upweighted by exactly the odds of the learner being right.

Exercise 2. Show that after the round-tt update with the AdaBoost choice of αt\alpha_t, the reweighted distribution makes the current weak learner hth_t look like random guessing: its weighted error under w(t+1)w^{(t+1)} equals 1/21/2.

Solution

Step 1. Split the round-tt weighted mass by correctness. Misclassified examples carry total weight εt\varepsilon_t, correct ones 1−εt1 - \varepsilon_t, under w(t)w^{(t)}. The update multiplies misses by eαte^{\alpha_t} and hits by e−αte^{-\alpha_t}.

Step 2. Substitute eαt=(1−εt)/εte^{\alpha_t} = \sqrt{(1-\varepsilon_t)/\varepsilon_t}. The new unnormalized mass on misses is εt⋅(1−εt)/εt=εt(1−εt)\varepsilon_t \cdot \sqrt{(1-\varepsilon_t)/\varepsilon_t} = \sqrt{\varepsilon_t(1-\varepsilon_t)}, and on hits is (1−εt)⋅εt/(1−εt)=εt(1−εt)(1-\varepsilon_t)\cdot \sqrt{\varepsilon_t/(1-\varepsilon_t)} = \sqrt{\varepsilon_t(1-\varepsilon_t)}. The two masses are equal.

Step 3. Since the normalizer is their sum 2εt(1−εt)2\sqrt{\varepsilon_t(1-\varepsilon_t)}, the new weighted error on the misses is

εt(1−εt)2εt(1−εt)=12.\frac{\sqrt{\varepsilon_t(1-\varepsilon_t)}}{2\sqrt{\varepsilon_t(1-\varepsilon_t)}} = \frac12.

The next round therefore cannot reuse hth_t: it has been made to look useless, forcing the boosting to find new structure.

Exercise 3 (code). Run one AdaBoost round by hand in numpy. Given fixed stump predictions and labels, compute the weighted error, αt\alpha_t, and the updated normalized weights, then verify the property from Exercise 2: the stump's weighted error under the new weights is 1/21/2.

Solution

Step 1. Start from uniform weights, score the stump, derive αt\alpha_t, apply the exponential reweighting, normalize, then recompute the weighted error.

python · numpy
import numpy as np

y    = np.array([+1, +1, -1, -1, +1, -1, +1, -1], dtype=float)
pred = np.array([+1, -1, -1, -1, +1, +1, +1, -1], dtype=float)   # 2 of 8 wrong

w = np.full(len(y), 1 / len(y))
eps = np.sum(w * (pred != y))
alpha = 0.5 * np.log((1 - eps) / eps)

w_new = w * np.exp(-alpha * y * pred)
w_new /= w_new.sum()

eps_new = np.sum(w_new * (pred != y))

assert np.isclose(eps, 0.25)              # 2 wrong out of 8 under uniform weights
assert np.isclose(eps_new, 0.5)           # reweighting neutralizes this stump
print(f"eps={eps:.3f}, alpha={alpha:.3f}, eps under new weights={eps_new:.3f}")

Step 2. The initial weighted error is 0.250.25, giving α≈0.549\alpha \approx 0.549 as in Exercise 1. After the update the same stump scores exactly 0.50.5, confirming the equal-mass result derived by hand: AdaBoost rebalances the weights so the just-used learner is no better than a coin flip.

The next lesson generalizes this idea to any differentiable loss with gradient boosting.


References

  1. [1]Freund, Y., & Schapire, R. E. (1997). "A decision-theoretic generalization of on-line learning and an application to boosting". Journal of Computer and System Sciences, 55(1), 119-139. The original AdaBoost algorithm (doi:10.1006/jcss.1997.1504).↩
  2. [2]Hastie, T., Tibshirani, R., & Friedman, J. (2009). The Elements of Statistical Learning (2nd ed.). Springer. AdaBoost as stagewise additive modeling with exponential loss (doi:10.1007/978-0-387-84858-7).↩