Curva AUC - ROC
Aprenda a calcular e plotar curvas AUC-ROC em Python com sklearn. Entenda TPR, FPR, limiares e quando usar AUC em vez de acurácia.
A curva AUC-ROC (Área Sob a Curva Característica de Operação do Receptor) é uma das métricas mais importantes para avaliar modelos de classificação binária. Ela captura a capacidade do modelo de separar exemplos positivos e negativos em cada limiar de decisão possível, fornecendo uma visão muito mais rica do que uma única pontuação de acurácia.
Este capítulo aborda:
- O que é a curva ROC e como ela é construída
- Como interpretar TPR, FPR e os termos da matriz de confusão por trás deles
- Como calcular a AUC-ROC com o
scikit-learndo Python - Como plotar a curva ROC com o
matplotlib - Quando a AUC-ROC é a métrica correta e quando não é
Termos Principais: TP, FP, TN, FN
Antes de mergulhar na curva em si, é importante entender os quatro resultados que um classificador binário pode produzir. Para qualquer previsão, o rótulo real é positivo (P) ou negativo (N), e o rótulo previsto é positivo ou negativo:
| Previsto Positivo | Previsto Negativo | |
|---|---|---|
| Real Positivo | Verdadeiro Positivo (TP) | Falso Negativo (FN) |
| Real Negativo | Falso Positivo (FP) | Verdadeiro Negativo (TN) |
Essas quatro células são os blocos fundamentais de cada métrica de avaliação abordada neste capítulo. Para uma introdução mais aprofundada, consulte o capítulo Matriz de Confusão.
O que é a Curva ROC?
Um classificador geralmente produz uma pontuação de probabilidade (um número entre 0 e 1) em vez de um rótulo direto. Você escolhe um limiar — digamos, 0.5 — e tudo acima do limiar é previsto como positivo.
Alterar o limiar muda o equilíbrio entre capturar verdadeiros positivos e sinalizar incorretamente verdadeiros negativos:
- Um limiar muito baixo captura mais positivos (alto recall), mas também produz mais falsos alarmes.
- Um limiar muito alto é seletivo (alta precisão), mas perde positivos reais.
A curva ROC representa essa troca para cada limiar possível de uma só vez:
- Eixo X — Taxa de Falsos Positivos (FPR): que fração dos negativos reais são sinalizados incorretamente como positivos.
- Eixo Y — Taxa de Verdadeiros Positivos (TPR), também chamada de Recall ou Sensibilidade: que fração dos positivos reais são identificados corretamente.
TPR = TP / (TP + FN) e FPR = FP / (FP + TN)
Cada ponto na curva ROC é uma configuração de limiar. Juntos, eles traçam um caminho de (0, 0) — o limiar mais seletivo, sem prever nada como positivo — a (1, 1) — o limiar menos seletivo, prevendo tudo como positivo.
O que é AUC?
A AUC (Área Sob a Curva) resume toda a curva ROC em um único número entre 0 e 1:
| AUC | Significado |
|---|---|
| 1.0 | Classificador perfeito — separa cada positivo de cada negativo |
| 0.5 | Sem habilidade — equivalente a adivinhação aleatória |
| < 0.5 | Pior que aleatório (suas probabilidades previstas estão invertidas) |
| 0.7 – 0.8 | Aceitável para muitos problemas práticos |
| 0.8 – 0.9 | Bom |
| > 0.9 | Excelente |
Intuitivamente, a AUC é a probabilidade de o modelo classificar um exemplo positivo escolhido aleatoriamente acima de um exemplo negativo escolhido aleatoriamente.
Calculando AUC-ROC com scikit-learn
O módulo sklearn.metrics fornece roc_curve() para obter os valores de TPR/FPR por limiar e roc_auc_score() para obter o número único da AUC.
Exemplo mínimo funcional
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score, roc_curve
# 1. Create a toy binary-classification dataset
X, y = make_classification(n_samples=500, n_features=10, random_state=42)
# 2. Split into train / test sets
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=42
)
# 3. Train a logistic regression model
model = LogisticRegression(random_state=42)
model.fit(X_train, y_train)
# 4. Get probability scores for the positive class (column 1)
y_proba = model.predict_proba(X_test)[:, 1]
# 5. Compute AUC
auc = roc_auc_score(y_test, y_proba)
print(f"AUC-ROC: {auc:.3f}")
# 6. Get the (fpr, tpr, thresholds) arrays for plotting
fpr, tpr, thresholds = roc_curve(y_test, y_proba)
print(f"Number of threshold points: {len(thresholds)}")A saída será próxima de:
AUC-ROC: 0.944
Number of threshold points: 30O valor exato varia ligeiramente dependendo da sua versão do scikit-learn, mas deve estar no intervalo de 0.90–0.97 para este conjunto de dados.
Plotando a curva ROC
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score, roc_curve
X, y = make_classification(n_samples=500, n_features=10, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
model = LogisticRegression(random_state=42)
model.fit(X_train, y_train)
y_proba = model.predict_proba(X_test)[:, 1]
auc = roc_auc_score(y_test, y_proba)
fpr, tpr, _ = roc_curve(y_test, y_proba)
plt.figure(figsize=(7, 5))
plt.plot(fpr, tpr, color="steelblue", lw=2, label=f"ROC curve (AUC = {auc:.2f})")
plt.plot([0, 1], [0, 1], color="gray", linestyle="--", label="Random baseline (AUC = 0.50)")
plt.xlabel("False Positive Rate (FPR)")
plt.ylabel("True Positive Rate (TPR)")
plt.title("ROC Curve")
plt.legend(loc="lower right")
plt.tight_layout()
plt.savefig("roc_curve.png", dpi=120)
plt.show()A linha diagonal tracejada representa um classificador aleatório. Quanto mais a curva ROC se curva em direção ao canto superior esquerdo, melhor o modelo.
Comparando Múltiplos Modelos
Um fluxo de trabalho comum é treinar vários modelos e comparar suas curvas ROC no mesmo gráfico:
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score, roc_curve
import matplotlib.pyplot as plt
X, y = make_classification(n_samples=500, n_features=10, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
models = {
"Logistic Regression": LogisticRegression(random_state=42),
"Decision Tree": DecisionTreeClassifier(max_depth=3, random_state=42),
}
plt.figure(figsize=(7, 5))
for name, clf in models.items():
clf.fit(X_train, y_train)
y_proba = clf.predict_proba(X_test)[:, 1]
fpr, tpr, _ = roc_curve(y_test, y_proba)
auc = roc_auc_score(y_test, y_proba)
plt.plot(fpr, tpr, lw=2, label=f"{name} (AUC = {auc:.2f})")
plt.plot([0, 1], [0, 1], "k--", label="Random (AUC = 0.50)")
plt.xlabel("False Positive Rate")
plt.ylabel("True Positive Rate")
plt.title("ROC Curve Comparison")
plt.legend(loc="lower right")
plt.tight_layout()
plt.savefig("roc_comparison.png", dpi=120)
plt.show()O modelo com a maior área sob sua curva geralmente é a melhor escolha para este conjunto de dados. Para um fluxo de seleção de modelos rigoroso, combine isso com validação cruzada.
Escolhendo um Ponto de Operação (Limiar)
A AUC resume todos os limiares, mas eventualmente você precisará escolher um limiar para produção. Duas estratégias comuns:
1. Estatística J de Youden
O J de Youden maximiza TPR − FPR, encontrando o único ponto na curva ROC mais distante da diagonal:
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_curve
import numpy as np
X, y = make_classification(n_samples=500, n_features=10, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
model = LogisticRegression(random_state=42)
model.fit(X_train, y_train)
y_proba = model.predict_proba(X_test)[:, 1]
fpr, tpr, thresholds = roc_curve(y_test, y_proba)
j_scores = tpr - fpr
best_idx = int(np.argmax(j_scores))
best_threshold = thresholds[best_idx]
print(f"Best threshold (Youden's J): {best_threshold:.3f}")
print(f"TPR at best threshold: {tpr[best_idx]:.3f}")
print(f"FPR at best threshold: {fpr[best_idx]:.3f}")2. Limiar orientado pelo negócio
Na detecção de fraudes, você pode tolerar mais falsos positivos para capturar mais fraudes (limiar mais baixo). Na triagem médica, você pode querer muito poucos falsos negativos (também limiar mais baixo). Escolher o limiar é, em última análise, uma decisão de negócio informada pelo custo de cada tipo de erro.
AUC-ROC vs. Outras Métricas
| Métrica | Melhor quando... | Atenção... |
|---|---|---|
| Acurácia | As classes estão equilibradas | Enganosa em conjuntos de dados desbalanceados |
| Precisão / Recall | Você se preocupa principalmente com uma classe | Requer um limiar |
| F1-Score | Equilíbrio harmônico entre precisão e recall | Ainda dependente de limiar |
| AUC-ROC | Comparando modelos ou ajustando limiares | Não significativa para multi-classe sem extensão |
| AUC-PR (AUC Precisão-Recall) | Forte desequilíbrio de classes (positivos raros) | Menos intuitiva que a ROC |
Para dados severamente desbalanceados — por exemplo, 1% de positivos — a curva AUC-PR (precisão vs. recall) é frequentemente mais informativa do que a AUC-ROC, porque a FPR pode parecer pequena mesmo quando muitos negativos são sinalizados incorretamente.
Armadilhas Comuns
Passar rótulos diretos em vez de probabilidades. O roc_auc_score precisa de pontuações de probabilidade, não de rótulos 0/1. Use model.predict_proba(X_test)[:, 1], não model.predict(X_test).
Esquecer de especificar a classe positiva. Por padrão, roc_curve e roc_auc_score tratam o rótulo inteiro maior como positivo. Passe pos_label=1 explicitamente quando seus rótulos não forem inteiros 0/1.
Sobreajuste à AUC no conjunto de treinamento. Sempre avalie em um conjunto de teste separado ou use validação cruzada para obter uma estimativa confiável de AUC. O capítulo Grid Search mostra como passar scoring='roc_auc' diretamente para o GridSearchCV.
AUC ≈ 0.5 nem sempre significa que o modelo é ruim. Pode significar que os exemplos positivos e negativos genuinamente se sobrepõem no espaço de características, ou que suas características não são informativas para a tarefa.
Exemplo Prático: Previsão de Doenças
O exemplo completo a seguir usa o conjunto de dados de câncer de mama que acompanha o scikit-learn:
from sklearn.datasets import load_breast_cancer
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import roc_auc_score, roc_curve
import matplotlib.pyplot as plt
# Load dataset (569 samples, 30 features, binary labels: malignant=0 / benign=1)
data = load_breast_cancer()
X, y = data.data, data.target
# Scale features — important for logistic regression
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
X_train, X_test, y_train, y_test = train_test_split(
X_scaled, y, test_size=0.2, random_state=42, stratify=y
)
classifiers = {
"Logistic Regression": LogisticRegression(max_iter=1000, random_state=42),
"Random Forest": RandomForestClassifier(n_estimators=100, random_state=42),
}
plt.figure(figsize=(7, 5))
for name, clf in classifiers.items():
clf.fit(X_train, y_train)
y_proba = clf.predict_proba(X_test)[:, 1]
auc = roc_auc_score(y_test, y_proba)
fpr, tpr, _ = roc_curve(y_test, y_proba)
plt.plot(fpr, tpr, lw=2, label=f"{name} (AUC = {auc:.3f})")
plt.plot([0, 1], [0, 1], "k--", label="Random (AUC = 0.50)")
plt.xlabel("False Positive Rate")
plt.ylabel("True Positive Rate")
plt.title("ROC Curve — Breast Cancer Dataset")
plt.legend(loc="lower right")
plt.tight_layout()
plt.savefig("breast_cancer_roc.png", dpi=120)
plt.show()Ambos os classificadores devem produzir pontuações AUC acima de 0.98 neste conjunto de dados, confirmando que as características do câncer de mama são altamente preditivas.
Tópicos Relacionados
- Matriz de Confusão — os blocos fundamentais TP/FP/TN/FN usados ao longo deste capítulo
- Regressão Logística — o modelo mais comum combinado com avaliação por AUC-ROC
- Validação Cruzada — como obter estimativas confiáveis de AUC que generalizam além de uma única divisão treino/teste
- Grid Search — como ajustar hiperparâmetros com
scoring='roc_auc' - Árvore de Decisão — outro classificador binário cujas saídas de probabilidade podem ser avaliadas com AUC-ROC
Resumo
| Conceito | Ponto Principal |
|---|---|
| Curva ROC | Plota TPR vs. FPR em cada limiar de decisão |
| AUC | Área sob a curva ROC; 1.0 = perfeito, 0.5 = aleatório |
roc_auc_score | Passe pontuações de probabilidade, não rótulos diretos |
roc_curve | Retorna arrays (fpr, tpr, thresholds) para plotagem |
| Seleção de limiar | Use o J de Youden ou conhecimento do domínio para escolher um limiar de produção |
| Quando preferir AUC-PR | Conjuntos de dados severamente desbalanceados com positivos raros |
A AUC-ROC fornece um único número independente de limiar que resume a capacidade discriminativa do modelo em todos os pontos de operação. Use-a para comparar modelos, ajustar hiperparâmetros e comunicar a qualidade do classificador — depois escolha o limiar específico que corresponde à tolerância da sua aplicação para falsos positivos versus falsos negativos.