DRRG:Deep Relational Reasoning Graph Network for Arbitrary Shape Text Detection:代码解读(loss)

前言

今天继续写代码解读,下面就是loss损失函数的解读,让我们一起学习,至于损失函数都哟什么,大家可以去看我翻译的原文,我先把损失函数部分粘贴过来。

损失函数

文字组件预测损失为由两个损失组成,计算公式为:
在这里插入图片描述
其中Lreg是平滑的L1 [20]回归损失,Lcls是交叉熵分类损失。 分类损失的计算公式为:在这里插入图片描述
其中Ltr代表TR的损失; Ltcrp仅计算TR内部的像素,而Ltcrn仅计算TR外部的像素。 Ltcrn用于抑制TCR中的背景噪声。 以这种方式,获得的TCR可以有益于后处理步骤。 OHEM [22]用于TR损耗,其中正负之间的比例设置为3:1。 在我们的实验中,权重λ1和λ2分别根据经验设置为1.0和0.5。
由于非TCR区域缺少高度和方向属性,因此我们仅计算TCR区域的回归损失:在这里插入图片描述
其中hki,sinθ和cosθ是真实值,hˆ ki,ˆ sinθ和ˆ cosθ是相应的预测值; Ω表示TCR中的一组正元素; h是真值框中文本组件的高度。 权重log(h + 1)对于大规模文本组件的高度回归很有帮助。 在我们的论文中,将超参数β设置为1.0。

总代码

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


class TextLoss(nn.Module):

    def __init__(self):
        super().__init__()

    @staticmethod
    def ohem(predict, target, train_mask, negative_ratio=3.):
        pos = (target * train_mask).byte()
        neg = ((1 - target) * train_mask).byte()

        n_pos = pos.float().sum()

        if n_pos.item() > 0:
            loss_pos = F.cross_entropy(predict[pos], target[pos], reduction='sum')
            loss_neg = F.cross_entropy(predict[neg], target[neg], reduction='none')
            n_neg = min(int(neg.float().sum().item()), int(negative_ratio * n_pos.float()))
        else:
            loss_pos = torch.tensor(0.)
            loss_neg = F.cross_entropy(predict[neg], target[neg], reduction='none')
            n_neg = 100
        loss_neg, _ = torch.topk(loss_neg, n_neg)

        return (loss_pos + loss_neg.sum()) / (n_pos + n_neg).float()

    @staticmethod
    def smooth_l1_loss(inputs, target, sigma=9.0):
        try:
            diff = torch.abs(inputs - target)
            less_one = (diff < 1.0 / sigma).float()
            loss = less_one * 0.5 * diff ** 2 * sigma \
                   + torch.abs(torch.tensor(1.0) - less_one) * (diff - 0.5 / sigma)
            loss = torch.mean(loss) if loss.numel() > 0 else torch.tensor(0.0)
        except Exception as e:
            print('RPN_REGR_Loss Exception:', e)
            loss = torch.tensor(0.0)

        return loss

    def gcn_loss(self, gcn_data):
        # gcn loss
        gcn_pred = gcn_data[0]
        labels = gcn_data[1].view(-1).long()
        loss = F.cross_entropy(gcn_pred, labels)  # *torch.tensor(0.0)

        return loss

    def forward(self, inputs, gcn_data, train_mask, tr_mask, tcl_mask, radii_map, sin_map, cos_map):
        """
        calculate textsnake loss
        :param inputs: (Variable), network predict, (BS, 8, H, W)
        :param gcn_data: (Variable), (gcn_pred ,gtmat_batch)
        :param tr_mask: (Variable), TR target, (BS, H, W)
        :param tcl_mask: (Variable), TCL target, (BS, H, W)
        :param sin_map: (Variable), sin target, (BS, H, W)
        :param cos_map: (Variable), cos target, (BS, H, W)
        :param radii_map: (Variable), radius target, (BS, H, W)
        :param train_mask: (Variable), training mask, (BS, H, W)
        :return: loss_tr, loss_tcl, loss_radii, loss_sin, loss_cos
        """

        tr_pred = inputs[:, :2].permute(0, 2, 3, 1).contiguous().view(-1, 2)  # (BSxHxW, 2)
        tcl_pred = inputs[:, 2:4].permute(0, 2, 3, 1).contiguous().view(-1, 2)  # (BSxHxW, 2)
        sin_pred = inputs[:, 4].contiguous().view(-1)  # (BSxHxW,)
        cos_pred = inputs[:, 5].contiguous().view(-1)  # (BSxHxW,)

        # regularize sin and cos: sum to 1
        scale = torch.sqrt(1.0 / (sin_pred ** 2 + cos_pred ** 2 + 0.0001))
        sin_pred = sin_pred * scale
        cos_pred = cos_pred * scale

        top_pred = inputs[:, 6].contiguous().view(-1)  # (BSxHxW,)
        bot_pred = inputs[:, 7].contiguous().view(-1)  # (BSxHxW,)

        train_mask = train_mask.contiguous().view(-1)  # (BSxHxW,)

        tr_mask = tr_mask.contiguous().view(-1)
        tcl_mask = tcl_mask[:, :, :, 0].contiguous().view(-1)
        sin_map = sin_map.contiguous().view(-1)
        cos_map = cos_map.contiguous().view(-1)
        top_map = radii_map[:, :, :, 0].contiguous().view(-1)
        bot_map = radii_map[:, :, :, 1].contiguous().view(-1)

        # loss_tr = F.cross_entropy(tr_pred[train_mask], tr_mask[train_mask].long())
        loss_tr = self.ohem(tr_pred, tr_mask.long(), train_mask.long())

        loss_tcl = torch.tensor(0.)
        tr_train_mask = train_mask * tr_mask
        tr_neg_mask = 1 - tr_train_mask
        if tr_train_mask.sum().item() > 0:
            loss_tcl_pos = F.cross_entropy(tcl_pred[tr_train_mask], tcl_mask[tr_train_mask].long())
            loss_tcl_neg = F.cross_entropy(tcl_pred[tr_neg_mask], tcl_mask[tr_neg_mask].long())
            loss_tcl = loss_tcl_pos #+ loss_tcl_neg

        # geometry losses
        loss_radii = torch.tensor(0.)
        loss_sin = torch.tensor(0.)
        loss_cos = torch.tensor(0.)
        tcl_train_mask = train_mask * tcl_mask
        if tcl_train_mask.sum().item() > 0:
            ones = torch.ones_like(top_pred[tcl_mask]).float()

            loss_top = F.smooth_l1_loss(top_pred[tcl_mask] / (top_map[tcl_mask]+0.01), ones, reduction='none')
            loss_bot = F.smooth_l1_loss(bot_pred[tcl_mask] / (bot_map[tcl_mask]+0.01), ones, reduction='none')

            rad_map = top_map[tcl_mask] + bot_map[tcl_mask]
            # loss_radii = torch.mean(torch.log10(rad_map+1.0)*(loss_top+loss_bot))
            loss_radii = torch.mean(loss_top + loss_bot)

            # loss_radii=torch.tensor(0);
            loss_sin = self.smooth_l1_loss(sin_pred[tcl_mask], sin_map[tcl_mask])
            loss_cos = self.smooth_l1_loss(cos_pred[tcl_mask], cos_map[tcl_mask])

        # ##  Graph convolution loss
        gcn_loss = self.gcn_loss(gcn_data)
        # gcn_loss = torch.tensor(0.)

        return loss_tr, loss_tcl, loss_sin, loss_cos, loss_radii, gcn_loss


