Bootstrap Aggregation (Bagging) in Python
Scopri come il bagging riduce la varianza nei modelli ML e come implementare BaggingClassifier in Python con scikit-learn.
La bootstrap aggregation — comunemente chiamata bagging — è una tecnica di ensemble che addestra più modelli su campioni casuali diversi dei tuoi dati e poi combina le loro previsioni. Il risultato è un modello meno sensibile al rumore e agli outlier, che generalizza meglio su dati non visti rispetto a qualsiasi singolo modello addestrato sull'intero dataset.
Questo capitolo tratta:
- Cos'è il campionamento bootstrap e perché è utile
- Il compromesso bias–varianza che il bagging affronta
- Come implementare
BaggingClassifiereBaggingRegressorcon scikit-learn - I principali iperparametri e come regolarli
- L'errore out-of-bag (OOB) come stima di validazione gratuita
- Quando il bagging è utile e quando preferire altri metodi
Come funziona il campionamento bootstrap
Il termine bootstrap si riferisce al campionamento con reinserimento. Dato un dataset di n esempi, un campione bootstrap viene creato estraendo n esempi a caso, dove ogni estrazione è indipendente e lo stesso esempio può apparire più di una volta.
import numpy as np
rng = np.random.default_rng(42)
data = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
bootstrap_sample = rng.choice(data, size=len(data), replace=True)
out_of_bag = np.setdiff1d(data, bootstrap_sample)
print("Original data: ", data)
print("Bootstrap sample:", bootstrap_sample)
print("Out-of-bag: ", out_of_bag)Output:
Original data: [ 1 2 3 4 5 6 7 8 9 10]
Bootstrap sample: [1 8 7 5 5 9 1 7 3 1]
Out-of-bag: [ 2 4 6 10]In media, un campione bootstrap contiene circa il 63 % degli esempi originali unici; il restante 37 % viene escluso. Questi esempi out-of-bag (OOB) possono essere usati come set di validazione integrato senza necessità di una suddivisione hold-out separata.
Il compromesso bias–varianza
Ogni modello produce previsioni con una certa combinazione di bias (errore sistematico dovuto a ipotesi errate) e varianza (sensibilità alle fluttuazioni nei dati di addestramento). Il bagging si rivolge ai modelli con alta varianza e basso bias — in particolare agli alberi decisionali profondi.
Un singolo albero decisionale non potato può memorizzare perfettamente il training set, ma fallisce notevolmente su nuovi dati. Mediando le previsioni di molti alberi, ciascuno addestrato su un campione bootstrap leggermente diverso, le fluttuazioni casuali si annullano e la varianza diminuisce — senza aumentare significativamente il bias.
Il bagging non aiuta i modelli che hanno già un bias elevato (ad esempio, i modelli lineari poco profondi), perché mediare molti modelli distorti produce comunque una risposta distorta. In questi casi, metodi di boosting come Gradient Boosting sono più appropriati.
Implementare BaggingClassifier
scikit-learn fornisce BaggingClassifier per i task di classificazione. L'esempio seguente confronta un singolo albero decisionale con un ensemble con bagging sul dataset breast cancer.
from sklearn.ensemble import BaggingClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_breast_cancer
from sklearn.metrics import accuracy_score
# Load dataset
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
)
# Baseline: single decision tree
single_tree = DecisionTreeClassifier(random_state=42)
single_tree.fit(X_train, y_train)
single_acc = accuracy_score(y_test, single_tree.predict(X_test))
# Bagging ensemble of 50 decision trees
bagging_model = BaggingClassifier(
estimator=DecisionTreeClassifier(),
n_estimators=50,
random_state=42,
)
bagging_model.fit(X_train, y_train)
bagging_acc = accuracy_score(y_test, bagging_model.predict(X_test))
print(f"Single tree accuracy: {single_acc:.2f}")
print(f"Bagging accuracy: {bagging_acc:.2f}")Output:
Single tree accuracy: 0.95
Bagging accuracy: 0.96Il modello con bagging supera il singolo albero. Su dataset più rumorosi o più piccoli, il margine è generalmente maggiore.
Parametri principali del costruttore
| Parametro | Predefinito | Cosa controlla |
|---|---|---|
estimator | DecisionTreeClassifier() | Il base learner da aggregare |
n_estimators | 10 | Numero di modelli da addestrare |
max_samples | 1.0 | Frazione (o numero) di righe del training per campione bootstrap |
max_features | 1.0 | Frazione (o numero) di feature estratte per ogni base learner |
bootstrap | True | Campiona le righe con reinserimento; impostare False per il pasting |
bootstrap_features | False | Campiona anche le feature con reinserimento |
oob_score | False | Stima la generalizzazione usando gli esempi out-of-bag |
Errore out-of-bag (OOB)
Poiché ogni base learner vede circa il 63 % dei dati di addestramento, il restante 37 % può essere usato per valutare quel learner senza toccare il test set. Mediando queste valutazioni OOB su tutti gli estimatori si ottiene l'OOB score — una stima quasi gratuita dell'accuratezza sul test.
from sklearn.ensemble import BaggingClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_breast_cancer
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
)
bagging_oob = BaggingClassifier(
estimator=DecisionTreeClassifier(),
n_estimators=50,
oob_score=True, # enable OOB evaluation
random_state=42,
)
bagging_oob.fit(X_train, y_train)
print(f"OOB score: {bagging_oob.oob_score_:.2f}")Output:
OOB score: 0.96L'OOB score è vicino all'accuratezza sul test set trattenuto, il che lo rende utile come verifica rapida — soprattutto quando il dataset è troppo piccolo per permettersi una suddivisione di validazione separata. Per una stima più rigorosa, abbina il bagging alla cross-validation.
BaggingRegressor
Il bagging è ugualmente utile per la regressione. Sostituisci BaggingClassifier con BaggingRegressor e scegli un base learner per la regressione.
from sklearn.ensemble import BaggingRegressor
from sklearn.tree import DecisionTreeRegressor
from sklearn.model_selection import train_test_split
from sklearn.datasets import fetch_california_housing
from sklearn.metrics import mean_squared_error
import numpy as np
X, y = fetch_california_housing(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
bagging_reg = BaggingRegressor(
estimator=DecisionTreeRegressor(),
n_estimators=50,
random_state=42,
)
bagging_reg.fit(X_train, y_train)
rmse = np.sqrt(mean_squared_error(y_test, bagging_reg.predict(X_test)))
print(f"BaggingRegressor RMSE: {rmse:.4f}")Output:
BaggingRegressor RMSE: 0.5080Valutazione con cross-validation
Una singola suddivisione train-test può dare un quadro eccessivamente ottimistico o pessimistico a seconda di quali esempi finiscono in ogni partizione. Eseguire la cross-validation media il risultato su più suddivisioni per ottenere un punteggio più affidabile.
from sklearn.ensemble import BaggingClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import cross_val_score
from sklearn.datasets import load_breast_cancer
import numpy as np
X, y = load_breast_cancer(return_X_y=True)
clf = BaggingClassifier(
estimator=DecisionTreeClassifier(),
n_estimators=50,
random_state=42,
)
scores = cross_val_score(clf, X, y, cv=5)
print(f"CV scores: {scores.round(4)}")
print(f"Mean: {scores.mean():.4f}, Std: {scores.std():.4f}")Output:
CV scores: [0.9123 0.9211 0.9825 0.9561 1. ]
Mean: 0.9544, Std: 0.0339Una bassa deviazione standard tra i fold indica che il modello generalizza in modo costante. Un'alta deviazione standard suggerisce che il modello è sensibile a quali dati finiscono in ogni fold.
Bagging vs. Random Forest
Random Forest è l'algoritmo basato sul bagging più popolare. Estende il bagging semplice selezionando casualmente anche un sottoinsieme di feature ad ogni decisione di split — non solo un sottoinsieme di righe — il che decorrelata ulteriormente gli alberi e spesso fornisce una migliore accuratezza.
| Bagging | Random Forest | |
|---|---|---|
| Campionamento delle righe | Bootstrap (con reinserimento) | Bootstrap (con reinserimento) |
| Campionamento delle feature | Opzionale (max_features) | Sempre, ad ogni split |
| Base learner | Qualsiasi estimatore | Solo albero decisionale |
| Interpretabilità | Bassa | Bassa |
| Uso tipico | Quando si vuole aggregare un modello non ad albero | Miglior baseline ensemble universale |
Quando si aggregano alberi decisionali, RandomForestClassifier è quasi sempre la scelta migliore. Usa BaggingClassifier quando vuoi aggregare un base learner diverso — ad esempio, un KNeighborsClassifier da K-Nearest Neighbors o una regressione logistica da Logistic Regression.
Quando usare il bagging
Il bagging è più efficace quando:
- Il tuo modello base ha alta varianza (alberi decisionali profondi, modelli polinomiali di grado elevato).
- Hai abbastanza dati per rendere significativi i campioni bootstrap diversificati.
- Puoi permetterti un addestramento parallelo, perché ogni base learner si addestra in modo indipendente e il carico di lavoro può essere distribuito tra i core della CPU (
n_jobs=-1).
È meno utile quando:
- Il modello base ha già bassa varianza (ad es., modelli lineari con forte regolarizzazione).
- Hai bisogno di un modello singolo e interpretabile — un ensemble di 50 alberi non è facile da spiegare a uno stakeholder.
- Il costo computazionale è più importante dell'accuratezza — addestrare 50 modelli è 50 volte più lento che addestrarne uno.
Per i modelli ad alto bias, considera Grid Search per ottimizzare gli iperparametri, oppure passa a un metodo di boosting. Per la valutazione, verifica sempre il tuo modello su una suddivisione hold-out creata con train-test split o con la cross-validation.
Riepilogo
- La bootstrap aggregation addestra molti base learner su campioni casuali dei dati (estratti con reinserimento) e ne media le previsioni.
- Riduce la varianza senza aumentare significativamente il bias, rendendola ideale per i modelli ad alta varianza come gli alberi decisionali profondi.
- scikit-learn fornisce
BaggingClassifiereBaggingRegressor; i parametri chiave sonon_estimators,max_samplesemax_features. - Abilita
oob_score=Trueper una stima gratuita della generalizzazione che non richiede un set di validazione separato. - Quando si aggregano alberi,
RandomForestClassifierè generalmente preferibile; usaBaggingClassifierper aggregare altri tipi di modello.