yolov3损失函数_YOLO v3

0f638964b81e90ba6a1c4a579d51c56f.png

1.前言

距离上次论文阅读过去蛮久的了,在完成老板的要求后,终于有空上班的时候苟一苟了,下面就记录一下自己阅读yolov3的一些心得,方便以后回顾

本文目录:

  • yolo v3的网络结构
  • yolo v3的先验框anchor
  • yolo v3的前向传播和损失函数

2.Yolo v3的网络结构

在刚开始阅读yolo v3的时候总感觉缺了点东西,后来才知道,作者并没有像v1中那样,清晰的给出网络的结构图。于是乎,找呀找,找到了这张宝图,图的作者还是比较用心的,基本比较严谨,可以拿来作为参考。

d66f6d6ce63664c6c334a5719c217fe6.png
图1:yolo v3网络结构

原来yolo v2中的darknet-19(特征提取网络)在yolo v3中变成了darknet-53(上图中红色虚线部分),网络越深,表达效果肯定也会得到提升,不过,这也要归功于ResNet的残差组件。有关ResNet的部分,可以参考ResNet介绍。

DBL:上图中的左下角部分,卷积+BatchNormal+Lecky_relu,基本是v3最小的组件,除了最后一个卷积,其余的后面都会跟着BN、Lecky_relu。

Resn:n代表数字,有res1,res2, … ,res8等等,表示这个res_block里含有多少个res_unit。这是yolo_v3的大组件,yolo_v3开始借鉴了ResNet的残差结构,使用这种结构可以让网络结构更深(从v2的darknet-19上升到v3的darknet-53,前者没有残差结构)。对于res_block的解释,可以在图1的右下角直观看到,其基本组件也是DBL。

concat与add:concat是张量的拼接,将darknet-53中间层与后面某一层经过上采样得到的张量进行拼接,会造成维度的增加。而对于残差组件中的add操作,只会将对应位置进行相加,不会造成维度的变化。

从上图可以看到,这个特征提取网络最后有三个输出,分别对应三个分支。这三个输出特征图的宽高分别为(w/8,h/8),(w/16,h/16),(w/32,h/32)。

说到这,想起来前段时间看Faster RCNN。作者发现对于小物体检测效果不佳,于是后面进行了一个小的升级,将自己的VGG卷积积换成了FPN(特征金字塔网络),在我之前的文章中,也说到过这,可以参考文章前半部分。

不过,需要注意的是,yolo v3对特征进行融合的时候,还是与FPN有些许不同的,这里对两个特征尺度提取的张量进行融合,没有用add,而是用的concat,这两个是不一样的,虽然效果相近。

对yolo v3的网络结构大概说到这,下面看看yolo v3的先验框anchor。

2.YOLO V3 的Anchor

YOLO V3继承了YOLO V2中的锚(anchor,我们理解为基础框/先验框,后面需要以这些框为基础进行bounding box微调),但是又不太一样。下面引用知乎大佬对这部分的一些理解:

anchor box最初是由Faster RCNN引入的。anchor box(论文中也称为bounding box prior,后面均使用anchor box)其实就是从训练集的所有ground truth box中统计(使用k-means)出来的在训练集中最经常出现的几个box形状和尺寸。比如,在某个训练集中最常出现的box形状有扁长的、瘦高的和宽高比例差不多的正方形这三种形状。我们可以预先将这些统计上的先验(或来自人类的)经验加入到模型中,这样模型在学习的时候,瞎找的可能性会变小,有助于模型快速收敛了

在YOLO V2中,设置了5个宽高比例的anchor(通过聚类获得),每个cell负责的anchor的数量为5,而在YOLO V3中,共设置9个宽高不同的anchor(同样是通过聚类获取得到),每个cell的anchor的数量为9/3=3个,因为YOLO V3有3个feature_map,不同feature_map的size感受野是不一样的,较小size的feature_map具有较大的感受野,所以负责检测较大的物体,同理,较大size的feature_map感受野较小,负责检测较小的物体。

在yolo v1中,每个cell中的三个bbox(bounding box)只能用来预测同一个物体,但是到v3,作者取消了这一限制,同一个cell中不同的bbox可以用来预测不同的物体。了解了这个之后,并且获得了量化后的anchor,我们要怎么在实际模型中加入anchor的先验知识呢?

可能有人会说,可以让第一个bbox预测与第一个anchor相似的box,第二个bbox预测与第二个anchor相似的box,以此类推......那么问题来了,我们如何绑定每个gt(ground truth)对应的anchor,也就是说,我们要用bbox来预测哪一个ground truth,答案是IOU(交并比),也就是说与gt IOU最大的anchor对应的bbox来预测该gt。

