Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Combining Different Models for Ensemble Learning

Open in Colab Open in Kaggle

Combining Different Models for Ensemble Learning

This notebook accompanies Chapter 7 of Machine Learning with PyTorch and Scikit-Learn by Sebastian Raschka & Vahid Mirjalili.

Why Ensemble Methods?

The goal of ensemble methods is to combine different classifiers into a meta-classifier that has better generalization performance than each individual classifier alone. For example, assuming we collected predictions from 10 experts, ensemble methods would allow us to strategically combine their predictions to come up with a prediction that is more accurate and robust than the predictions by each individual expert.

In this notebook, we will cover:

  • The mathematical foundation of why ensembles work

  • Majority voting classifiers (hard and soft voting)

  • Bagging with bootstrap samples

  • Adaptive Boosting (AdaBoost)

  • Gradient Boosting and XGBoost

Learning Objectives

By the end of this notebook, you will be able to:

  1. Explain why combining classifiers improves performance (the mathematical intuition)

  2. Implement a majority vote classifier from scratch

  3. Distinguish between hard voting and soft voting

  4. Apply bagging to reduce variance using bootstrap samples

  5. Understand AdaBoost’s weight update mechanism

  6. Compare bagging vs boosting approaches

  7. Use XGBoost for gradient boosting

All imports successful!

1. Learning with Ensembles

The Core Insight: Error Diversification

The key insight behind ensembles is beautifully simple:

If each classifier is better than random guessing and makes different mistakes, combining them yields higher accuracy.

Think of it like asking multiple experts for their opinion. Even if each expert is imperfect, if their errors are uncorrelated, averaging their answers cancels out individual mistakes while preserving the true signal.

Mathematical Foundation

Let’s make this concrete. Suppose we have nn independent classifiers, each with error rate ϵ\epsilon (probability of being wrong). For a majority vote to be wrong, more than half the classifiers must be wrong simultaneously.

Using the binomial distribution, the ensemble error probability is:

P(ensemble error)=∑k=⌈n/2⌉n(nk)ϵk(1−ϵ)n−kP(\text{ensemble error}) = \sum_{k=\lceil n/2 \rceil}^{n} \binom{n}{k} \epsilon^k (1-\epsilon)^{n-k}
Individual classifier error: 25.0%
Ensemble of 11 classifiers:  3.4%

That's a 86% reduction in error rate!
<Figure size 1000x600 with 1 Axes>
Key insight: As long as each classifier is better than random guessing (error < 50%),
the ensemble will outperform individual classifiers.

The Critical Assumption: Independence

The mathematical magic above relies on a crucial assumption: the classifiers make independent errors. If all classifiers make the same mistakes, combining them won’t help!

This is why ensemble methods work hard to create diversity among their base learners:

MethodHow it Creates Diversity
VotingDifferent algorithms (logistic regression, SVM, tree)
BaggingDifferent random subsets of training data
Random ForestDifferent data subsets AND different feature subsets
BoostingSequential focus on different “hard” examples

2. Combining Classifiers via Majority Vote

Two Voting Strategies

Hard Voting (Majority Vote): Each classifier casts a vote for a class label, and we predict the class that received the most votes.

Soft Voting: Each classifier provides probability estimates, and we predict the class with the highest average probability.

Let’s see both in action:

Hard voting with weights [0.2, 0.2, 0.6]:
  Predictions: [0, 0, 1]
  Weighted vote for class 0: 0.4 = 0.4
  Weighted vote for class 1: 0.6
  Winner: Class 1
Soft voting example:
Classifier probabilities [P(class 0), P(class 1)]:
  Classifier 1: [0.9 0.1]
  Classifier 2: [0.8 0.2]
  Classifier 3: [0.4 0.6]

Weighted average probabilities: [0.58 0.42]
Predicted class: 0

Note: Even though 2 classifiers favored class 0, the weighted
probabilities still favor class 0 (0.58 > 0.42)

Implementing a Majority Vote Classifier

Let’s implement our own MajorityVoteClassifier that supports both hard and soft voting:

MajorityVoteClassifier defined successfully!

Applying Majority Voting to the Iris Dataset

Let’s combine three different classifiers using our MajorityVoteClassifier:

  1. Logistic Regression

  2. Decision Tree

  3. K-Nearest Neighbors

