目录
yolov5 6.0版本 参考
https://blog.csdn.net/qq_37541097/article/details/123594351

可以看出yolov5- 5.0后面的版本主干主要由CSP模块变成了C3模块,yolov5-5.0之前的版本主干还是和yolov4类似的CSP结果,可参考
YOLOV5网络结构_Laughing-q的博客-CSDN博客_yolov5结构图
CSP和C3区别如下:
之前讲yolov4 yolov4原理_xd_MrCheng的博客-CSDN博客
时CSP结构如下:

一般来说CBM1只起到下采样2倍,真正意义上的CSP结构如下,把CBM1不要(因为CBM1只是为了下采样2倍)

其中CBM结构都是卷积核1x1且步长为1的卷积层
残差模组Resunit则是一个1x1的卷积加一个3x3的卷积,在相加
而C3结构和CSP结构有些不一样,C3结构如下:

其中CBM换成了CBS, 表示激活函数有Mish换成了SiLu
并且相对CSP,C3结构经过残差模组Resunit后少了一个卷积层, 直接连到了Concat,正好只有3个CBS,或者说只有3个卷积,可能才叫C3,并且C3中的残差模组,有的地方也叫BottleNeck1
单纯的C3结构是不会进行下采样的,每次下采样都是C3前面会接一个3x3且步长为2的卷积
在Neck部分的变化还是相对较大的,首先是将SPP换成成了SPPF(Glenn Jocher自己设计的),两者的作用是一样的,但后者效率更高。SPP结构如下图所示,是将输入并行通过多个不同大小的MaxPool,然后做进一步融合,能在一定程度上解决目标多尺度问题。

SPP结构

SPPF结构
而SPPF结构是将输入串行通过多个5x5大小的MaxPool层,这里需要注意的是串行两个5x5大小的MaxPool层是和一个9x9大小的MaxPool层计算结果是一样的,串行三个5x5大小的MaxPool层是和一个13x13大小的MaxPool层计算结果是一样的。
也就说用两个串联的5x5的MaxPool层替代一个9x9的maxPool层,因为两个5x5的串联的池化层效果就是9x9的池化层.
用3个串联的5x5的MaxPool层替代一个13x13的maxPool层, 以此减小计算时间。二者效果基本是一样的。但计算时间SPPF比SPP减半了
这和用2个3x3的卷积来替代5x5的卷积一样,减小计算量
在YOLOv4中,Neck的PFN和PAN结构是没有引入CSP结构的,但在YOLOv5中作者在FPN和PAN结构中加入了C3结构,但注意,Neck中C3中用到的叫bottleneck2, 和backbone中的bottleneck1(即残差模组resunit)是不一样的,bottleneck2没有残差连接,如下图:
Neck中的C3结构:

yolov5-Neck

YoloV5-Neck
yolov5的head和yolov4,yolov3一样
参考YOLOv5网络详解_太阳花的小绿豆的博客-CSDN博客_yolov5网络结构详解

继续参考YOLOv5网络详解_太阳花的小绿豆的博客-CSDN博客_yolov5网络结构详解



还是参考 YOLOv5网络详解_太阳花的小绿豆的博客-CSDN博客_yolov5网络结构详解



yolov3是GT中心点G落在那个格子,那个格子对应的Anchor就负责预测这个GT
比如上图,只有A这个格子回去预测GT,那现在由于网络预测目标中心点相对gird cell左上角的偏移调整到了-0.5-1.5,所以只要gird cell左上角点距离GT中心点在(-0.5,1.5)范围内它们对应的Anchor都能回归到GT的位置处,比如C点, 距离GT中心点x方向距离明显<0.5 , y方向的距离明<1.5, 所以C这个单元格满足, 同理B也是, 满足这样就扩充了正样本数量
更详细的介绍,参考【YOLOv5】正样本分配详解_mjiansun的博客-CSDN博客
具体如下:
Yolov5算法使用如下3种方式增加正样本个数:




如上图,绿色框是GT,假设中心点为绿色的点G, 红色实线锚框是1这个gird单元格对应的锚框,其中心点为A,也就是1单元格中心那个黑色的点,而网络预测的x,y偏移量其实就是想让虚线 红色实线锚框上下左右平移,使得中心点变为G,即让中心点与GT重合, 如红色虚线锚框(假设红色虚线锚框中心点是G),之前这个平移量范围是0-1之间的,现在变成了-0.5-1.5, 所以2和3这个单元格对应的anchor通过平移也能和GT的中心点重合,(在缩放宽度和高度,就能让anchor和GT完全重合,所以是一个平移+缩放的过程,这里只讨论平移)

debug 代码
上述正样本采样过程在yolov5通过函数build_targets实现,为了方便理解上述代码,对每一步进行debug,具体如
- def build_targets(self, p, targets):
- # Build targets for compute_loss(), input targets(image,class,x,y,w,h)
- na, nt = self.na, targets.shape[0] # number of anchors, targets
- tcls, tbox, indices, anch = [], [], [], []
- gain = torch.ones(7, device=targets.device) # normalized to gridspace gain
-
- ## 前置处理
- # same as .repeat_interleave(nt)
- ai = torch.arange(na, device=targets.device).float().view(na, 1).repeat(1, nt)
- targets = torch.cat((targets.repeat(na, 1, 1), ai[:, :, None]), 2) # append anchor indices
- g = 0.5 # bias
- off = torch.tensor([[0, 0],
- [1, 0], [0, 1], [-1, 0], [0, -1], # j,k,l,m
- # [1, 1], [1, -1], [-1, 1], [-1, -1], # jk,jm,lk,lm
- ], device=targets.device).float() * g # offsets
-
- for i in range(self.nl):
- ## target映射到当前层的尺度
- anchors = self.anchors[i]
- gain[2:6] = torch.tensor(p[i].shape)[[3, 2, 3, 2]] # xyxy gain
- # Match targets to anchors
- t = targets * gain
- if nt:
- # Matches
- r = t[:, :, 4:6] / anchors[:, None] # wh ratio
- j = torch.max(r, 1 / r).max(2)[0] < self.hyp['anchor_t'] # compare
- t = t[j] # filter
-
- # Offsets
- gxy = t[:, 2:4] # grid xy
- gxi = gain[[2, 3]] - gxy # inverse
- j, k = ((gxy % 1 < g) & (gxy > 1)).T
- l, m = ((gxi % 1 < g) & (gxi > 1)).T
- j = torch.stack((torch.ones_like(j), j, k, l, m))
- t = t.repeat((5, 1, 1))[j]
- offsets = (torch.zeros_like(gxy)[None] + off[:, None])[j]
- else:
- t = targets[0]
- offsets = 0
-
- # 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
- a = t[:, 6].long() # anchor indices
- indices.append((b, a, gj.clamp_(0, gain[3] - 1), gi.clamp_(0, gain[2] - 1))) # image, anchor, grid indices
- tbox.append(torch.cat((gxy - gij, gwh), 1)) # box
- anch.append(anchors[a]) # anchors
- tcls.append(c) # class
-
- return tcls, tbox, indices, anch