接下来,还有一个问题,就是说我如何让bbox的形状保持与anchor类似,也就是说bbox在训练时即使会发生变化,但是仍然会保持与anchor类似的长宽比。yolo的做法是,不让bbox直接预测实际box的长和宽,而是与anchor的长和宽绑定,公式如下:

:anchor box的宽和高

:bbox预测的宽和高

:转换后实际的宽和高

这样不管bbox预测的

是多少,经过转换都与anchor的宽和高相关。

对于box中心点坐标

的预测是相对于grid cell来说的,公式如下:

其中:

是grid cell方格左上角顶点相对于整个feature map的坐标。

是bbox预测的坐标偏移,使用sigmoid函数将值压缩在0-1之间,这样可以保证最终预测的box的中心点在当前grid cell方格内。作者表示如果不进行这样压缩的话,模型训练可能难以收敛。最终,可以得到实际输出的box参数计算如下:

3.YOLO V3的前向传播和损失函数

3.1.YOLO V3的前向传播

如图一所示,yolo v3模型接受输入为416*416*3的图片,返回三个不同size的特征图y1(13*13*255)、y2(26*26*255)和y3(52*52*255),这里的255跟实际数据集的类别数有关。COCO数据集一共有80类,每个特征图的grid cell负责三个先验框anchor,所以有:

255 = 3 * [80(类别估计概率分布) + 1(置信度) + 4(预测框)]

对于训练初期,网络输出的y1、y2和y3会与实际框的位置信息、类别有较大差异,所以会产生非常大的损失,然后根据损失反向传递不断优化模型参数,直到模型收敛就好了.......

3.2.YOLO V3的损失函数

值得一提的是,作者在训练的时候并不是将

转换成
后与gt的对应参数求误差,而是采用相反的过程,将gt的参数转换为与
对应的
,然后再计算误差。

假设真实框的坐标为

,所以:

由于sigmoid的反函数比较难计算,所以直接计算对应的sigmoid函数的值:

这样,我们就可以根据训练的输出

以及gt对应的
求出误差。

所以接下来大致分为两部分,第一部分介绍对真值的操作过程,第二部分介绍得到要回归的真值之后如何与神经网络的输出构建loss

08d9bac8900311726882201b4a3dd3ca.png
图二:gt预处理

首先看对真值的操作过程。何为对真值的操作?上面已经说过,我们需要将gt转换成与网络输出相同的格式然后进行loss计算,下面结合图二,看一下整体流程:

输入是target,每一行是一个box的信息[image,class,x,y,w,h],分别代表属于batch中的哪张图片、box的类别以及框的坐标。然后针对不同的特征图尺寸进行单独处理,下面以最小尺寸为例,其他类似:

  1. 首先选取n个box的长宽真值wh_gt(n*2),分别与anchor计算IOU,保留大于iou阈值的框(m个),其他的框说明不适合在当前尺寸特征图下进行检测。
以下针对剩余的m个框

2. 提取框位置的真值xy_gt,并与向下取整后的值做差(之前说过,框位置坐标是相对于grid cell方格左上角顶点的偏移,而对于xy_gt向下取整正好对应着左上角的坐标),得到

3. 提取框尺寸真值wh_gt,并与对应的anchor比较,计算尺寸偏差

4. 提取框的类别cls。

5. 记录剩余m个框对应anchor的id和对应图片的id,以及位置取整后的值(对应grid cell左上角的坐标,表示用哪个格子进行预测)indicies。

在获得gt真值预处理后的结果indicies、

以及cls,如何与网络的输出构建loss呢?请看下图:

adfae85292ea60a5a6c48947d342677a.png
图三:yolo v3 loss计算
  1. 按照ouput的大小构建tconf,表示框的真实置信度。

2. 按照真值的indices将对应的某张图片中的某张格子的某个anchor的置信度置1。因为框要由这个图片的这个格子的这个anchor预测,所以这个图片中的这个格子的这个预测的置信度的真值应该为1。

3. 将ouput中的值与真值比较构建位置的loss(lxy),尺寸的loss(lwh),类别的loss(lcls),置信度的loss(lconf)。

4. 然后加权得到总的Loss。

具体的计算如下:

