summaryrefslogtreecommitdiff
path: root/utils.py
diff options
context:
space:
mode:
authorVineet Kumar <git@vineetk.net>2026-04-20 10:03:17 -0400
committerVineet Kumar <git@vineetk.net>2026-04-20 10:03:17 -0400
commit9dd8b3007e79f0634f868d405f5e7c48917305fd (patch)
treeb2dd74419a78b28190664f482b30263879742d7a /utils.py
parent8f571ee51854540d186aafc01d25908bba5ea09e (diff)
improve training for class-imbalanced DR classification
- WeightedRandomSampler for balanced class sampling during training - FocalLoss (gamma=2) for hard-example mining, no alpha (sampler handles balance) - Stronger augmentation: RandomResizedCrop, ColorJitter, GaussianBlur, 30deg rotation - Discriminative LR: backbone at 0.1x, head at 1x - 3-epoch linear warmup + cosine decay scheduler - Early stopping on macro F1 instead of weighted F1 - Updated defaults: batch_size=64, epochs=50, patience=10 - Comparison chart now shows both macro and weighted F1 - DataParallel checkpoint handling (strip module. prefix) - Enable cudnn.benchmark for training speed
Diffstat (limited to 'utils.py')
-rw-r--r--utils.py30
1 files changed, 28 insertions, 2 deletions
diff --git a/utils.py b/utils.py
index a836ca2..90c79d5 100644
--- a/utils.py
+++ b/utils.py
@@ -1,6 +1,8 @@
1import random 1import random
2import numpy as np 2import numpy as np
3import torch 3import torch
4import torch.nn as nn
5import torch.nn.functional as F
4 6
5CLASS_NAMES = [ 7CLASS_NAMES = [
6 'Healthy (0)', 8 'Healthy (0)',
@@ -18,8 +20,7 @@ def set_seed(seed: int = 42) -> None:
18 np.random.seed(seed) 20 np.random.seed(seed)
19 torch.manual_seed(seed) 21 torch.manual_seed(seed)
20 torch.cuda.manual_seed_all(seed) 22 torch.cuda.manual_seed_all(seed)
21 torch.backends.cudnn.deterministic = True 23 torch.backends.cudnn.benchmark = True
22 torch.backends.cudnn.benchmark = False
23 24
24 25
25def compute_class_weights(labels) -> torch.FloatTensor: 26def compute_class_weights(labels) -> torch.FloatTensor:
@@ -35,3 +36,28 @@ def get_device() -> torch.device:
35 if torch.cuda.is_available(): 36 if torch.cuda.is_available():
36 return torch.device('cuda') 37 return torch.device('cuda')
37 return torch.device('cpu') 38 return torch.device('cpu')
39
40
41class FocalLoss(nn.Module):
42 """Focal loss with optional per-class alpha weighting.
43
44 Focuses learning on hard, misclassified examples by down-weighting
45 easy examples (high pt). Good for class imbalance in medical imaging.
46
47 Args:
48 alpha: Per-class weight tensor (same shape as class weights for CE).
49 If None, no per-class weighting is applied.
50 gamma: Focusing parameter. gamma=0 reduces to standard CE.
51 gamma=2 is the standard value from the RetinaNet paper.
52 """
53
54 def __init__(self, alpha=None, gamma: float = 2.0):
55 super().__init__()
56 self.alpha = alpha
57 self.gamma = gamma
58
59 def forward(self, inputs: torch.Tensor, targets: torch.Tensor) -> torch.Tensor:
60 ce_loss = F.cross_entropy(inputs, targets, weight=self.alpha, reduction='none')
61 pt = torch.exp(-ce_loss)
62 focal_loss = ((1.0 - pt) ** self.gamma) * ce_loss
63 return focal_loss.mean()