28 votos

Forma correcta de calibrar los clasificadores con CalibratedClassifierCV

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

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

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

Me temo que al utilizar el mismo conjunto de entrenamiento estoy rompiendo el disjoint data regla. 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 que tiene la desventaja de dejar menos datos para el entrenamiento. Además, si CalibratedClassifierCV sólo debería ajustarse a modelos ajustados en un conjunto de entrenamiento diferente, ¿por qué sus opciones por defecto serían cv=3 ¿que también se ajustará al estimador base? ¿La validación cruzada maneja la regla de disyunción por sí misma?

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

29voto

codevour Puntos 3246

Hay dos cosas que se mencionan en el Documentos de CalibratedClassifierCV que dan pistas sobre las formas en que se puede utilizar:

estimador_de_base: Si cv=prefit, el clasificador debe haber sido ajustado ya en los datos.

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

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

Número uno:

  • Entrena tu modelo como siempre, your_model.fit(X_train, y_train) .
  • A continuación, crea su instancia CCCV, your_cccv = CalibratedClassifierCV(your_model, cv='prefit') . Obsérvese que se ha fijado cv para indicar que su modelo ya ha sido ajustado.
  • Por último, llama a your_cccv.fit(X_validation, y_validation) . Estos datos de validación se utilizan únicamente con fines de calibración.

Número dos:

  • Tienes una nueva, sin formación modelo.
  • A continuación, se crea your_cccv=CalibratedClassifierCV(your_untrained_model, cv=3) . Aviso cv es ahora el número de pliegues.
  • Por último, llama a your_cccv.fit(X, y) . Dado que su modelo no está entrenado, X e y deben utilizarse tanto para el entrenamiento como para la calibración. La forma de asegurar que los datos son "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 para la calibración. El método dos utiliza la validación cruzada para intentar aprovechar al máximo los datos para ambos fines.

27voto

Vitaly Puntos 53

Yo también estoy interesado en esta cuestión y quería añadir algunos experimentos para entender mejor el CalibratedClassifierCV (CCCV).

Como ya se ha dicho, hay dos formas de utilizarlo.

#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 calibrándolo sobre los mismos datos que hemos ajustado.

#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 que hay que utilizar un conjunto disjunto, esto podría ser útil porque permite inspeccionar my_clf (por ejemplo, para ver el 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 se promedian los coeficientes).

Decidí comparar estos 3 métodos en cuanto a su calibración en un conjunto de pruebas completamente retenido.

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

He añadido un poco de desequilibrio de clases y sólo he proporcionado 500 muestras para hacer de éste un problema difícil.

Hago 100 ensayos, cada vez probando cada método y trazando su curva de calibración.

enter image description here

Boxplots de las puntuaciones Brier en todas las pruebas:

enter image description here

Aumentar el número de muestras a 10.000:

enter image description here

enter image description here

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

enter image description here

enter image description here

Parece que no hay suficientes muestras para calibrar. Aumentando las muestras a 10.000

enter image description here

enter image description here

Código 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')

Por lo tanto, los resultados de la puntuación Brier no son concluyentes, pero según las curvas parece ser mejor utilizar el segundo método.

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X