卷积模块:YOLOV8中的卷积模块是由卷积-BN-激活函数一键三连组成
def autopad(k, p=None, d=1): # kernel, padding, dilation
"""Pad to 'same' shape outputs."""
if d > 1:
k = d * (k - 1) + 1 if isinstance(k, int) else [d * (x - 1) + 1 for x in k] # actual kernel-size
if p is None:
p = k // 2 if isinstance(k, int) else [x // 2 for x in k] # auto-pad
return p
class Conv(nn.Module):
"""Standard convolution with args(ch_in, ch_out, kernel, stride, padding, groups, dilation, activation)."""
default_act = nn.SiLU() # default activation
def __init__(self, c1, c2, k=1, s=1, p=None, g=1, d=1, act=True):
"""Initialize Conv layer with given arguments including activation."""
super().__init__()
self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p, d), groups=g, dilation=d, bias=False)
self.bn = nn.BatchNorm2d(c2)
self.act = self.default_act if act is True else act if isinstance(act, nn.Module) else nn.Identity()
def forward(self, x):
"""Apply convolution, batch normalization and activation to input tensor."""
return self.act(self.bn(self.conv(x)))
1、C1模块
代码:
class C1(nn.Module):
"""CSP Bottleneck with 1 convolution."""
def __init__(self, c1, c2, n=1):
"""Initializes the CSP Bottleneck with configurations for 1 convolution with arguments ch_in, ch_out, number."""
super().__init__()
self.cv1 = Conv(c1, c2, 1, 1)
self.m = nn.Sequential(*(Conv(c2, c2, 3) for _ in range(n)))
def forward(self, x):
"""Applies cross-convolutions to input in the C3 module."""
y = self.cv1(x)
print(y.size())
return self.m(y) + y
C1模块,是由2个卷积组成,一个教CV1,一个是Sequential,里面默认有一个卷积。
CV1,输入大小为(batch,c1,H,W),输出为(batch,c2,H,W)
m,输入大小为(batch,c2,H,W),输出大小为(batch,c2,H,W)
经过cv1,再经过m,在加到原来位置上,这里是对应位置加,不是拼接
输入大小为(1,10,256,256)
data = torch.zeros(1,10,256,256)
M=C1(10, 20, 3)
out = M(data)
2、代码难点
self.m = nn.Sequential(*(Conv(c2, c2, 3) for _ in range(n)))
self.m = nn.Sequential(*(Conv(c2, c2, 3) for _ in range(n)))
有的老哥可能python学的是一知半解,可能有的老哥看到这个就有点懵逼,咋有个*号。
请认真看下面这2个例子:
例子1
def Test(*args):
print('参数个数:', len(args))
print('参数内容:', args)
data=[1, 2, 3, 4]
Test(data)
结果显示我们这里就只有一个参数,但参数内容就一个大数组。
例子2
传参的时候,把*data传进去。
def Test(*args):
print('参数个数:', len(args))
print('参数内容:', args)
data=[1, 2, 3, 4]
Test(*data)
刚刚好是我们那4个数
我们是想把data传进函数,可以看出,第二种方式更符合我们的要求。
这个时候尝试取出参数的第一个数:
def Test(*args):
print('参数个数:', len(args))
print('参数内容第一个数:', args[0])
2种方式是不同的:
这个时候去打开sequential的源码;
别的就不用看了,就看参数初始化的参数列表,是带*的,这里就不深究了,毕竟不是软件工程的,如果不想加*,可以去尝试改一下源码,把参数中的第一个参数取出来,然后在是各种操作。
self.m = nn.Sequential(*[Conv(c2, c2, 3) for _ in range(n)])
self.m = nn.Sequential(*(Conv(c2, c2, 3) for _ in range(n)))
正如源码中所写那样参数列表是带*的,那我们在将一个list传进去的时候,是应该带一个*的