Gradient Boosting (XGBoost, LightGBM)

Gradient-boosted decision trees are the default winning model on structured, tabular data. This page explains the algorithm from residual fitting to the second-order gain formula used by XGBoost and LightGBM, compares the major libraries, and gives a production-style training recipe with early stopping.


1. The Core Idea

Gradient Boosting: Fit the Residuals, Add, Repeat F0 = mean(y) initial guess Tree 1 fits -grad L(F0) Tree 2 fits -grad L(F1) ... M rounds F_M(x) final model residuals r1 y - F0 residuals r2 y - F1 F_m(x) = F_{m-1}(x) + eta * h_m(x) (eta = learning rate, h_m = small tree) Each tree is weak (depth 3-8 or 15-63 leaves). The ensemble is strong because errors are corrected in sequence. For squared loss the negative gradient is exactly the residual y - F(x); other losses give pseudo-residuals.

Gradient boosting (Friedman 2001) builds an additive model one small tree at a time. Each new tree is fit to the negative gradient of the loss with respect to the current predictions — for squared error that is simply the residual y - F(x). The tree's output is scaled by a learning rate η (typically 0.01–0.1) and added to the running model. Where bagging averages independent deep trees to cut variance, boosting chains shallow trees to cut bias.

Because it works on gradients, the same algorithm handles any differentiable loss: log-loss for classification, Huber or quantile loss for robust regression, Poisson or Tweedie for counts and insurance claims, and pairwise/listwise losses for ranking.

2. How a Round Works

Modern libraries (XGBoost, LightGBM, CatBoost) use a second-order Taylor expansion of the loss. For each row i they compute gradient gi and Hessian hi of the loss at the current prediction. For a candidate tree, the optimal value of leaf j with row set Ij is:

wj* = - Σgi / (Σhi + λ)

and the gain of splitting a node into left/right children is:

Gain = ½ [ GL2/(HL+λ) + GR2/(HR+λ) - (GL+GR)2/(HL+HR+λ) ] - γ

λ is L2 regularization on leaf weights, γ a minimum gain to split. Split finding only needs sums of g and h per bin, which is why histogram binning (bucketing each feature into ~256 bins) makes training so fast: it turns sorting into counting.

  1. Compute gi, hi for all rows at current predictions.
  2. Optionally subsample rows (subsample) and columns (colsample_bytree).
  3. Grow a tree greedily using the gain formula over feature histograms.
  4. Set leaf values wj*, multiply by η, add to predictions.
  5. Evaluate on the validation set; stop if no improvement for k rounds.

3. XGBoost vs LightGBM vs CatBoost

Level-wise (XGBoost) vs Leaf-wise (LightGBM) Growth Level-wise: balanced, grows depth by depth Leaf-wise: splits the max-gain leaf

XGBoost popularized the regularized second-order objective and grows trees level-wise by default (it also supports grow_policy="lossguide"). LightGBM grows leaf-wise — always splitting the leaf with the largest gain — which reaches lower loss with fewer leaves but can overfit small data unless num_leaves and min_child_samples are constrained; it also adds gradient-based one-side sampling and exclusive feature bundling for speed. CatBoost uses symmetric (oblivious) trees and ordered target statistics for categorical features, which reduces target leakage and usually needs the least tuning on categorical-heavy data. scikit-learn's HistGradientBoostingClassifier is a LightGBM-style implementation with no extra dependency.

XGBoostLightGBMCatBoost
Default growthLevel-wiseLeaf-wiseSymmetric
CategoricalsNative (enable_categorical)Native (categorical_feature)Native, ordered encoding
Main complexity knobmax_depthnum_leavesdepth
GPUYesYesYes

4. Learning Rate, Rounds and Early Stopping

Early Stopping: Train vs Validation Loss boosting rounds best iteration train loss keeps falling forever validation loss bottoms out, then rises Set n_estimators high and let early_stopping_rounds pick M.

Learning rate and number of rounds trade off against each other: halving η roughly doubles the rounds needed and usually improves generalization slightly. The standard recipe is to fix a small learning rate (0.03–0.1), set the round count high, and let early stopping on a validation set pick the iteration. Unlike a random forest, boosting does overfit as rounds increase, so early stopping is not optional.

5. Example: LightGBM with Early Stopping

import lightgbm as lgb
import numpy as np
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_absolute_error

X, y = fetch_california_housing(return_X_y=True, as_frame=True)
X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.2, random_state=0)
X_tr, X_va, y_tr, y_va = train_test_split(X_tr, y_tr, test_size=0.2, random_state=0)

