SENet论文解读及代码实例

SENet是一种引入通道注意力机制的卷积神经网络,通过全局信息聚合和通道权重学习,提升特征图中有效信息的比例,从而提高模型识别精度。SEBlock模块包含Squeeze和Excitation两部分,分别用于获取全局信息和计算通道权重。尽管参数量和计算量有所增加,但在Resnet50基础上实现的SE-Resnet50能显著提升准确率,但可能对计算资源有限的设备不友好。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

SENet论文解读及代码实例

在SENet前的网络是针对卷积空间来做处理的,而SENet考虑了卷积通道上的的权重(明确地建模网络卷积特征通道之间的相互依赖关系),通过训练每个通道权重来抑制无用特征,提高有用特征在分类网络的占比,从而实现识别精度的提升。SENet引入权重计算网络,识别精度提升的代价为参数量提高10%左右,计算量提高1%左右。
1)SE block模块原理
下图为SE模块的建立过程 ,在SE模块中,生成的特征图经聚合层(Squeeze)生成每一个通道的全局信息,公式如下:
在这里插入图片描述

全局信息通过激励层生成每个通道的权值大小,每个通道再与权值相乘便可得到带权值的特征图。
在这里插入图片描述

在SE模块实际使用中,Squeeze层通过全局平均池化将全局空间信息压缩压缩到全局信息中, 可由下式表示:
在这里插入图片描述

为了利用全局信息来学习通道之间的权重,我们必须通过激励(Excitatio
-n)来获取权重,其中激励必须满足两个条件:必须能够学习通道之间的非线性相互作用;必须学习一种非互斥的关系,因为我们想要确保多个通道被允许被强调。 可用以下公式表示:
在这里插入图片描述

该结构由两个全连接层构成,这两个全连接层的作用就是融合各通道的feature map信息,第一个全连接层起降维作用,降维比取到16,激活函数采用Relu,第二个全连接将维数增加至原始输入维数,激活函数为sigmoid函数。
最后将 和 相乘得到权重
2)SE block模块使用
SE block可以嵌入各种卷积网络中使用,如Resnet、ResNeXt、Xception等,下图为SE block嵌入Resnet50的模块图:
在这里插入图片描述

SE-Resnet50在性能的变化:运算量从3.86GFlop增加至3.87GFlop,准确率从Resnet50提升至Resnet101的水平。模型结构如下:
在这里插入图片描述
代码实现SE-Resnet50

import torch.nn as nn
import torch.nn.functional as F
import math
from torchsummary import summary

