yolov5核心代码理解: anchor匹配策略-跨网格预测,compute_loss(p, targets, model)和build_targets(p, targets, model)理解

本文主要讲述yolov5anchor匹配策略-跨网格预测以及损失函数计算的核心过程理解,网络部分相对容易这里不再赘述。

1. yolov5跨网格匹配策略

yolov5最重要的便是跨网格进行预测,从当前网格的上、下、左、右的四个网格中找到离目标中心点最近的两个网格,再加上当前网格共三个网格进行匹配。增大正样本的数量,加快模型收敛。

 j, k = ((gxy % 1. < g) & (gxy > 1.)).Tl, m = ((gxy % 1. > (1 - g)) & (gxy < (gain[[2, 3]] - 1.))).T# a:anchor索引值,包含添加的正样本(让近于当前网格中心点的上下左右中的两个网格充当正样本)信息# t: gt框信息, 包含添加的正样本# offsets: 每个框中心点的偏移值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

yolov5预测bbox公式如下:

  • tx,ty,tw,th:预测的坐标信息
  • bx,xy,bw,bh: 最终预测坐标信息
  • б:表示sigmoid,将坐标归一化到0~1
  • cx,cy: 中心点所在的网格的左上角坐标
  • pw,py: anchor框的大小

代码如下:

pxy = ps[:, :2].sigmoid() * 2. - 0.5
pwh = (ps[:, 2:4].sigmoid() * 2) ** 2 * anchors[i]

2. yolov5核心代码compute_loss和build_targets理解


