Introdução
Neste laboratório, aprenderemos a utilizar curvas de calibração para avaliar as probabilidades previstas de um modelo de classificação. Usaremos a biblioteca scikit-learn para realizar a classificação e visualizar os resultados.
Dicas da Máquina Virtual
Após o arranque da máquina virtual, clique no canto superior esquerdo para mudar para a aba Notebook e aceder ao Jupyter Notebook para a prática.
Por vezes, pode ser necessário aguardar alguns segundos para o Jupyter Notebook terminar o carregamento. A validação das operações não pode ser automatizada devido a limitações no Jupyter Notebook.
Se tiver problemas durante o aprendizado, não hesite em contactar o Labby. Forneça feedback após a sessão e resolveremos o problema rapidamente para si.
Conjunto de Dados
Utilizaremos um conjunto de dados sintético para classificação binária com 100.000 amostras e 20 características. Das 20 características, apenas 2 são informativas, 10 são redundantes (combinações aleatórias das características informativas) e as restantes 8 são não informativas (números aleatórios). Das 100.000 amostras, 1.000 serão utilizadas para o ajuste do modelo e o restante para testes.
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
X, y = make_classification(
n_samples=100_000, n_features=20, n_informative=2, n_redundant=10, random_state=42
)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.99, random_state=42
)
Curvas de Calibração
Vamos comparar vários classificadores com curvas de calibração. Primeiro, vamos comparar:
- Regressão Logística (utilizada como linha de base)
- Naive Bayes Gaussian não calibrado
- Naive Bayes Gaussian com calibração isotónica e sigmóide
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
from sklearn.calibration import CalibratedClassifierCV, CalibrationDisplay
from sklearn.linear_model import LogisticRegression
from sklearn.naive_bayes import GaussianNB
lr = LogisticRegression(C=1.0)
gnb = GaussianNB()
gnb_isotonic = CalibratedClassifierCV(gnb, cv=2, method="isotonic")
gnb_sigmoid = CalibratedClassifierCV(gnb, cv=2, method="sigmoid")
clf_list = [
(lr, "Regressão Logística"),
(gnb, "Naive Bayes"),
(gnb_isotonic, "Naive Bayes + Isotônico"),
(gnb_sigmoid, "Naive Bayes + Sigmóide"),
]
fig = plt.figure(figsize=(10, 10))
gs = GridSpec(4, 2)
colors = plt.get_cmap("Dark2")
ax_calibration_curve = fig.add_subplot(gs[:2, :2])
calibration_displays = {}
for i, (clf, name) in enumerate(clf_list):
clf.fit(X_train, y_train)
display = CalibrationDisplay.from_estimator(
clf,
X_test,
y_test,
n_bins=10,
name=name,
ax=ax_calibration_curve,
color=colors(i),
)
calibration_displays[name] = display
ax_calibration_curve.grid()
ax_calibration_curve.set_title("Gráficos de Calibração (Naive Bayes)")
## Adicionar histograma
grid_positions = [(2, 0), (2, 1), (3, 0), (3, 1)]
for i, (_, name) in enumerate(clf_list):
row, col = grid_positions[i]
ax = fig.add_subplot(gs[row, col])
ax.hist(
calibration_displays[name].y_prob,
range=(0, 1),
bins=10,
label=name,
color=colors(i),
)
ax.set(title=name, xlabel="Probabilidade média prevista", ylabel="Contagem")
plt.tight_layout()
plt.show()
Classificador de Vetores de Suporte Linear
Em seguida, vamos comparar:
- Regressão Logística (linha de base)
- Classificador de Vetores de Suporte Linear (SVC) não calibrado
- SVC Linear com calibração isotónica e sigmóide
import numpy as np
from sklearn.svm import LinearSVC
class NaivelyCalibratedLinearSVC(LinearSVC):
"""LinearSVC com método `predict_proba` que escala ingenuamente
a saída da função `decision_function` para classificação binária."""
def fit(self, X, y):
super().fit(X, y)
df = self.decision_function(X)
self.df_min_ = df.min()
self.df_max_ = df.max()
def predict_proba(self, X):
"""Escala min-max da saída da função `decision_function` para [0, 1]."""
df = self.decision_function(X)
calibrated_df = (df - self.df_min_) / (self.df_max_ - self.df_min_)
proba_pos_class = np.clip(calibrated_df, 0, 1)
proba_neg_class = 1 - proba_pos_class
proba = np.c_[proba_neg_class, proba_pos_class]
return proba
lr = LogisticRegression(C=1.0)
svc = NaivelyCalibratedLinearSVC(max_iter=10_000, dual="auto")
svc_isotonic = CalibratedClassifierCV(svc, cv=2, method="isotonic")
svc_sigmoid = CalibratedClassifierCV(svc, cv=2, method="sigmoid")
clf_list = [
(lr, "Regressão Logística"),
(svc, "SVC"),
(svc_isotonic, "SVC + Isotônico"),
(svc_sigmoid, "SVC + Sigmóide"),
]
fig = plt.figure(figsize=(10, 10))
gs = GridSpec(4, 2)
ax_calibration_curve = fig.add_subplot(gs[:2, :2])
calibration_displays = {}
for i, (clf, name) in enumerate(clf_list):
clf.fit(X_train, y_train)
display = CalibrationDisplay.from_estimator(
clf,
X_test,
y_test,
n_bins=10,
name=name,
ax=ax_calibration_curve,
color=colors(i),
)
calibration_displays[name] = display
ax_calibration_curve.grid()
ax_calibration_curve.set_title("Gráficos de Calibração (SVC)")
## Adicionar histograma
grid_positions = [(2, 0), (2, 1), (3, 0), (3, 1)]
for i, (_, name) in enumerate(clf_list):
row, col = grid_positions[i]
ax = fig.add_subplot(gs[row, col])
ax.hist(
calibration_displays[name].y_prob,
range=(0, 1),
bins=10,
label=name,
color=colors(i),
)
ax.set(title=name, xlabel="Probabilidade média prevista", ylabel="Contagem")
plt.tight_layout()
plt.show()
Avaliação
Avaliaremos os classificadores com várias métricas de classificação: brier_score_loss, log_loss, precisão, revocação, pontuação F1 e AUC ROC.
from collections import defaultdict
import pandas as pd
from sklearn.metrics import (
precision_score,
recall_score,
f1_score,
brier_score_loss,
log_loss,
roc_auc_score,
)
scores = defaultdict(list)
for i, (clf, name) in enumerate(clf_list):
clf.fit(X_train, y_train)
y_prob = clf.predict_proba(X_test)
y_pred = clf.predict(X_test)
scores["Classificador"].append(name)
for metric in [brier_score_loss, log_loss, roc_auc_score]:
nome_métrica = metric.__name__.replace("_", " ").replace("score", "").capitalize()
scores[nome_métrica].append(metric(y_test, y_prob[:, 1]))
for metric in [precision_score, recall_score, f1_score]:
nome_métrica = metric.__name__.replace("_", " ").replace("score", "").capitalize()
scores[nome_métrica].append(metric(y_test, y_pred))
score_df = pd.DataFrame(scores).set_index("Classificador")
score_df.round(decimals=3)
Resumo
Aprendemos como usar curvas de calibração para avaliar as probabilidades previstas de um modelo de classificação. Comparámos vários classificadores com curvas de calibração e avaliámo-los com várias métricas de classificação. Também aprendemos que a calibração paramétrica sigmóide pode lidar com situações em que a curva de calibração do classificador base é sigmóide, mas não quando é sigmóide transposta. A calibração isotónica não paramétrica pode lidar com ambas as situações, mas pode exigir mais dados para produzir bons resultados.