SimAM:无参数Attention!助力分类/检测/分割涨点!


本项目基于中山大学提出的无参数SimAM注意力机制,在Caltech101的16类子集上验证其效果。SimAM从神经科学出发,通过能量函数挖掘神经元重要性,生成三维权重,优于传统一维、二维注意力。项目构建含SimAM的TowerNet模型,与ResNet50等经典网络对比,经数据准备、模型训练后,显示加入SimAM后性能和鲁棒性显著提升,验证了其有效性。

☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜

① 项目背景

  • 本文是中山大学在注意力机制方面的尝试,从神经科学理论出发,构建了一种能量函数挖掘神经元重要性,并对此推导出了解析解以加速计算。通过ImageNet分类、COCO检测与分割等任务验证了所提SimAM的灵活性与有效性。值得一提的是,所提SimAM是一种无参数注意力模块。

  • 现有的注意力模块通常被继承到每个块中,以改进来自先前层的输出。这种细化步骤通常沿着通道维度(a)或空间维度(b)操作,这些方法生成一维或二维权重,并平等对待每个通道或空间位置中的神经元,通道注意力:1D注意力,它对不同通道区别对待,对所有位置同等对待;空域注意力:2D注意力,它对不同位置区别对待,对所有通道同等对待。这可能会限制他们学习更多辨别线索的能力。因此三维权重©优于传统的一维和二维权重注意力。

  • 原文中作者推导了一个能量函数,并发现了每个神经元的重要性。根据(Hillyard et al.,1998),哺乳动物大脑中的注意调节通常表现为神经元反应的增益(即缩放)效应,因此作者使用缩放运算符而不是加法来细化特征,项目后边有介绍函数推导过程。

论文地址:http://proceedings.mlr.press/v139/yang21o.html

② 数据准备

2.1 解压缩数据集

我们将网上获取的数据集以压缩包的方式上传到aistudio数据集中,并加载到我们的项目内。

在使用之前我们进行数据集压缩包的一个解压。

In [1]
!unzip -oq /home/aistudio/data/data69664/Images.zip -d work/dataset
In [1]
import paddleimport numpy as npfrom typing import Callable#参数配置config_parameters = {    "class_dim": 16,  #分类数
    "target_path":"/home/aistudio/work/",                     
    'train_image_dir': '/home/aistudio/work/trainImages',    'eval_image_dir': '/home/aistudio/work/evalImages',    'epochs':100,    'batch_size': 32,    'lr': 0.01}

2.2 划分数据集

接下来我们使用标注好的文件进行数据集类的定义,方便后续模型训练使用。

In [3]
import osimport shutil

train_dir = config_parameters['train_image_dir']
eval_dir = config_parameters['eval_image_dir']
paths = os.listdir('work/dataset/Images')if not os.path.exists(train_dir):
    os.mkdir(train_dir)if not os.path.exists(eval_dir):
    os.mkdir(eval_dir)for path in paths:
    imgs_dir = os.listdir(os.path.join('work/dataset/Images', path))
    target_train_dir = os.path.join(train_dir,path)
    target_eval_dir = os.path.join(eval_dir,path)    if not os.path.exists(target_train_dir):
        os.mkdir(target_train_dir)    if not os.path.exists(target_eval_dir):
        os.mkdir(target_eval_dir)    for i in range(len(imgs_dir)):        if ' ' in imgs_dir[i]:
            new_name = imgs_dir[i].replace(' ', '_')        else:
            new_name = imgs_dir[i]
        target_train_path = os.path.join(target_train_dir, new_name)
        target_eval_path = os.path.join(target_eval_dir, new_name)     
        if i % 5 == 0:
            shutil.copyfile(os.path.join(os.path.join('work/dataset/Images', path), imgs_dir[i]), target_eval_path)        else:
            shutil.copyfile(os.path.join(os.path.join('work/dataset/Images', path), imgs_dir[i]), target_train_path)print('finished train val split!')

2.3 数据集定义与数据集展示

2.3.1 数据集展示

