Scikit-Methode zum Kalibrieren von Klassifikatoren mit CalibratedClassifierCV

14

Scikit verfügt über CalibratedClassifierCV , mit dem wir unsere Modelle für ein bestimmtes X, Y-Paar kalibrieren können. Es heißt auch klar, dassdata for fitting the classifier and for calibrating it must be disjoint.

Wenn sie disjunkt sein müssen, ist es legitim, den Klassifikator mit den folgenden zu trainieren?

model = CalibratedClassifierCV(my_classifier)
model.fit(X_train, y_train)

Ich fürchte, dass ich mit dem gleichen Trainingssatz gegen die disjoint dataRegel verstoße. Eine Alternative könnte sein, einen Validierungssatz zu haben

my_classifier.fit(X_train, y_train)
model = CalibratedClassifierCV(my_classifier, cv='prefit')
model.fit(X_valid, y_valid)

Was den Nachteil hat, weniger Daten für das Training zu hinterlassen. Wenn CalibratedClassifierCV nur für Modelle geeignet sein sollte, die in ein anderes Trainingsset passen, warum sollten es dann Standardoptionen sein cv=3, die auch für den Basisschätzer passen? Behandelt die Kreuzvalidierung die disjunkte Regel alleine?

Frage: Wie wird CalibratedClassifierCV richtig angewendet?

sapo_cosmico
quelle

Antworten:

17

In den CalibratedClassifierCV-Dokumenten werden zwei Dinge erwähnt, die darauf hinweisen , wie sie verwendet werden können:

base_estimator: Wenn cv = prefit, muss der Klassifikator bereits auf Daten passen.

cv: Wenn "prefit" übergeben wird, wird davon ausgegangen, dass base_estimator bereits installiert wurde und alle Daten für die Kalibrierung verwendet werden.

Ich kann das offensichtlich falsch interpretieren, aber es scheint, dass Sie das CCCV (kurz für CalibratedClassifierCV) auf zwei Arten verwenden können:

Nummer Eins:

  • Sie trainieren Ihr Modell wie gewohnt your_model.fit(X_train, y_train).
  • Anschließend erstellen Sie Ihre CCCV-Instanz your_cccv = CalibratedClassifierCV(your_model, cv='prefit'). Beachten Sie cv, dass Sie festlegen , dass Ihr Modell bereits angepasst wurde.
  • Schließlich rufst du an your_cccv.fit(X_validation, y_validation). Diese Validierungsdaten werden ausschließlich zu Kalibrierungszwecken verwendet.

Nummer zwei:

  • Sie haben ein neues, ungeschultes Modell.
  • Dann erschaffst du your_cccv=CalibratedClassifierCV(your_untrained_model, cv=3). Beachten Sie cvjetzt die Anzahl der Falten.
  • Schließlich rufst du an your_cccv.fit(X, y). Da Ihr Modell nicht geschult ist, müssen X und Y sowohl für das Training als auch für die Kalibrierung verwendet werden. Der Weg, um sicherzustellen, dass die Daten "disjunkt" sind, ist eine Kreuzvalidierung: Für jede gegebene Falte teilt CCCV X und Y in Ihre Trainings- und Kalibrierungsdaten auf, sodass sie sich nicht überlappen.

TLDR: Mit der ersten Methode können Sie steuern, was für das Training und für die Kalibrierung verwendet wird. Methode zwei verwendet die Kreuzvalidierung, um zu versuchen, für beide Zwecke das Beste aus Ihren Daten zu machen.

Pintas
quelle
12

Ich interessiere mich auch für diese Frage und wollte einige Experimente hinzufügen, um CalibratedClassifierCV (CCCV) besser zu verstehen.

Wie bereits gesagt, gibt es zwei Möglichkeiten, es zu verwenden.

#Method 1, train classifier within CCCV
model = CalibratedClassifierCV(my_clf)
model.fit(X_train_val, y_train_val)

#Method 2, train classifier and then use CCCV on DISJOINT set
my_clf.fit(X_train, y_train)
model = CalibratedClassifierCV(my_clf, cv='prefit')
model.fit(X_val, y_val)

Alternativ könnten wir die zweite Methode ausprobieren, aber nur mit denselben Daten kalibrieren, auf die wir angepasst haben.

#Method 2 Non disjoint, train classifier on set, then use CCCV on SAME set used for training
my_clf.fit(X_train_val, y_train_val)
model = CalibratedClassifierCV(my_clf, cv='prefit')
model.fit(X_train_val, y_train_val)

Die Dokumentation warnt zwar vor der Verwendung eines disjunkten Satzes, dies kann jedoch nützlich sein, da Sie dann eine Überprüfung durchführen können my_clf(z. B. um zu sehen coef_, welche im CalibratedClassifierCV-Objekt nicht verfügbar sind). (Weiß jemand, wie man dies von den kalibrierten Klassifikatoren erhält - zum einen gibt es drei davon, würden Sie also die Koeffizienten mitteln?).

Ich habe mich entschlossen, diese 3 Methoden im Hinblick auf ihre Kalibrierung an einem vollständig ausgestreckten Testgerät zu vergleichen.

Hier ist ein Datensatz:

X, y = datasets.make_classification(n_samples=500, n_features=200,
                                    n_informative=10, n_redundant=10,
                                    #random_state=42, 
                                    n_clusters_per_class=1, weights = [0.8,0.2])

