第一次写CSDN博客嘻嘻,深度学习入门小菜鸟,希望像做笔记记录自己学的东西,也希望能帮助到同样入门的人,更希望大佬们帮忙纠错啦~侵权立删。
目录
一、背景介绍
1.下采样
2.常见下采样做法
二、前身:YOLOv2中的PassThrough层
三、Focus层
1.原理
2.代码分析
Focus层是在YOLOv5中被提出来的。感觉像是一种特殊的下采样的方式。
下采样就是一种缩小图像的手法,用来降低特征的维度并保留有效信息,一定程度上避免过拟合,都是以牺牲部分信息为代价,换取数据量的减少。下采样就是池化操作。但是池化的目的不仅如此,还需要考虑旋转、平移、伸缩不变形等待。采样有最大值采样,平均值采样,随机区域采样等,对应池化:比如最大值池化,平均值池化,随机池化等。
平均值池化:对邻域内特征点只求平均,有点像平滑滤波,根据滑窗的尺寸控制下采样的力度,尺寸越大,它的采样率越高,但边缘信息损失越大。
最大值池化:对邻域内特征点取最大,类似锐化,突出滑窗内的细节点(特殊点)。
具体如下图所示:
PassThrough层和YOLOv5中的Focus层很像(前身?感觉好像作用一样啊,有没有知道的大佬可以给我讲讲)。他是将相邻的特征堆积在不同的通道中,这样可以将大尺度特征图下采样后与小尺度特征图进行融合,进而增加了小目标检测的精确度。
原理如下:
Focus层原理和PassThrough层很类似。它采用切片操作把高分辨率的图片(特征图)拆分成多个低分辨率的图片/特征图,即隔列采样+拼接。原理图如下:
原始的640 × 640 × 3的图像输入Focus结构,采用切片(slice)操作,先变成320 × 320 × 12的特征图,拼接(Concat)后,再经过一次卷积(CBL(后期改为SiLU,即为CBS))操作,最终变成320 × 320 × 64的特征图。
Focus层将w-h平面上的信息转换到通道维度,再通过3*3卷积的方式提取不同特征。采用这种方式可以减少下采样带来的信息损失 。
Focus层及其相关代码如下(models/common.py)
(YOLOv5源码:GitHub - ultralytics/yolov5: YOLOv5 in PyTorch > ONNX > CoreML > TFLite:)
def autopad(k, p=None): # kernel, padding自动填充的设计,更加灵活多变
# Pad to 'same'
if p is None:
p = k // 2 if isinstance(k, int) else [x // 2 for x in k]
# auto-pad自动填充,通过自动设置填充数p
#如果k是整数,p为k与2整除后向下取整;如果k是列表等,p对应的是列表中每个元素整除2。
return p
class Conv(nn.Module):
# 这里对应结构图部分的CBL,CBL = conv+BN+Leaky ReLU
def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups
super().__init__()
self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False)
self.bn = nn.BatchNorm2d(c2)
#将其变为均值为0,方差为1的正态分布,通道数为c2
self.act = nn.SiLU() if act is True else (act if isinstance(act, nn.Module) else nn.Identity())
#其中nn.Identity()是网络中的占位符,并没有实际操作,在增减网络过程中,可以使得整个网络层数据不变,便于迁移权重数据;nn.SiLU()一种激活函数(S形加权线性单元)。
def forward(self, x):#正态分布型的前向传播
return self.act(self.bn(self.conv(x)))
def forward_fuse(self, x):#普通前向传播
return self.act(self.conv(x))
class Focus(nn.Module):
# Focus wh information into c-space
def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups
super().__init__()
self.conv = Conv(c1 * 4, c2, k, s, p, g, act)
# self.contract = Contract(gain=2)
def forward(self, x): # x(b,c,w,h) -> y(b,4c,w/2,h/2)
return self.conv(torch.cat([x[..., ::2, ::2], x[..., 1::2, ::2], x[..., ::2, 1::2], x[..., 1::2, 1::2]], 1))
#图片被分为4块。x[..., ::2, ::2]指图片的左上角那一块, x[..., 1::2, ::2]指右上角那一块,x[..., ::2, 1::2]指左下角那一块,x[..., 1::2, 1::2]指右下角那一块。都是每隔一个采样(采奇数列)。用cat连接这些采样图,生成通道数为12的特征图
# return self.conv(self.contract(x))
先采取切片操作(x[..., ::2, ::2], x[..., 1::2, ::2], x[..., ::2, 1::2], x[..., 1::2, 1::2] )把图片分成1,2,3,4共4块(如上面的原理图)
然后进行一个连接
然后再来一次卷积,这里的卷积是自定义卷积:先进行一次卷积,然后变化成正态分布,最后来个SiLU激活,完成。