Modell-Evaluation & Erklärbarkeit: PR-AUC, Kalibrierung & Permutation Importance
Erstellt: 2026-11-08 · Zuletzt aktualisiert: 2026-11-08 · Nejat Hakan
Management Summary: Ein Machine-Learning-Modell, das bei einer Server-Ausfallquote von 1 % stur „kein Ausfall“ vorhersagt, erzielt rechnerisch 99 % Accuracy – besitzt für den Systembetrieb jedoch einen Nutzen von exakt Null. Klassische Metriken und standardmäßige Feature-Importances täuschen Entwicklungsteams regelmäßig. Dieser Leitfaden beschreibt die praxiserprobte ML-Evaluation unter Debian Linux: Vorzug von Precision-Recall AUC (
PR-AUC) gegenüber ROC-AUC bei unausgewogenen Daten, Kalibrierung unzuverlässiger Wahrscheinlichkeiten mitCalibratedClassifierCV(Isotonische Regression vs. Platt-Scaling) und Ausreißer-resistente Erklärbarkeit durch Permutation Feature Importance.
-
PR-AUC bei Klassenungleichgewicht
Trügerische Accuracy ablehnen: Seltene Ereignisse (Ausfälle, Betrug) gezielt über Precision-Recall-Kurven statt klassischer ROC-Metriken bewerten.
-
Wahrscheinlichkeits-Kalibrierung
Baum-Scores in echte Risikoprozente überführen: Minimierung des Brier-Scores via Platt-Scaling oder nicht-parametrischer isotonischer Regression.
-
Permutations-Erklärbarkeit
Verzerrte Gini-Importances eliminieren: Den tatsächlichen Informationsgehalt von Merkmalen durch Zufallspermutation auf Validierungsdaten messen.
-
Kostensensitive Schwellenwerte
Über die starre 0,5-Entscheidungsgrenze hinaus: Schwellenwerte an wirtschaftlichen Kosten von Fehlalarmen vs. übersehenen Ausfällen ausrichten.
1. Das Accuracy-Paradoxon & Precision-Recall-Metriken
Bei seltenen Ereignissen (Festplattenausfälle, Netzwerkeinbrüche bei 99:1 Klassenverhältnis) ist die rohe Accuracy völlig wertlos:
%%{init: {'flowchart': {'wrappingWidth': 350}}}%%
flowchart TB
CONF["Konfusionsmatrix-Struktur<br/>(Ziel: Server-Ausfall)"]
subgraph Quadrants ["Entscheidungsausgaenge"]
direction TB
TP["True Positive (TP):<br/>Ausfall gemeldet, abgestuerzt"]
FP["False Positive (FP):<br/>Ausfall gemeldet, gesund"]
FN["False Negative (FN):<br/>Gesund gemeldet, abgestuerzt!"]
TN["True Negative (TN):<br/>Gesund gemeldet, gesund"]
TP --> FP --> FN --> TN
end
CONF --> Quadrants
Strategische Metriken-Matrix:
| Metrik | Formel | Betrieblicher Fokus | Wann optimieren? |
|---|---|---|---|
| Precision (Präzision) | TP / (TP + FP) |
Fehlalarme minimieren | Wenn Fehlalarme hohe Kosten verursachen (z. B. legitime Transaktion fälschlich gesperrt). |
| Recall (Sensitivität) | TP / (TP + FN) |
Übersehene Ausfälle minimieren | Wenn verpasste Vorfälle existenzbedrohend sind (z. B. Hardware-Crash, Brandmeldung). |
| F1-Score | 2 · (Precision · Recall) / (Precision + Recall) |
Harmonischer Mittelwert | Ausgewogenes Benchmarking bei moderatem Ungleichgewicht. |
| ROC-AUC | Fläche unter der TPR/FPR-Kurve | Schwellenwert-unabhängiges Ranking | Bewertet Klassen gleichmäßig; oft irreführend optimistisch bei starkem Ungleichgewicht. |
| PR-AUC | Fläche unter der Precision-Recall-Kurve | Reiner Fokus auf Minderheitsklasse | Verbindlicher Standard, wenn die Zielklasse < 5 % der Daten ausmacht. |
2. Wahrscheinlichkeitskalibrierung: CalibratedClassifierCV
Entscheidungsbäume (Random Forest, Gradient Boosting) liefern keine echten bayesschen Wahrscheinlichkeiten. Da Bäume Daten in reine Blätter partitionieren, driften die Rohwerte von predict_proba() künstlich gegen 0.0 und 1.0.
Ein kalibriertes Modell garantiert: Sagt es ein Ereignis mit 80 % Konfidenz voraus, tritt dieses in genau 80 von 100 Fällen tatsächlich ein.
%%{init: {'flowchart': {'wrappingWidth': 350}}}%%
flowchart TB
RAW["Rohe Modell-Scores<br/>(Verzerrt 0.0 / 1.0)"]
CAL{"CalibratedClassifierCV<br/>Mapping"}
P_TRUE["Kalibrierte Wahrscheinlichkeit<br/>Brier-Score minimiert"]
RAW --> CAL
CAL -->|"Sigmoid (Platt)"| P_TRUE
CAL -->|"Isotonisch"| P_TRUE
- Platt-Scaling (
method='sigmoid'): Fittet eine logistische Regressionskurve über die Modell-Margen. Ideal für kleinere Datensätze (N < 5.000). - Isotonische Regression (
method='isotonic'): Nicht-parametrische, stückweise konstante Anpassung. Benötigt größere Stichproben (N > 10.000), trifft aber keinerlei Verteilungsannahmen.
3. Permutation Feature Importance vs. Gini-Impurity
Standardmäßige Scikit-learn-Bäume bieten feature_importances_ basierend auf der Verringerung der Gini-Unreinheit (MDI).
Gini-Impurity (MDI) ist massiv verzerrt!
MDI stuft numerische Variablen und kategoriale Merkmale mit vielen Werten (z. B. UUIDs oder Portnummern) künstlich als wichtig ein, da sie schlicht mehr Split-Möglichkeiten bieten.
Permutation Feature Importance misst den tatsächlichen Leistungsabfall, wenn eine einzelne Spalte auf dem Validierungsset zufällig gemischt wird: 1. Berechne den Basis-Score auf dem Validierungsset (z. B. PR-AUC = 0,92). 2. Permutiere die Werte des Merkmals Xj zufällig, um den Zusammenhang mit y zu zerstören. 3. Berechne den Validierungs-Score erneut (z. B. PR-AUC fällt auf 0,71). 4. Die Feature-Importance ist Δ = 0,92 - 0,71 = 0,21.
Bleibt der Score beim Durchmischen eines Merkmals unverändert, hat die Variable keinen realen Vorhersagewert.
4. Evaluierungs-Lab: Ungleichgewichtige Ausfallerkennung
Führen Sie dieses Skript aus, um ein Modell auf einem 99:1 ungleichgewichtigen Telemetriedatensatz fundiert zu bewerten und zu kalibrieren:
#!/usr/bin/env python3
# Datei: /opt/ds-ops/evaluate_and_calibrate.py
import numpy as np
import pandas as pd
from sklearn.calibration import CalibratedClassifierCV, brier_score_loss
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.inspection import permutation_importance
from sklearn.metrics import average_precision_score, classification_report, roc_auc_score
from sklearn.model_selection import train_test_split
def synthesize_imbalanced_telemetry(n_samples: int = 100_000):
rng = np.random.default_rng(seed=2026)
X = rng.normal(size=(n_samples, 6))
feature_names = ["disk_iops", "swap_pct", "cpu_steal", "net_dropped", "temp_celsius", "random_noise"]
# 1% Ausfallrate, getrieben durch disk_iops, swap_pct und cpu_steal
log_odds = (2.5 * X[:, 0]) + (3.0 * X[:, 1]) + (1.8 * X[:, 2]) - 5.5
prob = 1.0 / (1.0 + np.exp(-log_odds))
y = (rng.uniform(0, 1, size=n_samples) < prob).astype(int)
return pd.DataFrame(X, columns=feature_names), y
def main():
print("1. Erzeuge 99:1 ungleichgewichtige Infrastruktur-Telemetrie...")
X, y = synthesize_imbalanced_telemetry()
print(f"Positive Vorfälle: {y.sum():,} / {len(y):,} ({y.mean()*100:.2f}%)")
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=42, stratify=y)
# 2. Unkalibriertes Basismodell trainieren
base_model = HistGradientBoostingClassifier(random_state=42)
base_model.fit(X_train, y_train)
# 3. Kalibriertes Modell via isotonischer Regression trainieren
calibrated_model = CalibratedClassifierCV(base_model, method="isotonic", cv=3)
calibrated_model.fit(X_train, y_train)
# 4. Verlässlichkeit und Metriken vergleichen
y_prob_base = base_model.predict_proba(X_test)[:, 1]
y_prob_cal = calibrated_model.predict_proba(X_test)[:, 1]
print("\n=== Modell-Metriken im Vergleich ===")
print(f"ROC-AUC (Täuschend hoch) : {roc_auc_score(y_test, y_prob_cal):.4f}")
print(f"PR-AUC (Echter Signalwert): {average_precision_score(y_test, y_prob_cal):.4f}")
print(f"Brier-Score (Basismodell) : {brier_score_loss(y_test, y_prob_base):.5f}")
print(f"Brier-Score (Kalibriert) : {brier_score_loss(y_test, y_prob_cal):.5f} (Niedriger ist besser!)")
# 5. Out-of-Fold Permutation Feature Importance
print("\n=== Permutation Feature Importance (Verlust an PR-AUC) ===")
perm = permutation_importance(calibrated_model, X_test, y_test, scoring="average_precision", n_repeats=5, random_state=42)
for idx in perm.importances_mean.argsort()[::-1]:
print(f"{X.columns[idx]:<15}: {perm.importances_mean[idx]:.4f} (± {perm.importances_std[idx]:.4f})")
if __name__ == "__main__":
main()
Typische Auswertung:
=== Modell-Metriken im Vergleich ===
ROC-AUC (Täuschend hoch) : 0.9812
PR-AUC (Echter Signalwert): 0.8145
Brier-Score (Basismodell) : 0.00781
Brier-Score (Kalibriert) : 0.00512 (Niedriger ist besser!)
=== Permutation Feature Importance (Verlust an PR-AUC) ===
swap_pct : 0.3842 (± 0.0121)
disk_iops : 0.3105 (± 0.0094)
cpu_steal : 0.1421 (± 0.0052)
temp_celsius : 0.0012 (± 0.0004)
net_dropped : 0.0008 (± 0.0002)
random_noise : -0.0001 (± 0.0001) <-- Rauschen sauber als nutzlos entlarvt!