我们先看一下解压缩后的数据集长成什么样子,对比分析经典模型在Caltech101抽取16类mini版数据集上的效果

    1. 该数据集的源为Caltech101,本次实验共抽取16类来进行比较。
    1. 该数据集一共有验证集1440=4532张图片,验证集384=1232张照片。
    1. 该数据集主要有以下集中类型的图片,在下方展示。
In [3]
import osimport randomfrom matplotlib import pyplot as pltfrom PIL import Image

imgs = []
paths = os.listdir('work/dataset/Images')for path in paths:   
    img_path = os.path.join('work/dataset/Images', path)    if os.path.isdir(img_path):
        img_paths = os.listdir(img_path)
        img = Image.open(os.path.join(img_path, random.choice(img_paths)))
        imgs.append((img, path))

f, ax = plt.subplots(4, 4, figsize=(12,12))for i, img in enumerate(imgs[:16]):
    ax[i//4, i%4].imshow(img[0])
    ax[i//4, i%4].axis('off')
    ax[i//4, i%4].set_title('label: %s' % img[1])
plt.show()

2.3.2 导入数据集的定义实现

In [2]
#数据集的定义class Dataset(paddle.io.Dataset):
    """
    步骤一:继承paddle.io.Dataset类
    """
    def __init__(self, transforms: Callable, mode: str ='train'):
        """
        步骤二:实现构造函数,定义数据读取方式
        """
        super(Dataset, self).__init__()
        
        self.mode = mode
        self.transforms = transforms

        train_image_dir = config_parameters['train_image_dir']
        eval_image_dir = config_parameters['eval_image_dir']

        train_data_folder = paddle.vision.DatasetFolder(train_image_dir)
        eval_data_folder = paddle.vision.DatasetFolder(eval_image_dir)        
        if self.mode  == 'train':
            self.data = train_data_folder        elif self.mode  == 'eval':
            self.data = eval_data_folder    def __getitem__(self, index):
        """
        步骤三:实现__getitem__方法,定义指定index时如何获取数据,并返回单条数据(训练数据,对应的标签)
        """
        data = np.array(self.data[index][0]).astype('float32')

        data = self.transforms(data)

        label = np.array([self.data[index][1]]).astype('int64')        
        return data, label        
    def __len__(self):
        """
        步骤四:实现__len__方法,返回数据集总数目
        """
        return len(self.data)
In [3]
from paddle.vision import transforms as T#数据增强transform_train =T.Compose([T.Resize((256,256)),                            #T.RandomVerticalFlip(10),
                            #T.RandomHorizontalFlip(10),
                            T.RandomRotation(10),
                            T.Transpose(),
                            T.Normalize(mean=[0, 0, 0],                           # 像素值归一化
                                        std =[255, 255, 255]),                    # transforms.ToTensor(), # transpose操作 + (img / 255),并且数据结构变为PaddleTensor
                            T.Normalize(mean=[0.50950350, 0.54632660, 0.57409690],# 减均值 除标准差    
                                        std= [0.26059777, 0.26041326, 0.29220656])# 计算过程:output[channel] = (input[channel] - mean[channel]) / std[channel]
                            ])
transform_eval =T.Compose([ T.Resize((256,256)),
                            T.Transpose(),
                            T.Normalize(mean=[0, 0, 0],                           # 像素值归一化
                                        std =[255, 255, 255]),                    # transforms.ToTensor(), # transpose操作 + (img / 255),并且数据结构变为PaddleTensor
                            T.Normalize(mean=[0.50950350, 0.54632660, 0.57409690],# 减均值 除标准差    
                                        std= [0.26059777, 0.26041326, 0.29220656])# 计算过程:output[channel] = (input[channel] - mean[channel]) / std[channel]
                            ])

2.3.3 实例化数据集类

根据所使用的数据集需求实例化数据集类,并查看总样本量。

In [4]
train_dataset =Dataset(mode='train',transforms=transform_train)
eval_dataset  =Dataset(mode='eval', transforms=transform_eval )#数据异步加载train_loader = paddle.io.DataLoader(train_dataset, 
                                    places=paddle.CUDAPlace(0), 
                                    batch_size=32, 
                                    shuffle=True,                                    #num_workers=2,
                                    #use_shared_memory=True
                                    )
