Start learning

Gradient Boosting

Lesson 37. About 7 minutes.

Gradient boosting builds a model step by step: each new small tree is trained to predict the errors the model is still making, and a fraction of its prediction is added on.

Your Path Through This Lesson

0 of 5 done

By the End of This Lesson You Can
  • Follow gradient boosting's first steps by hand.
  • Explain the roles of the learning rate and the number of trees.
  • Train and tune gradient boosting in scikit-learn.

The Idea: Fix the Leftovers

Imagine guessing a student's marks in rounds:

  1. Start with a simple guess: the average mark.
  2. Look at the residuals: how far off each guess is.
  3. Train a small tree to predict those residuals.
  4. Add a fraction of that tree's prediction to your guess.
  5. Repeat: new residuals, new tree, small correction.

Each tree fixes a bit of what's left. After a few hundred rounds, the corrections add up to an accurate model.

where each is a small tree and is the learning rate.

By Hand With Our Students

Marks: Noman 20, Lamia 40, Rafi 50, Tanvir 40, Arif 50 (hours 1–5). Learning rate .

Step 0. Start with the mean: every prediction is 40.

Step 1. Residuals (): −20, 0, 10, 0, 10.

Step 2. Fit a one-split tree to the residuals. The best split (as in the regression tree lesson) is hours ≤ 1.5: the left leaf predicts −20, the right leaf predicts the average of 0, 10, 0, 10 = 5.

Step 3. Update with :

Student Old prediction Tree says New prediction
Noman 40 −20
Lamia, Rafi, Tanvir, Arif 40 +5

Every prediction moved a little towards the truth. New residuals: −18, −0.5, 9.5, −0.5, 9.5. The next tree works on those.

python
import numpy as np
from sklearn.ensemble import GradientBoostingRegressor

hours = np.array([[1], [2], [3], [4], [5]])
marks = np.array([20, 40, 50, 40, 50])

gb = GradientBoostingRegressor(n_estimators=3, learning_rate=0.1, max_depth=1).fit(hours, marks)
for step, pred in enumerate(gb.staged_predict(hours), start=1):
    print(f"after tree {step}: {pred.round(2)}")
Output
after tree 1: [38.  40.5 40.5 40.5 40.5]
after tree 2: [36.2  40.95 40.95 40.95 40.95]
after tree 3: [34.58 41.36 41.36 41.36 41.36]

After tree 1 the predictions are 38 and 40.5, exactly as calculated.

Why "Gradient"?

The residual is the negative gradient of the squared-error loss with respect to the prediction. So each tree takes one step of gradient descent, not on the coefficients, but on the predictions themselves. Swap in a different loss (absolute error, log loss for classification) and the same machinery works: each tree fits that loss's negative gradient.

Learning Rate vs Number of Trees

Small learning rate (0.05) Large learning rate (0.5)
Each tree's contribution Small, careful steps Big jumps
Trees needed Many Few
Generalisation Usually better Overfits sooner

The two settings trade off: halve the learning rate and you need roughly twice as many trees.

python
from sklearn.datasets import load_diabetes
from sklearn.model_selection import train_test_split

X, y = load_diabetes(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=0)

model = GradientBoostingRegressor(n_estimators=500, learning_rate=0.05, max_depth=2, subsample=0.8, random_state=0)
model.fit(X_train, y_train)

test_scores = [np.mean((y_test - p) ** 2) for p in model.staged_predict(X_test)]
best = int(np.argmin(test_scores)) + 1
print(f"best number of trees: {best}")
print(f"test R2 with all 500 trees: {model.score(X_test, y_test):.3f}")
Output
best number of trees: 52
test R2 with all 500 trees: 0.210

After a point, extra trees start fitting noise and the test error creeps back up. In practice you find that point automatically with early stopping (n_iter_no_change) or cross-validation.

Key Settings

Setting Meaning Typical
n_estimators Number of trees 100–1000
learning_rate Shrink each tree's contribution 0.01–0.2
max_depth Depth of each tree 2–5 (shallow!)
subsample Fraction of rows per tree (stochastic boosting) 0.5–1.0
n_iter_no_change Stop early when the validation score stops improving e.g. 10
Tip

For large datasets, use HistGradientBoostingRegressor / HistGradientBoostingClassifier. They bin the features first and are many times faster, with built-in early stopping and support for missing values.

Try It Yourself
  1. Continue the hand calculation for one more tree. What split does it pick, and what are the new predictions? Check with staged_predict.
  2. Retrain the diabetes model with learning_rate=0.5. How does the best number of trees change?
The Key Idea

Gradient boosting adds small trees one at a time, each fitted to the current residuals, scaled by a learning rate.

Summary

  • Start from the mean; each tree fits the current residuals; add times its prediction.
  • "Gradient" because residuals are the negative gradient of the squared-error loss.
  • Learning rate and number of trees trade off; keep trees shallow.
  • Use early stopping or cross-validation to choose the number of trees.
  • Next: the optimised, regularised version used in competitions and industry, XGBoost.