Strip Pooling:重构池化的空间注意力机制


本文引入strip pooling策略,以1×N或N×1长条形核实现池化,高效获取大范围感受野信息,并构建相关模块。基于此,使用Caltech101的16类数据集,对比ResNet50等经典模型,搭建含该注意力机制的TowerNet,经训练优化,在像素级预测任务中有效建模远程依赖,取得良好效果。

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

① 项目背景

  • 1.事实证明,空间池化在捕获用于场景解析等像素级预测任务的远程上下文信息方面非常有效。在本文中,除了通常具有N x N规则形状的常规空间池化之外,我们通过引入一种称为strip pooling的新池化策略来重新考虑空间池化的公式,该策略考虑了一个长而窄的核,即1 x N或N x1。基于strip pooling,我们进一步研究空间池化体系结构的设计方法。

  • 2.池化操作是在逐像素预测任务中获取较大感受野范围较为高效的做法,传统一般采取N ∗N N* NN∗N的正规矩形区域进行池化,在这篇文章中引入了一种新的池化策略,就是使用长条形的池化kernel来实现池化,即是池化的核心被重新设计为构建了strip pooling操作。

  • 3.这个操作的引入使得网络可以更加高效获取网络大范围感受野下的信息,在这个理念的基础上搭建了使用多个长条池化层构建的新模块。

       

论文地址:https://arxiv.org/pdf/2003.13328.pdf

② 数据准备

2.1 解压缩数据集

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

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

In [3]
!unzip -oq /home/aistudio/data/data69664/Images.zip -d work/dataset
    In [4]
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 [5]
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!')
       
finished train val split!
       

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

2.3.1 数据集展示

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

In [7]
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 [ ]
#数据集的定义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 [ ]
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 [ ]
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 StripPooling注意力机制

3.3.1 SP模块的介绍

       

  • MPM具体由两个子分支组成,具体来说:
  • 1.如上图(a),为原始的PPM模块,可以用于捕获特征位置的短距离依赖关系。
  • 2.如上图(b),使用strip pooling的方式,可以捕获更长距离特征之间的依赖关系。

这两种依赖关系对于网络的预测来说都至关重要。因此,MPM分别使用这两个分支生成对应的特征图,然后将两个子模块的输出拼接并用1x1卷积得到最终的输出特征。

In [4]
import paddlefrom paddle.fluid.layers.nn import transposeimport paddle.nn as nnimport mathimport paddle.nn.functional as Fclass SPBlock(nn.Layer):
    def __init__(self, inplanes, outplanes, norm_layer=None):
        super(SPBlock, self).__init__()
        midplanes = outplanes
        self.conv1 = nn.Conv2D(inplanes, midplanes, kernel_size=(3, 1), padding=(1, 0), bias_attr=False)
        self.bn1 = nn.BatchNorm(midplanes)
        self.conv2 = nn.Conv2D(inplanes, midplanes, kernel_size=(1, 3), padding=(0, 1), bias_attr=False)
        self.bn2 = nn.BatchNorm(midplanes)
        self.conv3 = nn.Conv2D(midplanes, outplanes, kernel_size=1, bias_attr=True)
        self.pool1 = nn.AdaptiveAvgPool2D((None, 1))
        self.pool2 = nn.AdaptiveAvgPool2D((1, None))
        self.relu = nn.ReLU()
        self.sigmoid = nn.Sigmoid()    def forward(self, x):
        identity = x
        _, _, h, w = x.shape
        x1 = self.pool1(x)
        x1 = self.conv1(x1)
        x1 = self.bn1(x1)
        x1 = paddle.expand(x1,[-1, -1, h, w])        #x1 = F.interpolate(x1, (h, w))

        x2 = self.pool2(x)
        x2 = self.conv2(x2)
        x2 = self.bn2(x2)
        x2 = paddle.expand(x2,[-1, -1, h, w])        #x2 = F.interpolate(x2, (h, w))

        x = self.relu(x1 + x2)
        x = x * self.sigmoid(self.conv3(x))        return xif __name__ == '__main__':
    x = paddle.randn(shape=[1, 16, 64, 128])    # b, c, h, w

    sp_model = SPBlock(16,16)
    y = sp_model(x)    print(y.shape)
       
[1, 16, 64, 128]
       

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

