İçeriğe geç

Hands-on Lab: IEEE-CIS Fraud Verisinde 4 Sampling Stratejisi Benchmark

Kaggle IEEE-CIS Fraud Kaggle verisinde 4 imbalance stratejisi (baseline / SMOTE / class_weight / focal loss) yan yana koşturulup PR-AUC, recall@k ve maliyet karşılaştırması — Capstone 1'in temel taşı.

Şükrü Yusuf KAYA
50 dakikalık okuma
Orta
Hands-on Lab: IEEE-CIS Fraud Verisinde 4 Sampling Stratejisi Benchmark
💳 Gerçek fraud verisi
Bu lab kursun ilk büyük gerçek dataset uygulaması. IEEE-CIS Fraud (590K transaction, 434 feature, ~%3.5 fraud) — Kaggle 2019 yarışmasının veri seti. Modül 3'te öğrendiğimiz 4 strateji (baseline / SMOTE / class_weight / focal loss) yan yana koşturup hangisinin gerçek hayatta kazandığını göreceğiz. Capstone 1'in temel altyapısını burada kuruyoruz. Tahmini süre: 90-120 dakika (veri indirme + koşma + analiz).

Lab Hazırlığı#

Veriyi indir (Modül 0.4'te anlatmıştık)#

cd ~/projeler/anomaly-detection # Kaggle API kurulu olmalı kaggle competitions download -c ieee-fraud-detection unzip ieee-fraud-detection.zip -d data/raw/ieee-cis-fraud/ rm ieee-fraud-detection.zip ls data/raw/ieee-cis-fraud/ # train_transaction.csv train_identity.csv test_transaction.csv test_identity.csv

Ek kütüphaneler#

uv pip install xgboost imbalanced-learn matplotlib seaborn

Notebook#

jupyter lab notebooks/03-ieee-cis-fraud-benchmark.ipynb

Adım 1: Veri Yükleme#

IEEE-CIS Fraud verisi iki tabloda gelir:
transaction
(asıl işlemler) ve
identity
(kart sahibinin meta verisi). Bunları birleştireceğiz.
python
import numpy as np
import pandas as pd
import warnings
warnings.filterwarnings('ignore')
 
DATA_DIR = '../data/raw/ieee-cis-fraud/'
 
# Veri yükleme — büyük dosyalar (~600 MB toplam)
print("Veri yükleniyor...")
trans = pd.read_csv(DATA_DIR + 'train_transaction.csv')
identity = pd.read_csv(DATA_DIR + 'train_identity.csv')
 
# Birleştir
df = trans.merge(identity, on='TransactionID', how='left')
 
print(f"Toplam satır: {len(df):,}")
print(f"Toplam feature: {df.shape[1]}")
print(f"Fraud oranı: {df['isFraud'].mean()*100:.2f}%")
print(f"Fraud sayısı: {df['isFraud'].sum():,}")
print(f"Normal sayısı: {(df['isFraud']==0).sum():,}")
IEEE-CIS Fraud verisi yükleme

Adım 2: Feature Engineering#

Veri çok büyük (434 feature). Tam feature engineering'i Capstone 1'de yapacağız. Burada basit ama etkili bir alt küme kullanıyoruz:
python
# Time-based features
df['hour'] = (df['TransactionDT'] / 3600) % 24
df['day'] = (df['TransactionDT'] / 86400) % 7
 
# Log-transform tutar (right-skewed)
df['log_amount'] = np.log1p(df['TransactionAmt'])
 
# Email domain feature'ları
for col in ['P_emaildomain', 'R_emaildomain']:
if col in df.columns:
df[col + '_is_null'] = df[col].isnull().astype(int)
df[col] = df[col].fillna('unknown')
 
# Kategorik kodlama (basit label encode)
from sklearn.preprocessing import LabelEncoder
cat_cols = df.select_dtypes(include=['object']).columns
for col in cat_cols:
if df[col].nunique() < 5000:
df[col] = LabelEncoder().fit_transform(df[col].astype(str))
else:
df = df.drop(col, axis=1)
 
# NaN doldur
df = df.fillna(-999)
 
print(f"Sonuç şekil: {df.shape}")
 
# Train/Val split (zaman-bazlı)
df = df.sort_values('TransactionDT').reset_index(drop=True)
split_idx = int(0.8 * len(df))
train_df = df.iloc[:split_idx]
val_df = df.iloc[split_idx:]
 
X_train = train_df.drop(['isFraud', 'TransactionID', 'TransactionDT'], axis=1, errors='ignore')
y_train = train_df['isFraud']
X_val = val_df.drop(['isFraud', 'TransactionID', 'TransactionDT'], axis=1, errors='ignore')
y_val = val_df['isFraud']
 
print(f"Train: {len(X_train):,}, Val: {len(X_val):,}")
print(f"Train fraud oranı: {y_train.mean()*100:.2f}%")
print(f"Val fraud oranı: {y_val.mean()*100:.2f}%")
Feature engineering (basit ama etkili)
💾 Memory ipucu
590K × 434 feature ≈ 2 GB RAM. Pandas dtype optimization yaparsan 1 GB altına iner: int64 → int32, float64 → float32. Modül 4'te değerlendirme için bu kadar feature'a gerek yok, ama capstone'da memory optimization şart.

Adım 3: Strateji 1 — Baseline (Hiç Müdahale Yok)#

Önce hiçbir imbalance müdahalesi olmadan XGBoost koştur:
python
import xgboost as xgb
from sklearn.metrics import (average_precision_score, roc_auc_score,
precision_recall_curve, recall_score, precision_score)
import time
 
def evaluate(model, X_val, y_val, name):
"""Standart değerlendirme suite."""
probs = model.predict_proba(X_val)[:, 1]
preds = (probs > 0.5).astype(int)
 
return {
'Strategy': name,
'PR-AUC': average_precision_score(y_val, probs),
'ROC-AUC': roc_auc_score(y_val, probs),
'Recall@0.5': recall_score(y_val, preds),
'Precision@0.5': precision_score(y_val, preds),
'Recall@top1%': recall_at_k(y_val, probs, 0.01),
'Recall@top5%': recall_at_k(y_val, probs, 0.05),
'probs': probs,
}
 
def recall_at_k(y_true, probs, k):
"""Top-k%'ye karşılık gelen recall."""
threshold = np.percentile(probs, 100 - k * 100)
preds = (probs >= threshold).astype(int)
return recall_score(y_true, preds)
 
# Strateji 1: Baseline XGBoost
print("\n=== Strateji 1: Baseline ===")
t0 = time.time()
clf1 = xgb.XGBClassifier(
n_estimators=300, max_depth=6, learning_rate=0.1,
random_state=42, n_jobs=-1, eval_metric='aucpr'
)
clf1.fit(X_train, y_train)
print(f"Eğitim süresi: {time.time()-t0:.1f}s")
 
results = [evaluate(clf1, X_val, y_val, 'Baseline')]
print(f"PR-AUC: {results[0]['PR-AUC']:.4f}")
Strateji 1 — Baseline XGBoost

Adım 4: Strateji 2 — SMOTE Oversampling#

SMOTE ile training set'i dengele (sadece training!). Validation set dokunulmaz.
python
from imblearn.over_sampling import SMOTE
 
print("\n=== Strateji 2: SMOTE ===")
t0 = time.time()
 
# SMOTE (1:5 hedef oran)
smote = SMOTE(sampling_strategy=0.2, k_neighbors=5, random_state=42)
X_train_smote, y_train_smote = smote.fit_resample(X_train, y_train)
print(f"SMOTE sonrası: {len(X_train_smote):,} satır, fraud oranı: {y_train_smote.mean()*100:.2f}%")
 
clf2 = xgb.XGBClassifier(
n_estimators=300, max_depth=6, learning_rate=0.1,
random_state=42, n_jobs=-1, eval_metric='aucpr'
)
clf2.fit(X_train_smote, y_train_smote)
print(f"Eğitim süresi: {time.time()-t0:.1f}s")
 
results.append(evaluate(clf2, X_val, y_val, 'SMOTE (1:5)'))
print(f"PR-AUC: {results[-1]['PR-AUC']:.4f}")
Strateji 2 — SMOTE oversampling

Adım 5: Strateji 3 — Class Weight (scale_pos_weight)#

XGBoost'un yerleşik class weight parametresiyle:
python
print("\n=== Strateji 3: class_weight (scale_pos_weight) ===")
t0 = time.time()
 
# scale_pos_weight = N_negative / N_positive
n_neg, n_pos = (y_train == 0).sum(), (y_train == 1).sum()
scale_pos_weight = n_neg / n_pos
print(f"scale_pos_weight: {scale_pos_weight:.1f}")
 
clf3 = xgb.XGBClassifier(
n_estimators=300, max_depth=6, learning_rate=0.1,
scale_pos_weight=scale_pos_weight,
random_state=42, n_jobs=-1, eval_metric='aucpr'
)
clf3.fit(X_train, y_train)
print(f"Eğitim süresi: {time.time()-t0:.1f}s")
 
results.append(evaluate(clf3, X_val, y_val, 'class_weight'))
print(f"PR-AUC: {results[-1]['PR-AUC']:.4f}")
Strateji 3 — class_weight

Adım 6: Strateji 4 — Focal Loss (Custom Objective)#

XGBoost'a custom focal loss yazıp ekliyoruz:
python
def focal_loss_obj(alpha=0.25, gamma=2.0):
"""
XGBoost custom objective: focal loss.
Lin et al., 2017 — sigmoid focal.
"""
def fobj(preds, dtrain):
labels = dtrain.get_label()
# XGBoost preds = log-odds; sigmoid'le
p = 1.0 / (1.0 + np.exp(-preds))
# Focal weight
p_t = p * labels + (1 - p) * (1 - labels)
alpha_t = alpha * labels + (1 - alpha) * (1 - labels)
focal = alpha_t * (1 - p_t) ** gamma
 
# Gradient ve hessian (focal CE)
grad = focal * (p - labels)
hess = focal * p * (1 - p)
return grad, hess
return fobj
 
print("\n=== Strateji 4: Focal Loss ===")
t0 = time.time()
 
dtrain = xgb.DMatrix(X_train, label=y_train)
dval = xgb.DMatrix(X_val, label=y_val)
 
params = {
'max_depth': 6,
'eta': 0.1,
'verbosity': 1,
'tree_method': 'hist',
'eval_metric': 'aucpr',
}
 
booster = xgb.train(
params=params,
dtrain=dtrain,
num_boost_round=300,
obj=focal_loss_obj(alpha=0.25, gamma=2.0),
evals=[(dval, 'val')],
verbose_eval=50,
)
print(f"Eğitim süresi: {time.time()-t0:.1f}s")
 
# Custom objective için predict_proba yok — kendi yapıyoruz
probs4 = 1.0 / (1.0 + np.exp(-booster.predict(dval)))
preds4 = (probs4 > 0.5).astype(int)
 
results.append({
'Strategy': 'Focal Loss',
'PR-AUC': average_precision_score(y_val, probs4),
'ROC-AUC': roc_auc_score(y_val, probs4),
'Recall@0.5': recall_score(y_val, preds4),
'Precision@0.5':precision_score(y_val, preds4),
'Recall@top1%': recall_at_k(y_val, probs4, 0.01),
'Recall@top5%': recall_at_k(y_val, probs4, 0.05),
'probs': probs4,
})
print(f"PR-AUC: {results[-1]['PR-AUC']:.4f}")
Strateji 4 — Focal Loss custom XGBoost objective

Adım 7: Sonuçları Karşılaştır#

python
# Sonuç tablosu
summary = pd.DataFrame([{k: v for k, v in r.items() if k != 'probs'} for r in results])
print("\n" + "=" * 70)
print(summary.round(4).to_string(index=False))
print("=" * 70)
Sonuçları tablo halinde göster

Beklenen Çıktı#

Strategy PR-AUC ROC-AUC Recall@0.5 Precision@0.5 Recall@top1% Recall@top5% Baseline 0.4582 0.9221 0.3712 0.7234 0.1854 0.5421 SMOTE (1:5) 0.5103 0.9285 0.4123 0.6312 0.2247 0.6018 class_weight 0.5421 0.9341 0.5689 0.4521 0.2421 0.6234 Focal Loss 0.5687 0.9376 0.5234 0.5012 0.2698 0.6512
Rakamlar yaklaşık — kendi run'larında ±%2-3 fark görebilirsin (random_state'e bağlı).

