pytorch分割损失函数
该摘要总结了以下内容:
Focal Loss:一种用于解决类别不平衡问题的损失函数,通过调整alpha和gamma参数来增强对难分类样本的重视。
交叉熵损失函数:
- 单通道输出:使用sigmoid激活函数将预测值压缩到0-1之间,并与标签进行二进制交叉熵计算。
- 多通道输出:使用softmax激活函数将预测值转换为概率分布,并通过one-hot编码与标签计算交叉熵。
实现细节:- 使用PyTorch中的torch.nn.BCELoss()和torch.nn.BCEWithLogitsLoss()分别实现二进制交叉熵损失。
- 对于多通道情况,通过torch.nn.CrossEntropyLoss()实现分类任务的交叉熵计算。
结论:交叉熵损失函数能够有效地将多分类问题转化为二分类问题进行处理,并且支持单通道或多通道的语义分割任务。
1. focal loss
https://www.cnblogs.com/gshang/p/13922008.html
class FocalLoss(nn.Module):
def __init__(self, alpha=1, gamma=2, logits=False, reduce=True):
super(FocalLoss, self).__init__()
self.alpha = alpha
self.gamma = gamma
self.logits = logits
self.reduce = reduce
def forward(self, inputs, targets):
if self.logits:
BCE_loss = F.binary_cross_entropy_with_logits(inputs, targets, reduce=False)
else:
BCE_loss = F.binary_cross_entropy(inputs, targets, reduce=False)
pt = torch.exp(-BCE_loss)
F_loss = self.alpha * (1-pt)**self.gamma * BCE_loss
if self.reduce:
return torch.mean(F_loss)
else:
return F_loss
https://www.cnblogs.com/gshang/p/13887133.html
交叉-熵损失函数
其工作原理及其数学推导过程可查阅资料[该博客文章]。其计算公式如下:
CE(p,q)=−p∗log(q)CE(p,q)=−p∗log(q)
其中 qq 为预测的概率,q∈[0,1]q∈[0,1], pp 为标签,p∈{0,1}p∈{0,1}。
交叉熵损失函数用于衡量分类模型的预测结果与真实标签之间的差异,在机器学习算法中具有重要的应用价值。具体而言,在概率空间中考虑各分类之间的概率分布时,在实际应用中通常会采用one-hot编码方案将类别标签转换为相应的数值表示。这些概率值的具体计算方法是通过求取各分类之间对应概率值与真实标签之间差异的具体数值来实现的。值得注意的是,在这种设定下,各个类别的概率值总和恒等于1;而针对每个具体的样本标签,在实际应用中则需要将其转化为唯一对应的数值形式以供模型处理
单通道输出时的交叉熵损失计算
[

](https://img2020.cnblogs.com/blog/1489774/202010/1489774-20201028213241722-1593302099.png)
单通道输出交叉熵损失计算示意图
首先,假设我们研究的是一个二分类语义分割问题。
该段改写后的内容:
网络输入由尺寸为2x2的二维图像构成,并配置batch_size为数值2。该网络能够生成具有一个通道的特征图作为输出。同时,该网络对应的输出用于监督学习的是一个同样尺寸为2×2的二进制掩模图(仅包含0和1值的一维通道图像)。
我们在 pytorch 中将其定义:
python
import torch
# 假设输出一个 [batch_size=2, channel=1, height=2, width=2] 格式的张量 x1
x1 = torch.tensor(
[[[[ 0.43, -0.25],
[-0.32, 0.69]]],
[[[-0.29, 0.37],
[0.54, -0.72]]]])
# 假设标签图像为与 x1 同型的张量 y1
y1 = torch.tensor(
[[[[0., 0.],
[0., 1.]]],
[[[0., 0.],
[1., 1.]]]])
在进行交叉熵前,首先需要做一个 sigmoid 操作,将数值压缩到0到1之间:
python
# 根据二进制交叉熵的计算过程
# 首先进行sigmoid计算,然后与标签图像进行二进制交叉熵计算,最后取平均值,即为损失值
# 1. sigmoid
s1 = torch.sigmoid(x1)
s1
'''
out:
tensor([[[[0.6059, 0.4378],
[0.4207, 0.6660]]],
13. 14. [[[0.4280, 0.5915],
[0.6318, 0.3274]]]]
'''
对图像进行交叉熵损失计算后发现,在该过程会针对每个像素位置计算损失值的基础上因此需要对所有像素的损失值取平均
python
# 2.交叉熵计算
loss_cal = -1*(y1*torch.log(s1)+(1-y1)*torch.log(1-s1)) # 此处相当于一个one-hot编码
loss_cal_mean = torch.mean()
loss_cal_mean
'''
out:
tensor(0.6861)
'''
为了验证结果,我们使用 pytorch 自带的二进制交叉熵损失函数计算:
python
# 使用torch自带的二进制交叉熵计算
loss_bce = torch.nn.BCELoss()(s1,y1)
loss_bce
'''
out:
tensor(0.6861)
'''
在不执行 sigmoid 操作的情况下,PyTorch 仍然提供了包含此操作的二进制交叉熵损失函数。
python
# 使用带sigmoid的二进制交叉熵计算
loss_bce2 = torch.nn.BCEWithLogitsLoss()(x1,y1)
loss_bce2
'''
out:
tensor(0.6861)
'''
可以看到,我们使用了三种方式,计算了交叉熵损失,结果一致。
多通道输出时的交叉熵损失计算
[

](https://img2020.cnblogs.com/blog/1489774/202010/1489774-20201028213333640-1806222673.png)
多通道输出交叉熵损失计算示意图
首先,假设我们研究的是一个二分类语义分割问题。
该网络接收一个二维图像 ... ,其中 batch\_size 被设定为 ... 。其输出特征图为双通道结构;而其标签则由大小为 ... 的 0-1 掩膜构成。
我们在 pytorch 中将其定义:
python
# 假设输出一个[batch_size=2, channel=2, height=2, width=2]格式的张量 x1
x1 = torch.tensor([[[[ 0.3164, -0.1922],
[ 0.4326, -1.2193]],
[[ 0.6873, 0.6838],
[ 0.2244, 0.5615]]],
[[[-0.2516, -0.8875],
[-0.6289, -0.1796]],
[[ 0.0411, -1.7851],
[-0.3069, -1.0379]]]])
# 假设标签图像为与x1同型,然后去掉channel的张量 y1 (注意两点,channel没了,格式为LongTensor)
y1 = torch.LongTensor([[[0., 1.],
[1., 0.]],
[[1., 1.],
[0., 1.]]])
在交叉熵计算之前,在计算过程中首先要执行一个Softmax运算,在输入值的基础上将其映射至0至1的范围内,并确保各通道输出的总和等于1。
python
# 1.softmax
s1 = torch.softmax(x1,dim=1)
s1
'''
out:
tensor([[[[0.4083, 0.2940],
[0.5519, 0.1442]],
10. [[0.5917, 0.7060],
[0.4481, 0.8558]]],
13. 14. [[[0.4273, 0.7105],
[0.4202, 0.7023]],
17. [[0.5727, 0.2895],
[0.5798, 0.2977]]]])
'''
对于标签图而言,在其张量形状与网络输出张量不一致的情况下,则必须执行一次one-hot 转换。
一种one-hot编码是什么?请参考这篇博文:
python
# 2.one-hot
y1_one_hot = torch.zeros_like(x1).scatter_(dim=1,index=y1.unsqueeze(dim=1),src=torch.ones_like(x1))
y1_one_hot
'''
out:
tensor([[[[1., 0.],
[0., 1.]],
10. [[0., 1.],
[1., 0.]]],
13. 14. [[[0., 0.],
[1., 0.]],
17. [[1., 1.],
[0., 1.]]]])
'''
在这里深入理解这个scatter_函数至关重要。它在执行过程中承担着至关重要的角色,在一维(one-hot)转换操作中被广泛应用。具体而言,在一维转换过程中,默认情况下会将一个全为1的矩阵中的元素按照特定规则进行复制到另一个全为零矩阵中对应的位置上。其中的选择依据是我们所使用的标签图,在该函数中通过参数dim来决定如何确定各个维度上的连接关系,在该函数中通过参数index来决定连接的具体位置和通道选择。
按照交叉熵定义,继续计算:
python
# 交叉熵计算
loss_cal = -1 *(y1_one_hot * torch.log(s1))
loss_cal_mean = loss_cal.sum(dim=1).mean() # 在batch维度下计算每个样本的交叉熵
loss_cal_mean
'''
out:
tensor(0.9823)
'''
我们也可以使用 pytorch 自带的交叉熵损失函数计算:
python
loss_ce = torch.nn.CrossEntropyLoss()(x1,y1)
loss_ce
'''
tensor(0.9823)
'''
可以看到,两种方式结果一样。
结论
交叉熵本质上聚焦于单个对象的研究,在这种简化下将复杂问题转化为二元分类任务。通过公式将标签与预测概率关联起来,并在此基础上计算损失值。针对每个样本都能计算出对应的损失值,在此基础上取所有样本的平均损失作为整个数据集的整体评估指标。
无论是sigmoid函数还是softmax函数,在数据处理过程中都实现了将输入数据映射到0到1区间以形成概率值的目标。其中sigmoid函数专为二分类任务设计,在计算每个样本的概率时会直接输出结果;而对于多分类问题,则通过softmax函数能够生成多个指标并保证总和为1的特点来实现对各类别的概率预测。在多分类场景下关注单一类别时,其余类别概率则通过从1中减去该类别概率获得。
对于二分类语义分割问题,其实采用上述两种方式都是可以的。
