【深度学习】(8) CNN中的通道注意力机制(SEnet、ECAnet),附Tensorflow完整代码

article/2025/9/19 19:22:24

各位同学好,今天和大家分享一下attention注意力机制在CNN卷积神经网络中的应用,重点介绍三种注意力机制,及其代码复现。

在我之前的神经网络专栏的文章中也使用到过注意力机制,比如在MobileNetV3、EfficientNet网络中都是用了SE注意力机制,感兴趣的可以看一下:https://blog.csdn.net/dgvv4/category_11517910.html。那么今天就和大家来聊一聊注意力机制。


1. 引言

注意力机制源于对人类视觉的研究。在认知科学中,由于信息处理的瓶颈,人类会选择性地关注所有信息中的一部分,同时忽略其他可见信息。为了合理利用有限的视觉信息处理资源,人类需要选择视觉区域中的特定部分,然后重点关注它。

注意力机制没有严格的数学定义,例如传统的局部图像特征提取、滑动窗口方法等都可以看作是一种注意力机制。在神经网络中,注意力机制通常是一个额外的神经网络,能够硬性选择输入的某些部分,或者给输入的不同部分分配不同的权重注意力机制能够从大量的信息中筛选出重要的信息。

在神经网络中引入注意力机制有很多种方法,以卷积神经网络为例,可以在空间维度增加引入注意力机制,也可以在通道维度增加注意力机制(SE),当然也有混合维度(CBAM)即空间维度和通道维度增加注意力机制。


2. SENet注意力机制

2.1 方法介绍

SE注意力机制(Squeeze-and-Excitation Networks)在通道维度增加注意力机制,关键操作是squeeze和excitation。

通过自动学习的方式,即使用另外一个新的神经网络获取到特征图的每个通道的重要程度,然后用这个重要程度去给每个特征赋予一个权重值,从而让神经网络重点关注某些特征通道提升对当前任务有用的特征图的通道,并抑制对当前任务用处不大的特征通道。

如下图所示,在输入SE注意力机制之前(左侧白图C2),特征图的每个通道的重要程度都是一样的,通过SENet之后(右侧彩图C2),不同颜色代表不同的权重,使每个特征通道的重要性变得不一样了,使神经网络重点关注某些权重值大的通道。


2.2 实现过程:

(1)Squeeze(Fsq):通过全局平均池化将每个通道的二维特征(H*W)压缩为1个实数,将特征图从 [h, w, c] ==> [1,1,c]

(2)excitation(Fex)给每个特征通道生成一个权重值,论文中通过两个全连接层构建通道间的相关性,输出的权重值数目和输入特征图的通道数相同。[1,1,c] ==> [1,1,c]

(3)Scale(Fscale):将前面得到的归一化权重加权到每个通道的特征上论文中使用的是乘法,逐通道乘以权重系数。[h,w,c]*[1,1,c] ==> [h,w,c]

下面我用EfficientNet中的SE注意力机制来说明一下这个流程。

squeeze操作:特征图经过全局平均池化,将特征图压缩成特征向量[1,1,c]

excitation操作:FC1层+Swish激活+FC2层+Sigmoid激活。通过全连接层(FC1),将特征图向量的通道维度降低为原来的1/r,即[1,1,c*1/r];然后经过Swish激活函数;再通过一个全连接层(FC2),将特征图向量的特征图上升回原来[1,1,c];然后经过sigmoid函数转化为一个0-1之间的归一化权重向量。

scale操作:将归一化权重和原输入特征图逐通道相乘,生成加权后的特征图。

小节:

(1)SENet的核心思想是通过全连接网络根据loss损失来自动学习特征权重,而不是直接根据特征通道的数值分配来判断,使有效的特征通道的权重大。当然SE注意力机制不可避免的增加了一些参数和计算量,但性价比还是挺高的。

(2)论文认为excitation操作中使用两个全连接层相比直接使用一个全连接层,它的好处在于,具有更多的非线性,可以更好地拟合通道间的复杂关联。


2.3 代码复现

