Wahrscheinlichkeitskalibrierungskurven

Beginner

This tutorial is from open-source community. Access the source code

Einführung

In diesem Lab werden wir lernen, wie man Kalibrierungskurven verwendet, um die vorhergesagten Wahrscheinlichkeiten eines Klassifizierungsmodells zu evaluieren. Wir werden scikit-learn verwenden, um die Klassifizierung durchzuführen und die Ergebnisse zu visualisieren.

VM-Tipps

Nachdem die VM gestartet ist, klicken Sie in der oberen linken Ecke, um zur Registerkarte Notebook zu wechseln und Jupyter Notebook für die Übung zu öffnen.

Manchmal müssen Sie einige Sekunden warten, bis Jupyter Notebook vollständig geladen ist. Die Validierung von Vorgängen kann aufgrund der Einschränkungen von Jupyter Notebook nicht automatisiert werden.

Wenn Sie bei der Lernphase Probleme haben, können Sie Labby gerne fragen. Geben Sie nach der Sitzung Feedback, und wir werden das Problem für Sie prompt beheben.

Dataset

Wir werden einen synthetischen binären Klassifizierungsdatensatz mit 100.000 Proben und 20 Merkmalen verwenden. Von den 20 Merkmalen sind nur 2 informativ, 10 sind redundant (zufällige Kombinationen der informativen Merkmale) und die verbleibenden 8 sind nicht informativ (zufällige Zahlen). Von den 100.000 Proben werden 1.000 zur Modellanpassung verwendet und der Rest zur Testung.

from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split

X, y = make_classification(
    n_samples=100_000, n_features=20, n_informative=2, n_redundant=10, random_state=42
)

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.99, random_state=42
)

Kalibrierungskurven

Wir werden mehrere Klassifizierer mit Kalibrierungskurven vergleichen. Zunächst werden wir vergleichen:

  • Logistische Regression (als Referenz)
  • Unkalibrierter Gaußscher Naiver Bayes
  • Gaußscher Naiver Bayes mit isotoner und sigmoidaler Kalibrierung
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec

from sklearn.calibration import CalibratedClassifierCV, CalibrationDisplay
from sklearn.linear_model import LogisticRegression
from sklearn.naive_bayes import GaussianNB

lr = LogisticRegression(C=1.0)
gnb = GaussianNB()
gnb_isotonic = CalibratedClassifierCV(gnb, cv=2, method="isotonic")
gnb_sigmoid = CalibratedClassifierCV(gnb, cv=2, method="sigmoid")

clf_list = [
    (lr, "Logistische Regression"),
    (gnb, "Naiver Bayes"),
    (gnb_isotonic, "Naiver Bayes + Isoton"),
    (gnb_sigmoid, "Naiver Bayes + Sigmoid"),
]

fig = plt.figure(figsize=(10, 10))
gs = GridSpec(4, 2)
colors = plt.get_cmap("Dark2")

ax_calibration_curve = fig.add_subplot(gs[:2, :2])
calibration_displays = {}
for i, (clf, name) in enumerate(clf_list):
    clf.fit(X_train, y_train)
    display = CalibrationDisplay.from_estimator(
        clf,
        X_test,
        y_test,
        n_bins=10,
        name=name,
        ax=ax_calibration_curve,
        color=colors(i),
    )
    calibration_displays[name] = display

ax_calibration_curve.grid()
ax_calibration_curve.set_title("Kalibrierungsplots (Naiver Bayes)")

## Füge Histogramm hinzu
grid_positions = [(2, 0), (2, 1), (3, 0), (3, 1)]
for i, (_, name) in enumerate(clf_list):
    row, col = grid_positions[i]
    ax = fig.add_subplot(gs[row, col])

    ax.hist(
        calibration_displays[name].y_prob,
        range=(0, 1),
        bins=10,
        label=name,
        color=colors(i),
    )
    ax.set(title=name, xlabel="Durchschnittliche vorhergesagte Wahrscheinlichkeit", ylabel="Anzahl")

plt.tight_layout()
plt.show()

Linearer Support-Vektor-Klassifizierer

Als nächstes werden wir vergleichen:

  • Logistische Regression (Referenz)
  • Unkalibrierter Linearer Support-Vektor-Klassifizierer (SVC)
  • Linearer SVC mit isotoner und sigmoidaler Kalibrierung
import numpy as np

from sklearn.svm import LinearSVC


