轻量型神经网络 shufflenet V1和shufflenet V2

2023-05-16

1.shufflenet V1

ShuffleNet是旷视科技(Face++)提出的一种计算高效的CNN模型,其和MobileNet和SqueezeNet等一样主要是想应用在移动端 所以,ShuffleNet的设计目标也是如何利用有限的计算资源来达到最好的模型精度,这需要很好地在速度和精度之间做平衡。 在mobilenet v1中的核心操作是引入了深度可分离卷积,即通过对feature map的每个channel维度进行分组,分组数等于通道数, 然后通过1*1的卷积快进行通道之间信息的融合,防止通道信息无法进行交流。大的卷积核可以起到增大感受野的作用和在卷积操作的过程中代替池化层进行下采样。 而在shufflenet中的核心操作则是pointwise group convolution和channel shuffle,这在保持精度的同时大大降低了模型的计算量。其中group convolution和mobilenet中的depthwise convolution 类似,不过group convolution是认为的将feature map分成一定的group,然后再进行卷积,为防止通道信息的割裂,卷积完成之后进行channel shuffle,这也是shufflenet网络名称的由来。

                                                                                                                  

 

其中channel shuffle的代码如下:

def channel_shuffle(x, groups):
    # type: (torch.Tensor, int) -> torch.Tensor
    batchsize, num_channels, height, width = x.data.size()
    channels_per_group = num_channels // groups

    # reshape
    x = x.view(batchsize, groups,
               channels_per_group, height, width)

    x = torch.transpose(x, 1, 2).contiguous()

    # flatten
    x = x.view(batchsize, -1, height, width)

    return x

如图b,c为shufflenet的基本模块,b模块不进行下采样,c模块进行下采样,通过b,c模块的堆叠便可得到shufflenet的结构。

 

 

                                                                                                   

                                                                                                                                                               shufflenet v1基本模块


基于上面改进的ShuffleNet基本单元,设计的ShuffleNet模型如表1所示。可以看到开始使用的普通的3x3的卷积和max pool层。然后是三个阶段,每个阶段都是重复堆积了几个ShuffleNet的基本单元。对于每个阶段,第一个基本单元采用的是stride=2,这样特征图width和height各降低一半,而通道数增加一倍。后面的基本单元都是stride=1,特征图和通道数都保持不变。对于基本单元来说,其中瓶颈层,就是3x3卷积层的通道数为输出通道数的1/4,这和残差单元的设计理念是一样的。不过有个细节是,对于stride=2的基本单元,由于原输入会贡献一部分最终输出的通道数,那么在计算1/4时到底使用最终的通道数,还是仅仅未concat之前的通道数。文章没有说清楚,但是个人认为应该是后者吧。其中g控制了group convolution中的分组数,分组越多,在相同计算资源下,可以使用更多的通道数,所以g越大时,采用了更多的卷积核。

                                                                                                   

                                                                                                                                                              shufflenet v1网络结构图

 

 

 

2.shufflenet V2

shufflenet v2在v1的基础上通过大量的实验提出了四条设计轻量神经网络的准则:

(a)1x1卷积进行平衡输入和输出的通道大小;

(b)group convolution要谨慎使用,注意分组数;

(c)避免网络的碎片化;

          网络碎片化会降低模型的额并行度 一些网络如Inception,它们倾向于采用“多路”结构,即存在一个block中很多不同的小卷积或者pooling, 这很容易造成网络碎片化,减低模型的并行度,相应速度会慢.

(d)减少元素级运算,

          对于元素级(element-wise operators)比如ReLU和Add,虽然它们的FLOPs较小,但是却需要较大的MAC(memory access cost, MAC)。 实验发现如果将ResNet中残差单元中的ReLU和shortcut移除的话,速度有20%的提升。

在ShuffleNetv1的模块中,大量使用了1x1组卷积,这违背了b原则, 另外v1采用了类似ResNet中的瓶颈层(bottleneck layer), 输入和输出通道数不同,这违背了a原则。同时使用过多的组,也违背了c原则。 skip connection中存在大量的元素级Add运算,这违背了d原则。

根据前面的四条准则提出了shufflenet v2,为了改善v1的缺陷,v2版本在非下采样模块中引入了一种新的运算:channel split。具体来说,在开始时先将输入特征图在通道维度分成两个分支, 在论文和代码中的体现则是将特征图在channel维度上平均分成两部分.

 

 

                                                                                                    

                                                                                                                                                                     shufflenet v2基本模块图 

 

shufflenet v2的pytorch实现代码如下:

