Ensemble Methods2001intermediate11 min read

Greedy Function Approximation: A Gradient Boosting Machine

التقريب الجشع للدوال: آلة التعزيز التدرُّجي

Friedman, J. H. — Annals of Statistics

The problem

Supervised learning needs a way to build highly accurate predictive models from data. Single decision trees are interpretable but weak. AdaBoost showed that combining many weak learners could produce a strong one, but it was limited to exponential loss and . Researchers needed a general framework that could combine weak learners to minimize any differentiable — for , classification, and robust estimation alike.

The contribution

Friedman reframed as in function space. Instead of adjusting parameters, each iteration adds a new (typically a small ) that approximates the negative of the loss function — the direction of steepest improvement. This yields a general "" paradigm that works with any differentiable loss: least squares, absolute deviation, for robust regression, and logistic/multinomial deviance for classification. He introduced () for , showed that stochastic subsampling improves , and developed tree-specific enhancements that make the algorithm highly practical.

The impact

Gradient boosting became arguably the most successful supervised learning algorithm for structured/tabular data. XGBoost, LightGBM, and CatBoost — all direct descendants — have dominated Kaggle competitions and power production systems from fraud detection to medical diagnosis. The paper's conceptual leap (optimization in function space) also influenced how researchers think about model ensembles, additive models, and the relationship between statistical learning and numerical optimization.

Think of a committee of short-sighted advisors. The first advisor glances at the problem and gives a rough prediction — far from perfect. A second advisor is shown only the errors the first one made and focuses exclusively on correcting those. A third advisor sees only the errors remaining after the first two. Each advisor is weak alone, but their combined advice — stacked correction upon correction — becomes remarkably accurate.

That is gradient boosting: an assembly line of small, imperfect models, each one sculpting away the residual errors left by all its predecessors.

The problem: single models are weak, brute-force ensembles are blind

By the late 1990s, machine learning practitioners faced a frustrating dilemma. A single decision tree is transparent and fast but makes crude predictions — it's the "weak learner" of theory. You could grow a big tree, but it overfits the data and performs poorly on new examples.

Ensemble methods offered an escape. AdaBoost had shown that combining many weak learners, each weighted by its accuracy, could produce a strong classifier. But AdaBoost was tied to the exponential loss function and lacked a clear path to regression, robust estimation, or arbitrary objectives. Meanwhile, (as in random forests) reduced variance by averaging independent trees, but each tree worked in isolation — none could learn from another's mistakes.

What was missing was a principled way to build an ensemble sequentially, where each new model specifically targets the errors remaining from the previous round, and do so for any loss function the practitioner cares about.

Open in Lab
Compare how AdaBoost re-weights samples versus how Gradient Boosting fits residuals. Toggle between the two to see the fundamental difference.
The demo wakes as you arrive…

The insight: gradient descent, but in function space

In ordinary gradient descent, you have parameters θ\theta and a loss L(θ)L(\theta). You compute ∇θL\nabla_\theta L and take a step: θ←θ−η∇θL\theta \leftarrow \theta - \eta \nabla_\theta L. The key difficulty is choosing the right parameterization.

Friedman's breakthrough was to do gradient descent in function space. Instead of asking "which direction should I move my parameters?", he asked: "at each data point, which direction should I move my prediction to reduce the loss?" The answer is the negative gradient of the loss with respect to the current prediction:

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

These rir_i are called pseudo-residuals. For squared error they literally equal the ordinary residuals yi−F(xi)y_i - F(x_i). For other loss functions they point in the direction of steepest improvement at each data point. The trick is: you can't follow the gradient literally (you don't have a free prediction at every point), so instead you fit a new tree to approximate those pseudo-residuals. The tree becomes a finite, generalizable version of the ideal gradient step.

Open in Lab
Step through 5 boosting rounds. Watch how pseudo-residuals shrink as the ensemble corrects errors iteration by iteration.
The demo wakes as you arrive…

The algorithm: gradient boosting step by step

The gradient boosting algorithm has an elegant structure. First, you choose a loss function L(y,F)L(y, F) matching your problem — squared error for regression, logistic deviance for classification, or Huber loss for robust regression. Then you iterate:

1. Initialize with a constant prediction: F0(x)=arg⁡min⁡c∑iL(yi,c)F_0(x) = \arg\min_c \sum_i L(y_i, c) (for squared error, this is just the mean of yy).

2. For each round m=1,2,…,Mm = 1, 2, \ldots, M:

  • Compute pseudo-residuals: rim=−∂L(yi,Fm−1(xi))∂Fm−1(xi)r_{im} = -\frac{\partial L(y_i, F_{m-1}(x_i))}{\partial F_{m-1}(x_i)}
  • Fit a regression tree hm(x)h_m(x) to the pseudo-residuals {rim}\{r_{im}\}
  • Find the optimal step size γm\gamma_m via : γm=arg⁡min⁡γ∑iL(yi,Fm−1(xi)+γ⋅hm(xi))\gamma_m = \arg\min_\gamma \sum_i L(y_i, F_{m-1}(x_i) + \gamma \cdot h_m(x_i))
  • Update: Fm(x)=Fm−1(x)+η⋅γm⋅hm(x)F_m(x) = F_{m-1}(x) + \eta \cdot \gamma_m \cdot h_m(x)