Ich warf ein Klassenungleichgewicht ein und stellte nur 500 Proben zur Verfügung, um dies zu einem schwierigen Problem zu machen.

Ich führe 100 Versuche durch, wobei jedes Mal jede Methode ausprobiert und ihre Kalibrierungskurve gezeichnet wird.

Bildbeschreibung hier eingeben

Boxplots des Brier werden in allen Versuchen gewertet:

Bildbeschreibung hier eingeben

Erhöhung der Anzahl der Proben auf 10.000:

Bildbeschreibung hier eingeben

Bildbeschreibung hier eingeben

Wenn wir den Klassifikator in Naive Bayes ändern, gehen wir auf 500 Proben zurück:

Bildbeschreibung hier eingeben

Bildbeschreibung hier eingeben

Dies scheint nicht genug Proben zu sein, um zu kalibrieren. Erhöhung der Stichproben auf 10.000

Bildbeschreibung hier eingeben

Bildbeschreibung hier eingeben

Vollständiger Code

print(__doc__)

# Based on code by Alexandre Gramfort <[email protected]>
#         Jan Hendrik Metzen <[email protected]>

import matplotlib.pyplot as plt

from sklearn import datasets
from sklearn.naive_bayes import GaussianNB
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import brier_score_loss
from sklearn.calibration import CalibratedClassifierCV, calibration_curve
from sklearn.model_selection import train_test_split


def plot_calibration_curve(clf, name, ax, X_test, y_test, title):

    y_pred = clf.predict(X_test)
    if hasattr(clf, "predict_proba"):
        prob_pos = clf.predict_proba(X_test)[:, 1]
    else:  # use decision function
        prob_pos = clf.decision_function(X_test)
        prob_pos = \
            (prob_pos - prob_pos.min()) / (prob_pos.max() - prob_pos.min())

    clf_score = brier_score_loss(y_test, prob_pos, pos_label=y.max())

    fraction_of_positives, mean_predicted_value = \
        calibration_curve(y_test, prob_pos, n_bins=10, normalize=False)

    ax.plot(mean_predicted_value, fraction_of_positives, "s-",
             label="%s (%1.3f)" % (name, clf_score), alpha=0.5, color='k', marker=None)

    ax.set_ylabel("Fraction of positives")
    ax.set_ylim([-0.05, 1.05])
    ax.set_title(title)

    ax.set_xlabel("Mean predicted value")

    plt.tight_layout()
    return clf_score

    fig, (ax1, ax2, ax3) = plt.subplots(nrows=3, ncols=1, figsize=(6,12))

    ax1.plot([0, 1], [0, 1], "k:", label="Perfectly calibrated",)
    ax2.plot([0, 1], [0, 1], "k:", label="Perfectly calibrated")
    ax3.plot([0, 1], [0, 1], "k:", label="Perfectly calibrated")

    scores = {'Method 1':[],'Method 2':[],'Method 3':[]}


fig, (ax1, ax2, ax3) = plt.subplots(nrows=3, ncols=1, figsize=(6,12))

ax1.plot([0, 1], [0, 1], "k:", label="Perfectly calibrated",)
ax2.plot([0, 1], [0, 1], "k:", label="Perfectly calibrated")
ax3.plot([0, 1], [0, 1], "k:", label="Perfectly calibrated")

scores = {'Method 1':[],'Method 2':[],'Method 3':[]}

for i in range(0,100):

    X, y = datasets.make_classification(n_samples=10000, n_features=200,
                                        n_informative=10, n_redundant=10,
                                        #random_state=42, 
                                        n_clusters_per_class=1, weights = [0.8,0.2])

    X_train_val, X_test, y_train_val, y_test = train_test_split(X, y, test_size=0.80,
                                                        #random_state=42
                                                               )

    X_train, X_val, y_train, y_val = train_test_split(X_train_val, y_train_val, test_size=0.80,
                                                      #random_state=42
                                                     )

    #my_clf = GaussianNB()
    my_clf = LogisticRegression()

    #Method 1, train classifier within CCCV
    model = CalibratedClassifierCV(my_clf)
    model.fit(X_train_val, y_train_val)
    r = plot_calibration_curve(model, "all_cal", ax1, X_test, y_test, "Method 1")
    scores['Method 1'].append(r)

    #Method 2, train classifier and then use CCCV on DISJOINT set
    my_clf.fit(X_train, y_train)
    model = CalibratedClassifierCV(my_clf, cv='prefit')
    model.fit(X_val, y_val)
    r = plot_calibration_curve(model, "all_cal", ax2, X_test, y_test, "Method 2")
    scores['Method 2'].append(r)

    #Method 3, train classifier on set, then use CCCV on SAME set used for training
    my_clf.fit(X_train_val, y_train_val)
    model = CalibratedClassifierCV(my_clf, cv='prefit')
    model.fit(X_train_val, y_train_val)
    r = plot_calibration_curve(model, "all_cal", ax3, X_test, y_test, "Method 2 non Dis")
    scores['Method 3'].append(r)

import pandas
b = pandas.DataFrame(scores).boxplot()
plt.suptitle('Brier score')

Die Ergebnisse des Brier-Scores sind also nicht schlüssig, aber nach den Kurven scheint es am besten zu sein, die zweite Methode zu verwenden.

user0
quelle