Scikit poprawny sposób kalibracji klasyfikatorów za pomocą CalibratedClassifierCV

16

Scikit ma CalibratedClassifierCV , co pozwala nam skalibrować nasze modele na konkretnej parze X, y. Stwierdza to również jasnodata for fitting the classifier and for calibrating it must be disjoint.

Jeśli muszą być rozłączne, czy uzasadnione jest przeszkolenie klasyfikatora w następujących kwestiach?

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

Obawiam się, że stosując ten sam zestaw treningowy, łamię disjoint datazasadę. Alternatywą może być zestaw sprawdzania poprawności

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

Co ma tę wadę, że pozostawia mniej danych na szkolenie. Ponadto, jeśli CalibratedClassifierCV powinien pasować tylko do modeli pasujących do innego zestawu treningowego, dlaczego miałoby to być domyślne opcje cv=3, które pasują również do estymatora podstawowego? Czy walidacja krzyżowa sama sobie radzi z zasadą rozłączności?

Pytanie: jaki jest właściwy sposób korzystania z CalibratedClassifierCV?

sapo_cosmico
źródło

Odpowiedzi:

18

W dokumentach CalibratedClassifierCV wspomniane są dwie rzeczy, które wskazują, w jaki sposób można go użyć:

base_estimator: Jeśli cv = prefit, klasyfikator musi być już dopasowany do danych.

cv: Jeżeli „wstępne dopasowanie” zostanie zaliczone, zakłada się, że base_estimator został już dopasowany i wszystkie dane są wykorzystywane do kalibracji.

Mogę oczywiście interpretować to źle, ale wygląda na to, że możesz użyć CCCV (skrót od CalibratedClassifierCV) na dwa sposoby:

Numer jeden:

  • Trenować swój model jak zwykle your_model.fit(X_train, y_train).
  • Następnie należy utworzyć instancję CCCV, your_cccv = CalibratedClassifierCV(your_model, cv='prefit'). Zauważ, że ustawiono cvopcję oznaczenia, że ​​model jest już dopasowany.
  • Wreszcie dzwonisz your_cccv.fit(X_validation, y_validation). Te dane walidacyjne są wykorzystywane wyłącznie do celów kalibracji.

Numer dwa:

  • Masz nowy, nieprzeszkolony model.
  • Następnie tworzysz your_cccv=CalibratedClassifierCV(your_untrained_model, cv=3). Uwaga cvto teraz liczba fałd.
  • Wreszcie dzwonisz your_cccv.fit(X, y). Ponieważ twój model nie jest przeszkolony, X i y muszą być używane zarówno do treningu, jak i kalibracji. Sposobem na zapewnienie, że dane są „rozłączne”, jest wzajemna walidacja: dla każdego danego złożenia CCCV podzieli X i y na twoje dane treningowe i kalibracyjne, aby się nie nakładały.

TLDR: Metoda pierwsza pozwala kontrolować, co jest używane do treningu i kalibracji. Metoda druga wykorzystuje weryfikację krzyżową, aby jak najlepiej wykorzystać dane do obu celów.

Pintas
źródło
14

Interesuje mnie również to pytanie i chciałem dodać eksperymenty, aby lepiej zrozumieć CalibratedClassifierCV (CCCV).

Jak już powiedziano, istnieją dwa sposoby korzystania z niego.

#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)

Alternatywnie, możemy wypróbować drugą metodę, ale po prostu skalibrować na tych samych danych, na których się dopasowaliśmy.

#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)

Chociaż dokumenty ostrzegają przed użyciem zestawu rozłącznego, może to być przydatne, ponieważ pozwala ci to sprawdzić my_clf(np. Zobaczyć coef_, które są niedostępne z obiektu CalibratedClassifierCV). (Czy ktoś wie, jak to uzyskać od skalibrowanych klasyfikatorów --- po pierwsze, są trzy z nich, więc czy średnie współczynniki?).

Postanowiłem porównać te 3 metody pod względem ich kalibracji na całkowicie przetestowanym zestawie testowym.

Oto zestaw danych:

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])

Wrzuciłem nierównowagę klas i dostarczyłem tylko 500 próbek, aby uczynić to trudnym problemem.

Przeprowadzam 100 prób, za każdym razem próbując każdej metody i wykreślając jej krzywą kalibracji.

wprowadź opis zdjęcia tutaj

Wykresy wyników Briera we wszystkich próbach:

wprowadź opis zdjęcia tutaj

Zwiększenie liczby próbek do 10.000:

wprowadź opis zdjęcia tutaj

wprowadź opis zdjęcia tutaj

Jeśli zmienimy klasyfikator na Naive Bayes, wracając do 500 próbek:

wprowadź opis zdjęcia tutaj

wprowadź opis zdjęcia tutaj

Wydaje się, że to za mało próbek do kalibracji. Zwiększenie próbek do 10.000

wprowadź opis zdjęcia tutaj

wprowadź opis zdjęcia tutaj

Pełny kod

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')

Tak więc wyniki Briera są niejednoznaczne, ale zgodnie z krzywymi najlepszym rozwiązaniem wydaje się zastosowanie drugiej metody.

użytkownik0
źródło