ohem

先看用于TR损耗的ohem。

        pos = (target * train_mask).byte()
        neg = ((1 - target) * train_mask).byte()

先算出积极数和消极数(就是正样本和负样本)的真值,算法是正样本的比例*总数再求比特数。负样本数则是(1-正样本比例)*总数再求比特数。

n_pos = pos.float().sum()

对正样本求和

        if n_pos.item() > 0:
            loss_pos = F.cross_entropy(predict[pos], target[pos], reduction='sum')
            loss_neg = F.cross_entropy(predict[neg], target[neg], reduction='none')
            n_neg = min(int(neg.float().sum().item()), int(negative_ratio * n_pos.float()))
        else:
            loss_pos = torch.tensor(0.)
            loss_neg = F.cross_entropy(predict[neg], target[neg], reduction='none')
            n_neg = 100
        loss_neg, _ = torch.topk(loss_neg, n_neg)

判断一下正样本个数,如果正样本真值大于0求一下真值和预测值的交叉熵,然后求一个最小值在(负样本真值)和(正样本*negative_ratio),说一下negative_ratio这个参数,他是3,因为通常来说,正负样本比例是1:3是最好的(参看难例挖掘)。
否则的话正损失为0,负损失为交叉熵结果。
最后这个就是取出负样本的前100个最大的。

 return (loss_pos + loss_neg.sum()) / (n_pos + n_neg).float()

最后结果:(正样本加负样本前100损失总和)/(样本总和)
接下来就是:Smooth L1 Loss损失很有意思。

smooth_l1_loss

try异常处理部分就不提了,就是做一个尝试,如果可以运行就运行,不行的话就输出异常,不会出现程序错误,这个没啥说的。我们说函数,先把函数的公式写出来
在这里插入图片描述
其中x为真值和预测值差值为diff。

diff = torch.abs(inputs - target)

这就是那个差值X。

less_one = (diff < 1.0 / sigma).float()

这是我见过最有意思的函数,它的作用是判断,如果小于1.0 / sigma,less_one就等于1.后面我们会说他怎么用。

            loss = less_one * 0.5 * diff ** 2 * sigma \
                   + torch.abs(torch.tensor(1.0) - less_one) * (diff - 0.5 / sigma)

可以看到这是一个计算式我们做一个假设,假设diff < 1.0 / sigma,那么这个式子就会变为:less_one * 0.5 * diff ** 2 * sigma因为后面的: torch.abs(torch.tensor(1.0) - less_one)等于0,如果diff >1.0 / sigma式子变为:(diff - 0.5 / sigma)正好完成了判断。这里有一个问题就是为什么要把判断条件除以9再乘以9我也有疑惑。但是结果不变。

