Scikit bonne façon d'étalonner les classificateurs avec CalibratedClassifierCV

14

Scikit a CalibratedClassifierCV , qui nous permet d'étalonner nos modèles sur une paire X, y particulière. Il indique également clairement quedata for fitting the classifier and for calibrating it must be disjoint.

S'ils doivent être disjoints, est-il légitime de former le classificateur avec les éléments suivants?

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

Je crains qu'en utilisant le même ensemble d'entraînement, j'enfreigne la disjoint datarègle. Une alternative pourrait être d'avoir un ensemble de validation

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

Ce qui a l'inconvénient de laisser moins de données pour la formation. De plus, si CalibratedClassifierCV ne doit être ajusté que sur des modèles adaptés à un ensemble d'entraînement différent, pourquoi serait-ce les options par défaut cv=3qui conviennent également à l'estimateur de base? La validation croisée gère-t-elle la règle disjointe seule?

Question: quelle est la bonne façon d'utiliser CalibratedClassifierCV?

sapo_cosmico
la source

Réponses:

17

Il y a deux choses mentionnées dans les documents CalibratedClassifierCV qui suggèrent les façons dont il peut être utilisé:

base_estimator: Si cv = prefit, le classificateur doit déjà avoir été ajusté sur les données.

cv: Si «prefit» est passé, on suppose que base_estimator a déjà été installé et toutes les données sont utilisées pour l'étalonnage.

Je peux évidemment interpréter cela de manière erronée, mais il semble que vous pouvez utiliser le CCCV (abréviation de CalibratedClassifierCV) de deux manières:

Numéro un:

  • Vous formez votre modèle comme d' habitude, your_model.fit(X_train, y_train).
  • Ensuite, vous créez votre instance de CCCV, your_cccv = CalibratedClassifierCV(your_model, cv='prefit'). Notez que vous définissez cvpour signaler que votre modèle est déjà en forme.
  • Enfin, vous appelez your_cccv.fit(X_validation, y_validation). Ces données de validation sont utilisées uniquement à des fins d'étalonnage.

Numéro deux:

  • Vous avez un nouveau modèle non formé .
  • Ensuite, vous créez your_cccv=CalibratedClassifierCV(your_untrained_model, cv=3). Notez cvmaintenant le nombre de plis.
  • Enfin, vous appelez your_cccv.fit(X, y). Parce que votre modèle n'est pas formé, X et y doivent être utilisés à la fois pour la formation et l'étalonnage. La façon de s'assurer que les données sont «disjointes» est la validation croisée: pour tout pli donné, CCCV divisera X et y en vos données de formation et d'étalonnage, afin qu'elles ne se chevauchent pas.

TLDR: La première méthode vous permet de contrôler ce qui est utilisé pour la formation et pour l'étalonnage. La deuxième méthode utilise la validation croisée pour essayer de tirer le meilleur parti de vos données dans les deux cas.

Pintas
la source
12

Je suis également intéressé par cette question et je voulais ajouter quelques expériences pour mieux comprendre CalibratedClassifierCV (CCCV).

Comme cela a déjà été dit, il existe deux façons de l'utiliser.

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

Alternativement, nous pourrions essayer la deuxième méthode mais simplement calibrer sur les mêmes données que nous avons ajustées.

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

Bien que les documents vous avertissent d'utiliser un ensemble disjoint, cela pourrait être utile car il vous permet ensuite d'inspecter my_clf (par exemple, pour voir les éléments coef_qui ne sont pas disponibles à partir de l'objet CalibratedClassifierCV). (Quelqu'un sait-il comment l'obtenir à partir des classificateurs calibrés --- pour un, il y en a trois, alors feriez-vous la moyenne des coefficients?).

J'ai décidé de comparer ces 3 méthodes en termes de calibrage sur un banc d'essai complètement tenu.

Voici un ensemble de données:

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

J'ai ajouté un certain déséquilibre de classe et fourni seulement 500 échantillons pour en faire un problème difficile.

Je lance 100 essais, essayant à chaque fois chaque méthode et traçant sa courbe d'étalonnage.

entrez la description de l'image ici

Boxplots des scores Brier sur tous les essais:

entrez la description de l'image ici

Augmentation du nombre d'échantillons à 10 000:

entrez la description de l'image ici

entrez la description de l'image ici

Si nous changeons le classificateur en Naive Bayes, revenons à 500 échantillons:

entrez la description de l'image ici

entrez la description de l'image ici

Cela ne semble pas être suffisant pour calibrer les échantillons. Augmentation des échantillons à 10 000

entrez la description de l'image ici

entrez la description de l'image ici

Code complet

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

Ainsi, les résultats du score de Brier ne sont pas concluants, mais selon les courbes, il semble préférable d'utiliser la deuxième méthode.

user0
la source