Bootstrap Aggregation (Bagging) Explained
Learners will implement bootstrap sampling by hand, train classifiers on each sample, and understand why averaging diverse models reduces variance.
Bootstrap Aggregation (Bagging) Explained is a free Machine Learning Academy lesson on CoddyKit — lesson 1 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Machine Learning Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
What Is Bootstrap Aggregation?
Bootstrap Aggregation, commonly called Bagging, is an ensemble technique that trains multiple models on different random subsets of the training data and combines their predictions. The word bootstrap comes from statistics and means sampling with replacement from your data. By averaging or voting across many diverse models, bagging dramatically reduces the variance of the final prediction without increasing bias.
Sampling With Replacement Explained
Sampling with replacement means each bootstrap sample is drawn independently from the full dataset — the same row can appear multiple times in a single sample while other rows may be excluded entirely. For a dataset of N examples, each bootstrap sample also contains N rows. On average, about 63.2% of unique examples appear in any given bootstrap sample, and the remaining ~37% form the out-of-bag set that can be used for validation.
import numpy as np
np.random.seed(42)
data = np.arange(10) # [0, 1, 2, ..., 9]
bootstrap_sample = np.random.choice(data, size=len(data), replace=True)
print('Original:', data)
print('Bootstrap sample:', bootstrap_sample)Why Diversity Reduces Variance
Suppose you have n independent models each with variance σ². If you average their predictions, the variance of the average is σ²/n — it shrinks as you add more models. In practice, models trained on bootstrap samples are correlated (they share the same training distribution), so the reduction is partial but still significant. The key insight is that averaging reduces variance without increasing bias, leading to better generalisation.
Implementing Bagging by Hand
You can implement bagging manually by training a list of estimators, each on a different bootstrap sample, then averaging their outputs. This shows exactly what BaggingClassifier does under the hood. Understanding the manual version makes it easier to diagnose problems and extend the technique to custom models.
from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import load_iris
import numpy as np
X, y = load_iris(return_X_y=True)
n_estimators = 10
models = []
for _ in range(n_estimators):
idx = np.random.choice(len(X), size=len(X), replace=True)
X_boot, y_boot = X[idx], y[idx]
tree = DecisionTreeClassifier()
tree.fit(X_boot, y_boot)
models.append(tree)
# Predict by majority vote
predictions = np.array([m.predict(X) for m in models])
ensemble_pred = [np.bincount(col).argmax() for col in predictions.T]
print('Ensemble accuracy:', np.mean(ensemble_pred == y))scikit-learn BaggingClassifier
scikit-learn provides BaggingClassifier that wraps any base estimator and automates the bootstrap sampling and aggregation. You control n_estimators (number of models), max_samples (fraction or absolute count of training samples per model), and max_features (number of features to use). Setting oob_score=True computes a free validation score using the out-of-bag examples for each estimator.
from sklearn.ensemble import BaggingClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
X, y = load_breast_cancer(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
bag = BaggingClassifier(
estimator=DecisionTreeClassifier(),
n_estimators=100,
oob_score=True,
random_state=42
)
bag.fit(X_train, y_train)
print('OOB score:', bag.oob_score_)
print('Test accuracy:', bag.score(X_test, y_test))Bagging for Regression
Bagging is not limited to classification. BaggingRegressor applies the same idea: train multiple regressors on bootstrap samples and average their numeric predictions. This is especially valuable when the base learner is a high-variance model like an unpruned decision tree. A single tree memorises noise; the average of many trees smooths it out, reducing the overall test RMSE significantly.
from sklearn.ensemble import BaggingRegressor
from sklearn.tree import DecisionTreeRegressor
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import cross_val_score
import numpy as np
X, y = fetch_california_housing(return_X_y=True)
single_tree = DecisionTreeRegressor(random_state=42)
bag_reg = BaggingRegressor(estimator=DecisionTreeRegressor(), n_estimators=100, random_state=42)
single_rmse = np.sqrt(-cross_val_score(single_tree, X, y, scoring='neg_mean_squared_error', cv=3).mean())
bag_rmse = np.sqrt(-cross_val_score(bag_reg, X, y, scoring='neg_mean_squared_error', cv=3).mean())
print(f'Single tree RMSE: {single_rmse:.4f}')
print(f'Bagged tree RMSE: {bag_rmse:.4f}')The Out-of-Bag Fraction
Since ~37% of training examples are excluded from each bootstrap sample, those examples can serve as a validation set for their respective estimator without any additional data split. The OOB score is computed by predicting each training point using only the estimators that did not see it during training. This gives a nearly unbiased estimate of generalisation performance, similar to leave-one-out cross-validation but far cheaper to compute.
Effect of n_estimators on Performance
Adding more estimators to a bagging ensemble almost always helps (or at worst does not hurt) test performance. Unlike neural network depth, there is no overfitting penalty for adding more trees — variance keeps decreasing while bias stays flat. In practice, performance plateaus after several hundred estimators. The main trade-off is compute time: doubling n_estimators doubles training time. Always check whether the marginal gain justifies the extra cost.
from sklearn.ensemble import BaggingClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import cross_val_score
import numpy as np
X, y = load_breast_cancer(return_X_y=True)
for n in [1, 10, 50, 200]:
bag = BaggingClassifier(estimator=DecisionTreeClassifier(), n_estimators=n, random_state=42)
score = cross_val_score(bag, X, y, cv=5).mean()
print(f'n_estimators={n:4d}: CV accuracy={score:.4f}')Bagging vs a Single Complex Model
A single complex model (deep tree, high-degree polynomial) achieves low bias but high variance — it fits training data well but fluctuates wildly on unseen data. Bagging tames that variance by averaging across many such models. The combined prediction is much more stable. This is why bagging works best when the base learner is high variance and low bias. Applying bagging to a simple, already-underfitting model (like a linear regression) gives little benefit because the variance was already low.
Parallelism: Bagging Is Embarrassingly Parallel
Each estimator in a bagging ensemble is trained independently, with no data dependencies between them. This makes bagging embarrassingly parallel — all estimators can be trained simultaneously on multiple CPU cores. scikit-learn exposes this via the n_jobs=-1 parameter, which uses all available cores. This is in sharp contrast to boosting methods like XGBoost, where each model depends on the previous one and training is inherently sequential.
from sklearn.ensemble import BaggingClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import load_breast_cancer
import time
X, y = load_breast_cancer(return_X_y=True)
bag_serial = BaggingClassifier(n_estimators=200, n_jobs=1, random_state=42)
bag_parallel = BaggingClassifier(n_estimators=200, n_jobs=-1, random_state=42)
start = time.time(); bag_serial.fit(X, y); print('Serial:', round(time.time()-start, 2), 's')
start = time.time(); bag_parallel.fit(X, y); print('Parallel:', round(time.time()-start, 2), 's')Limitations and When Bagging Fails
Bagging has important limitations. First, it increases compute and memory linearly with the number of estimators. Second, it offers no improvement in bias — if your base model is too simple, bagging many simple models still gives a simple ensemble. Third, bagging makes the model harder to interpret — you lose the single decision tree you could show a stakeholder. Finally, bagging alone does not decorrelate trees if all features are used at every split, which is where Random Forests add the extra trick of feature sub-sampling.
Quick Check
Test your understanding of Bootstrap Aggregation concepts from this lesson.
Lesson Recap
In this lesson you learned: bootstrap aggregation trains multiple models on random resamples with replacement, averaging predictions reduces variance without increasing bias, and the out-of-bag examples provide free validation for each estimator. Next up we explore the Random Forest trick of sub-sampling features to decorrelate trees further.
Frequently asked questions
Is the “Bootstrap Aggregation (Bagging) Explained” lesson free?
Yes — the full text of “Bootstrap Aggregation (Bagging) Explained” is free to read here on the web, and the Machine Learning Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Machine Learning Academy course, upgrade to CoddyKit PRO.
What will I learn in “Bootstrap Aggregation (Bagging) Explained”?
Learners will implement bootstrap sampling by hand, train classifiers on each sample, and understand why averaging diverse models reduces variance. You practise Machine Learning Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Machine Learning Academy?
No prior experience is required. Machine Learning Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 1 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Bootstrap Aggregation (Bagging) Explained” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Machine Learning Academy lesson?
Yes. Every Machine Learning Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Bootstrap Aggregation (Bagging) Explained
- Random Feature Selection: The Random Forest Trick
- Out-of-Bag Error: Free Validation Inside the Forest
- Voting Ensembles: Hard Vote vs Soft Vote