9e9f56907ac23c383d62bea0d96ae34c.png
图四:yolo v3损失函数
  • 参数
    在训练中,如果某个grid cell的bbox没有负责预测某个对象,那我们就不应该训练该bbox的条件类别概率和坐标参数,因为使用这些参数的前提是在明确清楚该bbox负责预测某个gt box(后面说明怎么决定是否负责),即不应该根据条件类别概率和中心坐标输出误差调整相应的weights,那如何不进行这部分训练呢,当然是不让他们对loss做出贡献,也就没有它们什么事情了,这个时候就需要参数
    了,当该bbox负责预测某个gt box时,
    ,否则
  • 参数
    的取值是由grid cell的bbox有没有负责预测某个对象决定的。如果负责,那么
    ,否则
    。如何确定有没有负责预测某个对象呢?前文已经说过,与gt IOU最大的anchor对应的bbox来预测该gt。
  • 参数
    :当某个bbox不负责对应grid cell中gt box的预测,但是又与该gt box的IOU大于设定的阈值时(论文中是0.5,darknet中针对COCO数据集使用的是0.7),忽略该bbox所有输出的对loss的误差贡献,包括置信度误差,这时
    ,其他情况

本篇大致说了一些yolo v3的框架,最后附上上文说的gt真值的预处理(build_targets)以及loss的计算(compute_loss),留着慢慢啃吧。。。。。。

def build_targets(p, targets, model):   #p:prediction
    # Build targets for compute_loss(), input targets(image,class,x,y,w,h)
    nt = targets.shape[0]   #一个batch中target的总数
    tcls, tbox, indices, anch = [], [], [], []
    gain = torch.ones(6, device=targets.device)  # normalized to gridspace gain
    off = torch.tensor([[1, 0], [0, 1], [-1, 0], [0, -1]], device=targets.device).float()  # overlap offsets

    style = None
    multi_gpu = type(model) in (nn.parallel.DataParallel, nn.parallel.DistributedDataParallel)
    for i, j in enumerate(model.yolo_layers):
        anchors = model.module.module_list[j].anchor_vec if multi_gpu else model.module_list[j].anchor_vec
        gain[2:] = torch.tensor(p[i].shape)[[3, 2, 3, 2]]  # xyxy gain
        na = anchors.shape[0]  # number of anchors
        at = torch.arange(na).view(na, 1).repeat(1, nt)  # anchor tensor, same as .repeat_interleave(nt)
        #type = (na,nt)
        
        # Match targets to anchors
        a, t, offsets = [], targets * gain, 0
        if nt:
            # r = t[None, :, 4:6] / anchors[:, None]  # wh ratio
            # j = torch.max(r, 1. / r).max(2)[0] < model.hyp['anchor_t']  # compare
            j = wh_iou(anchors, t[:, 4:6]) > model.hyp['iou_t']  # iou(3,n) = wh_iou(anchors(3,2), gwh(n,2))
            a, t = at[j], t.repeat(na, 1, 1)[j]  # filter

            # overlaps
            gxy = t[:, 2:4]  # grid xy
            z = torch.zeros_like(gxy)
            if style == 'rect2':
                g = 0.2  # offset
                j, k = ((gxy % 1. < g) & (gxy > 1.)).T
                a, t = torch.cat((a, a[j], a[k]), 0), torch.cat((t, t[j], t[k]), 0)
                offsets = torch.cat((z, z[j] + off[0], z[k] + off[1]), 0) * g

            elif style == 'rect4':
                g = 0.5  # offset
                j, k = ((gxy % 1. < g) & (gxy > 1.)).T
                l, m = ((gxy % 1. > (1 - g)) & (gxy < (gain[[2, 3]] - 1.))).T
                a, t = torch.cat((a, a[j], a[k], a[l], a[m]), 0), torch.cat((t, t[j], t[k], t[l], t[m]), 0)
                offsets = torch.cat((z, z[j] + off[0], z[k] + off[1], z[l] + off[2], z[m] + off[3]), 0) * g

        # Define
        b, c = t[:, :2].long().T  # image, class
        gxy = t[:, 2:4]  # grid xy
        gwh = t[:, 4:6]  # grid wh
        gij = (gxy - offsets).long()
        gi, gj = gij.T  # grid xy indices

        # Append
        indices.append((b, a, gj, gi))  # image, anchor, grid indices
        tbox.append(torch.cat((gxy - gij, gwh), 1))  # box
        anch.append(anchors[a])  # anchors
        tcls.append(c)  # class
        if c.shape[0]:  # if any targets
            assert c.max() < model.nc, 'Model accepts %g classes labeled from 0-%g, however you labelled a class %g. ' 
                                       'See https://github.com/ultralytics/yolov3/wiki/Train-Custom-Data' % (
                                           model.nc, model.nc - 1, c.max())

    return tcls, tbox, indices, anch


