yolov4 LOSS代码详解【附代码】中

简介: 笔记

get_target(获得y_true)


获得网络预测结果

        #-----------------------------------------------#
        #   获得网络应该有的预测结果
        #-----------------------------------------------#
        y_true, noobj_mask, box_loss_scale = self.get_target(l, targets, scaled_anchors, in_h, in_w)

这里用到了get_target这个函数。先附上该函数的代码


 

def get_target(self, l, targets, anchors, in_h, in_w):
        # 特征图索引:0,1,2,
        # targets:列表形式,长度batchsize,每个元素又是5列,边界框信息和类别
        # scaled_anchors:缩放到特征层的anchor
        # in_h, in_w:特征层大小
        #-----------------------------------------------------#
        #   计算一共有多少张图片
        #-----------------------------------------------------#
        bs              = len(targets)
        #-----------------------------------------------------#
        #   用于选取哪些先验框不包含物体
        #-----------------------------------------------------#
        # 创建一个(batshzie,3,13,13)全1矩阵
        noobj_mask      = torch.ones(bs, len(self.anchors_mask[l]), in_h, in_w, requires_grad = False)
        #-----------------------------------------------------#
        #   让网络更加去关注小目标
        #   创建一个(batshzie,3,13,13)全0矩阵
        #-----------------------------------------------------#
        box_loss_scale  = torch.zeros(bs, len(self.anchors_mask[l]), in_h, in_w, requires_grad = False)
        #-----------------------------------------------------#
        #   batch_size, 3, 13, 13, 5 + num_classes
        #   y_true用来存放ground truth 信息
        #-----------------------------------------------------#
        y_true          = torch.zeros(bs, len(self.anchors_mask[l]), in_h, in_w, self.bbox_attrs, requires_grad = False)
        for b in range(bs):            
            if len(targets[b])==0:
                continue
            batch_target = torch.zeros_like(targets[b])
            #-------------------------------------------------------#
            #   计算出正样本在特征层上的中心点?
            #-------------------------------------------------------#
            batch_target[:, [0,2]] = targets[b][:, [0,2]] * in_w
            batch_target[:, [1,3]] = targets[b][:, [1,3]] * in_h
            batch_target[:, 4] = targets[b][:, 4]
            batch_target = batch_target.cpu()
            #-------------------------------------------------------#
            #   将真实框转换一个形式
            #   num_true_box, 4
            #-------------------------------------------------------#
            gt_box          = torch.FloatTensor(torch.cat((torch.zeros((batch_target.size(0), 2)), batch_target[:, 2:4]), 1))
            #-------------------------------------------------------#
            #   将先验框转换一个形式
            #   9, 4
            #-------------------------------------------------------#
            anchor_shapes   = torch.FloatTensor(torch.cat((torch.zeros((len(anchors), 2)), torch.FloatTensor(anchors)), 1))
            #-------------------------------------------------------#
            #   计算交并比
            #   self.calculate_iou(gt_box, anchor_shapes) = [num_true_box, 9]每一个真实框和9个先验框的重合情况
            #   best_ns:
            #   [每个真实框最大的重合度max_iou, 每一个真实框最重合的先验框的序号]
            #-------------------------------------------------------#
            best_ns = torch.argmax(self.calculate_iou(gt_box, anchor_shapes), dim=-1)
            for t, best_n in enumerate(best_ns):
                if best_n not in self.anchors_mask[l]:
                    continue
                #----------------------------------------#
                #   判断这个先验框是当前特征点的哪一个先验框
                #  [[6, 7, 8], [3, 4, 5], [0, 1, 2]]
                #----------------------------------------#
                k = self.anchors_mask[l].index(best_n)
                #----------------------------------------#
                #   获得真实框属于哪个网格点
                #  floor 返回一个新张量,包含输入input张量每个元素的floor,即取不大于元素的最大整数。
                #----------------------------------------#
                i = torch.floor(batch_target[t, 0]).long()
                j = torch.floor(batch_target[t, 1]).long()
                #----------------------------------------#
                #   取出真实框的种类
                #----------------------------------------#
                c = batch_target[t, 4].long()
                #----------------------------------------#
                #   noobj_mask代表无目标的特征点
                #----------------------------------------#
                noobj_mask[b, k, j, i] = 0
                #----------------------------------------#
                #   tx、ty代表中心调整参数的真实值
                #   y_true[b, k, j, i, 0] 意思是取第几各个batch的第k个锚框(每个层有三个),在特征层上第j行第i列网格点
                #----------------------------------------#
                y_true[b, k, j, i, 0] = batch_target[t, 0]
                y_true[b, k, j, i, 1] = batch_target[t, 1]
                y_true[b, k, j, i, 2] = batch_target[t, 2]
                y_true[b, k, j, i, 3] = batch_target[t, 3]   # 前几行这是box的信息
                y_true[b, k, j, i, 4] = 1  # bbox由5个信息组成,前四个是坐标信息,第5个是Pc,是否由目标
                y_true[b, k, j, i, c + 5] = 1  # c + 5指定获得的第几个类,5是前5个维度(x,y,w,h,Pc),从下个维度开始是类
                #----------------------------------------#
                #   用于获得xywh的比例
                #   大目标loss权重小,小目标loss权重大
                #----------------------------------------#
                box_loss_scale[b, k, j, i] = batch_target[t, 2] * batch_target[t, 3] / in_w / in_h
        return y_true, noobj_mask, box_loss_scale