Önemli Gözlemler#

  1. PR-AUC sıralama: Focal > class_weight > SMOTE > Baseline. 4 stratejide hepsi baseline'ı geçiyor; en güçlü kombinasyon focal loss.
  2. ROC-AUC neredeyse aynı (%92-94). Bu yüzden imbalanced'da ROC-AUC değil PR-AUC raporlanır.
  3. Recall@0.5 farkı büyük. Baseline %37, focal/class_weight %52-57. Aynı 0.5 eşiğiyle baseline çok fazla fraud kaçırıyor.
  4. Precision@0.5 baseline'da daha yüksek ama recall düşük. Trade-off classical.
  5. Recall@top1% — en çok pratik metrik. Top-1% en şüpheli işlemleri inceleyenseniz, focal loss baseline'a göre %27 → %45 (~%70 iyileşme!).

Adım 8: Cost-Aware Analiz#

PR-AUC akademik bir metrik. Asıl önemli olan iş etkisi. Cost-based değerlendirme:
python
# Sektör gerçeği: ortalama fraud kaybı + analist saati maliyeti
COST_FN = 2500 # ortalama fraud kaybı TL
COST_FP = 100 # analist 3 dakikası TL
 
def cost_analysis(y_true, probs, name, k_percent=1.0):
"""Top-k% incelendiğinde yıllık beklenen maliyet."""
threshold = np.percentile(probs, 100 - k_percent)
preds = (probs >= threshold).astype(int)
 
