IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 人工智能 -> SENET与 CBAM 注意力机制 -> 正文阅读

[人工智能]SENET与 CBAM 注意力机制

原文链接:https://blog.csdn.net/xu380393916/article/details/109304082

一、SENET
SENET是2017年的世界冠军,SE全称Squeeze-and-Excitation是一个模块,将现有的网络嵌入SE模块的话,那么该网络就是SENet,它几乎可以嵌入当前流行的任何网络,那么为什么会引出这个东西呢,来看下图:

在这里插入图片描述

SE结构
一个SEblock的过程分为 Squeeze(压缩) 和 Excitation(激发) 两个步骤:
Squeeze(压缩) 通过在Feature Map层上执行Global Average Pooling,得到当前Feature Map的全局压缩特征量;
Excitation(激发) 通过两层全连接的bottleneck结构得到Feature Map中每个通道的权值,并将加权后的Feature Map作为下一层网络的输入
一个feature map经过一系列卷积池化得到的feature map,通常我们认为这个得到的feature map的每个通道都是同样重要的,我们并没有分那个通道重要,那个通道不怎么重要,那么实际上会不会有这种情况呢,就是得到的featurmap 的每个通道的重要性都不一样,比如确实有的图片三个通道,我只有一个通道有用,其它通道没什么用,比如一张包含动物的图片,那么背景肯定不怎么重要,它转化为灰度图后,只有一个通道比较重要,其它的不怎么重要,那么其实实际上我们得到的feature map每个通道的重要程度还是不一样的,也就是说每个通道其实还应该有一个重要性权值才行,然后每个通道的重要性权值乘以每个通道原来的值,就是我们求的真正feature map,这个feature map不同的通道重要性不一样(可能权值大的乘以原来的数要大些)如上面得到最终图,每个通道颜色不一样,也就代表着不同的重要性
那么每个通道的权值或者重要性怎么来呢,就是上面这块,做法如下:
在这里插入图片描述

其实很简单,假入原来feature map 是 h * w * c的,给它做一个global池化(池化窗口就是h * w 得到的就是1 * 1窗口,通道数不变)得到 1 * 1 * c的feature map,然后它再接两个全连接层(第一个全连接层神经元个数是c/16,相当于对c进行了降维,输入是c个特征,第二个全连接层神经元个数为c,相当于又增维回到了c个特征,这样做比直接用一个 Fully Connected 层的好处在于具有更多的非线性,可以更好地拟合通道间复杂的相关性,极大地减少了参数量和计算量),然后再接一个sigmod层(这里采用sigmod应该是通道之间是具有相关性的,所以不能用softmax,softmax的话,最终加起来必须为1),输出1 * 1 * c,
原来的feature map维度h * w * c,得到的是通道的权值维度1 * 1 * c,然后它们进行相乘,这里是全乘,不是矩阵相乘,然后得到的feature map对应的每个通道重要性就不一样了(可能更重要的它的值要大些)
那么h * w * c 和1 * 1 * c 是怎么相乘的呢?看一个例子
在这里插入图片描述

像这样相乘

这个是一个通道的,看到没它乘的倍率是1,

这个通道的倍率是乘2

它就是这么实现不同通道乘以不同的倍率,从而得到不同的重要性的
相当于是每次乘以一列通道

注意:对于图片数组,我们来熟悉下:
在这里插入图片描述

一行

一行里面一个像素点对应的一列通道

一个像素
注意:
每个通道的权值都是网路学习出来的,那么怎么学习呢,记住学习,只要有参数和loss就可以,如下
在这里插入图片描述

所以它主要学习的是SE模块这两个全连接层的参数,它们学到了,自然最终结果就有了,所用用最终的分类损失去更新这两个全连接层的参数就可以了
#注意上面采用两层全连接,第一层全连接单元个数为c/16,主要是为了压缩参数的如下,
c. * c/r + c/r * c = 2c2/r = 2/r * c2
只用一层全连接参数的话参数个数:
c * c = c * 2

