Modo corretto di Scikit per calibrare i classificatori con CalibratedClassifierCV


14

Scikit ha CalibratedClassifierCV , che ci consente di calibrare i nostri modelli su una particolare coppia X, y. Lo afferma anche chiaramentedata for fitting the classifier and for calibrating it must be disjoint.

Se devono essere disgiunti, è legittimo addestrare il classificatore con quanto segue?

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

Temo che usando lo stesso set di allenamento sto infrangendo la disjoint dataregola. Un'alternativa potrebbe essere quella di avere un set di validazione

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

Il che ha lo svantaggio di lasciare meno dati per la formazione. Inoltre, se CalibratedClassifierCV dovrebbe adattarsi solo a modelli adattati a un set di addestramento diverso, perché dovrebbero essere le opzioni predefinite cv=3, che si adatteranno anche allo stimatore di base? La convalida incrociata gestisce da sola la regola disgiunta?

Domanda: qual è il modo corretto di utilizzare CalibratedClassifierCV?

Risposte:


17

Ci sono due cose menzionate nei documenti CalibratedClassifierCV che suggeriscono i modi in cui può essere usato:

base_estimator: se cv = prefit, il classificatore deve essere già stato adattato ai dati.

cv: se viene passato "prefit", si presume che base_estimator sia già stato inserito e che tutti i dati vengano utilizzati per la calibrazione.

Potrei ovviamente interpretare questo errore, ma sembra che tu possa usare il CCCV (abbreviazione di CalibratedClassifierCV) in due modi:

Numero uno:

  • Ti alleni il tuo modello, come al solito, your_model.fit(X_train, y_train).
  • Poi, si crea l'istanza CCCV, your_cccv = CalibratedClassifierCV(your_model, cv='prefit'). Si noti che è impostato cvper segnalare che il modello è già stato adattato.
  • Finalmente chiami your_cccv.fit(X_validation, y_validation). Questi dati di convalida vengono utilizzati esclusivamente a scopo di calibrazione.

Numero due:

  • Hai un nuovo modello non addestrato .
  • Quindi crei your_cccv=CalibratedClassifierCV(your_untrained_model, cv=3). L'avviso cvè ora il numero di pieghe.
  • Finalmente chiami your_cccv.fit(X, y). Poiché il modello non è addestrato, X e y devono essere utilizzati sia per l'allenamento che per la calibrazione. Il modo per garantire che i dati siano "disgiunti" è la convalida incrociata: per ogni data piega, CCCV suddividerà X e y nei dati di allenamento e calibrazione, in modo che non si sovrappongano.

TLDR: il metodo 1 consente di controllare ciò che viene utilizzato per l'allenamento e per la calibrazione. Il secondo metodo usa la validazione incrociata per cercare di ottenere il massimo dai tuoi dati per entrambi gli scopi.


12

Sono interessato anche a questa domanda e volevo aggiungere alcuni esperimenti per comprendere meglio CalibratedClassifierCV (CCCV).

Come è già stato detto, ci sono due modi per usarlo.

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

In alternativa, potremmo provare il secondo metodo, ma semplicemente calibrare gli stessi dati su cui ci siamo adattati.

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

Sebbene i documenti avvertano di utilizzare un set disgiunto, questo potrebbe essere utile perché consente quindi di ispezionare my_clf(ad esempio, per vedere coef_, che non sono disponibili dall'oggetto CalibratedClassifierCV). (Qualcuno sa come ottenere questo dai classificatori calibrati --- per uno, ce ne sono tre, quindi faresti coefficienti medi?).

Ho deciso di confrontare questi 3 metodi in termini di calibrazione su un set di test completamente tenuto.

Ecco un set di dati:

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

Ho presentato uno squilibrio di classe e fornito solo 500 campioni per rendere questo un problema difficile.

Eseguo 100 prove, ogni volta provando ogni metodo e tracciando la sua curva di calibrazione.

inserisci qui la descrizione dell'immagine

Boxplots del punteggio Brier in tutte le prove:

inserisci qui la descrizione dell'immagine

Aumentando il numero di campioni a 10.000:

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

Se cambiamo il classificatore in Naive Bayes, tornando a 500 campioni:

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

Questo sembra non essere abbastanza campioni da calibrare. Aumento dei campioni a 10.000

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

Codice completo

print(__doc__)

# Based on code by Alexandre Gramfort <alexandre.gramfort@telecom-paristech.fr>
#         Jan Hendrik Metzen <jhm@informatik.uni-bremen.de>

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

Quindi, i risultati del punteggio di Brier sono inconcludenti, ma secondo le curve sembra essere meglio usare il secondo metodo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.