目录
Conv
Conv是yolov5中的最核心模块,代码如下:
# 为same卷积或same池化自动扩充,为了让卷积或池化以后的特征图的大小不变,在输入的特征图上要做0填充,填充多少通过以下函数进行计算
def autopad(k, p=None): # kernel, padding
# Pad to 'same'
if p is None:
# k是int类型则整除2;或若干的整数值则循环整除
p = k // 2 if isinstance(k, int) else [x // 2 for x in k] # auto-pad
return p
class Conv(nn.Module):
# Standard convolution 标准卷积:conv+BN+hardswish
# 初始化构造函数 c1为输入channel的值, c2为输出channel的值, kernel, stride, padding, groups
def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True):
super(Conv, self).__init__()
self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False)
self.bn = nn.BatchNorm2d(c2)
self.act = nn.Hardswish() if act else nn.Identity() # 若act=false, 则不使用激活函数
def forward(self, x): # 网络的执行顺序是根据forward函数来决定的
return self.act(self.bn(self.conv(x))) # conv->bn->hardswish
def fuseforward(self, x): # 没有BN,只有卷积和激活
return self.act(self.conv(x))
当不指定 p 时,Conv2d 的 pad 值是让 k//2 (除法向下取整) 计算得到的 (aotopad函数) 。
有以下两种情况:
- 当s=1时候,显然经过 Conv2d 后特征的大小不变;
- 当s=2时候,那么经过 Conv2d 后特征的大小减半;
Conv:Conv2d + BatchNorm2d + Hardswish,bias默认为False,即不使用bias偏置,Conv2d和BatchNorm2d结合使用以后,会进行融合操作,融合时卷积的base值会被消掉。
![]()
Conv模块
代码中使用 fuse_conv_and_bn 函数合并了 Conv2d 层和 BatchNorm2d 层。在模型训练完成后,代码在推理阶段和导出模型时,将卷积层和BN层进行融合。为了可视化,可以关闭 models / yolo.py -- fuse()
def fuse(self): # fuse model Conv2d() + BatchNorm2d() layers print('Fusing layers... ') # for m in self.model.modules(): # if type(m) is Conv and hasattr(m, 'bn'): # m._non_persistent_buffers_set = set() # pytorch 1.6.0 compatability # m.conv = fuse_conv_and_bn(m.conv, m.bn) # update conv # delattr(m, 'bn') # remove batchnorm # m.forward = m.fuseforward # update forward # self.info() return self当关闭 fuse() 后,也使用 Netron 对 yolov5s.torchscript.pt 可视化,看到 BN。
对于任何用到shape、size返回值的参数时,例如:tensor.view(tensor.size(0), -1)这类操作,避免直接使用tensor.size的返回值,而是加上int转换,tensor.view(int(tensor.size(0)), -1)。为了避免pytorch 导出 onnx 时候,对size 进行跟踪,跟踪时候会生成gather、shape的节点。
修改代码:models/yolo.py
# bs, _, ny, nx = x[i].shape # x(bs,255,20,20) to x(bs,3,20,20,85) bs, _, ny, nx = map(int, x[i].shape)
Focus


若要图显示输出的尺寸,可以修改 export.py 代码,并重新导出onnx
# Checks onnx.save(shape_inference.infer_shapes(onnx_model), f)
切片
x[..., ::2, ::2], 红色图 w, h 从0开始,每隔一个切一个
x[..., 1::2, ::2], 黄色图 w 从1开始, h 从0开始,每隔一个切一个
x[..., ::2, 1::2], 绿色图 w 从0开始, h 从1开始,每隔一个切一个
x[..., 1::2, 1::2] 蓝色图 w, h 从1开始,每隔一个切一个
Co