深度学习之路=====11=====>>ShuffleNet(tensorflow2)

news2025/7/17 6:01:06

简介

来源:CVPR2017 作者:张祥雨,西安交通大学本硕博,原微软亚洲研究院研究员

特点

  1. 逐点分组卷积(pointwise group conv):使用了kernel_size=1的分组卷积,大大降低模型参数量和计算量
  2. 深度卷积也称逐通道卷积(Depthwise Convolution):区别于深度可分离卷积(depthwise separable convolution==depthwise convoluton+pointwise convolution),进一步降低模型参数

创新点

  • 通道重组(channel shuffle):分组卷积后的特征图的某个通道仅仅来自输入通道的一部分(边界效应),channel shuffle能将这种边界效应消除,使得第二次分组卷积前的每个组的输入都能包含所有组的特征图。

分组卷积

通过下图可明显看到分组卷积带来的参数量和计算量降低,下面通过公式定量分析分组卷积带来的降参优势
设输入特征shape为 M × H × W M\times H\times W M×H×W,卷积核shape为 N × h × w N\times h\times w N×h×w
对于普通卷积,参数量为: M × h × w × N M\times h\times w\times N M×h×w×N,计算量为: M × h × w × N × H × W M\times h\times w\times N\times H\times W M×h×w×N×H×W
对于分组卷积,设组数为G,每组输入通道数为 M G \frac{M}{G} GM,每组卷积核数为 N G \frac{N}{G} GN,每组卷积后再CONCAT,
则参数量为: M G × h × w × N G × G \frac{M}{G}\times h\times w\times \frac{N}{G}\times G GM×h×w×GN×G,计算量为: M G × h × w × N G × G × H × W \frac{M}{G}\times h\times w\times \frac{N}{G}\times G\times H\times W GM×h×w×GN×G×H×W
所以分组卷积的参数量和计算量为普通卷积参数量和计算量的 1 G \frac{1}{G} G1

在这里插入图片描述
分组卷积过程为:先将输入split为groups组,每组分组卷积,最后再concat
具体代码为:见下文

深度卷积(depthwise conv)

在深度学习之路=====8=====>>Xception(tensorflow2)中对深度卷积进行了介绍,这里不再介绍,简而言之,深度卷积就是分组数等于输入通道数的分组卷积,且每个组卷积核数为1,即卷积核数于输入通道数相等。

通道重组(channel shuffle)

