26 · Baum-Ensembles und Gradient Boosting
modellvergleich_auc.png
Abbildung · Quellcode

Erzeugt von fig_modellvergleich() in module/26-ensembles-boosting/code/figures.py, Zeile 98–191.
Python
Python-Code: in eine Datei mit Endung
.py schreiben und mit dem ▶-Knopf in VS Code ausführen – oder Zeile für Zeile in die Python-Konsole. Setzt die in Modul 02 eingerichtete Umgebung voraus.def fig_modellvergleich(X, y, cv) -> None: """CV-AUC per model with error bars. Title/caption never hardcode a winner -- build.py/README quote whatever this run actually produced.""" tree = DecisionTreeClassifier(max_depth=4, random_state=SEED) rf = RandomForestClassifier( n_estimators=300, max_features="sqrt", class_weight="balanced", random_state=SEED, n_jobs=-1, ) hgb = HistGradientBoostingClassifier( max_iter=300, max_depth=5, learning_rate=0.05, class_weight="balanced", random_state=SEED, ) model_specs = [ ("Entscheidungsbaum\n(Tiefe 4)", build_pipeline(tree)), ("Random\nForest", build_pipeline(rf)), ("HistGradBoost\n(sklearn)", build_native_missing_pipeline(hgb)), ] # Try optional boosters pos_weight = float((y == 0).sum()) / float((y == 1).sum()) try: import xgboost as xgb except Exception: xgb = None if xgb is not None: try: xgb_m = xgb.XGBClassifier( n_estimators=300, max_depth=5, learning_rate=0.05, scale_pos_weight=pos_weight, eval_metric="auc", random_state=SEED, verbosity=0, use_label_encoder=False, ) except TypeError: xgb_m = xgb.XGBClassifier( n_estimators=300, max_depth=5, learning_rate=0.05, scale_pos_weight=pos_weight, eval_metric="auc", random_state=SEED, verbosity=0, ) model_specs.append(("XGBoost", build_pipeline(xgb_m))) try: import lightgbm as lgb except Exception: lgb = None if lgb is not None: lgb_m = lgb.LGBMClassifier( n_estimators=300, max_depth=5, learning_rate=0.05, is_unbalance=True, random_state=SEED, verbose=-1, ) model_specs.append(("LightGBM", build_pipeline(lgb_m))) names, means, stds = [], [], [] for name, pipe in model_specs: with warnings.catch_warnings(): warnings.simplefilter("ignore") scores = cross_val_score(pipe, X, y, cv=cv, scoring="roc_auc") names.append(name) means.append(scores.mean()) stds.append(scores.std()) x = np.arange(len(names)) colors = [PRIMARY, PALETTE[2], PALETTE[4], EVENT, PALETTE[5]][:len(names)] # Data-driven verdict for the title -- never a hardcoded winner. tree_mean = means[0] other_pairs = list(zip(names[1:], means[1:])) best_name, best_mean = max(other_pairs, key=lambda t: t[1]) best_name_plain = best_name.replace("\n", " ").strip() gap = best_mean - tree_mean if gap > 0.02: verdict = f"{best_name_plain} übertrifft den einzelnen Baum" elif gap > -0.02: verdict = "Ensembles liegen etwa gleichauf mit dem einzelnen Baum" else: verdict = "der einzelne Baum schneidet hier nicht schlechter ab" fig, ax = plt.subplots(figsize=(max(7, len(names) * 1.5), 4.5)) bars = ax.bar(x, means, yerr=stds, color=colors, width=0.55, capsize=5, error_kw={"lw": 1.5}) ax.axhline(0.5, color=SECONDARY, lw=0.8, ls="--", label="Zufallsklassifikator") for bar, m, s in zip(bars, means, stds): ax.text(bar.get_x() + bar.get_width() / 2, bar.get_height() + s + 0.01, f"{m:.3f}", ha="center", va="bottom", fontsize=9, fontweight="bold") ax.set_xticks(x) ax.set_xticklabels(names, fontsize=10) ax.set_ylim(0.4, 1.0) ax.set_ylabel("Kreuzvalidierte ROC-AUC (±1 SD)") ax.set_title(f"Modellvergleich: Baum-Ensembles & Gradient Boosting\n({verdict}, n≈{len(y)})") ax.legend(loc="lower right") save(fig, ASSETS / "modellvergleich_auc.png")