Metode Ensemble
Metodele ensemble combină mai mulți algoritmi de învățare pentru a obține performanțe mai bune decât oricare model individual. Principiul de bază: "înțelepciunea mulțimii".
De ce funcționează?
Dacă avem mai mulți clasificatori independenți, fiecare cu acuratețe > 50%, combinarea lor prin vot majoritar crește probabilitatea de a obține răspunsul corect.
import numpy as np
# Simulare: 10 clasificatori cu acuratețe 60%
n_clasificatori = 10
acuratete_individual = 0.6
n_simulari = 10000
# Vot majoritar
corecte = 0
for _ in range(n_simulari):
voturi = np.random.random(n_clasificatori) < acuratete_individual
if voturi.sum() > n_clasificatori / 2:
corecte += 1
print(f"Acuratețe individuală: {acuratete_individual:.0%}")
print(f"Acuratețe ensemble (vot): {corecte/n_simulari:.0%}")
# ~73% pentru 10 clasificatori cu 60% acuratețe
Tipuri de ensemble
| Metodă | Strategie | Reduce |
|---|---|---|
| Bagging | Antrenare paralelă pe subseturi | Varianță |
| Boosting | Antrenare secvențială, focus pe erori | Bias |
| Voting | Combinare modele diferite | Ambele |
| Stacking | Model care învață să combine | Ambele |
Bagging (Bootstrap Aggregating)
Bagging antrenează modele identice pe subseturi diferite ale datelor (sampling cu înlocuire).
Principiu
- Creează N subseturi prin bootstrap (sampling cu înlocuire)
- Antrenează câte un model pe fiecare subset
- Combină predicțiile prin vot (clasificare) sau medie (regresie)
from sklearn.ensemble import BaggingClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
# Date
X, y = make_classification(n_samples=1000, n_features=20, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# Bagging cu arbori de decizie
bagging = BaggingClassifier(
estimator=DecisionTreeClassifier(),
n_estimators=100,
max_samples=0.8, # 80% din date pentru fiecare model
max_features=0.8, # 80% din features
bootstrap=True, # Sampling cu înlocuire
random_state=42
)
bagging.fit(X_train, y_train)
print(f"Acuratețe Bagging: {accuracy_score(y_test, bagging.predict(X_test)):.3f}")
# Comparație cu un singur arbore
tree = DecisionTreeClassifier(random_state=42)
tree.fit(X_train, y_train)
print(f"Acuratețe Arbore singur: {accuracy_score(y_test, tree.predict(X_test)):.3f}")
Random Forest
Random Forest este bagging cu arbori de decizie + selecție aleatoare de features la fiecare split.
from sklearn.ensemble import RandomForestClassifier
# Random Forest
rf = RandomForestClassifier(
n_estimators=100, # Numărul de arbori
max_depth=10, # Adâncimea maximă
max_features='sqrt', # sqrt(n_features) la fiecare split
min_samples_split=5, # Minim samples pentru split
min_samples_leaf=2, # Minim samples în frunze
bootstrap=True,
random_state=42,
n_jobs=-1 # Paralelizare
)
rf.fit(X_train, y_train)
print(f"Acuratețe Random Forest: {accuracy_score(y_test, rf.predict(X_test)):.3f}")
Importanța features
import matplotlib.pyplot as plt
import numpy as np
# Importanța features
importances = rf.feature_importances_
indices = np.argsort(importances)[::-1]
plt.figure(figsize=(10, 6))
plt.title("Importanța Features - Random Forest")
plt.bar(range(len(importances)), importances[indices])
plt.xticks(range(len(importances)), [f'F{i}' for i in indices], rotation=45)
plt.tight_layout()
plt.show()
Out-of-Bag (OOB) Score
Datele nefolosite în bootstrap pot fi folosite pentru validare.
rf_oob = RandomForestClassifier(
n_estimators=100,
oob_score=True, # Activează OOB
random_state=42
)
rf_oob.fit(X_train, y_train)
print(f"OOB Score: {rf_oob.oob_score_:.3f}")
print(f"Test Score: {rf_oob.score(X_test, y_test):.3f}")
Boosting
Boosting antrenează modele secvențial, fiecare nou model focalizându-se pe erorile celor anterioare.
AdaBoost (Adaptive Boosting)
Crește ponderile pentru exemplele clasificate greșit.
from sklearn.ensemble import AdaBoostClassifier
# AdaBoost
ada = AdaBoostClassifier(
estimator=DecisionTreeClassifier(max_depth=1), # "Stumps"
n_estimators=100,
learning_rate=0.1,
random_state=42
)
ada.fit(X_train, y_train)
print(f"Acuratețe AdaBoost: {accuracy_score(y_test, ada.predict(X_test)):.3f}")
Gradient Boosting
Antrenează modele pentru a prezice reziduurile (erorile) modelelor anterioare.
from sklearn.ensemble import GradientBoostingClassifier
# Gradient Boosting
gb = GradientBoostingClassifier(
n_estimators=100,
learning_rate=0.1,
max_depth=3,
min_samples_split=5,
min_samples_leaf=2,
subsample=0.8, # Stochastic GB
random_state=42
)
gb.fit(X_train, y_train)
print(f"Acuratețe Gradient Boosting: {accuracy_score(y_test, gb.predict(X_test)):.3f}")
XGBoost
XGBoost (Extreme Gradient Boosting) - implementare optimizată și regularizată.
# pip install xgboost
from xgboost import XGBClassifier
xgb = XGBClassifier(
n_estimators=100,
learning_rate=0.1,
max_depth=3,
subsample=0.8,
colsample_bytree=0.8,
reg_alpha=0.1, # Regularizare L1
reg_lambda=1.0, # Regularizare L2
random_state=42,
use_label_encoder=False,
eval_metric='logloss'
)
xgb.fit(X_train, y_train)
print(f"Acuratețe XGBoost: {accuracy_score(y_test, xgb.predict(X_test)):.3f}")
LightGBM
LightGBM - mai rapid, eficient pentru date mari.
# pip install lightgbm
from lightgbm import LGBMClassifier
lgbm = LGBMClassifier(
n_estimators=100,
learning_rate=0.1,
max_depth=3,
num_leaves=31,
subsample=0.8,
colsample_bytree=0.8,
random_state=42,
verbose=-1
)
lgbm.fit(X_train, y_train)
print(f"Acuratețe LightGBM: {accuracy_score(y_test, lgbm.predict(X_test)):.3f}")
CatBoost
CatBoost - excelent pentru date categorice.
# pip install catboost
from catboost import CatBoostClassifier
cat = CatBoostClassifier(
n_estimators=100,
learning_rate=0.1,
max_depth=3,
random_state=42,
verbose=0
)
cat.fit(X_train, y_train)
print(f"Acuratețe CatBoost: {accuracy_score(y_test, cat.predict(X_test)):.3f}")
Voting
Voting combină predicțiile mai multor modele diferite.
Hard Voting
Vot majoritar - alege clasa cu cele mai multe voturi.
from sklearn.ensemble import VotingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.tree import DecisionTreeClassifier
# Definire modele
models = [
('lr', LogisticRegression(max_iter=1000)),
('svm', SVC(kernel='rbf', probability=True)),
('dt', DecisionTreeClassifier(max_depth=5))
]
# Hard voting
voting_hard = VotingClassifier(estimators=models, voting='hard')
voting_hard.fit(X_train, y_train)
print(f"Acuratețe Hard Voting: {accuracy_score(y_test, voting_hard.predict(X_test)):.3f}")
Soft Voting
Media probabilităților - necesită modele cu predict_proba.
# Soft voting (media probabilităților)
voting_soft = VotingClassifier(estimators=models, voting='soft')
voting_soft.fit(X_train, y_train)
print(f"Acuratețe Soft Voting: {accuracy_score(y_test, voting_soft.predict(X_test)):.3f}")
Voting cu ponderi
# Voting cu ponderi diferite
voting_weighted = VotingClassifier(
estimators=models,
voting='soft',
weights=[2, 1, 1] # LR are pondere dublă
)
voting_weighted.fit(X_train, y_train)
print(f"Acuratețe Weighted Voting: {accuracy_score(y_test, voting_weighted.predict(X_test)):.3f}")
Stacking
Stacking folosește un meta-model care învață să combine predicțiile.
from sklearn.ensemble import StackingClassifier
# Modele de bază
base_models = [
('rf', RandomForestClassifier(n_estimators=50, random_state=42)),
('gb', GradientBoostingClassifier(n_estimators=50, random_state=42)),
('svm', SVC(kernel='rbf', probability=True, random_state=42))
]
# Meta-model
stacking = StackingClassifier(
estimators=base_models,
final_estimator=LogisticRegression(),
cv=5, # Cross-validation pentru predicții
stack_method='auto', # predict_proba dacă disponibil
passthrough=False # True pentru a include și features originale
)
stacking.fit(X_train, y_train)
print(f"Acuratețe Stacking: {accuracy_score(y_test, stacking.predict(X_test)):.3f}")
Comparație metode
from sklearn.model_selection import cross_val_score
import pandas as pd
models = {
'Decision Tree': DecisionTreeClassifier(max_depth=5, random_state=42),
'Random Forest': RandomForestClassifier(n_estimators=100, random_state=42),
'AdaBoost': AdaBoostClassifier(n_estimators=100, random_state=42),
'Gradient Boosting': GradientBoostingClassifier(n_estimators=100, random_state=42),
'Voting': VotingClassifier(
estimators=[
('rf', RandomForestClassifier(n_estimators=50)),
('gb', GradientBoostingClassifier(n_estimators=50))
],
voting='soft'
)
}
results = []
for name, model in models.items():
scores = cross_val_score(model, X, y, cv=5, scoring='accuracy')
results.append({
'Model': name,
'Mean': scores.mean(),
'Std': scores.std()
})
df_results = pd.DataFrame(results)
print(df_results.to_string(index=False))
Vizualizare: Bagging vs Boosting
import matplotlib.pyplot as plt
import numpy as np
from sklearn.datasets import make_moons
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import BaggingClassifier, AdaBoostClassifier
# Date
X, y = make_moons(n_samples=300, noise=0.3, random_state=42)
def plot_decision_boundary(model, X, y, ax, title):
h = 0.02
x_min, x_max = X[:, 0].min() - 0.5, X[:, 0].max() + 0.5
y_min, y_max = X[:, 1].min() - 0.5, X[:, 1].max() + 0.5
xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
np.arange(y_min, y_max, h))
Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
ax.contourf(xx, yy, Z, alpha=0.3, cmap='coolwarm')
ax.scatter(X[:, 0], X[:, 1], c=y, cmap='coolwarm', edgecolors='black')
ax.set_title(title)
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
# Arbore simplu
tree = DecisionTreeClassifier(max_depth=3, random_state=42)
tree.fit(X, y)
plot_decision_boundary(tree, X, y, axes[0, 0], 'Decision Tree')
# Bagging
bagging = BaggingClassifier(
estimator=DecisionTreeClassifier(max_depth=3),
n_estimators=50, random_state=42
)
bagging.fit(X, y)
plot_decision_boundary(bagging, X, y, axes[0, 1], 'Bagging (50 arbori)')
# AdaBoost
ada = AdaBoostClassifier(
estimator=DecisionTreeClassifier(max_depth=1),
n_estimators=50, random_state=42
)
ada.fit(X, y)
plot_decision_boundary(ada, X, y, axes[1, 0], 'AdaBoost (50 stumps)')
# Random Forest
rf = RandomForestClassifier(n_estimators=50, max_depth=3, random_state=42)
rf.fit(X, y)
plot_decision_boundary(rf, X, y, axes[1, 1], 'Random Forest (50 arbori)')
plt.tight_layout()
plt.show()
Hiperparametri importanți
Random Forest
| Parametru | Descriere | Valori tipice |
|---|---|---|
n_estimators |
Numărul de arbori | 100-500 |
max_depth |
Adâncimea maximă | 5-20 sau None |
max_features |
Features per split | 'sqrt', 'log2' |
min_samples_split |
Min samples pentru split | 2-10 |
min_samples_leaf |
Min samples în frunză | 1-5 |
Gradient Boosting
| Parametru | Descriere | Valori tipice |
|---|---|---|
n_estimators |
Numărul de etape | 100-1000 |
learning_rate |
Rata de învățare | 0.01-0.3 |
max_depth |
Adâncime arbori | 3-10 |
subsample |
Fracțiune date per arbore | 0.5-1.0 |
Tuning cu GridSearchCV
from sklearn.model_selection import GridSearchCV
# Random Forest
param_grid_rf = {
'n_estimators': [50, 100, 200],
'max_depth': [5, 10, None],
'max_features': ['sqrt', 'log2'],
'min_samples_split': [2, 5, 10]
}
grid_rf = GridSearchCV(
RandomForestClassifier(random_state=42),
param_grid_rf,
cv=5,
scoring='accuracy',
n_jobs=-1
)
grid_rf.fit(X_train, y_train)
print(f"Cei mai buni parametri RF: {grid_rf.best_params_}")
print(f"Cel mai bun scor RF: {grid_rf.best_score_:.3f}")
Când să folosești fiecare metodă?
| Situație | Metodă recomandată |
|---|---|
| Date multe, features multe | Random Forest, XGBoost |
| Date puține | AdaBoost, Voting |
| Overfitting arbori | Bagging |
| Underfitting | Boosting |
| Modele diverse disponibile | Voting, Stacking |
| Date categorice | CatBoost |
| Date foarte mari | LightGBM |
| Interpretabilitate | Random Forest (feature importance) |
Rezumat
- Bagging (Random Forest) - reduce varianța, arbori independenți
- Boosting (XGBoost, LightGBM) - reduce bias, arbori secvențiali
- Voting - combină modele diferite prin vot
- Stacking - meta-model care învață combinația optimă
- Random Forest - robust, puțin tuning necesar
- XGBoost/LightGBM - performanță de top, necesită tuning