Wahrscheinlichkeitskalibrierung für die 3-Klassen-Klassifikation

Machine LearningMachine LearningBeginner
Jetzt üben

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

💡 Dieser Artikel wurde von AI-Assistenten übersetzt. Um die englische Version anzuzeigen, können Sie hier klicken

Einführung

In diesem Lab wird gezeigt, wie man die Sigmoid-Kalibrierung für die 3-Klassen-Klassifikation in Python mit scikit-learn verwendet. Es wird gezeigt, wie sich die Sigmoid-Kalibrierung auf die vorhergesagten Wahrscheinlichkeiten auswirkt und wie sie zur Verbesserung der Modellleistung eingesetzt werden kann.

Tipps für die VM

Nachdem der VM-Start abgeschlossen ist, klicken Sie in der oberen linken Ecke, um zur Registerkarte Notebook zu wechseln und Jupyter Notebook für die Übung zu nutzen.

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.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL sklearn(("Sklearn")) -.-> sklearn/CoreModelsandAlgorithmsGroup(["Core Models and Algorithms"]) sklearn(("Sklearn")) -.-> sklearn/ModelSelectionandEvaluationGroup(["Model Selection and Evaluation"]) sklearn(("Sklearn")) -.-> sklearn/UtilitiesandDatasetsGroup(["Utilities and Datasets"]) ml(("Machine Learning")) -.-> ml/FrameworkandSoftwareGroup(["Framework and Software"]) sklearn/CoreModelsandAlgorithmsGroup -.-> sklearn/ensemble("Ensemble Methods") sklearn/ModelSelectionandEvaluationGroup -.-> sklearn/metrics("Metrics") sklearn/ModelSelectionandEvaluationGroup -.-> sklearn/calibration("Probability Calibration") sklearn/UtilitiesandDatasetsGroup -.-> sklearn/datasets("Datasets") ml/FrameworkandSoftwareGroup -.-> ml/sklearn("scikit-learn") subgraph Lab Skills sklearn/ensemble -.-> lab-49074{{"Wahrscheinlichkeitskalibrierung für die 3-Klassen-Klassifikation"}} sklearn/metrics -.-> lab-49074{{"Wahrscheinlichkeitskalibrierung für die 3-Klassen-Klassifikation"}} sklearn/calibration -.-> lab-49074{{"Wahrscheinlichkeitskalibrierung für die 3-Klassen-Klassifikation"}} sklearn/datasets -.-> lab-49074{{"Wahrscheinlichkeitskalibrierung für die 3-Klassen-Klassifikation"}} ml/sklearn -.-> lab-49074{{"Wahrscheinlichkeitskalibrierung für die 3-Klassen-Klassifikation"}} end

Daten

Wir generieren einen Klassifikationsdatensatz mit 2000 Proben, 2 Merkmalen und 3 Zielklassen. Wir teilen die Daten wie folgt auf:

  • train: 600 Proben (zum Trainieren des Klassifikators)
  • valid: 400 Proben (zum Kalibrieren der vorhergesagten Wahrscheinlichkeiten)
  • test: 1000 Proben
import numpy as np
from sklearn.datasets import make_blobs

np.random.seed(0)

X, y = make_blobs(
    n_samples=2000, n_features=2, centers=3, random_state=42, cluster_std=5.0
)
X_train, y_train = X[:600], y[:600]
X_valid, y_valid = X[600:1000], y[600:1000]
X_train_valid, y_train_valid = X[:1000], y[:1000]
X_test, y_test = X[1000:], y[1000:]

Anpassen und Kalibrierung

Wir trainieren einen Random Forest-Klassifikator mit 25 Basis-Schätzern (Bäumen) auf den zusammengefügten Trainings- und Validierungsdaten (1000 Proben). Dies ist der unkalibrierte Klassifikator.

from sklearn.ensemble import RandomForestClassifier

clf = RandomForestClassifier(n_estimators=25)
clf.fit(X_train_valid, y_train_valid)

Um den kalibrierten Klassifikator zu trainieren, beginnen wir mit demselben Random Forest-Klassifikator, trainieren ihn jedoch nur mit der Trainingsdatensatzuntermenge (600 Proben) und kalibrieren ihn dann in einem zweistufigen Prozess mit method='sigmoid' unter Verwendung der Validierungsdatensatzuntermenge (400 Proben).