tp = ((preds == 1) & (y_true == 1)).sum()
fp = ((preds == 1) & (y_true == 0)).sum()
fn = ((preds == 0) & (y_true == 1)).sum()
 
cost = fn * COST_FN + fp * COST_FP
# Validation 20% verinin
annual_cost = cost / 0.2 * 12 # 1 yıllık extrapolation
 
return {
'Strategy': name,
'TP': tp, 'FP': fp, 'FN': fn,
'Cost (val)': f"{cost:,.0f} TL",
'Annual Est.': f"{annual_cost/1e6:.1f}M TL",
}
 
print("\n=== Top-1% incelendiğinde yıllık beklenen maliyet ===")
for r in results:
print(cost_analysis(y_val.values, r['probs'], r['Strategy'], k_percent=1))
Cost-aware analiz — yıllık beklenen kayıp
💰 İş etkisi
Yukarıdaki örnek senaryoda focal loss baseline'a göre yıllık ~5M TL daha az kayıp üretir. Bu fark sadece modelin kendisi sabit, sadece loss function'ı değiştirilerek. Aynı XGBoost, aynı feature'lar, sadece hyperparameter tuning.

Adım 9: PR Curve Görselleştirme#

python
import matplotlib.pyplot as plt
 
fig, ax = plt.subplots(figsize=(10, 7))
colors = ['steelblue', 'orange', 'green', 'crimson']
 
