SafeZone AI Learn
Learn/ Machine Learning/ Supervised Learning · lesson 6 of 8

Gradient boosting

Gradient descent in function space: fit the residuals, shrink, repeat — watched live, round by round — and the second-order, regularized objective that XGBoost actually optimizes.

Forests fixed trees by averaging independent mistakes. Boosting fixes them by making the mistakes sequential: each new tree is trained on what the ensemble so far got wrong. That sounds like a heuristic; the deep result of this lesson is that it is gradient descent — the same algorithm from the Optimization module, running not over a parameter vector but over the space of functions. Once you see that, every knob (learning rate, number of rounds, tree depth) maps onto optimization concepts you already own, and XGBoost’s famous objective becomes a Newton step with regularization rather than folklore.

Forward stagewise additive modeling

Build the model as a sum of MM small functions (here, shallow trees):

FM(x)=F0(x)+m=1Mνhm(x),F_M(x) = F_0(x) + \sum_{m=1}^{M} \nu\, h_m(x),

grown greedily: at round mm, freeze everything built so far and add the one tree that most improves the loss,

hm=argminhi=1n(yi,  Fm1(xi)+h(xi)).h_m = \arg\min_h \sum_{i=1}^n \ell\big(y_i,\; F_{m-1}(x_i) + h(x_i)\big).

For squared error the inner problem is transparent: =12(yiFm1(xi)h(xi))2\ell = \tfrac12(y_i - F_{m-1}(x_i) - h(x_i))^2 is minimized by fitting hh to the residuals ri=yiFm1(xi)r_i = y_i - F_{m-1}(x_i). Boosting for regression is literally: fit a tree to what’s left over, add it, repeat.

The gradient-descent view

Friedman’s reframing: treat the vector of training predictions F=(F(x1),,F(xn))F = (F(x_1), \ldots, F(x_n)) as the “parameters.” Gradient descent on the total loss would update FFνgF \leftarrow F - \nu\, g where gi=(yi,Fi)/Fig_i = \partial \ell(y_i, F_i) / \partial F_i. We cannot move each F(xi)F(x_i) independently — the update must be a function that generalizes to new xx — so we do the next best thing: fit a tree to the negative gradient and take a step along it. For squared error, gi=yiF(xi)-g_i = y_i - F(x_i): the residuals are the negative gradient, and residual-fitting is revealed as steepest descent in function space. The recipe then generalizes to any differentiable loss by swapping the “pseudo-residuals”:

Lossgi-g_i (what the next tree fits)Character
Squared erroryiF(xi)y_i - F(x_i)plain residuals
Absolute errorsign(yiF(xi))\operatorname{sign}(y_i - F(x_i))robust to outliers
Log loss (classification)yiσ(F(xi))y_i - \sigma(F(x_i))lesson 3’s gradient, reborn
Huberclipped residualsrobustness with smoothness

The optimization module’s vocabulary now prices every hyperparameter: ν\nu is the learning rate; MM is the iteration count; and since each round reduces training loss essentially monotonically, boosting will overfit as MM \to \infty — unlike a forest, where more trees only average. Early stopping on validation loss is the regularizer, and the empirical law of the trade is that a smaller ν\nu needs proportionally more rounds but buys a lower, flatter test-error valley (shrinkage = taking many cautious steps through function space instead of few greedy ones).

Round 0 is the mean. Each press of “+1 round” fits a tree to the current residuals and adds ν times it — watch the blue ensemble absorb the data’s shape coarsest-bumps first. The sparkline tells the real story: train MSE falls monotonically, test MSE falls, bottoms out, then climbs — the overfit turn that early stopping exists to catch. Now the two experiments: (1) ν = 1.0 vs ν = 0.05 — count rounds to the test minimum and compare the minimum’s depth and width; (2) depth 1 (stumps: additive, no interactions) vs depth 4 — watch stumps struggle with curvature that depth-2 handles in a few rounds.

What XGBoost actually optimizes

Modern implementations sharpen the gradient view in three moves. Take a second-order Taylor expansion of the loss around the current prediction (with gi=Fg_i = \partial_F \ell, hi=F2h_i = \partial^2_F \ell), and add explicit tree regularization — γ\gamma per leaf, λ2\tfrac\lambda2 on leaf weights:

