目录
- 一、MLCA注意力机制
- 1.1MLCA注意力介绍
- 1.2MLCA核心代码
- 五、添加MLCA注意力机制
- 5.1STEP1
- 5.2STEP2
- 5.3STEP3
- 5.4STEP4
- 六、yaml文件与运行
- 6.1yaml文件
- 6.2运行成功截图
一、MLCA注意力机制
1.1MLCA注意力介绍
MLCA(Multi-Level Channel Attention,多级通道注意力)是一种用于提升卷积神经网络(CNN)性能的注意力机制,主要通过在多个层次上捕捉不同通道间的依赖关系,来增强网络对重要特征的关注。MLCA的工作流程:
- 特征提取: 首先,输入图像经过卷积网络的多层卷积提取出多层次的特征图,这些特征图代表了从不同尺度和不同深度捕捉到的特征信息。
- 通道权重计算: 在每个层次的特征图上,MLCA 分别计算出每个通道的重要性权重,使用类似 SE 模块的全局池化操作得到全局特征表示,然后通过一系列非线性操作(如全连接、激活函数)生成通道权重。
- 多级加权: 通过通道权重对各层次的特征图进行加权操作,增强重要特征通道的响应,抑制无关或冗余的特征。
- 融合与输出: 对不同层次的特征进行融合,形成最终的特征表达,然后将其传递给后续的网络模块(如分类器或检测头)。
工作流程图如下所示:
-
输入
输入数据的维度为 (𝐶,𝑊,𝐻),其中:𝐶 表示通道数W 表示宽度H 表示高度 -
局部区域池化(LAP)和区域池化(AP)
在这一步,输入数据被分成不同的局部区域块,这些块的大小为 (ks,ks),其中ks=5(kernel size)。通过这一步操作,宽度和高度被缩小为 W/ks 和 H/ks,形成局部特征表示。这一步类似于局部区域的平均池化,帮助提取局部的空间特征。 -
全局平均池化(GAP)
接着进行全局平均池化(GAP),将每个局部区域的特征聚合到通道维度上,形成全局通道特征。这会将每个通道的信息进一步简化,汇总成单个全局特征。 -
1D卷积(Conv1d)
然后通过一个1D卷积操作,对这些全局特征进行进一步处理。1D卷积的目的是在通道维度上提取不同通道之间的相关性,以增强通道注意力机制。这里,卷积核大小 𝑘,k 作用于通道维度,以捕捉更多通道间的依赖关系。 -
特征重塑(Reshape)
卷积后的输出特征经过重塑(Reshape)操作,恢复到原始的特征形状,使其可以与原始特征对齐。 -
未池化操作(UNAP)
为了将池化后的特征与原始输入对齐,这里使用了未池化操作(UNAP),将特征图恢复到原始的分辨率。通过 UNAP 操作,池化缩减的特征图的维度被扩展回去,以保持输入和输出特征在相同维度上的一致性。 -
通道加权
经过上述过程后,MLCA模块生成的局部通道注意力权重会与输入特征进行加权叠加,结合了通道间的相互依赖关系以及局部特征,产生增强的特征表示。 -
输出
最终输出维度与输入维度保持一致,为 (𝐶,𝑊,𝐻)。通过这种通道和局部特征的融合,网络能够更加有效地聚焦在重要的局部特征和通道间的全局依赖关系上,提升模型的表达能力。
1.2MLCA核心代码
import torch
import torch.nn as nn
import torch.nn.functional as F
import mathclass MLCA(nn.Module):def __init__(self, in_size, local_size=5, gamma=2, b=1, local_weight=0.5):super(MLCA, self).__init__()# ECA 计算方法self.local_size = local_sizeself.gamma = gammaself.b = bt = int(abs(math.log(in_size, 2) + self.b) / self.gamma) # eca gamma=2k = t if t % 2 else t + 1self.conv = nn.Conv1d(1, 1, kernel_size=k, padding=(k - 1) // 2, bias=False)self.conv_local = nn.Conv1d(1, 1, kernel_size=k, padding=(k - 1) // 2, bias=False)self.local_weight = local_weightself.local_arv_pool = nn.AdaptiveAvgPool2d(local_size)self.global_arv_pool = nn.AdaptiveAvgPool2d(1)def forward(self, x):local_arv = self.local_arv_pool(x)global_arv = self.global_arv_pool(local_arv)b, c, m, n = x.shapeb_local, c_local, m_local, n_local = local_arv.shape# (b,c,local_size,local_size) -> (b,c,local_size*local_size)-> (b,local_size*local_size,c)-> (b,1,local_size*local_size*c)temp_local = local_arv.view(b, c_local, -1).transpose(-1, -2).reshape(b, 1, -1)temp_global = global_arv.view(b, c, -1).transpose(-1, -2)y_local = self.conv_local(temp_local)y_global = self.conv(temp_global)# (b,c,local_size,local_size) <- (b,c,local_size*local_size)<-(b,local_size*local_size,c) <- (b,1,local_size*local_size*c)y_local_transpose = y_local.reshape(b, self.local_size * self.local_size, c).transpose(-1, -2).view(b, c,self.local_size,self.local_size)# y_global_transpose = y_global.view(b, -1).transpose(-1, -2).unsqueeze(-1)y_global_transpose = y_global.view(b, -1).unsqueeze(-1).unsqueeze(-1) # 代码修正# print(y_global_transpose.size())# 反池化att_local = y_local_transpose.sigmoid()att_global = F.adaptive_avg_pool2d(y_global_transpose.sigmoid(), [self.local_size, self.local_size])# print(att_local.size())# print(att_global.size())att_all = F.adaptive_avg_pool2d(att_global * (1 - self.local_weight) + (att_local * self.local_weight), [m, n])# print(att_all.size())x = x * att_allreturn xdef 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-sizeif p is None:p = k // 2 if isinstance(k, int) else [x // 2 for x in k] # auto-padreturn pclass Conv(nn.Module):"""Standard convolution with args(ch_in, ch_out, kernel, stride, padding, groups, dilation, activation)."""default_act = nn.SiLU() # default activationdef __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))class C2f_MLCA(nn.Module):"""Faster Implementation of CSP Bottleneck with 2 convolutions."""def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5):"""Initialize CSP bottleneck layer with two convolutions with arguments ch_in, ch_out, number, shortcut, groups,expansion."""super().__init__()self.c = int(c2 * e) # hidden channelsself.cv1 = Conv(c1, 2 * self.c, 1, 1)self.cv2 = Conv((2 + n) * self.c, c2, 1) # optional act=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))def forward(self, x):"""Forward pass through C2f layer."""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))def forward_split(self, x):"""Forward pass using split() instead of chunk()."""y = list(self.cv1(x).split((self.c, self.c), 1))y.extend(m(y[-1]) for m in self.m)return self.cv2(torch.cat(y, 1))class Bottleneck(nn.Module):"""Standard bottleneck."""def __init__(self, c1, c2, shortcut=True, g=1, k=(3, 3), e=0.5):"""Initializes a bottleneck module with given input/output channels, shortcut option, group, kernels, andexpansion."""super().__init__()c_ = int(c2 * e) # hidden channelsself.cv1 = Conv(c1, c_, k[0], 1)self.cv2 = Conv(c_, c2, k[1], 1, g=g)self.add = shortcut and c1 == c2self.MLCA = MLCA(c2)def forward(self, x):"""'forward()' applies the YOLO FPN to input data."""return x + self.MLCA(self.cv2(self.cv1(x))) if self.add else self.MLCA(self.cv2(self.cv1(x)))if __name__ == "__main__":attention = MLCA(in_size=64)inputs = torch.randn((2, 55, 16, 16))result = attention(inputs)print(result.shape)
五、添加MLCA注意力机制
5.1STEP1
首先找到ultralytics/nn文件路径下新建一个Add-module的python文件包【这里注意一定是python文件包,新建后会自动生成_init_.py】,如果已经跟着我的教程建立过一次了可以省略此步骤,随后新建一个MLCA.py文件并将上文中提到的注意力机制的代码全部粘贴到此文件中,如下图所示
5.2STEP2
在STEP1中新建的_init_.py文件中导入增加改进模块的代码包如下图所示
5.3STEP3
找到ultralytics/nn文件夹中的task.py文件,在其中按照下图添加
5.4STEP4
定位到ultralytics/nn文件夹中的task.py文件中的def parse_model(d, ch, verbose=True): # model_dict, input_channels(3)函数添加如图代码,【如果不好定位可以直接ctrl+f搜索定位】
六、yaml文件与运行
6.1yaml文件
以下是添加MLCA注意力机制在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 GFLOPss: [0.50, 0.50, 1024] # summary: 319 layers, 9458752 parameters, 9458736 gradients, 21.7 GFLOPsm: [0.50, 1.00, 512] # summary: 409 layers, 20114688 parameters, 20114672 gradients, 68.5 GFLOPsl: [1.00, 1.00, 512] # summary: 631 layers, 25372160 parameters, 25372144 gradients, 87.6 GFLOPsx: [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, 1, MLCA, []]- [-1, 2, C2PSA, [1024]] # 11# YOLO11n head
head:- [-1, 1, nn.Upsample, [None, 2, "nearest"]]- [[-1, 6], 1, Concat, [1]] # cat backbone P4- [-1, 2, C3k2, [512, False]] # 14- [-1, 1, nn.Upsample, [None, 2, "nearest"]]- [[-1, 4], 1, Concat, [1]] # cat backbone P3- [-1, 2, C3k2, [256, False]] # 17 (P3/8-small)- [-1, 1, Conv, [256, 3, 2]]- [[-1, 14], 1, Concat, [1]] # cat head P4- [-1, 2, C3k2, [512, False]] # 20 (P4/16-medium)- [-1, 1, Conv, [512, 3, 2]]- [[-1, 11], 1, Concat, [1]] # cat head P5- [-1, 2, C3k2, [1024, True]] # 23 (P5/32-large)- [[17, 20, 23], 1, Detect, [nc]] # Detect(P3, P4, P5)
6.2运行成功截图
OK 以上就是添加MLCA注意力机制的全部过程了,后续将持续更新尽情期待