极致分离卷积块 XSepConv 学习笔记 (附代码)

news2024/12/23 3:47:24

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

代码地址:

1.是什么?

XSepConv是由清华大学提出的,它是一种新型的卷积神经网络模块,可以在保持计算量不变的情况下提高模型的性能。XSepConv的特点是将深度卷积和逐通道卷积结合起来,同时使用可分离卷积和组卷积来提高模型的性能。

XSepConv是一种新型的极度分离卷积块,它将空间可分离卷积融合为深度卷积,以进一步降低大内核的计算成本和参数大小。XSepConv由3个部分组成:2x2深度卷积核;1个k深度卷积核;和k1个深度卷积核。1×k和k×1深度卷积一起形成所说的空间分离深度卷积,其中2×2深度卷积起着重要的作用用于捕获由于固有的结构缺陷而可能被空间分离深度卷积丢失的信息。

2.为什么?

1.之前应用较多的轻量级网络大多是基于深度可分离卷积的,属于分组卷积的个例,其中
Depthwise separable convolutions = Depthwise convolution + Pointwise convolution

2.Nips2019的工作Convolution with even-sized kernels and symmetric padding,使用对称padding策略解决了偶数尺寸大小的卷积提取特征带来的位置偏移问题。与深度卷积相比,偶数大小的传统卷积也可以获得具有竞争力的精度

3.之前的相关工作如MixNet探索了卷积核大小对于性能的影响,大尺寸的卷积被证明可以有效增强网络性能

4.空间分离卷积 == KxK卷积 = Kx1卷积 + 1xK卷积,可以进一步节省计算成本。考虑到基于深度卷积的CNNs主要侧重于对通道数的探索以达到降低计算成本的目的,可以通过对正交空间空间维数的进一步分解来实现更大幅度的压缩,特别是对大深度卷积核。空间可分卷积可以看作是空间层次上的分解,由宽度方向的卷积和高度方向的卷积组成,近似替代原来的二维空间卷积,从而降低了计算复杂度。然而,直接将空间可分卷积应用在网络架构中会带来大量的信息损失。

提出了一种极致分离的卷积块XSepConv,它将深度分离卷积与空间可分卷积混合在一起,形成空间可分的深度卷积,进一步减少了大深度卷积核的参数大小和计算量。考虑到空间分离卷积特别是在水平和竖直方向上缺乏足够的捕获信息的能力,额外的操作需要捕捉信息在其他方向(e.g.对角方向)避免信息大量丢失。在这里,我们使用一个简单而有效的操作,2X2DWConv与改进的对称填充策略,在一定程度上以补偿上述副作用。
 

3.怎么样?

3.1网络结构

1.basic XSepConv block:
K×K DW = 2×2 DW(改进对称padding策略)+1×K DW + K×1 DW
利用空间分离卷积来减小大深度卷积核的参数大小和计算复杂度,并使用一个额外的2x2深度卷积和改进的对称填充策略来补偿空间可分卷积带来的副作用。

2.down sampling XSepConv block:
K×K DW = 2×2 DW(改进对称padding策略)+1×K DW(stride=2) + K×1 DW(stride=2)
相当于在竖直水平两个方向分别down sampling,论文中还分析了具体计算量和参数量的计算,讨论了down sampling模块适用于卷积核尺寸>7

3.2原理分析

当使用偶数卷积核时,非对称padding方式,例如只在右下方向,常常用来保证特征图大小不变。因此,激活值会偏移到空间位置的左上角,这就是移位问题。为了解决这个问题上图(a)的操作为: 

1.对输入特征图按照通道分为4个组
2.对4个组特征分别按照左上,左下,右上和右下方向Padding
3.对4个padding后的特征分别使用2x2卷积提取特征
4.对提取到的特征进行拼接

然而,在图像分类等大多数计算机视觉任务中,最重要的输出是最后一层而不是中间的单层。如图2(a)所示,传统对称padding策略将在最后一层的一些输出处遇到不对称。如果沿着所有四个移位方向都不同的路径(如图2(a)中的绿线)的信息流通,则在最终输出中消除位置偏移。否则,位置偏移将在至少一个方向上累积,例如,图2(a)中的红线指示左上方向的位置偏移将在最终将特征挤压到空间位置的左上角并导致性能下降的最终输出中累积。为了解决这个问题,本文提出了改进的对称padding策略,具体如图2(b):
1.定义4个padding方向

