在代码注释中看到C2f其实是
CSP Bottleneck with 2 convolutions
找来CSP的图
大致是把一个output按channel拆成2部分,其中一部分不动,
另一部分过conv,再拼回去,
因为是with 2 convolutions, 会有2个conv.
下面根据代码来走一遍流程:
在ultralytics/nn/modules/block.py
class C2f(nn.Module):
"""CSP Bottleneck with 2 convolutions."""
def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion
super().__init__()
self.c = int(c2 * e) # hidden channels
self.cv1 = Conv(c1, 2 * self.c, 1, 1)
self.cv2 = Conv((2 + n) * self.c, c2, 1) # optional act=FReLU(c2)
self.m = nn.ModuleList(Bottleneck(self.c, self.c, shortcut, g, k=((3, 3), (3, 3)), e=1.0) for _ in range(n))
def forward(self, x):
"""Forward pass through C2f layer."""
y = list(self.cv1(x).chunk(2, 1))
y.extend(m(y[-1]) for m in self.m)
return self.cv2(torch.cat(y, 1))
假设输入x 为(1,32,104,160)
C2f:
cv1: conv2d(32,32, 1x1, s=1), SiLU
cv2: conv2d(48,32, 1x1, s=1), SiLU
x 过cv1, (1,32, 104, 160), 按channel分成2个16,得到y为list, 0:(1,16,104,160),1:(1,16,104,160)
y[-1]要过一个bottleneck:
class Bottleneck(nn.Module):
"""Standard bottleneck."""
def __init__(self, c1, c2, shortcut=True, g=1, k=(3, 3), e=0.5): # ch_in, ch_out, shortcut, groups, kernels, expand
super().__init__()
c_ = int(c2 * e) # hidden channels
self.cv1 = Conv(c1, c_, k[0], 1)
self.cv2 = Conv(c_, c2, k[1], 1, g=g)
self.add = shortcut and c1 == c2
def forward(self, x):
"""'forward()' applies the YOLOv5 FPN to input data."""
return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))
bottlenect的cv1: conv2d:(16,16, (3x3).s=1,p=1), SiLU, size不变
bottlenect的cv2: conv2d:(16,16, (3x3).s=1,p=1), SiLU, size不变,
加上y[-1]本身,还是(1,16,104,160), 加到y list的末尾,
最后cat channel, (1,48,104,160),
过C2f的cv2, 得到(1,32,104,160)