【Block总结】PSA,金字塔挤压注意力,解决传统注意力机制在捕获多尺度特征时的局限性

论文信息

  • 标题: EPSANet: An Efficient Pyramid Squeeze Attention Block on Convolutional Neural Network
  • 论文链接: arXiv
  • GitHub链接: https://github.com/murufeng/EPSANet

在这里插入图片描述

创新点

EPSANet提出了一种新颖的金字塔挤压注意力(PSA)模块,旨在解决传统注意力机制在捕获多尺度特征时的局限性。其主要创新点包括:

  1. 高效性: PSA模块能够在较低的计算成本下有效提取多尺度空间信息。
  2. 灵活性: 该模块可以作为即插即用的组件,轻松集成到现有的卷积神经网络(CNN)架构中。
  3. 多尺度特征表示: EPSANet通过自适应地重新校准跨维通道的注意权重,增强了特征表示能力。

方法

EPSANet的核心是PSA模块,其实现过程如下:

  1. 多尺度特征提取: 通过Squeeze and Concat (SPC)模块获得通道维度上的多尺度特征图。
  2. 注意力计算: 使用SEWeight模块提取不同尺度特征图的注意力,生成通道方向的注意力向量。
  3. 再校准: 通过Softmax对通道维度的注意向量进行再校准,得到多尺度信道的再校准权重。
  4. 特征融合: 在重新校准的权重和对应的特征图上进行按元素乘积,输出丰富的多尺度特征图。
    在这里插入图片描述

效果

EPSANet在多个计算机视觉任务中表现出色,包括图像分类、目标检测和实例分割。与传统的通道注意力方法相比,EPSANet在性能上有显著提升。例如:

  • 在ImageNet数据集上,EPSANet的Top-1准确率比SENet-50提高了1.93%。
  • 在MS-COCO数据集上,使用Mask-RCNN时,EPSANet在目标检测和实例分割任务中分别提高了2.7和1.7的AP值。

实验结果

实验结果表明,EPSANet在多个标准数据集上均超越了当前最新的技术。具体表现为:

  • 在COCO 2017数据集上,EPSANet的目标检测率超越了ECANet,AP75指标提高了1.4%。
  • 论文中进行了大量的定性和定量实验,验证了EPSANet在图像分类、目标检测和实例分割方面的先进性能。

总结

EPSANet通过引入金字塔挤压注意力模块,成功地提升了卷积神经网络在多尺度特征提取方面的能力。其灵活的设计使得EPSANet能够广泛应用于各种计算机视觉任务,展现出良好的泛化性能和高效性。该研究为未来的深度学习模型设计提供了新的思路和方法。

代码

import torch
import torch.nn as nn