eval_loader = paddle.io.DataLoader (eval_dataset, 
                                    places=paddle.CUDAPlace(0), 
                                    batch_size=32,                                    #num_workers=2,
                                    #use_shared_memory=True
                                    )print('训练集样本量: {},验证集样本量: {}'.format(len(train_loader), len(eval_loader)))
训练集样本量: 45,验证集样本量: 12

③ 模型选择和开发

3.1 对比网络构建

本次我们选取了经典的卷积神经网络resnet50,vgg19,mobilenet_v2来进行实验比较。

In [ ]
network = paddle.vision.models.vgg19(num_classes=16)#模型封装model = paddle.Model(network)#模型可视化model.summary((-1, 3,256 , 256))
In [ ]
network = paddle.vision.models.resnet50(num_classes=16)#模型封装model2 = paddle.Model(network)#模型可视化model2.summary((-1, 3,256 , 256))

3.2 对比网络训练

In [ ]
#优化器选择class SaveBestModel(paddle.callbacks.Callback):
    def __init__(self, target=0.5, path='work/best_model', verbose=0):
        self.target = target
        self.epoch = None
        self.path = path    def on_epoch_end(self, epoch, logs=None):
        self.epoch = epoch    def on_eval_end(self, logs=None):
        if logs.get('acc') > self.target:
            self.target = logs.get('acc')
            self.model.save(self.path)            print('best acc is {} at epoch {}'.format(self.target, self.epoch))

callback_visualdl = paddle.callbacks.VisualDL(log_dir='work/vgg19')
callback_savebestmodel = SaveBestModel(target=0.5, path='work/best_model')
callbacks = [callback_visualdl, callback_savebestmodel]

base_lr = config_parameters['lr']
epochs = config_parameters['epochs']def make_optimizer(parameters=None):
    momentum = 0.9

    learning_rate= paddle.optimizer.lr.CosineAnnealingDecay(learning_rate=base_lr, T_max=epochs, verbose=False)
    weight_decay=paddle.regularizer.L2Decay(0.0001)
    optimizer = paddle.optimizer.Momentum(
        learning_rate=learning_rate,
        momentum=momentum,
        weight_decay=weight_decay,
        parameters=parameters)    return optimizer

optimizer = make_optimizer(model.parameters())

model.prepare(optimizer,
              paddle.nn.CrossEntropyLoss(),
              paddle.metric.Accuracy())

model.fit(train_loader,
          eval_loader,
          epochs=100,
          batch_size=1,           # 是否打乱样本集     
          callbacks=callbacks, 
          verbose=1)   # 日志展示格式

3.3 Simam注意力机制

3.3.1 simam_module模块的介绍

一个simam_module块可以被看作是一个计算单元,旨在增强卷积神经网络中特征的表达能力。它可以将任何中间特征张量作为输入并通过转换输出了与张量具有相同size同时具有增强表征的作用。

3.3.2 Simam注意力机制推导公式

In [5]
import paddlefrom paddle.fluid.layers.nn import transposeimport paddle.nn as nnimport mathimport paddle.nn.functional as Fclass simam_module(nn.Layer):
    def __init__(self, channels, e_lambda = 1e-4):
        super(simam_module, self).__init__()

        self.activaton = nn.Sigmoid()
        self.e_lambda = e_lambda    def __repr__(self):
        s = self.__class__.__name__ + '('
        s += ('lambda=%f)' % self.e_lambda)        return s    @staticmethod
    def get_module_name():
        return "simam"

    def forward(self, x):

        b, c, h, w = x.shape
        
        n = w * h - 1

        x_minus_mu_square = (x - x.mean(axis=[2,3], keepdim=True)).pow(2)
        y = x_minus_mu_square / (4 * (x_minus_mu_square.sum(axis=[2,3], keepdim=True) / n + self.e_lambda)) + 0.5

        return x * self.activaton(y)if __name__ == '__main__':
    x = paddle.randn(shape=[1, 16, 64, 128])    # b, c, h, w

    simam = simam_module(16)
    y = simam(x)    print(y.shape)