get_target函数需要传入5个参数,l【特征层索引】,targets【真实值】,anchors【已经缩放后的anchors】,in_h=in_w=19.


我们先创建一个全1的mask tensor【后面用来记录不含目标的地方】,此时的noobj_mask shape为【batch_size,3,19,19】


   

#-----------------------------------------------------#
        #   计算一共有多少张图片
        #-----------------------------------------------------#
        bs              = len(targets)
        #-----------------------------------------------------#
        #   用于选取哪些先验框不包含物体
        #-----------------------------------------------------#
        # 创建一个(batshzie,3,13,13)全1矩阵
        noobj_mask      = torch.ones(bs, len(self.anchors_mask[l]), in_h, in_w, requires_grad = False)

在创建一个全0的tensor box_loss_scale。shape 【batch_size,3,19,19】


 

#-----------------------------------------------------#
        #   让网络更加去关注小目标
        #   创建一个(batshzie,3,13,13)全0矩阵
        #-----------------------------------------------------#
        box_loss_scale  = torch.zeros(bs, len(self.anchors_mask[l]), in_h, in_w, requires_grad = False)

再创建一个全0的tensor,用来记录ground truth信息。shape【batch_size,3,19,19,5+num_classes】=【4,3,19,19,6】


这里再多嘴一句,上面这种shape的张量可以这样理解:有3种anchor,每种anchor都会负责一个19 * 19的cell网格,而每个cell内也都均有5+num_classes个参数需要预测。


 

#   y_true用来存放ground truth 信息
        #-----------------------------------------------------#
        y_true          = torch.zeros(bs, len(self.anchors_mask[l]), in_h, in_w, self.bbox_attrs, requires_grad = False)

batch_target[真实值映射到特征层]


下面这段代码的是遍历每个batch,然后判断一下target[b]中是否有目标,如果有目标就创建一个与target[b]shape一样的全0张量。由于我这里target长度是4【表示4个batch】,第一个target[0]的长度为5【表示这张图像有5个标注的目标】。 target[b]的shape为【该图有几个目标,5(这个5坐标信息和类)】


     

for b in range(bs):            
            if len(targets[b]) == 0:
                continue
            batch_target = torch.zeros_like(targets[b])

此刻的target[0]为:【center_x,center_y,w,h,class】。


