本文参考了:添加链接描述
1:首先看一下交叉熵的公式:
2:这里我们考虑图片交叉熵损失:
2.1:以语义分割为例:模型的output的维度为(B,C,H,W),假如有四十个类别,那么(1,40,256,256)。图片的标签即label即真实值即groundtruth维度为(1,1,256,256)。
2.2:在代码中:输入和标签直接输入到nn.CrossEntropyLoss中。那么两个维度都不一样是如何计算的呢?
class CrossEntropyLoss2d(nn.Module):
def __init__(self, device, weight):
super(CrossEntropyLoss2d, self).__init__()
self.weight = torch.tensor(weight).to(device)
self.num_classes = len(self.weight) + 1 # +1 for void
if self.num_classes < 2**8:
self.dtype = torch.uint8
else:
self.dtype = torch.int16
self.ce_loss = nn.CrossEntropyLoss(
torch.from_numpy(np.array(weight)).float(),
reduction='none',
ignore_index=-1
)
self.ce_loss.to(device)
def forward(self, inputs_scales, targets_scales):
losses = []
for inputs, targets in zip(inputs_scales, targets_scales):
# mask = targets > 0
targets_m_1 = targets.clone() #深拷贝
targets_m = targets_m_1-1 #减去
# targets_to_one_hot = torch.nn.functional.one_hot(targets_m.to(torch.int64)) #值为-1的样本不参与计算
loss_all = self.ce_loss(inputs, targets_m.long())
number_of_pixels_per_class = \
torch.bincount(targets.flatten().type(self.dtype),
minlength=self.num_classes)
divisor_weighted_pixel_sum = \
torch.sum(number_of_pixels_per_class[1:] * self.weight) # without void
losses.append(torch.sum(loss_all) / divisor_weighted_pixel_sum)
# losses.append(torch.sum(loss_all) / torch.sum(mask.float()))
return losses
经过查看发现:交叉熵会将输入的通道维度进行压缩。且会对label进行one-hot编码。添加链接描述
3:那么是如何进行one-hot编码的呢?
标签:(1,5,5)
假如我们的pred:有四个通道即四个类别。(1,4,5,5)
那我们标签进行编码也有四个通道:(1,5,5,4)。他就是将每一行的类别进行one-hot编码。
我们进行reshape到与pred一样大小。(1,4,5,5)
经过one-hot编码之后的标签:(1,4,5,5)
那么我们的损失就可以计算为:-1/n(0*log(0.7497)+…),当pred预测为负值,则不参与计算。一般经过softmax之后的概率是不为负的,这里是随机生成的数据有负值。