Scikit maneira correta de calibrar classificadores com CalibratedClassifierCV

14

O Scikit possui o CalibratedClassifierCV , que permite calibrar nossos modelos em um par X, y específico. Também afirma claramente quedata for fitting the classifier and for calibrating it must be disjoint.

Se eles devem ser desarticulados, é legítimo treinar o classificador com o seguinte?

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

Receio que, usando o mesmo conjunto de treinamento, estou infringindo a disjoint dataregra. Uma alternativa pode ser ter um conjunto de validação

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

Qual tem a desvantagem de deixar menos dados para o treinamento. Além disso, se CalibratedClassifierCV deve ser adequado apenas aos modelos adequados a um conjunto de treinamento diferente, por que seriam as opções padrão cv=3, que também se ajustam ao estimador de base? A validação cruzada lida com a regra disjunta sozinha?

Pergunta: qual é a maneira correta de usar o CalibratedClassifierCV?

sapo_cosmico
fonte

Respostas:

17

Há duas coisas mencionadas nos documentos CalibratedClassifierCV que sugerem as maneiras como ele pode ser usado:

base_estimator: Se cv = prefit, o classificador já deve ter sido ajustado nos dados.

cv: Se “prefit” for passado, presume-se que o base_estimator já tenha sido ajustado e todos os dados sejam utilizados para calibração.

Obviamente, posso estar interpretando isso errado, mas parece que você pode usar o CCCV (abreviação de CalibratedClassifierCV) de duas maneiras:

Número um:

  • Você treina seu modelo como de costume your_model.fit(X_train, y_train).
  • Em seguida, você cria sua instância do CCCV your_cccv = CalibratedClassifierCV(your_model, cv='prefit'). Observe que você definiu cvpara sinalizar que seu modelo já estava em forma.
  • Finalmente, você liga your_cccv.fit(X_validation, y_validation). Esses dados de validação são usados ​​apenas para fins de calibração.

Número dois:

  • Você tem um novo modelo não treinado .
  • Então você cria your_cccv=CalibratedClassifierCV(your_untrained_model, cv=3). Aviso cvé agora o número de dobras.
  • Finalmente, você liga your_cccv.fit(X, y). Como seu modelo não é treinado, X e y devem ser usados ​​para treinamento e calibração. A maneira de garantir que os dados sejam "disjuntos" é a validação cruzada: para qualquer dobra, o CCCV dividirá X e y em seus dados de treinamento e calibração, para que não se sobreponham.

TLDR: O método um permite controlar o que é usado para treinamento e calibração. O método dois usa a validação cruzada para tentar tirar o máximo proveito dos seus dados para os dois propósitos.

Pintas
fonte
12

Também estou interessado nesta questão e gostaria de adicionar algumas experiências para entender melhor o CalibratedClassifierCV (CCCV).

Como já foi dito, existem duas maneiras de usá-lo.

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

Como alternativa, poderíamos tentar o segundo método, mas apenas calibrar os mesmos dados em que nos encaixamos.

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

Embora os documentos avisem para usar um conjunto separado, isso pode ser útil porque permite inspecionar my_clf(por exemplo, ver os coef_que não estão disponíveis no objeto CalibratedClassifierCV). (Alguém sabe como obter isso dos classificadores calibrados - para um, existem três deles, então você faria a média dos coeficientes?).

Decidi comparar esses três métodos em termos de calibração em um conjunto de testes completamente estendido.

Aqui está um conjunto de dados:

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

Joguei em algum desequilíbrio de classe e forneci apenas 500 amostras para tornar este um problema difícil.

Eu corro 100 tentativas, cada vez tentando cada método e plotando sua curva de calibração.

insira a descrição da imagem aqui

Boxplots do Brier pontuam em todos os ensaios:

insira a descrição da imagem aqui

Aumentando o número de amostras para 10.000:

insira a descrição da imagem aqui

insira a descrição da imagem aqui

Se mudarmos o classificador para Naive Bayes, voltando para 500 amostras:

insira a descrição da imagem aqui

insira a descrição da imagem aqui

Parece não haver amostras suficientes para calibrar. Aumentando as amostras para 10.000

insira a descrição da imagem aqui

insira a descrição da imagem aqui

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

Portanto, os resultados da pontuação de Brier são inconclusivos, mas de acordo com as curvas, parece melhor usar o segundo método.

user0
fonte