Skip to content

Cost-Sensitive Learning and Focal Loss: Training the Loss Function for Imbalanced Data

Alternative to sampling: modifying the loss function. Cost matrix, class weight, sample weight, asymmetric loss, focal loss (Lin et al., 2017), Tversky loss, and practical applications in imbalanced AD.

Şükrü Yusuf KAYA
26 min read
Intermediate
Cost-Sensitive Learning ve Focal Loss: Loss Function'ı Imbalanced'a Eğitmek
💸 Hatalar eşit değildir
Bir fraud transaction'ı kaçırmak (FN) ile bir normal transaction'a yanlış alarm üretmek (FP) aynı maliyette değildir. FN ortalama 2.500 TL fraud kaybı; FP ortalama 5 TL analist saati maliyet. Yani FN ≈ 500 × FP. Standart loss bunları eşit cezalandırır — yanlış. Bu derste loss function'ı maliyet bilinciyle eğitmenin tekniklerini göreceğiz.

Cost Matrix: Hatalar Para Birimine Çevrilir#

Klasik confusion matrix (2 sınıf):
Tahmin: 0Tahmin: 1
Gerçek: 0TNFP
Gerçek: 1FNTP
Cost matrix bu kutulara para birimi koyar:
Tahmin: 0Tahmin: 1
Gerçek: 00 TLC(FP)
Gerçek: 1C(FN)0 TL

Sektörel Cost Matrix Örnekleri#

Banking Kart Fraud

  • C(FN) = ortalama fraud tutarı ≈ 2.500 TL
  • C(FP) = analist 3 dakika × 2.000 TL/saat = 100 TL
  • Cost oranı: FN / FP ≈ 25

Network IDS

  • C(FN) = bir attack kaçma → 50.000 TL ortalama hasar
  • C(FP) = SOC analist 4 dakika → 130 TL
  • Cost oranı: ~385

Predictive Maintenance

  • C(FN) = bir bearing fail → 60.000 TL üretim kaybı
  • C(FP) = gereksiz bakım 2 saat = 8.000 TL
  • Cost oranı: ~7.5

Healthcare ECG

  • C(FN) = kalp krizi kaçırma — sınırsız (hayat değeri)
  • C(FP) = gereksiz inceleme 500 TL
  • Cost oranı: çok yüksek, asimetrik

Asıl Optimizasyon#

Cost matrix bilinince, optimal model toplam beklenen maliyeti minimize eder:
Bu aynı zamanda optimal eşik hesaplamayı sağlar. Bir model p(anomali|x) verirse:
Örn: C(FP)=100, C(FN)=2500. Eşik = 100/2600 ≈ 0.04 — yani p(anomali) > 0.04 olunca alarm. Eğer modelin default eşiği 0.5 ise, bu eşiği değiştirmemek ekonomik olarak yanlış.
python
import numpy as np
from sklearn.metrics import confusion_matrix
 
def expected_cost(y_true, y_pred, c_fn, c_fp):
"""Toplam beklenen maliyet."""
tn, fp, fn, tp = confusion_matrix(y_true, y_pred).ravel()
return fn * c_fn + fp * c_fp
 
def optimal_threshold(probs, y_true, c_fn=2500, c_fp=100):
"""Maliyeti minimize eden eşiği bul."""
thresholds = np.linspace(0.01, 0.99, 99)
costs = []
for t in thresholds:
y_pred = (probs >= t).astype(int)
costs.append(expected_cost(y_true, y_pred, c_fn, c_fp))
best_idx = np.argmin(costs)
return thresholds[best_idx], costs[best_idx]
 
# Örnek (bir modelin probability output'u varsayalım)
# best_t, best_c = optimal_threshold(model.predict_proba(X_val)[:,1], y_val)
Cost-aware threshold seçimi

Class Weight: En Basit Yaklaşım#

