yolov4项目记录6-损失计算

目录

一、概述

二、计算

1.位置回归损失

2.物体置信度损失

3.分类损失

三、代码


一、概述

预测的内容,包含了物体所在的位置(位置回归损失),是否有物体(物体置信度损失),物体的分类(分类损失)三样内容,因此我们使用这三种数据来计算总体损失。物体所在位置主要使用IOU,而是否有物体以及分类的预测损失使用二分类交叉熵损失。

Loss=Loss_{loc}+Loss_{obj}+Loss_{cls}

二、计算

1.位置回归损失

下面的式子就是位置回归损失的计算公式,意思就是对每一个网格(共S^2个网格),的每一个锚框(共B个,其实就是3个),去计算CIOU这样的一个数。同时前面有一个示性函数I,也就是说,只有网格里面有物体的时候,才会计算位置回归损失,没物体的话就不算,就是0。

Loss_{loc} = \lambda_{iou}\sum_{i=0}^{S^2}\sum_{j=0}^{B}I_{ij}^{obj}L_{ciou}

下面是CIOU的计算细节。其中:

lambda: 损失的系数,可以调整。

d:两个框中心点的距离

c:两个框最小外包框的对角线长度

v:根据实际框宽高的夹角,以及预测框宽高的夹角,二者之差,所得的一个数,也能够表示出两个框的相似度。两个框越相似,夹角之差越小,这个v就越小,如果两个框一模一样,v就是0。

IOU:两个框的交并比

L_{ciou}=1-IOU+\frac{d^2}{c^2}+\alpha v

v=\frac{4}{\pi^2}(arctan\frac{w^{gt}}{h^{gt}}-arctan\frac{w}{h})^2

\alpha=\frac{v}{(1-IOU)+v}

2.物体置信度损失

相当于对“这个网格是否有物体”这件事情进行分类,使用二分类交叉熵损失。注意这里对“有物体的网格”,以及“没有物体的网格”,全部都进行了计算。

Loss_{obj}=-\lambda_{cls}\sum_{i=0}^{S^2}\sum_{j=0}^{B}I_{ij}^{obj}\lambda_c[\hat{C}_{i}^{j}log(C_{i}^{j})+(1-\hat{C}_{i}^{j})log(1-C_{i}^{j})]-\lambda_{cls}\sum_{i=0}^{S^2}\sum_{j=0}^{B}I_{ij}^{noobj}\lambda_c[\hat{C}_{i}^{j}log(C_{i}^{j})+(1-\hat{C}_{i}^{j})log(1-C_{i}^{j})]

 其中:

C hat是第i个网格第j个框,实际的物体置信度,通常是1或者0

C是第i个网格第j个框,预测的物体置信度

I依旧是示性函数,B, S^2等也和前面的一致。

3.分类损失

类似于逻辑回归,整体逻辑和上面的物体置信度损失一致,只不过这里是针对于有物体的那些网格,才会有物体分类这一说。

Loss_{cls}=-\sum_{i=0}^{S^2}\sum_{j=0}^{B}I_{ij}^{obj}\sum_{c\in classes}\lambda_c[\hat{p}_{i}^{j}(c)log(p_{i}^{j}(c))+(1-\hat{p}_{i}^{j}(c))log((1-p_{i}^{j}(c)))]

三、代码

这里代码需要和前面文章的目标构建结合起来,因为需要用到标签数据的构建。

class YOLOLoss(nn.Module):
    def __init__(self, anchors, num_classes, img_size, label_smooth=0, cuda=True):
        super(YOLOLoss, self).__init__()
        self.anchors = anchors
        self.num_anchors = len(anchors)
        self.num_classes = num_classes
        self.bbox_attr = 5 + num_classes
        self.img_size = img_size
        self.feature_length = [img_size[0] // 8, img_size[0] // 16, img_size[0] // 32]
        self.label_smooth = label_smooth

        self.ignore_thresh = 0.7
        # 损失函数用的lambda因子
        self.lambda_conf = 1.0
        self.lambda_cls = 1.0
        self.lambda_loc = 1.0
        self.cuda = cuda

    def forward(self, input: torch.Tensor, targets=None):
        # 一共有多少张图片
        bs = input.size(0)
        # 特征图的高、宽
        in_h = input.size(2)
        in_w = input.size(3)
        # 计算步长,有2个,分别是h和w的步长stride_h, stride_w,每一个网格的大小
        stride_h = self.img_size[0] / in_h
        stride_w = self.img_size[1] / in_w
        # scaled_anchors, 把anchor换成以网格为单位
        scaled_anchors = [(a_w/stride_w, a_h/stride_h) for a_w, a_h in self.anchors]
        # 对预测的形状调整成(B, num_anchors, H, W, n_ch)
        prediction = input.view(bs, int(self.num_anchors/3), self.bbox_attr, in_h, in_w).permute(0,1,3,4,2).contiguous()
        conf = torch.sigmoid(prediction[..., 4])
        pred_cls = torch.sigmoid(prediction[..., 5:])
        # # build_target1,构造掩码,填充掩码正样本
        mask, noobj_mask, t_box, tconf, tcls, box_loss_scale_x, box_loss_scale_y = self.get_target(targets,scaled_anchors,in_w, in_h)
        # # build_target2,筛选负样本,并对头部做decode
        noobj_mask, pred_boxes_for_ciou = self.get_ignore(prediction,targets,scaled_anchors,in_w, in_h,noobj_mask)
        if self.cuda:
            mask, noobj_mask = mask.cuda(), noobj_mask.cuda()
            box_loss_scale_x, box_loss_scale_y = box_loss_scale_x.cuda(), box_loss_scale_y.cuda()
            tconf, tcls = tconf.cuda(), tcls.cuda()
            pred_boxes_for_ciou = pred_boxes_for_ciou.cuda()
            t_box = t_box.cuda()

        # 计算ciou损失
        box_loss_scale = 2 - box_loss_scale_x * box_loss_scale_y
        ciou = box_ciou(pred_boxes_for_ciou[mask.bool()], t_box[mask.bool()])
        loss_ciou = 1-ciou
        loss_ciou = loss_ciou * box_loss_scale[mask.bool()]
        loss_loc = torch.sum(loss_ciou / bs)
        # 计算位置置信度损失
        loss_conf = torch.sum(BCELoss(conf, mask)*mask / bs) + \
            torch.sum(BCELoss(conf, mask)*noobj_mask / bs)
        # 计算分类置信度损失
        loss_cls = torch.sum(BCELoss(pred_cls[mask==1], smooth_labels(tcls[mask==1], self.label_smooth, self.num_classes))/bs)

        # 计算总损失
        loss = loss_conf*self.lambda_conf + loss_cls*self.lambda_cls + loss_loc*self.lambda_loc
        return loss, loss_conf.item(), loss_cls.item(), loss_loc.item()
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值