YOLOV8改进模块
目录:
1.LSKA
LSKA将深度卷积层的2D卷积核分解为级联的水平1D和垂直1D内核。与标准LKA设计不同,提出的分解使得可以直接使用注意力模块中的深度卷积层的大内核,而不需要任何额外的块。与标准LKA模块相比,VAN中的提议LSKA模块可以获得相当的性能,并降低计算复杂性和内存占用。随着内核尺寸的增加,LSKA设计使VAN更加偏向于对象的形状而不是纹理。
注意力机制被用于选择图像中最重要区域。通常,它们可以分成四类:空间注意力 ,通道注意力,时间注意力和分支注意力。通道注意力关注模型层的“什么”语义属性。由于特征图的每个通道都是一个检测器的响应图,也称为滤波器 ,通道注意力机制允许模型关注跨通道的特定对象属性。与通道注意力不同,空间注意力关注模型应该关注“在哪里”语义相关的区域。STN 、GENet 和非局部神经网络 是涉及不同类型空间注意力方法的一些代表性作品。
自注意力是一种空间注意力,已应用于CNN和ViTs 。视觉注意力网络(VAN)提出了一种新的CNN主干网络,该网络结合了CNN的属性和自注意力模块。作者采用了具有大型核注意力(LKA)模块的CNN网络架构,以实现CNN的局灶性、长程依赖性和自注意力模块的空间适应性特性,类似于ViTs。此外,LKA模块具有通道适应性,这在标准的CNN和变压器中的自注意力模块中并不自然存在。为了提高计算效率,LKA采用具有深度卷积的膨胀卷积(DW-D-Conv)来实现更大的有效感受野(ERFs)。
将大核大小的深度卷积分解为小核大小的深度卷积,然后是具有相当大核大小的膨胀深度卷积。这种大核分解有助于减轻深度卷积单独使用大核大小而导致的计算成本二次方增加的问题。通过将深度卷积和深度扩展卷积的二维权核拆分为两个级联的一维可分离权核,可以得到等效的改进LKA结构。
与一般卷积、自注意和LKA模块相比,LSKA模块成功的四个重要特性如表I所示。
长距离依赖。自注意力机制是一个关键组件,使转换器能够模拟长程依赖。大核是捕捉全局信息的另一种方法。为了实现这一目标,遵循中的LKA设计,将大核分解为两个小核,而不是使用简单大核设计,因为它具有高计算足迹和优化难度。为了验证的LSKA的长距离依赖性,利用有效感受野(ERF)生成方法,生成VAN-LSKA-Tiny的ERF图。热图中分布的更大程度的暗区表示更大的ERF。从图4a到4f,观察到暗区从核大小7扩展到65,这表明LSKA方法可以有效地捕获图像中的长距离依赖。
空间和信道适应性。空间注意和通道注意是基于上下文依赖关系自适应地重新校准特征权重的两种常见策略。继承了LKA的设计,与自关注相比,LKA包含了更低参数和计算复杂度的属性。LKA与LSKA的不同之处在于,采用了水平和垂直级联的内核,进一步降低了内存和计算复杂度。
对于极大型核的可扩展性。VAN中的LKA-trivial会导致计算成本随着核大小的增加而呈二次方增长。LKA设计显著降低了计算足迹,但是当核大小超过23 \times 23时,模型参数的数量会随着核大小的增加而增加。当最近最先进的SLaK-Decomposed和SLaK-Sparse Decomposed方法被引入VAN时,当核大小超过100时,它们比LKA具有更低的参数数量和计算足迹。令人惊讶的是,与LKA和SLaK相比,所提出的LKA-trivial和LKA的LSAKA-trivial和LSAKA版本不仅降低了计算成本,而且保持了VAN的模型参数数量相对恒定。请注意,核大小也指最大感受野(MRF)。
关于精度性能。随着核大小从23增加到53,LSKA-Base显示出持续增长。相反,LKA-Base在核大小超过23后开始饱和。这些结果表明,就参数大小、FLOPs和精度而言,LSKA在极大型核上具有可扩展性。
# LSKA注意力机制
#注意:dilation与k决定卷积核大小2xd-1=5,|k/d|=7
class LSKA(nn.Module):
def __init__(self, c1, c2, k=21):
super().__init__()
# depth-wise convolution
self.conv0 = nn.Conv2d(c1, 1, 5, padding=2)
self.conv1 = nn.Conv2d(1, c1, 5, padding=2)
# depth-wise dilation convolution
self.conv_spatial_1 = nn.Conv2d(c1, 1, 7, stride=1, padding=9, dilation=3)
self.conv_spatial_2 = nn.Conv2d(1, c1, 7, stride=1, padding=9, dilation=3)
# channel convolution (1×1 convolution)
self.conv2 = nn.Conv2d(c1, c2, 1)
def forward(self, x):
u = x.clone()
u = self.conv2(u)
attn = self.conv0(x)
attn = self.conv1(attn)
attn = self.conv_spatial_1(attn)
attn = self.conv_spatial_2(attn)
attn = self.conv2(attn)
return u + attn
2.BiFPN
BiFPN 全称 Bidirectional Feature Pyramid Network 加权双向(自顶向下 + 自低向上)特征金字塔网络。相比较于PANet,BiFPN在设计上的改变:
总结下图:
蓝色部分为自顶向下的通路,传递的是高层特征的语义信息;红色部分是自底向上的通路,传递的是低层特征的位置信息;紫色部分是上述第二点提到的同一层在输入节点和输入节点间新加的一条边。
1.我们删除那些只有一条输入边的节点。这么做的思路很简单:如果一个节点只有一条输入边而没有特征融合,那么它对旨在融合不同特征的特征网络的贡献就会很小。删除它对我们的网络影响不大,同时简化了双向网络;如上图d 的 P7右边第一个节点
2.如果原始输入节点和输出节点处于同一层,我们会在原始输入节点和输出节点之间添加一条额外的边。思路:以在不增加太多成本的情况下融合更多的特性;
3.与只有一个自顶向下和一个自底向上路径的PANet不同,我们处理每个双向路径(自顶向下和自底而上)路径作为一个特征网络层,并重复同一层多次,以实现更高层次的特征融合。如下图EfficientNet 的网络结构所示,我们对BiFPN是重复使用多次的。而这个使用次数也不是我们认为设定的,而是作为参数一起加入网络的设计当中,使用NAS技术算出来的。
4.Weighted Feature Fusion 带权特征融合:学习不同输入特征的重要性,对不同输入特征有区分的融合。
设计思路:传统的特征融合往往只是简单的 feature map 叠加/相加 (sum them up),比如使用concat或者shortcut连接,而不对同时加进来的 feature map 进行区分。然而,不同的输入 feature map 具有不同的分辨率,它们对融合输入 feature map 的贡献也是不同的,因此简单的对他们进行相加或叠加处理并不是最佳的操作。所以这里我们提出了一种简单而高效的加权特融合的机制。
总结BiFPN = 新型加强版的PANet(重复双向跨尺度连接) + 带权重的特征融合机制
### 加BiFPN模块 ########
class BiFPN_Concat2(nn.Module):
def __init__(self, dimension=1):
super(BiFPN_Concat2, self).__init__()
self.d = dimension
self.w = nn.Parameter(torch.ones(2, dtype=torch.float32), requires_grad=True)
self.epsilon = 0.0001
def forward(self, x):
w = self.w
weight = w / (torch.sum(w, dim=0) + self.epsilon) # 将权重进行归一化
# Fast normalized fusion
x = [weight[0] * x[0], weight[1] * x[1]]
return torch.cat(x, self.d)
# 三个分支concat操作
class BiFPN_Concat3(nn.Module):
def __init__(self, dimension=1):
super(BiFPN_Concat3, self).__init__()
self.d = dimension
# 设置可学习参数 nn.Parameter的作用是:将一个不可训练的类型Tensor转换成可以训练的类型parameter
# 并且会向宿主模型注册该参数 成为其一部分 即model.parameters()会包含这个parameter
# 从而在参数优化的时候可以自动一起优化
self.w = nn.Parameter(torch.ones(3, dtype=torch.float32), requires_grad=True)
self.epsilon = 0.0001
def forward(self, x):
w = self.w
weight = w / (torch.sum(w, dim=0) + self.epsilon) # 将权重进行归一化
# Fast normalized fusion
x = [weight[0] * x[0], weight[1] * x[1], weight[2] * x[2]]
return torch.cat(x, self.d)
3.EMA
EMA注意力机制,基于跨空间学习的高效多尺度注意力模块,效果优于ECA、CBAM、CA ,小目标涨点明显
在各种计算机视觉任务中,通道或空间注意力机制在产生更清晰的特征表示方面的显著有效性得到了证明。然而,通过通道降维来建模跨通道关系可能会给提取深度视觉表示带来副作用。提出了一种新的高效的多尺度注意力(EMA)模块。以保留每个通道上的信息和降低计算开销为目标,将部分通道重塑为批量维度,并将通道维度分组为多个子特征,使空间语义特征在每个特征组中均匀分布。具体来说,除了对全局信息进行编码以重新校准每个并行分支中的通道权重外,还通过跨维度交互进一步聚合两个并行分支的输出特征,以捕获像素级成对关系。
class EMA(nn.Module):
def __init__(self, channels, factor=32):
super(EMA, self).__init__()
self.groups = factor
assert channels // self.groups > 0
self.softmax = nn.Softmax(-1)
self.agp = nn.AdaptiveAvgPool2d((1, 1))
self.pool_h = nn.AdaptiveAvgPool2d((None, 1))
self.pool_w = nn.AdaptiveAvgPool2d((1, None))
self.gn = nn.GroupNorm(channels // self.groups, channels // self.groups)
self.conv1x1 = nn.Conv2d(channels // self.groups, channels // self.groups, kernel_size=1, stride=1, padding=0)
self.conv3x3 = nn.Conv2d(channels // self.groups, channels // self.groups, kernel_size=3, stride=1, padding=1)
def forward(self, x):
b, c, h, w = x.size()
group_x = x.reshape(b * self.groups, -1, h, w) # b*g,c//g,h,w
x_h = self.pool_h(group_x)
x_w = self.pool_w(group_x).permute(0, 1, 3, 2)
hw = self.conv1x1(torch.cat([x_h, x_w], dim=2))
x_h, x_w = torch.split(hw, [h, w], dim=2)
x1 = self.gn(group_x * x_h.sigmoid() * x_w.permute(0, 1, 3, 2).sigmoid())
x2 = self.conv3x3(group_x)
x11 = self.softmax(self.agp(x1).reshape(b * self.groups, -1, 1).permute(0, 2, 1))
x12 = x2.reshape(b * self.groups, c // self.groups, -1) # b*g, c//g, hw
x21 = self.softmax(self.agp(x2).reshape(b * self.groups, -1, 1).permute(0, 2, 1))
x22 = x1.reshape(b * self.groups, c // self.groups, -1) # b*g, c//g, hw
weights = (torch.matmul(x11, x12) + torch.matmul(x21, x22)).reshape(b * self.groups, 1, h, w)
return (group_x * weights.sigmoid()).reshape(b, c, h, w)
4.Mish 激活函数
激活函数对数据进行非线性变化,去除数据中的冗余信息并保留有效特征,增强模型的非线性表达能力。YOLOv8中的卷积模块结构由卷积块、归一化块和SiLU激活函数组成。
在输入值在很大或很小的情况下,SiLU激活函数接近为一个线性函数,从而使神经网络在训练线性模型的初始阶段实现更快速的收敛。但SiLU激活函数在处理非线性特征时,会产生梯度饱和和梯度爆炸的问题,从而导致神经网络的性能下降,因此采用Mish激活函数来进行对SiLU损失函数的代替:
Mish激活函数有更好的平滑性,在整个输入范围具有可微连续性。在处理数据时,可以更好地捕获数据中存在的非线性关系,来提升模型的准确度和泛化能力。而且Mish激活函数在输入为0时附近的曲率更高,可以有效避免SiLU激活函数在训练过程中出现梯度爆炸和梯度饱和的问题。
#ultralytics\nn\modules\conv.py内Conv类中
# default_act = nn.SiLU() # default activation
default_act=nn.Mish()