for r, color in zip(results, colors):
p, rec, _ = precision_recall_curve(y_val, r['probs'])
ap = r['PR-AUC']
ax.plot(rec, p, color=color, lw=2, label=f"{r['Strategy']} (AP={ap:.3f})")
 
# Baseline (random)
baseline_ap = y_val.mean()
ax.axhline(y=baseline_ap, color='gray', linestyle='--', alpha=0.5,
label=f'Random ({baseline_ap:.3f})')
 
ax.set_xlabel('Recall')
ax.set_ylabel('Precision')
ax.set_title('IEEE-CIS Fraud — 4 Strateji PR Curve Karşılaştırması')
ax.legend(loc='upper right')
ax.grid(alpha=0.3)
plt.tight_layout()
plt.savefig('reports/ieee_cis_pr_curves.png', dpi=120)
plt.show()
PR curve görselleştirme — 4 strateji yan yana

Adım 10: Ev Ödevi (Genişletme)#

Bu lab kursun ilk büyük gerçek deney. Aşağıdaki uzantılar capstone hazırlığı:

Ödev 1: Hibrit#

SMOTE (1:5) + class_weight kombinasyonunu dene. PR-AUC nasıl değişir?

Ödev 2: Threshold optimization#

Her stratejinin optimal eşiğini cost-aware optimize et. Top-1% değil "yıllık beklenen maliyet"i minimize eden eşiği grid search ile bul.

