Matriz de Confusão no Sklearn: Como Avaliar Modelos de Classificação
Updated on
Seu modelo de classificação reporta 95% de accuracy, então você o coloca em produção. Aí você descobre que ele deixa passar 80% dos casos positivos que realmente importam — transações fraudulentas, diagnósticos de doenças, produtos defeituosos. Só a accuracy esconde informações críticas sobre onde e como um modelo falha.
Um único número de accuracy comprime todos os tipos de erro em uma única métrica. Um filtro de spam que deixa passar todos os e-mails de spam e classifica corretamente todos os e-mails legítimos ainda assim pode alcançar alta accuracy se spam for apenas 5% do total. Você precisa enxergar o quadro completo: quantos positivos o modelo captura, quantos negativos ele classifica incorretamente e exatamente onde os erros acontecem.
A matriz de confusão decompõe o desempenho do modelo em seus quatro componentes — verdadeiros positivos, verdadeiros negativos, falsos positivos e falsos negativos. Combinada com métricas derivadas como precision, recall e F1-score, ela oferece insights acionáveis sobre o que seu modelo acerta e erra. O Scikit-learn fornece confusion_matrix, classification_report e ConfusionMatrixDisplay para tornar essa análise direta.
O que é uma Matriz de Confusão?
Uma matriz de confusão é uma tabela que compara os rótulos previstos com os rótulos reais de um modelo de classificação. Para classificação binária, ela é uma grade 2x2:
| Predicted Positive | Predicted Negative | |
|---|---|---|
| Actual Positive | True Positive (TP) | False Negative (FN) |
| Actual Negative | False Positive (FP) | True Negative (TN) |
Cada célula conta o número de amostras que se encaixam naquela categoria:
- True Positive (TP): o modelo previu positivo, e era realmente positivo. Correto.
- True Negative (TN): o modelo previu negativo, e era realmente negativo. Correto.
- False Positive (FP): o modelo previu positivo, mas era realmente negativo. Erro do tipo I.
- False Negative (FN): o modelo previu negativo, mas era realmente positivo. Erro do tipo II.
Matriz de Confusão Básica com Sklearn
from sklearn.metrics import confusion_matrix
import numpy as np
# Actual and predicted labels
y_true = [1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1]
y_pred = [1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1]
cm = confusion_matrix(y_true, y_pred)
print(cm)
# [[5 1]
# [2 7]]Como ler esse output: o sklearn organiza a matriz com linha 0 = actual negative, linha 1 = actual positive.
| Predicted 0 | Predicted 1 | |
|---|---|---|
| Actual 0 | TN = 5 | FP = 1 |
| Actual 1 | FN = 2 | TP = 7 |
Então, o modelo identificou corretamente 5 negativos e 7 positivos, enquanto cometeu 1 erro de falso positivo e 2 erros de falso negativo.
Extraindo Valores Individuais
from sklearn.metrics import confusion_matrix
y_true = [1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1]
y_pred = [1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1]
cm = confusion_matrix(y_true, y_pred)
tn, fp, fn, tp = cm.ravel()
print(f"True Negatives: {tn}")
print(f"False Positives: {fp}")
print(f"False Negatives: {fn}")
print(f"True Positives: {tp}")
# True Negatives: 5
# False Positives: 1
# False Negatives: 2
# True Positives: 7Precision, Recall, F1-Score e Accuracy
Essas métricas são derivadas diretamente da matriz de confusão:
| Metric | Formula | What It Answers |
|---|---|---|
| Accuracy | (TP + TN) / (TP + TN + FP + FN) | De todas as previsões, quantas estavam corretas? |
| Precision | TP / (TP + FP) | Dos positivos previstos, quantos eram realmente positivos? |
| Recall (Sensitivity) | TP / (TP + FN) | Dos positivos reais, quantos capturamos? |
| Specificity | TN / (TN + FP) | Dos negativos reais, quantos identificamos corretamente? |
| F1-Score | 2 * (Precision * Recall) / (Precision + Recall) | Média harmônica de precision e recall |
from sklearn.metrics import (
accuracy_score, precision_score, recall_score, f1_score
)
y_true = [1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1]
y_pred = [1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1]
print(f"Accuracy: {accuracy_score(y_true, y_pred):.4f}")
print(f"Precision: {precision_score(y_true, y_pred):.4f}")
print(f"Recall: {recall_score(y_true, y_pred):.4f}")
print(f"F1-Score: {f1_score(y_true, y_pred):.4f}")
# Accuracy: 0.8000
# Precision: 0.8750
# Recall: 0.7778
# F1-Score: 0.8235Quando Priorizar Precision vs Recall
| Scenario | Prioritize | Why |
|---|---|---|
| Detecção de spam | Precision | Falsos positivos (e-mail legítimo marcado como spam) irritam usuários |
| Triagem de doenças | Recall | Falsos negativos (doença não detectada) são perigosos |
| Detecção de fraude | Recall | Perder fraude é mais custoso do que investigar alarmes falsos |
| Resultados de mecanismo de busca | Precision | Resultados irrelevantes degradam a experiência do usuário |
| Detecção de defeitos na manufatura | Recall | Produtos defeituosos chegando aos clientes é custoso |
| Recomendação de conteúdo | Precision | Recomendações irrelevantes reduzem engajamento |
Classification Report
O classification_report do sklearn calcula precision, recall, F1-score e support (número de ocorrências reais) para cada classe em uma única chamada:
from sklearn.metrics import classification_report
y_true = [1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1]
y_pred = [1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1]
print(classification_report(y_true, y_pred, target_names=['Negative', 'Positive']))Output:
precision recall f1-score support
Negative 0.71 0.83 0.77 6
Positive 0.88 0.78 0.82 9
accuracy 0.80 15
macro avg 0.80 0.81 0.80 15
weighted avg 0.81 0.80 0.80 15- macro avg: média não ponderada entre as classes. Trata todas as classes igualmente.
- weighted avg: média ponderada pelo support de cada classe. Leva em conta desbalanceamento.
- support: número de amostras reais em cada classe.
Visualizando a Matriz de Confusão
Usando ConfusionMatrixDisplay
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_breast_cancer
import matplotlib.pyplot as plt
# Load and split data
data = load_breast_cancer()
X_train, X_test, y_train, y_test = train_test_split(
data.data, data.target, test_size=0.2, random_state=42
)
# Train model
model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
# Plot confusion matrix
cm = confusion_matrix(y_test, y_pred)
disp = ConfusionMatrixDisplay(
confusion_matrix=cm,
display_labels=data.target_names
)
disp.plot(cmap='Blues')
plt.title('Breast Cancer Classification')
plt.tight_layout()
plt.savefig('confusion_matrix.png', dpi=150)
plt.show()Usando Seaborn Heatmap
Para mais customização, use seaborn diretamente:
from sklearn.metrics import confusion_matrix
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_breast_cancer
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
# Load, split, train
data = load_breast_cancer()
X_train, X_test, y_train, y_test = train_test_split(
data.data, data.target, test_size=0.2, random_state=42
)
model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
# Create confusion matrix
cm = confusion_matrix(y_test, y_pred)
# Plot with seaborn
plt.figure(figsize=(8, 6))
sns.heatmap(
cm,
annot=True,
fmt='d',
cmap='Blues',
xticklabels=data.target_names,
yticklabels=data.target_names,
square=True,
linewidths=0.5
)
plt.xlabel('Predicted Label')
plt.ylabel('True Label')
plt.title('Confusion Matrix - Breast Cancer Classification')
plt.tight_layout()
plt.savefig('confusion_matrix_seaborn.png', dpi=150)
plt.show()Matriz de Confusão Normalizada
Contagens brutas podem ser enganosas quando as classes têm tamanhos diferentes. Normalizar mostra proporções em vez disso:
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_breast_cancer
import matplotlib.pyplot as plt
data = load_breast_cancer()
X_train, X_test, y_train, y_test = train_test_split(
data.data, data.target, test_size=0.2, random_state=42
)
model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
# Normalized confusion matrix (by true labels)
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# Raw counts
ConfusionMatrixDisplay.from_predictions(
y_test, y_pred,
display_labels=data.target_names,
cmap='Blues',
ax=axes[0]
)
axes[0].set_title('Raw Counts')
# Normalized (rows sum to 1)
ConfusionMatrixDisplay.from_predictions(
y_test, y_pred,
display_labels=data.target_names,
normalize='true',
cmap='Blues',
values_format='.2%',
ax=axes[1]
)
axes[1].set_title('Normalized by True Label')
plt.tight_layout()
plt.savefig('confusion_matrix_normalized.png', dpi=150)
plt.show()O parâmetro normalize aceita três opções:
| Value | Normalization | Use Case |
|---|---|---|
'true' | Linhas somam 1 (divide pela contagem da classe real) | Ver recall por classe |
'pred' | Colunas somam 1 (divide pela contagem da classe prevista) | Ver precision por classe |
'all' | Todas as células somam 1 (divide pelo total) | Ver distribuição geral |
Matriz de Confusão Multi-Classe
A matriz de confusão se estende naturalmente para mais de duas classes. Cada linha representa uma classe real, e cada coluna representa uma classe prevista:
from sklearn.metrics import confusion_matrix, classification_report, ConfusionMatrixDisplay
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt
# Load iris dataset (3 classes)
iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(
iris.data, iris.target, test_size=0.3, random_state=42
)
# Train and predict
model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
# Confusion matrix
cm = confusion_matrix(y_test, y_pred)
print("Confusion Matrix:")
print(cm)
# Classification report
print("\nClassification Report:")
print(classification_report(
y_test, y_pred,
target_names=iris.target_names
))
# Visualize
disp = ConfusionMatrixDisplay(
confusion_matrix=cm,
display_labels=iris.target_names
)
disp.plot(cmap='Blues')
plt.title('Iris Classification - 3 Classes')
plt.tight_layout()
plt.savefig('multi_class_confusion_matrix.png', dpi=150)
plt.show()Estratégias de Averaging em Multi-Classe
Ao calcular precision, recall e F1 para problemas multi-classe, você precisa escolher um método de averaging:
from sklearn.metrics import precision_score, recall_score, f1_score
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris
iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(
iris.data, iris.target, test_size=0.3, random_state=42
)
model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
for avg in ['micro', 'macro', 'weighted']:
p = precision_score(y_test, y_pred, average=avg)
r = recall_score(y_test, y_pred, average=avg)
f1 = f1_score(y_test, y_pred, average=avg)
print(f"{avg:8s} -- Precision: {p:.4f}, Recall: {r:.4f}, F1: {f1:.4f}")| Average | Method | Best For |
|---|---|---|
micro | TP, FP, FN totais somados em todas as classes | Quando desbalanceamento de classes importa |
macro | Média não ponderada por classe | Quando todas as classes são igualmente importantes |
weighted | Média ponderada pelo support de cada classe | Escolha padrão para datasets desbalanceados |
Exemplo Completo: Avaliação End-to-End de Classificação
from sklearn.metrics import (
confusion_matrix, classification_report, ConfusionMatrixDisplay,
accuracy_score, precision_score, recall_score, f1_score, roc_auc_score
)
from sklearn.model_selection import train_test_split
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.datasets import load_breast_cancer
import matplotlib.pyplot as plt
import numpy as np
# Load data
data = load_breast_cancer()
X_train, X_test, y_train, y_test = train_test_split(
data.data, data.target, test_size=0.2, random_state=42, stratify=data.target
)
# Build pipeline
pipeline = Pipeline([
('scaler', StandardScaler()),
('model', GradientBoostingClassifier(
n_estimators=200, max_depth=3, random_state=42
))
])
# Train
pipeline.fit(X_train, y_train)
y_pred = pipeline.predict(X_test)
y_prob = pipeline.predict_proba(X_test)[:, 1]
# Confusion matrix
cm = confusion_matrix(y_test, y_pred)
tn, fp, fn, tp = cm.ravel()
print("=" * 50)
print("MODEL EVALUATION REPORT")
print("=" * 50)
print(f"\nConfusion Matrix:")
print(f" TP={tp}, FP={fp}")
print(f" FN={fn}, TN={tn}")
print(f"\nAccuracy: {accuracy_score(y_test, y_pred):.4f}")
print(f"Precision: {precision_score(y_test, y_pred):.4f}")
print(f"Recall: {recall_score(y_test, y_pred):.4f}")
print(f"F1-Score: {f1_score(y_test, y_pred):.4f}")
print(f"ROC-AUC: {roc_auc_score(y_test, y_prob):.4f}")
print(f"\nDetailed Report:")
print(classification_report(y_test, y_pred, target_names=data.target_names))
# Visualize
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
ConfusionMatrixDisplay.from_predictions(
y_test, y_pred, display_labels=data.target_names,
cmap='Blues', ax=axes[0]
)
axes[0].set_title('Raw Counts')
ConfusionMatrixDisplay.from_predictions(
y_test, y_pred, display_labels=data.target_names,
normalize='true', values_format='.1%', cmap='Blues', ax=axes[1]
)
axes[1].set_title('Normalized')
plt.tight_layout()
plt.savefig('full_evaluation.png', dpi=150)
plt.show()Explorando Resultados de Classificação com PyGWalker
Depois de montar sua matriz de confusão, vá além das métricas e investigue os erros explorando os dados brutos de forma interativa. PyGWalker (opens in a new tab) transforma seus resultados de predição em uma interface de visual analytics com drag-and-drop no Jupyter:
import pandas as pd
import pygwalker as pyg
# Build results DataFrame with features and predictions
results = pd.DataFrame(X_test, columns=data.feature_names)
results['actual'] = y_test
results['predicted'] = y_pred
results['correct'] = y_test == y_pred
results['confidence'] = y_prob
# Launch interactive exploration
walker = pyg.walk(results)Filtre as amostras classificadas incorretamente, compare distribuições de features entre grupos TP/FP/FN/TN e identifique padrões que expliquem onde o modelo tem dificuldade.
Para iterar em experimentos de classificação no Jupyter — ajustando thresholds, testando modelos diferentes ou explorando combinações de features — o RunCell (opens in a new tab) oferece um AI agent que acelera o loop de experimentação.
FAQ
O que é uma matriz de confusão no sklearn?
Uma matriz de confusão é uma tabela que mostra as contagens de previsões corretas e incorretas para cada classe. No sklearn, confusion_matrix(y_true, y_pred) retorna um array numpy 2D em que as linhas representam as classes reais e as colunas representam as classes previstas. Para classificação binária, ela mostra true positives, true negatives, false positives e false negatives.
Como ler uma matriz de confusão?
Na matriz de confusão do sklearn, as linhas são os rótulos reais e as colunas são os rótulos previstos. Para classificação binária: o canto superior esquerdo é true negatives (TN), o superior direito é false positives (FP), o inferior esquerdo é false negatives (FN) e o inferior direito é true positives (TP). Os elementos na diagonal são previsões corretas.
Qual é a diferença entre precision e recall?
Precision mede quantos dos positivos previstos são realmente positivos (TP / (TP + FP)). Recall mede quantos dos positivos reais o modelo capturou (TP / (TP + FN)). Precision responde “quando o modelo diz positivo, com que frequência ele está certo?”, enquanto recall responde “de todos os positivos reais, quantos o modelo encontrou?”.
Quando devo usar F1-score em vez de accuracy?
Use F1-score quando suas classes são desbalanceadas. Se 95% das amostras são negativas, um modelo que sempre prevê negativo obtém 95% de accuracy, mas 0% de recall nos positivos. O F1-score é a média harmônica de precision e recall, então ele penaliza modelos que sacrificam uma métrica em favor da outra.
Como plotar uma matriz de confusão em Python?
Use ConfusionMatrixDisplay.from_predictions(y_true, y_pred) como o método mais rápido. Para mais customização, calcule a matriz com confusion_matrix() e plote com seaborn.heatmap(). Ambas as abordagens suportam matrizes normalizadas, mapas de cores personalizados e rótulos de classes.
O que normalize='true' faz no ConfusionMatrixDisplay?
Definir normalize='true' divide cada linha pelo total de amostras reais daquela classe, então cada linha soma 1. Isso mostra o recall por classe como porcentagem. Use normalize='pred' para ver precision por classe, ou normalize='all' para ver a proporção geral.
Conclusão
A matriz de confusão é a base da avaliação de modelos de classificação. A accuracy, sozinha, é insuficiente — você precisa ver os tipos específicos de erros que seu modelo comete. Use confusion_matrix e classification_report do sklearn para obter a visão completa, visualize com ConfusionMatrixDisplay ou heatmaps do seaborn para apresentações e relatórios, e normalize quando os tamanhos das classes diferirem. Escolha sua métrica principal com base no custo de negócio de cada tipo de erro: precision quando falsos positivos são caros, recall quando falsos negativos são perigosos e F1-score quando você precisa de uma medida balanceada.