Skip to content

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 PositivePredicted Negative
Actual PositiveTrue Positive (TP)False Negative (FN)
Actual NegativeFalse 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 0Predicted 1
Actual 0TN = 5FP = 1
Actual 1FN = 2TP = 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:  7

Precision, Recall, F1-Score e Accuracy

Essas métricas são derivadas diretamente da matriz de confusão:

MetricFormulaWhat It Answers
Accuracy(TP + TN) / (TP + TN + FP + FN)De todas as previsões, quantas estavam corretas?
PrecisionTP / (TP + FP)Dos positivos previstos, quantos eram realmente positivos?
Recall (Sensitivity)TP / (TP + FN)Dos positivos reais, quantos capturamos?
SpecificityTN / (TN + FP)Dos negativos reais, quantos identificamos corretamente?
F1-Score2 * (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.8235

Quando Priorizar Precision vs Recall

ScenarioPrioritizeWhy
Detecção de spamPrecisionFalsos positivos (e-mail legítimo marcado como spam) irritam usuários
Triagem de doençasRecallFalsos negativos (doença não detectada) são perigosos
Detecção de fraudeRecallPerder fraude é mais custoso do que investigar alarmes falsos
Resultados de mecanismo de buscaPrecisionResultados irrelevantes degradam a experiência do usuário
Detecção de defeitos na manufaturaRecallProdutos defeituosos chegando aos clientes é custoso
Recomendação de conteúdoPrecisionRecomendaçõ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:

ValueNormalizationUse 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}")
AverageMethodBest For
microTP, FP, FN totais somados em todas as classesQuando desbalanceamento de classes importa
macroMédia não ponderada por classeQuando todas as classes são igualmente importantes
weightedMédia ponderada pelo support de cada classeEscolha 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.

📚