3.3.3 注意力多尺度特征融合卷积神经网络的搭建

In [6]
import paddle.nn.functional as F# 构建模型(Inception层)class Inception(paddle.nn.Layer):
    def __init__(self, in_channels, c1, c2, c3, c4):
        super(Inception, self).__init__()        # 路线1,卷积核1x1
        self.route1x1_1 = paddle.nn.Conv2D(in_channels, c1, kernel_size=1)        # 路线2,卷积层1x1、卷积层3x3
        self.route1x1_2 = paddle.nn.Conv2D(in_channels, c2[0], kernel_size=1)
        self.route3x3_2 = paddle.nn.Conv2D(c2[0], c2[1], kernel_size=3, padding=1)        # 路线3,卷积层1x1、卷积层5x5
        self.route1x1_3 = paddle.nn.Conv2D(in_channels, c3[0], kernel_size=1)
        self.route5x5_3 = paddle.nn.Conv2D(c3[0], c3[1], kernel_size=5, padding=2)        # 路线4,池化层3x3、卷积层1x1
        self.route3x3_4 = paddle.nn.MaxPool2D(kernel_size=3, stride=1, padding=1)
        self.route1x1_4 = paddle.nn.Conv2D(in_channels, c4, kernel_size=1)    def forward(self, x):
        route1 = F.relu(self.route1x1_1(x))
        route2 = F.relu(self.route3x3_2(F.relu(self.route1x1_2(x))))
        route3 = F.relu(self.route5x5_3(F.relu(self.route1x1_3(x))))
        route4 = F.relu(self.route1x1_4(self.route3x3_4(x)))
        out = [route1, route2, route3, route4]        return paddle.concat(out, axis=1)  # 在通道维度(axis=1)上进行连接# 构建 BasicConv2d 层def BasicConv2d(in_channels, out_channels, kernel, stride=1, padding=0):
    layer = paddle.nn.Sequential(
                paddle.nn.Conv2D(in_channels, out_channels, kernel, stride, padding), 
                paddle.nn.BatchNorm2D(out_channels, epsilon=1e-3),
                paddle.nn.ReLU())    return layer# 搭建网络class TowerNet(paddle.nn.Layer):
    def __init__(self, in_channel, num_classes):
        super(TowerNet, self).__init__()
        self.b1 = paddle.nn.Sequential(
                    BasicConv2d(in_channel, out_channels=64, kernel=3, stride=2, padding=1),
                    paddle.nn.MaxPool2D(2, 2))
        self.b2 = paddle.nn.Sequential(
                    BasicConv2d(64, 128, kernel=3, padding=1),
                    paddle.nn.MaxPool2D(2, 2))
        self.b3 = paddle.nn.Sequential(
                    BasicConv2d(128, 256, kernel=3, padding=1),
                    paddle.nn.MaxPool2D(2, 2),
                    simam_module(256))
        self.b4 = paddle.nn.Sequential(
                    BasicConv2d(256, 256, kernel=3, padding=1),
                    paddle.nn.MaxPool2D(2, 2),
                    simam_module(256))
        self.b5 = paddle.nn.Sequential(
                    Inception(256, 64, (64, 128), (16, 32), 32),
                    paddle.nn.MaxPool2D(2, 2),
                    simam_module(256),
                    Inception(256, 64, (64, 128), (16, 32), 32),
                    paddle.nn.MaxPool2D(2, 2),
                    simam_module(256),
                    Inception(256, 64, (64, 128), (16, 32), 32))
        self.AvgPool2D=paddle.nn.AvgPool2D(2)
        self.flatten=paddle.nn.Flatten()
        self.b6 = paddle.nn.Linear(256, num_classes)    def forward(self, x):
        x = self.b1(x)
        x = self.b2(x)
        x = self.b3(x)
        x = self.b4(x)
        x = self.b5(x)
        x = self.AvgPool2D(x)
        x = self.flatten(x)
        x = self.b6(x)        return x
