Curva AUC - ROC
Calcola e traccia curve AUC-ROC in Python con sklearn. Comprendi TPR, FPR, soglie e quando usare AUC invece dell'accuratezza.
La curva AUC-ROC (Area Under the Receiver Operating Characteristic Curve) è una delle metriche più importanti per valutare i modelli di classificazione binaria. Cattura la capacità di un modello di separare esempi positivi e negativi attraverso ogni possibile soglia di decisione, fornendo un quadro molto più ricco rispetto a un singolo punteggio di accuratezza.
Questo capitolo tratta:
- Cosa rappresenta la curva ROC e come viene costruita
- Come interpretare TPR, FPR e i termini della matrice di confusione che li sostengono
- Come calcolare AUC-ROC con
scikit-learndi Python - Come tracciare la curva ROC con
matplotlib - Quando AUC-ROC è la metrica giusta e quando non lo è
Termini Chiave: TP, FP, TN, FN
Prima di addentrarsi nella curva stessa, è importante comprendere i quattro esiti che un classificatore binario può produrre. Per qualsiasi previsione, l'etichetta effettiva è positiva (P) o negativa (N), e l'etichetta prevista è positiva o negativa:
| Previsto Positivo | Previsto Negativo | |
|---|---|---|
| Effettivamente Positivo | Vero Positivo (TP) | Falso Negativo (FN) |
| Effettivamente Negativo | Falso Positivo (FP) | Vero Negativo (TN) |
Queste quattro celle sono i mattoni di ogni metrica di valutazione trattata in questo capitolo. Per un'introduzione più approfondita, consulta il capitolo sulla Matrice di Confusione.
Cos'è la Curva ROC?
Un classificatore di solito produce un punteggio di probabilità (un numero compreso tra 0 e 1) piuttosto che un'etichetta netta. Si sceglie una soglia — ad esempio, 0.5 — e tutto ciò che supera la soglia viene previsto come positivo.
Modificare la soglia sposta l'equilibrio tra il catturare i veri positivi e il segnalare accidentalmente i veri negativi:
- Una soglia molto bassa cattura più positivi (alto recall) ma produce anche più falsi allarmi.
- Una soglia molto alta è selettiva (alta precisione) ma manca i positivi reali.
La curva ROC rappresenta graficamente questo compromesso per ogni possibile soglia contemporaneamente:
- Asse X — Tasso di Falsi Positivi (FPR): quale frazione dei negativi effettivi viene erroneamente segnalata come positiva.
- Asse Y — Tasso di Veri Positivi (TPR), detto anche Recall o Sensibilità: quale frazione dei positivi effettivi viene correttamente identificata.
TPR = TP / (TP + FN) e FPR = FP / (FP + TN)
Ogni punto sulla curva ROC corrisponde a un'impostazione di soglia. Insieme, tracciano un percorso da (0, 0) — la soglia più selettiva, che non prevede nulla come positivo — a (1, 1) — la soglia meno selettiva, che prevede tutto come positivo.
Cos'è l'AUC?
L'AUC (Area Under the Curve) sintetizza l'intera curva ROC in un singolo numero compreso tra 0 e 1:
| AUC | Significato |
|---|---|
| 1.0 | Classificatore perfetto — separa ogni positivo da ogni negativo |
| 0.5 | Nessuna capacità predittiva — equivalente a una scelta casuale |
| < 0.5 | Peggio del caso casuale (le probabilità previste sono invertite) |
| 0.7 – 0.8 | Accettabile per molti problemi pratici |
| 0.8 – 0.9 | Buono |
| > 0.9 | Eccellente |
In modo intuitivo, l'AUC è la probabilità che il modello classifichi un esempio positivo scelto a caso più in alto rispetto a un esempio negativo scelto a caso.
Calcolo dell'AUC-ROC con scikit-learn
Il modulo sklearn.metrics fornisce roc_curve() per ottenere i valori TPR/FPR soglia per soglia e roc_auc_score() per ottenere il singolo valore AUC.
Esempio funzionante minimale
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)}")L'output sarà simile a:
AUC-ROC: 0.944
Number of threshold points: 30Il valore esatto varia leggermente in base alla versione di scikit-learn, ma dovrebbe essere nell'intervallo 0.90–0.97 per questo dataset.
Tracciare la 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()La linea tratteggiata diagonale rappresenta un classificatore casuale. Più la curva ROC si avvicina all'angolo in alto a sinistra, migliore è il modello.
Confronto di Modelli Multipli
Un flusso di lavoro comune consiste nell'addestrare diversi modelli e confrontare le loro curve ROC sullo stesso grafico:
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()Il modello con l'area maggiore sotto la sua curva è generalmente la scelta migliore per questo dataset. Per un flusso di lavoro rigoroso di selezione del modello, combinalo con la cross-validation.
Scelta di un Punto Operativo (Soglia)
L'AUC riassume tutte le soglie, ma alla fine sarà necessario scegliere una soglia per la produzione. Due strategie comuni:
1. Statistica J di Youden
La J di Youden massimizza TPR − FPR, trovando il singolo punto sulla curva ROC più lontano dalla diagonale:
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. Soglia guidata dal business
Nel rilevamento delle frodi si possono tollerare più falsi positivi per individuare più frodi (soglia più bassa). Nello screening medico si possono volere pochissimi falsi negativi (anch'essa soglia più bassa). La scelta della soglia è in definitiva una decisione aziendale informata dal costo di ciascun tipo di errore.
AUC-ROC vs. Altre Metriche
| Metrica | Migliore quando... | Attenzione a... |
|---|---|---|
| Accuratezza | Le classi sono bilanciate | Fuorviante su dataset sbilanciati |
| Precisione / Recall | Ci si preoccupa principalmente di una classe | Richiede una soglia |
| F1-Score | Bilanciamento armonico di precisione e recall | Ancora dipendente dalla soglia |
| AUC-ROC | Confronto di modelli o regolazione delle soglie | Non significativo per multi-classe senza estensioni |
| AUC-PR (AUC Precisione-Recall) | Forte squilibrio di classe (positivi rari) | Meno intuitivo della ROC |
Per dati fortemente sbilanciati — ad esempio, 1% di positivi — la curva AUC-PR (precisione vs. recall) è spesso più informativa dell'AUC-ROC, perché l'FPR può sembrare piccolo anche quando molti negativi vengono segnalati in modo errato.
Errori Comuni
Passare etichette nette invece di probabilità. roc_auc_score richiede punteggi di probabilità, non etichette 0/1. Usa model.predict_proba(X_test)[:, 1], non model.predict(X_test).
Dimenticare di specificare la classe positiva. Per impostazione predefinita, roc_curve e roc_auc_score trattano l'etichetta intera maggiore come positiva. Passa esplicitamente pos_label=1 quando le etichette non sono interi 0/1.
Overfitting sull'AUC del set di addestramento. Valuta sempre su un test set tenuto separato o usa la cross-validation per ottenere una stima affidabile dell'AUC. Il capitolo sulla Grid Search mostra come passare scoring='roc_auc' direttamente a GridSearchCV.
AUC ≈ 0.5 non significa sempre che il modello sia scadente. Può significare che gli esempi positivi e negativi si sovrappongono genuinamente nello spazio delle feature, oppure che le feature non sono informative per il compito.
Esempio Pratico: Previsione di Malattie
Il seguente esempio end-to-end utilizza il dataset sul cancro al seno fornito con 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()Entrambi i classificatori dovrebbero produrre punteggi AUC superiori a 0.98 su questo dataset, confermando che le feature del cancro al seno sono altamente predittive.
Argomenti Correlati
- Matrice di Confusione — i mattoni TP/FP/TN/FN usati in tutto questo capitolo
- Regressione Logistica — il modello più comune abbinato alla valutazione AUC-ROC
- Cross-Validation — come ottenere stime AUC affidabili che si generalizzano oltre una singola divisione train/test
- Grid Search — come ottimizzare gli iperparametri con
scoring='roc_auc' - Albero Decisionale — un altro classificatore binario i cui output di probabilità possono essere valutati con AUC-ROC
Riepilogo
| Concetto | Punto Chiave |
|---|---|
| Curva ROC | Traccia TPR vs. FPR ad ogni soglia di decisione |
| AUC | Area sotto la curva ROC; 1.0 = perfetto, 0.5 = casuale |
roc_auc_score | Passa punteggi di probabilità, non etichette nette |
roc_curve | Restituisce array (fpr, tpr, thresholds) per il tracciamento |
| Selezione della soglia | Usa la J di Youden o la conoscenza del dominio per scegliere una soglia di produzione |
| Quando preferire AUC-PR | Dataset fortemente sbilanciati con positivi rari |
L'AUC-ROC fornisce un singolo numero indipendente dalla soglia che riassume la capacità discriminativa del modello su tutti i punti operativi. Usalo per confrontare modelli, ottimizzare iperparametri e comunicare la qualità del classificatore — poi scegli la soglia specifica che corrisponde alla tolleranza della tua applicazione per i falsi positivi rispetto ai falsi negativi.