tensor([


       [0.2352, 0.1637, 0.2928, 0.3273, 0.0000],

       [0.2097, 0.5896, 0.1135, 0.4490, 0.0000],

       [0.8569, 0.5296, 0.1382, 0.2500, 0.0000],

       [0.6867, 0.5395, 0.1201, 0.2763, 0.0000],

       [0.6061, 0.1637, 0.2911, 0.3273, 0.0000]], device='cuda:0')


计算出正样本在特征层上的中心点。可以计算出我们的target对应到我们19 *19 的特征层上坐标是多少。


       

#-------------------------------------------------------#
            #   计算出正样本在特征层上的中心点?
            #-------------------------------------------------------#
            batch_target[:, [0,2]] = targets[b][:, [0,2]] * in_w
            batch_target[:, [1,3]] = targets[b][:, [1,3]] * in_h
            batch_target[:, 4] = targets[b][:, 4]
            batch_target = batch_target.cpu()

则现在我们得到batch_target就记录下了我们的第一个batch中ground truth坐标映射到特征层的坐标信息。


tensor([[ 4.4688,  3.1094,  5.5625,  6.2188,  0.0000],

       [ 3.9844, 11.2031,  2.1562,  8.5312,  0.0000],

       [16.2812, 10.0625,  2.6250,  4.7500,  0.0000],

       [13.0469, 10.2500,  2.2813,  5.2500,  0.0000],

       [11.5156,  3.1094,  5.5312,  6.2188,  0.0000]], device='cuda:0')


我们对上面在特征层上得到gt坐标形式稍加改变。batch_target shape为【5,5】。size(0)=5,先创建一个【5,2】的全0tensor,然后与batch_target的第2列(w)至第3列(h)进行拼接。


       

#-------------------------------------------------------#
            #   将真实框转换一个形式
            #   num_true_box, 4
            #-------------------------------------------------------#
            gt_box          = torch.FloatTensor(torch.cat((torch.zeros((batch_target.size(0), 2)), batch_target[:, 2:4]), 1))

此刻的gt_box:


tensor([


       [0.0000, 0.0000, 5.5625, 6.2188],

       [0.0000, 0.0000, 2.1562, 8.5312],

       [0.0000, 0.0000, 2.6250, 4.7500],

       [0.0000, 0.0000, 2.2813, 5.2500],

       [0.0000, 0.0000, 5.5312, 6.2188]])


同时将先验框也转变一个形式:将一个【3,2】的全零tensor和anchors进行拼接。


       

#-------------------------------------------------------#
            #   将先验框转换一个形式
            #   9, 4
            #-------------------------------------------------------#
            anchor_shapes   = torch.FloatTensor(torch.cat((torch.zeros((len(anchors), 2)), torch.FloatTensor(anchors)), 1))

此刻anchor_shape:【实际就是给anchor前面多加了两列0】


tensor([


       [ 0.0000,  0.0000,  0.3750,  0.5000],

       [ 0.0000,  0.0000,  0.5938,  1.1250],

       [ 0.0000,  0.0000,  1.2500,  0.8750],

       [ 0.0000,  0.0000,  1.1250,  2.3438],

       [ 0.0000,  0.0000,  2.3750,  1.7188],

       [ 0.0000,  0.0000,  2.2500,  4.5625],

       [ 0.0000,  0.0000,  4.4375,  3.4375],

       [ 0.0000,  0.0000,  6.0000,  7.5938],

       [ 0.0000,  0.0000, 14.3438, 12.5312]])


IOU计算并获得anchor索引:


通过定义的函数计算IOU,主要传入两个参数,gt_box和anchor_shapes【前者是target映射到特征层上的gt,后者是anchor映射到特征层上缩放后的anchor】


注:这里的iou并不是预测的box和gt的。 iou的计算我这里不再写,另一篇文章有。


best_ns返回的是每个最大iou索引序号【表示哪些anchor有目标的以上】

#-------------------------------------------------------#
            #   计算交并比
            #   self.calculate_iou(gt_box, anchor_shapes) = [num_true_box, 9]每一个真实框和9个先验框的重合情况
            #   best_ns:
            #   [每个真实框最大的重合度max_iou, 每一个真实框最重合的先验框的序号]
            #-------------------------------------------------------#
            best_ns = torch.argmax(self.calculate_iou(gt_box, anchor_shapes), dim=-1)