Iris Dataset (2 classes, 2 features)
========================================
Training samples: 50
Test samples: 50
Features: Sepal width, Petal length
Classes: Versicolor (0), Virginica (1)
10-fold cross validation (ROC AUC):

ROC AUC: 0.92 (+/- 0.15) [Logistic regression]
ROC AUC: 0.87 (+/- 0.18) [Decision tree]
ROC AUC: 0.85 (+/- 0.13) [KNN]
Comparison with Majority Voting:

ROC AUC: 0.92 (+/- 0.15) [Logistic regression]
ROC AUC: 0.87 (+/- 0.18) [Decision tree]
ROC AUC: 0.85 (+/- 0.13) [KNN]
ROC AUC: nan (+/- nan) [Majority voting]

** The ensemble outperforms all individual classifiers! **
<Figure size 1000x700 with 1 Axes>
<Figure size 900x700 with 4 Axes>
The majority voting classifier combines the decision boundaries of all three classifiers.

Tuning the Ensemble with GridSearchCV

We can use scikit-learn’s GridSearchCV to tune hyperparameters of the individual classifiers within our ensemble:

Grid Search Results:
============================================================
nan +/- nan {'decisiontreeclassifier__max_depth': 1, 'pipeline-1__clf__C': 0.001}
nan +/- nan {'decisiontreeclassifier__max_depth': 1, 'pipeline-1__clf__C': 0.1}
nan +/- nan {'decisiontreeclassifier__max_depth': 1, 'pipeline-1__clf__C': 100.0}
nan +/- nan {'decisiontreeclassifier__max_depth': 2, 'pipeline-1__clf__C': 0.001}
nan +/- nan {'decisiontreeclassifier__max_depth': 2, 'pipeline-1__clf__C': 0.1}
nan +/- nan {'decisiontreeclassifier__max_depth': 2, 'pipeline-1__clf__C': 100.0}

Best parameters: {'decisiontreeclassifier__max_depth': 1, 'pipeline-1__clf__C': 0.001}
Best ROC AUC: nan

3. Bagging: Bootstrap Aggregating

Bagging (Bootstrap Aggregating), invented by Leo Breiman in 1996, creates diversity by training each model on a different random subset of the training data.

How Bagging Works

  1. Bootstrap sampling: Draw nn samples from the training set with replacement

    • Some examples appear multiple times, others not at all

    • On average, each bootstrap sample contains ~63.2% unique examples

  2. Train a model on each bootstrap sample

  3. Aggregate predictions by majority vote (classification) or averaging (regression)

Why It Works

Bagging reduces variance without increasing bias. Each tree is trained on a random subset of rows. Although each individual tree has errors, those errors are random and uncorrelated. The average of a group of random errors tends toward zero, so these errors cancel out and what’s left is the true relationship.

Best for: High-variance, low-bias models (e.g., unpruned decision trees)

Bootstrap Sampling Demonstration
==================================================

Original training set indices: [0, 1, 2, 3, 4, 5, 6]

3 bootstrap samples (with replacement):
  Sample 1: [np.int64(6), np.int64(3), np.int64(4), np.int64(6), np.int64(2), np.int64(4), np.int64(4)] (4/7 unique = 57%)
  Sample 2: [np.int64(6), np.int64(1), np.int64(2), np.int64(6), np.int64(2), np.int64(2), np.int64(4)] (4/7 unique = 57%)
  Sample 3: [np.int64(3), np.int64(2), np.int64(5), np.int64(4), np.int64(1), np.int64(3), np.int64(5)] (5/7 unique = 71%)

Theoretical: ~63.2% unique samples per bootstrap (1 - 1/e)

Applying Bagging to the Wine Dataset

Let’s apply bagging to classify wine samples:

Wine Dataset (2 classes, 2 features)
========================================
Training samples: 95
Test samples: 24
Features: Alcohol, OD280/OD315
Results: Single Tree vs Bagging
==================================================
Model                        Train Acc     Test Acc
--------------------------------------------------
Decision Tree                    1.000        0.833
Bagging (500 trees)              1.000        0.917

** Bagging improves test accuracy while maintaining training accuracy! **
<Figure size 1000x400 with 2 Axes>
Notice: Bagging produces smoother decision boundaries.

4. Leveraging Weak Learners via Adaptive Boosting

Key Difference from Bagging

