Perché l'Accuracy Non Basta
La valutazione dei modelli di Machine Learning è molto più complessa di quanto sembri. L'errore più comune tra i principianti è affidarsi esclusivamente all'accuracy (percentuale di previsioni corrette). Con dataset sbilanciati, dove una classe rappresenta il 95% dei dati, un modello che predice sempre la classe maggioritaria ottiene un'accuracy del 95% pur essendo completamente inutile. Servono metriche più sofisticate per valutare correttamente un modello.
In questo articolo esploreremo l'intero arsenale di metriche disponibili per classificazione e regressione, capiremo quando usare ciascuna e costruiremo un framework di valutazione robusto con cross-validation.
Cosa Imparerai in Questo Articolo
- Confusion Matrix: TP, TN, FP, FN
- Precision, Recall e F1-Score
- ROC Curve e AUC
- Metriche di regressione: MSE, MAE, R²
- Cross-validation: valutazione robusta
- Come scegliere la metrica giusta per il tuo problema
La Confusion Matrix
La confusion matrix è la base di tutte le metriche di classificazione. Per un problema binario, organizza le previsioni in quattro categorie: True Positive (TP) — correttamente predetto come positivo; True Negative (TN) — correttamente predetto come negativo; False Positive (FP) — erroneamente predetto come positivo (errore di tipo I); False Negative (FN) — erroneamente predetto come negativo (errore di tipo II).
from sklearn.metrics import (
confusion_matrix, classification_report,
precision_score, recall_score, f1_score,
roc_auc_score, roc_curve, accuracy_score
)
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
import numpy as np
# Dataset
data = load_breast_cancer()
X, y = data.data, data.target
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# Modello
pipeline = Pipeline([
('scaler', StandardScaler()),
('clf', LogisticRegression(max_iter=10000, random_state=42))
])
pipeline.fit(X_train, y_train)
# Predizioni e probabilità'
y_pred = pipeline.predict(X_test)
y_proba = pipeline.predict_proba(X_test)[:, 1]
# Confusion Matrix
cm = confusion_matrix(y_test, y_pred)
tn, fp, fn, tp = cm.ravel()
print("Confusion Matrix:")
print(f" TN={tn} FP={fp}")
print(f" FN={fn} TP={tp}")
# Metriche
print(f"\nAccuracy: {accuracy_score(y_test, y_pred):.3f}")
print(f"Precision: {precision_score(y_test, y_pred):.3f}")
print(f"Recall: {recall_score(y_test, y_pred):.3f}")
print(f"F1-Score: {f1_score(y_test, y_pred):.3f}")
print(f"ROC-AUC: {roc_auc_score(y_test, y_proba):.3f}")
# Report completo
print(f"\n{classification_report(y_test, y_pred, target_names=data.target_names)}")
Precision, Recall e F1-Score
Precision = TP / (TP + FP): di tutti i campioni predetti come positivi, quanti lo sono davvero? Alta precision significa pochi falsi positivi. Cruciale quando i falsi positivi sono costosi (es. approvazione crediti: non vuoi approvare un cliente insolvente).
Recall (Sensitivity) = TP / (TP + FN): di tutti i campioni realmente positivi, quanti sono stati trovati? Alto recall significa pochi falsi negativi. Cruciale quando i falsi negativi sono pericolosi (es. diagnosi tumore: non vuoi mancare un caso positivo).
F1-Score = 2 * (Precision * Recall) / (Precision + Recall): la media armonica di precision e recall. Utile quando serve un bilanciamento tra le due metriche. La media armonica penalizza valori estremi: se una delle due metriche è molto bassa, l'F1 sarà basso.
Trade-off Precision-Recall: Aumentare la soglia di classificazione (sopra 0.5) aumenta la precision ma diminuisce il recall. Abbassarla fa il contrario. Non esiste una soglia universalmente migliore: dipende dal costo relativo dei falsi positivi vs falsi negativi nel tuo specifico dominio.
ROC Curve e AUC
La ROC curve (Receiver Operating Characteristic) visualizza il trade-off tra True Positive Rate (recall) e False Positive Rate al variare della soglia di classificazione. L'AUC (Area Under the Curve) riassume la ROC in un singolo numero: 1.0 è un modello perfetto, 0.5 è equivalente al caso (classificazione random). L'AUC misura la capacità del modello di separare le classi indipendentemente dalla soglia scelta.
from sklearn.metrics import precision_recall_curve, roc_curve, roc_auc_score
from sklearn.model_selection import cross_val_score, StratifiedKFold
from sklearn.ensemble import RandomForestClassifier
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
import numpy as np
# Pipeline
pipeline = Pipeline([
('scaler', StandardScaler()),
('clf', RandomForestClassifier(n_estimators=100, random_state=42))
])
# --- CROSS-VALIDATION con più' metriche ---
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
metrics = ['accuracy', 'precision', 'recall', 'f1', 'roc_auc']
print("Cross-Validation Results:")
for metric in metrics:
scores = cross_val_score(pipeline, X, y, cv=cv, scoring=metric)
print(f" {metric:<12s}: {scores.mean():.3f} (+/- {scores.std():.3f})")
# --- THRESHOLD TUNING ---
pipeline.fit(X_train, y_train)
y_proba = pipeline.predict_proba(X_test)[:, 1]
# Precision-Recall per diverse soglie
precisions, recalls, thresholds = precision_recall_curve(y_test, y_proba)
# Trovare la soglia che massimizza F1
f1_scores = 2 * (precisions * recalls) / (precisions + recalls + 1e-10)
best_idx = np.argmax(f1_scores)
best_threshold = thresholds[best_idx] if best_idx < len(thresholds) else 0.5
print(f"\nMiglior threshold per F1: {best_threshold:.3f}")
print(f" Precision: {precisions[best_idx]:.3f}")
print(f" Recall: {recalls[best_idx]:.3f}")
print(f" F1: {f1_scores[best_idx]:.3f}")
Metriche di Regressione
Per la regressione, le metriche misurano la distanza tra valori predetti e reali. MSE (Mean Squared Error) penalizza errori grandi quadraticamente. MAE (Mean Absolute Error) tratta tutti gli errori linearmente, più robusto agli outlier. RMSE (Root MSE) ha le stesse unità di misura del target, più interpretabile. R² indica la proporzione di varianza spiegata dal modello: 1.0 è perfetto, 0 equivale a predire sempre la media.
Come Scegliere la Metrica Giusta
La scelta della metrica dipende dal dominio e dai costi degli errori. Per la diagnosi medica, il recall è critico (non vuoi mancare un caso). Per lo spam filtering, la precision è importante (non vuoi perdere email importanti). Per dataset sbilanciati, F1-Score e AUC sono preferibili all'accuracy. Per la regressione con outlier, MAE è migliore di MSE. Discuti sempre con gli stakeholder di dominio per capire quale tipo di errore è più costoso.
Punti Chiave
- L'accuracy da sola è insufficiente, specialmente con dataset sbilanciati
- Precision misura la correttezza dei positivi predetti; Recall misura la completezza
- F1-Score bilancia Precision e Recall con la media armonica
- ROC-AUC misura la capacità di separazione indipendentemente dalla soglia
- Cross-validation con StratifiedKFold è il gold standard per la valutazione
- La scelta della metrica dipende dai costi relativi dei diversi tipi di errore nel tuo dominio