下图为通道重排示意图,首先将特征图的通道维度reshape为 ( g r o u p s , c h a n n e l s / g r o u p s ) \left(groups,channels/groups\right) (groups,channels/groups),再将其在这两个维度上进行转置,最后再reshape为原shape:
具体代码为:

  def channel_shuffle(self,input,groups):
        n, h, w, c = input.shape
        x= tf.reshape(input, [-1, h, w, groups, c // groups])
        x = tf.transpose(x, [0, 1, 2, 4, 3])
        output = tf.reshape(x, [-1, h, w, c])
        return output

请添加图片描述
下图为shuffleNet基本模块,由1X1->3x3->1x1的bottleneck结构变化而来,主要为了降低参数量,Bottleneck 三步走是先 pointwise conv 对数据进行降维(降低通道数),再进行常规卷积核的卷积,最后 pointwise conv 对数据进行升维(将通道数恢复为输入时的Channels)。

在这里插入图片描述

ShuffleNet 网络结构

在Stage2, Stage3,Stage4)中,先使用步长为 2 的 ShuffleNet Unit(上图中的(c)),又堆叠若干个个步长为 1 的 ShuffleNet Unit(上图中的(b))。另外,某一个 stage 的输出通道数是上一个 stage 的输出通道数的两倍,且在每个 Unit 里,设置 Bottleneck 的通道数是输出通道数的四分之一
在这里插入图片描述

import tensorflow as tf
import numpy as np
from tensorflow.keras.layers import *
from tensorflow.keras import Model

class Conv(Model):
    def __init__(self,filters,kernel_size,strides):
        super().__init__()
        self.layers_list=[]
        self.layers_list.append(Conv2D(filters=filters,kernel_size=kernel_size,strides=strides,padding='same'))
        self.layers_list.append(BatchNormalization())
        self.layers_list.append(Activation('relu'))
    def call(self,x):
        for layer in self.layers_list:
            x=layer(x)
        return x

class ShuffleNet_unit(Model):
    def __init__(self,filters,in_channels,groups,mode=0):
        super().__init__()
        self.mode=mode
        self.groups=groups
        if self.mode==0:
            self.filters=filters
        else:
            self.filters=filters-in_channels
        self.bottleneck_filters=self.filters//4
        self.gc_list1=[]
        for i in range(groups):
            self.gc_list1.append(Conv2D(self.bottleneck_filters//groups,kernel_size=1,strides=1,padding='same'))
        self.b1=BatchNormalization()
        self.a1=Activation('relu')
        if self.mode==0:
            self.dwc=DepthwiseConv2D(kernel_size=3,strides=1,padding='same')
        else:
            self.dwc=DepthwiseConv2D(kernel_size=3,strides=2,padding='same')
        self.gc_list2=[]
        for i in range(groups):
            self.gc_list2.append(Conv2D(self.filters//groups,kernel_size=1,strides=1,padding='same'))
        if self.mode==1:
            self.residual=AveragePooling2D(pool_size=(3,3),strides=2,padding='same')
        self.a_last=Activation('relu')
        
    def call(self,x):
        #print(x.shape)
        if self.mode==0:
            residual=x
        else:
            residual=self.residual(x)
        x_list1=tf.split(x,self.groups)
        ##第一个逐点分组卷积
        for i in range(len(self.gc_list1)):
            x_list1[i]=self.gc_list1[i](x_list1[i])
        x=tf.concat(x_list1,-1)
        #print(x.shape)
        ##通道重组
        x=self.channel_shuffle(x,self.groups)
        x=self.dwc(x)
        x_list2=tf.split(x,self.groups)
        ##第二个逐点分组卷积
        for i in range(len(self.gc_list2)):
            x_list2[i]=self.gc_list2[i](x_list2[i])
        x=tf.concat(x_list2,-1)
        #print(x.shape)
        #print('------next unit-----')
        if self.mode==0:
            y=x+residual
        else:
            y=tf.concat([x,residual],-1)
        return y
            
    def channel_shuffle(self,input,groups):
        n, h, w, c = input.shape
        x= tf.reshape(input, [-1, h, w, groups, c // groups])
        x = tf.transpose(x, [0, 1, 2, 4, 3])
        output = tf.reshape(x, [-1, h, w, c])
        return output
class stage(Model):
    def __init__(self,out_channels,in_channels,repeat,groups):
        super().__init__()   
        self.unit_s2=ShuffleNet_unit(out_channels,in_channels,groups,1)
        self.unit_s1=[]
        for i in range(repeat):
            self.unit_s1.append(ShuffleNet_unit(out_channels,in_channels,groups,0))
    def call(self,x):
        x=self.unit_s2(x)
        for unit in self.unit_s1:
            x=unit(x)
            print(x.shape)
        return x
          
class Shuffle_Net(Model):
    def __init__(self,num_filters,in_channels,repeat_list):
        super().__init__()
        
        self.layers_list=[]
        self.layers_list.append(Conv(24,3,2))
        self.layers_list.append(MaxPooling2D(pool_size=(3,3),strides=2,padding='same'))
        self.out_channels=num_filters
        self.in_channels=in_channels
        for i,repeat in enumerate(repeat_list):
            
            channels_in_stage=self.in_channels[i]
            #print("this stage: in channels is %d,out channel is %d,repeats is %d >>>>>>>>>>>>"%(self.in_channels[i],self.out_channels,repeat))
            self.layers_list.append(stage(self.out_channels,channels_in_stage,repeat,1))
            self.out_channels *=2

        self.layers_list.append(GlobalAveragePooling2D())
        self.layers_list.append(Dense(1000))
    def call(self,x):
        for layer in self.layers_list:
            x=layer(x)
        return x
##最后,还是验证模型正确性
model=Shuffle_Net(144,[24,144,288,576],[3,7,3])
inputs = np.zeros((1, 224, 224, 3), np.float32)
print(inputs.shape)
model(inputs)
model.summary()
##输出:
#####与表格中网格结构一致
(1, 224, 224, 3)
(1, 28, 28, 144)
(1, 28, 28, 144)
(1, 28, 28, 144)
(1, 14, 14, 288)
(1, 14, 14, 288)
(1, 14, 14, 288)
(1, 14, 14, 288)
(1, 14, 14, 288)
(1, 14, 14, 288)
(1, 14, 14, 288)
(1, 7, 7, 576)
(1, 7, 7, 576)
(1, 7, 7, 576)
##下面是sumarry
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv_10 (Conv)               multiple                  768       
_________________________________________________________________
max_pooling2d_10 (MaxPooling multiple                  0         
_________________________________________________________________
stage_30 (stage)             multiple                  37494     
_________________________________________________________________
stage_31 (stage)             multiple                  308772    
_________________________________________________________________
stage_32 (stage)             multiple                  546696    
_________________________________________________________________
global_average_pooling2d_10  multiple                  0         
_________________________________________________________________
dense_10 (Dense)             multiple                  577000    
=================================================================
Total params: 1,470,730
Trainable params: 1,470,682
Non-trainable params: 48
________________________________________________

bottlenet介绍
ShuffleNet算法详解
轻量级神经网络——shuffleNet

Tensorflow笔记——channel shuffle的实现

keras实现分组卷积
ShuffleNet V1 网络结构的原理与 Tensorflow2.0 实现

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

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

相关文章

阅读书《电子电路原理》截取的一些最核心的思想,找了个课程上海交通大学 郑益慧主讲做辅助(保证基本的理解是对的)。电路要以基本特性为基础从设计角度理解

一、戴维南 和 诺顿 物理量 过程戴维南等效诺顿等效步骤 l将负载电阻开路将负载电阻短路步骤 2计算或测量开路电 压, 即戴维南电压计算或测量短路电流,即诺顿电流步骤 3将电压源短路,电流源开路将电压源短路,电流源开路&#xff…

学生网页课程设计期末作业 HTML+CSS+JavaScript甜品蛋糕网页设计(5页)

🎀 精彩专栏推荐👇🏻👇🏻👇🏻 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 💂 作者主页: 【主页——🚀获取更多优质源码】 🎓 web前端期末大作业…

如何使用 Nginx 部署 React App 到 linux server

油鹳视频:How To Deploy A React App - Using NGINX & Linux https://www.youtube.com/watch?vKFwFDZpEzXY&t547s 1. 获得一个 linux server 方法很多种,例如 aws EC2 , 阿里云 ECS , linode 等 2. 登录远程服务器并设置服务器 命…

二十八、CANdelaStudio实践-10服务(SessionControl)

本专栏将由浅入深的展开诊断实际开发与测试的数据库编辑,包含大量实际开发过程中的步骤、使用技巧与少量对Autosar标准的解读。希望能对大家有所帮助,与大家共同成长,早日成为一名车载诊断、通信全栈工程师。 本文介绍10服务(DiagnosticSessionControl)的查看与编辑,欢迎…

看我如何连夜自建网站背刺我的求职对手们

摘要:在竞争如此激烈的当下,作为一名IT新人,怎么才能让HR眼前一亮,从万千简历中脱颖而出成为最亮的那个崽呢?本文分享自华为云社区《【一行代码秒上云】连夜自建网站背刺我的求职对手们 !》,作者…

红队隧道加密之OpenSSL加密反弹Shell(一)

前言 在红队的后渗透阶段中, 往往需要反弹shell进行下一步的内网横向渗透, 而这种反弹shell大多数都有一个缺点, 那就是其传输的流量都是明文传输, 相当容易被内网的防火软件所捕捉, 要是蓝队对此流量进行朔源分析, 很容易就能复现攻击的整个流程 例如此处用netcat反弹Shell进…

十、CANdelaStudio入门-States

本专栏将由浅入深的展开诊断实际开发与测试的数据库编辑,包含大量实际开发过程中的步骤、使用技巧与少量对Autosar标准的解读。希望能对大家有所帮助,与大家共同成长,早日成为一名车载诊断、通信全栈工程师。 本文介绍CANdelaStudio的States概念,欢迎各位朋友订阅、评论,可…

AcWing245. 你能回答这些问题吗 线段树详解

3.2线段树 例题分析 245. 你能回答这些问题吗 - AcWing题库 **题意:**给一条序列,如何动态维护区间的最大子段和,包括询问某区间的最大字段和和修改某个数。 分析:线段树struct保留什么信息。能否通过左右儿子的这些信息求出父…

HA RabbitMQ on K8s helm部署实战

RabbitMQ on K8s helm部署实战获取helm chart修改必要参数外部如何访问?安装rabbitmq登录管理界面导入mq metadata优化helm chart 生成的statefuleset yamlhelm chart 生成的pod yaml调整mq log等级promethrus 监控mq配置prometheus采集metrics查看prometheus rabbi…

零基础借助arpl自动编译工具在一小时之内将旧笔记本、嵌入式设备等改造成黑群辉

文章目录1. 前言2. 编译2.1. 到GitHub把编译引导需要用的img文件下载到电脑上2.2. 将下载下来的IMG文件解压为img文件,用rufus或者其他写盘工具写入到U盘中,2.3. 进行引导启动2.4. 获得编译系统的IP地址2.5. 在同一局域网下,打开这个IP地址2.…

Java学习笔记 --- IO流

一、文件 什么是文件 文件是保存数据的地方 文件流 文件在程序中是以流的形式来操作的 流:数据在数据源(文件)和程序(内存)之间经历的路径 输入流:数据从数据源(文件)到程序&a…

Pr:文本面板之转录文本

Pr菜单:窗口/文本Text在文本面板的转录文本 Transcript选项卡中,单击“转录序列” Transcribe sequence按钮,然后设置好选项并进行语音到文本的转录。创建转录文本对话框Create transcript语言Language选择视频中语音所使用的语言。也可选择不…

2.7 基本放大电路的派生电路

在实际应用中,为了进一步改善放大电路的性能,可用多只晶体管构成复合管来取代基本电路中的一只晶体管;也可根据需要将两种基本接法组合起来,以得到多方面性能俱佳的放大电路。 一、复合管放大电路 1、复合管 (1&…

【路径规划】(1) Dijkstra 算法求解最短路,附python完整代码

好久不见,我又回来了,这段时间把路径规划的一系列算法整理一下,感兴趣的点个关注。今天介绍一下机器人路径规划算法中最基础的 Dijkstra 算法,文末有 python 完整代码,那我们开始吧。 1. 算法介绍 1959 年&#xff0c…

Bugku MISC 啊哒 贝斯手

啊哒 下载文件,解压后发现是一张图片,用010editor打开 可以看到里面有个flag.txt 。使用kali中的binwalk进行文件分离 查看文件 : binwalk ada.jpg 分离文件 : binwalk -e ada.jpg --run-asroot 打开分离后的文件夹 可以看到有一…

T288401 B-莲子的机械动力学

专攻超统一物理学的莲子,对机械结构的运动颇有了解。如下图所示,是一个三进制加法计算器的(超简化)示意图。 一个四位的三进制整数,从低到高位,标为 x_1,x_2,x_3,x_4x1​,x2​,x3​,x4​。换言之&#xff0…

第八章《Java高级语法》第12节:Lambda表达式

Lambda 表达式是 JDK8 的一个新特性,它可以定义大部分的匿名内部类,从而让程序员能写出更优雅的Java代码,尤其在集合的各种操作中可以极大地优化代码结构。 8.12.1 认识Lambda表达式 一个接口的实现类可以被定义为匿名类。经过大量实践,人们发现定义一个接口的匿名实现类…

ADAU1860调试心得(8)FASTDSP-0 通道输入

这个程序,我们正式要用到 DSP 了,ADC 进来的数据,经过 FASTDSP 的算法进行处理,再 送给 DAC 推到耳机,通道我们输入 0 到输出为例,还是先做直通,DSP 路过一下,并不做处理。 首先是寄…

WebStorm创建第一个Express项目

WebStorm创建Express项目步骤如下: 1、在WebStorm创建项目 选择项目存储位置,然后点击create,再选择创建的窗口,一般都是创建在this window上 2、进入窗口会终端会开始下载Express项目所需要的文件,我们等到出现如下图…

C++中的多态(下)

🧸🧸🧸各位大佬大家好,我是猪皮兄弟🧸🧸🧸 文章目录一、C11当中的final和overridefinaloverride二、重载&重定义(隐藏)&重写(覆盖)三、抽象类(接口类)四、接口继…