此刻得到的best_ns为,即这几个anchor预测到了目标。可以这样理解,这张图里出现了5个目标,第一个目标由7号anchor预测到了,第二个目标到第四个由5号anchor预测到了,最后一个目标由7号anchor预测到了。


tensor([7, 5, 5, 5, 7])


判断目标落在哪个特征层的哪个先验框


最初前面我们定义了一个self.anchors_mask=[[6,7,8],[3,4,5],[0,1,2]]-->对应于19,38,56特征层的anchor。通过上面的索引也就知道了,出现的这5个目标分别落在了19 * 19特征层,38*38,38*38,38*38,38*38,19 * 19上。


           f

or t, best_n in enumerate(best_ns):
                if best_n not in self.anchors_mask[l]:
                    continue
                #----------------------------------------#
                #   判断这个先验框是当前特征点的哪一个先验框
                #  [[6, 7, 8], [3, 4, 5], [0, 1, 2]]
                #----------------------------------------#
                k = self.anchors_mask[l].index(best_n)

通过for循环对上面的bast_ns进行遍历,先确定一下我们一个best_n到底在不在anchors_mask上。


因为此时l为0,对应的19 * 19的特征层,所以anchors_mask[0] = [6,7,8],则k=1.


通过上面的操作,我们知道了当前目标应该由几号anchor进行预测,但还不知道当前的gt属于19 * 19中的哪个cell,所以我们现在需要再确定一下gt落在了哪个cell。


方法也很简单,前面我们通过batch_target获得了gt在当前特征层上的box坐标,那么我们仅需要获得这些box的(center_x,center_y)即可,并对这些中心点坐标取整就可以知道目标落在哪个cell。


           

#----------------------------------------#
                #   获得真实框属于哪个网格点
                #  floor 返回一个新张量,包含输入input张量每个元素的floor,即取不大于元素的最大整数。
                #----------------------------------------#
                i = torch.floor(batch_target[t, 0]).long()
                j = torch.floor(batch_target[t, 1]).long()

得到i和j如下,说明我这个目标的中心点落在了19 * 19 的第4行第3列的cell处。


i=4;


j=3;


然后我们还可以获得落在该cell处的类是什么。


         

#----------------------------------------#
                #   取出真实框的种类
                #----------------------------------------#
                c = batch_target[t, 4].long()

是否还记得我们前面定义了一个全1的tensor noobj_mask。此刻我们可以获得所有无目标的地方,或者说有目标的地方【1表示无目标,0表示有目标,当然你反过来也可以】。此时由于是第一次的遍历,b=0,k=1,j=3.i=4,表示第一个batch,中第k anchor,3,4有目标。


             

#----------------------------------------#
                #   noobj_mask代表无目标的特征点
                #----------------------------------------#
                noobj_mask[b, k, j, i] = 0

前面我们还定义了一个y_true的全0 tensor用来记录gt,shape是【batch_size,3,19,19,5+num_classes】。然后我们记录下当前目标box坐标信息。前4列是box信息,然后又看到y_true[...,4]=1表示有目标,后面c+5表示当前为获得第几个类。


           

#----------------------------------------#
                #   tx、ty代表中心调整参数的真实值
                #   y_true[b, k, j, i, 0] 意思是取第几各个batch的第k个锚框(每个层有三个),在特征层上第j行第i列网格点
                #----------------------------------------#
                y_true[b, k, j, i, 0] = batch_target[t, 0]
                y_true[b, k, j, i, 1] = batch_target[t, 1]
                y_true[b, k, j, i, 2] = batch_target[t, 2]
                y_true[b, k, j, i, 3] = batch_target[t, 3]   # 前几行这是box的信息
                y_true[b, k, j, i, 4] = 1  # bbox由5个信息组成,前四个是坐标信息,第5个是Pc,是否由目标
                y_true[b, k, j, i, c + 5] = 1  # c + 5指定获得的第几个类,5是前5个维度(x,y,w,h,Pc),从下个维度开始是类

