Part V — Non-Linear Models, Trees, Ensembles & Kernel Methods · Chapter 5

Gradient Boosting Machines (GBM)

Hook

AdaBoost reweights points — misclassified ones get louder. What if, instead of reweighting anything, each new model just predicted whatever the current model still gets wrong?

Intuition
Rounds = 0 — total SSE = 101.2

Five points, nowhere near a straight line. At 0 rounds the fit is just the flat average — bad everywhere. Each round after that fits a small step to whatever's still left over, and the curve bends closer to the data.

Formalize

This is gradient boosting. Start from the simplest possible model — a constant — then repeatedly fit a new small model to whatever the current ensemble still gets wrong, and add it in:

F0(x)=yˉ,Fm(x)=Fm1(x)+hm(x)F_0(x) = \bar{y}, \qquad F_m(x) = F_{m-1}(x) + h_m(x)
  • F0(x)F_0(x) — the starting prediction: just the training mean yˉ\bar{y}, before any model has run.
  • Fm(x)F_m(x) — the ensemble's prediction after mm rounds.
  • hm(x)h_m(x) — round mm's new "weak learner", here a single-split regression stump.

hmh_m is fit to each point's pseudo-residual — the negative gradient of the loss with respect to the current prediction. For squared-error loss L=12(yF)2L = \frac{1}{2}(y - F)^2, that gradient works out to something completely ordinary:

ri(m)=LF(xi)F=Fm1=yiFm1(xi)r_i^{(m)} = -\frac{\partial L}{\partial F(x_i)}\bigg|_{F = F_{m-1}} = y_i - F_{m-1}(x_i)
  • ri(m)r_i^{(m)} — point ii's pseudo-residual going into round mm.
  • yiy_i — point ii's true target value.
  1. Squared-error loss makes it literal

    For squared-error loss, "fit the negative gradient" and "fit the ordinary leftover residual" are the exact same instruction — that's why the very first boosting example anyone sees is usually just "fit the residuals," with the gradient framing hidden underneath.

  2. Any differentiable loss plugs into the same recipe

    Swap in a different differentiable loss (say, one built for classification) and the pseudo-residual formula changes, but the outer loop — fit a small model to the current gradient, add it in, repeat — doesn't.

  3. Every round only ever sees what's still wrong

    Round mm's stump never sees yy directly — only r(m)r^{(m)}, the part of yy that Fm1F_{m-1} hasn't explained yet. Whatever the ensemble already gets right stays untouched.

Play
Rounds = 0 — total SSE = 101.2
residuals: [-5.60, -4.60, 1.40, 2.40, 6.40]

Watch the residual list shrink as rounds are added — this is the "pseudo-residual" input each new stump is actually trained on, made visible.

Worked example

Five points: (1,1),(2,2),(3,8),(4,9),(5,13)(1,1), (2,2), (3,8), (4,9), (5,13).

  1. F0 is just the mean

    yˉ=(1+2+8+9+13)/5=6.6\bar{y} = (1+2+8+9+13)/5 = 6.6. Each point's residual going into round 1 is yi6.6y_i-6.6:

    • x=1x=1: 16.6=5.61-6.6=-5.6
    • x=2x=2: 26.6=4.62-6.6=-4.6
    • x=3x=3: 86.6=1.48-6.6=1.4
    • x=4x=4: 96.6=2.49-6.6=2.4
    • x=5x=5: 136.6=6.413-6.6=6.4

    Squaring and summing: (5.6)2+(4.6)2+1.42+2.42+6.42=31.36+21.16+1.96+5.76+40.96=101.2(-5.6)^2+(-4.6)^2+1.4^2+2.4^2+6.4^2 = 31.36+21.16+1.96+5.76+40.96 = 101.2.

  2. Round 1's stump finds the split at x = 2.5

    Splitting into {1,2}\{1,2\} and {3,4,5}\{3,4,5\}:

    • Left group mean: (5.64.6)/2=5.1(-5.6-4.6)/2=-5.1
    • Right group mean: (1.4+2.4+6.4)/3=3.4(1.4+2.4+6.4)/3=3.4

    This beats every other candidate split by a wide margin. F1(x)=6.65.1=1.5F_1(x) = 6.6 - 5.1 = 1.5 for x<2.5x < 2.5, and 6.6+3.4=106.6 + 3.4 = 10 for x2.5x \geq 2.5.

  3. New residuals, and a new SSE

    yF1y-F_1 at each point:

    • x=1x=1: 11.5=0.51-1.5=-0.5
    • x=2x=2: 21.5=0.52-1.5=0.5
    • x=3x=3: 810=28-10=-2
    • x=4x=4: 910=19-10=-1
    • x=5x=5: 1310=313-10=3

    Squaring and summing: (0.5)2+0.52+(2)2+(1)2+32=0.25+0.25+4+1+9=14.5(-0.5)^2+0.5^2+(-2)^2+(-1)^2+3^2 = 0.25+0.25+4+1+9=14.5 — down from 101.2101.2. Real progress, but x=3,4,5x=3,4,5 are still off by as much as 3.

  4. Round 2 fits a fresh stump to those residuals

    Splitting at x=4.5x = 4.5 (isolating the single worst point, x=5x=5):

    • Left group mean (x=1,2,3,4x=1,2,3,4): (0.5+0.521)/4=0.75(-0.5+0.5-2-1)/4=-0.75
    • Right group mean (x=5x=5): 3/1=33/1=3

    F2(x)=F1(x)0.75F_2(x) = F_1(x) - 0.75 for x<4.5x < 4.5, and F1(x)+3=13F_1(x) + 3 = 13 for x4.5x \geq 4.5:

    • x=1,2x=1,2: 1.50.75=0.751.5-0.75=0.75
    • x=3,4x=3,4: 100.75=9.2510-0.75=9.25
    • x=5x=5: 10+3=1310+3=13

    New residuals yF2y-F_2 are 0.25,1.25,1.25,0.25,00.25, 1.25, -1.25, -0.25, 0, giving a final SSE of just 0.252+1.252+1.252+0.252+02=3.250.25^2+1.25^2+1.25^2+0.25^2+0^2=3.25.

Checkpoint

Chain enough rounds of stumps to push the total squared error below 5. One round alone isn’t enough — check the numbers.

Rounds = 0 — total SSE = 101.2
Move the rounds slider to try it
Summary
Fm(x)=Fm1(x)+hm(x),hm fit to ri(m)=yiFm1(xi)F_m(x) = F_{m-1}(x) + h_m(x), \qquad h_m \text{ fit to } r_i^{(m)} = y_i - F_{m-1}(x_i)

AdaBoost reweights the data; gradient boosting reweights nothing — it just keeps handing each new weak learner a smaller and smaller piece of what's left unexplained. Two rounds here cut total error from 101.2101.2 to 3.253.25 — over 30x — and the mechanism generalizes past regression: swap the loss, and the same "fit the gradient, add it in" loop produces a boosted classifier instead.