In [7]
model = paddle.Model(TowerNet(3, config_parameters['class_dim']))
model.summary((-1, 3, 256, 256))

④改进模型的训练和优化器的选择

In [8]
#优化器选择class SaveBestModel(paddle.callbacks.Callback):
    def __init__(self, target=0.5, path='work/best_model', verbose=0):
        self.target = target
        self.epoch = None
        self.path = path    def on_epoch_end(self, epoch, logs=None):
        self.epoch = epoch    def on_eval_end(self, logs=None):
        if logs.get('acc') > self.target:
            self.target = logs.get('acc')
            self.model.save(self.path)            print('best acc is {} at epoch {}'.format(self.target, self.epoch))

callback_visualdl = paddle.callbacks.VisualDL(log_dir='work/CA_Inception_Net')
callback_savebestmodel = SaveBestModel(target=0.5, path='work/best_model')
callbacks = [callback_visualdl, callback_savebestmodel]

base_lr = config_parameters['lr']
epochs = config_parameters['epochs']def make_optimizer(parameters=None):
    momentum = 0.9

    learning_rate= paddle.optimizer.lr.CosineAnnealingDecay(learning_rate=base_lr, T_max=epochs, verbose=False)
    weight_decay=paddle.regularizer.L2Decay(0.0002)
    optimizer = paddle.optimizer.Momentum(
        learning_rate=learning_rate,
        momentum=momentum,
        weight_decay=weight_decay,
        parameters=parameters)    return optimizer

optimizer = make_optimizer(model.parameters())
In [9]
model.prepare(optimizer,
              paddle.nn.CrossEntropyLoss(),
              paddle.metric.Accuracy())
In [10]
model.fit(train_loader,
          eval_loader,
          epochs=100,
          batch_size=1,           # 是否打乱样本集     
          callbacks=callbacks, 
          verbose=1)   # 日志展示格式

⑤模型训练效果展示

在增加了simam_module模块的注意力机制后,性能有了较大幅度的提升,模型也更鲁棒(蓝色曲线为添加SimAM注意力机制后)。

⑥项目总结

  • 1.受启发于人脑注意力机制,本文提出一种3D注意力模块并设计了一种能量函数用于计算注意力权值;
  • 2.该文献推导出了能量函数的解析解加速了注意力权值的计算并得到了一种轻量型注意力模块;
  • 3.将所提注意力嵌入到现有ConvNet中在不同任务上进行了灵活性与有效性的验证。 


# http  # 加载  # 均值  # 是一个  # 的是  # 它对  # 解压缩  # 压缩包  # 数据结构  # 出了  # 中山大学  # ai  # 并发  # 继承  # 运算符  # html  # asic  # igs  # red  # cos  # 异步加载  # 区别 


相关栏目: 【 Google疑问12 】 【 Facebook疑问10 】 【 网络优化91478 】 【 技术知识72672 】 【 云计算0 】 【 GEO优化84317 】 【 优选文章0 】 【 营销推广36048 】 【 网络运营41350 】 【 案例网站102563 】 【 AI智能45237


