YOLOv8-ultralytics-8.2.103部分代码阅读笔记-conv.py
conv.py
ultralytics\nn\modules\conv.py
目录
conv.py
1.所需的库和模块
2.def autopad(k, p=None, d=1):
3.class Conv(nn.Module):
4.class Conv2(Conv):
5.class LightConv(nn.Module):
6.class DWConv(Conv):
7.class DWConvTranspose2d(nn.ConvTranspose2d):
8.class ConvTranspose(nn.Module):
9.class Focus(nn.Module):
10.class GhostConv(nn.Module):
11.class RepConv(nn.Module):
12.class ChannelAttention(nn.Module):
13.class SpatialAttention(nn.Module):
14.class CBAM(nn.Module):
15.class Concat(nn.Module):
1.所需的库和模块
# Ultralytics YOLO 🚀, AGPL-3.0 license
# 卷积模块。
"""Convolution modules."""
import math
import numpy as np
import torch
import torch.nn as nn
# 这段代码定义了一个名为 __all__ 的元组,它通常用于 Python 模块中,以明确指出该模块导出哪些名称。当有人导入这个模块时,只有 __all__ 中列出的名称会被导出。这是一种控制模块导出内容的方式,使得模块的使用者可以清楚地知道哪些类和函数是模块的公共API。
__all__ = (
"Conv",
"Conv2",
"LightConv",
"DWConv",
"DWConvTranspose2d",
"ConvTranspose",
"Focus",
"GhostConv",
"ChannelAttention",
"SpatialAttention",
"CBAM",
"Concat",
"RepConv",
)
2.def autopad(k, p=None, d=1):
# 这段代码定义了一个名为 autopad 的函数,其目的是自动计算卷积层所需的填充(padding),以便在卷积操作后输出特征图的尺寸与输入特征图的尺寸相同,这通常被称为“same”填充。
# 参数说明。
# k :卷积核的大小,可以是一个整数或一个整数列表。
# p :填充的大小,可以是一个整数或一个整数列表。如果为 None ,则会自动计算。
# d :扩张率(dilation),默认为 1。
def autopad(k, p=None, d=1): # kernel, padding, dilation
# 填充至“相同”形状的输出。
"""Pad to 'same' shape outputs."""
# 扩张卷积核大小。
# 如果扩张率 d 大于 1,需要调整卷积核大小 k ,因为扩张卷积会影响实际的卷积核大小。
if d > 1:
# 如果 k 是一个整数,计算实际的卷积核大小为 d * (k - 1) + 1 。
# 如果 k 是一个列表,对列表中的每个元素应用上述计算。
k = d * (k - 1) + 1 if isinstance(k, int) else [d * (x - 1) + 1 for x in k] # actual kernel-size
# 自动计算填充。
# 如果 p 为 None ,则根据卷积核大小 k 自动计算填充大小。
if p is None:
# 如果 k 是一个整数,填充大小 p 计算为 k // 2 。
# 如果 k 是一个列表,对列表中的每个元素应用上述计算。
p = k // 2 if isinstance(k, int) else [x // 2 for x in k] # auto-pad
# 返回值。函数返回计算出的填充大小 p 。
return p
# autopad 函数是一个实用的工具,用于在构建卷积神经网络时自动计算所需的填充,以确保输入和输出特征图的尺寸相同。这对于保持特征图的空间维度在网络中传递时非常重要,尤其是在实现需要“same”填充的网络结构时。
3.class Conv(nn.Module):
# 这段代码定义了一个名为 Conv 的类,它是 PyTorch 的一个模块,用于实现一个标准的二维卷积层,包括卷积操作、批量归一化和激活函数。
# 定义了一个名为 Conv 的类,它继承自 PyTorch 的 nn.Module ,用于实现标准的二维卷积层。
class Conv(nn.Module):
# 标准卷积,带有参数(ch_in、ch_out、kernel、stride、padding、groups、dilation、activation)。
"""Standard convolution with args(ch_in, ch_out, kernel, stride, padding, groups, dilation, activation)."""
# 默认激活函数。定义了一个类变量 default_act ,它是这个类中卷积层的默认激活函数,这里使用的是 nn.SiLU() ,也称为 Swish 激活函数。
default_act = nn.SiLU() # default activation
# 初始化方法。定义了 __init__ 方法,它是类的构造函数,用于初始化 Conv 类的实例。
# 1.c1 :参数指定输入通道的数量。
# 2.c2 :参数指定输出通道的数量。
# 3.k :参数指定卷积核的大小,默认为 1。
# 4.s :参数指定卷积的步长,默认为 1。
# 5.p :参数指定卷积的填充大小,如果为 None ,则会自动计算。
# 6.g :参数指定卷积的组数,默认为 1。
# 7.d :参数指定卷积的扩张率,默认为 1。
# 8.act :参数指定是否使用激活函数,默认为 True 。
def __init__(self, c1, c2, k=1, s=1, p=None, g=1, d=1, act=True):
# 使用给定的参数(包括激活)初始化 Conv 层。
"""Initialize Conv layer with given arguments including activation."""
# 超类初始化。调用父类 nn.Module 的构造函数,完成类的初始化。
super().__init__()
# 卷积层。创建一个 nn.Conv2d 卷积层,根据输入参数进行配置。
# c1 和 c2 分别是输入和输出通道数。 k 是卷积核大小。 s 是步长。 autopad(k, p, d) 计算自动填充的大小,以保持输入和输出的空间尺寸相同。 groups=g 指定卷积的组数。 dilation=d 指定卷积的扩张率。 bias=False 表示不使用偏置项。
self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p, d), groups=g, dilation=d, bias=False)
# 批量归一化层。创建一个 nn.BatchNorm2d 批量归一化层,用于归一化卷积层的输出。 c2 是输出通道数。
self.bn = nn.BatchNorm2d(c2)
# 激活函数。根据 act 参数的值配置激活函数。 如果 act 为 True ,则使用默认激活函数 self.default_act 。 如果 act 是 nn.Module 的实例,则使用指定的激活函数。 否则,使用 nn.Identity() ,即不应用激活函数。
self.act = self.default_act if act is True else act if isinstance(act, nn.Module) else nn.Identity()
# 前向传播方法。定义了 forward 方法,它是模块在前向传播时的行为。
# x :是输入张量。
def forward(self, x):
# 对输入张量应用卷积、批量标准化和激活。
"""Apply convolution, batch normalization and activation to input tensor."""
# self.conv(x) 将输入张量 x 通过卷积层。 self.bn(self.conv(x)) 将卷积层的输出通过批量归一化层。 self.act(self.bn(self.conv(x))) 将批量归一化层的输出通过激活函数,得到最终的输出。
return self.act(self.bn(self.conv(x)))
# 融合前向传播方法。定义了 forward_fuse 方法,提供了一个简化的前向传播过程,只包括卷积和激活函数,不包括批量归一化。
# x :是输入张量。
def forward_fuse(self, x):
# 对二维数据执行转置卷积。
"""Perform transposed convolution of 2D data."""
# self.conv(x) 将输入张量 x 通过卷积层。 self.act(self.conv(x)) 将卷积层的输出通过激活函数,得到最终的输出。
return self.act(self.conv(x))
# 这个 Conv 类提供了一个灵活且功能丰富的卷积层实现,可以根据输入参数自动配置卷积层,并可以选择性地应用激活函数。这个类可以作为构建更复杂神经网络模型的基本构建块。
4.class Conv2(Conv):
# 这段代码定义了一个名为 Conv2 的类,它是 Conv 类的子类,用于实现一个简化的 RepConv 模块,其中包含了卷积的融合操作。
# 定义了一个名为 Conv2 的类,它继承自 Conv 类。 类的文档字符串说明这个类是一个简化的 RepConv 模块,具有卷积融合功能。
class Conv2(Conv):
# 简化的 RepConv 模块与 Conv 融合。
"""Simplified RepConv module with Conv fusing."""
# 初始化方法。定义了 __init__ 方法,它是类的构造函数,用于初始化 Conv2 类的实例。
# 1.c1 :参数指定输入通道的数量。
# 2.c2 :参数指定输出通道的数量。
# 3.k :参数指定卷积核的大小,默认为 3。
# 4.s :参数指定卷积的步长,默认为 1。
# 5.p :参数指定卷积的填充大小,如果为 None ,则会自动计算。
# 6.g :参数指定卷积的组数,默认为 1。
# 7.d :参数指定卷积的扩张率,默认为 1。
# 8.act :参数指定是否使用激活函数,默认为 True 。
def __init__(self, c1, c2, k=3, s=1, p=None, g=1, d=1, act=True):
# 使用给定的参数(包括激活)初始化 Conv 层。
"""Initialize Conv layer with given arguments including activation."""
# 调用父类 Conv 的构造函数,完成类的初始化。
super().__init__(c1, c2, k, s, p, g=g, d=d, act=act)
# 额外的 1x1 卷积层。创建一个额外的 1x1 卷积层 self.cv2 ,用于实现 RepConv 模块中的融合操作。
# c1 和 c2 分别是输入和输出通道数。 卷积核大小为 1。 步长为 s 。 autopad(1, p, d) 计算自动填充的大小。 groups=g 指定卷积的组数。 dilation=d 指定卷积的扩张率。 bias=False 表示不使用偏置项。
self.cv2 = nn.Conv2d(c1, c2, 1, s, autopad(1, p, d), groups=g, dilation=d, bias=False) # add 1x1 conv
# 前向传播方法。定义了 forward 方法,它是模块在前向传播时的行为。
# x :是输入张量。
def forward(self, x):
# 使用给定的参数(包括激活)初始化 Conv 层。
"""Apply convolution, batch normalization and activation to input tensor."""
# self.conv(x) 将输入张量 x 通过父类中的卷积层。 self.cv2(x) 将输入张量 x 通过额外的 1x1 卷积层。 self.conv(x) + self.cv2(x) 将两个卷积层的输出相加。
# self.bn(...) 将相加后的输出通过批量归一化层。 self.act(...) 将批量归一化层的输出通过激活函数,得到最终的输出。
return self.act(self.bn(self.conv(x) + self.cv2(x)))
# 融合前向传播方法。定义了 forward_fuse 方法,提供了一个简化的前向传播过程,只包括卷积和激活函数,不包括额外的 1x1 卷积层。
# x :是输入张量。
def forward_fuse(self, x):
# 对输入张量应用融合卷积、批量标准化和激活。
"""Apply fused convolution, batch normalization and activation to input tensor."""
# self.conv(x) 将输入张量 x 通过父类中的卷积层。 self.bn(...) 将卷积层的输出通过批量归一化层。 self.act(...) 将批量归一化层的输出通过激活函数,得到最终的输出。
return self.act(self.bn(self.conv(x)))
# 融合卷积方法。定义了 fuse_convs 方法,用于将两个卷积层融合。
def fuse_convs(self):
# 融合并行卷积。
"""Fuse parallel convolutions."""
# self.conv.weight.data
# 在 PyTorch 中, self.conv.weight.data 指的是卷积层 self.conv 的权重参数的数据。在 nn.Conv2d 卷积层中, weight 是一个 nn.Parameter 对象,它包含了卷积核的权重值。 data 属性是指这个参数当前的值。
# 以下是 self.conv.weight.data 的一些关键点 :
# 1. 权重参数 ( weight ) : 每个卷积层都有一组权重参数,这些参数是在训练过程中需要学习的。 在 nn.Conv2d 中, weight 参数的形状通常是 (out_channels, in_channels // groups, kernel_height, kernel_width) 。
# 2. 参数数据 ( data ) : data 属性提供了对参数值的直接访问,允许你读取或修改参数的值。 例如,如果你想要查看或更改卷积层的权重,可以直接操作 self.conv.weight.data 。
# 3. 修改权重 : 你可以直接赋值来修改权重 : self.conv.weight.data = new_weights ,其中 new_weights 是一个与权重形状相同的张量。 请注意,这种直接修改参数数据的方式会绕过 PyTorch 的自动梯度计算,因此通常不推荐在训练过程中这样做,除非你清楚自己在做什么。
# 4. 梯度计算 : 在反向传播期间,PyTorch 会自动计算 self.conv.weight 的梯度,并存储在 self.conv.weight.grad 中。 如果你通过 self.conv.weight.data 修改了权重,这个修改不会影响梯度计算,除非你同时更新 .grad 属性。
# 5. 冻结权重 : 如果你想要冻结某个层的权重,不对其进行更新,可以设置 self.conv.weight.requires_grad = False 。
# 创建一个与 self.conv 权重相同大小的零张量 w 。
# 在 Conv2 类的 fuse_convs 方法中, self.conv.weight.data 被用来访问和修改卷积层的权重,这是实现卷积融合的关键步骤。通过将额外的 1x1 卷积层的权重添加到主卷积层的权重中,可以实现两个卷积操作的融合,从而优化网络结构。
w = torch.zeros_like(self.conv.weight.data)
# 计算 w 的空间维度索引 i 。
# 这行代码是 Python 中的一个列表推导式,它用于创建一个列表 i ,列表中的元素是 w 张量的形状( shape )中最后两个维度的一半。这通常用于确定在二维空间(如图像或特征图)中的中心位置。
# w.shape[2:] :这部分获取张量 w 的形状,并切片以获取最后两个维度的大小。例如,如果 w 的形状是 (n, m, h, w) ,则 w.shape[2:] 将返回 (h, w) 。
# x // 2 :这是一个整数除法操作,它将每个维度的大小除以 2,结果是一个整数。这通常用于找到每个维度的中点。
# [i for x in w.shape[2:]] :这是一个列表推导式,它遍历 w.shape[2:] 中的每个元素 x ,并计算 x // 2 ,然后将结果收集到列表 i 中。
i = [x // 2 for x in w.shape[2:]]
# 将 self.cv2 的权重复制到 w 的中心位置。
w[:, :, i[0] : i[0] + 1, i[1] : i[1] + 1] = self.cv2.weight.data.clone()
# 将 w 加到 self.conv 的权重上,实现两个卷积层的融合。
self.conv.weight.data += w
# __delattr__
# 在Python中, __delattr__ 是一个特殊方法(也称为魔术方法或双下划线方法),它在尝试删除对象的属性时被调用。当你使用 del 语句来删除一个对象的属性时,Python会自动调用这个方法来执行删除操作。
# __delattr__ 方法接受一个参数,即要删除的属性的名称(通常是一个字符串)。你可以在这个方法中自定义属性删除的行为。
# 删除 self.cv2 属性。
self.__delattr__("cv2")
# 将 self.forward 方法指向 self.forward_fuse ,使得后续的前向传播不再使用额外的 1x1 卷积层。
self.forward = self.forward_fuse
# Conv2 类是一个扩展自 Conv 类的模块,它实现了 RepConv 模块中的卷积融合功能。通过 fuse_convs 方法,可以将两个卷积层融合为一个,减少模型的参数数量和计算量。这个类可以用于需要动态融合卷积层的场景。
5.class LightConv(nn.Module):
# 这段代码定义了一个名为 LightConv 的类,它是 PyTorch 的一个模块,用于实现一个轻量级的卷积结构,通常用于深度学习模型中以减少计算量。
# 类定义和文档字符串。 定义了一个名为 LightConv 的类,它继承自 nn.Module ,用于实现轻量级的卷积。
class LightConv(nn.Module):
# 使用 args(ch_in、ch_out、kernel) 进行轻卷积。
"""
Light convolution with args(ch_in, ch_out, kernel).
https://github.com/PaddlePaddle/PaddleDetection/blob/develop/ppdet/modeling/backbones/hgnet_v2.py
"""
# 初始化方法。定义了 __init__ 方法,它是类的构造函数,用于初始化 LightConv 类的实例。
# 1.c1 :参数指定输入通道的数量。
# 2.c2 :参数指定输出通道的数量。
# 3.k :参数指定第二个卷积层的卷积核大小,默认为 1。
# 4.act :参数指定激活函数,默认为 nn.ReLU() 。
def __init__(self, c1, c2, k=1, act=nn.ReLU()):
# 使用给定的参数(包括激活)初始化 Conv 层。
"""Initialize Conv layer with given arguments including activation."""
# 调用父类 nn.Module 的构造函数,完成类的初始化。
super().__init__()
# self.conv1 是一个 Conv 卷积层,用于将输入通道 c1 转换为输出通道 c2 ,卷积核大小为 1,不包含激活函数。
self.conv1 = Conv(c1, c2, 1, act=False)
# self.conv2 是一个 DWConv (深度卷积)层,用于在 c2 通道上应用 k 大小的卷积核,并应用指定的激活函数。
self.conv2 = DWConv(c2, c2, k, act=act)
# 前向传播方法。定义了 forward 方法,它是模块在前向传播时的行为。
# x :是输入张量。
def forward(self, x):
# 对输入张量应用 2 个卷积。
"""Apply 2 convolutions to input tensor."""
# self.conv1(x) 将输入张量 x 通过第一个卷积层 self.conv1 。 self.conv2(self.conv1(x)) 将第一个卷积层的输出通过第二个卷积层 self.conv2 ,得到最终的输出。
return self.conv2(self.conv1(x))
# LightConv 类通过组合两个卷积层(一个 Conv 层和一个 DWConv 层)来实现轻量级的卷积操作。这种结构通常用于减少模型的计算量和参数数量,同时保持有效的特征提取能力。这个类可以作为构建更复杂神经网络模型的基本构建块。
6.class DWConv(Conv):
# 这段代码定义了一个名为 DWConv 的类,它是 Conv 类的子类,用于实现深度卷积(Depth-wise Convolution)。深度卷积是一种在卷积神经网络中使用的卷积类型,它在空间维度上对输入的每个通道分别进行卷积,而不是使用共享权重。
# 类定义和文档字符串。定义了一个名为 DWConv 的类,它继承自 Conv 类。
class DWConv(Conv):
# 深度卷积。
"""Depth-wise convolution."""
# 初始化方法。定义了 __init__ 方法,它是类的构造函数,用于初始化 DWConv 类的实例。
# 1.c1 :参数指定输入通道的数量。
# 2.c2 :参数指定输出通道的数量。
# 3.k :参数指定卷积核的大小,默认为 1。
# 4.s :参数指定卷积的步长,默认为 1。
# 5.d :参数指定卷积的扩张率,默认为 1。
# 6.act :参数指定是否使用激活函数,默认为 True 。
def __init__(self, c1, c2, k=1, s=1, d=1, act=True): # ch_in, ch_out, kernel, stride, dilation, activation
# 使用给定的参数初始化深度卷积。
"""Initialize Depth-wise convolution with given parameters."""
# math.gcd(a, b, *integers)
# math.gcd() 函数是 Python 标准库 math 模块中的一个函数,用于计算两个或多个整数的最大公约数(Greatest Common Divisor,GCD)。最大公约数是能同时整除这些整数的最大正整数。
# 参数说明 :
# a 和 b :两个非负整数,函数将计算它们的最大公约数。
# *integers :额外的整数参数, math.gcd() 可以接受两个以上的整数,并计算所有提供的整数的最大公约数。
# 返回值 :
# 返回 a 和 b (以及任何额外提供的整数)的最大公约数。
# 在深度学习模型中,特别是在实现深度卷积(Depth-wise Convolution)时, math.gcd() 函数可以用来确定卷积层的组数,这样可以使每个输入通道独立地进行卷积操作,从而减少参数数量和计算量。
# 例如,在 DWConv 类的初始化方法中,使用 math.gcd(c1, c2) 来设置组数,其中 c1 和 c2 分别是输入和输出通道的数量。这样做可以确保每个输入通道都与相同的卷积核进行卷积,这是实现深度卷积的关键步骤。
# 超类初始化。
# 调用父类 Conv 的构造函数,完成类的初始化。
# g=math.gcd(c1, c2) 计算输入通道数和输出通道数的最大公约数,并将其用作卷积的组数( groups 参数)。这是深度卷积的关键特性,因为它允许每个输入通道独立地与卷积核进行卷积。
super().__init__(c1, c2, k, s, g=math.gcd(c1, c2), d=d, act=act)
# DWConv 类通过继承 Conv 类并设置组数为输入通道数和输出通道数的最大公约数,实现了深度卷积。这种卷积方式在计算上更加高效,因为它减少了模型的参数数量和计算量,同时保持了对输入通道的独立处理能力。
# 这使得 DWConv 类特别适合于需要轻量级卷积操作的场景,如在移动设备或资源受限的环境中部署的深度学习模型。
7.class DWConvTranspose2d(nn.ConvTranspose2d):
# 这段代码定义了一个名为 DWConvTranspose2d 的类,它是 PyTorch 中 nn.ConvTranspose2d 的子类,用于实现深度转置卷积(Depth-wise Transpose Convolution)。深度转置卷积是深度卷积的逆操作,通常用于在神经网络中进行上采样或解卷积。
# 类定义和文档字符串。定义了一个名为 DWConvTranspose2d 的类,它继承自 nn.ConvTranspose2d ,用于实现深度转置卷积。
class DWConvTranspose2d(nn.ConvTranspose2d):
# 深度转置卷积。
"""Depth-wise transpose convolution."""
# 初始化方法。定义了 __init__ 方法,它是类的构造函数,用于初始化 DWConvTranspose2d 类的实例。
# 1.c1 :参数指定输入通道的数量。
# 2.c2 :参数指定输出通道的数量。
# 3.k :参数指定卷积核的大小,默认为 1。
# 4.s :参数指定卷积的步长,默认为 1。
# 5.p1 :参数指定输入的填充大小,默认为 0。
# 6.p2 :参数指定输出的填充大小,默认为 0。
def __init__(self, c1, c2, k=1, s=1, p1=0, p2=0): # ch_in, ch_out, kernel, stride, padding, padding_out
# 使用给定的参数初始化 DWConvTranspose2d 类。
"""Initialize DWConvTranspose2d class with given parameters."""
# 超类初始化。
# groups=math.gcd(c1, c2) 计算输入通道数和输出通道数的最大公约数,并将其用作转置卷积的组数( groups 参数)。这是深度转置卷积的关键特性,因为它允许每个输入通道独立地与转置卷积核进行卷积。
super().__init__(c1, c2, k, s, p1, p2, groups=math.gcd(c1, c2))
# DWConvTranspose2d 类通过继承 nn.ConvTranspose2d 并设置组数为输入通道数和输出通道数的最大公约数,实现了深度转置卷积。这种卷积方式在计算上更加高效,因为它减少了模型的参数数量和计算量,同时保持了对输入通道的独立处理能力。
# 这使得 DWConvTranspose2d 类特别适合于需要轻量级转置卷积操作的场景,如在生成对抗网络(GANs)或分割网络中的上采样步骤。
8.class ConvTranspose(nn.Module):
# 这段代码定义了一个名为 ConvTranspose 的类,它是 PyTorch 的一个模块,用于实现一个二维转置卷积层(也称为反卷积层)。
# 类定义和文档字符串。
# 定义了一个名为 ConvTranspose 的类,它继承自 nn.Module ,用于实现二维转置卷积层。 类的文档字符串说明这个类实现了一个二维转置卷积层。
class ConvTranspose(nn.Module):
# 卷积转置第二层。
"""Convolution transpose 2d layer."""
# 默认激活函数。定义了一个类变量 default_act ,它是这个类中转置卷积层的默认激活函数,这里使用的是 nn.SiLU() 。
default_act = nn.SiLU() # default activation
# 初始化方法。定义了 __init__ 方法,它是类的构造函数,用于初始化 ConvTranspose 类的实例。
# 1.c1 :参数指定输入通道的数量。
# 2.c2 :参数指定输出通道的数量。
# 3.k :参数指定转置卷积核的大小,默认为 2。
# 4.s :参数指定转置卷积的步长,默认为 2。
# 5.p :参数指定转置卷积的填充大小,默认为 0。
# 6.bn :参数指定是否使用批量归一化,默认为 True 。
# 7.act :参数指定是否使用激活函数,默认为 True 。
def __init__(self, c1, c2, k=2, s=2, p=0, bn=True, act=True):
# 使用批量标准化和激活函数初始化 ConvTranspose2d 层。
"""Initialize ConvTranspose2d layer with batch normalization and activation function."""
# 超类初始化和转置卷积层。调用父类 nn.Module 的构造函数,完成类的初始化。
super().__init__()
# torch.nn.ConvTranspose2d(in_channels, out_channels, kernel_size, stride=1, padding=0, output_padding=0, groups=1, bias=True, dilation=1, padding_mode='zeros')
# nn.ConvTranspose2d 是 PyTorch 中的一个模块,用于实现二维转置卷积(也称为反卷积或上采样卷积)。
# 转置卷积通常用于生成比输入更大的输出,例如在生成对抗网络(GANs)和卷积神经网络(CNNs)的解码器部分。
# in_channels :输入通道的数量。
# out_channels :输出通道的数量。
# kernel_size :卷积核的大小,可以是单个整数或是一个包含两个整数的元组。
# stride :卷积的步长,默认为1。可以是单个整数或是一个包含两个整数的元组。
# padding :输入的每一边补充0的数量,默认为0。
# output_padding :输出的每一边额外补充0的数量,默认为0。用于控制输出的大小。
# groups :将输入分成若干组,默认为1。
# bias :如果为True,则会添加偏置,默认为True。
# dilation :卷积核元素之间的间距,默认为1。
# padding_mode :可选的填充模式,包括 ‘zeros’, ‘reflect’, ‘replicate’ 或 ‘circular’。默认为 ‘zeros’。
# 创建一个 nn.ConvTranspose2d 转置卷积层,根据输入参数进行配置。如果 bn 为 True ,则转置卷积层不包含偏置项( bias=False ),否则包含偏置项( bias=True )。
self.conv_transpose = nn.ConvTranspose2d(c1, c2, k, s, p, bias=not bn)
# 批量归一化层。根据 bn 参数的值,创建一个 nn.BatchNorm2d 批量归一化层或一个 nn.Identity 恒等层。如果 bn 为 True ,则使用批量归一化层;否则,使用恒等层,即不进行任何操作。
self.bn = nn.BatchNorm2d(c2) if bn else nn.Identity()
# 激活函数。根据 act 参数的值配置激活函数。如果 act 为 True ,则使用默认激活函数 self.default_act ;如果 act 是 nn.Module 的实例,则使用指定的激活函数;否则,使用恒等层,即不应用激活函数。
self.act = self.default_act if act is True else act if isinstance(act, nn.Module) else nn.Identity()
# 前向传播方法。
# 定义了 forward 方法,它是模块在前向传播时的行为。 x :是输入张量。
def forward(self, x):
# 对输入应用转置卷积、批量标准化和激活。
"""Applies transposed convolutions, batch normalization and activation to input."""
# self.conv_transpose(x) 将输入张量 x 通过转置卷积层。 self.bn(self.conv_transpose(x)) 将转置卷积层的输出通过批量归一化层。 self.act(self.bn(self.conv_transpose(x))) 将批量归一化层的输出通过激活函数,得到最终的输出。
return self.act(self.bn(self.conv_transpose(x)))
# 融合前向传播方法。
# 定义了 forward_fuse 方法,提供了一个简化的前向传播过程,只包括转置卷积和激活函数,不包括批量归一化。 x 是输入张量。
def forward_fuse(self, x):
# 对输入应用激活和卷积转置操作。
"""Applies activation and convolution transpose operation to input."""
# self.conv_transpose(x) 将输入张量 x 通过转置卷积层。 self.act(self.conv_transpose(x)) 将转置卷积层的输出通过激活函数,得到最终的输出。
return self.act(self.conv_transpose(x))
# ConvTranspose 类提供了一个灵活的转置卷积层实现,它可以根据输入参数自动配置转置卷积核大小、步长、填充,并可以选择性地应用批量归一化和激活函数。这个类可以作为构建更复杂神经网络模型的基本构建块,特别是在需要进行上采样或解卷积操作的场景中。
9.class Focus(nn.Module):
# 这段代码定义了一个名为 Focus 的类,它是 PyTorch 的一个模块,用于将空间信息(宽和高)整合到通道空间中。这种操作通常用于在目标检测模型中处理多尺度信息。
# 类定义和文档字符串。定义了一个名为 Focus 的类,它继承自 nn.Module 。
class Focus(nn.Module):
# 将 wh 信息聚焦到 c 空间。
"""Focus wh information into c-space."""
# 初始化方法。定义了 __init__ 方法,它是类的构造函数,用于初始化 Focus 类的实例。
# 1.c1 :参数指定输入通道的数量。
# 2.c2 :参数指定输出通道的数量。
# 3.k :参数指定卷积核的大小,默认为 1。
# 4.s :参数指定卷积的步长,默认为 1。
# 5.p :参数指定卷积的填充大小,如果为 None ,则会自动计算。
# 6.g :参数指定卷积的组数,默认为 1。
# 7.act :参数指定是否使用激活函数,默认为 True 。
def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True):
# 使用用户定义的通道、卷积、填充、组和激活值初始化 Focus 对象。
"""Initializes Focus object with user defined channel, convolution, padding, group and activation values."""
# 超类初始化和卷积层。
# 调用父类 nn.Module 的构造函数,完成类的初始化。
super().__init__()
# 创建一个 Conv 卷积层,其输入通道数为 c1 * 4 (将输入张量的通道数扩展四倍),输出通道数为 c2 ,其他参数如卷积核大小、步长、填充、组数和激活函数根据输入参数配置。
self.conv = Conv(c1 * 4, c2, k, s, p, g, act=act)
# self.contract = Contract(gain=2)
# 前向传播方法。定义了 forward 方法,它是模块在前向传播时的行为。
# x :是输入张量,其形状为 (b, c, w, h) ,其中 b 是批次大小, c 是通道数, w 是宽度, h 是高度。
def forward(self, x):
# 将卷积应用于连接张量并返回输出。
# 输入形状为 (b,c,w,h),输出形状为 (b,4c,w/2,h/2)。
"""
Applies convolution to concatenated tensor and returns the output.
Input shape is (b,c,w,h) and output shape is (b,4c,w/2,h/2).
"""
# torch.cat((x[..., ::2, ::2], x[..., 1::2, ::2], x[..., ::2, 1::2], x[..., 1::2, 1::2]), 1) 将输入张量 x 在空间维度上每隔一个像素取样,并在通道维度上进行拼接。这样,输入张量的通道数扩展为原来的四倍,而宽度和高度减半。
# self.conv(...) 将拼接后的张量通过卷积层,得到最终的输出张量,其形状为 (b, 4c, w/2, h/2) 。
return self.conv(torch.cat((x[..., ::2, ::2], x[..., 1::2, ::2], x[..., ::2, 1::2], x[..., 1::2, 1::2]), 1))
# return self.conv(self.contract(x))
# Focus 类通过将输入张量在空间维度上每隔一个像素取样并在通道维度上拼接,实现了将空间信息整合到通道空间的操作。这种操作有助于模型在处理不同尺度的特征时捕捉更多的上下文信息,常用于目标检测模型中的特征融合。
10.class GhostConv(nn.Module):
# 这段代码定义了一个名为 GhostConv 的类,它是 PyTorch 的一个模块,用于实现 Ghost Convolution,这是一种在深度学习中用于提高模型效率的技术。Ghost Convolution 通过创建额外的“幽灵”或“廉价”卷积来增加特征图的通道数,而不显著增加计算量。
# 类定义和文档字符串。定义了一个名为 GhostConv 的类,它继承自 nn.Module ,用于实现 Ghost Convolution。
class GhostConv(nn.Module):
# 幽灵卷积https://github.com/huawei-noah/ghostnet。
"""Ghost Convolution https://github.com/huawei-noah/ghostnet."""
# 初始化方法。定义了 __init__ 方法,它是类的构造函数,用于初始化 GhostConv 类的实例。
# 1.c1 :参数指定输入通道的数量。
# 2.c2 :参数指定输出通道的数量。
# 3.k :参数指定卷积核的大小,默认为 1。
# 4.s :参数指定卷积的步长,默认为 1。
# 5.g :参数指定卷积的组数,默认为 1。
# 6.act :参数指定是否使用激活函数,默认为 True 。
def __init__(self, c1, c2, k=1, s=1, g=1, act=True):
# 使用主要且廉价的操作初始化 Ghost Convolution 模块,以实现高效的特征学习。
"""Initializes Ghost Convolution module with primary and cheap operations for efficient feature learning."""
# 超类初始化和卷积层。
# 调用父类 nn.Module 的构造函数,完成类的初始化。
super().__init__()
# c_ 是隐藏通道数,等于输出通道数 c2 的一半。
c_ = c2 // 2 # hidden channels
# self.cv1 是第一个 Conv 卷积层,用于将输入通道 c1 转换为隐藏通道 c_ 。
self.cv1 = Conv(c1, c_, k, s, None, g, act=act)
# self.cv2 是第二个 Conv 卷积层,用于将隐藏通道 c_ 再次转换为隐藏通道 c_ ,这里使用了 5x5 的卷积核和 1 的步长。
self.cv2 = Conv(c_, c_, 5, 1, None, c_, act=act)
# 前向传播方法。定义了 forward 方法,它是模块在前向传播时的行为。
# x :是输入张量。
def forward(self, x):
# 通过具有跳过连接的 Ghost Bottleneck 层进行前向传播。
"""Forward propagation through a Ghost Bottleneck layer with skip connection."""
# 将输入张量 x 通过第一个卷积层 self.cv1 。
y = self.cv1(x)
# 将第一个卷积层的输出 y 和第二个卷积层的输出 self.cv2(y) 在通道维度上进行拼接。
return torch.cat((y, self.cv2(y)), 1)
# GhostConv 类通过两个卷积层和一个通道拼接操作实现了 Ghost Convolution,这种结构可以在不显著增加计算量的情况下增加特征图的通道数,从而提高模型的特征学习能力。这种技术特别适用于需要在保持计算效率的同时提高模型性能的场景。
11.class RepConv(nn.Module):
# 这段代码定义了一个名为 RepConv 的类,它是 PyTorch 的一个模块,用于实现 RepVGG 风格的卷积块,这种结构在训练和部署时有不同的表现形式。
# 类定义和文档字符串。
# 定义了一个名为 RepConv 的类,它继承自 nn.Module 。
class RepConv(nn.Module):
"""
RepConv is a basic rep-style block, including training and deploy status.
This module is used in RT-DETR.
Based on https://github.com/DingXiaoH/RepVGG/blob/main/repvgg.py
"""
# 默认激活函数。定义了一个类变量 default_act ,它是这个类中卷积层的默认激活函数,这里使用的是 nn.SiLU() 。
default_act = nn.SiLU() # default activation
# 初始化方法。定义了 __init__ 方法,它是类的构造函数,用于初始化 RepConv 类的实例。
# 1.c1 :参数指定输入通道的数量。
# 2.c2 :参数指定输出通道的数量。
# 3.k :参数指定卷积核的大小,默认为 3。
# 4.s :参数指定卷积的步长,默认为 1。
# 5.p :参数指定卷积的填充大小,默认为 1。
# 6.g :参数指定卷积的组数,默认为 1。
# 7.d :参数指定卷积的扩张率,默认为 1。
# 8.act :参数指定是否使用激活函数,默认为 True 。
# 9.bn :参数指定是否使用批量归一化,默认为 False 。
# 10.deploy :参数指定是否为部署状态,默认为 False 。
def __init__(self, c1, c2, k=3, s=1, p=1, g=1, d=1, act=True, bn=False, deploy=False):
# 使用输入、输出和可选激活函数初始化光卷积层。
"""Initializes Light Convolution layer with inputs, outputs & optional activation function."""
# 调用父类 nn.Module 的构造函数,完成类的初始化。
super().__init__()
# 使用 assert 语句确保卷积核大小 k 为 3,填充 p 为 1,这是 RepConv 设计的固定参数。
assert k == 3 and p == 1
# 属性初始化。
# 初始化类属性 g , c1 , c2 。
self.g = g
self.c1 = c1
self.c2 = c2
# 根据 act 参数的值初始化激活函数 self.act 。
self.act = self.default_act if act is True else act if isinstance(act, nn.Module) else nn.Identity()
# 批量归一化和卷积层。
# 如果 bn 为 True 且 c2 等于 c1 且 s 等于 1,则初始化一个批量归一化层 self.bn 。
self.bn = nn.BatchNorm2d(num_features=c1) if bn and c2 == c1 and s == 1 else None
# 初始化第一个卷积层 self.conv1 ,使用 Conv 类,卷积核大小为 k ,步长为 s ,填充为 p 。
self.conv1 = Conv(c1, c2, k, s, p=p, g=g, act=False)
# 初始化第二个卷积层 self.conv2 ,使用 Conv 类,卷积核大小为 1,步长为 s ,填充为 p - k // 2 。
self.conv2 = Conv(c1, c2, 1, s, p=(p - k // 2), g=g, act=False)
# RepConv 类实现了 RepVGG 风格的卷积块,它包含两个卷积层 :一个标准卷积层和一个 1x1 卷积层。这种设计允许在训练时使用两个卷积层,而在部署时将它们融合为一个卷积层,以减少模型的复杂度和提高推理速度。这种技术特别适用于需要在保持训练性能的同时提高部署效率的场景。
# 这段代码定义了 RepConv 类中的 forward_fuse 方法,这个方法实现了前向传播过程,其中只包含卷积操作和激活函数。
# 方法定义。 forward_fuse 是 RepConv 类的一个方法,用于执行前向传播。
# x :是输入张量,它其形状通常是 (batch_size, channels, height, width) 。将通过卷积层和激活函数。
def forward_fuse(self, x):
"""Forward process."""
# 卷积操作。 self.conv(x) :将输入张量 x 通过卷积层。
# 激活函数。 self.act :激活函数,根据 __init__ 方法中的参数 act 确定。如果 act 是 True ,则使用默认激活函数 self.default_act ;如果 act 是 nn.Module 的实例,则使用指定的激活函数;否则,使用恒等函数 nn.Identity() ,即不应用激活函数。
# 返回值。 return self.act(self.conv(x)) :返回经过卷积层和激活函数处理后的输出张量。
return self.act(self.conv(x))
# forward_fuse 方法提供了一个简化的前向传播过程,只包括卷积和激活函数,适用于需要快速推理的场景。这种方法可以减少计算量和模型复杂度,提高模型的运行效率。在 RepVGG 架构中,这种方法特别有用,因为它允许在部署时将多个卷积层融合为一个,从而提高推理速度。
# 这段代码定义了 RepConv 类中的 forward 方法,它实现了标准的前向传播过程,包括两个卷积操作、批量归一化(如果启用),以及激活函数。
# 前向传播方法 forward 。
# 方法定义。 forward 是 RepConv 类的一个方法,用于执行前向传播。
# x :输入张量,其形状通常是 (batch_size, channels, height, width) 。
def forward(self, x):
"""Forward process."""
# 批量归一化。如果 self.bn (批量归一化层)不存在,则 id_out 为0(即不进行批量归一化);如果存在,则对输入张量 x 进行批量归一化。
id_out = 0 if self.bn is None else self.bn(x)
# 卷积操作。 self.conv1(x) :将输入张量 x 通过第一个卷积层 self.conv1 。 self.conv2(x) :将输入张量 x 通过第二个卷积层 self.conv2 。
# 残差连接。 self.conv1(x) + self.conv2(x) + id_out :将两个卷积层的输出相加,再加上批量归一化的输出(如果有)。这种结构形成了一个残差连接,其中 id_out 作为残差路径的一部分。
# 激活函数。 self.act :激活函数,根据 __init__ 方法中的参数 act 确定。如果 act 是 True ,则使用默认激活函数 self.default_act ;如果 act 是 nn.Module 的实例,则使用指定的激活函数;否则,使用恒等函数 nn.Identity() ,即不应用激活函数。
# 返回值。 return self.act(self.conv1(x) + self.conv2(x) + id_out) :返回经过两个卷积层、可能的批量归一化,以及激活函数处理后的输出张量。
return self.act(self.conv1(x) + self.conv2(x) + id_out)
# forward 方法实现了一个包含两个卷积层和批量归一化的前向传播过程,其中还包括了一个残差连接。这种设计允许模型在训练时利用两个卷积层学习复杂的特征,同时在部署时可以通过融合这两个卷积层来简化模型结构,提高推理效率。这种技术特别适用于需要在保持训练性能的同时提高部署效率的场景。
# 这段代码定义了一个名为 get_equivalent_kernel_bias 的方法,它用于将 RepConv 类中包含的两个卷积层(一个3x3卷积和一个1x1卷积)以及可能的批量归一化层(如果有的话)融合成一个等效的3x3卷积核和偏置。
# 方法定义和文档字符串。定义了一个名为 get_equivalent_kernel_bias 的方法,用于获取等效的卷积核和偏置。
def get_equivalent_kernel_bias(self):
# 通过将 3x3 核、1x1 核和标识核及其偏差相加,返回等效核和偏差。
"""Returns equivalent kernel and bias by adding 3x3 kernel, 1x1 kernel and identity kernel with their biases."""
# 融合卷积核和偏置。
# self.conv1 是3x3卷积层, self.conv2 是1x1卷积层, self.bn 是批量归一化层(如果存在)。
# _fuse_bn_tensor 方法被调用三次,分别对这三个层进行处理,以融合它们的权重和偏置。
# kernel3x3 和 bias3x3 是3x3卷积层的融合后的卷积核和偏置。
kernel3x3, bias3x3 = self._fuse_bn_tensor(self.conv1)
# kernel1x1 和 bias1x1 是1x1卷积层的融合后的卷积核和偏置。
kernel1x1, bias1x1 = self._fuse_bn_tensor(self.conv2)
# kernelid 和 biasid 是批量归一化层(如果存在)的融合后的卷积核和偏置。
kernelid, biasid = self._fuse_bn_tensor(self.bn)
# 填充1x1卷积核到3x3。
# self._pad_1x1_to_3x3_tensor(kernel1x1) 方法将1x1卷积核 kernel1x1 填充为3x3卷积核。
# 将三个卷积核相加 : kernel3x3 、 填充后的 kernel1x1 和 kernelid ,得到最终的等效卷积核。
# 将三个偏置相加 : bias3x3 、 bias1x1 和 biasid ,得到最终的等效偏置。
# 返回最终的等效卷积核和偏置。
return kernel3x3 + self._pad_1x1_to_3x3_tensor(kernel1x1) + kernelid, bias3x3 + bias1x1 + biasid
# get_equivalent_kernel_bias 方法提供了一种将多个卷积层和批量归一化层融合为一个等效卷积核和偏置的方法。这种融合操作通常在模型部署时进行,以减少模型的复杂度和提高推理速度。通过这种方式,可以在不牺牲模型性能的情况下,优化模型的结构和效率。
# 这段代码定义了 RepConv 类中的一个私有方法 _pad_1x1_to_3x3_tensor ,它用于将一个 1x1 的卷积核张量扩展为一个 3x3 的卷积核张量。
# 方法定义和文档字符串。定义了一个名为 _pad_1x1_to_3x3_tensor 的私有方法,它接受一个参数。
# kernel1x1 :是一个 1x1 的卷积核张量。
def _pad_1x1_to_3x3_tensor(self, kernel1x1):
# 将 1x1 张量填充为 3x3 张量。
"""Pads a 1x1 tensor to a 3x3 tensor."""
# 参数检查和填充操作。
if kernel1x1 is None:
# 如果 kernel1x1 是 None ,则方法返回 0。
return 0
else:
# torch.nn.functional.pad(input, pad, mode='constant', value=0)
# torch.nn.functional.pad 是 PyTorch 中的一个函数,用于对输入的张量(tensor)进行填充(padding)。这个函数非常灵活,允许你在不同的维度上应用不同大小的填充。
# 参数说明 :
# input ( torch.Tensor ) : 需要被填充的输入张量。
# pad ( int 或 tuple ) : 指定填充的大小。
# 如果是 int ,表示在所有边界上应用相同的填充大小。
# 如果是 tuple ,应该遵循 (左, 右, 上, 下) 的顺序,对于多维数据,可以扩展为 (左, 右, 上, 下, 前, 后) 。
# mode ( str , 可选) : 指定填充的模式。
# 'constant' : 常数填充,默认值,使用 value 参数指定的值进行填充。
# 'reflect' : 反射填充,类似于图像处理中的反射效果。
# 'replicate' : 复制边缘值进行填充。
# value ( float 或 int , 可选) : 当 mode 为 'constant' 时,用于指定填充的常数值,默认为 0。
# 返回值 :
# 返回一个新的张量,该张量是输入张量经过指定填充后的结果。
# torch.nn.functional.pad 函数在深度学习中非常有用,尤其是在需要对输入数据进行预处理或在自定义层中调整张量尺寸时。
# 如果 kernel1x1 不是 None ,则使用 torch.nn.functional.pad 函数对其进行填充。
# torch.nn.functional.pad(kernel1x1, [1, 1, 1, 1]) 将 kernel1x1 张量在四个边界(上下左右)各填充 1 个单位,从而将 1x1 的张量转换为 3x3 的张量。
# 返回值。 方法返回填充后的 3x3 卷积核张量。
return torch.nn.functional.pad(kernel1x1, [1, 1, 1, 1])
# 这段代码定义了一个名为 _fuse_bn_tensor 的私有方法,它是用于将卷积层( Conv )或批量归一化层( nn.BatchNorm2d )的权重和偏置融合到一个单独的卷积核中。这种融合通常用于模型部署阶段,以简化网络结构并提高推理速度。
# 方法定义和文档字符串。定义了一个名为 _fuse_bn_tensor 的私有方法,用于融合神经网络分支的权重和偏置。
# branch :是要融合的卷积或批量归一化层。
def _fuse_bn_tensor(self, branch):
# 通过融合神经网络的分支来生成适合卷积的内核和偏差。
"""Generates appropriate kernels and biases for convolution by fusing branches of the neural network."""
# 参数检查。
if branch is None:
# 如果 branch 是 None ,则返回 (0, 0) ,表示没有融合发生。
return 0, 0
# 融合 Conv 层。
# 这段代码是 _fuse_bn_tensor 方法中的一部分,它处理当 branch 参数是 Conv 类实例时的情况。这里的目的是从 Conv 实例中提取卷积层的权重和相关联的批量归一化层( BatchNorm2d )的参数,以便后续进行融合操作。
# 使用 isinstance 函数检查 branch 是否是 Conv 类的实例。
if isinstance(branch, Conv):
# 提取卷积层权重。如果 branch 是 Conv 实例,从它的 conv 属性中提取卷积核权重。 branch.conv.weight 是一个 torch.Tensor ,包含了卷积层的权重参数。
kernel = branch.conv.weight
# 提取批量归一化层参数。从 branch 的 bn 属性中提取批量归一化层的参数。
# running_mean :运行均值,记录了特征在训练过程中的均值。
running_mean = branch.bn.running_mean
# running_var :运行方差,记录了特征在训练过程中的方差。
running_var = branch.bn.running_var
# gamma :缩放因子,用于调整批量归一化后的值。
gamma = branch.bn.weight
# beta :偏移因子,用于在缩放后进一步调整值。
beta = branch.bn.bias
# eps :epsilon值,用于防止除以零的情况,增加数值稳定性。
eps = branch.bn.eps
# 这些参数是从 Conv 实例中的批量归一化层中提取的,它们将用于后续的融合操作,以生成一个新的卷积核和偏置,这样可以在不使用单独的批量归一化层的情况下,通过卷积操作直接实现相同的效果。这种融合操作通常在模型部署时进行,以减少模型的计算复杂度和提高推理速度。
# 融合 BatchNorm2d 层。
# 这段代码处理的是当 branch 参数是 nn.BatchNorm2d 类实例时的情况,即直接对一个批量归一化层进行操作。这里的目的是创建一个恒等卷积核( id_tensor ),并提取批量归一化层的参数,以便后续进行融合操作。
# 类型检查。使用 isinstance 函数检查 branch 是否是 nn.BatchNorm2d 类的实例。
elif isinstance(branch, nn.BatchNorm2d):
# 创建恒等卷积核。
# 如果类实例 self 没有名为 id_tensor 的属性,则创建一个。
if not hasattr(self, "id_tensor"):
# input_dim 计算每个组的输入通道数,这是通过总输入通道数 self.c1 除以组数 self.g 得到的。
input_dim = self.c1 // self.g
# kernel_value 是一个使用 NumPy 创建的零填充数组,形状为 (self.c1, input_dim, 3, 3) ,用于存储恒等卷积核的值。
kernel_value = np.zeros((self.c1, input_dim, 3, 3), dtype=np.float32)
# 通过一个循环,将 kernel_value 的对角线元素(中心位置)设置为 1,从而创建一个 3x3 的恒等卷积核。
for i in range(self.c1):
kernel_value[i, i % input_dim, 1, 1] = 1
# self.id_tensor 是将 kernel_value 从 NumPy 数组转换为 PyTorch 张量,并移动到与 branch.weight 相同的设备上。
self.id_tensor = torch.from_numpy(kernel_value).to(branch.weight.device)
# 提取批量归一化层参数。
# kernel 被设置为刚刚创建的恒等卷积核 self.id_tensor 。
kernel = self.id_tensor
# 从 branch 中提取批量归一化层的 运行均值 running_mean 、 运行方差 running_var 、 缩放因子 gamma 、 偏置 beta 和 epsilon 值 eps 。
running_mean = branch.running_mean
running_var = branch.running_var
gamma = branch.weight
beta = branch.bias
eps = branch.eps
# 这些参数将用于后续的融合操作,以生成一个新的卷积核和偏置,这样可以在不使用单独的批量归一化层的情况下,通过卷积操作直接实现相同的效果。这种融合操作通常在模型部署时进行,以减少模型的计算复杂度和提高推理速度。
# 计算融合后的卷积核和偏置。
# 这段代码是 _fuse_bn_tensor 方法中的关键部分,它负责将批量归一化层的参数融合到卷积核中,以便在不使用单独的批量归一化操作的情况下,通过卷积层实现相同的效果。
# 计算标准差。 running_var 是批量归一化层的运行方差。 eps 是一个很小的数值,用于防止除以零的情况,增加数值稳定性。 sqrt() 函数计算方差的平方根,得到标准差 std 。
std = (running_var + eps).sqrt()
# 计算缩放因子。 gamma 是批量归一化层的缩放因子。 gamma / std 计算每个通道的缩放因子,通过将 gamma 除以标准差 std 得到。 reshape(-1, 1, 1, 1) 将缩放因子 t 重塑为四维张量,以匹配卷积核的形状,使其可以与卷积核进行逐元素相乘。
t = (gamma / std).reshape(-1, 1, 1, 1)
# 融合卷积核和批量归一化参数。
# kernel * t 将卷积核 kernel 与缩放因子 t 进行逐元素相乘,融合了批量归一化的缩放效果。
# beta - running_mean * gamma / std 计算融合后的偏置,它考虑了批量归一化的偏移效果。这里, beta 是批量归一化的偏置, running_mean 是运行均值, gamma 是缩放因子, std 是标准差。
# 返回值。 方法返回融合后的卷积核和偏置。这些值可以用于更新卷积层的权重和偏置,使得在不使用批量归一化层的情况下,卷积层能够模拟出相同的效果。
return kernel * t, beta - running_mean * gamma / std
# 这段代码通过融合批量归一化层的参数到卷积核中,实现了在模型部署时去除批量归一化层的目的。这种方法可以减少模型的参数数量和计算量,提高模型的推理速度,同时保持模型的精度不变。
# _fuse_bn_tensor 方法通过融合卷积层和批量归一化层的参数,生成了一个新的卷积核和偏置,这在模型部署时非常有用,因为它可以减少网络的复杂度并提高推理速度。这种方法允许在不牺牲模型性能的情况下,优化模型的结构和效率。
# 这段代码定义了一个名为 fuse_convs 的方法,它用于将 RepConv 类中的两个卷积层(一个3x3卷积和一个1x1卷积)融合成一个单独的卷积层,并从类中移除不再需要的属性。
# 方法定义和文档字符串。定义了一个名为 fuse_convs 的方法,用于融合卷积层。
def fuse_convs(self):
# 将两个卷积层合并为一个层,并从类中删除未使用的属性。
"""Combines two convolution layers into a single layer and removes unused attributes from the class."""
# 检查和融合条件。
if hasattr(self, "conv"):
# 如果类实例 self 已经有一个名为 conv 的属性,说明融合已经完成,直接返回。
return
# 获取等效卷积核和偏置。调用 get_equivalent_kernel_bias 方法获取等效的卷积核和偏置。
kernel, bias = self.get_equivalent_kernel_bias()
# 创建新的卷积层。
# 创建一个新的 nn.Conv2d 卷积层,其参数从 self.conv1 中获取。
# requires_grad_(False) 确保新卷积层的参数在训练过程中不会被更新。
self.conv = nn.Conv2d(
in_channels=self.conv1.conv.in_channels,
out_channels=self.conv1.conv.out_channels,
kernel_size=self.conv1.conv.kernel_size,
stride=self.conv1.conv.stride,
padding=self.conv1.conv.padding,
dilation=self.conv1.conv.dilation,
groups=self.conv1.conv.groups,
bias=True,
).requires_grad_(False)
# 设置卷积核和偏置。将等效的卷积核和偏置设置到新创建的卷积层中。
# 设置卷积核。
# self.conv 是在 fuse_convs 方法中新创建的 nn.Conv2d 卷积层。
# weight 是 self.conv 的一个属性,它包含了卷积层的权重参数。
# data 属性允许直接访问和修改权重参数的值。
# kernel 是通过 get_equivalent_kernel_bias 方法计算得到的等效卷积核,它融合了原始卷积层和批量归一化层的参数。
# 这行代码将融合后的卷积核赋值给 self.conv 的权重参数,使得新卷积层能够模拟原始层的行为。
self.conv.weight.data = kernel
# 设置偏置。
# bias 是通过 get_equivalent_kernel_bias 方法计算得到的等效偏置,它同样融合了原始卷积层和批量归一化层的参数。
# 这行代码将融合后的偏置赋值给 self.conv 的偏置参数,确保新卷积层在应用卷积操作后能够正确地调整输出值。
self.conv.bias.data = bias
# 断开参数与梯度的连接。
for para in self.parameters():
# tensor.detach_()
# 在 PyTorch 中, detach_() 是一个就地(in-place)操作,用于将一个张量从当前计算图中分离出来。这意味着,在此之后,对该张量所做的任何操作都不会被记录在计算图中,也不会参与梯度的反向传播。这个函数通常用于优化模型的性能,特别是在模型推理阶段,因为推理不需要计算梯度。
# 参数说明 : 没有参数 : detach_() 是一个就地操作,它直接修改调用它的张量。
# 返回值 : 返回一个从计算图中分离出来的新张量,实际上就是原始张量的一个视图(view),但不会跟踪梯度。
# 注意事项 :
# detach_() 是一个就地操作,它直接修改原始张量。如果你需要保留原始张量的梯度信息,应该先克隆(clone)该张量,然后再调用 detach_() 。
# 在模型推理阶段,通常不需要计算梯度,因此可以使用 detach_() 来减少内存消耗和提高计算效率。
# detach_() 是 PyTorch 中管理梯度计算和优化模型性能的重要工具之一。
# 遍历类实例 self 的所有参数,调用 detach_ 方法,使参数与梯度计算断开连接。
para.detach_()
# 删除不再需要的属性。
# 使用 __delattr__ 方法删除不再需要的属性,包括 conv1 、 conv2 、 nm (如果存在) 、 bn (如果存在) 和 id_tensor (如果存在) 。
self.__delattr__("conv1")
self.__delattr__("conv2")
if hasattr(self, "nm"):
self.__delattr__("nm")
if hasattr(self, "bn"):
self.__delattr__("bn")
if hasattr(self, "id_tensor"):
self.__delattr__("id_tensor")
# fuse_convs 方法通过融合两个卷积层和一个可能的批量归一化层,创建了一个新的卷积层,该层包含了所有必要的信息来模拟原始层的行为。
# 然后,它删除了原始的卷积层和批量归一化层,以简化模型结构。这种方法特别适用于模型部署阶段,因为它可以减少模型的复杂度,提高推理速度,同时保持模型性能。
12.class ChannelAttention(nn.Module):
# 这段代码定义了一个名为 ChannelAttention 的类,它是 PyTorch 的一个模块,用于实现通道注意力机制(Channel-attention module)。这种机制通常用于深度学习中的特征重标定,以增强模型对重要特征的关注度。
# 类定义和文档字符串。定义了一个名为 ChannelAttention 的类,它继承自 nn.Module 。
class ChannelAttention(nn.Module):
# 通道注意力模块https://github.com/open-mmlab/mmdetection/tree/v3.0.0rc1/configs/rtmdet。
"""Channel-attention module https://github.com/open-mmlab/mmdetection/tree/v3.0.0rc1/configs/rtmdet."""
# 初始化方法。定义了 __init__ 方法,它是类的构造函数,用于初始化 ChannelAttention 类的实例。
# 1.channels :参数指定输入特征图的通道数。
def __init__(self, channels: int) -> None:
# 初始化类并设置所需的基本配置和实例变量。
"""Initializes the class and sets the basic configurations and instance variables required."""
super().__init__()
# torch.nn.AdaptiveAvgPool2d(output_size)
# nn.AdaptiveAvgPool2d 是 PyTorch 中的一个函数,它用于实现自适应平均池化(Average Pooling)操作。这种池化方法可以根据目标输出尺寸自动调整池化核的大小,使得输入特征图能够被池化到指定的输出尺寸。
# 参数说明 :
# output_size ( int 或 tuple ): 池化输出的特征图尺寸。 如果是 int ,表示输出特征图的高度和宽度都是这个值。 如果是 tuple ,形式为 (h, w) ,表示输出特征图的高度和宽度分别为 h 和 w 。
# 返回值 :
# 返回一个 AdaptiveAvgPool2d 模块。
# nn.AdaptiveAvgPool2d 是一个非常有用的池化操作,特别是在需要将特征图池化到特定尺寸(如全局平均池化)时。它能够自动调整池化核的大小,使得输出特征图的尺寸与预期一致,这在很多深度学习模型中非常有用,尤其是在分类任务中。
# self.pool 是一个自适应平均池化层,用于将输入特征图的每个通道的空间维度(高和宽)缩减到1。
self.pool = nn.AdaptiveAvgPool2d(1)
# self.fc 是一个一维卷积层,用于在通道维度上进行特征转换,保持通道数不变。
self.fc = nn.Conv2d(channels, channels, 1, 1, 0, bias=True)
# self.act 是 Sigmoid 激活函数,用于将卷积输出转换为介于0和1之间的值,表示每个通道的重要性。
self.act = nn.Sigmoid()
# 前向传播方法。定义了 forward 方法,它是模块在前向传播时的行为。
# x :是输入特征图,其形状通常是 (batch_size, channels, height, width) 。
def forward(self, x: torch.Tensor) -> torch.Tensor:
# 使用激活对输入的卷积进行前向传递,可选择使用批量标准化。
"""Applies forward pass using activation on convolutions of the input, optionally using batch normalization."""
# self.pool(x) 将输入特征图 x 通过自适应平均池化层,得到形状为 (batch_size, channels, 1, 1) 的特征图。
# self.fc(self.pool(x)) 将池化后的特征图通过一维卷积层,进行通道维度的特征转换。
# self.act(self.fc(self.pool(x))) 将卷积输出通过 Sigmoid 激活函数,得到通道注意力权重。
# x * self.act(self.fc(self.pool(x))) 将输入特征图 x 与通道注意力权重相乘,实现通道注意力机制。
return x * self.act(self.fc(self.pool(x)))
# ChannelAttention 类通过自适应平均池化、一维卷积和 Sigmoid 激活函数实现了通道注意力机制。这种机制能够根据特征图的通道响应自动调整每个通道的重要性,有助于模型关注更有信息量的特征。
13.class SpatialAttention(nn.Module):
# 这段代码定义了一个名为 SpatialAttention 的类,它是 PyTorch 的一个模块,用于实现空间注意力机制(Spatial-attention module)。空间注意力机制允许模型在空间维度上关注图像的不同区域,通常用于特征重标定。
# 类定义和文档字符串。定义了一个名为 SpatialAttention 的类,它继承自 nn.Module 。
class SpatialAttention(nn.Module):
# 空间注意模块。
"""Spatial-attention module."""
# 初始化方法。
# 定义了 __init__ 方法,它是类的构造函数,用于初始化 SpatialAttention 类的实例。
# 1.kernel_size :参数指定卷积核的大小,默认为 7,但必须是 3 或 7。
def __init__(self, kernel_size=7):
# 使用内核大小参数初始化空间注意模块。
"""Initialize Spatial-attention module with kernel size argument."""
super().__init__()
# assert 语句确保 kernel_size 是一个有效的值。
assert kernel_size in {3, 7}, "kernel size must be 3 or 7"
# padding 计算卷积层的填充大小,对于 kernel_size 为 7 时,填充为 3;对于 kernel_size 为 3 时,填充为 1。
padding = 3 if kernel_size == 7 else 1
# self.cv1 是一个二维卷积层,用于在空间维度上应用注意力机制。它将两个通道的输入转换为一个通道的输出。
self.cv1 = nn.Conv2d(2, 1, kernel_size, padding=padding, bias=False)
# self.act 是 Sigmoid 激活函数,用于将卷积输出转换为介于 0 和 1 之间的值,表示空间位置的重要性。
self.act = nn.Sigmoid()
# 前向传播方法。定义了 forward 方法,它是模块在前向传播时的行为。
# x :是输入特征图,其形状通常是 (batch_size, channels, height, width) 。
def forward(self, x):
# 将通道和空间注意力应用于输入以进行特征重新校准。
"""Apply channel and spatial attention on input for feature recalibration."""
# torch.mean(x, 1, keepdim=True) 计算输入特征图在通道维度上的平均值,结果形状为 (batch_size, 1, height, width) 。
# torch.max(x, 1, keepdim=True)[0] 计算输入特征图在通道维度上的最大值,结果形状为 (batch_size, 1, height, width) 。
# torch.cat([...], 1) 将平均值和最大值在通道维度上拼接,形成一个新的特征图,形状为 (batch_size, 2, height, width) 。
# self.cv1(...) 将拼接后的特征图通过卷积层,得到空间注意力图,形状为 (batch_size, 1, height, width) 。
# self.act(...) 将卷积输出通过 Sigmoid 激活函数,得到最终的空间注意力权重。
# x * self.act(self.cv1(...)) 将输入特征图 x 与空间注意力权重相乘,实现空间注意力机制。
return x * self.act(self.cv1(torch.cat([torch.mean(x, 1, keepdim=True), torch.max(x, 1, keepdim=True)[0]], 1)))
# SpatialAttention 类通过计算输入特征图的通道平均值和最大值,然后通过卷积层和 Sigmoid 激活函数实现空间注意力机制。这种机制能够根据特征图的空间响应自动调整每个空间位置的重要性,有助于模型关注图像中更重要的区域。
14.class CBAM(nn.Module):
# 这段代码定义了一个名为 CBAM 的类,它是 PyTorch 的一个模块,用于实现卷积块注意力模块(Convolutional Block Attention Module, CBAM)。CBAM 结合了通道注意力(Channel Attention)和空间注意力(Spatial Attention)来增强特征图的信息。
# 类定义和文档字符串。定义了一个名为 CBAM 的类,它继承自 nn.Module 。
class CBAM(nn.Module):
# 卷积块注意模块。
"""Convolutional Block Attention Module."""
# 定义了 __init__ 方法,它是类的构造函数,用于初始化 CBAM 类的实例。
# 1.c1 :参数指定输入特征图的通道数。
# 2.kernel_size :参数指定空间注意力模块中卷积核的大小,默认为 7。
def __init__(self, c1, kernel_size=7):
# 使用给定的输入通道(c1)和内核大小初始化 CBAM。
"""Initialize CBAM with given input channel (c1) and kernel size."""
super().__init__()
# self.channel_attention 是一个 ChannelAttention 实例,用于实现通道注意力机制。
self.channel_attention = ChannelAttention(c1)
# self.spatial_attention 是一个 SpatialAttention 实例,用于实现空间注意力机制。
self.spatial_attention = SpatialAttention(kernel_size)
# 前向传播方法。定义了 forward 方法,它是模块在前向传播时的行为。
# x :是输入特征图,其形状通常是 (batch_size, channels, height, width) 。
def forward(self, x):
# 应用通过 C1 模块的前向传递。
"""Applies the forward pass through C1 module."""
# self.channel_attention(x) 将输入特征图 x 通过通道注意力模块,增强通道信息。
# self.spatial_attention(self.channel_attention(x)) 将通道注意力的输出进一步通过空间注意力模块,增强空间信息。
# 返回 :最终的特征图,它已经经过通道和空间注意力机制的增强。
return self.spatial_attention(self.channel_attention(x))
# CBAM 类通过结合通道注意力和空间注意力机制,增强了特征图的信息,有助于模型更好地理解和处理图像数据。这种注意力机制特别适用于需要细粒度特征提取的视觉任务,如图像分类、目标检测和语义分割等。通过这种方式,模型可以自适应地关注图像中更重要的通道和区域,从而提高性能。
15.class Concat(nn.Module):
# 这段代码定义了一个名为 Concat 的类,它是 PyTorch 的一个模块,用于将一系列张量沿着指定的维度进行拼接。
# 类定义和文档字符串。定义了一个名为 Concat 的类,它继承自 nn.Module 。
class Concat(nn.Module):
# 沿维度连接张量列表。
"""Concatenate a list of tensors along dimension."""
# 初始化方法。定义了 __init__ 方法,它是类的构造函数,用于初始化 Concat 类的实例。
# 1.dimension :参数指定张量拼接的维度,默认为 1,即通道维度。
def __init__(self, dimension=1):
# 沿指定维度连接张量列表。
"""Concatenates a list of tensors along a specified dimension."""
super().__init__()
# self.d 存储拼接的维度。
self.d = dimension
# 前向传播方法。定义了 forward 方法,它是模块在前向传播时的行为。
# x :是一个张量列表,需要被拼接。
def forward(self, x):
# YOLOv8 mask Proto 模块的前向传递。
"""Forward pass for the YOLOv8 mask Proto module."""
# torch.cat(tensors, dim=0, *, out=None) -> Tensor
# torch.cat 是 PyTorch 中的一个函数,用于将多个张量(tensors)沿着指定的维度进行拼接。这个函数是处理多维数组时常用的操作之一,特别是在需要合并特征图或在不同网络层之间传递数据时。
# 参数说明 :
# tensors :一个张量序列(可以是元组或列表),这些张量将被拼接。
# dim :指定拼接的维度,默认为 0,即沿着第一个维度(通常是批次维度)进行拼接。
# out :一个可选的输出张量,用于存储拼接结果。
# 返回值 :
# 返回一个新的张量,它是输入张量沿着指定维度拼接后的结果。
# 注意事项 :
# 除了拼接维度外,其他所有维度的大小必须相同。
# 如果需要在特定维度上拼接多个张量,这些张量在其他维度上的形状必须兼容。
# torch.cat 是 PyTorch 中处理张量拼接的基本工具,它在深度学习模型构建、数据处理和特征融合等多个方面都有广泛的应用。
# torch.cat(x, self.d) 使用 PyTorch 的 torch.cat 函数将列表 x 中的张量沿着维度 self.d 进行拼接。
# 返回 :拼接后的张量。
return torch.cat(x, self.d)
# Concat 类提供了一个简单而直接的方式来沿着指定维度拼接一系列张量。这种操作在深度学习中非常常见,尤其是在需要合并来自不同分支的特征图时,例如在特征金字塔网络(FPN)或多尺度特征融合中。通过 Concat 类,可以轻松地在模型中实现这种拼接操作,使得模型结构更加清晰和模块化。