Gradient Boosting

Decision trees, one correction at a time

Mikhail Mikhasenko · Lecture 2B

AdaBoost recap

Iterative error correction with weak learners.

  • Each new tree sees the same events, reweighted.
  • Misclassified events get larger weights.
  • The final model is a weighted vote of all trees.
  • Sensitive to outliers and label noise: they keep growing in weight.

Famous use: the Viola–Jones face detector (2001).

Today: gradient boosting

Instead of reweighting events, each new tree is trained on what is still missing:

new target=residual=y−current prediction.\text{new target}=\text{residual}=y-\text{current prediction}.

The same idea works for any differentiable loss, which covers regression and classification.

A gradual approach: crocodile and turtle

A turtle visits a crocodile. Every day she covers only η=5%\eta=5\% of the remaining distance.

After dd days the remaining fraction is (1−η)d(1-\eta)^d, so the covered fraction is

1−(1−η)d≈1−e−ηd.1-(1-\eta)^d\;\approx\;1-e^{-\eta d}.

  • She never arrives exactly, but gets arbitrarily close.
  • 95% covered when (0.95)d≤0.05(0.95)^d\le0.05, i.e. after d=59d=59 days.
  • Only for ηd≪1\eta d\ll1 is the covered fraction ≈ηd\approx\eta d.

Boosting does the same: each tree covers a fraction η\eta of the remaining residual.

Illustration of a crocodile in an armchair and a turtle arriving at the window

Notation

Symbol Meaning
i=1,…,Ni=1,\ldots,N training events, with features xi=(xi1,…,xin)∈ℝnx_i=(x_{i1},\ldots,x_{in})\in\mathbb R^n and label yiy_i
F(x)F(x) model output (“score”) for an event with features xx
L(y,F)L(y,F) loss of one event; training minimizes ∑i=1NL(yi,F(xi))\sum_{i=1}^N L\big(y_i,F(x_i)\big)
m=1,…,Mm=1,\ldots,M boosting round; FmF_m is the model after mm trees, F0F_0 a constant
γm(x)\gamma_m(x) output of tree mm; its leaves are regions RmjR_{mj}, j=1,…,Jmj=1,\ldots,J_m, with values γmj\gamma_{mj}
η∈(0,1]\eta\in(0,1] learning rate (“shrinkage”): the fraction of each tree that is added

Start with regression

Features x∈ℝnx\in\mathbb R^n, label y∈ℝy\in\mathbb R.

Squared-error loss per event:

L(y,F)=12(y−F)2.L(y,F)=\tfrac12\,\big(y-F\big)^2 .

Its average over events is (half) the MSE. The factor 12\tfrac12 only simplifies derivatives.

The residual of event ii is what the model still misses:

ri=yi−F(xi).r_i=y_i-F(x_i).

It is also minus the slope of the loss:

ri=−∂L(yi,F)∂F|F=F(xi).r_i=-\frac{\partial L(y_i,F)}{\partial F}\bigg|_{F=F(x_i)}.

This second form generalizes to other losses.

Reaching the target in steps

Each tree is a function of xx. The model is their sum:

F0F_0 constant
+η+\,\eta
γ1(x)\gamma_1(x): fit to y−F0y-F_0
+η+\,\eta
γ2(x)\gamma_2(x): fit to y−F1y-F_1
+⋯+\;\cdots

FM(x)=F0+η∑m=1Mγm(x),Fm(x)=Fm−1(x)+ηγm(x).F_M(x)=F_0+\eta\sum_{m=1}^{M}\gamma_m(x),\qquad F_m(x)=F_{m-1}(x)+\eta\,\gamma_m(x).

Tree mm is trained on the residuals left over by the previous m−1m-1 trees.

Algorithm for squared error

  1. Start with a constant: F0=y‾=1N∑iyiF_0=\bar y=\frac1N\sum_i y_i.
  2. For m=1,…,Mm=1,\ldots,M:
    1. residuals ri=yi−Fm−1(xi)r_{i}=y_i-F_{m-1}(x_i) for every event;
    2. grow a regression tree γm\gamma_m that predicts rir_i from xix_i;
    3. update Fm(x)=Fm−1(x)+ηγm(x)F_m(x)=F_{m-1}(x)+\eta\,\gamma_m(x).
  3. Predict ŷ(x)=FM(x)\hat y(x)=F_M(x).

Step 2b hides the two technical questions: which regions RmjR_{mj} (splitting) and which values γmj\gamma_{mj} (leaves).

Why a constant y‾\bar y? It is the best single number:

∂∂c∑i12(yi−c)2=0⇒c=y‾.\frac{\partial}{\partial c}\sum_i\tfrac12(y_i-c)^2=0\;\Rightarrow\;c=\bar y.

Why η<1\eta<1? Small steps average over many trees and fit the noise more slowly.

J. H. Friedman, “Greedy Function Approximation: A Gradient Boosting Machine”, Ann. Stat. 29 (2001) 1189.

A tree is a piecewise-constant function

Tree mm cuts feature space into JmJ_m disjoint leaves Rm1,…,RmJmR_{m1},\ldots,R_{mJ_m}. In each leaf it returns a constant γmj\gamma_{mj}:

γm(x)=∑j=1Jmγmj𝟏[x∈Rmj],\gamma_m(x)=\sum_{j=1}^{J_m}\gamma_{mj}\,\mathbf 1\big[x\in R_{mj}\big],

where 𝟏[⋅]=1\mathbf 1[\cdot]=1 if the condition holds and 00 otherwise.

So the update reads

Fm(x)=Fm−1(x)+η∑j=1Jmγmj𝟏[x∈Rmj].F_m(x)=F_{m-1}(x)+\eta\sum_{j=1}^{J_m}\gamma_{mj}\,\mathbf 1\big[x\in R_{mj}\big].

tree m=4m=4, J4=4J_4=4 leaves
x1<0.4x_1<0.4?
yes
x2<0.7x_2<0.7?
R41R_{41}
γ41=2.9\gamma_{41}=2.9
R42R_{42}
γ42=−1.9\gamma_{42}=-1.9
no
x1<0.8x_1<0.8?
R43R_{43}
γ43=0.3\gamma_{43}=0.3
R44R_{44}
γ44=0.7\gamma_{44}=0.7

How leaf values are computed

Take the regions RmjR_{mj} as given. Choose each leaf value to minimize the loss of the events in that leaf:

γmj=argminγ∑i:xi∈RmjL(yi,Fm−1(xi)+γ).\gamma_{mj}=\arg\min_{\gamma}\sum_{i:\,x_i\in R_{mj}}L\big(y_i,\,F_{m-1}(x_i)+\gamma\big).

Leaves do not interact: every leaf is a separate one-number problem.

For L=12(y−F)2L=\tfrac12(y-F)^2, with NjN_{j} events in the leaf:

∂∂γ∑i∈Rmj12(ri−γ)2=0⇒γmj=1Nj∑i∈Rmjri.\frac{\partial}{\partial\gamma}\sum_{i\in R_{mj}}\tfrac12\big(r_i-\gamma\big)^2=0 \;\Rightarrow\; \gamma_{mj}=\frac1{N_{j}}\sum_{i\in R_{mj}}r_i .

The leaf value is the mean residual in the leaf.

Example: residuals 1.2,0.8,1.01.2,\,0.8,\,1.0 give γ=1.0\gamma=1.0; with η=0.3\eta=0.3 these events move up by 0.30.3.

How a split is chosen

A node holds events II. A candidate split uses feature kk and cut cc: IL={i∈I:xik<c}I_L=\{i\in I:\,x_{ik}<c\}, IR=I\ILI_R=I\setminus I_L.

With the optimal leaf value (the mean), a node’s loss is ∑i∈I12(ri−r‾I)2=12∑i∈Iri2−12(∑i∈Iri)2NI.\;\sum_{i\in I}\tfrac12(r_i-\bar r_I)^2=\tfrac12\sum_{i\in I}r_i^2-\tfrac12\frac{(\sum_{i\in I}r_i)^2}{N_I}. The ∑ri2\sum r_i^2 terms cancel in the loss reduction:

Gain(k,c)=12[(∑i∈ILri)2NL+(∑i∈IRri)2NR−(∑i∈Iri)2NI]≥0.\text{Gain}(k,c)=\frac12\left[\frac{\big(\sum_{i\in I_L}r_i\big)^2}{N_L}+\frac{\big(\sum_{i\in I_R}r_i\big)^2}{N_R}-\frac{\big(\sum_{i\in I}r_i\big)^2}{N_I}\right]\;\ge 0 .

  1. For each feature kk, sort the events by xikx_{ik}; candidate cuts are midpoints between neighbouring distinct values.
  2. Scan left to right, updating ∑ILri\sum_{I_L}r_i and NLN_L with running sums: all cuts in 𝒪(N)\mathcal O(N) after sorting.
  3. Take the (k,c)(k,c) with the largest gain; recurse into both children until the depth limit, a minimum leaf size, or no positive gain.

Where does the first tree cut?

Boosting a regression, one tree at a time

Other losses: pseudo-residuals

For MSE the residual is minus the gradient. For any differentiable loss define the pseudo-residual