scikit-learn ve XGBoost class_weight parametresi sunar. Loss'u sınıf ağırlığıyla çarp:
Pratik formül: (w_1 = N/(2 \cdot N_1)), (w_0 = N/(2 \cdot N_0)).
from sklearn.utils.class_weight import compute_class_weight import numpy as np # Otomatik hesaplama weights = compute_class_weight('balanced', classes=np.unique(y), y=y) class_weight_dict = dict(zip(np.unique(y), weights)) print(class_weight_dict) # {0: 0.5, 1: 100} gibi # scikit-learn'de from sklearn.ensemble import RandomForestClassifier clf = RandomForestClassifier(class_weight='balanced', random_state=42) # Veya custom oran clf = RandomForestClassifier(class_weight={0: 1, 1: 100}, random_state=42) # XGBoost'ta import xgboost as xgb # scale_pos_weight = N_neg / N_pos n_pos, n_neg = (y==1).sum(), (y==0).sum() clf = xgb.XGBClassifier(scale_pos_weight=n_neg/n_pos, random_state=42)
Avantaj: Tek parametre, tüm sklearn modellerinde destekli. Dezavantaj: Tüm pozitif örneklere eşit ağırlık — "kolay" pozitiflere gereksiz ağırlık verir.

Focal Loss (Lin et al., 2017)#

Class weight'in zekice gelişmiş hali. RetinaNet (object detection)'tan geldi ama imbalanced AD'de yaygınlaştı.

Standard Cross Entropy:#

Burada (p_t = p) eğer y=1, (p_t = 1-p) eğer y=0.

Focal Loss:#

İki ek terim:
  1. (\alpha_t) — class weight (genelde (\alpha) pozitif için, (1-\alpha) negatif için)
  2. ((1-p_t)^\gamma)focusing parameter
Sezgi: model bir örneği zaten iyi sınıflandırıyorsa ((p_t) yüksek), loss'un katkısı azaltılır. Tersine, zor örnekler ((p_t) düşük) loss'a daha çok katkı yapar.
γ değeriEtki
0Standard CE (class weight ile)
1Hafif focusing
2Tipik standart (RetinaNet papers)
5Çok agresif focusing

Sezgisel Örnek#

Bir pozitif örnek için p=0.9 → focal loss = standart CE'nin 0.01 katı (γ=2) Bir pozitif örnek için p=0.1 → focal loss = standart CE'nin 0.81 katı (γ=2)
Yani modelin "kolay yaktığı" örnekler artık eğitime az katkı verir; "yanlış yaptıkları" daha çok katkı verir. Hard example mining otomatik.
python
import torch
import torch.nn as nn
import torch.nn.functional as F
 
class FocalLoss(nn.Module):
"""
Binary focal loss.
Lin et al., 2017 — RetinaNet.
"""
def __init__(self, alpha=0.25, gamma=2.0, reduction='mean'):
super().__init__()
self.alpha = alpha
self.gamma = gamma
self.reduction = reduction
 
def forward(self, logits, targets):
# logits: [N], targets: [N] (0 veya 1)
bce = F.binary_cross_entropy_with_logits(logits, targets.float(), reduction='none')
p = torch.sigmoid(logits)
p_t = p * targets + (1 - p) * (1 - targets)
alpha_t = self.alpha * targets + (1 - self.alpha) * (1 - targets)
 
focal_factor = (1 - p_t) ** self.gamma
loss = alpha_t * focal_factor * bce
 
if self.reduction == 'mean':
return loss.mean()
elif self.reduction == 'sum':
return loss.sum()
return loss
 
# Kullanım
focal = FocalLoss(alpha=0.25, gamma=2.0)
# logits = model(X)
# loss = focal(logits, y)
PyTorch'ta Focal Loss

Focal Loss Hyperparameter Tuning#

Veri imbalanceα (positive weight)γ (focusing)
1:100.5-0.71-2
1:1000.252
1:1.0000.12-3
1:10.000+0.053-5
Pratik: γ=2 standart başlangıç. α'yı veri imbalance'a göre ayarla. Çok agresif γ (>5) yakınsama problemi yapabilir.

Asymmetric Loss (Ben-Baruch et al., 2020)#

