【YOLO】深入理解 CSP 瓶颈模块的变种:Bottleneck、C3、C3k、C2f 和 C3k2

发布于:2024-11-27 ⋅ 阅读:(442) ⋅ 点赞:(0)

深入理解 CSP 瓶颈模块的变种:Bottleneck、C3、C3k、C2f 和 C3k2

从 YOLOv3 到 YOLOv11,Ultralytics 团队结合当时的主流结构提出了各种适用于 YOLO 的模块,涵盖了不同的创新和优化思路,从而应对不断变化的目标检测需求。这些模块在每一代 YOLO 发展中扮演了重要角色,从特征提取的方式到模型的速度优化,体现了目标检测网络的逐步演进。从这些模块的发展中,我们可以看出目标检测网络在精度、速度和特征提取能力方面的逐步改进。这篇博客我们来探讨这些模块的演变过程:Bottleneck、C3、C3k、C2f 和 C3k2,理解它们之间的联系和区别,以及它们在不同应用场景中的适用性。

在这里插入图片描述


标准瓶颈模块:Bottleneck

在这里插入图片描述

Bottleneck 是最基础的模块,用于构建更复杂的 CSP 结构。它包含两个卷积层,能够有效地减少计算量并提取特征。这个模块还可以选择是否使用 shortcut 连接,以增强梯度传播。

class Bottleneck(nn.Module):
    """Standard bottleneck."""

    def __init__(self, c1, c2, shortcut=True, g=1, k=(3, 3), e=0.5):
        """Initializes a standard bottleneck module with optional shortcut connection and configurable parameters."""
        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):
        """Applies the YOLO FPN to input data."""
        return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))

Bottleneck 模块通过两个卷积层来提取特征,并可以选择是否加入shortcut,使其在某些情况下具有更好的梯度传播效果。

基础模块:C3

在这里插入图片描述

C3 是 CSP 瓶颈模块的一个基础版本,它的目的是通过增加特征的传递路径来提升网络的表现。C3 包含三个卷积层和一系列瓶颈层,能够高效提取不同层次的特征。

class C3(nn.Module):
    def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):
        super().__init__()
        c_ = int(c2 * e)  # hidden channels
        self.cv1 = Conv(c1, c_, 1, 1)  # 第一个卷积层
        self.cv2 = Conv(c1, c_, 1, 1)  # 第二个卷积层
        self.cv3 = Conv(2 * c_, c2, 1)  # 第三个卷积层,用于融合特征
        self.m = nn.Sequential(*(Bottleneck(c_, c_, shortcut, g) for _ in range(n)))  # 多个瓶颈层的组合

    def forward(self, x):
        return self.cv3(torch.cat((self.m(self.cv1(x)), self.cv2(x)), 1))  # 合并特征

C3 中,特征被分为两条路径,一条路径通过多层瓶颈层来提取复杂的特征,另一条路径直接传递输入特征。最后,通过拼接两条路径的输出,来增加模型的表达能力。

可定制卷积核的 C3k

C3kC3 模块的一个变体,主要改进在于它允许自定义卷积核的大小(kernel size)。这使得 C3k 可以更好地适应不同尺寸的图像特征,尤其是当我们需要捕捉更大范围的上下文信息时。

class C3k(C3):
    def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5, k=3):
        super().__init__(c1, c2, n, shortcut, g, e)
        self.m = nn.Sequential(*(Bottleneck(c_, c_, shortcut, g, k=(k, k)) for _ in range(n)))

与基础的 C3 不同,C3k 在初始化时允许设置不同的卷积核大小 k,以便更灵活地应对各种不同的特征提取需求。比如,在需要捕捉更大区域信息时,我们可以设置更大的 k 值。当 k 设置为 3 时,C3k 在功能上与 C3 相等。

加速模块:C2f

在这里插入图片描述

C2f 是一个为了提升处理速度而设计的 CSP 瓶颈模块。它通过减少卷积层的数量和采用更高效的特征合并策略来提高速度。

class C2f(nn.Module):
    def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5):
        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)  # 最终融合层
        self.m = nn.ModuleList(Bottleneck(self.c, self.c, shortcut, g) for _ in range(n))  # 瓶颈层列表

    def forward(self, x):
        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))  # 最终特征融合

C2f 中,通过减少瓶颈层的数量以及对特征的快速分割和合并,达到了加速网络的目的,非常适合在对速度要求较高的应用场景中使用。