from sklearn.calibration import CalibratedClassifierCV

clf = RandomForestClassifier(n_estimators=25)
clf.fit(X_train, y_train)
cal_clf = CalibratedClassifierCV(clf, method="sigmoid", cv="prefit")
cal_clf.fit(X_valid, y_valid)

Wahrscheinlichkeiten vergleichen

Wir zeichnen ein 2-Simplex mit Pfeilen, die die Änderung der vorhergesagten Wahrscheinlichkeiten der Testproben anzeigen.

import matplotlib.pyplot as plt

plt.figure(figsize=(10, 10))
colors = ["r", "g", "b"]

clf_probs = clf.predict_proba(X_test)
cal_clf_probs = cal_clf.predict_proba(X_test)
## Plot arrows
for i in range(clf_probs.shape[0]):
    plt.arrow(
        clf_probs[i, 0],
        clf_probs[i, 1],
        cal_clf_probs[i, 0] - clf_probs[i, 0],
        cal_clf_probs[i, 1] - clf_probs[i, 1],
        color=colors[y_test[i]],
        head_width=1e-2,
    )

## Plot perfect predictions, at each vertex
plt.plot([1.0], [0.0], "ro", ms=20, label="Klasse 1")
plt.plot([0.0], [1.0], "go", ms=20, label="Klasse 2")
plt.plot([0.0], [0.0], "bo", ms=20, label="Klasse 3")

## Plot boundaries of unit simplex
plt.plot([0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], "k", label="Simplex")

## Annotate points 6 points around the simplex, and mid point inside simplex
plt.annotate(
    r"($\frac{1}{3}$, $\frac{1}{3}$, $\frac{1}{3}$)",
    xy=(1.0 / 3, 1.0 / 3),
    xytext=(1.0 / 3, 0.23),
    xycoords="data",
    arrowprops=dict(facecolor="black", shrink=0.05),
    horizontalalignment="center",
    verticalalignment="center",
)
plt.plot([1.0 / 3], [1.0 / 3], "ko", ms=5)
plt.annotate(
    r"($\frac{1}{2}$, $0$, $\frac{1}{2}$)",
    xy=(0.5, 0.0),
    xytext=(0.5, 0.1),
    xycoords="data",
    arrowprops=dict(facecolor="black", shrink=0.05),
    horizontalalignment="center",
    verticalalignment="center",
)
plt.annotate(
    r"($0$, $\frac{1}{2}$, $\frac{1}{2}$)",
    xy=(0.0, 0.5),
    xytext=(0.1, 0.5),
    xycoords="data",
    arrowprops=dict(facecolor="black", shrink=0.05),
    horizontalalignment="center",
    verticalalignment="center",
)
plt.annotate(
    r"($\frac{1}{2}$, $\frac{1}{2}$, $0$)",
    xy=(0.5, 0.5),
    xytext=(0.6, 0.6),
    xycoords="data",
    arrowprops=dict(facecolor="black", shrink=0.05),
    horizontalalignment="center",
    verticalalignment="center",
)
plt.annotate(
    r"($0$, $0$, $1$)",
    xy=(0, 0),
    xytext=(0.1, 0.1),
    xycoords="data",
    arrowprops=dict(facecolor="black", shrink=0.05),
    horizontalalignment="center",
    verticalalignment="center",
)
plt.annotate(
    r"($1$, $0$, $0$)",
    xy=(1, 0),
    xytext=(1, 0.1),
    xycoords="data",
    arrowprops=dict(facecolor="black", shrink=0.05),
    horizontalalignment="center",
    verticalalignment="center",
)
plt.annotate(
    r"($0$, $1$, $0$)",
    xy=(0, 1),
    xytext=(0.1, 1),
    xycoords="data",
    arrowprops=dict(facecolor="black", shrink=0.05),
    horizontalalignment="center",
    verticalalignment="center",
)
## Add grid
plt.grid(False)
for x in [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]:
    plt.plot([0, x], [x, 0], "k", alpha=0.2)
    plt.plot([0, 0 + (1 - x) / 2], [x, x + (1 - x) / 2], "k", alpha=0.2)
    plt.plot([x, x + (1 - x) / 2], [0, 0 + (1 - x) / 2], "k", alpha=0.2)