import tensorflow as tf
from tensorflow.keras import layers, Model, Input# se注意力机制
def se_block(inputs, ratio=4):  # ratio代表第一个全连接层下降通道数的系数# 获取输入特征图的通道数in_channel = inputs.shape[-1]# 全局平均池化[h,w,c]==>[None,c]x = layers.GlobalAveragePooling2D()(inputs)# [None,c]==>[1,1,c]x = layers.Reshape(target_shape=(1,1,in_channel))(x)# [1,1,c]==>[1,1,c/4]x = layers.Dense(in_channel//ratio)(x)  # 全连接下降通道数# relu激活x = tf.nn.relu(x)# [1,1,c/4]==>[1,1,c]x = layers.Dense(in_channel)(x)  # 全连接上升通道数# sigmoid激活,权重归一化x = tf.nn.sigmoid(x)# [h,w,c]*[1,1,c]==>[h,w,c]outputs = layers.multiply([inputs, x])  # 归一化权重和原输入特征图逐通道相乘return outputs  # 测试SE注意力机制
if __name__ == '__main__':# 构建输入inputs = Input([56,56,24])x = se_block(inputs)  # 接收SE返回值model = Model(inputs, x)  # 构建网络模型print(x.shape)  # (None, 56, 56, 24)model.summary()  # 输出SE模块的结构

查看SE模块的结构框架

