文章目录[隐藏]
「语义分割」介绍
语义分割是计算机视觉领域中的一项任务,旨在将图像中的每个像素分类为不同的语义类别。与对象检测任务不同,语义分割不仅需要识别图像中的物体,还需要对每个像素进行分类,从而实现对图像的细粒度理解和分析。
语义分割可以被看作是像素级别的图像分割,其目标是为图像中的每个像素分配一个特定的语义类别标签。每个像素都被视为图像的基本单位,因此语义分割可以提供更详细和准确的图像分析结果。
「语义分割」vs「分类」
- 在语义分割任务中,由于需要对每个像素进行分类,因此需要使用像素级别的损失函数。
- 语义分割任务中,图像中各个类别的像素数量通常不均衡,例如背景像素可能占据了大部分。
- 语义分割任务需要对图像中的每个像素进行分类,同时保持空间连续性。
「语义分割」损失函数
Dice Loss
Dice Loss(Dice损失)是一种常用的损失函数,主要用于语义分割任务中,衡量模型生成分割结果与真实分割标签之间的相似度。它基于Dice系数(Dice coefficient),也称为Sørensen-Dice系数,用于度量两个集合的相似性。
Dice Loss的取值范围为0到1之间。当预测结果与真实标签完全一致时,Dice Loss为0;当两者完全不一致时,Dice Loss为1。因此,Dice Loss越小表示模型的分割结果与真实标签越相似,表示模型性能越好。
class DiceLoss(nn.Module):
def __init__(self, weight=None, size_average=True):
super(DiceLoss, self).__init__()
def forward(self, inputs, targets, smooth=1):
#comment out if your model contains a sigmoid or equivalent activation layer inputs = F.sigmoid(inputs)
#flatten label and prediction tensors inputs = inputs.view(-1)
targets = targets.view(-1)
intersection = (inputs * targets).sum()
dice = (2.*intersection + smooth)/(inputs.sum() + targets.sum() + smooth)
return 1 - dice
BCE-Dice Loss
BCE-Dice Loss是将Dice Loss和标准的二元交叉熵(Binary Cross-Entropy, BCE)损失结合在一起的一种损失函数,通常用于分割模型中。
BCE-Dice Loss的优点在于,它结合了两种不同的损失函数,可以综合考虑像素级别的分类准确性(通过BCE损失)和分割结果的相似性(通过Dice Loss)。BCE损失在训练初期具有较好的稳定性,有助于加快模型的收敛速度。而Dice Loss则更关注于像素级别的相似性,可以促使模型生成更平滑和连续的分割结果。
class DiceBCELoss(nn.Module):
def __init__(self, weight=None, size_average=True):
super(DiceBCELoss, self).__init__()
def forward(self, inputs, targets, smooth=1):
#comment out if your model contains a sigmoid or equivalent activation layer inputs = F.sigmoid(inputs)
#flatten label and prediction tensors inputs = inputs.view(-1)
targets = targets.view(-1)
intersection = (inputs * targets).sum()
dice_loss = 1 - (2.*intersection + smooth)/(inputs.sum() + targets.sum() + smooth)
BCE = F.binary_cross_entropy(inputs, targets, reduction='mean')
Dice_BCE = BCE + dice_loss
return Dice_BCE
Jaccard/Intersection over Union (IoU) Loss
Jaccard Loss,也称为Intersection over Union (IoU) Loss,是一种常用的损失函数,用于语义分割任务中评估模型的分割结果与真实分割标签之间的相似性。它基于Jaccard指数(Jaccard Index),也称为IoU指标,用于度量两个集合之间的重叠程度。
Jaccard Loss的取值范围为0到1之间。当预测结果与真实标签完全一致时,Jaccard Loss为0;当两者完全不一致时,Jaccard Loss为1。因此,Jaccard Loss越小表示模型的分割结果与真实标签越相似,表示模型性能越好。
class IoULoss(nn.Module):
def __init__(self, weight=None, size_average=True):
super(IoULoss, self).__init__()
def forward(self, inputs, targets, smooth=1):
#comment out if your model contains a sigmoid or equivalent activation layer inputs = F.sigmoid(inputs)
#flatten label and prediction tensors inputs = inputs.view(-1)
targets = targets.view(-1)
#intersection is equivalent to True Positive count #union is the mutually inclusive area of all labels & predictions intersection = (inputs * targets).sum()
total = (inputs + targets).sum()
union = total - intersection
IoU = (intersection + smooth)/(union + smooth)
return 1 - IoU
Focal Loss
Focal Loss是一种用于解决分类任务中类别不平衡问题的损失函数。它被广泛用于目标检测任务中,特别是在处理少数类别样本较多的情况下。
Focal Loss基于交叉熵损失进行扩展,将样本的权重进行动态调整。与交叉熵损失函数相比,Focal Loss引入了一个衰减因子 ,其中 是预测的概率值。这个衰减因子能够使得易分类的样本( 较高)的权重降低,从而减少对分类正确样本的贡献。
ALPHA = 0.8
GAMMA = 2
class FocalLoss(nn.Module):
def __init__(self, weight=None, size_average=True):
super(FocalLoss, self).__init__()
def forward(self, inputs, targets, alpha=ALPHA, gamma=GAMMA, smooth=1):
#comment out if your model contains a sigmoid or equivalent activation layer inputs = F.sigmoid(inputs)
#flatten label and prediction tensors inputs = inputs.view(-1)
targets = targets.view(-1)
#first compute binary cross-entropy BCE = F.binary_cross_entropy(inputs, targets, reduction='mean')
BCE_EXP = torch.exp(-BCE)
focal_loss = alpha * (1-BCE_EXP)gamma * BCE
return focal_loss
Tversky Loss
Tversky Loss的设计灵感来自Tversky指数(Tversky index),它是一种用于度量集合之间相似性的指标。Tversky Loss使用了两个常数(alpha和beta),用于调整在损失函数中对误分类的惩罚程度。
当alpha=beta=0.5时,Tversky指数简化为Dice系数,该系数也等于F1得分。当alpha=beta=1时,公式转化为Tanimoto系数,而当alpha+beta=1时,得到一组F-beta得分。
ALPHA = 0.5
BETA = 0.5
class TverskyLoss(nn.Module):
def __init__(self, weight=None, size_average=True):
super(TverskyLoss, self).__init__()
def forward(self, inputs, targets, smooth=1, alpha=ALPHA, beta=BETA):
#comment out if your model contains a sigmoid or equivalent activation layer inputs = F.sigmoid(inputs)
#flatten label and prediction tensors inputs = inputs.view(-1)
targets = targets.view(-1)
#True Positives, False Positives & False Negatives TP = (inputs * targets).sum()
FP = ((1-targets) * inputs).sum()
FN = (targets * (1-inputs)).sum()
Tversky = (TP + smooth) / (TP + alpha*FP + beta*FN + smooth)
return 1 - Tversky
Lovasz Hinge Loss
Lovasz Hinge Loss的设计思想是,在计算IoU得分之前,根据预测误差对预测结果进行排序,然后累积计算每个误差对IoU得分的影响。然后,将该梯度向量与初始误差向量相乘,以最大程度地惩罚降低IoU得分的预测结果。
https://github.com/bermanmaxim/LovaszSoftmax
Combo Loss
Combo Loss的设计目标是处理多器官分割任务中输入和输出不平衡的情况。它综合了Dice Loss和修改后的交叉熵损失函数,以平衡对假阳性和假阴性的惩罚。该损失函数具有额外的常数,用于调整对错误预测的惩罚程度。
ALPHA = 0.5 # < 0.5 penalises FP more, > 0.5 penalises FN more CE_RATIO = 0.5 #weighted contribution of modified CE loss compared to Dice loss class ComboLoss(nn.Module):
def __init__(self, weight=None, size_average=True):
super(ComboLoss, self).__init__()
def forward(self, inputs, targets, smooth=1, alpha=ALPHA, beta=BETA, eps=1e-9):
#flatten label and prediction tensors inputs = inputs.view(-1)
targets = targets.view(-1)
#True Positives, False Positives & False Negatives intersection = (inputs * targets).sum()
dice = (2. * intersection + smooth) / (inputs.sum() + targets.sum() + smooth)
inputs = torch.clamp(inputs, eps, 1.0 - eps)
out = - (ALPHA * ((targets * torch.log(inputs)) + ((1 - ALPHA) * (1.0 - targets) * torch.log(1.0 - inputs))))
weighted_ce = out.mean(-1)
combo = (CE_RATIO * weighted_ce) - ((1 - CE_RATIO) * dice)
return combo
损失函数选择方法
-
任务需求:根据特定的分割任务的需求和特点,选择适合的损失函数。例如,对于类别不平衡的数据集,可以考虑使用Tversky Loss或Combo Loss等能够处理不平衡情况的损失函数。
-
实验评估:在实验中,使用不同的损失函数进行训练,并评估它们在验证集或测试集上的性能。比较它们在IoU、准确率、召回率等指标上的表现,选择性能最佳的损失函数。
-
超参数调整:一些损失函数具有额外的超参数,如Tversky Loss中的alpha和beta,可以通过调整这些超参数来进一步优化损失函数的性能。