Inhaltsverzeichnis
- Warum Ensemble-Methoden?
- Grundprinzipien: Bagging und Boosting
- Random Forest
- Gradient Boosting
- XGBoost – Extreme Gradient Boosting
- Vergleich der Ensemble-Methoden
- Hyperparameter-Tuning
- Feature Importance und Interpretierbarkeit
- Praktisches Projekt: Titanic-Überleben
- Zusammenfassung und Ausblick
- Anhang: Schnellreferenz Ensemble-Methoden
Ensemble-Methoden – Random Forest & Gradient Boosting
Fach: Data Science & Artificial Intelligence
Schulstufe: 12. Schulstufe – HTL Informatik
Voraussetzungen: Supervised Learning, Decision Trees, Python / scikit-learn
Autor: HTL Pinkafeld – IF/IT
1. Warum Ensemble-Methoden?
Ein einzelner Decision Tree hat ein bekanntes Problem: er neigt stark zu Overfitting – der Baum lernt die Trainingsdaten auswendig, generalisiert aber schlecht auf neue Daten.
Die Idee hinter Ensembles
„Viele schwache Lerner zusammen ergeben einen starken Lerner."
Statt einem einzigen komplexen Modell kombinieren Ensemble-Methoden viele einfache Modelle (sog. Base Learner) zu einer gemeinsamen Vorhersage. Die Analogie aus dem Alltag:
- Einzelner Experte entscheidet → riskant, abhängig von Tagesform
- Komitee aus Experten entscheidet → Fehler einzelner werden ausgeglichen
Vorteile gegenüber einzelnen Modellen
| Eigenschaft | Einzelner Decision Tree | Ensemble |
|---|---|---|
| Overfitting | Sehr anfällig | Deutlich reduziert |
| Stabilität | Instabil (kleine Änderungen → anderer Baum) | Robust |
| Genauigkeit | Mittel | Hoch |
| Interpretierbarkeit | Gut | Eingeschränkt |
| Trainingszeit | Schnell | Länger |
2. Grundprinzipien: Bagging und Boosting
Es gibt zwei grundlegende Strategien, wie Modelle kombiniert werden können:
2.1 Bagging (Bootstrap Aggregating)
Beim Bagging werden mehrere Modelle unabhängig voneinander auf verschiedenen Teilmengen der Trainingsdaten trainiert. Die Vorhersagen werden anschließend aggregiert.
Trainingsdaten
│
├─ Zufällige Stichprobe 1 → Modell 1 ─┐
├─ Zufällige Stichprobe 2 → Modell 2 ─┤→ Mehrheitsvoting / Mittelwert
├─ Zufällige Stichprobe 3 → Modell 3 ─┤
└─ ... ┘
- Stichproben mit Zurücklegen (Bootstrap): ein Datenpunkt kann mehrfach vorkommen
- Modelle werden parallel trainiert
- Ziel: Varianz reduzieren
2.2 Boosting
Beim Boosting werden Modelle sequentiell trainiert. Jedes neue Modell konzentriert sich auf die Fehler seines Vorgängers.
Trainingsdaten
│
└─ Modell 1 → Fehler analysieren
│
Modell 2 (fokussiert auf Fehler von M1) → Fehler analysieren
│
Modell 3 (fokussiert auf Fehler von M2)
│
Finale Vorhersage (gewichtete Summe)
- Modelle werden sequentiell trainiert
- Jedes Modell korrigiert den Restfehler (Residual) des Vorgängers
- Ziel: Bias reduzieren
2.3 Gegenüberstellung
| Bagging | Boosting | |
|---|---|---|
| Trainingsreihenfolge | Parallel | Sequentiell |
| Fokus | Varianz reduzieren | Bias reduzieren |
| Datenpunkte | Zufällige Teilmengen | Fehlklassifizierte stärker gewichtet |
| Overfitting-Risiko | Gering | Kann hoch sein bei zu vielen Iterationen |
| Beispiele | Random Forest | Gradient Boosting, XGBoost, AdaBoost |
3. Random Forest
3.1 Grundidee
Random Forest ist die bekannteste Bagging-Methode. Es werden viele Decision Trees trainiert, die sich in zwei Punkten unterscheiden:
- Zufällige Datenstichproben: Jeder Baum wird auf einer Bootstrap-Stichprobe trainiert (~63 % der Daten)
- Zufällige Feature-Auswahl: Bei jedem Split wird nur eine zufällige Teilmenge der Features betrachtet
Die Zufälligkeit sorgt dafür, dass die Bäume diversifiziert sind – sie machen unterschiedliche Fehler, die sich beim Aggregieren gegenseitig aufheben.
3.2 Vorhersage
- Klassifikation: Mehrheitsvoting – die Klasse mit den meisten Stimmen gewinnt
- Regression: Mittelwert aller Baum-Vorhersagen
3.3 Out-of-Bag Error (OOB)
Da jeder Baum nur auf ~63 % der Daten trainiert wird, können die restlichen ~37 % (Out-of-Bag-Samples) als Validierungsset verwendet werden – ohne separaten Test-Split.
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
# Daten laden und aufteilen
iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(
iris.data, iris.target, test_size=0.2, random_state=42
)
# Random Forest trainieren
rf = RandomForestClassifier(
n_estimators=100, # Anzahl der Bäume
max_depth=None, # Bäume wachsen vollständig
max_features='sqrt', # √(Anzahl Features) pro Split
oob_score=True, # Out-of-Bag-Evaluation aktivieren
random_state=42
)
rf.fit(X_train, y_train)
# Ergebnisse
print(f"Test Accuracy: {accuracy_score(y_test, rf.predict(X_test)):.2%}")
print(f"OOB Accuracy: {rf.oob_score_:.2%}")
3.4 Wichtige Hyperparameter
| Parameter | Bedeutung | Typische Werte |
|---|---|---|
n_estimators |
Anzahl der Bäume | 100–500 |
max_depth |
Maximale Tiefe jedes Baums | None, 5–20 |
max_features |
Features pro Split | 'sqrt', 'log2', float |
min_samples_split |
Mindestproben für einen Split | 2–10 |
min_samples_leaf |
Mindestproben in einem Blatt | 1–5 |
3.5 Stärken und Schwächen
| ✅ Stärken | ❌ Schwächen |
|---|---|
| Sehr robust gegen Overfitting | Weniger interpretierbar als einzelner Baum |
| Funktioniert gut ohne viel Tuning | Hoher Speicherbedarf bei vielen Bäumen |
| Liefert Feature Importance | Langsamer als einzelner Baum |
| Integrierte OOB-Validierung | Schlechter bei sehr hochdimensionalen Daten |
| Kein Scaling der Features nötig | Extrapoliert nicht gut (nur Interpolation) |
4. Gradient Boosting
4.1 Grundidee
Gradient Boosting baut Bäume sequentiell, wobei jeder neue Baum den Restfehler (Residual) des bisherigen Modells minimiert.
Der Name leitet sich von der Gradientenabstieg-Optimierung ab: Anstatt Parameter eines einzelnen Modells zu optimieren, werden neue Modelle in die Richtung des stärksten Fehlerabbaus hinzugefügt.
4.2 Algorithmus Schritt für Schritt
1. Starte mit einem einfachen Modell F₀ (z.B. Mittelwert von y)
2. Für jede Iteration t = 1, 2, ..., T:
a) Berechne die Residuen: rᵢ = yᵢ - Fₜ₋₁(xᵢ)
b) Trainiere einen neuen Baum hₜ auf den Residuen
c) Aktualisiere das Modell:
Fₜ(x) = Fₜ₋₁(x) + η · hₜ(x)
(η = Lernrate, steuert Schrittgröße)
3. Finale Vorhersage: F_T(x)
Intuition: Jeder neue Baum „repariert" die Fehler aller vorherigen Bäume. Die Lernrate η kontrolliert, wie stark jeder Baum beiträgt – kleine Werte führen zu robusteren Modellen, brauchen aber mehr Bäume.
4.3 Scikit-learn: Gradient Boosting
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.metrics import classification_report
# Brustkrebsdatensatz (binäre Klassifikation)
data = load_breast_cancer()
X_train, X_test, y_train, y_test = train_test_split(
data.data, data.target, test_size=0.2, random_state=42
)
# Gradient Boosting Modell
gb = GradientBoostingClassifier(
n_estimators=200, # Anzahl Boosting-Runden
learning_rate=0.1, # Lernrate η
max_depth=3, # Flache Bäume sind typisch!
subsample=0.8, # Stochastic Gradient Boosting
random_state=42
)
gb.fit(X_train, y_train)
print(classification_report(y_test, gb.predict(X_test),
target_names=data.target_names))
4.4 Wichtige Hyperparameter
| Parameter | Bedeutung | Hinweis |
|---|---|---|
n_estimators |
Anzahl der Bäume | Mehr = besser, aber langsamer |
learning_rate |
Schrittgröße pro Baum | Kleiner = robuster, mehr Bäume nötig |
max_depth |
Tiefe jedes Baums | Typisch: 3–5 (flach!) |
subsample |
Anteil Trainingsdaten pro Baum | < 1.0 = Stochastic GB |
Wichtige Faustregel:
learning_rateundn_estimatorssind gegenläufig – kleine Lernrate braucht mehr Bäume. Typische Kombination:learning_rate=0.05,n_estimators=500.
5. XGBoost – Extreme Gradient Boosting
5.1 Was ist XGBoost?
XGBoost ist eine hochoptimierte Implementierung von Gradient Boosting, die in vielen Kaggle-Wettbewerben Maßstäbe gesetzt hat. Es bietet gegenüber scikit-learns GradientBoosting:
- Geschwindigkeit: Parallelisierung und Cache-optimierte Datenstrukturen
- Regularisierung: Eingebaute L1- und L2-Regularisierung
- Fehlende Werte: Automatische Behandlung ohne Preprocessing
- Früher Stopp: Training stoppt automatisch, wenn keine Verbesserung mehr eintritt
5.2 Installation und Verwendung
pip install xgboost
import xgboost as xgb
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
data = load_breast_cancer()
X_train, X_test, y_train, y_test = train_test_split(
data.data, data.target, test_size=0.2, random_state=42
)
# XGBoost-Modell (scikit-learn API)
model = xgb.XGBClassifier(
n_estimators=300,
learning_rate=0.05,
max_depth=4,
subsample=0.8,
colsample_bytree=0.8, # Anteil Features pro Baum
reg_alpha=0.1, # L1-Regularisierung
reg_lambda=1.0, # L2-Regularisierung
use_label_encoder=False,
eval_metric='logloss',
random_state=42
)
# Training mit Early Stopping
model.fit(
X_train, y_train,
eval_set=[(X_test, y_test)],
early_stopping_rounds=20, # Stoppt wenn 20 Runden keine Verbesserung
verbose=False
)
print(f"Beste Iteration: {model.best_iteration}")
print(f"Test Accuracy: {accuracy_score(y_test, model.predict(X_test)):.2%}")
5.3 Early Stopping
Early Stopping verhindert Overfitting automatisch:
Iteration 1: Validation-Loss = 0.342
Iteration 5: Validation-Loss = 0.198
Iteration 10: Validation-Loss = 0.145 ← Minimum
Iteration 15: Validation-Loss = 0.148
Iteration 20: Validation-Loss = 0.151
Iteration 30: Validation-Loss = 0.163 ← Stop! (20 Runden ohne Verbesserung)
6. Vergleich der Ensemble-Methoden
6.1 Direkter Vergleich auf Breast-Cancer-Datensatz
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import cross_val_score
from sklearn.datasets import load_breast_cancer
import xgboost as xgb
import numpy as np
data = load_breast_cancer()
X, y = data.data, data.target
modelle = {
'Decision Tree': DecisionTreeClassifier(max_depth=5, random_state=42),
'Random Forest': RandomForestClassifier(n_estimators=100, random_state=42),
'Gradient Boosting': GradientBoostingClassifier(n_estimators=100, random_state=42),
'XGBoost': xgb.XGBClassifier(n_estimators=100, random_state=42,
eval_metric='logloss', verbosity=0),
}
print(f"{'Modell':<22} {'CV Accuracy':>12} {'Std':>8}")
print("-" * 45)
for name, model in modelle.items():
scores = cross_val_score(model, X, y, cv=5, scoring='accuracy')
print(f"{name:<22} {scores.mean():>11.2%} {scores.std():>7.2%}")
6.2 Wann welche Methode?
| Situation | Empfehlung |
|---|---|
| Schneller guter Baseline | Random Forest |
| Maximale Genauigkeit | XGBoost / LightGBM |
| Viele irrelevante Features | Random Forest (robuster) |
| Strukturierte/tabellarische Daten | XGBoost |
| Interpretierbarkeit wichtig | Random Forest (Feature Importance) |
| Wenig Zeit für Tuning | Random Forest |
| Kaggle-Wettbewerbe | XGBoost / LightGBM |
7. Hyperparameter-Tuning
7.1 Grid Search
Systematisches Durchsuchen aller Parameterkombinationen:
from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_breast_cancer
data = load_breast_cancer()
param_grid = {
'n_estimators': [100, 200, 300],
'max_depth': [None, 5, 10],
'max_features': ['sqrt', 'log2'],
}
rf = RandomForestClassifier(random_state=42)
grid_search = GridSearchCV(
rf, param_grid,
cv=5,
scoring='accuracy',
n_jobs=-1, # alle CPU-Kerne nutzen
verbose=1
)
grid_search.fit(data.data, data.target)
print(f"Beste Parameter: {grid_search.best_params_}")
print(f"Beste CV Accuracy: {grid_search.best_score_:.2%}")
7.2 Random Search
Bei vielen Hyperparametern ist Random Search effizienter als Grid Search – es werden zufällige Kombinationen getestet:
from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import randint
param_dist = {
'n_estimators': randint(50, 500),
'max_depth': [None, 3, 5, 10, 20],
'max_features': ['sqrt', 'log2', 0.3, 0.5],
'min_samples_leaf': randint(1, 10),
}
random_search = RandomizedSearchCV(
RandomForestClassifier(random_state=42),
param_dist,
n_iter=50, # 50 zufällige Kombinationen testen
cv=5,
scoring='accuracy',
n_jobs=-1,
random_state=42
)
random_search.fit(data.data, data.target)
print(f"Beste Parameter: {random_search.best_params_}")
7.3 Vergleich der Suchmethoden
| Methode | Vorteil | Nachteil |
|---|---|---|
| Grid Search | Findet garantiert das Beste im Raster | Exponentiell teuer bei vielen Parametern |
| Random Search | Deutlich schneller, meist ähnlich gut | Kein Guarantee auf beste Kombination |
| Bayesian Optimization | Intelligenteste Suche | Komplexer zu implementieren (optuna, hyperopt) |
8. Feature Importance und Interpretierbarkeit
8.1 Feature Importance aus Random Forest
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_breast_cancer
data = load_breast_cancer()
rf = RandomForestClassifier(n_estimators=200, random_state=42)
rf.fit(data.data, data.target)
# Feature Importance als DataFrame
importance_df = pd.DataFrame({
'feature': data.feature_names,
'importance': rf.feature_importances_
}).sort_values('importance', ascending=True).tail(10)
# Visualisierung
importance_df.plot(kind='barh', x='feature', y='importance', legend=False)
plt.xlabel('Wichtigkeit (Gini-Impurity Reduktion)')
plt.title('Top 10 wichtigste Features – Random Forest')
plt.tight_layout()
plt.show()
8.2 Permutation Importance
Eine robustere Alternative: Features werden zufällig durchgemischt, und der Abfall in der Modellgüte wird gemessen.
from sklearn.inspection import permutation_importance
result = permutation_importance(rf, X_test, y_test, n_repeats=10, random_state=42)
perm_df = pd.DataFrame({
'feature': data.feature_names,
'importance_mean': result.importances_mean,
'importance_std': result.importances_std
}).sort_values('importance_mean', ascending=False).head(10)
print(perm_df.to_string(index=False))
8.3 SHAP-Werte (Ausblick)
Für tiefergehende Interpretierbarkeit bietet die SHAP-Bibliothek erklärbare Vorhersagen auf Einzelfall-Ebene:
# pip install shap
import shap
explainer = shap.TreeExplainer(rf)
shap_values = explainer.shap_values(X_test[:10])
# Visualisierung für einen einzelnen Datenpunkt
shap.force_plot(explainer.expected_value[1], shap_values[1][0], X_test[0],
feature_names=data.feature_names)
9. Praktisches Projekt: Titanic-Überleben
9.1 Aufgabenstellung
Auf Basis von Passagierdaten (Alter, Klasse, Geschlecht, ...) soll vorhergesagt werden, ob eine Person den Titanic-Untergang überlebt hat.
9.2 Vollständige Pipeline
import pandas as pd
import numpy as np
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.preprocessing import LabelEncoder
from sklearn.metrics import classification_report, confusion_matrix
import seaborn as sns
import matplotlib.pyplot as plt
# ── 1. Daten erstellen (vereinfacht) ──────────────────────────────────────
np.random.seed(42)
n = 500
data = pd.DataFrame({
'pclass': np.random.choice([1, 2, 3], n, p=[0.25, 0.25, 0.5]),
'sex': np.random.choice(['male', 'female'], n, p=[0.65, 0.35]),
'age': np.random.normal(30, 14, n).clip(1, 80),
'sibsp': np.random.choice([0, 1, 2, 3], n, p=[0.6, 0.25, 0.1, 0.05]),
'fare': np.random.exponential(35, n).clip(5, 500),
'embarked': np.random.choice(['S', 'C', 'Q'], n, p=[0.72, 0.19, 0.09]),
})
# Überleben hängt von Klasse und Geschlecht ab
survive_prob = (
(data['sex'] == 'female') * 0.5 +
(data['pclass'] == 1) * 0.2 +
(data['pclass'] == 2) * 0.1 -
(data['age'] > 60) * 0.1 + 0.15
).clip(0.05, 0.95)
data['survived'] = np.random.binomial(1, survive_prob)
# ── 2. Preprocessing ──────────────────────────────────────────────────────
le = LabelEncoder()
data['sex_enc'] = le.fit_transform(data['sex'])
data['embarked_enc'] = le.fit_transform(data['embarked'])
features = ['pclass', 'sex_enc', 'age', 'sibsp', 'fare', 'embarked_enc']
X = data[features]
y = data['survived']
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# ── 3. Modelle vergleichen ────────────────────────────────────────────────
modelle = {
'Random Forest': RandomForestClassifier(n_estimators=100, random_state=42),
'Gradient Boosting': GradientBoostingClassifier(n_estimators=100, learning_rate=0.1,
max_depth=3, random_state=42),
}
for name, model in modelle.items():
model.fit(X_train, y_train)
cv = cross_val_score(model, X, y, cv=5).mean()
print(f"\n{name} (CV: {cv:.2%})")
print(classification_report(y_test, model.predict(X_test),
target_names=['Nicht überlebt', 'Überlebt']))
# ── 4. Confusion Matrix visualisieren ────────────────────────────────────
best_model = modelle['Random Forest']
cm = confusion_matrix(y_test, best_model.predict(X_test))
sns.heatmap(cm, annot=True, fmt='d', cmap='Oranges',
xticklabels=['Nicht überlebt', 'Überlebt'],
yticklabels=['Nicht überlebt', 'Überlebt'])
plt.title('Confusion Matrix – Random Forest Titanic')
plt.tight_layout()
plt.show()
10. Zusammenfassung und Ausblick
Überblick Ensemble-Methoden
| Methode | Strategie | Basis-Lerner | Stärke |
|---|---|---|---|
| Random Forest | Bagging + zufällige Features | Decision Trees | Robust, kein Tuning nötig |
| Gradient Boosting | Boosting (Residuen) | Flache Decision Trees | Hohe Genauigkeit |
| XGBoost | Optimiertes Boosting | Flache Decision Trees | Schnell, regularisiert |
Checkliste: Ensemble-Methoden einsetzen
✅ Ausgangspunkt: Random Forest als schneller, robuster Baseline
✅ Für maximale Genauigkeit: XGBoost / Gradient Boosting
✅ Hyperparameter: n_estimators + learning_rate zuerst tunen
✅ Overfitting prüfen: Train-Score vs. Test-Score / Cross-Validation
✅ Feature Importance analysieren: irrelevante Features entfernen
✅ Early Stopping bei Boosting-Methoden aktivieren
Ausblick: Nächste Themen
- Unsupervised Learning: K-Means Clustering, PCA, Dimensionsreduktion
- Deep Learning: Neuronale Netze, Backpropagation, CNNs
- Natural Language Processing: Textklassifikation, Transformer-Modelle
- MLOps: Modelle versionieren, deployen und überwachen
Anhang: Schnellreferenz Ensemble-Methoden
# Random Forest
from sklearn.ensemble import RandomForestClassifier, RandomForestRegressor
rf = RandomForestClassifier(n_estimators=100, oob_score=True, random_state=42)
# Gradient Boosting
from sklearn.ensemble import GradientBoostingClassifier, GradientBoostingRegressor
gb = GradientBoostingClassifier(n_estimators=200, learning_rate=0.05, max_depth=3)
# XGBoost
import xgboost as xgb
xgb_model = xgb.XGBClassifier(n_estimators=300, learning_rate=0.05,
max_depth=4, subsample=0.8,
colsample_bytree=0.8, eval_metric='logloss')
# Hyperparameter-Tuning
from sklearn.model_selection import GridSearchCV, RandomizedSearchCV
# Feature Importance
importances = rf.feature_importances_ # Gini-basiert
from sklearn.inspection import permutation_importance
perm = permutation_importance(rf, X_test, y_test, n_repeats=10)
HTL Pinkafeld – IF/IT | Data Science & AI | 12. Schulstufe