实际中可能会用一个比例reduction_ratio
reduction_ratio = 0.5的话
那么就是c * 0.5c + 0.5c * c = c2
reduction_ratio如果 = 0.3的话那么就是c * 0.3c + 0.3c * c = 0.6c2
如果只用一层全连接层的话那么就是
c * c = c * 2个,显然参数减少了0.4c2个

SE的代码实现
SE模块实现的 Pytorch版:

class SELayer(nn.Module):
    def __init__(self, channel, reduction=16):
        super(SELayer, self).__init__()
        self.avg_pool = nn.AdaptiveAvgPool2d(1)
        self.fc = nn.Sequential(
            nn.Linear(channel, channel // reduction, bias=False),
            nn.ReLU(inplace=True),
            nn.Linear(channel // reduction, channel, bias=False),
            nn.Sigmoid()
        )

    def forward(self, x):
        b, c, _, _ = x.size()
        y = self.avg_pool(x).view(b, c)
        y = self.fc(y).view(b, c, 1, 1)
        return x * y.expand_as(x)
12345678910111213141516

SE-ResNet模型的 Pytorch版:

class SEBottleneck(nn.Module):
        expansion = 4

        def __init__(self, inplanes, planes, stride=1, downsample=None, reduction=16):
            super(SEBottleneck, self).__init__()
            self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False)
            self.bn1 = nn.BatchNorm2d(planes)
            self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride,
                                   padding=1, bias=False)
            self.bn2 = nn.BatchNorm2d(planes)
            self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False)
            self.bn3 = nn.BatchNorm2d(planes * 4)
            self.relu = nn.ReLU(inplace=True)
            self.se = SELayer(planes * 4, reduction)
            self.downsample = downsample
            self.stride = stride

        def forward(self, x):
            residual = x

            out = self.conv1(x)
            out = self.bn1(out)
            out = self.relu(out)

            out = self.conv2(out)
            out = self.bn2(out)
            out = self.relu(out)

            out = self.conv3(out)
            out = self.bn3(out)
            out = self.se(out)

            if self.downsample is not None:
                residual = self.downsample(x)

            out += residual
            out = self.relu(out)

            return out
123456789101112131415161718192021222324252627282930313233343536373839

二、CBAM模块
CBAM
它是2018年的分类冠军,它和SE一样也是一个模型,现在任何流行网络都可以嵌入这个模块,那么它的由来是什么呢?
SE的由来是因为不同通道的像素的重要性可能不一样,那么既然这样,同一个通道的不同位置像素重要性也可能不一样,所以就有了CBAM,既考虑不同通道像素的重要性,又考虑了同一通道不同位置像素的重要性

组成
该注意力模块( CBAM ),可以在通道和空间维度上进行 Attention 。其包含两个子模块 Channel Attention Module(CAM) 和 Spartial Attention Module(SAM)
Convolutional Block Attention Module (CBAM) 表示卷积模块的注意力机制模块。是一种结合了空间(spatial)和通道(channel)的注意力机制模块。相比于senet只关注通道(channel)的注意力机制可以取得更好的效果。
它相对于SE多了一个空间attension,这个空间其实就是宽高对应的方形或者说是一个通道对应的feature map,SE只关注通道,它既关注通道,也关注宽高,
它的大概流程如下:
**在这里插入图片描述**

CAM的结构与SE有何区别?
其结构如上图所示,相比SE,只是多了一个并行的Max Pooling层。那为什么加个并行的呢?结果导向,作者通过实验说明这样的效果好一些,我感觉其好一些的原因应该是多一种信息编码方式,使得获得的信息更加全面了吧,可能再加一些其他并行操作效果会更好?
先来看通道上的attension:
在这里插入图片描述