plt.title("Änderung der vorhergesagten Wahrscheinlichkeiten bei Testproben nach Sigmoid-Kalibrierung")
plt.xlabel("Wahrscheinlichkeit Klasse 1")
plt.ylabel("Wahrscheinlichkeit Klasse 2")
plt.xlim(-0.05, 1.05)
plt.ylim(-0.05, 1.05)
_ = plt.legend(loc="best")

Vergleich der Log-Loss

Wir vergleichen die Log-Loss des unkalibrierten und des kalibrierten Klassifikators bei den Vorhersagen der 1000 Testproben.

from sklearn.metrics import log_loss

score = log_loss(y_test, clf_probs)
cal_score = log_loss(y_test, cal_clf_probs)

print("Log-Loss von")
print(f" * unkalibrierten Klassifikator: {score:.3f}")
print(f" * kalibrierten Klassifikator: {cal_score:.3f}")

Grid generieren und plotten

Wir generieren ein Gitter von möglichen unkalibrierten Wahrscheinlichkeiten über den 2-Simplex, berechnen die entsprechenden kalibrierten Wahrscheinlichkeiten und zeichnen für jede Pfeile. Die Pfeile sind nach der höchsten unkalibrierten Wahrscheinlichkeit gefärbt. Dies veranschaulicht die gelernten Kalibrierungskarten:

plt.figure(figsize=(10, 10))
## Generiere Gitter von Wahrscheinlichkeitswerten
p1d = np.linspace(0, 1, 20)
p0, p1 = np.meshgrid(p1d, p1d)
p2 = 1 - p0 - p1
p = np.c_[p0.ravel(), p1.ravel(), p2.ravel()]
p = p[p[:, 2] >= 0]

## Verwende die drei klassenspezifischen Kalibrierer, um kalibrierte Wahrscheinlichkeiten zu berechnen
kalibrierter_klassifikator = cal_clf.calibrated_classifiers_[0]
vorhersage = np.vstack(
    [
        kalibrierer.predict(this_p)
        for kalibrierer, this_p in zip(kalibrierter_klassifikator.calibrators, p.T)
    ]
).T

## Normalisiere die kalibrierten Vorhersagen erneut, um sicherzustellen, dass sie innerhalb des
## Simplex bleiben. Dieser gleiche Normalisierungsschritt wird intern von der
## predict-Methode von CalibratedClassifierCV bei Mehrklassenproblemen durchgeführt.
vorhersage /= vorhersage.sum(axis=1)[:, None]

## Zeichne die Änderungen der vorhergesagten Wahrscheinlichkeiten, die durch die Kalibrierer verursacht werden
for i in range(vorhersage.shape[0]):
    plt.arrow(
        p[i, 0],
        p[i, 1],
        vorhersage[i, 0] - p[i, 0],
        vorhersage[i, 1] - p[i, 1],
        head_width=1e-2,
        color=colors[np.argmax(p[i])],
    )

## Zeichne die Grenzen des Einheits-Simplex
plt.plot([0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], "k", label="Simplex")

plt.grid(False)
for x in [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]:
    plt.plot([0, x], [x, 0], "k", alpha=0.2)
    plt.plot([0, 0 + (1 - x) / 2], [x, x + (1 - x) / 2], "k", alpha=0.2)
    plt.plot([x, x + (1 - x) / 2], [0, 0 + (1 - x) / 2], "k", alpha=0.2)

plt.title("Gelernte Sigmoid-Kalibrierungskarte")
plt.xlabel("Wahrscheinlichkeit Klasse 1")
plt.ylabel("Wahrscheinlichkeit Klasse 2")
plt.xlim(-0.05, 1.05)
plt.ylim(-0.05, 1.05)

plt.show()

Zusammenfassung

In diesem Lab wurde gezeigt, wie man die Sigmoid-Kalibrierung für die 3-Klassen-Klassifikation in Python mit scikit-learn verwendet. Es wurde der Einfluss der Kalibrierung auf die vorhergesagten Wahrscheinlichkeiten demonstriert und gezeigt, wie sie zur Verbesserung der Modellleistung genutzt werden kann.