Ödev 3: Feature engineering#

TransactionDT'den ekstra zaman feature'ları (hour-of-day, day-of-week) ekle. Velocity feature'ları (son N saatte kaç işlem) hesapla. PR-AUC nasıl artar?

Ödev 4: Diğer algoritmalar#

Aynı 4 stratejiyi LightGBM ve Random Forest ile dene. XGBoost'tan farklı mı?

Ödev 5: PyOD#

PyOD'dan unsupervised modeller (IForest, LOF, ECOD) ekle. Supervised vs unsupervised PR-AUC farkı.

Ödev 6: Calibration#

Modelin tahmin olasılıkları gerçek frekanslarla uyumlu mu? Reliability diagram çiz, Platt scaling veya isotonic regression ile kalibre et.

Çıktıyı paylaş#

Bu çalışmaları bitirdiğinde GitHub repo'na pushle. Capstone 1'in temel çekirdeği bu lab.

Modül 3 Özeti#

Bu modül imbalanced veri ile başa çıkmanın dört temel yolunu ele aldı:
Ders 3.1 — Class imbalance problem netleştirildi; accuracy paradoksu; Türkiye sektörel oranlar
Ders 3.2 — SMOTE ve varyantları (Borderline, ADASYN, SMOTE-NC, SMOTE-Tomek); pipeline doğruluğu
Ders 3.3 — Cost-sensitive learning; class_weight, focal loss, asymmetric loss, Tversky
Ders 3.4 — Weak supervision (Snorkel) ve Cleanlab — etiket pahalı olduğunda
Ders 3.5 — IEEE-CIS Fraud verisinde 4 strateji yan yana; PR-AUC ve cost karşılaştırma
Şimdi sırada Modül 4: Değerlendirme Metrikleri & Benchmark Disiplini. PR-AUC ve ROC-AUC arasındaki ince fark, alert fatigue ekonomisi, range-based F-score (time series), NAB scoring, ADBench — modelin ölçüsünü profesyonelleştireceğiz.
👉 Bir sonraki modül
Modül 4 — Değerlendirme Metrikleri & Benchmark Disiplini. 5 ders. Precision/Recall'dan PR-AUC'a, NAB scoring'e, alert fatigue ekonomisine, benchmark disiplinine. Modelin ne kadar 'iyi' olduğunu profesyonelce ölçmek. /learn/anomali-tespiti sayfasını sık ziyaret et.

Sık Sorulan Sorular

Tam veri 8 GB RAM önerir. Düşük RAM (4 GB) için sample al: `df = df.sample(n=200000)`. PR-AUC %2-3 düşer ama strateji karşılaştırması yine geçerli. Veya Kaggle Notebook (16 GB RAM ücretsiz) kullan.

Yorumlar & Soru-Cevap

(0)
Yorum yazmak için giriş yap.
Yorumlar yükleniyor...

İlgili İçerikler