在代码注释中看到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)

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