灵活组合模块:C3k2

在这里插入图片描述

C3k2 结合了 C2f 的速度优势和 C3k 的灵活性。它允许在运行时选择是否使用 C3k 层来处理特征,提供了很高的可配置性。

class C3k2(C2f):
    def __init__(self, c1, c2, n=1, c3k=False, e=0.5, g=1, shortcut=True):
        super().__init__(c1, c2, n, shortcut, g, e)
        self.m = nn.ModuleList(
            C3k(self.c, self.c, 2, shortcut, g) if c3k else Bottleneck(self.c, self.c, shortcut, g) for _ in range(n)
        )

c3k 参数设置为 True 时,C3k2 将使用 C3k 层,能够利用不同卷积核大小的灵活性;否则,它将使用标准的瓶颈层,与 C2f 类似。

C3k2C2f 的不同之处在于,C3k2 通过 c3k 参数可以选择是否使用 C3k 模块中的可变卷积核大小。这使得 C3k2 在需要更灵活的特征提取时具备优势,例如在处理需要不同感受野的场景时,可以通过调整 C3k 来适应特定的特征提取需求。而 C2f 则保持固定的结构和速度优势,更适合对计算资源有严格限制的应用场景。

C3k2 的灵活性与 C3kC3 的关系

在这里插入图片描述

对于 C3k2 模块,当 c3k=Truec3k=False 时,其内部模块的选择会有所不同,这影响了其与 C3C3k 的关系:

  • C3k2c3k=True

    • 在这种情况下,C3k2 将使用 C3k 模块。C3k 模块允许使用不同大小的卷积核,这给模型带来了灵活性,特别是在需要不同感受野来处理复杂特征的场景。
    • 这样一来,C3k2 在内部使用 C3k 的特性,通过可配置的卷积核大小来适应不同的任务需求。
  • C3k2c3k=False

    • 在这种情况下,C3k2 使用的模块与标准的 C2f 类似,即使用了固定的 Bottleneck 层。
    • 在这个配置下,C3k2 在特性上与 C2f 没有区别,主要强调的是速度优化,而不是灵活性。

因此,C3k2c3k=Truec3k=False 时的区别在于是否使用了 C3k 模块的灵活卷积核。当 c3k=True 时,C3k2 可以提供更灵活的卷积核大小,因此它与 C3C3k 有区别。而当 c3k=False 时,C3k2C2f 一致,关注的是加速特性。

总结来说,C3k2c3k=True 时与 C3k 类似,强调灵活性,而在 c3k=False 时与 C2f 一致,强调速度和效率。这样,C3k2 既可以作为灵活的特征提取模块,也可以作为快速的特征融合模块,具体使用哪个取决于您的任务需求。

C3k2C2f 的不同之处在于,C3k2 通过 c3k 参数可以选择是否使用 C3k 模块中的可变卷积核大小。这使得 C3k2 在需要更灵活的特征提取时具备优势,例如在处理需要不同感受野的场景时,可以通过调整 C3k 来适应特定的特征提取需求。而 C2f 则保持固定的结构和速度优势,更适合对计算资源有严格限制的应用场景。

总结

  • Bottleneck 是最基础的模块,用于特征提取和减少计算量。
  • C3 是基础模块,包含三层卷积和多层瓶颈层,用于增强特征传递。
  • C3kC3 的基础上增加了卷积核大小的可配置性,使其更适合不同的特征提取需求。
  • C2f 通过简化结构来加速处理,适合对计算速度有较高要求的场景。
  • C3k2 结合了 C3kC2f 的优势,提供了更高的灵活性和速度。

在某些特定情况下,这些模块是等价的:

  • C3k 的卷积核大小 k 设置为 3 时,它在功能上与 C3 相等。此时,C3k 提供了与 C3 相同的特征提取能力,但增加了卷积核大小的可配置性。
  • C3k2c3k 参数设置为 False 时,C3k2 的功能与 C2f 相同。它在这种情况下只是在结构上提供了额外的灵活性,但并没有使用 C3k 层的优势。

这些模块的设计思路各有侧重,使用时可以根据具体任务的需求来选择合适的变种。例如,在需要快速处理和灵活特征提取的情况下,可以选择 C3k2 模块;而在需要保持基础结构的情况下,C3 则是不错的选择。没有绝对的好与不好,只有适合还是不适合。


网站公告

今日签到

点亮在社区的每一天
去签到