Obji[gih(xi)+12hih(xi)2]+γT+λ2j=1Twj2.\text{Obj} \approx \sum_i \big[ g_i\, h(x_i) + \tfrac{1}{2} h_i\, h(x_i)^2 \big] + \gamma T + \tfrac{\lambda}{2}\sum_{j=1}^{T} w_j^2 .

For a fixed tree structure with leaf sets IjI_j, this is a separate quadratic per leaf, solved in closed form:

wj=iIjgiiIjhi+λ,Obj=12j=1T(Ijgi)2Ijhi+λ+γT.w_j^\ast = -\frac{\sum_{i \in I_j} g_i}{\sum_{i \in I_j} h_i + \lambda}, \qquad \text{Obj}^\ast = -\frac{1}{2}\sum_{j=1}^{T} \frac{\big(\sum_{I_j} g_i\big)^2}{\sum_{I_j} h_i + \lambda} + \gamma T .

Read the pieces: the leaf weight is a Newton step (gradient over curvature — the adaptive-methods lesson’s preconditioning instinct, per leaf), λ\lambda shrinks it exactly as ridge shrinks coefficients, and γ\gamma sets a hard bar a split’s gain must clear to justify existing — pre-pruning by objective, not heuristic. Splits are then chosen to maximize the gain in Obj\text{Obj}^\ast, which is CART’s greedy search with a principled, loss-aware criterion. Everything else in the tool’s reputation — histogram-binned split finding, sparsity-aware defaults, column/row subsampling (bagging’s decorrelation trick, borrowed back) — is systems engineering on top of this objective. LightGBM and CatBoost differ in growth order (leaf-wise), binning, and categorical handling, not in the math above.

Using it like an adult

Boosting’s knobs interact, but the structure is now legible: capacity per round (depth 3–8; interactions need depth), step size (ν\nu 0.02–0.1), steps (MM: large, with early stopping doing the choosing), and stochastic decorrelation (subsample ~0.8). The honest comparison table for tabular work:

Random forestGradient boosting
Fittingparallel, independentsequential, error-driven
More treesnever hurtsoverfits — early-stop
Tuning effortnearly nonereal but structured
Typical accuracy ceilingstrongstate of the art on tabular
Failure modecorrelation floor, no extrapolationlabel noise chased hard; still no extrapolation

Boosted trees remain the first serious model to try on structured/tabular data in 2026 — deep learning claims the crown only where representation must be learned (images, text, audio). Both tree ensembles share the extrapolation blindness from last lesson; and boosting’s log-loss scores need the calibration check from lesson 3 before anyone treats them as probabilities.

import xgboost as xgb

model = xgb.XGBClassifier(
    n_estimators=2000, learning_rate=0.05, max_depth=5,
    subsample=0.8, colsample_bytree=0.8, reg_lambda=1.0,
    early_stopping_rounds=50, eval_metric="logloss",
)
model.fit(X_tr, y_tr, eval_set=[(X_va, y_va)], verbose=False)
print(model.best_iteration)        # M chosen by the validation curve, not by faith

Exercises

Work these before the next lesson

  1. Show that for squared error, the stagewise-optimal hmh_m is the least-squares tree fit to the residuals, and that the negative functional gradient equals those residuals — the two derivations meeting.
    Solution

    Worked solutions are part of Premiumunlock all of them for £5/month →

  2. 4 more exercises — each with a worked solution — are part of Premium. Unlock everything for £5/month →

References

  • J. Friedman, “Greedy Function Approximation: A Gradient Boosting Machine”, Annals of Statistics 2001 — the function-space view.
  • Y. Freund & R. Schapire, “A Decision-Theoretic Generalization of On-Line Learning…”, 1997 — AdaBoost, the ancestor (exponential loss).
  • T. Chen & C. Guestrin, “XGBoost: A Scalable Tree Boosting System”, KDD 2016 — the objective derived above.
  • G. Ke et al., “LightGBM”, NeurIPS 2017; L. Prokhorenkova et al., “CatBoost”, NeurIPS 2018.
  • ESL ch. 10 — boosting as additive modeling.