VizRef / src /training /losses.py
chenx906's picture
Add model and inference code
18a82fb
Raw
History Blame Contribute Delete
4.14 kB
import torch
import torch.nn as nn
import torch.nn.functional as F
class LabelSmoothingCrossEntropy(nn.Module):
"""
Cross entropy loss with label smoothing
Helps prevent overconfidence in predictions
"""
def __init__(self, smoothing=0.1):
super().__init__()
self.smoothing = smoothing
self.confidence = 1.0 - smoothing
def forward(self, pred, target):
pred = pred.log_softmax(dim=-1)
with torch.no_grad():
true_dist = torch.zeros_like(pred)
true_dist.fill_(self.smoothing / (pred.size(-1) - 1))
true_dist.scatter_(1, target.data.unsqueeze(1), self.confidence)
return torch.mean(torch.sum(-true_dist * pred, dim=-1))
class FocalLoss(nn.Module):
"""
Focal Loss for addressing class imbalance
Focuses learning on hard examples
"""
def __init__(self, alpha=1, gamma=2, reduction='mean'):
super().__init__()
self.alpha = alpha
self.gamma = gamma
self.reduction = reduction
def forward(self, inputs, targets):
ce_loss = F.cross_entropy(inputs, targets, reduction='none')
pt = torch.exp(-ce_loss)
focal_loss = self.alpha * (1 - pt) ** self.gamma * ce_loss
if self.reduction == 'mean':
return focal_loss.mean()
elif self.reduction == 'sum':
return focal_loss.sum()
else:
return focal_loss
class MixUpCrossEntropy(nn.Module):
"""
Loss function for MixUp augmentation
Computes loss for mixed targets
"""
def __init__(self):
super().__init__()
def forward(self, pred, y_a, y_b, lam):
loss_a = F.cross_entropy(pred, y_a)
loss_b = F.cross_entropy(pred, y_b)
return lam * loss_a + (1 - lam) * loss_b
class CutMixCrossEntropy(nn.Module):
"""
Loss function for CutMix augmentation
Similar to MixUp but for CutMix
"""
def __init__(self):
super().__init__()
def forward(self, pred, y_a, y_b, lam):
loss_a = F.cross_entropy(pred, y_a)
loss_b = F.cross_entropy(pred, y_b)
return lam * loss_a + (1 - lam) * loss_b
class WeightedCrossEntropy(nn.Module):
"""
Weighted cross entropy for handling class imbalance
"""
def __init__(self, class_weights=None):
super().__init__()
self.class_weights = class_weights
def forward(self, inputs, targets):
if self.class_weights is not None:
weight = self.class_weights.to(inputs.device)
else:
weight = None
return F.cross_entropy(inputs, targets, weight=weight)
def get_loss_function(loss_name='cross_entropy', **kwargs):
"""
Factory function to get loss function by name
Args:
loss_name: Name of the loss function
**kwargs: Additional arguments for the loss function
Returns:
Loss function instance
"""
loss_dict = {
'cross_entropy': nn.CrossEntropyLoss,
'label_smoothing': LabelSmoothingCrossEntropy,
'focal': FocalLoss,
'weighted_ce': WeightedCrossEntropy,
'mixup': MixUpCrossEntropy,
'cutmix': CutMixCrossEntropy,
}
if loss_name not in loss_dict:
raise ValueError(f"Unknown loss function: {loss_name}")
return loss_dict[loss_name](**kwargs)
# Example usage for calculating class weights
def calculate_class_weights(dataset):
"""
Calculate class weights for weighted loss
Args:
dataset: Dataset instance with label information
Returns:
torch.Tensor: Class weights
"""
from collections import Counter
# Count labels
labels = []
for _, label, _ in dataset:
labels.append(label)
# Calculate weights (inverse frequency)
label_counts = Counter(labels)
total = len(labels)
num_classes = len(label_counts)
weights = []
for i in range(num_classes):
count = label_counts.get(i, 1) # Avoid division by zero
weight = total / (num_classes * count)
weights.append(weight)
return torch.tensor(weights, dtype=torch.float32)