roc_curve
czym jest krzywa ROC? ⭐
- ROC (Receiver Operating Characteristic) to wykres pokazujący zależność między True Positive Rate (TPR) a False Positive Rate (FPR).
- Jest jedną z najważniejszych metryk oceny modeli klasyfikacyjnych.
- Pokazuje kompromis między czułością (sensitivity) a specyficznością (specificity).
- AUC (Area Under Curve) to liczba od 0 do 1, gdzie 1 oznacza doskonały model.
- Jest szczególnie przydatna dla niezbalansowanych zbiorów danych.
interpretacja krzywej ROC ⭐
Składowe krzywej ROC:
- True Positive Rate (TPR/Sensitivity/Recall):
TP / (TP + FN)
- False Positive Rate (FPR/1-Specificity):
FP / (FP + TN)
- AUC: Powierzchnia pod krzywą ROC
Interpretacja AUC:
- AUC = 0.5: Model losowy (jak rzut monetą)
- AUC = 0.7-0.8: Dobry model
- AUC = 0.8-0.9: Bardzo dobry model
- AUC = 0.9-1.0: Doskonały model
przykładowy kod (krzywa ROC)
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_curve, roc_auc_score, auc
from sklearn.metrics import confusion_matrix
import matplotlib.pyplot as plt
import seaborn as sns
# Generowanie danych
np.random.seed(42)
n_samples = 2000
# Generowanie cech
feature1 = np.random.normal(0, 1, n_samples)
feature2 = np.random.normal(0, 1, n_samples)
# Tworzenie niezbalansowanego targetu
class_0_mask = np.random.choice([True, False], n_samples, p=[0.8, 0.2])
class_1_mask = ~class_0_mask
# Modyfikacja cech dla klasy 1
feature1[class_1_mask] += 1.5
feature2[class_1_mask] += 1.5
# Target
y = class_1_mask.astype(int)
# Tworzenie DataFrame
data = pd.DataFrame({
'feature1': feature1,
'feature2': feature2,
'target': y
})
# Podział danych
X = data[['feature1', 'feature2']]
y = data['target']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)
print("Rozkład klas:")
print(data['target'].value_counts(normalize=True))
1. podstawowa krzywa ROC
# Trenowanie modelu
model = RandomForestClassifier(random_state=42)
model.fit(X_train, y_train)
# Przewidywania
y_pred_proba = model.predict_proba(X_test)[:, 1]
# Obliczanie krzywej ROC
fpr, tpr, thresholds = roc_curve(y_test, y_pred_proba)
roc_auc = roc_auc_score(y_test, y_pred_proba)
# Wizualizacja
plt.figure(figsize=(12, 4))
plt.subplot(1, 3, 1)
plt.plot(fpr, tpr, color='darkorange', lw=2, label=f'ROC curve (AUC = {roc_auc:.3f})')
plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--', label='Random classifier')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('ROC Curve')
plt.legend(loc="lower right")
plt.grid(True, alpha=0.3)
# Dodanie punktów dla różnych progów
threshold_points = [0.2, 0.5, 0.8]
for threshold in threshold_points:
idx = np.argmin(np.abs(thresholds - threshold))
plt.plot(fpr[idx], tpr[idx], 'ro', markersize=8)
plt.annotate(f'T={threshold:.1f}', (fpr[idx], tpr[idx]),
xytext=(10, 10), textcoords='offset points')
2. porównanie różnych modeli
# Trenowanie różnych modeli
models = {
'Random Forest': RandomForestClassifier(random_state=42),
'Logistic Regression': LogisticRegression(random_state=42)
}
plt.subplot(1, 3, 2)
colors = ['darkorange', 'green', 'red']
for i, (name, model) in enumerate(models.items()):
# Trenowanie
model.fit(X_train, y_train)
y_pred_proba = model.predict_proba(X_test)[:, 1]
# Krzywa ROC
fpr, tpr, _ = roc_curve(y_test, y_pred_proba)
roc_auc = roc_auc_score(y_test, y_pred_proba)
# Wizualizacja
plt.plot(fpr, tpr, color=colors[i], lw=2,
label=f'{name} (AUC = {roc_auc:.3f})')
# Linia losowego klasyfikatora
plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--', label='Random classifier')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('ROC Curves - Model Comparison')
plt.legend(loc="lower right")
plt.grid(True, alpha=0.3)
3. analiza progów decyzyjnych
# Analiza różnych progów
def analyze_thresholds(y_true, y_pred_proba, thresholds=[0.1, 0.3, 0.5, 0.7, 0.9]):
"""Analiza różnych progów decyzyjnych"""
results = []
for threshold in thresholds:
y_pred = (y_pred_proba > threshold).astype(int)
tn, fp, fn, tp = confusion_matrix(y_true, y_pred).ravel()
tpr = tp / (tp + fn) # True Positive Rate
fpr = fp / (fp + tn) # False Positive Rate
results.append({
'threshold': threshold,
'tpr': tpr,
'fpr': fpr,
'precision': tp / (tp + fp) if (tp + fp) > 0 else 0,
'accuracy': (tp + tn) / (tp + tn + fp + fn)
})
return pd.DataFrame(results)
# Analiza progów
model = RandomForestClassifier(random_state=42)
model.fit(X_train, y_train)
y_pred_proba = model.predict_proba(X_test)[:, 1]
threshold_analysis = analyze_thresholds(y_test, y_pred_proba)
print("\nAnaliza progów decyzyjnych:")
print(threshold_analysis)
# Wizualizacja analizy progów
plt.subplot(1, 3, 3)
plt.plot(threshold_analysis['fpr'], threshold_analysis['tpr'], 'bo-', markersize=8)
for _, row in threshold_analysis.iterrows():
plt.annotate(f"T={row['threshold']:.1f}",
(row['fpr'], row['tpr']),
xytext=(5, 5), textcoords='offset points')
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Threshold Analysis')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
4. optymalizacja progu decyzyjnego
# Znajdowanie optymalnego progu
def find_optimal_threshold(y_true, y_pred_proba, metric='f1'):
"""Znajdowanie optymalnego progu na podstawie wybranej metryki"""
from sklearn.metrics import f1_score, precision_score, recall_score
thresholds = np.arange(0.1, 0.9, 0.01)
scores = []
for threshold in thresholds:
y_pred = (y_pred_proba > threshold).astype(int)
if metric == 'f1':
score = f1_score(y_true, y_pred)
elif metric == 'precision':
score = precision_score(y_true, y_pred)
elif metric == 'recall':
score = recall_score(y_true, y_pred)
else:
score = f1_score(y_true, y_pred)
scores.append(score)
optimal_threshold = thresholds[np.argmax(scores)]
optimal_score = max(scores)
return optimal_threshold, optimal_score, thresholds, scores
# Znajdowanie optymalnego progu
optimal_threshold, optimal_score, thresholds, scores = find_optimal_threshold(y_test, y_pred_proba, 'f1')
print(f"\nOptymalny próg (F1-score): {optimal_threshold:.3f}")
print(f"Optymalny F1-score: {optimal_score:.3f}")
# Wizualizacja optymalizacji progu
plt.figure(figsize=(10, 4))
plt.subplot(1, 2, 1)
plt.plot(thresholds, scores, 'b-', lw=2)
plt.axvline(x=optimal_threshold, color='red', linestyle='--',
label=f'Optimal threshold = {optimal_threshold:.3f}')
plt.xlabel('Threshold')
plt.ylabel('F1-Score')
plt.title('Threshold Optimization')
plt.legend()
plt.grid(True, alpha=0.3)
# Porównanie z domyślnym progiem
y_pred_default = (y_pred_proba > 0.5).astype(int)
y_pred_optimal = (y_pred_proba > optimal_threshold).astype(int)
from sklearn.metrics import classification_report
print("\nWyniki z domyślnym progiem (0.5):")
print(classification_report(y_test, y_pred_default))
print("\nWyniki z optymalnym progiem:")
print(classification_report(y_test, y_pred_optimal))
5. krzywa ROC dla różnych scenariuszy
# Symulacja różnych jakości modeli
plt.figure(figsize=(12, 4))
# Doskonały model
perfect_fpr = np.array([0, 0, 1])
perfect_tpr = np.array([0, 1, 1])
perfect_auc = auc(perfect_fpr, perfect_tpr)
# Dobry model (nasz rzeczywisty)
good_fpr, good_tpr, _ = roc_curve(y_test, y_pred_proba)
good_auc = roc_auc_score(y_test, y_pred_proba)
# Słaby model
weak_fpr = np.array([0, 0.3, 0.7, 1])
weak_tpr = np.array([0, 0.4, 0.6, 1])
weak_auc = auc(weak_fpr, weak_tpr)
# Losowy model
random_fpr = np.array([0, 1])
random_tpr = np.array([0, 1])
random_auc = 0.5
# Wizualizacja
plt.subplot(1, 3, 1)
plt.plot(perfect_fpr, perfect_tpr, 'g-', lw=2, label=f'Perfect (AUC = {perfect_auc:.3f})')
plt.plot(good_fpr, good_tpr, 'b-', lw=2, label=f'Good (AUC = {good_auc:.3f})')
plt.plot(weak_fpr, weak_tpr, 'orange', lw=2, label=f'Weak (AUC = {weak_auc:.3f})')
plt.plot(random_fpr, random_tpr, 'r--', lw=2, label=f'Random (AUC = {random_auc:.3f})')
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('ROC Curves - Different Model Qualities')
plt.legend()
plt.grid(True, alpha=0.3)
praktyczne ćwiczenia
-
Porównaj modele - stwórz krzywe ROC dla różnych algorytmów.
-
Threshold optimization - znajdź optymalny próg dla swojego problemu.
-
Business context - dostosuj próg do kosztów błędów.
-
Cross-validation - użyj cross-validation do oceny ROC-AUC.
-
Real data - przetestuj na rzeczywistych zbiorach danych.
dobre praktyki
- Interpretacja AUC: AUC > 0.8 oznacza dobry model.
- Threshold selection: Dostosuj próg do problemu biznesowego.
- Cross-validation: Używaj CV do rzetelnej oceny ROC-AUC.
- Multiple metrics: Nie polegaj tylko na ROC-AUC.
polecane źródła
- ROC Curve Tutorial – Towards Data Science
- ROC and AUC Guide – Machine Learning Mastery
- ROC Curve Documentation – scikit-learn
- ROC Analysis Tutorial – Kaggle