def compute_loss(p, targets, model):  # predictions, targets, model# p 预测结果, list[torch.tensor * 3], p[i] = (b, 3, h, w, nc + 5[x,y,w,h,conf])  --- h,w (80, 80)、(40, 40)、 (20, 20)ft = torch.cuda.FloatTensor if p[0].is_cuda else torch.Tensorlcls, lbox, lobj = ft([0]), ft([0]), ft([0])tcls, tbox, indices, anchors = build_targets(p, targets, model)  # 类别,box, 索引, anchorsh = model.hyp  # hyperparametersred = '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 3cp, cn = smooth_BCE(eps=0.0)# focal lossg = h['fl_gamma']  # focal loss gammaif g > 0:BCEcls, BCEobj = FocalLoss(BCEcls, g), FocalLoss(BCEobj, g)# per outputnt = 0  # targetsfor i, pi in enumerate(p):  # layer index, layer predictionsb, a, gj, gi = indices[i]  # image, anchor, gridy, gridxtobj = torch.zeros_like(pi[..., 0])  # target objnb = b.shape[0]  # number of targetsif nb:nt += nb  # cumulative targets# 取出对应位置预测值ps = pi[b, a, gj, gi]  # prediction subset corresponding to targets# GIoUpxy = ps[:, :2].sigmoid() * 2. - 0.5pwh = (ps[:, 2:4].sigmoid() * 2) ** 2 * anchors[i]pbox = torch.cat((pxy, pwh), 1)  # predicted boxgiou = 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 有物体的conf分支权重tobj[b, a, gj, gi] = (1.0 - model.gr) + model.gr * giou.detach().clamp(0).type(tobj.dtype)  # giou ratio# Classif model.nc > 1:  # cls loss (only if multiple classes)t = torch.full_like(ps[:, 5:], cn)  # targetst[range(nb), tcls[i]] = cplcls += BCEcls(ps[:, 5:], t)  # BCE 每个类单独计算Loss# 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 losslbox *= h['giou']lobj *= h['obj']lcls *= h['cls']bs = tobj.shape[0]  # batch sizeif red == 'sum':g = 3.0  # loss gainlobj *= g / bsif nt:lcls *= g / nt / model.nclbox *= g / ntloss = lbox + lobj + lclsreturn loss * bs, torch.cat((lbox, lobj, lcls, loss)).detach()def build_targets(p, targets, model):# Build targets for compute_loss(), input targets(image,class,x,y,w,h)# 获取检测层det = model.module.model[-1] if type(model) in (nn.parallel.DataParallel, nn.parallel.DistributedDataParallel) \else model.model[-1]  # Detect() modulena, nt = det.na, targets.shape[0]  # number of anchors, targetstcls, 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# anchor索引,表示当前bbox和当前层哪个anchor匹配at = torch.arange(na).view(na, 1).repeat(1, nt)  # anchor tensor, same as .repeat_interleave(nt)style = 'rect4'#   处理每个检测层(三个)for i in range(det.nl):# 三个anchors,已经除以当前特征图对应的strideanchors = det.anchors[i]# 将target中归一化后的xywh映射到三个尺度(80,80, 40,40, 20,20)的输出需要的系数gain[2:] = torch.tensor(p[i].shape)[[3, 2, 3, 2]]  # xyxy gain# Match targets to anchors# t 将标签框的xywh从基于0-1映射到基于特征图,target的xywh本身是归一化尺度,需要变成特征图尺寸# t GT框 shape:(nt, 6) , 6 = icxywh, i 指第i+1张图片,c为类别a, t, offsets = [], targets * gain, 0# 判断是否存在目标if nt:# 计算当前tartget的wh和anchor的wh比值# 如果最大比值大于预设值model.hyp['anchor_t']=4,则当前target和anchor匹配度不高,不强制回归,而把target丢弃r = t[None, :, 4:6] / anchors[:, None]  # wh ratio# 筛选满足条件1/hyp['anchor_t] < target_wh / anchor_wh < hyp['anchor_t]的框#.max(2)对第三维度的值进行maxj = 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))# t.repeat(na, 1, 1)是的t内的gtbox信息与at索引相对应, repeat(3,1,1)第一维度重复三次# a 筛选后的gtbox的anchor索引, 筛选后的gtbox信息a, t = at[j], t.repeat(na, 1, 1)[j]  # filter# overlapsgxy = t[:, 2:4]  # grid xy,label的中心点坐标z = torch.zeros_like(gxy)# 取得上下左右四个网格中近于当前网格中心点中的其中两个"""把相对于各个网格左上角x<0.5,y<0.5和相对于右下角的x<0.5,y<0.5的框提取出来,就是j,k,l,m在选取gij(标签分配的网格)的时候对这四个部分都做一个偏移(加去上面的off),"""if style == 'rect2':g = 0.2  # offsetj, k = ((gxy % 1. < g) & (gxy > 1.)).Ta, 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) * gelif style == 'rect4':g = 0.5  # offset# 对于筛选后的bbox,计算其落在哪个网格内,同时找出邻近的网格,将这些网格都认为是负责预测该bbox的网格# 浮点数取模的数学定义:对于两个浮点数a和b: a / b = a - n * b , 其中n为不超过 a/b的最大整数# gxy > 1.和gxy < (gain[[2, 3]] - 1.)判断中心点是否落在四个角,如果落在四个角,就不取界外的框(因为超出了边界)j, k = ((gxy % 1. < g) & (gxy > 1.)).Tl, m = ((gxy % 1. > (1 - g)) & (gxy < (gain[[2, 3]] - 1.))).T# a:anchor索引值,包含添加的正样本(让近于当前网格中心点的上下左右中的两个网格充当正样本)信息# t: gt框信息, 包含添加的正样本# offsets: 每个框中心点的偏移值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"""对每个bbox找出对应的正样本anchor。a 表示当前bbox和当前层的第几个anchor匹配b 表示当前bbox属于batch内部的第几张图片,c 是该bbox的类别gi,gj 是对应的负责预测该bbox的网格坐标gxy 负责预测网格中心点坐标xygwh 是对应的bbox的wh"""b, c = t[:, :2].long().T  # image, classgxy = t[:, 2:4]  # grid xygwh = t[:, 4:6]  # grid wh# 当前label落在哪个网格上gij = (gxy - offsets).long()gi, gj = gij.T  # grid xy indices# Append 添加索引,方便计算损失的时候去除对应位置的输出indices.append((b, a, gj, gi))  # image, anchor, grid indices# gxy - gij: 预测中心点坐标相对于gi,gj的偏移值tbox.append(torch.cat((gxy - gij, gwh), 1))  # boxanch.append(anchors[a])  # anchors,使用哪个anchor尺寸tcls.append(c)  # classreturn tcls, tbox, indices, anch

3. 总结

yolov5增加正样本的方法,最多可增大到原来的三倍,大大增加了正样本的数量,加速了模型的收敛。
目标检测重中之重可以理解为anchor的匹配策略,当下流行的anchor-free不过换了一种匹配策略罢了。
我想当下真正可创新之处在于更优的匹配策略。
鄙人拙见,请不吝指教。