In [ ]
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),
                    SPBlock(256,256))
        self.b4 = paddle.nn.Sequential(
                    BasicConv2d(256, 256, kernel=3, padding=1),
                    paddle.nn.MaxPool2D(2, 2),
                    SPBlock(256,256))
        self.b5 = paddle.nn.Sequential(
                    Inception(256, 64, (64, 128), (16, 32), 32),
                    paddle.nn.MaxPool2D(2, 2),
                    SPBlock(256,256),
                    Inception(256, 64, (64, 128), (16, 32), 32),
                    paddle.nn.MaxPool2D(2, 2),
                    SPBlock(256,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 [ ]
model = paddle.Model(TowerNet(3, config_parameters['class_dim']))
model.summary((-1, 3, 256, 256))
   

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

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/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 [ ]
model.prepare(optimizer,
              paddle.nn.CrossEntropyLoss(),
              paddle.metric.Accuracy())
    In [24]
model.fit(train_loader,
          eval_loader,
          epochs=100,
          batch_size=1,           # 是否打乱样本集     
          callbacks=callbacks, 
          verbose=1)   # 日志展示格式
   

⑤项目总结

  • 1.本文设计一个strip pooling模块(SPM),以有效地扩大骨干网络的感受野范围。 更具体地说,SPM由两个途径组成,它们专注于沿水平或垂直空间维度对远程上下文进行编码。对于池化特征图中的每个空间位置,它会对其全局的水平和垂直信息进行编码,然后使用这些编码来平衡其自身的权重以进行特征优化。
  • 2.此外,提出了一种新颖的附加残差构建模块,称为混合池模块(MPM),以进一步在高语义级别上对远程依赖性进行建模。 它通过利用具有不同内核形状的池操作来收集内容丰富的上下文信息,以探查具有复杂场景的图像。
  • 3.利用SP注意力机制改进模型设计,在相关评测数据集上取得了SOTA的效果。             


# 压缩包  # 在这个  # 长条形  # 如上图  # 是在  # 标准差  # 加载  # 均值  # 解压缩  # ai  # 数据结构  # 重构  # https  # asic  # igs  # red  # cos  # 异步加载 


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


相关推荐: Logic Pro 11更新全面解析:免费升级、AI功能与音乐制作流程  AI一键生成原创SEO文章  谷歌 Nano Banana:免费AI图像生成的强大工具  百度ai助手通知栏怎么关 百度ai助手通知消息屏蔽  AI视频制作教程:从图像到病毒式Vlog全攻略  Amazon Rekognition: 图像与视频分析的强大AI工具  雷小兔ai智能写作怎样导入素材_雷小兔ai智能写作素材上传与引用【技巧】  百度APP搜索框ai怎么关 百度APP搜索框ai图标去除  提升企业效率:QR Platform管理后台功能全面解析  DeepSeek分析Excel怎么用_DeepSeek分析Excel使用方法详细指南【教程】  豆包 AI 辅助进行家庭装修风格对比分析  软件工程师必备的AI工具:提升效率的六款利器  利用Gen AI和AI Agent进行软件测试:Ollama本地LLM实践  寻宝者的发现:古董探测与文物挖掘揭秘  壹伴AI智能排版如何自动生成文章配图_壹伴AI智能排版配图生成与版权说明【教程】  如何让ChatGPT模仿特定文风 创意写作与品牌话术生成教程  AI赋能招聘:高级策略助你领先猎头行业  PandaDoc vs. SignEasy: 2025 eSignature 平台终极对比  AI旅游攻略生成工具有哪些_一键生成行程规划的AI工具推荐  豆包AI怎么生成员工成长总结_豆包AI成长指标提取与案例编写【方法】  百度AI助手直接入口 一键直达官网入口  Semrush AI标题生成器:免费提升SEO和内容创作效率  AI 播客脚本写作工具:提升内容创作效率的终极指南  2025 YouTube自动化终极指南:利用AI实现高效内容创作和多平台发布  一键改变发型:Gemini AI 助你轻松打造时尚造型  Gemini手机端怎么开无障碍_Gemini无障碍设置方法【步骤】  eBookWriter AI:无需写作也能创作专业电子书  即梦ai能否生成节日主题插画_即梦ai节日主题关键词与元素库使用【攻略】  通义万相做小红书配图怎么用_通义万相做小红书配图使用方法详细指南【教程】  Claude如何导出对话记录_Claude对话导出方法【方法】  扣子AI如何绑定自有域名_扣子AI域名绑定与SSL配置【步骤】  理论框架写作指南:3步构建研究基石  Google Gemini 对复杂物理解题过程的逐步解析  10平米房间设计终极挑战:人类 vs AI,DIY极简主义胜出!  AI破译古文字:重现失落文明之声,揭秘历史真相  AI PPT生成工具有哪些_一键生成演示文稿的AI工具推荐  AI赋能抵押贷款:Total Expert AI 销售助理深度解析  如何用AI一键生成手机壁纸?4K高清AI壁纸生成关键词【分享】  豆包AI里的智能体有什么用_不同类型智能体使用场景介绍  Google NotebookLM:科研文献综述的免费AI工具  Tenorshare PDNob:免费AI图像翻译器,即时转换图像为文本  AI赋能科研探索:Google Research创新加速科学发现  AI时代软件工程师如何破局?未来必备技能全解析  TopMedi AI:AI语音克隆和文本转语音终极指南  SEO必备工具:网站分析与优化终极指南  Weavernote:AI驱动的知识管理与高效笔记应用  支付宝出行AI能否自动抢票_支付宝AI出行抢票设置与免密支付【方法】  AI末日预言?智能聊天机器人真的能替代信仰吗?  提升房地产业务:AI语音助手赋能房地产经纪公司  利用AI自动化回复Google Voice短信:终极指南 

 2025-07-25

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

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

点击免费数据支持

提交您的需求,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.