class SEWeightModule(nn.Module):

    def __init__(self, channels, reduction=16):
        super(SEWeightModule, self).__init__()
        self.avg_pool = nn.AdaptiveAvgPool2d(1)
        self.fc1 = nn.Conv2d(channels, channels//reduction, kernel_size=1, padding=0)
        self.relu = nn.ReLU(inplace=True)
        self.fc2 = nn.Conv2d(channels//reduction, channels, kernel_size=1, padding=0)
        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        out = self.avg_pool(x)
        out = self.fc1(out)
        out = self.relu(out)
        out = self.fc2(out)
        weight = self.sigmoid(out)

        return weight

def conv(in_planes, out_planes, kernel_size=3, stride=1, padding=1, dilation=1, groups=1):
    """standard convolution with padding"""
    return nn.Conv2d(in_planes, out_planes, kernel_size=kernel_size, stride=stride,
                     padding=padding, dilation=dilation, groups=groups, bias=False)

def conv1x1(in_planes, out_planes, stride=1):
    """1x1 convolution"""
    return nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=stride, bias=False)

class PSAModule(nn.Module):

    def __init__(self, inplans, planes, conv_kernels=[3, 5, 7, 9], stride=1, conv_groups=[1, 4, 8, 16]):
        super(PSAModule, self).__init__()
        self.conv_1 = conv(inplans, planes//4, kernel_size=conv_kernels[0], padding=conv_kernels[0]//2,
                            stride=stride, groups=conv_groups[0])
        self.conv_2 = conv(inplans, planes//4, kernel_size=conv_kernels[1], padding=conv_kernels[1]//2,
                            stride=stride, groups=conv_groups[1])
        self.conv_3 = conv(inplans, planes//4, kernel_size=conv_kernels[2], padding=conv_kernels[2]//2,
                            stride=stride, groups=conv_groups[2])
        self.conv_4 = conv(inplans, planes//4, kernel_size=conv_kernels[3], padding=conv_kernels[3]//2,
                            stride=stride, groups=conv_groups[3])
        self.se = SEWeightModule(planes // 4)
        self.split_channel = planes // 4
        self.softmax = nn.Softmax(dim=1)

    def forward(self, x):
        batch_size = x.shape[0]
        x1 = self.conv_1(x)
        x2 = self.conv_2(x)
        x3 = self.conv_3(x)
        x4 = self.conv_4(x)

        feats = torch.cat((x1, x2, x3, x4), dim=1)
        feats = feats.view(batch_size, 4, self.split_channel, feats.shape[2], feats.shape[3])

        x1_se = self.se(x1)
        x2_se = self.se(x2)
        x3_se = self.se(x3)
        x4_se = self.se(x4)

        x_se = torch.cat((x1_se, x2_se, x3_se, x4_se), dim=1)
        attention_vectors = x_se.view(batch_size, 4, self.split_channel, 1, 1)
        attention_vectors = self.softmax(attention_vectors)
        feats_weight = feats * attention_vectors
        for i in range(4):
            x_se_weight_fp = feats_weight[:, i, :, :]
            if i == 0:
                out = x_se_weight_fp
            else:
                out = torch.cat((x_se_weight_fp, out), 1)

        return out

if __name__ == "__main__":
    dim=512
    # 如果GPU可用,将模块移动到 GPU
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    # 输入张量 (batch_size, channels,height, width)
    x = torch.randn(1,dim,14,14).to(device)
    # 初始化 PSAModule 模块

    block = PSAModule(dim,dim) # kernel_size为height或者width
    print(block)
    block = block.to(device)
    # 前向传播
    output = block(x)
    print("输入:", x.shape)
    print("输出:", output.shape)

输出结果:
在这里插入图片描述

### 关于OLED HAL库的驱动信息与使用方法 #### OLED HAL 库简介 OLED HAL (Hardware Abstraction Layer) 库提供了一种抽象层来简化硬件操作,使得开发者能够更方便地控制OLED显示屏。该库通常包含了初始化屏幕、显示字符、图形绘制等功能。 对于STM32微控制器而言,HAL库提供了针对不同外设的支持,其中包括IIC接口用于连接0.96英寸OLED屏[^1]。为了实现这一目的,需要配置特定GPIO引脚作为IIC通信线路的一部分;例如,PB12至PB15被指定为GPIO输出以支持其他可能的功能需求[^2]。 #### 移植过程概述 当准备移植现有的OLED底层驱动程序,应按照如下方式处理: - **复制必要的源文件**:将四个主要的驱动文件放置到项目的根目录下,并确保`oled.c`成为编译目标之一。同,在主应用程序入口处通过`#include "oled.h"`语句引入相应的头文件定义。 - **解决特殊挑战**:如果遇到像`printf()`这样的函数带有可变数量参数的情况,则需查阅有关C99标准下的va_list机制文档来进行适当调整[^3]。 #### 示例代码展示 下面给出一段简单的示例代码片段,展示了如何利用上述提到的方法完成基本的文字打印任务: ```c #include "stm32f1xx_hal.h" #include "oled.h" int main(void){ /* 初始化系统 */ HAL_Init(); /* 初始化OLED并清屏 */ OLEDDISPLAY_init(); OLEDDISPLAY_clear(); while(1){ char str[] = "Hello, World!"; // 显示字符串 OLEDDISPLAY_showString(str); HAL_Delay(1000); // 延迟一秒 // 清除屏幕以便更新新内容 OLEDDISPLAY_clear(); } } ``` 此段代码实现了每隔一秒钟刷新一次屏幕上所呈现的信息的效果。 #### 获取资源链接 想要获取完整的OLED HAL库及其配套资料,可以通过访问提供的GitCode仓库地址下载整个项目包,其中不仅有详细的说明文档还有丰富的例子可供参考。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

AI浩

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

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

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

打赏作者

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

抵扣说明:

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

余额充值