AspectBaggingBoosting
TrainingParallel (independent trees)Sequential (each tree corrects previous)
FocusReduce varianceReduce bias
Base learnersComplex (deep trees)Simple (stumps)
Data sensitivityRobust to noiseSensitive to outliers

How AdaBoost Works

  1. Train a weak learner on weighted data (initially uniform weights)

  2. Increase weights of misclassified samples

  3. Train next learner on reweighted data

  4. Combine all learners with weighted vote

Let’s walk through the weight update mechanism step by step:

AdaBoost Weight Update Mechanism
==================================================
True labels:    [ 1  1  1 -1 -1 -1  1  1  1 -1]
Predictions:    [ 1  1  1 -1 -1 -1 -1 -1 -1 -1]
Correct?:       [1 1 1 1 1 1 0 0 0 1]
Initial weights: [0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1]

Step 1: Weighted error rate
  epsilon = 0.3 (30% of samples misclassified)

Step 2: Classifier weight
  alpha_j = 0.5 * ln((1 - 0.3) / 0.3)
  alpha_j = 0.424

  Interpretation: Better classifiers get higher weights

Step 3: Update sample weights
  Weight if correct:   0.1 * exp(-0.424) = 0.0655
  Weight if incorrect: 0.1 * exp(+0.424) = 0.1528

  Updated weights (before normalization):
  [0.0655 0.0655 0.0655 0.0655 0.0655 0.0655 0.1528 0.1528 0.1528 0.0655]

  Normalized weights (sum to 1):
  [0.0714 0.0714 0.0714 0.0714 0.0714 0.0714 0.1667 0.1667 0.1667 0.0714]

  Effect: Misclassified samples now have HIGHER weights!

Applying AdaBoost with scikit-learn

Results: Decision Stump vs AdaBoost
==================================================
Model                        Train Acc     Test Acc
--------------------------------------------------
Decision Stump                   0.916        0.875
AdaBoost (500 stumps)            0.968        0.917

** Many weak learners combine into a strong learner! **
<Figure size 1000x400 with 2 Axes>
Notice: AdaBoost creates complex boundaries from simple stumps.

5. Gradient Boosting and XGBoost

Gradient Boosting

Similar to AdaBoost but:

  • Fits new models to residual errors (gradients of the loss function)

  • Uses gradient descent to minimize a loss function

  • More flexible loss functions possible

XGBoost: Extreme Gradient Boosting

XGBoost is an optimized implementation of gradient boosting that provides:

  • Speed: Parallel processing and cache optimization

  • Regularization: L1 and L2 regularization to prevent overfitting

  • Missing values: Automatic handling of missing data

  • Tree pruning: Uses max_depth and prunes trees backward

XGBoost train/test accuracies: 0.968/0.917

Comparison: Bagging vs Boosting

AspectBaggingBoosting
TrainingParallelSequential
FocusReduce varianceReduce bias
Base learnersComplex (deep trees)Simple (stumps)
OverfittingResistantCan overfit
Data sensitivityRobust to noiseSensitive to outliers

When to Use Each Method

Bagging / Random Forests:

  • High variance models

  • Noisy data

  • Need interpretability

AdaBoost / Gradient Boosting:

  • High bias models

  • Clean data

  • Maximum predictive performance


6. Summary

Key Takeaways

  1. Ensembles work by combining classifiers with diverse, uncorrelated errors

  2. Majority voting combines different algorithms through votes or probabilities

  3. Bagging reduces variance by training on bootstrap samples (parallel)

  4. AdaBoost sequentially focuses on misclassified examples (sequential)

  5. Gradient boosting minimizes loss by fitting to residuals

  6. XGBoost provides optimized gradient boosting with regularization

Comparison of Methods

MethodApproachBest For
VotingDifferent algorithmsCombining diverse models
BaggingBootstrap samplesReducing variance
AdaBoostReweight samplesSequential correction
XGBoostGradient descentMaximum performance

References

Primary Source

Academic Papers

  • Breiman, L. (1996). Bagging Predictors. Machine Learning, 24(2), 123-140.

  • Freund, Y. & Schapire, R. (1997). A Decision-Theoretic Generalization of On-Line Learning. Journal of Computer and System Sciences.

  • Chen, T. & Guestrin, C. (2016). XGBoost: A Scalable Tree Boosting System. KDD.