rim=−[∂L(yi,F)∂F]F=Fm−1(xi).r_{im}=-\left[\frac{\partial L(y_i,F)}{\partial F}\right]_{F=F_{m-1}(x_i)} .

Treat the NN values F(x1),…,F(xN)F(x_1),\ldots,F(x_N) as parameters. −∂L/∂F-\partial L/\partial F is the direction of steepest descent. The tree generalizes that direction to all xx.

Gradient boosting = gradient descent in function space.

Loss L(y,F)L(y,F) Pseudo-residual rr
12(y−F)2\tfrac12(y-F)^2 y−Fy-F
|y−F|\lvert y-F\rvert sign(y−F)\operatorname{sign}(y-F)
Huber, width δ\delta y−Fy-F, clipped to [−δ,δ][-\delta,\delta]
BCE, y∈{0,1}y\in\{0,1\} y−σ(F)y-\sigma(F)

σ(F)=1/(1+e−F)\sigma(F)=1/(1+e^{-F}) is defined on the classification slides.

Leaf values for any loss

Expand each event’s loss around the current prediction, to second order in the small update γ\gamma:

L(yi,Fm−1(xi)+γ)≈L(yi,Fm−1(xi))+giγ+12hiγ2,gi=∂L∂F|Fm−1(xi),hi=∂2L∂F2|Fm−1(xi).L\big(y_i,F_{m-1}(x_i)+\gamma\big)\approx L\big(y_i,F_{m-1}(x_i)\big)+g_i\,\gamma+\tfrac12 h_i\,\gamma^2, \qquad g_i=\frac{\partial L}{\partial F}\bigg|_{F_{m-1}(x_i)},\;\; h_i=\frac{\partial^2 L}{\partial F^2}\bigg|_{F_{m-1}(x_i)} .

In leaf jj sum the derivatives: Gj=∑i∈RmjgiG_j=\sum_{i\in R_{mj}}g_i, Hj=∑i∈Rmjhi\;H_j=\sum_{i\in R_{mj}}h_i. Add a penalty 12λγ2\tfrac12\lambda\gamma^2 (λ≥0\lambda\ge0) against large leaf values:

leaf objective=Gjγ+12(Hj+λ)γ2→∂/∂γ=0γmj*=−GjHj+λ,loss change=−12Gj2Hj+λ.\text{leaf objective}\;=\;G_j\gamma+\tfrac12\big(H_j+\lambda\big)\gamma^2 \quad\xrightarrow{\;\partial/\partial\gamma=0\;}\quad \gamma_{mj}^{*}=-\frac{G_j}{H_j+\lambda}, \qquad\text{loss change}=-\frac12\,\frac{G_j^2}{H_j+\lambda}.

This is one Newton step per leaf. Check with MSE: gi=F−yi=−rig_i=F-y_i=-r_i, hi=1h_i=1, so γmj*=∑ri/(Nj+λ)\gamma^*_{mj}=\sum r_i/(N_j+\lambda), the mean residual for λ=0\lambda=0.

T. Chen, C. Guestrin, “XGBoost: A Scalable Tree Boosting System”, KDD 2016, arXiv:1603.02754.

Split gain for any loss

Insert the best leaf values into the objective. Splitting node II into ILI_L and IRI_R reduces the loss by

Gain=12[GL2HL+λ+GR2HR+λ−GI2HI+λ]−τ,\text{Gain}=\frac12\left[\frac{G_L^2}{H_L+\lambda}+\frac{G_R^2}{H_R+\lambda}-\frac{G_I^2}{H_I+\lambda}\right]-\tau ,

with GL=∑i∈ILgiG_L=\sum_{i\in I_L}g_i, HL=∑i∈ILhiH_L=\sum_{i\in I_L}h_i, and likewise for IRI_R and the parent II.

Term Meaning
G2H+λ\dfrac{G^2}{H+\lambda} how much a node could lower the loss with its best single value
λ≥0\lambda\ge0 leaf-value penalty: shrinks γ*\gamma^*, disfavours leaves with little curvature HH
τ≥0\tau\ge0 cost of one extra leaf: split only if the improvement exceeds τ\tau (gamma in XGBoost)

Same search as before: sort, scan cuts with running sums GL,HLG_L,H_L, keep the best. For MSE with λ=τ=0\lambda=\tau=0 this is the gain of the regression slide.