它这里和SE模块有点区别就是,SE只用了一个池化globalpool(一般是maxpool),而它这里用了两个池化maxpool,avgpool(池化本身是提取高层次特征,不同的池化意味着提取的高层次特征更加丰富),既然是两个那么输出肯定也是两个都是11c,然后将输出两个相加,再进行sigmod,结果也是11c,然后再和原来的feature map相乘
然后再来看空间上的attension

它的过程也很简单:
它是原来的feature map先做完通道attension ,然后它在这个基础之上进一步做空间attension的,它先将feature map 进行基于通道的池化,一般的池化都是在长宽的维度,它这个其实就是在列通道的维度池化(取一列通道的最大值,平均值),这就意味着一次池化一列通道变成了一个值就是一个通道了,长宽不变,假如输入feature map是 h * w * c,它这个一次池化后就变成了h * w * 1的feature map了,它进行了两次池化,那就是两个h * w * 1的feature map,然后将它们进行基于通道的拼接,那就变成了 h * w * 2的feature map了,然后对这个feature map用一个7*7的卷积核进行卷积,将通道数压缩成了1(因为只用了一个卷积核),得到一个新的feature map,然后对这个feature map进行sigmod的,得到一个attension feature map,然后将它和最开始的feature map进行相乘,比如下面:
原先的经过channel attension后的feature map是 h * w * c,那么经过空间attension后,它得到的attension feature map就是 h * w * 1了,那么它们怎么相乘呢,如下:
b.shape = (2,2,3)

在这里插入图片描述

这一列就是一个通道上的像素,显然,他每次乘都是乘以一个通道上的像素,对一个通道上的像素进行增大或者缩小,下一次再乘以另外一个通道的

最终它这个空间attension feature map肯定也是学习出来的,那么具体是学习什么呢?肯定是参数,它这个过程有哪些参数,其实就是再到数第二步,用一个77的卷积核对输入的2层feature map卷积,那么它学习的就是这个77的卷积核参数

代码实现