Focal loss'un yeni nesli. Pozitif ve negatif örnekler için farklı γ:
Burada (p_m = \max(p - m, 0)) — negatif örneklerde "margin" uygula.
Tipik tuning: (\gamma_+ = 0), (\gamma_- = 4), (m = 0.05).
Sezgi: Pozitif örneklere standart loss (her birine değer ver), ama negatif örneklere daha agresif focusing (kolay negatifleri loss'tan dışla).
Multi-label classification'da en çok kullanılan; binary AD'de de işe yarar.

Tversky Loss, Dice Loss (Vision AD için)#

Görsel anomaly detection'da (Modül 24) piksel seviyesi imbalance baskındır — defektsiz piksel ezici çoğunluk. Tversky loss bu duruma adapte:
α, β = FP/FN ağırlıkları. α=β=0.5 → Dice coefficient. α<β → Recall öncelikli.
Vision AD'de yaygın: defekt segmentation'da Tversky veya Focal Tversky loss standart. Modül 24'te detaylı.

Hangi Loss Ne Zaman?#

SenaryoÖnerilen Loss
1:10 imbalance, tabularClass weight + CE
1:100 imbalance, tabularFocal loss (γ=2)
1:1000+ imbalance, tabularFocal loss (γ=3) + class weight
Vision segmentationTversky veya Focal Tversky
Multi-label, tail-heavyAsymmetric loss
Compliance gerekli (yorumlanabilir)Class weight (basit, açıklanabilir)
Hızlı deney baselineXGBoost + scale_pos_weight

Karşılaştırma#

YöntemHyperparameterAçıklanabilirDL desteğiKlasik ML
Class weight1ÇokVarVar
Cost matrix + threshold2 (cost)ÇokManuelManuel
Focal loss2 (α, γ)OrtaVarYok
Asymmetric loss4 (α, γ±, m)AzVarYok
Tversky2 (α, β)OrtaVar (vision)Yok

Sampling vs Cost-Sensitive: Ne Zaman Hangisi?#

Bu iki yaklaşım rakip değil, tamamlayıcı:

Sampling İyidir#

  • Klasik ML modellerinde (RF, XGBoost, kNN)
  • Categorical feature ağırlıklı veride (SMOTE-NC)
  • Outlier'lar nadir ve "iyi tanımlı"

Cost-Sensitive İyidir#

  • Deep learning modellerinde
  • Maliyet açıkça hesaplanabiliyorsa
  • Eğitim seti çok büyük (oversampling pratik değil)
  • Adversarial fraud (sentetik veri attacker'ı yardımcı)

Hibrit Yaygın#

  • Hafif SMOTE (örn. 1:10'a çıkar) + class weight
  • DL'de: hafif oversampling + focal loss
  • Production'da bu hibrit en stabil sonuç verir
🎯 Üretim için en sağlam yaklaşım
(1) Sampling'i abartma — pozitif örnekleri 1:1'e değil 1:10'a getir; (2) Focal loss veya class_weight ekle; (3) Cost-aware threshold optimization yap; (4) PR-AUC ve cost metric'i ile değerlendir. Bu dörtlü Türkiye'deki en olgun fraud takımlarının default pipeline'ı.

Gerçek Vaka: Türk Bankası Fraud Pipeline 2024#

Bir büyük Türk bankası 2024'te yaptığı pipeline güncellemesi:
Önce: XGBoost + SMOTE 1:1
  • Train accuracy: %99.8
  • Test PR-AUC: 0.42
  • Üretim alarm rate: %0.4 (false positive %85)
Güncelleme adımları:
  1. SMOTE'u 1:1 → 1:5'e düşür (daha gerçekçi)
  2. XGBoost
    scale_pos_weight=200
    ekle (cost-aware)
  3. Threshold'u cost-aware optimize et (FN/FP = 30:1 oranı)
  4. Validation PR-AUC ve cost metric ile model seç
Sonra: Aynı XGBoost, sadece loss & threshold ayarı
  • Test PR-AUC: 0.57 (+%36)
  • Üretim alarm rate: %0.18 (false positive %62)
  • Aylık fraud kaybı: -1.4M TL
  • Aylık analist saat: -180K TL (alarm azaldı)
Modeli değiştirmeden, sadece loss function & threshold mühendisliği bu kadar fark yarattı. Bu ders bunun sırrı.
👉 Bir sonraki ders
Ders 3.4 — Weak Supervision & Snorkel. Etiket azlığında 'programmatic labeling': domain expert kuralları → otomatik etiket. Snorkel framework, label model, Cleanlab ile etiket düzeltme. Healthcare ve insurance fraud'da kritik.

Frequently Asked Questions

γ=2 standart başlangıç. Validation set'inde PR-AUC'a göre 1, 2, 3 dene. γ çok büyük (>5) model yakınsamasını yavaşlatır. Veri 1:10.000+ imbalanced ise γ=3-4 deneyebilirsin.

Yorumlar & Soru-Cevap

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

Related Content