The general algorithm

  1. F0=argminc∑iL(yi,c)F_0=\arg\min_c\sum_i L(y_i,c).
  2. For m=1,…,Mm=1,\ldots,M:
    1. compute gig_i and hih_i at Fm−1(xi)F_{m-1}(x_i) for every event;
    2. grow a tree: at each node choose the (k,c)(k,c) with the largest Gain\text{Gain}; stop at the depth limit or if no gain is positive;
    3. leaf values γmj=−Gj/(Hj+λ)\gamma_{mj}=-G_j/(H_j+\lambda);
    4. update Fm(x)=Fm−1(x)+η∑jγmj𝟏[x∈Rmj]F_m(x)=F_{m-1}(x)+\eta\sum_j\gamma_{mj}\mathbf 1[x\in R_{mj}].
  3. Output FM(x)F_M(x), or p(x)=σ(FM(x))p(x)=\sigma\big(F_M(x)\big) for classification.

Only gig_i and hih_i depend on the loss.

Changing the task means changing two derivatives, not the tree code.

The trees in round mm never see the labels directly, only gig_i and hih_i.

Classification: the score is a log-odds

Labels y∈{0,1}y\in\{0,1\}. Trees add up to an unbounded score F(x)∈ℝF(x)\in\mathbb R; map it to a probability with the logistic function

p(x)=σ(F(x))=11+e−F(x),F=logp1−p.p(x)=\sigma\big(F(x)\big)=\frac1{1+e^{-F(x)}},\qquad F=\log\frac{p}{1-p}.

Loss: binary cross-entropy (negative log-likelihood)

L(y,F)=−[ylogp+(1−y)log(1−p)]L(y,F)=-\big[y\log p+(1-y)\log(1-p)\big] L(y,F)=log(1+eF)−yF.\phantom{L(y,F)}=\log\!\big(1+e^{F}\big)-yF .

BCE loss as a function of the score F for y equals 1 and y equals 0

BCE: gradients and leaf values

With pi=σ(Fm−1(xi))p_i=\sigma\big(F_{m-1}(x_i)\big), using dσdF=σ(1−σ)\frac{d\sigma}{dF}=\sigma(1-\sigma):

gi=∂L∂F=pi−yi,hi=∂2L∂F2=pi(1−pi).g_i=\frac{\partial L}{\partial F}=p_i-y_i,\qquad h_i=\frac{\partial^2 L}{\partial F^2}=p_i(1-p_i).

Pseudo-residual: ri=yi−pir_i=y_i-p_i, the probability still missing.

Newton leaf value:

γmj=∑i∈Rmj(yi−pi)∑i∈Rmjpi(1−pi)+λ.\gamma_{mj}=\frac{\sum_{i\in R_{mj}}(y_i-p_i)}{\sum_{i\in R_{mj}}p_i(1-p_i)+\lambda}.

Start: F0=logy‾1−y‾F_0=\log\dfrac{\bar y}{1-\bar y}, the log-odds of the signal fraction y‾\bar y.

Worked example. Balanced sample, so F0=0F_0=0 and all pi=12p_i=\tfrac12. One leaf holds y=1,1,1,0y=1,1,1,0:

  • g=−12,−12,−12,+12⇒G=−1g=-\tfrac12,-\tfrac12,-\tfrac12,+\tfrac12\Rightarrow G=-1
  • h=14h=\tfrac14 each ⇒H=1\Rightarrow H=1
  • λ=0\lambda=0: γ=1\gamma=1 (leaf log-odds log3=1.10\log3=1.10)
  • η=0.3\eta=0.3: F=0.3F=0.3, p=σ(0.3)=0.57p=\sigma(0.3)=0.57

One algorithm, two losses

Regression: MSE Classification: BCE
Label and output y∈ℝy\in\mathbb R, prediction FF y∈{0,1}y\in\{0,1\}, log-odds FF, p=σ(F)p=\sigma(F)
Loss L(y,F)L(y,F) 12(y−F)2\tfrac12(y-F)^2 log(1+eF)−yF\log(1+e^{F})-yF
Start F0F_0 y‾\bar y log(y‾/(1−y‾))\log\big(\bar y/(1-\bar y)\big)
Gradient gig_i Fm−1(xi)−yiF_{m-1}(x_i)-y_i pi−yip_i-y_i
Curvature hih_i 11 pi(1−pi)p_i(1-p_i)
Leaf value γmj\gamma_{mj} ∑(yi−Fm−1(xi))Nj+λ\dfrac{\sum(y_i-F_{m-1}(x_i))}{N_j+\lambda} ∑(yi−pi)∑pi(1−pi)+λ\dfrac{\sum(y_i-p_i)}{\sum p_i(1-p_i)+\lambda}
Split gain 12[GL2/(HL+λ)+GR2/(HR+λ)−GI2/(HI+λ)]−τ\tfrac12\big[G_L^2/(H_L{+}\lambda)+G_R^2/(H_R{+}\lambda)-G_I^2/(H_I{+}\lambda)\big]-\tau same formula
Prediction FM(x)F_M(x) p(x)=σ(FM(x))p(x)=\sigma\big(F_M(x)\big), cut on pp

