目录
- 一、【】注意力机制
- 1.1【GCA】注意力介绍
- 1.2【GCA】核心代码
- 二、添加【GCA】注意力机制
- 2.1STEP1
- 2.2STEP2
- 2.3STEP3
- 2.4STEP4
- 三、yaml文件与运行
- 3.1yaml文件
- 3.2运行成功截图
一、【】注意力机制
1.1【GCA】注意力介绍
下图是【GCA】的结构图,让我们简单分析一下运行过程和优势
处理过程:
- 输入特征图
- 输入的特征图大小为 𝐶×𝐻×𝑊,其中 𝐶 是通道数𝐻和 𝑊分别是特征图的高度和宽度。
- Context Modeling(上下文建模):
- 首先,输入特征图通过一个 1×1 卷积层(𝑊𝑘),这一步将每个像素点的特征压缩并降低维度,卷积输出的大小仍为 𝐶×𝐻×𝑊。
- 之后,将特征图进行降维操作,应用 Softmax 函数,生成全局的上下文注意力图。这一步的结果是对特征图的全局加权,使得每个位置能够捕捉到整个图像范围内的全局上下文信息。注意力图应用于输入特征图,进行全局加权调整,得到全局上下文的增强特征图。
- Transform(特征变换):
- 第一步卷积操作(1×1 Conv):首先应用另一个 1×1 卷积层(𝑊1 )来重新映射全局上下文的特征,这步操作不会改变特征图的空间维度。
- LayerNorm 和 ReLU:然后通过 Layer Normalization(层归一化)和 ReLU 激活函数来标准化并引入非线性变换,从而增强特征的表达能力。
- 第二步卷积操作(1×1 Conv):接着,经过一个第二次的 1×1 卷积层(𝑊2)来进一步处理和压缩特征。
- 残差连接:
- 经过全局上下文增强的特征与原始输入特征通过 残差连接(Skip Connection) 进行叠加,这种操作保留了原始特征的同时,融入了全局上下文的增强信息,从而提高模型的鲁棒性和特征表达能力。
- 输出特征图:
- 输出特征图仍然是 𝐶×𝐻×𝑊,与输入保持相同的形状,但已经通过全局上下文增强和特征变换的处理。
优势: - 全局上下文捕捉:GC 模块的最大优势在于能够通过 Softmax 注意力机制对整个特征图进行全局建模。相比传统的局部卷积操作,该模块可以捕捉到图像全局的上下文信息,有助于识别跨越远距离的依赖关系,尤其是在处理复杂场景时,能够提高目标识别的精度。
- 计算效率高:
虽然该模块引入了注意力机制,但它通过 1×1 卷积来进行维度的压缩,降低了计算复杂度。相比标准的多头自注意力机制(如 Transformer),GC 模块在计算复杂度上更加友好,非常适合嵌入在卷积神经网络中。 - 残差连接增强特征表达:
残差连接使得原始特征得以保留,确保全局上下文信息的增强不会损失原有的特征表达能力。这种机制可以缓解梯度消失的问题,促进更深层次的网络结构训练。 - 通用性强:
由于 GC 模块使用的是标准的卷积和简单的注意力机制,它可以方便地嵌入到各种神经网络结构中,例如 ResNet、VGG 等,用于提升模型的全局信息感知能力。 - 适应性好:
该模块通过 Softmax 注意力机制学习全局上下文的加权方式,能够动态适应不同输入特征,从而使模型在不同场景下有更好的泛化能力和适应性。
1.2【GCA】核心代码
import torch # 导入 PyTorch
from torch import nn # 从 PyTorch 导入神经网络模块
# 定义 ContextBlock 类,继承自 nn.Module
class ContextBlock(nn.Module):
def __init__(self, inplanes, ratio, pooling_type='att', fusion_types=('channel_add',)):
super(ContextBlock, self).__init__()
# 验证 fusion_types 是否有效
valid_fusion_types = ['channel_add', 'channel_mul']
# 检查 pooling_type 是否在有效选项 ['avg', 'att'] 中
assert pooling_type in ['avg', 'att']
# 确认 fusion_types 是列表或元组
assert isinstance(fusion_types, (list, tuple))
# 确认 fusion_types 中的所有元素都在 valid_fusion_types 中
assert all([f in valid_fusion_types for f in fusion_types])
# 确保至少有一个 fusion 类型被指定
assert len(fusion_types) > 0, 'at least one fusion should be used'
self.inplanes = inplanes # 输入通道数
self.ratio = ratio # 缩减比例
self.planes = int(inplanes * ratio) # 缩减后的通道数
self.pooling_type = pooling_type # 池化类型('avg' 或 'att')
self.fusion_types = fusion_types # 融合类型
# 如果池化类型为 'att',定义注意力池化的卷积层和 softmax
if pooling_type == 'att':
self.conv_mask = nn.Conv2d(inplanes, 1, kernel_size=1)
self.softmax = nn.Softmax(dim=2) # 在维度 2 上做 softmax
else:
# 如果池化类型为 'avg',定义自适应平均池化层
self.avg_pool = nn.AdaptiveAvgPool2d(1)
# 定义 'channel_add' 融合类型的卷积层序列
if 'channel_add' in fusion_types:
self.channel_add_conv = nn.Sequential(
nn.Conv2d(self.inplanes, self.planes, kernel_size=1), # 1x1 卷积
nn.LayerNorm([self.planes, 1, 1]), # 层归一化
nn.ReLU(inplace=True), # 激活函数 ReLU
nn.Conv2d(self.planes, self.inplanes, kernel_size=1) # 1x1 卷积,恢复到原通道数
)
else:
self.channel_add_conv = None
# 定义 'channel_mul' 融合类型的卷积层序列
if 'channel_mul' in fusion_types:
self.channel_mul_conv = nn.Sequential(
nn.Conv2d(self.inplanes, self.planes, kernel_size=1), # 1x1 卷积
nn.LayerNorm([self.planes, 1, 1]), # 层归一化
nn.ReLU(inplace=True), # 激活函数 ReLU
nn.Conv2d(self.planes, self.inplanes, kernel_size=1) # 1x1 卷积,恢复到原通道数
)
else:
self.channel_mul_conv = None
# 定义空间池化方法
def spatial_pool(self, x):
batch, channel, height, width = x.size() # 获取输入张量的形状
if self.pooling_type == 'att': # 如果池化类型为 'att'
input_x = x.view(batch, channel, height * width) # 展平 H 和 W
input_x = input_x.unsqueeze(1) # 增加一个维度
context_mask = self.conv_mask(x) # 应用 1x1 卷积层
context_mask = context_mask.view(batch, 1, height * width) # 展平 H 和 W
context_mask = self.softmax(context_mask) # 在 H * W 上应用 softmax
context_mask = context_mask.unsqueeze(-1) # 增加一个维度
context = torch.matmul(input_x, context_mask) # 计算加权和
context = context.view(batch, channel, 1, 1) # 恢复形状
else:
context = self.avg_pool(x) # 如果池化类型为 'avg',直接应用平均池化
return context # 返回上下文张量
# 定义前向传播方法
def forward(self, x):
context = self.spatial_pool(x) # 获取上下文信息
out = x # 初始化输出
if self.channel_mul_conv is not None:
channel_mul_term = torch.sigmoid(self.channel_mul_conv(context)) # 应用通道乘法融合
out = out * channel_mul_term # 输出乘以融合结果
if self.channel_add_conv is not None:
channel_add_term = self.channel_add_conv(context) # 应用通道加法融合
out = out + channel_add_term # 输出加上融合结果
return out # 返回最终输出
# 测试代码块
if __name__ == "__main__":
in_tensor = torch.ones((1, 64, 128, 128)) # 创建一个全1的输入张量,形状为 (1, 64, 128, 128)
cb = ContextBlock(inplanes=64, ratio=0.25, pooling_type='att') # 创建 ContextBlock 实例
out_tensor = cb(in_tensor) # 传递输入张量进行前向传播
print(in_tensor.shape) # 打印输入张量的形状
print(out_tensor.shape) # 打印输出张量的形状
二、添加【GCA】注意力机制
2.1STEP1
首先找到ultralytics/nn文件路径下新建一个Add-module的python文件包【这里注意一定是python文件包,新建后会自动生成_init_.py】,如果已经跟着我的教程建立过一次了可以省略此步骤,随后新建一个GCA.py文件并将上文中提到的注意力机制的代码全部粘贴到此文件中,如下图所示
2.2STEP2
在STEP1中新建的_init_.py文件中导入增加改进模块的代码包如下图所示
2.3STEP3
找到ultralytics/nn文件夹中的task.py文件,在其中按照下图添加
2.4STEP4
定位到ultralytics/nn文件夹中的task.py文件中的def parse_model(d, ch, verbose=True): # model_dict, input_channels(3)函数添加如图代码,【如果不好定位可以直接ctrl+f搜索定位】
三、yaml文件与运行
3.1yaml文件
以下是添加【GCA】注意力机制在Backbone中的yaml文件,大家可以注释自行调节,效果以自己的数据集结果为准
# Ultralytics YOLO 🚀, AGPL-3.0 license
# YOLO11 object detection model with P3-P5 outputs. For Usage examples see https://docs.ultralytics.com/tasks/detect
# Parameters
nc: 80 # number of classes
scales: # model compound scaling constants, i.e. 'model=yolo11n.yaml' will call yolo11.yaml with scale 'n'
# [depth, width, max_channels]
n: [0.50, 0.25, 1024] # summary: 319 layers, 2624080 parameters, 2624064 gradients, 6.6 GFLOPs
s: [0.50, 0.50, 1024] # summary: 319 layers, 9458752 parameters, 9458736 gradients, 21.7 GFLOPs
m: [0.50, 1.00, 512] # summary: 409 layers, 20114688 parameters, 20114672 gradients, 68.5 GFLOPs
l: [1.00, 1.00, 512] # summary: 631 layers, 25372160 parameters, 25372144 gradients, 87.6 GFLOPs
x: [1.00, 1.50, 512] # summary: 631 layers, 56966176 parameters, 56966160 gradients, 196.0 GFLOPs
# YOLO11n backbone
backbone:
# [from, repeats, module, args]
- [-1, 1, Conv, [64, 3, 2]] # 0-P1/2
- [-1, 1, Conv, [128, 3, 2]] # 1-P2/4
- [-1, 2, C3k2, [256, False, 0.25]]
- [-1, 1, Conv, [256, 3, 2]] # 3-P3/8
- [-1, 2, C3k2, [512, False, 0.25]]
- [-1, 1, Conv, [512, 3, 2]] # 5-P4/16
- [-1, 2, C3k2, [512, True]]
- [-1, 1, Conv, [1024, 3, 2]] # 7-P5/32
- [-1, 2, C3k2, [1024, True]]
- [-1, 1, SPPF, [1024, 5]] # 9
- [-1, 2, C2PSA, [1024]] # 10
# YOLO11n head
head:
- [-1, 1, nn.Upsample, [None, 2, "nearest"]]
- [[-1, 6], 1, Concat, [1]] # cat backbone P4
- [-1, 2, C3k2, [512, False]] # 13
- [-1, 1, nn.Upsample, [None, 2, "nearest"]]
- [[-1, 4], 1, Concat, [1]] # cat backbone P3
- [-1, 2, C3k2, [256, False]] # 16 (P3/8-small)
- [-1,1,ContextBlock,[256]]
- [-1, 1, Conv, [256, 3, 2]]
- [[-1, 13], 1, Concat, [1]] # cat head P4
- [-1, 2, C3k2, [512, False]] # 19 (P4/16-medium)
- [-1, 1, Conv, [512, 3, 2]]
- [[-1, 10], 1, Concat, [1]] # cat head P5
- [-1, 2, C3k2, [1024, True]] # 22 (P5/32-large)
- [[17, 20, 23], 1, Detect, [nc]] # Detect(P3, P4, P5)
以上添加位置仅供参考,具体添加位置以及模块效果以自己的数据集结果为准
3.2运行成功截图
OK 以上就是添加【GCA】注意力机制的全部过程了,后续将持续更新尽情期待