#本来这个代码是要用在目标检测中的,之后和FPN级联所以输出了三个feature map,懒得修改了,一样能看
import torch
import torch.nn as nn
import torch.utils.model_zoo as model_zoo
from nanodet.model.module.activation import act_layers

model_urls = {
    'shufflenetv2_0.5x': 'https://download.pytorch.org/models/shufflenetv2_x0.5-f707e7126e.pth',
    'shufflenetv2_1.0x': 'https://download.pytorch.org/models/shufflenetv2_x1-5666bf0f80.pth',
    'shufflenetv2_1.5x': None,
    'shufflenetv2_2.0x': None,
}


def channel_shuffle(x, groups):
    # type: (torch.Tensor, int) -> torch.Tensor
    batchsize, num_channels, height, width = x.data.size()
    channels_per_group = num_channels // groups

    # reshape
    x = x.view(batchsize, groups,
               channels_per_group, height, width)

    x = torch.transpose(x, 1, 2).contiguous()

    # flatten
    x = x.view(batchsize, -1, height, width)

    return x


class ShuffleV2Block(nn.Module):
    def __init__(self, inp, oup, stride, activation='ReLU'):
        super(ShuffleV2Block, self).__init__()

        if not (1 <= stride <= 3):
            raise ValueError('illegal stride value')
        self.stride = stride

        branch_features = oup // 2
        assert (self.stride != 1) or (inp == branch_features << 1)

        if self.stride > 1:
            self.branch1 = nn.Sequential(
                self.depthwise_conv(inp, inp, kernel_size=3, stride=self.stride, padding=1),
                nn.BatchNorm2d(inp),
                nn.Conv2d(inp, branch_features, kernel_size=1, stride=1, padding=0, bias=False),
                nn.BatchNorm2d(branch_features),
                act_layers(activation),
            )
        else:
            self.branch1 = nn.Sequential()

        self.branch2 = nn.Sequential(
            nn.Conv2d(inp if (self.stride > 1) else branch_features,
                      branch_features, kernel_size=1, stride=1, padding=0, bias=False),
            nn.BatchNorm2d(branch_features),
            act_layers(activation),
            self.depthwise_conv(branch_features, branch_features, kernel_size=3, stride=self.stride, padding=1),
            nn.BatchNorm2d(branch_features),
            nn.Conv2d(branch_features, branch_features, kernel_size=1, stride=1, padding=0, bias=False),
            nn.BatchNorm2d(branch_features),
            act_layers(activation),
        )

    @staticmethod
    def depthwise_conv(i, o, kernel_size, stride=1, padding=0, bias=False):
        return nn.Conv2d(i, o, kernel_size, stride, padding, bias=bias, groups=i)

    def forward(self, x):
        if self.stride == 1:
            x1, x2 = x.chunk(2, dim=1)
            out = torch.cat((x1, self.branch2(x2)), dim=1)
        else:
            out = torch.cat((self.branch1(x), self.branch2(x)), dim=1)

        out = channel_shuffle(out, 2)

        return out


