Classical ML
Gradient Boosting & XGBoost
See one residual-fitting update, then connect the additive model to gradient-based boosting for other losses.
Jump to a section
Why this matters
Gradient boosting builds an additive predictor one weak learner at a time. Each new learner is fitted to a signal derived from the current model's loss, and a learning rate controls how much of that correction is added.
For squared error, that signal is the residual. For other losses, it is the negative gradient with respect to the current predictions. This is why residual fitting is a useful starting example but not a complete description of every boosting method.
Build the mental model
Boosting is sequential error-correction. The first model makes a crude prediction; you compute its residuals (what it missed); a second small tree predicts those residuals; you add a fraction of it to the prediction; recompute residuals; repeat. Each step chips away at the remaining error. Contrast with bagging (random forests), which trains many trees independently in parallel and averages them to reduce variance: boosting reduces bias by building dependent, corrective trees.
One residual-fitting step
The initial mean is 2.2. A stump splits the first two observations from the last three; a learning rate of 0.5 moves each prediction halfway toward its group’s target.
The visual loads as you reach this section.
Inspect the plotted values
x,y,series 1,1,Target 1,2.2,Initial mean 1,1.6,After one stump 2,1,Target 2,2.2,Initial mean 2,1.6,After one stump 3,3,Target 3,2.2,Initial mean 3,2.6,After one stump 4,3,Target 4,2.2,Initial mean 4,2.6,After one stump 5,3,Target 5,2.2,Initial mean 5,2.6,After one stump
Work through the math
Build an additive model stage by stage. Starting from a constant , at step :
where is the learning rate (shrinkage) and is the new weak learner. Gradient boosting fits to the negative gradient of the loss evaluated at the current predictions: the "pseudo-residuals":
For squared-error loss , this gradient is exactly : the ordinary residuals, which is why "fit the next tree to the residuals" is the intuition. Smaller means each tree contributes less, so you need more trees but generalize better. XGBoost extends this with a second-order (Newton) approximation using gradients and Hessians, plus explicit regularization on leaf weights and tree complexity, which is why it's both accurate and resistant to overfitting. Regularize with learning rate, tree depth, subsampling of rows/columns, and the number of trees (early stopping).
Key takeaway: Boosting fits each new tree to the negative gradient of the loss (the residuals, for squared error) and adds a shrunken copy: sequential error-correction that reduces bias, where bagging averages independent trees to reduce variance.
| Bagging / Random Forest | Boosting / XGBoost | |
|---|---|---|
| Tree training | Independent, parallel | Sequential, dependent on prior trees |
| Each tree fits | Bootstrap sample of the data | Negative gradient (residuals) |
| Reduces | Variance | Bias |
| Overfitting risk | Low with deep ensembles | Higher; needs shrinkage + early stopping |
| Tabular-data performance | Strong baseline | Usually best-in-class |
Read the implementation
1import numpy as np
2from sklearn.tree import DecisionTreeRegressor
3
4rng = np.random.default_rng(0)
5X = rng.uniform(-3, 3, size=(300, 1))
6y = np.sin(X[:, 0]) + rng.normal(0, 0.1, 300)
7
8lr, n_trees = 0.1, 100
9pred = np.full(len(y), y.mean()) # F_0 = mean
10trees = []
11for _ in range(n_trees):
12 residuals = y - pred # = negative gradient for sq. loss
13 tree = DecisionTreeRegressor(max_depth=2).fit(X, residuals) # weak learner
14 pred += lr * tree.predict(X) # F_(m+1) = F_m + lr·h
15 trees.append(tree)
16
17mse = np.mean((y - pred) ** 2)
18print(f"train MSE after {n_trees} trees: {mse:.3f}")Questions and trade-offs
- Conceptual: How does boosting differ from bagging / random forests? (Boosting builds trees sequentially, each correcting the last (reduces bias); bagging trains trees independently in parallel and averages them (reduces variance).)
- Implementation: What does each new tree fit in gradient boosting? (The negative gradient of the loss at current predictions: the residuals, for squared-error loss.)
- Applied: What does the learning rate control, and what's the tradeoff? (Shrinkage: how much each tree contributes. Smaller means slower learning, more trees needed, but better generalization.)
- Systems-level: Why is XGBoost both accurate and fast? (Second-order (gradient + Hessian) optimization, regularization on leaf weights, and engineering: histogram-based splits, sparsity awareness, and parallelized tree construction.)
- Failure modes: Why can gradient boosting overfit, and how do you prevent it? (It keeps reducing training error tree by tree; control with learning rate, shallow trees, row/column subsampling, L1/L2 regularization, and early stopping on a validation set.)
Check your understanding
Without looking: write the additive update , state what each tree fits (the negative gradient / residuals), and contrast boosting with bagging in one sentence. Check against Stage 3.
Oliver Perrin
Machine Learning Engineer · Founder, LiminalML
LiminalML brings together concept explanations, mathematical examples, and working visuals. Find more writing by Oliver on the LiminalML Substack.
Keep exploring the idea.
Ask a follow-up in a guided session, work through the self-check, or open the visual in Studio to build on it.
Related concepts
Classical ML
Bias-Variance Tradeoff
Separate systematic error from sensitivity to the training sample. Read an error decomposition without assuming every model follows a U-shaped curve.
Classical ML
Logistic Regression
Connect a linear score to a sigmoid probability, a cross-entropy loss, and a decision threshold.
Classical ML
Principal Component Analysis (PCA)
Compare centered data with its projection onto a principal direction. Understand what variance retention does and does not preserve.