Calibração de Probabilidade para Classificação de 3 Classes

Beginner

This tutorial is from open-source community. Access the source code

Introdução

Este laboratório demonstra como usar a calibração sigmóide para classificação em 3 classes em Python usando o scikit-learn. Ele mostra como a calibração sigmóide altera as probabilidades previstas e como pode ser usada para melhorar o desempenho do modelo.

Dicas da Máquina Virtual

Após o arranque da VM, clique no canto superior esquerdo para mudar para a aba Notebook para aceder ao Jupyter Notebook para praticar.

Por vezes, pode ser necessário esperar alguns segundos para o Jupyter Notebook terminar de carregar. A validação das operações não pode ser automatizada devido a limitações no Jupyter Notebook.

Se tiver problemas durante a aprendizagem, não hesite em contactar o Labby. Forneça feedback após a sessão e resolveremos o problema rapidamente para si.

Dados

Geramos um conjunto de dados de classificação com 2000 amostras, 2 características e 3 classes de destino. Em seguida, dividimos os dados da seguinte forma:

  • treino: 600 amostras (para treinar o classificador)
  • validação: 400 amostras (para calibrar as probabilidades previstas)
  • teste: 1000 amostras
import numpy as np
from sklearn.datasets import make_blobs

np.random.seed(0)

X, y = make_blobs(
    n_samples=2000, n_features=2, centers=3, random_state=42, cluster_std=5.0
)
X_train, y_train = X[:600], y[:600]
X_valid, y_valid = X[600:1000], y[600:1000]
X_train_valid, y_train_valid = X[:1000], y[:1000]
X_test, y_test = X[1000:], y[1000:]

Ajustamento e Calibração

Treinamos um classificador de floresta aleatória com 25 estimadores base (árvores) nos dados de treino e validação concatenados (1000 amostras). Este é o classificador não calibrado.

from sklearn.ensemble import RandomForestClassifier

clf = RandomForestClassifier(n_estimators=25)
clf.fit(X_train_valid, y_train_valid)

Para treinar o classificador calibrado, começamos com o mesmo classificador de floresta aleatória, mas treinamo-lo apenas com o subconjunto de dados de treino (600 amostras), e em seguida calibramos, com method='sigmoid', utilizando o subconjunto de dados de validação (400 amostras) num processo em duas etapas.

from sklearn.calibration import CalibratedClassifierCV

clf = RandomForestClassifier(n_estimators=25)
clf.fit(X_train, y_train)
cal_clf = CalibratedClassifierCV(clf, method="sigmoid", cv="prefit")
cal_clf.fit(X_valid, y_valid)

Comparar Probabilidades

Plotamos um 2-simplex com setas mostrando a mudança nas probabilidades previstas das amostras de teste.

import matplotlib.pyplot as plt

plt.figure(figsize=(10, 10))
colors = ["r", "g", "b"]

clf_probs = clf.predict_proba(X_test)
cal_clf_probs = cal_clf.predict_proba(X_test)
## Plotar setas
for i in range(clf_probs.shape[0]):
    plt.arrow(
        clf_probs[i, 0],
        clf_probs[i, 1],
        cal_clf_probs[i, 0] - clf_probs[i, 0],
        cal_clf_probs[i, 1] - clf_probs[i, 1],
        color=colors[y_test[i]],
        head_width=1e-2,
    )

## Plotar previsões perfeitas, em cada vértice
plt.plot([1.0], [0.0], "ro", ms=20, label="Classe 1")
plt.plot([0.0], [1.0], "go", ms=20, label="Classe 2")
plt.plot([0.0], [0.0], "bo", ms=20, label="Classe 3")

## Plotar limites do simplex unitário
plt.plot([0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], "k", label="Simplex")