class ShuffleNetV2(nn.Module):
    def __init__(self,
                 model_size='1.5x',
                 out_stages=(2, 3, 4),
                 with_last_conv=False,
                 kernal_size=3,
                 activation='ReLU'):
        super(ShuffleNetV2, self).__init__()
        print('model size is ', model_size)

        self.stage_repeats = [4, 8, 4]
        self.model_size = model_size
        self.out_stages = out_stages
        self.with_last_conv = with_last_conv
        self.kernal_size = kernal_size
        self.activation = activation
        if model_size == '0.5x':
            self._stage_out_channels = [24, 48, 96, 192, 1024]
        elif model_size == '1.0x':
            self._stage_out_channels = [24, 116, 232, 464, 1024]
        elif model_size == '1.5x':
            self._stage_out_channels = [24, 176, 352, 704, 1024]
        elif model_size == '2.0x':
            self._stage_out_channels = [24, 244, 488, 976, 2048]
        else:
            raise NotImplementedError

        # building first layer
        input_channels = 3
        output_channels = self._stage_out_channels[0]
        self.conv1 = nn.Sequential(
            nn.Conv2d(input_channels, output_channels, 3, 2, 1, bias=False),
            nn.BatchNorm2d(output_channels),
            act_layers(activation),
        )
        input_channels = output_channels#24

        self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)

        stage_names = ['stage{}'.format(i) for i in [2, 3, 4]]
        for name, repeats, output_channels in zip(
                stage_names, self.stage_repeats, self._stage_out_channels[1:]):
            seq = [ShuffleV2Block(input_channels, output_channels, 2, activation=activation)]
            for i in range(repeats - 1):
                seq.append(ShuffleV2Block(output_channels, output_channels, 1, activation=activation))
            setattr(self, name, nn.Sequential(*seq))
            input_channels = output_channels
        output_channels = self._stage_out_channels[-1]
        if self.with_last_conv:
            self.conv5 = nn.Sequential(
                nn.Conv2d(input_channels, output_channels, 1, 1, 0, bias=False),
                nn.BatchNorm2d(output_channels),
                act_layers(activation),
            )
            self.stage4.add_module('conv5', self.conv5)
        self._initialize_weights()

    def forward(self, x):
        x = self.conv1(x)
        x = self.maxpool(x)
        output = []
        for i in range(2, 5):
            stage = getattr(self, 'stage{}'.format(i))
            x = stage(x)
            if i in self.out_stages:#[2,3,4]
                output.append(x)
        return tuple(output)

    def _initialize_weights(self, pretrain=True):
        print('init weights...')
        for name, m in self.named_modules():
            if isinstance(m, nn.Conv2d):
                if 'first' in name:
                    nn.init.normal_(m.weight, 0, 0.01)
                else:
                    nn.init.normal_(m.weight, 0, 1.0 / m.weight.shape[1])
                if m.bias is not None:
                    nn.init.constant_(m.bias, 0)
            elif isinstance(m, nn.BatchNorm2d):
                nn.init.constant_(m.weight, 1)
                if m.bias is not None:
                    nn.init.constant_(m.bias, 0.0001)
                nn.init.constant_(m.running_mean, 0)
            elif isinstance(m, nn.BatchNorm1d):
                nn.init.constant_(m.weight, 1)
                if m.bias is not None:
                    nn.init.constant_(m.bias, 0.0001)
                nn.init.constant_(m.running_mean, 0)
            elif isinstance(m, nn.Linear):
                nn.init.normal_(m.weight, 0, 0.01)
                if m.bias is not None:
                    nn.init.constant_(m.bias, 0)
        if pretrain:
            url = model_urls['shufflenetv2_{}'.format(self.model_size)]
            if url is not None:
                pretrained_state_dict = model_zoo.load_url(url)
                print('=> loading pretrained model {}'.format(url))
                self.load_state_dict(pretrained_state_dict, strict=False)


if __name__ == "__main__":
    model = ShuffleNetV2(model_size='1.0x', )
    print(model)
    test_data = torch.rand(5, 3, 320, 320)
    test_outputs = model(test_data)
    for out in test_outputs:
        print(out.size())

 

 

 

 

 

 

 

 

reference:

1.https://zhuanlan.zhihu.com/p/32304419

2.https://link.zhihu.com/?target=https%3A//arxiv.org/pdf/1707.01083.pdf

3.https://zhuanlan.zhihu.com/p/48261931

4.https://arxiv.org/abs/1807.11164

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