yolov5核心代码: anchor匹配策略,compute_loss和build_targets理解相关推荐

  1. 【玩转yolov5】之anchor匹配策略(build_targets)分析(1)

    这里我们实际推演一下yolov5训练过程中的anchor匹配策略,为了简化数据和便于理解,设定以下训练参数. 输入分辨率(img-size):608x608 分类数(num_classes):2 ba ...

  2. YOLOV5训练代码train.py注释与解析

    YOLOv5代码注释版更新啦,注释的是最近的2021.07.14的版本,且注释更全 github: https://github.com/Laughing-q/yolov5_annotations Y ...

  3. YOLOv5核心基础知识讲解

    我这主要是江大白老师的内容!! 深入浅出Yolo系列之Yolov3&Yolov4&Yolov5&Yolox核心基础知识完整讲解(CSDN) 深入浅出Yolo系列之Yolov5核 ...

  4. GitHub上YOLOv5开源代码的训练数据定义

    GitHub上YOLOv5开源代码的训练数据定义 代码地址:https://github.com/ultralytics/YOLOv5 训练数据定义地址:https://github.com/ultr ...

  5. 淘宝店铺图片数据迁移核心代码

    核心代码 using System; using System.Collections.Generic; using System.Linq; using System.Text; using Sys ...

  6. 找不到匹配的key exchange算法_目标检测--匹配策略

    CVPR2020中的文章ATSS揭露到anchor-based和anchor-free的目标检测算法之间的效果差异原因是由于正负样本的选择造成的.而在目标检测算法中正负样本的选择是由gt与anchor ...

  7. Java 线程池框架核心代码分析

    转载自 Java 线程池框架核心代码分析 前言 多线程编程中,为每个任务分配一个线程是不现实的,线程创建的开销和资源消耗都是很高的.线程池应运而生,成为我们管理线程的利器.Java 通过Executo ...

  8. nuxt.js的核心代码_Nuxt.js中的通用应用程序代码结构

    nuxt.js的核心代码 by Krutie Patel 通过克鲁蒂·帕特尔(Krutie Patel) Nuxt.js中的通用应用程序代码结构 (Universal application code ...

  9. 第13章 程序的动态加载和执行(三,核心代码)

    这个核心代码也是本书唯一的一个核心代码,把这个读懂了,本书基本上通了,这个核心代码不难,只是前面知识的综合应用而已,所以用一到两个星期把这个三个程序读熟再进行下面的四章. 怎么样才算是读通了一个代码: ...

最新文章

  1. 【洛谷 P2464】[SDOI2008]郁闷的小J(线段树)
  2. android开发桌面源码,android launcher 源码 自己开发启动桌面
  3. SpringMVC RequestMapping注解详解
  4. iOS-在团队开发过程中控制代码版本
  5. 安卓开发笔记——关于图片的三级缓存策略(内存LruCache+磁盘DiskLruCache+网络Volley)...
  6. 好男人都结婚了吗?最后的研究结论亮了……
  7. python生成4位验证码_Python 生成4位验证码图片
  8. OnScrollListener
  9. NLP领域最优秀的8个预训练模型(附开源地址)
  10. 随手写了个android应用
  11. 服务器怎么架设为虚拟主机,架设服务器虚拟主机教程
  12. GB28181公网语音对讲
  13. c语言最长递增子序列nlogn,最长递增子序列
  14. 基于stm32的智能婴儿床(毕业设计)
  15. 第一章第十二题(以千米计的平均速度)(Average speed in kilometers)
  16. Nature计算社会科学特刊:如何对21世纪人类社会进行有意义的度量?
  17. 快速批量创建文件夹、文件的快捷键
  18. Java 3d 三维图形库使用
  19. Flowable 流程实例的挂起(暂停)与激活
  20. 2022低压电工考试模拟100题及答案

热门文章

  1. 用bioconda安装salmon报错
  2. PHP如何获取回调地址中的数据_php 回调地址,并返回参数的方法
  3. 结巴(jieba)分词 java 实现
  4. oracle logfile sync,发布版本2019_联众智慧内部oracle培训oracle培训相关emr_logfilesync_issue...
  5. 分享10个比B站更刺激的网站,千万别轻易点开
  6. 退休20年养老金需百万 40岁之前储备更保险
  7. 疫情过后适合做什么生意?
  8. 无限风光 : 近来地形算法学习小结【转】
  9. 唯一杰出级!百度智能云曦灵获信通院权威认证
  10. 计算机编程基础知识:程序员必知的硬核知识大全