Boosting a classifier

Regularization

A boosted ensemble can always lower its training loss. These controls keep the validation loss low:

Control Effect
Shrinkage η\eta small steps; smaller η\eta needs more trees MM but usually generalizes better
Tree depth (1–5 typical) depth DD allows interactions among up to DD features; shallow trees are weak learners
Subsampling each tree sees a random fraction of events (stochastic gradient boosting)
Leaf penalties λ\lambda, τ\tau, minimum HH per leaf limit leaf values and forbid tiny or uninformative leaves
Early stopping stop at the MM with the lowest validation loss

Huber loss: robust regression

With r=y−Fr=y-F and a width δ>0\delta>0:

Lδ(r)={12r2,|r|≤δ,δ(|r|−12δ),|r|>δ.L_\delta(r)=\begin{cases}\tfrac12r^2, & |r|\le\delta,\\[2pt] \delta\big(|r|-\tfrac12\delta\big), & |r|>\delta.\end{cases}

  • Quadratic near 0: as efficient as least squares for Gaussian errors.
  • Linear in the tails: robust like absolute loss.
  • Pseudo-residual: rr clipped to [−δ,δ][-\delta,\delta]. An outlier’s pull is capped.

Squared, absolute and Huber losses versus residual, and their pseudo-residuals

Other losses for classification

Binomial deviance, y∈{−1,+1}y\in\{-1,+1\}

L(y,F)=log(1+e−2yF),r=−∂L∂F=2y1+e2yF.L(y,F)=\log\big(1+e^{-2yF}\big),\qquad r=-\frac{\partial L}{\partial F}=\frac{2y}{1+e^{2yF}} .

Friedman’s convention. It is the BCE of the previous slides with labels 1+y2∈{0,1}\tfrac{1+y}{2}\in\{0,1\} and score 2F2F, so F=12logp1−pF=\tfrac12\log\frac p{1-p}.

Exponential loss, y∈{−1,+1}y\in\{-1,+1\}

L(y,F)=e−yF,gi=−yie−yiF(xi).L(y,F)=e^{-yF},\qquad g_i=-y_i\,e^{-y_iF(x_i)} .

The factor e−yiF(xi)e^{-y_iF(x_i)} is exactly the AdaBoost event weight: boosting with this loss reproduces AdaBoost.

It grows exponentially for badly misclassified events, hence the noise sensitivity.

Libraries and their parameter names

Symbol XGBoost ROOT TMVA (BDT)
MM n_estimators NTrees
η\eta learning_rate (eta) Shrinkage
depth max_depth MaxDepth
subsample subsample BaggedSampleFraction
λ\lambda reg_lambda —
τ\tau gamma —
loss objective BoostType=Grad

Also: LightGBM, CatBoost; in Julia XGBoost.jl, EvoTrees.jl, JLBoost.jl.

import xgboost as xgb
clf = xgb.XGBClassifier(
    objective="binary:logistic",  # BCE
    n_estimators=500, learning_rate=0.1,
    max_depth=3, reg_lambda=1.0,
    subsample=0.8, early_stopping_rounds=20)
clf.fit(X_train, y_train,
        eval_set=[(X_val, y_val)])
p = clf.predict_proba(X_test)[:, 1]

Regression: XGBRegressor with
objective="reg:squarederror".

Impact of boosted trees

  • State of the art on tabular data. Of 29 winning Kaggle solutions published in 2015, 17 used XGBoost; 8 used it alone.
  • Industry: risk modelling in finance, click-through-rate prediction, insurance, customer modelling. Accurate, fast, and comparatively interpretable.
  • High-energy physics: for years the most common architecture for signal/background separation at the LHC, e.g. in the Higgs discoveries and flavour tagging.

Kaggle statistic: Chen & Guestrin, arXiv:1603.02754.

BDT playground

Online: Alex Rogozhnikov’s gradient boosting playground and explanation.

Julia: a Pluto adaptation with JLBoost.jl: static preview · source

Live version, from this lecture’s folder:

julia notebooks/launch.jl
Tasks
  1. Reach a test loss below 0.05 on every dataset.
  2. Which datasets need depth ≥2\ge2? Why?
  3. Fix ηM\eta M and compare η=1\eta=1 with η=0.1\eta=0.1.
  4. Compare Newton and gradient updates at equal MM.

End

Thank you for the attention