轻量型神经网络 shufflenet V1和shufflenet V2 的相关文章

  • 虚拟USB设备总结

    开发环境 xff1a windows 首先来总结最近研究的虚拟USB设备 xff0c 进而虚拟USB键盘成功了 xff0c 开心 xff01 得出了一个C S框架 xff0c 首先说一下客户端 客户端有两个部分 xff0c 用户空间工具和底
  • C#Winform:《DataGridViewComboBoxCell值无效》解决方案

    值无效 xff0c 可能是你下拉框选项 xff0c 没有这样的值 xff0c 而你却设置这个值 dataGridView1 Rows i Cells 1 Value 61 Hello World 解决方法就是在窗体的构造函数里添加如下代码
  • FFmpeg笔记

    1 下载 xff0c 配置 FFmpeg官网 xff1a https ffmpeg org 用的系统是Ubuntu18 04 所以直接apt get就可以了 sudo apt get install ffmpeg 2 简介 xff0c 上手
  • 《WPF中TextBox绑定Double类型数据,文本框不能输入小数点》解决方案

    在App cs文件里面 xff0c 重写OnStatup xff0c 添加下面一条语句即可 span class token keyword public span span class token keyword partial span
  • stm32 HAL库串口收发-中断接收DMA发送不定长数据

    使用的时候发现 xff1a 接收完一个字节立即用DMA的方式发送出去 xff0c 会出现数据的丢失 xff0c 如用串口调试助手发送1234 xff0c 返回的只有13 目前只能用缓存buf 43 协议结束 xff08 如0x0d 0x0a
  • headers Authorization

    var auth 61 96 host user host pass 96 const buf 61 Buffer from auth 39 ascii 39 strauth 61 buf toString 39 base64 39 con
  • 平衡车入门---MPU6050陀螺仪的使用

    平衡车入门 MPU6050陀螺仪的使用 一 MPU6050简介二 学习MPU6050的步骤三 I2C协议简介四 MPU6050硬件介绍五 MPU6050的几个重要寄存器六 原始数据的单位换算七 角度换算 滤波算法 一 MPU6050简介 M
  • C++ 为什么基类的析构函数要声明为虚函数

    1 为什么声明基类析构函数为虚函数 xff1f xff08 1 xff09 基类指针 指向 基类对象 xff1a 不用考虑基类析构函数是否声明为虚函数 xff08 2 xff09 基类指针 指向 派生类对象 xff1a 若基类析构函数不为虚
  • std::map find和count效率测试

    1 简介 在使用标准模板库中的map容器且遇到键值对的值为自定义struct或class类型时 xff0c 考虑到特殊场景 xff08 即不能确保key自始至终唯一 xff09 xff0c 若插入新元素 xff08 new 对象 xff09
  • 随机生成8位长字符串(大小写字母及数字组合)

    1 简要说明 项目上开发要用到随机生成一个8位长的字符串 xff08 类似Java工具类中的UUID xff09 xff0c 作为id来对同一事物的不同个体进行唯一标识 xff0c 如同一个班级里学生名字几乎不同 xff0c 偶尔会有重复
  • C++引用和指针区别

    1 C 43 43 引用和指针区别 xff1a 指针是一个新的变量 xff0c 指向另一个变量的地址 xff0c 我们可以通过访问这个地址来修改另一个变量 xff1b 而引用是一个别名 xff0c 对引用的操作就是对变量的本身进行操作指针可
  • TCP/UDP端口号

    大家好呀 xff0c 我是请假君 xff0c 今天又来和大家一起学习数通了 xff0c 今天要分享的知识是TCP UDP端口号 在IP网络中 xff0c 一个IP地址可以唯一地标识一个主机 但一个主机上却可能同时有多个程序访问网络 要标识这
  • C/C++ 电脑微信dat文件解密及工具分享

    1 前言 最近想整理下照片 xff08 回忆 怀旧 xff09 xff0c 以前也知道在微信pc端聊天时 xff0c 图片 视频 文档等文件会缓存在一个目录下 xff08 电脑微信 左下角三条杠 设置 文件管理 xff09 xff0c 点击
  • ODBC::SQLExecDirect返回-1 错误信息ORA-00604 ORA-01000

    在通过使用微软提供的ODBC SDK读取数据库 xff08 SELECT xff09 时 xff0c 发现Oracle读着读着就读不到数据了 xff08 MySQL和SQL Server是正常的 xff09 xff0c 经调试发现SQLEx
  • std::vector与deque首尾增删及遍历(应用于CListCtrl虚拟列表)混合性能测试

    1 简介 在工作项目中应用MFC类库的CListCtrl刷新加载数据 xff0c 一开始是用InsertItem SetItemText 和DeleteItem 等成员函数来实现数据在列表视图控件中的新增和删除 xff08 最多显示500条