3. Output the final ensemble FM(x)F_M(x).

The factor η\eta (shrinkage / learning rate) scales down each tree's contribution, forcing the algorithm to take smaller, more cautious steps. This dramatically improves generalization at the cost of requiring more trees.

Fm(x)=Fm−1(x)+η⋅γm⋅hm(x)F_m(x) = F_{m-1}(x) + \eta \cdot \gamma_m \cdot h_m(x)
Gradient boosting update rule — the heart of the algorithm — At each round, the model adds a new weak learner that focuses on correcting the remaining errors of the current ensemble. The contribution of this learner is scaled before being added to the existing model. The final prediction is obtained by accumulating all of these incremental corrections.
Open in Lab
Click "Next Round" to add trees one at a time. Watch the ensemble prediction (green) converge toward the true function (blue dashes).
The demo wakes as you arrive…

The toolbox: loss functions for every problem

The power of gradient boosting lies in its modularity. Swap the loss function and you get a completely different behavior — all without changing the core algorithm. Friedman presented specific instantiations for the most important cases:

Least Squares (LS): L=12(y−F)2L = \frac{1}{2}(y - F)^2. The pseudo-residuals are simply (y−F)(y - F). Fast and smooth, but sensitive to outliers because large errors get squared.

Least Absolute Deviation (LAD): L=∣y−F∣L = |y - F|. Pseudo-residuals are sign(y−F)\text{sign}(y - F). Robust to outliers since all errors contribute equally in magnitude, but the gradient has no smoothness — it jumps at zero.

Huber loss: L=12(y−F)2L = \frac{1}{2}(y-F)^2 when ∣y−F∣≤δ|y-F| \leq \delta, else δ(∣y−F∣−δ/2)\delta(|y-F| - \delta/2). A hybrid — quadratic for small errors (smooth gradients) and linear for large errors (robust to outliers). The threshold δ\delta controls the transition.

Logistic / Deviance (classification): L=log⁡(1+e−2yF)L = \log(1 + e^{-2yF}) where y∈{−1,+1}y \in \{-1, +1\}. The negative gradient pushes misclassified points harder. Extends naturally to multiclass via multinomial deviance.

The mental model: each loss function shapes the landscape the algorithm descends through. Squared error creates a smooth bowl. Absolute deviation creates a V-shaped valley. Huber creates a bowl that flattens into a valley for outliers.

Open in Lab
Drag the outlier point to see how each loss function responds. Notice how Huber loss transitions from quadratic to linear behavior.
The demo wakes as you arrive…

Taming the greed: shrinkage and stochastic boosting

The "greedy" in the title is both the algorithm's strength and its Achilles heel. Each tree greedily corrects the current errors, but if each correction is too aggressive, the ensemble overfits. Friedman introduced two powerful regularization techniques:

Shrinkage (learning rate η\eta): Scale each tree's contribution by a small factor η∈(0,1]\eta \in (0, 1], typically 0.010.01–0.10.1. Think of it as a volume knob on each advisor: turning it down means each individual correction is quieter, but the committee needs more rounds to converge. Empirically, small η\eta with more trees almost always outperforms large η\eta with few trees. The smaller the steps, the better the generalization — at the cost of more computation.

Stochastic gradient boosting: At each round, fit the tree on a random subsample of the training data (typically 50–80%) instead of all of it. This adds randomness that reduces variance (similar to the benefit of bagging), speeds up each iteration, and acts as additional regularization. The idea parallels in parameter optimization.

Open in Lab
Drag the learning rate slider and watch overfitting emerge at high values. Low learning rates need more trees but generalize better.
The demo wakes as you arrive…

TreeBoost: why trees are the ideal base learner

Gradient boosting works with any base learner in principle, but regression trees are the overwhelmingly popular choice — and Friedman showed why. Trees partition the input space into disjoint regions {Rj}j=1J\{R_j\}_{j=1}^J and predict a constant in each:

h(x)=∑j=1Jcj⋅1(x∈Rj)h(x) = \sum_{j=1}^{J} c_j \cdot \mathbf{1}(x \in R_j)

This structure unlocks a critical optimization: instead of a single step size γ\gamma for the whole tree, Friedman's TreeBoost uses a separate optimal value γj\gamma_j in each leaf region, found by line search within that region:

γj=arg⁡min⁡γ∑xi∈RjL(yi,Fm−1(xi)+γ)\gamma_j = \arg\min_\gamma \sum_{x_i \in R_j} L(y_i, F_{m-1}(x_i) + \gamma)

This means each leaf takes its own best step — different parts of the input space can correct at different rates. It's like having a different-sized chisel for each part of the sculpture.

