其他分享
首页 > 其他分享> > 代码笔记13 语义分割交叉熵的实现(去除背景类)

代码笔记13 语义分割交叉熵的实现(去除背景类)

作者:互联网

记录

  算是自己一点点小小的记录,以前很少看开源的代码,都自己闷头写,最后才发现自己写的就是shi。不看不学不练啊,读开源代码不代表不自己造轮子,而是要学会别人编程的思想并学习,自己检讨我自己。
  最近确实压力很大,想把这篇文章水出来,可是一来没有人带,导师是拉项目大师,只会分配杂活,教你是不可能的,因为他自己也不会。师兄们也在这样的环境下很佛系,反正也没毕业要求,没人想着写文章或者做这方面的研究。找了这个方向别的老师,也就是私下里请教几个问题,毕竟不是亲导师,能回复回复你帮帮你都算不错了。这几个月学了很多也走了很多坑很多弯路,没有人理解这种感觉吧。还是感谢我的妈妈和女朋友,总能在精神上支持我,不过我这还没搞定呢哈哈哈哈。等到弄得差不多就把学到的东西总结一下慢慢发上来。

2d交叉熵

  这个我就不多说了,可以上网搜,这主要是一种信息熵来衡量信息差别的。主要问题在于,对于室内语义分割中,很大的情况我们是不需要背景类的,例如SUN-RGBD和NYU中都含有0(ignored)这一类,这一类本身不能作为一类算进损失函数中。
  pytorch中封装的交叉熵函数中1

CLASStorch.nn.CrossEntropyLoss(weight=None, size_average=None, ignore_index=- 100, reduce=None, reduction='mean', label_smoothing=0.0)

其中有ignored_index这一项,但其实不是我们所需要的,但我们可以通过这种方式一样实现这种功能,后续我会展示

实现方法

我一共展现了三种方法,我将完整的代码以及测试结果写在下面。
第一种是我写的,很垃圾,我的想法是将二维的特征图打成一维的,然后筛除0类别再进行交叉熵。
其实二三中都是,先做二位交叉熵,算出来每个像素的交叉熵值后,再根据label进行筛检。
第二种方法来源于github2
亏我当初写了快一天,比起来垃圾多了。。。

import torch
import torch.nn as nn
import torch.nn.functional as F

class CrossEntropyLoss1(nn.Module):
    def __init__(self, classesnum, device):
        super().__init__()
        # utilize weight for different classes in Loss function, the Loss made from the proportion of the pixel classes

        # self.weight = torch.FloatTensor([0.9627, 6.31385, 2.3358, 1.0000, 0.34482, 0.23155, 0.26256, 1.69797, 1.43697,  1.12408, 6.11624, 0.14348, 0.71114])
        self.weight = torch.FloatTensor([0.9627, 6.31385, 2.3358])
        self.weight = self.weight.to(device=device)
        self.crossentropyloss = nn.CrossEntropyLoss(weight=self.weight,size_average=False, reduce=False)
        # self.crossentropyloss = nn.CrossEntropyLoss()
        self.classesnum = classesnum


    def forward(self, score, label):
        B, C, W, H = score.size()
        label = label - 1
        # ignore the class 0,which is the background in sunrgbd,and it does not supposed to be one class
        label_mask = (label != -1)  #find background pixel,True for not, False for is
        score_mask = label_mask.expand(C, B, W, H).permute(1, 0, 2, 3) #expand it to the size of score maps, for exacting not background pixels

        for i in range(int(B)):
            per_label = label[i, :, :]
            per_score = score[i, :, :, :]
            per_labelmask = label_mask[i, :, :]
            per_scoremask = score_mask[i, :, :, :]
            per_label_ej = per_label[per_labelmask]
            per_score_ej = per_score[per_scoremask].reshape(self.classesnum, -1)
            if i == 0:
                lab_ej = per_label_ej
                score_ej = per_score_ej
            elif i > 0:
                lab_ej = torch.cat([lab_ej, per_label_ej], dim=0)
                score_ej = torch.cat([score_ej, per_score_ej], dim=1)

        lab_ej = lab_ej.unsqueeze(dim=0)
        score_ej = score_ej.unsqueeze(dim=0)
        t, pixelsum = lab_ej.size()

        loss = self.crossentropyloss(score_ej, lab_ej)

        input = F.softmax(score_ej, dim=1)
        arg_max = torch.argmax(input, dim=1)
        accurate = torch.sum(arg_max == lab_ej)
        accurate = accurate.item()

        return loss, pixelsum, accurate

class CrossEntropyLoss2(nn.Module):
    def __init__(self):
        super(CrossEntropyLoss2, self).__init__()
        self.weight = torch.FloatTensor([0.9627, 6.31385, 2.3358])

        self.ce_loss = nn.CrossEntropyLoss(weight = self.weight,
                                           size_average=False, reduce=False)

    def forward(self, inputs, targets):
        mask = targets > 0
        targets_m = targets.clone()
        targets_m[mask] -= 1
        loss_all = self.ce_loss(inputs, targets_m.long())
        loss_all = torch.masked_select(loss_all, mask)
        return loss_all

if __name__ == '__main__':
    loss1 = CrossEntropyLoss1(classesnum=3,device='cpu')
    loss2 = CrossEntropyLoss2()
    loss3 = nn.CrossEntropyLoss(weight=torch.FloatTensor([0.9627, 6.31385, 2.3358]),ignore_index=-1, size_average=False, reduce=False)
    label = torch.tensor([[[1,0,2,3],
                          [0,3,3,2],
                          [1,1,0,2],
                          [3,2,0,2]]])
    score = torch.randn([1,3,4,4])

    crent1 = loss1(score,label)
    crent2 = loss2(score,label)
    crent3 = loss3(score,label-1)

    print(crent1,'\n',crent2,'\n',crent3)


结果可见

(tensor([[ 2.0845,  6.6480,  1.6856,  4.9340,  2.6181,  7.4988,  1.6213,  3.3337,
          3.8872,  4.4253, 16.8919, 10.6067]]), 12, 2) 
 tensor([ 2.0845,  6.6480,  1.6856,  4.9340,  2.6181,  7.4988,  1.6213,  3.3337,
         3.8872,  4.4253, 16.8919, 10.6067]) 
 tensor([[[ 2.0845,  0.0000,  6.6480,  1.6856],
         [ 0.0000,  4.9340,  2.6181,  7.4988],
         [ 1.6213,  3.3337,  0.0000,  3.8872],
         [ 4.4253, 16.8919,  0.0000, 10.6067]]])

可见结果是一样的,不过ignored_index的方式是需要为负数的,这个通过lable-1就可以做到,而且出来的值其实是将ignored值输出为0了,后一步还需要做非零点数的计算取平均值

Refrences

[1] https://pytorch.org/docs/stable/generated/torch.nn.CrossEntropyLoss.html
[2] https://github.com/JindongJiang/RedNet

标签:13,ej,self,torch,语义,per,label,score,去除
来源: https://www.cnblogs.com/HumbleHater/p/16367135.html