Chapter 6: Ensemble Methods
Harness the wisdom of crowds in machine learning. Learn how to combine multiple models for superior predictions through bagging with Random Forests, boosting with AdaBoost and XGBoost, and sophisticated stacking techniques.
- Topics
- 4
- Demos
- 5
- Min read
- 51
Chapter Overview
The fundamental insight behind ensemble methods is that a committee of models often outperforms any individual member. Just as diverse perspectives lead to better decisions in human groups, diverse models can cancel out each other's errors.
Ensemble methods come in two main flavors. Bagging (Bootstrap AGGregatING) trains multiple models independently on different random samples and averages their predictions, reducing variance. Random Forests extend this by also randomizing feature selection, creating highly decorrelated trees.
Boosting takes a different approach: train models sequentially, with each new model focusing on the mistakes of the previous ones. AdaBoost reweights samples to emphasize misclassified examples. Gradient Boosting fits each new tree to the residuals (errors) of the ensemble so far, effectively performing gradient descent in function space.
These methods dominate machine learning competitions and real-world applications. Gradient Boosting implementations like XGBoost, LightGBM, and CatBoost are go-to choices for tabular data, often achieving state-of-the-art results with proper tuning.
This chapter covers:
- Bagging: Reducing variance by training on bootstrap samples and aggregating predictions
- Random Forests: Highly effective ensemble of decorrelated decision trees, feature importance, and tuning
- AdaBoost: Adaptive boosting with sample reweighting
- Gradient Boosting: The powerhouse algorithm (XGBoost, LightGBM, CatBoost) with regularization techniques
- Voting & Stacking: Combining diverse models for superior performance
Chapter Roadmap
Click any topic to jump in
Bagging & Random Forests
Reduce variance by training on bootstrap samples and combining diverse decision trees through majority vote.
Two sequential refinement strategies
Boosting
Sequential learning where each model corrects the mistakes of previous ones via sample reweighting.
Gradient Boosting
Gradient descent in function space — XGBoost, LightGBM, and CatBoost dominate tabular data.
Stacking
Train a meta-learner to optimally combine diverse base models for superior ensemble predictions.
Topics
4 topics in this chapter
The previous chapter, Decision Trees, ended on a frustrating trade-off. A deep tree captures complex patterns but has high variance: remove one training sample and the root split can change, producing a completely different tree. A pruned tree is stable but underfits. Could we keep the low bias of deep trees and remove most of their variance? The idea behind this chapter, Ensemble Methods, is that many imperfect models, combined well, can beat any one of them.
This first topic covers the simplest way to combine models: train many on slightly different data and average them. We start with bootstrap sampling, which manufactures many training sets from one, then define bagging and show with the variance of an average why it works, and where it stops working. Random forests add feature subsampling at each split to make the trees less alike. We then use the samples each tree never saw for out-of-bag error, a free validation estimate, and finish with tuning forests and reading their feature importances.
Definition
Bagging, short for bootstrap aggregating, trains B models of the same type on B bootstrap samples of the training data, each drawn with replacement, and averages their predictions or takes a majority vote. A random forest is bagging with decision trees in which each split considers only a random subset of the features.
In this topic
- 1Bootstrap Sampling
- 2Bagging
- 3Why Bagging Works
- 4Random Forests
- 5Feature Subsampling
- 6Out-of-Bag Error
- 7RF Hyperparameters
- 8RF Feature Importance
Bootstrap Sampling
A bootstrap sample draws points from an -point training set uniformly at random with replacement. Some points appear several times and others not at all. The probability a given point is never drawn is , which approaches , about 0.368, so each bootstrap sample contains about 63.2 percent of the distinct original points. The left-out 36.8 percent are that sample's out-of-bag (OOB) points. Bootstrap samples resemble the original data but differ from each other, which makes them a cheap way to simulate drawing new training sets from the same population.
Each bootstrap draw is independent with replacement, so the probability a specific sample is never selected in draws is . This means each bootstrap sample uses roughly of the original data. The out-of-bag fraction converges to by the limit definition of itself — one of the most elegant connections between combinatorics and analysis in statistics.
Dataset: [A, B, C, D, E]. One bootstrap sample might be?
Bagging
Bagging trains copies of a base model, each on its own bootstrap sample, giving models . For regression the prediction is their average, the formula above; for classification it is a majority vote or the average of predicted probabilities. The models are independent, so training parallelizes perfectly. Bagging leaves the bias roughly unchanged, since each model is fit the same way, but reduces variance. It therefore helps unstable, low-bias learners such as deep trees and does little for stable ones such as linear regression, whose bootstrap fits barely differ.
If models each have variance and pairwise correlation , the ensemble variance is . As , the second term vanishes but the first remains — the floor is set by . Bootstrap diversity drives below 1, making the floor lower. Bagging cannot reduce bias (the average prediction of infinitely many models), only variance — which is why it works best with low-bias, high-variance base learners like deep trees.
5 trees predict: [1,0,1,1,0] for classification. Bagged prediction?
Why Bagging Works
Averaging reduces variance. If models each have prediction variance and are uncorrelated, their average has variance . Bagged models are trained on overlapping data, so their errors are correlated with some pairwise correlation , and the variance of the average becomes . More models shrink only the second term; the first is a floor that no number of trees can lower. Bagging therefore pays off most when individual models have high variance and low correlation, which motivates random forests: decorrelate the trees to lower the floor.
If predictors each have variance and pairwise correlation , their average has variance . With uncorrelated predictors () this is , a reduction by exactly . With correlated predictors () the second term vanishes as grows but the first does not, so the variance saturates at . This is why random forests add feature subsampling on top of bootstrap sampling: it decorrelates the trees, pushing down and lowering the variance floor.
Single tree variance=100. 100 uncorrelated trees, each variance=100. Ensemble variance?
Random Forests
A random forest is bagged decision trees with one addition: at each split, the tree searches only a random subset of max_features features, drawn fresh at every node. Defaults are about features for classification, where is the total count, and a larger share for regression; Breiman suggested , and scikit-learn now uses all features for regression by default. Restricting the search makes trees less alike, lowering the correlation term from the previous concept, at the cost of slightly weaker individual trees. Trees are usually grown deep and unpruned, since the averaging handles variance. Random forests are strong defaults for tabular data and need little tuning.
At each split, considering only random features out of total introduces a binomial selection process. The probability that the single best feature is available at a given split is . For , there is only a chance the dominant feature appears — forcing the tree to find alternative splits. This decorrelation is multiplicative across tree depth: by the time a tree is levels deep, the chance of every split using the best feature drops to .
100 features, classification. At each split, how many features considered?
Feature Subsampling
Why does restricting features help? Suppose one feature is far more predictive than the rest. In plain bagging, nearly every tree splits on it at the root, so trees share the same structure and make correlated errors. When each split sees only a random subset, the dominant feature is often missing, and trees must find signal in other features. Individual trees become slightly worse, but the ensemble becomes more diverse, and the reduced correlation usually outweighs the weaker trees. max_features is the main tuning knob: smaller values increase diversity and bias; larger values make trees stronger but more alike.
Feature subsampling at each split is equivalent to adding a stochastic mask to the feature set. The information gain of a split on feature is , but it is only evaluated for . A dominant feature with would normally always be selected — the mask prevents this, ensuring the ensemble explores the full feature space rather than repeatedly exploiting the same greedy path.
Feature X is super predictive. Without subsampling, all trees split on X first. Problem?
Out-of-Bag Error
Each tree is trained on a bootstrap sample, so about 36.8 percent of training points are out-of-bag for it. For each training point, we can therefore collect predictions from only the trees that never saw it, about a third of the forest, and compare their combined vote with the true label. Averaging over all points gives the out-of-bag error, an estimate of generalization error that needs no separate validation set or extra training. In scikit-learn, set oob_score=True. It is slightly pessimistic, because each point is judged by only a third of the trees, and needs enough trees to be stable.
The OOB estimate is asymptotically equivalent to leave-one-out cross-validation. For each sample , roughly trees did not see it during training. The OOB prediction aggregates these independent votes, giving an unbiased estimate of the generalization error. As , the OOB error converges to the true leave-one-out error — the variance of the OOB estimator decreases as , making it increasingly reliable with more trees.
Sample X is OOB for trees [3, 7, 12, 25]. These trees predict [1, 1, 0, 1]. OOB prediction?
RF Hyperparameters
Random forests have few important knobs. n_estimators, the number of trees, improves stability with diminishing returns and does not cause overfitting; a few hundred is typical, limited by time and memory. max_features controls tree diversity and is the most influential. max_depth, min_samples_leaf, and max_leaf_nodes limit individual trees, which matters for noisy data and model size. bootstrap and max_samples control resampling. Tune with the OOB score or cross-validation. A large gap between training and OOB accuracy is normal for forests of deep trees, so judge overfitting by whether OOB accuracy improves when trees are restricted.
The key hyperparameters control the bias-variance trade-off at two levels: controls ensemble variance (more trees always helps, never hurts), while and control individual tree bias-variance (deeper trees have lower bias but higher variance). The interaction is multiplicative: a forest of shallow trees has high bias but extremely low variance, while a forest of deep trees has low bias with variance bounded by .
RF with n_estimators=100, max_depth=None. Train=99%, OOB=85%. Overfitting?
RF Feature Importance
A forest's impurity-based importance averages each feature's weighted impurity decrease over all trees, so it is much more stable than a single tree's: the run-to-run noise of one tree averages out. Feature subsampling also gives secondary features a chance to be used, so importance is spread more fairly among correlated features than in one tree. The biases from the previous chapter remain: the scores are computed on training data and favor high-cardinality features. Permutation importance on held-out or OOB data is the more reliable check, and correlated features should be judged as a group.
Mean Decrease in Impurity (MDI) for feature is — averaged over all trees and all nodes where feature was used to split. By the law of large numbers, as this converges to . The standard error decreases as , which is why RF importance is far more stable than single-tree importance. Permutation importance provides an alternative that is unbiased for correlated features.
Single tree: age importance varies 0.1-0.4 across runs. RF with 100 trees: age=0.25±0.02. Why?
Theory Exercise
Problem:
A Random Forest with 100 trees achieves 85% accuracy. Adding more trees to 500 improves to 86%. Would adding 5000 trees likely help much more? Why?
Hints:
- Think about diminishing returns
- What happens as variance approaches its minimum?
- Consider computational cost
Coding Exercise
Problem:
Train a RandomForestClassifier with out-of-bag scoring enabled, verify the OOB score approximates held-out test accuracy (free validation), then rank the top features by importance.
Hints:
- Set bootstrap=True and oob_score=True on the classifier
- Compare clf.oob_score_ to clf.score(X_test, y_test)
- Feature importances live in clf.feature_importances_; np.argsort to rank them
Related Problems on PixelBank
Bagging, from the previous topic, Bagging and Random Forests, fixes variance, but it cannot fix bias. Average a hundred depth-1 trees trained on bootstrap samples and you get roughly the same depth-1 tree back, still too simple for the data. If our weak models systematically miss part of the pattern, averaging independent copies of them misses it too. We need models that learn from each other's mistakes.
Boosting builds an ensemble sequentially. Each new model is trained to focus on the examples that the ensemble so far gets wrong, and the final prediction is a weighted vote in which better models count more. This topic contrasts boosting with bagging, then walks through AdaBoost, the first practical boosting algorithm, step by step: what a weak learner is, how each learner earns its voting weight, how sample weights are updated, and how the final vote is formed. We close with AdaBoost's main weakness, its sensitivity to noisy labels, which motivates the gradient boosting of the next topic.
Definition
Boosting combines weak learners, models only slightly better than chance, into a strong learner by training them sequentially, each focusing on the errors of the ensemble so far. AdaBoost does this by reweighting training samples, increasing the weights of misclassified ones, and combining learners in a vote weighted by their accuracy.
In this topic
- 1Boosting vs Bagging
- 2AdaBoost Algorithm
- 3Weak Learner
- 4Learner Weight α
- 5Sample Weight Update
- 6Final Prediction
- 7AdaBoost Sensitivity
Boosting vs Bagging
Bagging and boosting both combine many models but attack different errors. Bagging trains models independently on bootstrap samples and averages them; it reduces variance, so it suits deep, low-bias trees, and it parallelizes trivially. Boosting trains models in sequence, each one adjusted to the mistakes of the ensemble so far; it mainly reduces bias, so it suits shallow, high-bias learners such as stumps. Because each round depends on the previous one, boosting cannot parallelize across models, only within each model's training. Boosting is often more accurate when tuned, but more sensitive to noise and hyperparameters.
Bagging reduces variance by averaging models: . Boosting reduces bias by additive refinement: , where each targets the current errors. If every round has an edge of at least over random guessing, AdaBoost's training error is at most , so it falls exponentially in the number of rounds . This is why boosting can reach lower bias than bagging given enough rounds, while bagging cannot reduce the bias of its base learner at all.
100-tree RF on 8 CPUs vs 100-tree AdaBoost. Training time?
AdaBoost Algorithm
AdaBoost keeps a weight on every training sample, starting uniform at . Each round it trains a weak learner on the weighted data, computes its weighted error , the total weight of misclassified samples, and gives the learner a vote that grows as falls. It then multiplies the weights of misclassified samples up and correct ones down, renormalizes them to sum to 1, and moves to the next round. After rounds, the prediction is the sign of the weighted vote. Each round therefore concentrates on the examples the current ensemble finds hardest. The algorithm is equivalent to greedily minimizing an exponential loss.
At each round , AdaBoost minimizes the exponential loss . The optimal weak learner minimizes the weighted classification error , and its weight is the log-odds of correctness. This closed-form solution means each round of AdaBoost is a coordinate descent step on the exponential loss surface.
5 samples, initial weights all 0.2. Stump misclassifies sample 3. After update?
Weak Learner
A weak learner is any model whose weighted error is reliably below one half on binary problems, better than a coin flip by some edge . Decision stumps, one-split trees, are the classic choice: fast, high bias, and different every round because the weights change. Boosting theory shows that if every round achieves edge , the training error of AdaBoost falls at least as fast as after rounds, so weak learners can be combined into a strong one. A learner that is too strong, such as a deep tree, fits the weighted data perfectly and leaves boosting little to correct while overfitting.
A weak learner need only satisfy for some edge . After rounds, AdaBoost's training error satisfies . With (55 percent accuracy) the bound falls to about 0.08 after 500 rounds; with it is still after 5,000 rounds, so the guarantee is slow for tiny edges, and in practice convergence is usually much faster than the bound. This is the formal sense in which many weak learners combine into a strong one.
Decision stump: 'if age>30 then 1 else 0'. Accuracy=55%. Is this useful for AdaBoost?
Learner Weight α
Each weak learner's vote is , half the log-odds of being correct, where is its weighted error. A learner at 50 percent error gets ; as the error drops toward 0, grows without bound; and a learner worse than chance gets a negative weight, which flips its predictions. This value is not arbitrary: it is the step size that minimizes the exponential loss for that round. The log makes votes grow quickly near zero error, so a nearly perfect learner can dominate the ensemble.
The weight is the log-odds ratio of the weak learner's correctness. When (perfect learner), ; when (random), ; when (worse than random), , effectively flipping the learner's predictions. This function is the natural parameterization of Bernoulli success probability — it maps with the derivative growing sharply near 0 and 0.5.
Stump 1: error=0.3. Stump 2: error=0.1. Calculate α for each.
Sample Weight Update
After learner votes, each sample's weight is multiplied by if the learner got it wrong and by if right, then all weights are renormalized to sum to 1. The update is larger when is large: mistakes by a confident learner matter more. After renormalization, the misclassified samples hold exactly half the total weight, so the current learner looks no better than chance on the new weights, forcing the next learner to find something different. A sample misclassified repeatedly grows exponentially, which is the root of AdaBoost's sensitivity to noise.
The weight update is an exponential reweighting scheme. For a misclassified sample: , which grows exponentially with the learner's confidence. After rounds, a sample misclassified times has weight proportional to — persistently hard examples accumulate exponentially large weights. This drives the algorithm to focus on the hardest cases, but also makes it vulnerable to label noise.
Sample has weight 0.1, α=0.5. Correctly classified. New weight?
Final Prediction
The final classifier is the sign of the weighted sum of votes, with each weak learner outputting or and voting with weight . The magnitude of the sum is a confidence score: a value near zero means the strong learners disagree. Dividing by the total of the gives the margin, between -1 and +1. AdaBoost keeps increasing the margins of training points even after training error reaches zero, which helps explain why its test error often keeps falling. Calibrated probabilities need an extra step, such as a logistic transform of the score or Platt scaling.
The ensemble prediction is a weighted majority vote in function space. The magnitude acts as a confidence score — larger magnitude means more agreement among strong learners. The margin theory shows that AdaBoost maximizes the minimum margin over training samples, analogous to how SVMs maximize geometric margin. Larger margins lead to better generalization bounds.
3 stumps: h₁=+1 (α=0.5), h₂=-1 (α=0.8), h₃=+1 (α=0.6). Final prediction?
AdaBoost Sensitivity
AdaBoost minimizes an exponential loss, which grows exponentially with how badly a point is misclassified. A mislabeled example can never be fit correctly by honest learners, so its weight is multiplied up nearly every round, and soon a handful of noisy points dominate the training distribution. Later learners then contort themselves to fit them, and test accuracy falls. Mitigations: shrink each step with a learning rate below 1, cap the number of rounds with validation or early stopping, clean labels, or switch to gradient boosting with a robust loss such as the log loss or the Huber loss.
AdaBoost minimizes the exponential loss , which penalizes misclassifications exponentially — a sample with margin incurs loss , versus for margin . Mislabeled samples always have negative margins, so their weights grow without bound: after rounds. The learning rate shrinks each to , slowing exponential growth to — providing regularization at the cost of needing more rounds.
Mislabeled sample keeps getting misclassified. After 50 rounds, its weight is 0.4 (started at 0.01). Problem?
Theory Exercise
Problem:
AdaBoost uses decision stumps (depth-1 trees) as weak learners. Why not use deep trees? What would happen?
Hints:
- What does boosting assume about weak learners?
- What happens if individual learners are too strong?
- Think about overfitting
Coding Exercise
Problem:
Train an AdaBoost classifier with decision stumps and track how training and test accuracy evolve across boosting rounds. Identify where the test curve plateaus.
Hints:
- Use AdaBoostClassifier(estimator=DecisionTreeClassifier(max_depth=1))
- staged_predict() yields the ensemble prediction after each round
- The round maximizing test accuracy is where boosting stops helping
Related Problems on PixelBank
AdaBoost, from the previous topic, Boosting, showed that a sequence of weak learners can become a strong one, but it is tied to one loss, the exponential loss, which makes it fragile under label noise and unusable as-is for regression. What if we want to boost with squared error for prices, absolute error for heavy tails, or log loss for calibrated probabilities? We need a recipe that works for any differentiable loss.
Gradient boosting provides it by treating the ensemble's prediction as the thing being optimized and taking gradient descent steps, where each step is a small regression tree. This topic builds the idea from residual fitting with squared error to the general view of gradient descent in function space, then covers shrinkage, the learning rate that makes gradient boosting generalize. We tour the three libraries that dominate tabular machine learning, XGBoost, LightGBM, and CatBoost, and finish with the regularization and early stopping that keep boosted models from overfitting.
Definition
Gradient boosting builds an additive model of regression trees in sequence. Each new tree is fit to the negative gradient of the loss with respect to the current predictions, called pseudo-residuals, and is added with a small learning rate. For squared error, the pseudo-residuals are exactly the ordinary residuals.
In this topic
- 1Gradient Boosting Idea
- 2Residual Fitting
- 3Gradient Descent in Function Space
- 4Learning Rate (Shrinkage)
- 5XGBoost
- 6LightGBM
- 7CatBoost
- 8GB Regularization
- 9Early Stopping
Gradient Boosting Idea
Start with a simple prediction, such as the mean target. Compute each training point's residual, what the current model still gets wrong, and fit a small regression tree to those residuals. Add the tree's output to the model, recompute residuals, and repeat. Each tree corrects part of the remaining error, and the final model is the sum of all trees. Because each tree only needs to capture some structure in the residuals, shallow trees of depth 3 to 8 suffice. Unlike bagging, the trees are not interchangeable: each depends on all the ones before it.
Gradient boosting views the ensemble as performing gradient descent in function space. The ensemble at step is , where approximates the negative gradient evaluated at each training point. For squared error loss, the negative gradient is exactly the residual . Each tree is a step down the loss surface — the learning rate controls step size, analogous to gradient descent in parameter space.
True y=100. After tree 1: predict 70 (residual=30). Tree 2 fits residual, outputs 20. Ensemble prediction?
Residual Fitting
Formally, the model after trees is , where is the previous ensemble, is a tree fit to the residuals , and is the learning rate, typically between 0.01 and 0.3. For squared error, the residual is exactly the negative gradient of the loss with respect to the prediction, so fitting residuals is gradient descent. Scaling each tree by means the model never fully trusts any one tree, which reduces overfitting to the noise each tree picks up, at the cost of needing more trees.
The update is a functional gradient descent step. For MSE loss, fits the residuals , which are exactly the negative gradients . The learning rate is shrinkage: it regularizes by making each step smaller, requiring more trees but generalizing better. With , each tree contributes only of its full prediction, preventing any single tree from dominating the ensemble.
F₁=70, true y=100, η=0.1. Tree 2 predicts residual=30. New prediction?
Gradient Descent in Function Space
For a general differentiable loss , gradient boosting fits each tree to the pseudo-residuals, the negative gradient at each training point. They say how each prediction should move to reduce loss fastest. For squared error they equal ordinary residuals; for absolute error they are the signs of the residuals, which caps any outlier's influence; for log loss in binary classification, with the log-odds and the predicted probability, they are . After fitting the tree's structure, each leaf's value is usually re-optimized for the actual loss, often with a Newton step.
For a general differentiable loss , the pseudo-residuals are . For log-loss (classification), this gives , where is the sigmoid — the residual between true label and predicted probability. The tree performs a least-squares fit to these pseudo-residuals, then leaf values are optimized by a Newton step: .
Classification. True y=1, current P(y=1)=0.3. Pseudo-residual?
Learning Rate (Shrinkage)
The learning rate scales every tree's contribution. Friedman found empirically that small values, 0.1 or below, combined with more trees, generalize better than large steps: each tree corrects only part of the error, so the noise any single tree fits is diluted, and the ensemble's path through function space is smoother. The number of trees needed grows roughly in proportion to , so halving the rate roughly doubles training time. The practical recipe is to fix a small rate such as 0.05 to 0.1, then let early stopping choose the number of trees.
Shrinkage and number of trees are inversely coupled: the optimal ensemble size satisfies roughly . Empirically, with trees often outperforms with trees despite both taking the same total "step size" . This is because smaller steps allow each tree to specialize on the specific residual pattern at that stage rather than making large corrections that may overshoot.
η=0.3, 100 trees: 85% accuracy. η=0.1, 100 trees: 82%. η=0.1, 300 trees?
XGBoost
XGBoost, by Chen and Guestrin, made gradient boosting fast and robust enough to dominate tabular competitions. It optimizes a regularized objective: the loss plus a penalty on the number of leaves and an L2 penalty on leaf values. It uses both first and second derivatives of the loss, giving Newton-style leaf values , where and sum the gradients and Hessians in the leaf. It also offers sparsity-aware handling of missing values, a learned default direction at each split, column and row subsampling, parallel split search, and a fast histogram method. Trees grow level by level by default.
XGBoost adds regularization on leaf weights: the objective is , where is the number of leaves and are leaf values. The optimal leaf value becomes , where are first and second derivatives of the loss. Histogram-based splitting bins continuous features into 256 buckets, reducing split evaluation from to per feature.
sklearn GradientBoosting: 5 min training. XGBoost on same data?
LightGBM
LightGBM, from Microsoft, targets speed on large data. It grows trees leaf-wise: instead of splitting every node at a given depth, it repeatedly splits the single leaf with the largest loss reduction, which lowers loss faster for the same number of leaves but can overfit small data unless num_leaves and min_data_in_leaf are limited. GOSS keeps all samples with large gradients and only a random fraction of small-gradient ones, reweighting them to stay unbiased. Exclusive Feature Bundling merges sparse features that are rarely nonzero together. Histogram splits complete the speedup. Its paper reports training up to 20 times faster than conventional gradient boosting.
LightGBM uses leaf-wise growth: instead of growing all leaves at the same depth (level-wise), it greedily expands the leaf with the highest loss reduction. This produces asymmetric trees that are deeper where the data is complex. GOSS (Gradient-based One-Side Sampling) keeps all samples with large gradients (top ) and randomly samples from small-gradient samples (bottom at rate ), multiplying their weights by to correct the distribution. This achieves similar accuracy with significantly fewer split evaluations.
10M rows dataset. XGBoost: 30 min. LightGBM?
CatBoost
CatBoost, from Yandex, focuses on categorical features and on a subtle bias. Naive target encoding replaces a category with its mean target, which leaks each row's own label into its feature. CatBoost computes ordered target statistics: under a random permutation, each row's encoding uses only rows before it, smoothed by a prior. Ordered boosting applies the same principle to the residuals. It also builds symmetric trees, where all nodes at a depth share one split, which are fast to evaluate and resist overfitting. Its defaults are strong, so it often performs well with little tuning, especially on many high-cardinality categorical features.
CatBoost handles categorical features through ordered target encoding: for sample , the encoding of category uses only samples (preceding in a random permutation ), computing , where is a smoothing prior and is the dataset mean. This leave-one-out scheme prevents target leakage that plagues naive target encoding.
Data has 20 categorical features (city, category, etc.). XGBoost needs encoding. CatBoost?
GB Regularization
Gradient boosting can drive training loss toward zero, so regularization is essential. Tree size limits, max_depth of 3 to 8 or num_leaves in LightGBM, keep each tree weak. The learning rate and the number of trees control how far the model travels. Subsampling, subsample for rows and colsample_bytree for columns, trains each tree on a random fraction, adding randomness that reduces variance, an idea Friedman called stochastic gradient boosting. Leaf-value penalties, reg_lambda for L2 and reg_alpha for L1, shrink extreme leaf values. min_child_weight and gamma require a minimum gain or Hessian mass before splitting.
The regularization toolbox controls model complexity at multiple levels: limits tree complexity ( leaves), and add stochastic regularization by using random subsets of rows/columns per tree (analogous to dropout), and penalize leaf weights via norms. The effective degrees of freedom scales roughly as — reducing any of these terms constrains the model.
XGBoost overfitting. Current: max_depth=6, subsample=1.0. What to try?
Early Stopping
Because boosting keeps reducing training loss, the number of trees is itself a regularization parameter. Early stopping sets a large maximum, monitors loss on a validation set after each round, and stops once it has not improved for a patience number of rounds, keeping the best iteration. This tunes the number of trees within a single run. In XGBoost, pass an eval_set and early_stopping_rounds; LightGBM and CatBoost offer equivalents, and scikit-learn's HistGradientBoosting uses early_stopping and n_iter_no_change. The validation set is used for selection, so report final performance on separate test data.
Early stopping monitors validation loss as trees are added. The expected loss follows a U-shape: decreasing as bias reduces, then increasing as the model overfits. The patience parameter stops training when for consecutive rounds. This implicitly selects the optimal without needing to tune directly — a form of model selection embedded in the training loop.
n_estimators=10000, early_stopping_rounds=50. Val loss: decreases until tree 200, then increases. When to stop?
Theory Exercise
Problem:
Your XGBoost model with learning_rate=0.1 and n_estimators=100 is overfitting. What changes would you try?
Hints:
- Think about regularization options
- Consider tree complexity controls
- What about subsampling?
Coding Exercise
Problem:
Train a gradient boosting model with early stopping on an internal validation split so the optimal number of trees is selected automatically, rather than fixed by hand.
Hints:
- GradientBoostingClassifier supports validation_fraction + n_iter_no_change for early stopping
- Set a high n_estimators ceiling and let early stopping pick the real count
- Inspect gb.n_estimators_ after fitting to see where it actually stopped
Related Problems on PixelBank
So far in this chapter, Ensemble Methods, every ensemble has combined many models of the same kind: random forests average trees, and the gradient boosting of the previous topic, Gradient Boosting, sums them. In practice we often have several different strong models, such as a gradient-boosted model, a regularized logistic regression, and a support vector machine, each making somewhat different mistakes. Simply picking the best one throws away what the others know, but averaging them equally may let a weak model drag down a strong one.
Stacking learns how to combine them. Its base models make predictions, and a second-level meta-model learns from those predictions which model to trust and how much. This topic starts with simple voting ensembles, then builds two-level stacking and the cross-validation it needs to avoid leakage. We then examine why diverse base models matter, how to choose the meta-learner, blending as a cheaper alternative, when stacking is worth its cost, and how scikit-learn's StackingClassifier implements the whole procedure.
Definition
Stacking, or stacked generalization, trains several diverse base models, then trains a meta-model whose inputs are the base models' predictions, generated out-of-fold by cross-validation so that each prediction comes from a model that did not train on that example. The meta-model learns how best to combine the base predictions.
In this topic
- 1Voting Ensemble
- 2Stacking (Two-Level)
- 3Cross-Validation for Stacking
- 4Diverse Base Models
- 5Meta-Learner Choice
- 6Blending
- 7When Stacking Helps
- 8Sklearn StackingClassifier
Voting Ensemble
Voting is the simplest way to combine different models. Hard voting gives each model one vote for its predicted class and returns the majority. Soft voting averages predicted probabilities and picks the class with the highest average, so confident models count more than hesitant ones. Soft voting usually performs better, but only if the probabilities are reasonably calibrated; an overconfident model can dominate it. Weights can favor stronger models. Voting has no trainable combiner, so it cannot learn that one model is reliable only in some cases. scikit-learn provides VotingClassifier and VotingRegressor.
Hard voting counts the modal class: . Soft voting averages predicted probabilities: , then . Soft voting is strictly superior when base models are well-calibrated — it preserves information about prediction confidence that hard voting discards. A model that predicts contributes more than one predicting , but both count equally in hard voting.
3 models predict P(spam): [0.9, 0.4, 0.6]. Hard vs soft voting?
Stacking (Two-Level)
Stacking has two levels. At level 0, several base models are trained on the original features. At level 1, a meta-model is trained on a new dataset whose features are the base models' predictions for each training example, often probabilities, and whose target is the true label. The meta-model learns how much to trust each base model, and with a flexible meta-model, when to trust it. At prediction time, every base model predicts first and the meta-model combines them. A stack is rarely worse than its best member when built correctly, but the gain over that member is often small.
Stacking trains a meta-model on the vector of base predictions . The meta-model learns the optimal combination function — if base model is accurate for certain input regions but unreliable elsewhere, the meta-learner can learn to weight it conditionally. This is strictly more expressive than fixed-weight averaging, as can learn non-linear combinations like "trust the RF when the XGBoost prediction is uncertain."
Base models: RF (85%), XGB (87%), LogReg (82%). Meta-learner learns weights. Result?
Cross-Validation for Stacking
The meta-model must be trained on predictions like those it will see at test time, made by base models that never saw the example. If a base model is fit on all the training data and then predicts that same data, its predictions are overconfident, a deep forest is nearly perfect on its own training rows, and the meta-model learns to trust it blindly. The fix is out-of-fold prediction: split the data into folds, fit each base model on folds, and predict the held-out fold, so every training row gets an honest prediction. The base models are then refit on all the data for test time.
The meta-features must be out-of-fold predictions to avoid information leakage. In -fold stacking, base model is trained on folds and predicts fold , producing meta-feature for samples in fold . The resulting meta-features have the same distributional properties as test-time predictions, preventing the meta-learner from exploiting the base models' ability to memorize training data.
Train RF on all data, use its predictions for meta-learner. Problem?
Diverse Base Models
An ensemble improves on its members only where they disagree. For squared error, the ensemble's error equals the average member error minus the average disagreement of members from the ensemble, the ambiguity decomposition. Models that make the same mistakes add accuracy-weighted copies of the same errors. Diversity comes from different inductive biases: trees split on axis-aligned thresholds, linear models fit global trends, kernel methods measure similarity, and neural networks learn representations. Different feature sets or preprocessing add diversity too. A strong but redundant model adds less than a slightly weaker but different one.
Ensemble theory shows that the improvement from combining models scales with their disagreement: . The second term (ambiguity) is the diversity — the ensemble error equals the average individual error minus the average pairwise disagreement. This is why combining RF + LogReg + SVM (high diversity) outperforms RF1 + RF2 + RF3 (low diversity) even if individual models are equally accurate.
Stack: RF1, RF2, RF3 (just different seeds). Good ensemble?
Meta-Learner Choice
The meta-model's inputs are few, one or a few columns per base model, highly correlated with each other, and already strong predictors of the target. A simple model fits this setting best: logistic regression for classification, ridge or non-negative least squares for regression. A linear meta-model's weights are also readable, showing how much each base model contributes. Flexible meta-models such as boosted trees can learn when each base model is reliable, but they overfit the out-of-fold predictions easily and need much more data. Start linear with regularization, and move to something flexible only if cross-validation shows a real gain.
The meta-learner operates on -dimensional input (one feature per base model). With small (typically 3-10), simple models like logistic regression or ridge regression are less prone to overfitting. A linear meta-learner learns weights , which is interpretable — directly measures base model 's contribution. Complex meta-learners (XGBoost, neural nets) can learn interactions between base predictions but require more meta-training data to avoid overfitting.
Meta-learner options: XGBoost vs LogisticRegression. 5 base models. Which?
Blending
Blending is a simpler variant of stacking that replaces cross-validation with a single holdout split. Base models are trained on one part of the training data, they predict a separate blend set, and the meta-model is trained on those holdout predictions. Each base model is trained once instead of times, and the procedure is easy to reason about and leak-free by construction. The costs are that base models see less training data and the meta-model learns from fewer rows, so its estimates are noisier. Blending suits large datasets and quick experiments; stacking with cross-validation suits smaller data.
Blending uses a single holdout set instead of cross-validation: train base models on , generate meta-features on , train the meta-learner on . This reduces computation from model trainings (stacking with folds) to but wastes samples for base model training. The bias-variance trade-off: larger gives better meta-learner estimates but weaker base models trained on less data.
1M samples. Stacking with 5-fold CV: slow. Alternative?
When Stacking Helps
Stacking helps most when several base models are individually strong, their errors are not highly correlated, and there is enough data to fit the meta-model reliably. It is a staple of competitions, where a fraction of a percent decides the ranking. In production its costs weigh more: every prediction runs every base model, so latency and serving cost multiply, and there are more models to monitor, retrain, and debug. The gain over the best single model is often small and sometimes negative. Weigh it against simpler options, such as tuning the best model or averaging two.
The expected improvement from stacking is bounded by the reducible error of the best base model. If the best model captures of the signal, stacking can recover at most part of the remaining . The cost is times the inference latency and complexity. The ROI is highest when: (1) base models make uncorrelated errors, (2) the accuracy gap between the best and worst base model is small, and (3) the dataset is large enough to train a reliable meta-learner.
Production system needs simple, fast model. Worth stacking 10 models?
Sklearn StackingClassifier
Scikit-learn's StackingClassifier and StackingRegressor implement stacking safely. You pass a list of named base estimators and a final_estimator. During fit, it generates out-of-fold predictions for each base model with internal cross-validation, controlled by cv, trains the final estimator on them, and refits every base model on the full training data for prediction. stack_method selects which output becomes a meta-feature, predict_proba by default when available. passthrough=True also feeds the original features to the final estimator. The whole stack behaves like one estimator, so it can sit inside pipelines and be scored with cross_val_score.
Scikit-learn's StackingClassifier internally performs cross-validated predictions using cross_val_predict with method='predict_proba' (soft stacking) or method='predict' (hard stacking). The meta-features matrix has shape for base models and classes when using probabilities. The final_estimator receives this matrix and the true labels, fitting via standard .fit(X_meta, y). The cv parameter controls the number of folds for generating out-of-fold meta-features.
Code: StackingClassifier(estimators=[('rf',RF()),('xgb',XGB())], final_estimator=LogReg()). What happens?
Theory Exercise
Problem:
When would you choose Random Forest over Gradient Boosting, and vice versa? Consider training time, interpretability, and overfitting risk.
Hints:
- Which is parallelizable?
- Which requires more hyperparameter tuning?
- How do they handle overfitting?
Coding Exercise
Problem:
Build a StackingClassifier with three diverse base models (Random Forest, gradient boosting, logistic regression) and a logistic-regression meta-learner. Compare the stacked accuracy to each base model's cross-validated accuracy.
Hints:
- Pass base models as estimators=[(name, model), ...]
- Use final_estimator=LogisticRegression() and cv=5 (StackingClassifier builds out-of-fold meta-features internally)
- Diversity matters — mix a tree ensemble, a boosting model, and a linear model