loss = torch.mean(loss) if loss.numel() > 0 else torch.tensor(0.0)

这句话就是判断一下损失是否为0。为0就返回0,不为0返回损失。
然后是GCN损失。

gcn_loss

    def gcn_loss(self, gcn_data):
        # gcn loss
        gcn_pred = gcn_data[0]
        labels = gcn_data[1].view(-1).long()
        loss = F.cross_entropy(gcn_pred, labels)  # *torch.tensor(0.0)

        return loss

gcn损失不讲了就是交叉熵损失。
最后是我们的forward了这块也是,说不是特别清楚。略讲。

forward

        tr_pred = inputs[:, :2].permute(0, 2, 3, 1).contiguous().view(-1, 2)  # (BSxHxW, 2)
        tcl_pred = inputs[:, 2:4].permute(0, 2, 3, 1).contiguous().view(-1, 2)  # (BSxHxW, 2)
        sin_pred = inputs[:, 4].contiguous().view(-1)  # (BSxHxW,)
        cos_pred = inputs[:, 5].contiguous().view(-1)  # (BSxHxW,)

这是数据类型转换,基本就是取出需要的数据,就不详细说了。

        scale = torch.sqrt(1.0 / (sin_pred ** 2 + cos_pred ** 2 + 0.0001))
        sin_pred = sin_pred * scale
        cos_pred = cos_pred * scale

这里有意思第一句是cos平方加sin平方。我想了半天他也是1.然后在乘以他,好吧我们略过。

        top_pred = inputs[:, 6].contiguous().view(-1)  # (BSxHxW,)
        bot_pred = inputs[:, 7].contiguous().view(-1)  # (BSxHxW,)

        train_mask = train_mask.contiguous().view(-1)  # (BSxHxW,)

        tr_mask = tr_mask.contiguous().view(-1)
        tcl_mask = tcl_mask[:, :, :, 0].contiguous().view(-1)
        sin_map = sin_map.contiguous().view(-1)
        cos_map = cos_map.contiguous().view(-1)
        top_map = radii_map[:, :, :, 0].contiguous().view(-1)
        bot_map = radii_map[:, :, :, 1].contiguous().view(-1)

这块还是取所需要的数据。

loss_tr = self.ohem(tr_pred, tr_mask.long(), train_mask.long())

这是利用前面我们说的ohem函数求TR损耗。

        loss_tcl = torch.tensor(0.)
        tr_train_mask = train_mask * tr_mask
        tr_neg_mask = 1 - tr_train_mask

和前面的操作一样,求一下正负样本。如果这个数大于0:

       if tr_train_mask.sum().item() > 0:
            loss_tcl_pos = F.cross_entropy(tcl_pred[tr_train_mask], tcl_mask[tr_train_mask].long())
            loss_tcl_neg = F.cross_entropy(tcl_pred[tr_neg_mask], tcl_mask[tr_neg_mask].long())
            loss_tcl = loss_tcl_pos #+ loss_tcl_neg

用交叉熵求一下tcl损失。

        loss_radii = torch.tensor(0.)
        loss_sin = torch.tensor(0.)
        loss_cos = torch.tensor(0.)
        tcl_train_mask = train_mask * tcl_mask

求一下tcl_train_mask如果这个数大于0,用交叉熵求一下loss_top和 loss_bot,合并为loss_radii,然后求loss_sin和loss_cos(前面总的损失介绍都有介绍)

        if tcl_train_mask.sum().item() > 0:
            ones = torch.ones_like(top_pred[tcl_mask]).float()

            loss_top = F.smooth_l1_loss(top_pred[tcl_mask] / (top_map[tcl_mask]+0.01), ones, reduction='none')
            loss_bot = F.smooth_l1_loss(bot_pred[tcl_mask] / (bot_map[tcl_mask]+0.01), ones, reduction='none')

            rad_map = top_map[tcl_mask] + bot_map[tcl_mask]
            # loss_radii = torch.mean(torch.log10(rad_map+1.0)*(loss_top+loss_bot))
            loss_radii = torch.mean(loss_top + loss_bot)

            # loss_radii=torch.tensor(0);
            loss_sin = self.smooth_l1_loss(sin_pred[tcl_mask], sin_map[tcl_mask])
            loss_cos = self.smooth_l1_loss(cos_pred[tcl_mask], cos_map[tcl_mask])

gcn损失

gcn_loss = self.gcn_loss(gcn_data)

最后返回所有损失。

 return loss_tr, loss_tcl, loss_sin, loss_cos, loss_radii, gcn_loss

最后

最后说一下,为啥讲的不是每一句都那么详细,两点:一是了解不是特别清楚,原文说的也是,我怕讲错、二是,一半看代码注解就是为了做优化,所以结构更重要,只要你找得到关键的损失函数,就可以改。所以没必要太详细本人能力有限,先写到这。谢谢各位

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值