2.假设N为网络中使用2x2卷积层的个数,如果可以被4整除,则按照四个方向顺序padding: 

3.如果N%4 = 2,最后两层具有偶数大小内核的padding方向是相反的,以便尽可能地消除偏移。
4.如果N%4 = 1,最后一层使用传统对称padding策略. 

3.3结构设计

3.4代码实现

import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.nn import init

class hswish(nn.Module):
    def forward(self, x):
        out = x * F.relu6(x + 3, inplace=True) / 6
        return out

class hsigmoid(nn.Module):
    def forward(self, x):
        out = F.relu6(x + 3, inplace=True) / 6
        return out

class SeModule(nn.Module):
    def __init__(self, in_size, reduction=4):
        super(SeModule, self).__init__()
        self.se = nn.Sequential(
            nn.AdaptiveAvgPool2d(1),
            nn.Conv2d(in_size, in_size // reduction, kernel_size=1, stride=1, padding=0, bias=False),
            nn.BatchNorm2d(in_size // reduction),
            nn.ReLU(inplace=True),
            nn.Conv2d(in_size // reduction, in_size, kernel_size=1, stride=1, padding=0, bias=False),
            nn.BatchNorm2d(in_size),
            hsigmoid()
        )

    def forward(self, x):
        return x * self.se(x)
# DWConv
class Block(nn.Module):
    '''expand + depthwise + pointwise'''
    def __init__(self, kernel_size, in_size, expand_size, out_size, nolinear, semodule, stride):
        super(Block, self).__init__()
        self.stride = stride
        self.se = semodule

        self.conv1 = nn.Conv2d(in_size, expand_size, kernel_size=1, stride=1, padding=0, bias=False)
        self.bn1 = nn.BatchNorm2d(expand_size)
        self.nolinear1 = nolinear
        self.conv2 = nn.Conv2d(expand_size, expand_size, kernel_size=kernel_size, stride=stride, padding=kernel_size//2, groups=expand_size, bias=False)
        self.bn2 = nn.BatchNorm2d(expand_size)
        self.nolinear2 = nolinear
        self.conv3 = nn.Conv2d(expand_size, out_size, kernel_size=1, stride=1, padding=0, bias=False)
        self.bn3 = nn.BatchNorm2d(out_size)

        self.shortcut = nn.Sequential()
        if stride == 1 and in_size != out_size:
            self.shortcut = nn.Sequential(
                nn.Conv2d(in_size, out_size, kernel_size=1, stride=1, padding=0, bias=False),
                nn.BatchNorm2d(out_size),
            )

    def forward(self, x):
        out = self.nolinear1(self.bn1(self.conv1(x)))
        out = self.nolinear2(self.bn2(self.conv2(out)))
        out = self.bn3(self.conv3(out))
        if self.se != None:
            out = self.se(out)
        out = out + self.shortcut(x) if self.stride==1 else out
        return out
# XSepConv
class XSepBlock(nn.Module):
    ''' 1*1 expand + Improved Symmetric Padding + 2*2 DW + 1*K DW + k*1 DW + 1*1 out'''
    def __init__(self, kernel_size, in_size, expand_size, out_size, nolinear, semodule, stride, ith):
        super(Block, self).__init__()
        self.stride = stridE
        self.ith = ith

        # 1*1 expand
        self.conv1 = nn.Conv2d(in_size, expand_size, kernel_size=1, stride=1, padding=0, bias=False)
        self.bn1 = nn.BatchNorm2d(expand_size)
        self.nolinear1 = nolinear
        # 2*2 DW
        self.conv2 = nn.Conv2d(expand_size, expand_size, kernel_size=2, stride=1, padding=0, groups=expand_size, bias=False)
        self.bn2 = nn.BatchNorm2d(expand_size)
        self.nolinear2 = nolinear
        # 1*k DW 
        self.conv3 = nn.Conv2d(expand_size, expand_size, kernel_size=(1, kernel_size), stride=stride, 
                    padding=(0, kernel_size//2), groups=expand_size, bias=False)
        self.bn3 = nn.BatchNorm2d(expand_size)
        self.nolinear3 = nolinear
        # k*1 DW
        self.conv4 = nn.Conv2d(expand_size, expand_size, kernel_size=(kernel_size, 1), stride=stride, 
                    padding=(kernel_size//2, 0),, groups=expand_size, bias=False)
        self.bn4 = nn.BatchNorm2d(expand_size)
        # SE 
        self.se = semodule
        self.nolinear_se = nolinear
        # 1*1 out
        self.conv5 = nn.Conv2d(expand_size, out_size, kernel_size=1, stride=1, padding=0, bias=False)
        self.bn5 = nn.BatchNorm2d(out_size)

        self.shortcut = nn.Sequential()
        if stride == 1 and in_size != out_size:
            self.shortcut = nn.Sequential(
                nn.Conv2d(in_size, out_size, kernel_size=1, stride=1, padding=0, bias=False),
                nn.BatchNorm2d(out_size),
            )

    def forward(self, x):
        x1 = x
        if self.ith % 4 == 0:
            x1 = nn.functional.pad(x1,(0,1,1,0),mode = "constant",value = 0) # right top
        elif self.ith % 4 == 1:
            x1 = nn.functional.pad(x1,(0,1,0,1),mode = "constant",value = 0) # right bottom
        elif self.ith % 4 == 2:
            x1 = nn.functional.pad(x1,(1,0,1,0),mode = "constant",value = 0) # left top
        elif self.ith % 4 == 3:
            x1 = nn.functional.pad(x1,(1,0,0,1),mode = "constant",value = 0) # left bottom
        else:
            raise NotImplementedError('ith layer is not right')
          
        # 1*1 expand
        out = self.nolinear1(self.bn1(self.conv1(x1)))
        # 2*2 DW
        out = self.nolinear2(self.bn2(self.conv2(out)))
        # 1*k DW
        out = self.nolinear3(self.bn3(self.conv3(out)))
        # k*1 DW
        out = self.bn4(self.conv4(out))
        # SE
        if self.se != None:
            out = self.nolinear_se(self.se(out))
        # 1*1 out
        out = self.bn5(self.conv5(out))
        out = out + self.shortcut(x) if self.stride==1 else out
        return out
#按照论文设置改的,可能会有出入
class XSepMobileNetV3_Small(nn.Module):
    def __init__(self, num_classes=1000):
        super(MobileNetV3_Small, self).__init__()
        self.conv1 = nn.Conv2d(3, 16, kernel_size=3, stride=2, padding=1, bias=False)
        self.bn1 = nn.BatchNorm2d(16)
        self.hs1 = hswish()

        self.bneck = nn.Sequential(
            Block(3, 16, 16, 16, nn.ReLU(inplace=True), SeModule(16), 2),
            Block(3, 16, 72, 24, nn.ReLU(inplace=True), None, 2),
            Block(3, 24, 88, 24, nn.ReLU(inplace=True), None, 1),
            Block(5, 24, 96, 40, hswish(), SeModule(40), 2), # 下采样层使用DW,因为kernel_size < 7
            XSepBlock(5, 40, 240, 40, hswish(), SeModule(40), 1, 1),
            XSepBlock(5, 40, 240, 40, hswish(), SeModule(40), 1, 2),
            XSepBlock(5, 40, 120, 48, hswish(), SeModule(48), 1, 3),
            XSepBlock(5, 48, 144, 48, hswish(), SeModule(48), 1, 4),
            Block(5, 48, 288, 96, hswish(), SeModule(96), 2), # 下采样层使用DW,因为kernel_size < 7
            XSepBlock(3, 96, 576, 96, hswish(), SeModule(96), 1, 1), 
            XSepBlock(3, 96, 576, 96, hswish(), SeModule(96), 1, 2),
        )


        self.conv2 = nn.Conv2d(96, 576, kernel_size=1, stride=1, padding=0, bias=False)
        self.bn2 = nn.BatchNorm2d(576)
        self.hs2 = hswish()
        self.linear3 = nn.Linear(576, 1280)
        self.bn3 = nn.BatchNorm1d(1280)
        self.hs3 = hswish()
        self.linear4 = nn.Linear(1280, num_classes)
        self.init_params()

    def init_params(self):
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                init.kaiming_normal_(m.weight, mode='fan_out')
                if m.bias is not None:
                    init.constant_(m.bias, 0)
            elif isinstance(m, nn.BatchNorm2d):
                init.constant_(m.weight, 1)
                init.constant_(m.bias, 0)
            elif isinstance(m, nn.Linear):
                init.normal_(m.weight, std=0.001)
                if m.bias is not None:
                    init.constant_(m.bias, 0)

    def forward(self, x):
        out = self.hs1(self.bn1(self.conv1(x)))
        out = self.bneck(out)
        out = self.hs2(self.bn2(self.conv2(out)))
        out = F.avg_pool2d(out, 7)
        out = out.view(out.size(0), -1)
        out = self.hs3(self.bn3(self.linear3(out)))
        out = self.linear4(out)
        return out

def test():
    net = XSepMobileNetV3_Small()
    x = torch.randn(2,3,224,224)
    y = net(x)
    print(y.size())

参考:

XSepConv_MobileNetV3_small | 自己实现代码

XSepConv 极致分离卷积块优于DWConv | Extremely Separated Convolution

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1162233.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

group by用法和SQL执行顺序详解

一、group by 1、作用 数据分组 -> 组内数据处理&#xff08;求组内的最大值、最小值、平均值等&#xff09; 2、案例&#xff1a; Order Details&#xff08;订单详情表&#xff09; 表结构&#xff1a;每个Order&#xff0c;都对应着好几个不同的Product&#xff0c;每…

尚硅谷大数据项目《在线教育之实时数仓》笔记005

视频地址&#xff1a;尚硅谷大数据项目《在线教育之实时数仓》_哔哩哔哩_bilibili 目录 第9章 数仓开发之DWD层 P031 P032 P033 P034 P035 P036 P037 P038 P039 P040 第9章 数仓开发之DWD层 P031 DWD层设计要点&#xff1a; &#xff08;1&#xff09;DWD层的设计依…

Azure 机器学习 - 使用 Visual Studio Code训练图像分类 TensorFlow 模型

了解如何使用 TensorFlow 和 Azure 机器学习 Visual Studio Code 扩展训练图像分类模型来识别手写数字。 关注TechLead&#xff0c;分享AI全维度知识。作者拥有10年互联网服务架构、AI产品研发经验、团队管理经验&#xff0c;同济本复旦硕&#xff0c;复旦机器人智能实验室成员…

免费(daoban)gpt,同时去除广告

一. 内容简介 免费(daoban)gpt&#xff0c;同时去除广告&#xff0c;https://chat18.aichatos.xyz/&#xff0c;也可当gpt用&#xff0c;就是有点广告&#xff0c;大家也可以支持一下 二. 软件环境 2.1 Tampermonkey 三.主要流程 3.1 创建javascript脚本 点击添加新脚本 …

超详细!!!顺序表的实现

顺链表的实现 顺序表的概念及结构概念顺序表与数组的区别顺序表的结构 动态顺序表的实现头文件 "SeqList.h"定义结构体 SL 源文件顺列表的实现初始化顺列表 void SLInit(SL* ps)检查顺列表空间大小 void SLCheckCapacity(SL* ps)尾插数据 void SLPushBack(SL* ps,SLD…

java强转实验

不存在继承关系时&#xff0c;强转会出现编译时异常。即&#xff1a;无法将两个不同类型的对象做转换 当存在继承关系时&#xff0c;强转正常。备注&#xff1a;同名字段&#xff0c;类型一致&#xff0c;可以强转替代getset。同名字段&#xff0c;类型不一致&#xff0c;强转会…

网络安全之XSS漏洞

一. 引言 Cross-Site Scripting&#xff08;跨站脚本攻击&#xff09;简称XSS&#xff0c;是一种代码注入攻击。XSS 攻击通常指的是利用网页的漏洞&#xff0c;攻击者通过巧妙的方法注入 XSS 代码到网页&#xff0c;因为浏览器无法分辨哪些脚本是可信的&#xff0c;导致 XSS 脚…

matlab求解时变系统的Riccati矩阵微分方程

对于代数Riccati方程的求解网上能找到很多的资源&#xff0c;matlab也有成熟的函数&#xff0c;但是对于时变系统的Riccati矩阵微分方程&#xff0c;能找到的资料还比较少。 一、求解代数Riccati方程 可以在网上找到很多资料&#xff0c;如 https://blog.csdn.net/m0_622999…

python中有哪些你觉得超级牛的模块?

之前在做数据分析的时候&#xff0c;用过一个自动化生成数据探索报告的Python库&#xff1a;ydata_profiling 一般我们在做数据处理前会进行数据探索&#xff0c;包括看统计分布、可视化图表、数据质量情况等&#xff0c;这个过程会消耗很多时间&#xff0c;可能需要上百行代码…

Linux--线程--互斥锁

1.互斥量 a&#xff09;互斥量&#xff08;mutex&#xff09;从本质上来说是一把锁&#xff0c;一般在主线程中定义一个互斥量&#xff0c;就是定义一把锁。然后根据我们的需求来对线程操作这把锁。 b&#xff09;如果给所有的线程都加上锁了&#xff0c;线程们会去争取内存空…

2018年第三届 美亚杯电子取证 个人赛题解

1 Victor的笔记本电脑己成功取证并制作成法证映像档 (Forensic Image)&#xff0c;下列哪个是其MD5哈希值? (2分) A. FC20782C21751AB76B2A93F3A17922D0 B. 5F1BDEB87EE9F710C90CFB3A0BB01616 C. A0BB016160CFB3A0BB0161661670CFB3 D. 917ED59083C8B35C54D3FCBFE4C4BB0B E. F…

当你在浏览器地址栏输入一个URL后,将会发生的事情?个人笔记

客户端 在浏览器输入 URL 回车之后发生了什么&#xff08;超详细版&#xff09; - 知乎 (zhihu.com) 大致流程是&#xff1a; URL 解析DNS 查询TCP 连接处理请求接受响应渲染页面 1.URL解析 地址解析&#xff1a; 首先判断你输入是否是一个合法的URL还是一个待搜索的关键…

上市公司-供应链效率数据集(2000-2022年)

参照张倩肖&#xff08;2023&#xff09;、Feng&#xff08;2015&#xff09;、张树山&#xff08;2023&#xff09;的做法&#xff0c;团队以库存周转天数来衡量供应链效率 库存周转天数有效克服了因企业保留安全库存而导致供应链效率较低的测算误差&#xff0c;体现供应链上…

回归预测 | Matlab实现POA-CNN-SVM鹈鹕算法优化卷积神经网络-支持向量机多变量回归预测

Matlab实现POA-CNN-SVM鹈鹕算法优化卷积神经网络-支持向量机多变量回归预测 目录 Matlab实现POA-CNN-SVM鹈鹕算法优化卷积神经网络-支持向量机多变量回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.POS-CNN-SVM鹈鹕算法优化卷积神经网络-支持向量机的多变量回归…

好用的CRM软件都有哪些功能?

好用的CRM软件不仅仅是将客户资料存档&#xff0c;更大的作用还在于充分发挥数据的价值提升客户管理效率。如果您了解过多款CRM软件就一定会发现它们的功能都不尽相同&#xff0c;但是好用的CRM工具离不开这些功能&#xff1a; 一、客户视图 客户视图主要由4类数据组成&#…

基于springboot实现游戏分享网站系统项目【项目源码+论文说明】

基于springboot实现游戏分享网站演示 摘要 网络的广泛应用给生活带来了十分的便利。所以把游戏分享管理与现在网络相结合&#xff0c;利用java技术建设游戏分享网站&#xff0c;实现游戏分享的信息化。则对于进一步提高游戏分享管理发展&#xff0c;丰富游戏分享管理经验能起到…

跨境商城源码价格

在当今数字商务的时代&#xff0c;跨境电商已经成为了越来越多企业的选择。然而&#xff0c;要建立一个高效、便捷、全球化的跨境商城并不是一件简单的事情。所幸&#xff0c;现在有一个开源的解决方案&#xff0c;给企业提供了无限的可能性。跨境商城源码价格合乎实际&#xf…

浅谈AcrelEMS-CB商业建筑能源管理系统解决方案-安科瑞 蒋静

1概述 AcrelEMS-CB商业建筑能源管理系统&#xff0c;集电力监控、电能质量监测与治理、电气安全预警、能耗分析、照明控制、新能源使用、能源收费以及设备运维等功能于一体&#xff0c;通过一套系统对商业建筑的能源进行统一监控、统一运维和调度&#xff0c;系统可以通过WEB和…

对比学习(contrastive Learning)

起源和定义 自监督学习又可以分为对比学习(contrastive learning)和生成学习(generative learning)两条主要的技术路线。 比学习的核心思想是将正样本和负样本在特征空间对比&#xff0c;从而学习样本的特征表示&#xff0c;使得样本与正样本的特征表示尽可能接近。正样本和负…

webase编译合约一直转圈卡住解决方案

问题:webase编译合约一直转圈卡住,等再久也没反应 解决方案: 进入webase-web目录,然后进入static\js目录,执行以下命令: curl -#L https://osp-1257653870.cos.ap-guangzhou.myqcloud.com/WeBASE/download/solidity/wasm/v0.4.25.js -o v0.4.25.js curl -#L https://os…