Tree depth controls model complexity. Friedman found that shallow trees (J=4J = 4–88 terminal nodes) work best. Each tree captures low-order interactions (depth 1 = no interactions, pure additive; depth 2 = pairwise interactions). The ensemble then builds up complex interactions through the sum of many simple ones.

γj=arg⁡min⁡γ∑xi∈RjL(yi,Fm−1(xi)+γ)\gamma_j = \arg\min_\gamma \sum_{x_i \in R_j} L(y_i, F_{m-1}(x_i) + \gamma)
Per-leaf optimal step — TreeBoost's key enhancement — Unlike standard gradient boosting, each leaf of the tree can receive its own correction amount. This value is chosen to minimize the prediction error for the samples that fall into that leaf. As a result, different regions of the feature space can adjust at different rates, leading to more accurate updates.
Open in Lab
Explore the TreeBoost architecture. Click on any leaf node to see how its optimal step size is computed independently.
The demo wakes as you arrive…

Reading the model: feature importance and partial dependence

Despite being an ensemble of hundreds of trees, gradient boosting offers practical interpretability tools that Friedman highlighted:

Relative measures how much each contributes to reducing the loss. For each tree, the improvement in the splitting criterion at every node is attributed to the feature used for that split, then averaged over all trees in the ensemble. Features that appear in more splits and produce larger improvements rank higher. This gives practitioners a way to identify the most predictive variables in their data.

plots show the marginal effect of one or two features on the prediction, averaged over the values of all other features. They answer the question: "holding everything else constant, how does changing this feature affect the predicted outcome?" This reveals the shape of the relationship — whether it's linear, threshold-like, or has a more complex form.

These tools made gradient boosting not just accurate but actionable — practitioners could understand which features mattered and how they influenced predictions, a crucial requirement in domains like medicine and finance.

Open in Lab
See how feature importance is accumulated across trees. Click a feature to highlight all splits that use it.
The demo wakes as you arrive…

The same idea in code

Gradient boosting from scratch (regression with squared error)python

Simplified to show the idea — not the real implementation.

import numpy as np
from sklearn.tree import DecisionTreeRegressor

def gradient_boosting(X, y, n_rounds=100, lr=0.1, max_depth=3):
    """Build a gradient boosting ensemble for squared-error loss."""
    # Step 1: initialize with the mean
    F = np.full(len(y), y.mean())
    trees, gammas = [], []

    for m in range(n_rounds):
        # Step 2a: pseudo-residuals = negative gradient of L = 0.5*(y-F)^2
        residuals = y - F  # for squared error: -dL/dF = y - F

        # Step 2b: fit a small tree to the pseudo-residuals
        tree = DecisionTreeRegressor(max_depth=max_depth)
        tree.fit(X, residuals)

        # Step 2c: get the tree's predictions (acts as gradient step direction)
        h = tree.predict(X)

        # Step 2d: update ensemble with learning rate (shrinkage)
        F = F + lr * h

        trees.append(tree)

    return trees, lr

def predict(X, trees, lr, y_train_mean):
    """Predict with the trained ensemble."""
    F = np.full(len(X), y_train_mean)
    for tree in trees:
        F += lr * tree.predict(X)
    return F

# That's gradient boosting: fit residuals, add correction, repeat.
# XGBoost, LightGBM, CatBoost are optimized versions of this loop.

Why it mattered

  1. 1996

    AdaBoost

    Freund and Schapire introduced adaptive boosting — re-weighting misclassified samples to focus each new weak learner on hard examples. Proved boosting could turn weak classifiers into strong ones.

  2. 2000

    Additive Logistic Regression

    Friedman, Hastie, and Tibshirani gave a statistical view of boosting as stagewise additive modeling, connecting AdaBoost to forward stagewise logistic regression.

  3. 2001

    Gradient Boosting Machine

    This paper. Friedman generalized boosting to arbitrary differentiable loss functions via gradient descent in function space. TreeBoost, shrinkage, and interpretability tools.

  4. 2002

    Stochastic Gradient Boosting

    Friedman's follow-up showed that subsampling training data at each round improves both accuracy and speed — bringing bagging's variance reduction to boosting.

  5. 2016

    XGBoost

    Chen and Guestrin built an optimized, scalable implementation with second-order approximations, regularization, and systems-level engineering. Dominated Kaggle competitions and industry applications.

  6. 2017

    LightGBM

    Microsoft introduced histogram-based splitting and leaf-wise growth, dramatically speeding up training on large datasets while maintaining accuracy.

  7. 2018

    CatBoost

    Yandex's implementation added native handling of categorical features and ordered boosting to reduce prediction shift, achieving strong results with minimal preprocessing.

The XGBoost paper added second-order Taylor expansions of the loss, built-in regularization of tree complexity, and engineering innovations that made gradient boosting scalable to billions of examples. LightGBM and CatBoost continued the lineage with algorithmic and systems-level innovations. All three are direct descendants of the gradient boosting framework Friedman laid out in this paper.

CitationFriedman, Jerome H.. Greedy Function Approximation: A Gradient Boosting Machine. Annals of Statistics, 2001.

Terms in this paper