## Anotar pontos 6 pontos em torno do simplex e ponto médio dentro do simplex
plt.annotate(
    r"($\frac{1}{3}$, $\frac{1}{3}$, $\frac{1}{3}$)",
    xy=(1.0 / 3, 1.0 / 3),
    xytext=(1.0 / 3, 0.23),
    xycoords="data",
    arrowprops=dict(facecolor="black", shrink=0.05),
    horizontalalignment="center",
    verticalalignment="center",
)
plt.plot([1.0 / 3], [1.0 / 3], "ko", ms=5)
plt.annotate(
    r"($\frac{1}{2}$, $0$, $\frac{1}{2}$)",
    xy=(0.5, 0.0),
    xytext=(0.5, 0.1),
    xycoords="data",
    arrowprops=dict(facecolor="black", shrink=0.05),
    horizontalalignment="center",
    verticalalignment="center",
)
plt.annotate(
    r"($0$, $\frac{1}{2}$, $\frac{1}{2}$)",
    xy=(0.0, 0.5),
    xytext=(0.1, 0.5),
    xycoords="data",
    arrowprops=dict(facecolor="black", shrink=0.05),
    horizontalalignment="center",
    verticalalignment="center",
)
plt.annotate(
    r"($\frac{1}{2}$, $\frac{1}{2}$, $0$)",
    xy=(0.5, 0.5),
    xytext=(0.6, 0.6),
    xycoords="data",
    arrowprops=dict(facecolor="black", shrink=0.05),
    horizontalalignment="center",
    verticalalignment="center",
)
plt.annotate(
    r"($0$, $0$, $1$)",
    xy=(0, 0),
    xytext=(0.1, 0.1),
    xycoords="data",
    arrowprops=dict(facecolor="black", shrink=0.05),
    horizontalalignment="center",
    verticalalignment="center",
)
plt.annotate(
    r"($1$, $0$, $0$)",
    xy=(1, 0),
    xytext=(1, 0.1),
    xycoords="data",
    arrowprops=dict(facecolor="black", shrink=0.05),
    horizontalalignment="center",
    verticalalignment="center",
)
plt.annotate(
    r"($0$, $1$, $0$)",
    xy=(0, 1),
    xytext=(0.1, 1),
    xycoords="data",
    arrowprops=dict(facecolor="black", shrink=0.05),
    horizontalalignment="center",
    verticalalignment="center",
)
## Adicionar grade
plt.grid(False)
for x in [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]:
    plt.plot([0, x], [x, 0], "k", alpha=0.2)
    plt.plot([0, 0 + (1 - x) / 2], [x, x + (1 - x) / 2], "k", alpha=0.2)
    plt.plot([x, x + (1 - x) / 2], [0, 0 + (1 - x) / 2], "k", alpha=0.2)

plt.title("Mudança nas probabilidades previstas nas amostras de teste após calibração sigmoid")
plt.xlabel("Probabilidade da classe 1")
plt.ylabel("Probabilidade da classe 2")
plt.xlim(-0.05, 1.05)
plt.ylim(-0.05, 1.05)
_ = plt.legend(loc="best")

Comparação de Log-loss

Comparamos a perda de log do classificador não calibrado e calibrado nas previsões das 1000 amostras de teste.

from sklearn.metrics import log_loss

score = log_loss(y_test, clf_probs)
cal_score = log_loss(y_test, cal_clf_probs)

print("Log-loss de")
print(f" * classificador não calibrado: {score:.3f}")
print(f" * classificador calibrado: {cal_score:.3f}")

Gerar Grade e Plotar

Geramos uma grade de possíveis probabilidades não calibradas sobre o 2-simplex, calculamos as probabilidades calibradas correspondentes e plotamos setas para cada uma. As setas são coloridas de acordo com a probabilidade não calibrada mais alta. Isso ilustra o mapa de calibração aprendido:

plt.figure(figsize=(10, 10))
## Gerar grade de valores de probabilidade
p1d = np.linspace(0, 1, 20)
p0, p1 = np.meshgrid(p1d, p1d)
p2 = 1 - p0 - p1
p = np.c_[p0.ravel(), p1.ravel(), p2.ravel()]
p = p[p[:, 2] >= 0]

## Usar os calibradores de classe para calcular as probabilidades calibradas
calibrated_classifier = cal_clf.calibrated_classifiers_[0]
prediction = np.vstack(
    [
        calibrator.predict(this_p)
        for calibrator, this_p in zip(calibrated_classifier.calibrators, p.T)
    ]
).T

## Re-normalizar as previsões calibradas para garantir que permaneçam dentro do
## simplex. Esta mesma etapa de renormalização é executada internamente pelo
## método predict de CalibratedClassifierCV em problemas multiclasse.
prediction /= prediction.sum(axis=1)[:, None]

## Plotar as mudanças nas probabilidades previstas induzidas pelos calibradores
for i in range(prediction.shape[0]):
    plt.arrow(
        p[i, 0],
        p[i, 1],
        prediction[i, 0] - p[i, 0],
        prediction[i, 1] - p[i, 1],
        head_width=1e-2,
        color=colors[np.argmax(p[i])],
    )

## Plotar os limites do simplex unitário
plt.plot([0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], "k", label="Simplex")

plt.grid(False)
for x in [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]:
    plt.plot([0, x], [x, 0], "k", alpha=0.2)
    plt.plot([0, 0 + (1 - x) / 2], [x, x + (1 - x) / 2], "k", alpha=0.2)
    plt.plot([x, x + (1 - x) / 2], [0, 0 + (1 - x) / 2], "k", alpha=0.2)

plt.title("Mapa de calibração sigmoid aprendido")
plt.xlabel("Probabilidade da classe 1")
plt.ylabel("Probabilidade da classe 2")
plt.xlim(-0.05, 1.05)
plt.ylim(-0.05, 1.05)

plt.show()

Resumo

Este laboratório demonstrou como usar a calibração sigmóide para classificação em 3 classes em Python usando o scikit-learn. Ele demonstrou o impacto da calibração nas probabilidades previstas e como ela pode ser usada para melhorar o desempenho do modelo.