model = lgb.LGBMRegressor(
    n_estimators=5000,        # upper bound; early stopping picks the real number
    learning_rate=0.05,
    num_leaves=31,            # main capacity knob (leaf-wise growth)
    min_child_samples=20,
    subsample=0.8, subsample_freq=1,
    colsample_bytree=0.8,
    reg_lambda=1.0,
)
model.fit(X_tr, y_tr,
          eval_set=[(X_va, y_va)],
          callbacks=[lgb.early_stopping(100), lgb.log_evaluation(200)])

print("best iteration:", model.best_iteration_)
print("test MAE: %.3f" % mean_absolute_error(y_te, model.predict(X_te)))

# The equivalent in XGBoost:
# xgb.XGBRegressor(n_estimators=5000, learning_rate=0.05, max_depth=6,
#                  subsample=0.8, colsample_bytree=0.8,
#                  early_stopping_rounds=100, tree_method="hist")

Keep three splits: train, validation (for early stopping), and test (untouched). Using the test set for early stopping leaks it into model selection.

6. Tuning Order

  1. Capacity: num_leaves / max_depth and min_child_samples / min_child_weight.
  2. Randomization: subsample and colsample_bytree in 0.6–1.0.
  3. Regularization: reg_lambda, reg_alpha, min_split_gain.
  4. Finally lower the learning rate and re-run early stopping for a small final gain.

Bayesian search (Optuna) over 50–200 trials with early stopping inside each trial is the usual practice. Monotonic constraints (monotone_constraints) are worth knowing for regulated domains where a feature must only push predictions one way.

7. Pitfalls and Trade-offs

8. Worked Example: Two Rounds by Hand

Two Boosting Rounds on Four Points (learning rate 0.5) F0 = 4 MSE 6.5 Tree 1 x<=2: -2.5 | +2.5 F1 MSE 1.81 Tree 2 x<=2: -1.25 | +1.25 F2 MSE 0.64 Data: x = 1, 2, 3, 4 and y = 1, 2, 6, 7. Each tree is fit to the current residuals, then scaled by 0.5 before adding. F1 predicts 2.75 / 5.25; F2 predicts 2.125 / 5.875 for the left / right groups. Shrinkage means each round corrects only part of the error, leaving room for later trees.

With squared loss ½(y − F)2, the gradient is g = F − y and the Hessian is 1, so the leaf formula w* = −Σg / (Σh + λ) with λ = 0 is simply the mean residual in the leaf. Take four rows, x = 1, 2, 3, 4 and y = 1, 2, 6, 7, and a learning rate of 0.5.

  1. Initialize with the mean, F0 = 4. Residuals are −3, −2, 2, 3; MSE is 6.5.
  2. Round 1. The best stump splits at x ≤ 2, with leaf values −2.5 and +2.5. Adding half of that gives predictions 2.75 and 5.25, residuals −1.75, −0.75, 0.75, 1.75 and MSE about 1.81.
  3. Round 2. Fitting the new residuals, the same split is still the best (it reduces squared error from 7.25 to 1.0, versus about 3.17 for the alternatives), now with leaves ±1.25. Predictions become 2.125 and 5.875; MSE about 0.64.

Two things are visible. Training error falls geometrically, and with a learning rate of 1.0 it would have dropped to the within-group error in a single round — shrinkage deliberately slows this so later trees, built on different splits and row samples, share the work. And a stump can never separate rows 1 and 2; a deeper tree (or later rounds that pick x ≤ 1) is needed, which is why tree depth controls the order of feature interactions the ensemble can represent.

9. Choosing the Objective

ProblemLightGBM objectiveNotes
Regression, meanregressionSquared error; sensitive to outliers.
Regression with outliershuber, regression_l1Bounded gradients for large residuals.
Prediction intervalsquantile with alphaTrain one model per quantile, e.g. 0.1 and 0.9.
CountspoissonLog link; predictions stay positive.
Claims, zero-inflated amountstweedieCommon in insurance pricing.
Binary classificationbinaryLog-loss; check calibration before using as probabilities.
MulticlassmulticlassOne tree per class per round; cost scales with class count.
RankinglambdarankNeeds query groups; optimizes an NDCG-oriented objective.

Choosing the loss is a modeling decision, not a tuning detail: it decides which statistic of the conditional distribution (mean, median, quantile) the model estimates. Match it to the business metric, and evaluate with that metric rather than whatever the library reports by default.

10. Interpretation and Production

On medium-sized tabular data, boosted trees remain a strong default compared with deep networks; benchmarks such as Grinsztajn et al. (2022) found tree ensembles ahead on many such datasets. Compare against a random forest and a regularized linear model before committing.

11. Further Reading