YOLOv8-第Y9周:重要模块解读

  • 本文为365天深度学习训练营 中的学习记录博客
  • 原作者:K同学啊 | 接辅导、项目定制

文件路径: ...\ultralytics\nn\moudules\***

​ 以文件...\ultralytics\nn.moudules\conv.py为例,开头列举了该文件中定义的所有模型,如果需要新加一个模块,就需要在文件开头将其名称加入。(这是YOLOv8新增的一个类似声明的内容)

import math

inport numpy as np
import torch
import torch.nn as nn

__all__ = ('Conv', 'Conv2', 'LightConv', 'DWConv', 'DWConvTranspose2d', 'ConvTranspose', 'Focus', 'GhostConv',
           'ChannelAttention', 'SpatialAttention', 'CBAM', 'Concat', 'RepConv')

1. autopad

模块定义文件路径: ...\ultralytics\nn\modules\conv.py

  • 功能: 返回pad的大小,使得padding后输出张量的大小不变。
  • 参数:
    • k : 卷积核(kernel)的大小。类型可能是一个int , 也可能是一个序列
    • p : 填充(padding)的大小。默认为None
    • d : 扩张率(dilation rate)的大小,默认为1 。普通卷积的扩张率为1,空洞卷积的扩张率大于1。
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

2. Conv

模块定义文件路径: ...\ultralytics\nn\modules\conv.py

  • 功能: 标准卷积模块。
  • 参数:
    • 输入通道数(c1
    • 输出通道数(c2
    • 卷积核大小(k ,默认是1)
    • 步长(s , 默认是1)
    • 填充(p,默认为None)
    • 组(g,默认为1)
    • 扩张率(d,默认为1)
    • 是否采用激活函数(act,默认为True,且采用SiLU为激活函数)
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)))

    def forward_fuse(self, x):
        """Perform transposed convolution of 2D data."""
        return self.act(self.conv(x))

Conv类继承自nn.Module。它实现了标准的卷积操作,具有一些参数(ch_inch_outkernelstridepaddinggroupsdilationactivation)来定义卷积层的行为。

  1. Conv类的初始方法__init__中,首先调用了父类nn.Module的初始化方法super().__init__()
  2. 使用nn.Conv2d创建了一个卷积层self.conv,其中包括输入通道c1、输出通道c2、卷积核大小k、步长s、填充p、分组数g、膨胀率d、偏置bias等参数。
  3. 创建了皮归一化层self.bn,用于对卷积结果进行归一化处理。
  4. 根据act参数的类型,确定激活函数self.act,默认为nn.SiLU()

​ 在前向传播方法forward中,首先对输入张量x进行卷积操作self.conv(x),然后对卷积结果进行批归一化self.bn,最后使用激活函数self.act进行激活,并返回结果。

forward_fuse方法,用于执行转置卷积操作。他对输入张量x执行卷积操作self.conv(x),然后使用激活函数self.act进行激活,并返回结果。

3.Focus

模块定义文件路径: ...\ultralytics\nn\modules\conv.py

​ Focus是原作者自己设计出来,为了减少浮点数和提高速度,而不是增加feature map的,本质就是将图像进行切片,类似于下采样取值,将员图像的宽高信息切分,聚合到channel通道中。结构如下所示:

YOLOv8-第Y9周:重要模块解读_第1张图片

class Focus(nn.Module):
    """Focus wh information into c-space."""

    def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True):  # ch_in(输入通道数),ch_out(输出通道数),卷积核大小,步长,填充,分组数
        super().__init__()
        self.conv = Conv(c1 * 4, c2, k, s, p, g, act=act)
        # self.contract = Contract(gain=2)  # 降维模块(可选)

    def forward(self, x):  # x(b,c,w,h) -> y(b,4c,w/2,h/2) (输入x的维度为:批大小,通道数,宽度,高度;输入y的维度为:批大小,4倍通道数,宽度的一半,高度的一半)
        return self.conv(torch.cat((x[..., ::2, ::2], x[..., 1::2, ::2], x[..., ::2, 1::2], x[..., 1::2, 1::2]), 1))
        # 通过将输入张量x沿通道维度进行拼接四次得到新的输入,然后经过卷积层self.conv处理
        
        # 如果启用降维模块:
        # return self.conv(self.contract(x))

4. C2f

模块定义文件路径: ...\ultralytics\nn\modules\block.py

YOLOv8-第Y9周:重要模块解读_第2张图片

class C2f(nn.Module):
    """C2F层,快速实现带有2个卷积的CSP Bottleneck"""

    def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5): # ch_in为输入通道数,ch_out为输出通道数,n表示重复次数,shortcut表示是否使用快捷连接,g表示分组数,e表示扩展比例
        super().__init__()
        self.c = int(c2 * e)  # 隐藏通道数
        self.cv1 = Conv(c1, 2 * self.c, 1, 1)  # 第一个卷积层,用于将输入通道转换为2倍的隐藏通道数
        self.cv2 = Conv((2 + n) * self.c, c2, 1)  # 第二个卷积层,用于将隐藏通道数转换为输出通道数;也可选使用激活函数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))  # 创建包含n个Bottleneck块的ModuleList

    def forward(self, x):
        """通过C2f层进行前向传播"""
        y = list(self.cv1(x).chunk(2, 1))  # 将cv1的输出通道分为两部分
        y.extend(m(y[-1]) for m in self.m) # 将y的最后一个部分输入n个Bottleneck块中,得到一系列的输出
        return self.cv2(torch.cat(y, 1))   # 将这些输出连接起来并输入到cv2中进行最终的输出

    def forward_split(self, x):
        """使用split()而不是chunk()进行前向传播"""
        y = list(self.cv1(x).split((self.c, self.c), 1))  # 使用split函数将cv1的输出按指定大小分割
        y.extend(m(y[-1]) for m in self.m)  # 将y的最后一个部分输入n个Bottleneck块中,得到一系列的输出
        return self.cv2(torch.cat(y, 1))  # 将这些输出连接起来并输入到cv2中进行最终的输出

该模块包含了两个卷积层和一些Bottleneck模块的组合,下面是该类的主要成员和功能:

  • __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5 :初始化函数,接收输入通道数c1、输出通道数c2、重复次数n、是否使用shortcut连接shortcut、分组卷积的组数g、扩展因子e等参数。在初始化过程中,创建了包含两个卷积层和一些Bottleneck模块的组合。
  • forward(self, x):前向传播函数,接收输入张量x。在前向传播过程中,首先通过一个卷积层self.cv1对输入进行卷积操作,然后将输入分成两部分。接下来,通过一系列的Bottleneck模块self.m对其中一部分进行处理,并将处理后的结果与另一部分进行拼接。最后通过另一个卷积层self.cv2对拼接后的结果进行卷积操作,并返回输出张量。
  • forward_split(self, x) : 与 forward(self, x)类似的前向传播函数,但在处理输入分成两部分时,使用了split()方法代替了chrunk()方法。其余部分的功能与forward(self, x)相同。

你可能感兴趣的:(YOLO)