Przejdź do głównej zawartości

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

  1. Porównaj modele - stwórz krzywe ROC dla różnych algorytmów.

  2. Threshold optimization - znajdź optymalny próg dla swojego problemu.

  3. Business context - dostosuj próg do kosztów błędów.

  4. Cross-validation - użyj cross-validation do oceny ROC-AUC.

  5. 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