class NaivelyCalibratedLinearSVC(LinearSVC):
    """LinearSVC mit `predict_proba`-Methode, die die Ausgabe von `decision_function`
    naiv für die binäre Klassifizierung skaliert."""

    def fit(self, X, y):
        super().fit(X, y)
        df = self.decision_function(X)
        self.df_min_ = df.min()
        self.df_max_ = df.max()

    def predict_proba(self, X):
        """Min-Max-Skalierung der Ausgabe von `decision_function` auf [0, 1]."""
        df = self.decision_function(X)
        calibrated_df = (df - self.df_min_) / (self.df_max_ - self.df_min_)
        proba_pos_class = np.clip(calibrated_df, 0, 1)
        proba_neg_class = 1 - proba_pos_class
        proba = np.c_[proba_neg_class, proba_pos_class]
        return proba

lr = LogisticRegression(C=1.0)
svc = NaivelyCalibratedLinearSVC(max_iter=10_000, dual="auto")
svc_isotonic = CalibratedClassifierCV(svc, cv=2, method="isotonic")
svc_sigmoid = CalibratedClassifierCV(svc, cv=2, method="sigmoid")

clf_list = [
    (lr, "Logistische Regression"),
    (svc, "SVC"),
    (svc_isotonic, "SVC + Isoton"),
    (svc_sigmoid, "SVC + Sigmoid"),
]

fig = plt.figure(figsize=(10, 10))
gs = GridSpec(4, 2)

ax_calibration_curve = fig.add_subplot(gs[:2, :2])
calibration_displays = {}
for i, (clf, name) in enumerate(clf_list):
    clf.fit(X_train, y_train)
    display = CalibrationDisplay.from_estimator(
        clf,
        X_test,
        y_test,
        n_bins=10,
        name=name,
        ax=ax_calibration_curve,
        color=colors(i),
    )
    calibration_displays[name] = display

ax_calibration_curve.grid()
ax_calibration_curve.set_title("Kalibrierungsplots (SVC)")

## Füge Histogramm hinzu
grid_positions = [(2, 0), (2, 1), (3, 0), (3, 1)]
for i, (_, name) in enumerate(clf_list):
    row, col = grid_positions[i]
    ax = fig.add_subplot(gs[row, col])

    ax.hist(
        calibration_displays[name].y_prob,
        range=(0, 1),
        bins=10,
        label=name,
        color=colors(i),
    )
    ax.set(title=name, xlabel="Durchschnittliche vorhergesagte Wahrscheinlichkeit", ylabel="Anzahl")

plt.tight_layout()
plt.show()

Evaluation

Wir werden die Klassifizierer mit mehreren Klassifikationsmetriken evaluieren: brier_score_loss, log_loss, Genauigkeit, Vollständigkeit, F1-Score und ROC AUC.

from collections import defaultdict

import pandas as pd

from sklearn.metrics import (
    precision_score,
    recall_score,
    f1_score,
    brier_score_loss,
    log_loss,
    roc_auc_score,
)

scores = defaultdict(list)
for i, (clf, name) in enumerate(clf_list):
    clf.fit(X_train, y_train)
    y_prob = clf.predict_proba(X_test)
    y_pred = clf.predict(X_test)
    scores["Classifier"].append(name)

    for metric in [brier_score_loss, log_loss, roc_auc_score]:
        score_name = metric.__name__.replace("_", " ").replace("score", "").capitalize()
        scores[score_name].append(metric(y_test, y_prob[:, 1]))

    for metric in [precision_score, recall_score, f1_score]:
        score_name = metric.__name__.replace("_", " ").replace("score", "").capitalize()
        scores[score_name].append(metric(y_test, y_pred))

    score_df = pd.DataFrame(scores).set_index("Classifier")
    score_df.round(decimals=3)

Zusammenfassung

Wir haben gelernt, wie man Kalibrierungskurven verwendet, um die vorhergesagten Wahrscheinlichkeiten eines Klassifikationsmodells zu evaluieren. Wir haben mehrere Klassifizierer mit Kalibrierungskurven verglichen und sie mit mehreren Klassifikationsmetriken bewertet. Wir haben auch gelernt, dass die parametrische sigmoidale Kalibrierung mit Situationen umgehen kann, in denen die Kalibrierungskurve des Basisklassifizierers sigmoidal ist, aber nicht, wenn sie transponiert-sigmoidal ist. Die nicht-parametrische isotone Kalibrierung kann mit beiden Situationen umgehen, erfordert jedoch möglicherweise mehr Daten, um gute Ergebnisse zu erzielen.