相关推荐: 壹伴AI智能排版如何自动生成文章配图_壹伴AI智能排版配图生成与版权说明【教程】  百度AI搜索怎么用AI总结网页_百度AI搜索网页总结功能与调用【技巧】  AI赋能抵押贷款:Total Expert AI 销售助理深度解析  ChatGPT图像生成器完全指南:文化影响、伦理挑战与商业变革  AI Notebooks: 知识工作者的未来?赋能理解与洞察的工具  ChatGPT 角色扮演实战:提升沟通技巧与问题解决能力  消除噪音,提升音质:Audo.ai终极指南  AI驱动音频优化:提升音质的终极指南  Lovart AI设计助手:AI驱动设计,零成本开启创意新纪元  OpenAI Codex最强攻略:提升AI编码效率的秘诀  使用双端队列(deque)解决字母字符串问题  千问怎样调整回答语气_千问语气设置亲切专业等【指南】  股票 vs. ETF:解锁股市财富密码,新手投资完全指南  唇语解读的界限:名人的隐私与公众的好奇心  Z170芯片组内存兼容性问题终极指南  Gemini怎样写实用型提示词_Gemini实用提示词编写【攻略】  今日头条AI怎样推荐抢票工具_今日头条AI抢票工具推荐算法与筛选【技巧】  终极游戏工作站:帝王蝎椅沉浸式体验评测  谷歌 Gemini AI 助手详解:功能、应用与隐私设置  AI Vibe Coding: 快速打造落地页,低代码平台实战教程  利用 ChatGPT 进行高质量代码重构与优化  百度搜索ai助手怎么关闭 百度搜索ai对话屏蔽方法  智行ai抢票如何查看抢票进度_智行ai抢票进度查询与状态解读【实操】  智行ai抢票能否跨站抢票_智行ai抢票跨站抢票开启与规则【教程】  Midjourney怎样生成网页_Midjourney生成网页教程【方法】  如何让ChatGPT模仿特定文风 创意写作与品牌话术生成教程  批改网ai检测工具怎么导出检测结果_批改网ai检测工具报告导出与格式选择【指南】  AI虚拟女友:终极浪漫伴侣还是数字陷阱?  批改网AI检测工具怎样设置检测维度_批改网AI检测工具维度勾选与权重调整【技巧】  Claude怎么用新功能代码辅助_Claude代码辅助使用攻略【方法】  DeepSeek AI:AI通用谜题解题器,解题思路全解析  ChatGPT一键生成PPT怎么加目录_ChatGPTPPT目录添加【步骤】  百度浏览器ai对话怎么关 百度浏览器ai聊天窗口隐藏  利用AI赋能教育:学习方式的未来之路  AI加持:2025年最佳人工智能潜在客户生成工具  教你用AI帮你生成一份详细的搬家清单,告别手忙脚乱  AI面试作弊与反作弊:求职者与企业的博弈  Removebg怎样快速抠图_Removebg上传图片与自动抠图步骤【教程】  kimi生成ppt怎么编辑文字_kimi编辑文字后怎么保存  AI无镜头相机Paragraphica:颠覆传统摄影的新方式  千问怎么设置快捷指令_千问指令创建与一键调用【技巧】  百度AI搜索如何开启无痕搜索_百度AI搜索无痕模式设置与隐私保护【攻略】  定价3499炒到1.2万,豆包AI手机遭“封杀”,变革之路何去何从?  2025数据科学学习指南:技能、工具和学习路线图  通义千问怎样写小红书文案_通义千问文案写作步骤【步骤】  tofai最新官网入口 tofai网页版直接进入  蚂蚁阿福官方网站入口_网页版在线解读体检报告  如何使用 Gemini 进行 Google Cloud 架构成本预估  使用Agent AI Book Cover Creator轻松设计吸睛图书封面  5分钟教你用AI给黑白老照片上色,让回忆变得鲜活 

 2025-07-18

了解您产品搜索量及市场趋势,制定营销计划

同行竞争及网站分析保障您的广告效果

点击免费数据支持

提交您的需求,1小时内享受我们的专业解答。

南京市珐之弘网络技术有限公司


南京市珐之弘网络技术有限公司

南京市珐之弘网络技术有限公司专注海外推广十年,是谷歌推广.Facebook广告全球合作伙伴,我们精英化的技术团队为企业提供谷歌海外推广+外贸网站建设+网站维护运营+Google SEO优化+社交营销为您提供一站式海外营销服务。

 87067657

 13565296790

 87067657@qq.com

Notice

We and selected third parties use cookies or similar technologies for technical purposes and, with your consent, for other purposes as specified in the cookie policy.
You can consent to the use of such technologies by closing this notice, by interacting with any link or button outside of this notice or by continuing to browse otherwise.