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


目录
相关文章
|
2月前
|
算法 关系型数据库 文件存储
ProxylessNAS:直接在目标任务和硬件上进行神经架构搜索——论文解读
ProxylessNAS是一种直接在目标任务和硬件上进行神经架构搜索的方法,有效降低了传统NAS的计算成本。通过路径二值化和两路径采样策略,减少内存占用并提升搜索效率。相比代理任务方法,ProxylessNAS在ImageNet等大规模任务中展现出更优性能,兼顾准确率与延迟,支持针对不同硬件(如GPU、CPU、移动端)定制高效网络架构。
293 126
ProxylessNAS:直接在目标任务和硬件上进行神经架构搜索——论文解读
|
网络协议 IDE Linux
mongoose使用详细 -- 如何通过mongoose搭建服务器
mongoose使用详细 -- 如何通过mongoose搭建服务器
2340 0
|
分布式计算 数据可视化 大数据
Hue--介绍、功能、架构 | 学习笔记
快速学习 Hue--介绍、功能、架构
2973 0
Hue--介绍、功能、架构 | 学习笔记
|
边缘计算 数据可视化 物联网
node-red介绍
Node-RED最初是IBM在2013年末开发的一个开源项目——基于数据流(dataflow)的可视化编程工具。
node-red介绍
|
9月前
|
缓存 自然语言处理 搜索推荐
深入优化基于DeepSeek的智能客服系统:从基础到高级
本文在上一篇构建的DeepSeek智能客服系统基础上,深入探讨了性能优化、用户体验提升和高级功能集成的方法。通过缓存机制、异步处理优化性能;利用情感分析、个性化回答提升用户体验;引入语音识别、知识图谱等高级功能增强智能化水平。结合具体案例与代码示例,帮助开发者打造更高效、智能的客服系统。
|
JSON API 数据安全/隐私保护
阿里云邮件推送邮件发送失败的问题排查解决
阿里云邮件推送服务中邮件发送失败的排查方法包括:确认SMTP设置正确无误;验证发信域名和邮件地址;检查是否超出发送配额;审查邮件内容以确保合规;确保网络连接稳定;利用发送日志诊断具体问题。当阿里云邮件推送服务出现问题时,可考虑使用AOKSend作为替代方案,其配置简单且服务稳定可靠,支持多种配置选项,并提供详尽的文档支持。示例Python代码展示了如何使用AOKSend API发送邮件。这些步骤有助于确保邮件的顺利发送。
|
Linux API C++
【C++ 17 新特性 文件管理】探索C++ Filesystem库:文件和目录操作的全面指南(一)
【C++ 17 新特性 文件管理】探索C++ Filesystem库:文件和目录操作的全面指南
3085 3
|
机器学习/深度学习 自然语言处理 并行计算
一文快速读懂Transformer
Transformer模型近年来成为自然语言处理(NLP)领域的焦点,其强大的特征提取能力和并行计算优势在众多任务中取得显著效果。本文详细解读Transformer的原理,包括自注意力机制和编码器-解码器结构,并提供基于PyTorch的代码演示,展示了其在文本分类等任务中的应用。
|
机器学习/深度学习 人工智能 文字识别
ultralytics YOLO11 全新发布!(原理介绍+代码详见+结构框图)
本文详细介绍YOLO11,包括其全新特性、代码实现及结构框图,并提供如何使用NEU-DET数据集进行训练的指南。YOLO11在前代基础上引入了新功能和改进,如C3k2、C2PSA模块和更轻量级的分类检测头,显著提升了模型的性能和灵活性。文中还对比了YOLO11与YOLOv8的区别,并展示了训练过程和结果的可视化
19692 0