随机推荐

  • Matlab 实用代码集

    本博客将存放一些常用的Matlab代码片段 xff0c 整理成博客 xff0c 并持续更新 xff0c 以便写代码可以调用 1 函数多输入多输出 Matlab写函数的时候 xff0c 输入输出个数经常是不固定的 xff0c narginch
  • 基于c++的A-star算法

    资料 xff1a https www cnblogs com guxuanqing p 9610780 html 一 基础原理 1 从起点开始 xff0c 向周围八个方向扩展 测试新扩展的点的路径代价 xff0c 路径代价由已走的路径和距离
  • 倍增算法

    倍增算法 给定一个整数 M xff0c 对于任意一个整数集合 S xff0c 定义 校验值 如下 从集合 S 中取出 M 对数 即 2 M 个数 xff0c 不能重复使用集合中的数 xff0c 如果 S 中的整数不够 M 对 xff0c 则
  • Numpy学习——数组类型

    Numpy学习 数组类型 更多的数据类型转换 casting 不同数据类型的大小 结构体类型处理丢失的数据 更多的数据类型 转换 casting numpy会自动转换高精度数据类型 xff1a span class token operat
  • 深度学习推荐系统——前深度学习时代

    深度学习推荐系统 前深度学习时代 协同过滤相似度UserCFItemCF矩阵分解矩阵分解的求解过程消除用户和物品打分的偏差矩阵分解的优缺点 逻辑回归特征工程POLY2模型FM模型FFM模型 GDBT 43 LRLS PLM xff08 La
  • 深度学习推荐系统——深度学习时代

    深度学习推荐系统 深度学习时代 AutoRecDeep CrossingNeuralCFPNNWide amp DeepFM与深度学习模型结合FNNDeepFMNFM 注意力机制AFMDIN DIEN总结 AutoRec h r
  • 深度学习推荐系统——Embedding

    深度学习推荐系统 Embedding Embedding概述Word2vecItem2vec Embedding概述 Embedding操作的主要作用是将稀疏向量转换成稠密向量 xff0c 向量之间的距离反映了对象之间的相似性 从另一空间表
  • 堆与栈区别,以及分配内存的快慢

    毫无疑问 xff0c 显然从栈上分配内存更快 xff0c 因为从栈上分配内存仅仅就是栈指针的移动而已 操作系统会在底层对栈提供支持 xff0c 会分配专门的寄存器存放栈的地址 xff0c 栈的入栈出栈操作也十分简单 xff0c 并且有专门的
  • ubuntu下realsence相机通过ros话题直接读取内参

    roslaunch realsense2 camera rs camera span class token punctuation span launch span class token comment 打开相机节点 span rost
  • 语义栅格地图(六) realsense实际测试

    1 在ros中启动realsense 测试输出话题 xff1a roslaunch realsense2 camera rs camera launch 测试输出点云 xff1a roslaunch realsense2 camera rs
  • VINS on RealSense D435i

    关于Realsense D435i运行VINS系列 前言 在SLAM中 xff0c 主要是以激光SLAM和视觉SLAM为主 xff0c 激光雷达直接可以获取三维点云坐标信息 xff0c 所以激光SLAM会比视觉SLAM稳定许多 xff0c
  • CAN 扩展帧和标准帧的适用范围

    刚接触CAN不久 xff0c 对很多CAN相关的知识不了解 xff0c 就难以进行灵活的运用 今天弄懂了CAN的标准帧和扩展帧的使用场合 xff0c 故此做一下笔记 首先 xff0c 得知道为什么会有这两种不同的帧 其实原因和IPV4和IP
  • ZCU104开发板:开发板组件描述

    1 Zynq UltraScale 43 XCUZU7EV MPSoC ZCU104板上安装了Zynq UltraScale 43 XCZU7EV 2FFVC1156 MPSoC xff0c 它在同一设备中集成了功能强大的处理系统 xff0
  • (超简单)Ubuntu/linux上搭建pytorch-gpu环境

    xff08 超简单 xff09 Ubuntu linux上搭建pytorch gpu环境 1 下载miniconda conda 1 下载miniconda conda conda和miniconda可选择在清华镜像源中下载 xff0c 这
  • GAN训练中遇到的mode collapse(模式崩塌)

    1 梯度 loss爆炸 xff08 NaN xff0c Inf xff09 这两天一直在训练自己的GAN模型 xff0c 训练过程中鉴别器极其不稳定 xff0c 训练的几个epoch之后经常出现NAN xff0c 在加入WGAN中的梯度惩罚
  • 如何训练GAN?能够让GAN work的方法和技巧

    如何训练GAN xff1f 能够让GAN work的方法和技巧 尽管在生成对抗网络 xff08 GAN xff09 中的研究继续改善了这些模型的基本稳定性 xff0c 但我们使用了许多技巧来训练它们并使它们日复一日地稳定 xff08 翻译自
  • batch size,学习率(learning rate),and training time

    batch size 学习率 xff08 learning rate xff09 and training time 1 batch size和leaning rate的关系 现在深度学习中的绝大多数算法采用梯度下降法来进行训练 xff0c
  • torch.optim.lr_scheduler:pytorch必须掌握的的4种学习率衰减策略

    梯度下降算法需要我们指定一个学习率作为权重更新步幅的控制因子 xff0c 常用的学习率有0 01 0 001以及0 0001等 xff0c 学习率越大则权重更新 一般来说 xff0c 我们希望在训练初期学习率大一些 xff0c 使得网络收敛
  • 虚拟内存与物理内存,自己理解,删除了一些细节,更容易懂

    其实虚拟内存就是字面意思 xff0c 虚拟保存在磁盘中的 xff0c 我们知道32为操作系统下一个进程是4g大小空间 xff0c 我们物理内存 xff08 也就是我们说的内存条 xff09 xff0c 假如我们以8g内存条定义 假如我们没有
  • 轻量型神经网络 shufflenet V1和shufflenet V2

    1 shufflenet V1 ShuffleNet是旷视科技 Face 43 43 提出的一种计算高效的CNN模型 xff0c 其和MobileNet和SqueezeNet等一样主要是想应用在移动端 所以 xff0c ShuffleNet