La Potenza degli Ensemble Methods
Gli ensemble methods combinano più modelli deboli per crearne uno forte. L'intuizione è semplice: chiedere l'opinione a molti esperti mediocri e aggregare le risposte produce risultati migliori di affidarsi a un singolo esperto. Questa è la tecnica che vince la maggior parte delle competizioni di ML su Kaggle e che alimenta molti sistemi in produzione. I tre pilastri degli ensemble sono bagging, boosting e stacking.
Il bagging (Bootstrap Aggregating) addestra modelli indipendenti su sottoinsiemi casuali dei dati e ne aggrega le previsioni. Riduce la varianza. Il boosting addestra modelli sequenzialmente, dove ogni nuovo modello corregge gli errori del precedente. Riduce il bias. Lo stacking usa le previsioni di modelli base come input per un meta-modello che impara a combinarle ottimamente.
Cosa Imparerai in Questo Articolo
- Bagging e Random Forest: ridurre la varianza
- AdaBoost: il primo algoritmo di boosting
- Gradient Boosting: lo stato dell'arte
- XGBoost, LightGBM e CatBoost: le implementazioni moderne
- Stacking e Voting: combinare modelli eterogenei
- Hyperparameter tuning per ensemble
Bagging e Random Forest
Il bagging crea N copie del dataset tramite bootstrap sampling (campionamento con reinserimento), addestra un modello su ciascuna copia e aggrega le previsioni con voto di maggioranza (classificazione) o media (regressione). Il Random Forest estende il bagging aggiungendo la randomizzazione delle feature: ad ogni split, solo un sottoinsieme casuale delle feature viene considerato. Questa doppia randomizzazione produce alberi decorrelati, riducendo ulteriormente la varianza.
AdaBoost: Boosting Adattivo
AdaBoost (Adaptive Boosting) è stato il primo algoritmo di boosting di successo. Addestra modelli sequenzialmente, assegnando pesi maggiori ai campioni classificati erroneamente dal modello precedente. Ogni nuovo modello si concentra sugli errori più difficili. I modelli vengono combinati con una media pesata, dove il peso di ciascun modello dipende dalla sua accuracy.
from sklearn.ensemble import (
BaggingClassifier, RandomForestClassifier,
AdaBoostClassifier, GradientBoostingClassifier,
VotingClassifier, StackingClassifier
)
from sklearn.tree import DecisionTreeClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.model_selection import cross_val_score
from sklearn.datasets import load_breast_cancer
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
import numpy as np
data = load_breast_cancer()
X, y = data.data, data.target
# Modelli ensemble
models = {
'Decision Tree': DecisionTreeClassifier(random_state=42),
'Bagging (50 trees)': BaggingClassifier(
estimator=DecisionTreeClassifier(random_state=42),
n_estimators=50, random_state=42, n_jobs=-1
),
'Random Forest': RandomForestClassifier(
n_estimators=100, random_state=42, n_jobs=-1
),
'AdaBoost': AdaBoostClassifier(
n_estimators=100, learning_rate=0.1, random_state=42
),
'Gradient Boosting': GradientBoostingClassifier(
n_estimators=100, learning_rate=0.1,
max_depth=3, random_state=42
)
}
print("Confronto Ensemble Methods:")
for name, model in models.items():
scores = cross_val_score(model, X, y, cv=5, scoring='accuracy')
print(f" {name:<25s}: {scores.mean():.4f} (+/- {scores.std():.4f})")
Gradient Boosting: Il Re delle Competizioni
Il Gradient Boosting costruisce modelli sequenzialmente, dove ogni nuovo modello viene addestrato sui residui (errori) del modello precedente. Usa il gradient descent per minimizzare una loss function arbitraria. Il risultato è un modello estremamente potente ma che richiede un tuning accurato degli iperparametri per evitare l'overfitting.
XGBoost (eXtreme Gradient Boosting) è l'implementazione più popolare: aggiunge regularization L1/L2, gestione nativa dei valori mancanti, parallelizzazione e pruning intelligente. LightGBM di Microsoft è ottimizzato per velocità e memoria su grandi dataset (cresce l'albero leaf-wise invece che level-wise). CatBoost di Yandex gestisce nativamente le feature categoriche senza necessità di encoding manuale.
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.model_selection import GridSearchCV, cross_val_score
from sklearn.datasets import load_breast_cancer
import numpy as np
data = load_breast_cancer()
X, y = data.data, data.target
# Gradient Boosting di scikit-learn con Grid Search
param_grid = {
'n_estimators': [50, 100, 200],
'learning_rate': [0.01, 0.05, 0.1],
'max_depth': [3, 5, 7],
'min_samples_split': [2, 5],
'subsample': [0.8, 1.0]
}
gb = GradientBoostingClassifier(random_state=42)
# Nota: GridSearchCV con molti parametri e' lento
# In pratica si usa RandomizedSearchCV
from sklearn.model_selection import RandomizedSearchCV
random_search = RandomizedSearchCV(
gb, param_grid,
n_iter=20, # 20 combinazioni random
cv=5,
scoring='accuracy',
random_state=42,
n_jobs=-1
)
random_search.fit(X, y)
print(f"Best score: {random_search.best_score_:.4f}")
print(f"Best params: {random_search.best_params_}")
# XGBoost (se installato)
try:
from xgboost import XGBClassifier
xgb = XGBClassifier(
n_estimators=200,
learning_rate=0.05,
max_depth=5,
subsample=0.8,
colsample_bytree=0.8,
reg_alpha=0.1, # L1 regularization
reg_lambda=1.0, # L2 regularization
random_state=42,
eval_metric='logloss'
)
scores_xgb = cross_val_score(xgb, X, y, cv=5, scoring='accuracy')
print(f"\nXGBoost: {scores_xgb.mean():.4f} (+/- {scores_xgb.std():.4f})")
except ImportError:
print("\nXGBoost non installato. Installa con: pip install xgboost")
Stacking e Voting
Il Voting Classifier combina le previsioni di modelli diversi. L'hard voting usa il voto di maggioranza. Il soft voting media le probabilità predette (generalmente più efficace). Lo Stacking va oltre: usa le previsioni dei modelli base come feature di input per un meta-learner che impara la combinazione ottimale.
from sklearn.ensemble import (
VotingClassifier, StackingClassifier,
RandomForestClassifier, GradientBoostingClassifier
)
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.model_selection import cross_val_score
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import make_pipeline
from sklearn.datasets import load_breast_cancer
data = load_breast_cancer()
X, y = data.data, data.target
# Modelli base
rf = RandomForestClassifier(n_estimators=100, random_state=42)
gb = GradientBoostingClassifier(n_estimators=100, random_state=42)
svm = make_pipeline(StandardScaler(), SVC(probability=True, random_state=42))
lr = make_pipeline(StandardScaler(), LogisticRegression(max_iter=10000))
# Soft Voting
voting = VotingClassifier(
estimators=[('rf', rf), ('gb', gb), ('svm', svm), ('lr', lr)],
voting='soft'
)
# Stacking con meta-learner LogisticRegression
stacking = StackingClassifier(
estimators=[('rf', rf), ('gb', gb), ('svm', svm)],
final_estimator=LogisticRegression(max_iter=10000),
cv=5
)
# Confronto
for name, model in [('RF', rf), ('GB', gb), ('Voting', voting), ('Stacking', stacking)]:
scores = cross_val_score(model, X, y, cv=5, scoring='accuracy')
print(f"{name:<12s}: {scores.mean():.4f} (+/- {scores.std():.4f})")
Bagging vs Boosting: Il bagging riduce la varianza combinando modelli indipendenti (Random Forest). Il boosting riduce il bias correggendo errori sequenzialmente (XGBoost). Il bagging è parallelizzabile e più resistente all'overfitting. Il boosting è generalmente più potente ma richiede tuning più attento. In pratica, Gradient Boosting (XGBoost, LightGBM) è spesso la scelta migliore per dati tabulari.
Punti Chiave
- Bagging riduce la varianza combinando modelli indipendenti (Random Forest)
- Boosting riduce il bias correggendo errori sequenzialmente (AdaBoost, Gradient Boosting)
- XGBoost/LightGBM sono lo stato dell'arte per dati tabulari
- Learning rate basso + molti estimators = migliori risultati ma training più lento
- Stacking combina modelli eterogenei con un meta-learner
- RandomizedSearchCV è più efficiente di GridSearchCV con molti iperparametri