此时y_true[0,1,3,4,:]:这就表示第一张图(batch 0),设置的第二个anchor(k=1),在19 *19 的特征层的3行4列处有目标设置为1,该类对应为第1个类。


tensor([4.4688, 3.1094, 5.5625, 6.2188, 1.0000, 1.0000])


batch_target存放着当前batch[当前图像]所有目标的box信息和类信息,那肯定每个目标的box有大有小,小目标的box面积就小,大目标的box面积就大,那么我们就可以得到每个目标的box面积,与当前特征层(19 * 19)面积做个比值,这个值是什么意思呢?我们就可以得到大小目标占当前特征层的比例值。


           

#----------------------------------------#
                #   用于获得xywh的比例
                #   大目标loss权重小,小目标loss权重大
                #----------------------------------------#
                box_loss_scale[b, k, j, i] = batch_target[t, 2] * batch_target[t, 3] / in_w / in_h


目录
相关文章
|
6月前
|
Python
【论文复现】针对yoloV5-L部分的YoloBody部分重构(Slim-neck by GSConv)
【论文复现】针对yoloV5-L部分的YoloBody部分重构(Slim-neck by GSConv)
173 0
【论文复现】针对yoloV5-L部分的YoloBody部分重构(Slim-neck by GSConv)
|
机器学习/深度学习 编解码 算法
yolo原理系列——yolov1--yolov5详细解释
yolo原理系列——yolov1--yolov5详细解释
1224 0
yolo原理系列——yolov1--yolov5详细解释
|
6月前
|
算法 文件存储 计算机视觉
【YOLOv8改进】MobileNetV3替换Backbone (论文笔记+引入代码)
YOLO目标检测专栏探讨了MobileNetV3的创新改进,该模型通过硬件感知的NAS和NetAdapt算法优化,适用于手机CPU。引入的新架构包括反转残差结构和线性瓶颈层,提出高效分割解码器LR-ASPP,提升了移动设备上的分类、检测和分割任务性能。MobileNetV3-Large在ImageNet上准确率提升3.2%,延迟降低20%,COCO检测速度增快25%。MobileNetV3-Small则在保持相近延迟下,准确率提高6.6%。此外,还展示了MobileNetV3_InvertedResidual模块的代码实现。
|
机器学习/深度学习 计算机视觉 异构计算
Darknet53详细原理(含torch版源码)
Darknet53详细原理(含torch版源码)—— cifar10
438 0
Darknet53详细原理(含torch版源码)
|
机器学习/深度学习 编解码
MobileNetV1详细原理(含torch源码)
MobilenetV1(含torch源码)—— cifar10
363 0
MobileNetV1详细原理(含torch源码)
|
机器学习/深度学习 计算机视觉 异构计算
MobileNetV2详细原理(含torch源码)
MobileNetV2详细原理(含torch源码)—— cifar10
484 0
MobileNetV2详细原理(含torch源码)
|
机器学习/深度学习 存储 编解码
MobileNetV3详细原理(含torch源码)
MobilneNetV3详细原理(含torch源码)—— cifar10
728 0
MobileNetV3详细原理(含torch源码)
|
PyTorch 算法框架/工具
GoogLeNet InceptionV1代码复现+超详细注释(PyTorch)
GoogLeNet InceptionV1代码复现+超详细注释(PyTorch)
332 0
|
PyTorch 算法框架/工具 机器学习/深度学习
GoogLeNet InceptionV3代码复现+超详细注释(PyTorch)
GoogLeNet InceptionV3代码复现+超详细注释(PyTorch)
391 0
|
人工智能 自动驾驶 安全
YOLO v8!| 附教程+代码 以及 vs YOLOv6 v3.0
YOLO v8!| 附教程+代码 以及 vs YOLOv6 v3.0