Model: "model"
__________________________________________________________________________________________________
Layer (type)                    Output Shape         Param #     Connected to                     
==================================================================================================
input_1 (InputLayer)            [(None, 56, 56, 24)] 0                                            
__________________________________________________________________________________________________
global_average_pooling2d (Globa (None, 24)           0           input_1[0][0]                    
__________________________________________________________________________________________________
reshape (Reshape)               (None, 1, 1, 24)     0           global_average_pooling2d[0][0]   
__________________________________________________________________________________________________
dense (Dense)                   (None, 1, 1, 6)      150         reshape[0][0]                    
__________________________________________________________________________________________________
tf.nn.relu (TFOpLambda)         (None, 1, 1, 6)      0           dense[0][0]                      
__________________________________________________________________________________________________
dense_1 (Dense)                 (None, 1, 1, 24)     168         tf.nn.relu[0][0]                 
__________________________________________________________________________________________________
tf.math.sigmoid (TFOpLambda)    (None, 1, 1, 24)     0           dense_1[0][0]                    
__________________________________________________________________________________________________
multiply (Multiply)             (None, 56, 56, 24)   0           input_1[0][0]                    tf.math.sigmoid[0][0]            
==================================================================================================
Total params: 318
Trainable params: 318
Non-trainable params: 0
__________________________________________________________________________________________________

3. ECANet注意力机制

3.1 方法介绍

ECANet是通道注意力机制的一种实现形式,ECANet可以看做是SENet的改进版。

作者表明SENet中的降维会给通道注意力机制带来副作用,并且捕获所有通道之间的依存关系是效率不高的且是不必要的。

ECA注意力机制模块直接在全局平均池化层之后使用1x1卷积层,去除了全连接层。该模块避免了维度缩减,并有效捕获了跨通道交互。并且ECA只涉及少数参数就能达到很好的效果。

ECANet通过一维卷积 layers.Conv1D来完成跨通道间的信息交互,卷积核的大小通过一个函数来自适应变化,使得通道数较大的层可以更多地进行跨通道交互。自适应函数为:k = \left | \frac{log_{2}(C)}{\gamma } +\frac{b}{\gamma } \right |,其中\gamma =2, b=1


3.2 实现过程

(1)将输入特征图经过全局平均池化,特征图从[h,w,c]的矩阵变成[1,1,c]的向量

(2)计算得到自适应的一维卷积核大小kernel_size

(3)将kernel_size用于一维卷积中,得到对于特征图的每个通道的权重

(4)将归一化权重和原输入特征图逐通道相乘,生成加权后的特征图


3.3 代码实现

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import Model, layers
import mathdef eca_block(inputs, b=1, gama=2):# 输入特征图的通道数in_channel = inputs.shape[-1]# 根据公式计算自适应卷积核大小kernel_size = int(abs((math.log(in_channel, 2) + b) / gama))# 如果卷积核大小是偶数,就使用它if kernel_size % 2:kernel_size = kernel_size# 如果卷积核大小是奇数就变成偶数else:kernel_size = kernel_size + 1# [h,w,c]==>[None,c] 全局平均池化x = layers.GlobalAveragePooling2D()(inputs)# [None,c]==>[c,1]x = layers.Reshape(target_shape=(in_channel, 1))(x)# [c,1]==>[c,1]x = layers.Conv1D(filters=1, kernel_size=kernel_size, padding='same', use_bias=False)(x)# sigmoid激活x = tf.nn.sigmoid(x)# [c,1]==>[1,1,c]x = layers.Reshape((1,1,in_channel))(x)# 结果和输入相乘outputs = layers.multiply([inputs, x])return outputs# 验证ECA注意力机制
if __name__ == '__main__':# 构造输入层inputs = keras.Input(shape=[26,26,512])x = eca_block(inputs)  # 接收ECA输出结果model = Model(inputs, x)  # 构造模型model.summary()  # 查看网络架构

查看ECA模块,和SENet相比大大减少了参数量,参数量等于一维卷积的kernel_size的大小

Model: "model_1"
__________________________________________________________________________________________________
Layer (type)                    Output Shape         Param #     Connected to                     
==================================================================================================
input_2 (InputLayer)            [(None, 26, 26, 512) 0                                            
__________________________________________________________________________________________________
global_average_pooling2d_1 (Glo (None, 512)          0           input_2[0][0]                    
__________________________________________________________________________________________________
reshape_1 (Reshape)             (None, 512, 1)       0           global_average_pooling2d_1[0][0] 
__________________________________________________________________________________________________
conv1d (Conv1D)                 (None, 512, 1)       5           reshape_1[0][0]                  
__________________________________________________________________________________________________
tf.math.sigmoid_1 (TFOpLambda)  (None, 512, 1)       0           conv1d[0][0]                     
__________________________________________________________________________________________________
reshape_2 (Reshape)             (None, 1, 1, 512)    0           tf.math.sigmoid_1[0][0]          
__________________________________________________________________________________________________
multiply_1 (Multiply)           (None, 26, 26, 512)  0           input_2[0][0]                    reshape_2[0][0]                  
==================================================================================================
Total params: 5
Trainable params: 5
Non-trainable params: 0
__________________________________________________________________________________________________


http://chatgpt.dhexx.cn/article/cAklA4Be.shtml

相关文章

[ 注意力机制 ] 经典网络模型1——SENet 详解与复现

🤵 Author :Horizon Max ✨ 编程技巧篇:各种操作小结 🎇 机器视觉篇:会变魔术 OpenCV 💥 深度学习篇:简单入门 PyTorch 🏆 神经网络篇:经典网络模型 💻 …

算法 雪花算法 Python

Twitter 于 2010 年开源了内部团队在用的一款全局唯一 ID 生成算法 Snowflake,翻译过来叫做雪花算法。Snowflake 不借助数据库,可直接由编程语言生成,它通过巧妙的位设计使得 ID 能够满足递增属性,且生成的 ID 并不是依次连续的。…

聊聊雪花算法?

随便聊聊 哈喽,大家好,最近换了份工作,虽然后端技术栈是老了点,但是呢,这边的前端技术确是现在市面上最新的那一套技术:Vue3ViteTSXPinaElement-PlusNativeUI。我本人主要是学后端的,确被拉去做…

雪花算法生成实例

雪花算法生成实例 一、集群高并发情况下如何保证分布式唯一全局id生成?1.1 为什么需要分布式全局唯一ID以及分布式ID的业务需求1.2 ID生成规则部分硬性要求1.3 ID号生成系统的可用性要求 二、一般通用方案2.1 UUID2.2 数据库自增主键2.3 基于Redis生成全局id策略2.4…

算法 —— 雪花算法

文章目录 算法 —— 雪花算法简介实现原理结构图 算法 —— 雪花算法 简介 雪花算法是由 Twitter 公布的分布式主键生成算法,它能够保证不同进程主键的不重复性,以及相同进程主键的有序性。 实现原理 在同一个进程中,它首先是通过时间位保…

java雪花算法实现

基于雪花算法(Snowflake)模式雪花算法(Snowflake)是twitter公司内部分布式项目采用的ID生成算法,开源后广受国内大厂的好评,在该算法影响下各大公司相继开发出各具特色的分布式生成器。 Snowflake生成的是L…

雪花算法的实现原理

一位工作4年的小伙伴,去某东面试时被问到这样一道题,说请你简述一下雪花算法的实现原理。屏幕前的小伙伴,如果你遇到这个问题,你会怎么回答? 今天,我给大家分享一下我的理解。 1、什么是雪花算法 雪花算…

Python 实现雪花算法

Python 实现雪花算法 雪花算法:雪花算法是一种分布式全局唯一ID,一般不需要过多的深入了解,一般个人项目用不到分布式之类的大型架构,另一方面,则是因为,就算用到市面上很多 ID 生成器帮我们完成了这项工作…

雪花算法简介以及代码实现

文章目录 雪花算法分布式ID分布式ID需要满足的要求全局唯一:高性能:高可用:方便易用:安全:有序递增:要求具体的业务含义:独立部署: 组成代码实现Java代码实现Go语言实现 雪花算法 简介: 雪花算法是Twitter开源的分布式ID生成算法 Github仓库地址 雪花算法主要用于分布式系统中…

雪花算法(id生成算法)

SnowFlake 雪花算法 SnowFlake 中文意思为雪花,故称为雪花算法。最早是 Twitter 公司在其内部用于分布式环境下生成唯一 ID。在2014年开源 scala 语言版本。 实现原理 雪花算法原理就是生成一个的64位比特位的 long 类型的唯一 id。 最高1位固定值0&#xff0c…

什么是雪花算法?啥原理?

1、SnowFlake核心思想 SnowFlake 算法,是 Twitter 开源的分布式 ID 生成算法。 其核心思想就是:使用一个 64 bit 的 long 型的数字作为全局唯一 ID。在分布式系统中的应用十分广泛,且 ID 引入了时间戳,基本上保持自增的&#xf…

雪花算法-java

前言: 👏作者简介:我是笑霸final,一名热爱技术的在校学生。 📝个人主页:个人主页1 || 笑霸final的主页2 📕系列专栏:计算机基础专栏 📧如果文章知识点有错误的地方&#…

数据库中雪花算法是什么?

一、为何要用雪花算法 1、问题产生的背景 现如今越来越多的公司都在用分布式、微服务,那么对应的就会针对不同的服务进行数据库拆分,然后当数据量上来的时候也会进行分表,那么随之而来的就是分表以后id的问题。 例如之前单体项目中一个表中…

snowflake算法(雪花算法)

snowflake算法(雪花算法) 1.snowflake算法介绍 Snowflake算法产生是为了满足Twitter每秒上万条消息的请求,每条消息都必须分配一条唯一的id,这些id还需要一些大致的顺序(方便客户端排序),并且在分布式系统中不同机器…

分布式ID生成算法——雪花算法

一、分布式ID ID可以唯一标识一条记录。 对于单体架构,我们可以使用自增ID来保证ID的唯一性。但是,在分布式系统中,简单的使用自增ID就会导致ID冲突。这也就引出了分布式ID问题。分布式ID也要求满足分布式系统的高性能、高可用、高并发的特点…

【SnowFlake】雪花算法(Java版本)

SnowFlake雪花算法(Java版本) 一、SnowFlake算法二、代码实现三、应用场景四、优缺点五、分布式生成ID方式 一、SnowFlake算法 雪花算法(Snowflake)是twitter公司内部分布式项目采用的ID生成算法 Snowflake生成的是Long类型的ID,一个Long类型…

雪花算法以及具体实现

一、为何要用雪花算法 1、问题产生的背景 现如今越来越多的公司都在用分布式、微服务,那么对应的就会针对不同的服务进行数据库拆分,然后当数据量上来的时候也会进行分表,那么随之而来的就是分表以后id的问题。 例如之前单体项目中一个表中的…

什么是雪花算法,详解雪花算法原理

雪花算法(SnowFlake) 雪花算法是Twitter开源的分布式ID生成算法. 主要是由64bit的long型生成的全局ID,引入了时间戳和ID保持自增的属性. 64bit分为四个部分: 第一个部分是1bit, 这不 使用,没有意义; 第二个部分是41bit, 组成时间戳; 第三个部分是10bit, 工作机器ID,里面分为…

雪花算法原理及实现

背景 分布式高并发的环境下,最常见的就是每年双十一的十二点,大量用户同时抢购同一商品,毫秒级的时间下可能生成数万个订单,此时确保生成订单ID的唯一性变得至关重要。此外,在秒杀环境下,不仅要保障ID唯一…

雪花算法的原理和实现Java

SnowFlake 算法,是 Twitter 开源的分布式 id 生成算法。其核心思想就是:使用一个 64 bit 的 long 型的数字作为全局唯一 id。在分布式系统中的应用十分广泛,且ID 引入了时间戳,基本上保持自增的,后面的代码中有详细的注…