Gradient Boosting — Step-by-Step Mathematical Derivation

Imagine you are trying to guess a number. You make a first rough guess, someone tells you "you are a little too low," so you nudge your guess up. They say "still a bit low," so you nudge again. After many small, guided nudges, you land very close to the right answer. Gradient Boosting works exactly like this: it starts with a rough guess and then adds many small correction steps, where each correction is guided by the mistakes made so far.

Before proceeding, it is helpful to establish a few terms that recur throughout this derivation:

Big picture in one line: at every round, add a new weak learner that pushes the current model in the direction that reduces the loss the fastest.


Problem Setup

Let's first write down what we are given and what we want.

Putting all together, the ideal model is the one that minimizes the total loss:

F=argminFi=1nL(yi,F(xi))

Here argminF reads as "the function F that makes the following sum as small as possible," and i=1n means "add this up across all n training examples."

FM(x)=F0(x)+m=1Mνhm(x)

Stagewise Additive Construction

"Stagewise" simply means we improve the model one stage (round) at a time, and we never go back and change earlier pieces — we only add new ones.

(1)Fm(x)=Fm1(x)+ρmhm(x) (ρm,hm)=argminρ,hi=1nL(yi,Fm1(xi)+ρh(xi))

where

Why this step? This sets up the "one round at a time" recipe and honestly admits that the ideal one-shot correction is too hard to compute. That admission is what motivates the gradient-based shortcut used in every step that follows.


Step by Step — Mathematical Derivation

Step 1: Initialize the Model

F0(x)=argminγi=1nL(yi,γ)

where argminγ means we are searching for the value γ that minimizes L(yi,γ)

Step 2: Iteration Loop

The algorithm then iterates M times (where M is the number of trees). Each iteration is denoted as m

Step 2.1: Compute the Pseudo-Residuals
(for i = 1, 2, ..., n)rim=[L(yi,F(xi))F(xi)]F(x)=Fm1(x) L(y,F)=12(yF)2

We can compute the pseudo-residual

rim=[L(yi,F(xi))F(xi)]F(x)=Fm1(x)=12L(yiFm1(xi))2Fm1(xi)rim=yiFm1(xi)
Step 2.2: Fit a Weak Learner to the Pseudo-Residuals
hm=argminhi=1n(rimh(xi))2
Step 2.3: Find the Optimal Step Size
ρm=argminρi=1nL(yi,Fm1(xi)+ρhm(xi)) ρjm=argminρxiRjmL(yi,Fm1(xi)+ρ)
Step 2.4: Update the Ensemble
hm(x)={ρ1m,xR1mρ2m,xR2mρJmm,xRJmm hm(x)=j=1Jmρjm1(xRjm) Fm(x)=Fm1(x)+νj=1Jmρjm1(xRjm) Fm(x)=Fm1(x)+ν{ρ1mif xR1mρ2mif xR2mρJmmif xRJmm New prediction=Old prediction+learning rate×tree correction
Step 2.5: Repeat the Process
FM(x)=F0(x)+νm=1Mj=1Jmρjm1(xRjm)

Why the Method Works

FmFm1νFL
In compact form, the Gradient Boosting update is:

Fm(x)=Fm1(x)+νhm(x)

where hm(x) is trained to approximate the negative gradient of the loss at stage m1.

Gradient Boosting = fit a weak learner to the negative gradient of the loss, then take a small step in that direction—repeated many times. Choosing squared-error loss recovers the "fit trees to residuals" intuition; choosing other losses (log-loss, absolute error, etc.) gives us classification, robust regression, and more, all from the same framework.


Deep Dive on Step 2.3 and 2.3

Example

Suppose at round m the tree learns two split rules on a single feature (say, house size in sq. ft.). It ends up with three leaves:

graph TD
    A["size > 1500 ?"] -->|No| B["size > 800 ?"]
    A -->|Yes| R3["Leaf R_3m
points inside: 5
value +12"] B -->|No| R1["Leaf R_1m
points inside: 4
value -7"] B -->|Yes| R2["Leaf R_2m
points inside: 6
value +3"]

Here Jm=3, so the leaves are R1m,R2m,R3m.

Input size Leaf reached Correction returned
400 R1m 7
1200 R2m +3
2000 R3m +12

Key idea for the next step: the numbers 7,+3,+12 shown above came from

Where do these numbers actually come from?

Two different things get "derived" when a tree is built, and it helps to keep them separate:

  1. The split thresholds (e.g. 800, 1500) — these define the shape of the boxes and are chosen in Step 2.2.
  2. The leaf values (e.g. -7, +3, +12) — these are the number stored in each box. Step 2.2 sets a rough version; Step 2.3 computes the optimal one.
(a) How each leaf value is derived — it is just an average of residuals.

For squared-error loss, the best constant to place in a leaf is the mean of the residuals of the points that fell into it. To see why, minimize the leaf's loss:

ρjm=argminρxiRjm12(rimρ)2

Differentiate with respect to ρ and set it to zero:

xiRjm(rimρ)=0ρjm=1|Rjm|xiRjmrim

So in our example, +3 literally means "the residuals of the 6 houses inside leaf R2m averaged to +3." Each leaf value is simply the average correction its members were asking for.

The tree is not told 800 or 1500 in advance. It scans candidate thresholds for the feature and, for each one, checks how much it reduces the squared error of the residuals (how much "purer" the two resulting groups become). It keeps the single split that reduces the error the most, then repeats the same search independently on each child group until a stopping rule (max depth, min points per leaf) is hit.

best split=argminfeature,txileft(t)(rimr¯left)2error in left box+xiright(t)(rimr¯right)2error in right box

where r¯left and r¯right are the mean residuals on each side. The threshold that makes this total smallest becomes a split like size > 800.

(c) How the feature space gets partitioned.

Every chosen split is an axis-aligned cut (size > 800). Stacking these cuts carves the input space into the non-overlapping leaf boxes R1m,R2m,R3m. In our one-feature example the "space" is just the number line of house sizes, sliced into three intervals:

R1m: size800,R2m: 800<size1500,R3m: size>1500

In one line: split thresholds come from greedily minimizing residual error (Step 2.2); leaf values come from averaging the residuals inside each box (refined in Step 2.3).