class SElayer(nn.Module):
    def __init__(self,ratio,input_aisle):
        super(SElayer, self).__init__()
        self.pool=nn.AdaptiveAvgPool2d(1)
        self.fc=nn.Sequential(
            nn.Linear(input_aisle,input_aisle//ratio,bias=False),
            nn.ReLU(inplace=True),
            nn.Linear(input_aisle//ratio,input_aisle,bias=False),
            nn.Sigmoid()
                             )
    def forward(self,x):
        a, b, _, _ = x.size()
        #读取批数据图片数量及通道数
        y = self.pool(x).view(a, b)
        #经池化后输出a*b的矩阵
        y = self.fc(y).view(a, b, 1, 1)
        #经全连接层输出【啊,吧,1,1】矩阵
        return x * y.expand_as(x)
        #输出权重乘以特征图
class SE_block(nn.Module):
    def __init__(self, outchannels, ratio=16, ):
        super(SE_block, self).__init__()
        self.backbone=nn.Sequential(nn.Conv2d(in_channels=outchannels, out_channels=outchannels // 4, kernel_size=1, stride=1),
                                    nn.BatchNorm2d(outchannels // 4),
                                    nn.ReLU(inplace=True),
                                    nn.Conv2d(in_channels=outchannels // 4, out_channels=outchannels // 4, kernel_size=3, stride=1, padding=1),
                                    nn.BatchNorm2d(outchannels // 4),
                                    nn.ReLU(inplace=True),
                                    nn.Conv2d(in_channels=outchannels // 4, out_channels=outchannels, kernel_size=1, stride=1),
                                    nn.BatchNorm2d(outchannels),
                                    nn.ReLU(inplace=True),
                                    )
        self.Selayer=SElayer(ratio, outchannels)
    def forward(self,x):
        residual=x
        x=self.backbone(x)
        x=self.Selayer(x)
        x=x+residual
        return x
class SE_Idenitity(nn.Module):
    def __init__(self, inchannel,outchannels, stride=2,ratio=16,):
        super(SE_Idenitity, self).__init__()
        self.backbone = nn.Sequential(
                                nn.Conv2d(in_channels=inchannel, out_channels=outchannels//4, kernel_size=1, stride=1),
                                nn.BatchNorm2d(outchannels//4),
                                nn.ReLU(inplace=True),
                                nn.Conv2d(in_channels=outchannels//4, out_channels=outchannels//4, kernel_size=3, stride=stride, padding=1),
                                nn.BatchNorm2d(outchannels//4),
                                nn.ReLU(inplace=True),
                                nn.Conv2d(in_channels=outchannels//4, out_channels=outchannels, kernel_size=1, stride=1),
                                nn.BatchNorm2d(outchannels),
                                nn.ReLU(inplace=True),
                                    )
        self.residual=nn.Conv2d(in_channels=inchannel, out_channels=outchannels, kernel_size=1, stride=stride)
        self.selayer = SElayer(ratio, outchannels)
    def forward(self,x):
        residual=self.residual(x)
        x=self.backbone(x)
        x=self.selayer(x)
        x=x+residual
        return x
class SE_Resnet(nn.Module):
    def __init__(self,layer_num,outchannel_num,num_class=10):
        super(SE_Resnet, self).__init__()
        self.initial=nn.Sequential(nn.Conv2d(in_channels=3,out_channels=64,kernel_size=7,padding=3,stride=2),
                                   nn.BatchNorm2d(64),
                                   nn.ReLU(inplace=True),
                                   nn.MaxPool2d(kernel_size=3,padding=1,stride=2))
        self.residual=self.make_layer(layer_num,outchannel_num)
        self.pool=nn.AdaptiveAvgPool2d(1)
        self.fc=nn.Linear(2048,num_class)
    def make_layer(self,number_layer,number_outchannel):
        layer_list=[]

        for i in range(len(number_layer)):
            if i==0:
                stride=1
            else:
                stride=2
            layer_list.append(SE_Idenitity(number_outchannel[i],number_outchannel[i+1],stride))
            for j in range(number_layer[i]-1):
                layer_list.append(SE_block(number_outchannel[i+1]))
        return nn.Sequential(*layer_list)
    def forward(self,x):
        x=self.initial(x)
        x=self.residual(x)
        x=self.pool(x)
        x = x.view(x.size(0), -1)
        x = self.fc(x)
        return F.softmax(x,dim=1)

def SE_Resnet50(num_class):
    model=SE_Resnet([3,4,6,3],[64,256,512,1024,2048],num_class)
    return model
def SE_Resnet101(num_class):
    model=SE_Resnet([3,4,23,3],[64,256,512,1024,2048],num_class)
    return model
def SE_Resnet151(num_class):
    model = SE_Resnet([3, 8, 36, 3], [64,256, 512, 1024, 2048], num_class)
    return model
device=torch.device("cuda:0")
Xception=SE_Resnet50(10).to(device)
summary(Xception,(3,224,224))

Resnet50与SE-Resnet50模型参数及计算量对比如下图:
1)SE-Resnet50参数:
SE-Resnet50
2)Resnet50参数:
在这里插入图片描述
整体大小由328M提高至417M,感觉比论文中说的10%要大上不少,运行时间上跑了一个epoch在我这吃土的MX150上多接近40%的时间。
总结:电脑差的话,SE-Resnet整体性价比不高!!!

SENet(Squeeze-and-Excitation Networks)的概念首次被提出是在2017年的论文《Squeeze-and-Excitation Networks》中,这篇论文由Jie Hu等人撰写,并在CVPR 2018上发表[^1]。该论文详细介绍了如何通过引入通道注意力机制来改进卷积神经网络的表现。 以下是获取SENet相关论文的一些方法: ### 方法一:访问官方出版平台 论文最初发布于IEEE Xplore Digital Library以及arXiv预印本服务器。可以通过以下链接下载: - IEEE Xplore: [https://ieeexplore.ieee.org/document/8578913](https://ieeexplore.ieee.org/document/8578913) - arXiv: [https://arxiv.org/abs/1709.01507](https://arxiv.org/abs/1709.01507)[^2] ### 方法二:学术搜索引擎 除了直接从上述网站下载外,还可以利用Google Scholar等学术搜索引擎输入关键词"Squeeze-and-Excitation Networks Jie Hu"进行检索。这通常会提供多种免费或付费的下载选项[^3]。 ### PyTorch 实现示例 对于希望快速验证SENet效果的研究者来说,下面是一个简单的SE Block实现代码片段供参考: ```python import torch.nn as nn 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) ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

炼丹代师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值