def compute_loss(p, targets, model):  # predictions, targets, model
    ft = torch.cuda.FloatTensor if p[0].is_cuda else torch.Tensor
    lcls, lbox, lobj = ft([0]), ft([0]), ft([0])
    tcls, tbox, indices, anchors = build_targets(p, targets, model)  # targets
    h = model.hyp  # hyperparameters
    red = 'mean' # Loss reduction (sum or mean)

 # Define criteria
    BCEcls = nn.BCEWithLogitsLoss(pos_weight=ft([h['cls_pw']]), reduction=red)
    BCEobj = nn.BCEWithLogitsLoss(pos_weight=ft([h['obj_pw']]), reduction=red)

 # class label smoothing https://arxiv.org/pdf/1902.04103.pdf eqn 3
    cp, cn = smooth_BCE(eps=0.0)

 # focal loss
    g = h['fl_gamma']  # focal loss gamma
 if g > 0:
        BCEcls, BCEobj = FocalLoss(BCEcls, g), FocalLoss(BCEobj, g)

 # per output
    nt = 0 # targets
 for i, pi in enumerate(p):  # layer index, layer predictions
        b, a, gj, gi = indices[i]  # image, anchor, gridy, gridx
        tobj = torch.zeros_like(pi[..., 0])  # target obj

        nb = b.shape[0]  # number of targets
 if nb:
            nt += nb  # cumulative targets
            ps = pi[b, a, gj, gi]  # prediction subset corresponding to targets

 # GIoU
            pxy = ps[:, :2].sigmoid()
            pwh = ps[:, 2:4].exp().clamp(max=1E3) * anchors[i]
            pbox = torch.cat((pxy, pwh), 1)  # predicted box
            giou = bbox_iou(pbox.t(), tbox[i], x1y1x2y2=False, GIoU=True)  # giou(prediction, target)
            lbox += (1.0 - giou).sum() if red == 'sum' else (1.0 - giou).mean()  # giou loss

 # Obj
            tobj[b, a, gj, gi] = (1.0 - model.gr) + model.gr * giou.detach().clamp(0).type(tobj.dtype)  # giou ratio

 # Class
 if model.nc > 1:  # cls loss (only if multiple classes)
                t = torch.full_like(ps[:, 5:], cn)  # targets
                t[range(nb), tcls[i]] = cp
                lcls += BCEcls(ps[:, 5:], t)  # BCE

 # Append targets to text file
 # with open('targets.txt', 'a') as file:
 #     [file.write('%11.5g ' * 4 % tuple(x) + 'n') for x in torch.cat((txy[i], twh[i]), 1)]

        lobj += BCEobj(pi[..., 4], tobj)  # obj loss

    lbox *= h['giou']
    lobj *= h['obj']
    lcls *= h['cls']
 if red == 'sum':
        bs = tobj.shape[0]  # batch size
        g = 3.0 # loss gain
        lobj *= g / bs
 if nt:
            lcls *= g / nt / model.nc
            lbox *= g / nt

    loss = lbox + lobj + lcls
 return loss, torch.cat((lbox, lobj, lcls, loss)).detach()
YOLOv7的损失函数主要用于目标检测任务中,通过优化模型的参数来最小化预测框与真实框之间的差异。损失函数的作用是在训练过程中评估预测框与真实框之间的差异,并根据差异来调整模型的参数,使得模型能够更准确地定位和分类目标物体。 具体来说,YOLOv7的损失函数中引入了改进的Repulsion Loss。这个改进的损失函数可以解决目标遮挡场景下的目标检测问题。它通过考虑目标之间的相互排斥关系,提高了在密集人群检测中的效果。这样的改进使得YOLOv7能够更好地应对数据集中目标遮挡检测的难点,提高了目标检测的精度和性能。 需要注意的是,YOLOv7的损失函数的改进方法不仅适用于改进YOLOv5,也可以应用于其他的YOLO网络,如YOLOv4、v3等。这些改进的源代码可以适用于不同版本的YOLO系列网络,为不同版本的YOLO网络带来精度和性能的提升。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [YOLOv7原创改进损失函数Repulsion:独家首发最新改进Repulsion Loss解决目标遮挡场景下检测,为解决密集...](https://blog.csdn.net/qq_38668236/article/details/131051154)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [[YOLOv7/YOLOv5系列算法改进NO.7]损失函数改进](https://blog.csdn.net/m0_70388905/article/details/125419887)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值