def cbam_module(inputs,reduction_ratio=0.5,name=""):
    with tf.variable_scope("cbam_"+name, reuse=tf.AUTO_REUSE):
        #假如输入是[batsize,h,w,channel],
        #channel attension 因为要得到batsize * 1 * 1 * channel,它的全连接层第一层
      #隐藏层单元个数是channel / r, 第二层是channel,所以这里把channel赋值给hidden_num
        batch_size,hidden_num=inputs.get_shape().as_list()[0],inputs.get_shape().as_list()[3]
 
       #通道attension
        #全局最大池化,窗口大小为h * w,所以对于这个数据[batsize,h,w,channel],他其实是求每个h * w面积的最大值
      #这里实现是先对h这个维度求最大值,然后对w这个维度求最大值,平均池化也一样
        maxpool_channel=tf.reduce_max(tf.reduce_max(inputs,axis=1,keepdims=True),axis=2,keepdims=True)
        avgpool_channel=tf.reduce_mean(tf.reduce_mean(inputs,axis=1,keepdims=True),axis=2,keepdims=True)
        
        #上面全局池化结果为batsize * 1 * 1 * channel,它这个拉平输入到全连接层
      #这个拉平,它会保留batsize,所以结果是[batsize,channel]
        maxpool_channel = tf.layers.Flatten()(maxpool_channel)
        avgpool_channel = tf.layers.Flatten()(avgpool_channel)
        
        #将上面拉平后结果输入到全连接层,第一个全连接层hiddensize = channel/r = channel * reduction_ratio,
      #第二哥全连接层hiddensize = channel
        mlp_1_max=tf.layers.dense(inputs=maxpool_channel,units=int(hidden_num*reduction_ratio),name="mlp_1",reuse=None,activation=tf.nn.relu)
        mlp_2_max=tf.layers.dense(inputs=mlp_1_max,units=hidden_num,name="mlp_2",reuse=None)
        #全连接层输出结果为[batsize,channel],这里又降它转回到原来维度batsize * 1 * 1 * channel,
       mlp_2_max=tf.reshape(mlp_2_max,[batch_size,1,1,hidden_num])
 
        mlp_1_avg=tf.layers.dense(inputs=avgpool_channel,units=int(hidden_num*reduction_ratio),name="mlp_1",reuse=True,activation=tf.nn.relu)
        mlp_2_avg=tf.layers.dense(inputs=mlp_1_avg,units=hidden_num,name="mlp_2",reuse=True)
        mlp_2_avg=tf.reshape(mlp_2_avg,[batch_size,1,1,hidden_num])
 
        #将平均和最大池化的结果维度都是[batch_size,1,1,channel]相加,然后进行sigmod,维度不变
        channel_attention=tf.nn.sigmoid(mlp_2_max+mlp_2_avg)
         #和最开始的inputs相乘,相当于[batch_size,1,1,channel] * [batch_size,h,w,channel]
       #只有维度一样才能相乘,这里相乘相当于给每个通道作用了不同的权重
        channel_refined_feature=inputs*channel_attention
 
       
        #空间attension
        #上面得到的结果维度依然是[batch_size,h,w,channel],
      #下面要进行全局通道池化,其实就是一条通道里面那个通道的值最大,其实就是对channel这个维度求最大值
      #每个通道池化相当于将通道压缩到了1维,有两个池化,结果为两个[batch_size,h,w,1]feature map
        maxpool_spatial=tf.reduce_max(inputs,axis=3,keepdims=True)
        avgpool_spatial=tf.reduce_mean(inputs,axis=3,keepdims=True)

        #将两个[batch_size,h,w,1]的feature map进行通道合并得到[batch_size,h,w,2]的feature map
        max_avg_pool_spatial=tf.concat([maxpool_spatial,avgpool_spatial],axis=3)

       #然后对上面的feature map用1个7*7的卷积核进行卷积得到[batch_size,h,w,1]的feature map,因为是用一个卷积核卷的
     #所以将2个输入通道压缩到了1个输出通道
        conv_layer=tf.layers.conv2d(inputs=max_avg_pool_spatial, filters=1, kernel_size=(7, 7), padding="same", activation=None)
      #然后再对上面得到的[batch_size,h,w,1]feature map进行sigmod,这里为什么要用一个卷积核压缩到1个通道,相当于只得到了一个面积的值
     #然后进行sigmod,因为我们要求的就是feature map面积上不同位置像素的中重要性,所以它压缩到了一个通道,然后求sigmod
        spatial_attention=tf.nn.sigmoid(conv_layer)
 
       #上面得到了空间attension feature map [batch_size,h,w,1],然后再用这个和经过空间attension作用的结果相乘得到最终的结果
     #这个结果就是经过通道和空间attension共同作用的结果
        refined_feature=channel_refined_feature*spatial_attention
 
    return refined_feature

参考文献
CV领域常用的注意力机制模块(SE、CBAM)_weixin_42907473的博客-CSDN博客
https://blog.csdn.net/weixin_42907473/article/details/106525668
深度学习之卷积网络attention机制SENET、CBAM模块原理总结_weixin_33602281的博客-CSDN博客
https://blog.csdn.net/weixin_33602281/article/details/85223216
————————————————
版权声明:本文为CSDN博主「JY丫丫」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xu380393916/article/details/109304082

  人工智能 最新文章
2022吴恩达机器学习课程——第二课(神经网
第十五章 规则学习
FixMatch: Simplifying Semi-Supervised Le
数据挖掘Java——Kmeans算法的实现
大脑皮层的分割方法
【翻译】GPT-3是如何工作的
论文笔记:TEACHTEXT: CrossModal Generaliz
python从零学(六)
详解Python 3.x 导入(import)
【答读者问27】backtrader不支持最新版本的
上一篇文章      下一篇文章      查看所有文章
加:2021-08-05 17:21:26  更:2021-08-05 17:22:01 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/8 8:26:39-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码