Scikit forma correcta de calibrar clasificadores con CalibratedClassifierCV

14

Scikit tiene CalibratedClassifierCV , que nos permite calibrar nuestros modelos en un par X, y particular. También establece claramente quedata for fitting the classifier and for calibrating it must be disjoint.

Si deben ser disjuntos, ¿es legítimo entrenar al clasificador con lo siguiente?

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

Me temo que al usar el mismo conjunto de entrenamiento estoy rompiendo la disjoint dataregla. Una alternativa podría ser tener un conjunto de validación

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

Lo cual tiene la desventaja de dejar menos datos para el entrenamiento. Además, si CalibratedClassifierCV solo debe ajustarse a modelos que se ajustan a un conjunto de entrenamiento diferente, ¿por qué serían las opciones predeterminadas cv=3, que también se ajustarán al estimador base? ¿La validación cruzada maneja la regla disjunta por sí sola?

Pregunta: ¿cuál es la forma correcta de usar CalibratedClassifierCV?

sapo_cosmico
fuente

Respuestas:

17

Hay dos cosas mencionadas en los documentos CalibratedClassifierCV que sugieren las formas en que se puede usar:

base_estimator: si cv = prefit, el clasificador ya debe haberse ajustado a los datos.

cv: Si se pasa "prefit", se supone que ya se ha ajustado base_estimator y todos los datos se utilizan para la calibración.

Obviamente, puedo estar interpretando esto mal, pero parece que puede usar el CCCV (abreviatura de CalibratedClassifierCV) de dos maneras:

Número uno:

  • A entrenar a su modelo, como de costumbre, your_model.fit(X_train, y_train).
  • A continuación, se crea la instancia CCCV, your_cccv = CalibratedClassifierCV(your_model, cv='prefit'). Observe que configura cvpara marcar que su modelo ya se ha ajustado.
  • Finalmente llamas your_cccv.fit(X_validation, y_validation). Estos datos de validación se utilizan únicamente para fines de calibración.

Número dos:

  • Tienes un nuevo modelo sin entrenamiento .
  • Entonces creas your_cccv=CalibratedClassifierCV(your_untrained_model, cv=3). Aviso cves ahora el número de pliegues.
  • Finalmente llamas your_cccv.fit(X, y). Debido a que su modelo no está entrenado, X e y deben usarse tanto para entrenamiento como para calibración. La forma de garantizar que los datos sean 'disjuntos' es la validación cruzada: para cualquier pliegue dado, CCCV dividirá X e y en sus datos de entrenamiento y calibración, para que no se superpongan.

TLDR: el método uno le permite controlar lo que se utiliza para el entrenamiento y la calibración. El método dos utiliza la validación cruzada para tratar de aprovechar al máximo sus datos para ambos fines.

Pintas
fuente
12

También estoy interesado en esta pregunta y quería agregar algunos experimentos para comprender mejor CalibratedClassifierCV (CCCV).

Como ya se ha dicho, hay dos formas de 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)

Alternativamente, podríamos probar el segundo método pero solo calibrar con los mismos datos que ajustamos.

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

Aunque los documentos advierten sobre el uso de un conjunto disjunto, esto podría ser útil porque le permite luego inspeccionar my_clf(por ejemplo, para ver los coef_que no están disponibles en el objeto CalibratedClassifierCV). (¿Alguien sabe cómo obtener esto de los clasificadores calibrados, por ejemplo, hay tres de ellos, por lo que promediaría los coeficientes?).

Decidí comparar estos 3 métodos en términos de su calibración en un conjunto de prueba completamente extendido.

Aquí hay un conjunto de datos:

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

Agregué un desequilibrio de clase y solo proporcioné 500 muestras para hacer de este un problema difícil.

Realizo 100 pruebas, cada vez que pruebo cada método y trazo su curva de calibración.

ingrese la descripción de la imagen aquí

Gráficos de caja de los puntajes de Brier en todos los ensayos:

ingrese la descripción de la imagen aquí

Aumentar el número de muestras a 10,000:

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

Si cambiamos el clasificador a Naive Bayes, volviendo a 500 muestras:

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

Esto parece no ser suficientes muestras para calibrar. Incremento de muestras a 10,000

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

Código completo

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

Por lo tanto, los resultados del puntaje de Brier no son concluyentes, pero según las curvas parece ser mejor usar el segundo método.

usuario0
fuente