ShuffleNetV2网络结构讲解(TensorFlow-2.6.0实现结构)

ShuffleNetV2网络结构讲解(TensorFlow-2.6.0实现结构),第1张

文章目录
    • 1.论文地址
    • 2.ShuffleNetV1网络结构
    • 3.旷视轻量化卷积神经网络ShuffleNetV2
      • 引出
      • (1)四种不同的模型在两种硬件平台上的结果
      • (2)两个模型在两种硬件平台上的模型计算时间的对比
      • (3)神经网络参数量,计算量FLOPS,内存访问量MAC
    • 4.轻量化网络设计原则
      • (1)输入输出通道数相同时,内存访问量MAC最小
      • (2)分组数过大的分组卷积会增加MAC
      • (3)碎片化的 *** 作对并行加速并不友好
      • (4)逐元素 *** 作(Element-wise)带来的内存和耗时不可忽略
    • 5.ShuffleNetV2模型结构
    • 6.实验结果对比
    • 7.TensorFlow-2.6.0实现网络结构ShuffleNetV2

1.论文地址

1.https://arxiv.org/pdf/1807.11164.pdf


2.ShuffleNetV1网络结构

https://mydreamambitious.blog.csdn.net/article/details/124675932


3.旷视轻量化卷积神经网络ShuffleNetV2 引出

ShuffleNet v2指出使用FLOPs来衡量模型的速度只是间接的,因为一个模型的实际的运行时间除了要考虑计算量之外,还需要考虑如内存的访问量MAC,GPU的并行性,文件IO等 *** 作。论文指出要想衡量一个模型的推理速度,最直接的方式就是在真实世界中进行检测,也就是在具体的平台进行测试,在不同硬件上和不同的模型之间进行比较,从而达到测试模型的效果。

(1)四种不同的模型在两种硬件平台上的结果


在Imagenet分类数据集上进行准确率的验证,速度和FLOPs在四个不同的网络结构上两种硬件平台和四种不同的计算复杂度进行测试,发现我们提出的shuffleNetV2算法表现最好,并且都在其他所有模型的右上角。

结论:相同的FLOPs情况下,运算速度差异也非常的大,说明了单单只是用FLOPs衡量模型的速度是不够的,需要综合的考虑,因为其他方面也会影响速度,比如内存访问量MAC。所以不能用间接说明直接影响。

(2)两个模型在两种硬件平台上的模型计算时间的对比


(1)乘-加法浮点运算次数FLOPs仅反映卷积层,仅为间接指标;
(2)不同硬件上的测试结果不同;
(3)数据读写的内存MAC占用影响很大;
(4)Element-wise逐元素 *** 作带来的开销不可忽略。

注:FLOPS: 全大写,指每秒浮点运算次数,可以理解为计算的速度。是衡量硬件性能的一个指标。(硬件)
FLOPs: s小写,指浮点运算数,理解为计算量。可以用来衡量算法/模型的复杂度。(模型) 在论文中常用GFLOPs(1 GFLOPs = 10^9 FLOPs)

(3)神经网络参数量,计算量FLOPS,内存访问量MAC

https://mydreamambitious.blog.csdn.net/article/details/124639804


4.轻量化网络设计原则 (1)输入输出通道数相同时,内存访问量MAC最小


相同FLOPs的情况下:四个输出与输出通道不同的比例在GPU和ARM上的推理速度比较,可以看到当输入输出通道数为1:1的时候在GPU和ARM上的推理速度最快

(2)分组数过大的分组卷积会增加MAC



分组卷积会增加内存访问量MAC:假设分组为g,相同特征图输入尺寸大小h x w x c和计算量B:
可以看到上面的MAC计算公式和表,发现随着分组的g越大,内存访问量MAC也越来越大。
上面的表对比了在不同硬件上和不同的分组数,随着分组的g越大,推理(inference)的速度也越慢(主要是GPU)。

(3)碎片化的 *** 作对并行加速并不友好



从上面的表可以看到,随着碎片化增加,在硬件上的推理速度也越慢(特别是GPU),当通道数(channel)越小的时候影响越大。

(4)逐元素 *** 作(Element-wise)带来的内存和耗时不可忽略


逐元素的 *** 作对内存的开销也比较大,所以也不能忽视,可以看到当ReLU和“shortcut”都不使用的时候,推理的速度是最快的。


5.ShuffleNetV2模型结构



说明:
(c)首先是通道数分半(channel split):
右边的一半经过1x1卷积,之后经过BN,ReLU;再经过3x3的深度可分离卷积 *** 作步长stride为1,之后经过BN;最后经过1x1卷积,之后经过BN,ReLU。卷积完之后就是堆叠(concat) *** 作和channel shuffle;

(d)不使用通道数分半(channel split):
左边经过一个3x3的深度可分离卷积 *** 作,步长stride为2,经过BN,最后经过1x1卷积,之后经过BN,ReLU;

右边的一半经过1x1卷积,之后经过BN,ReLU;再经过3x3的深度可分离卷积 *** 作,步长stride为1,之后经过BN;最后经过1x1卷积,之后经过BN,ReLU。卷积完之后就是堆叠(concat) *** 作和channel shuffle;


6.实验结果对比


可以发现ShuffleNetV2-50模型FLOPs最小,同时top-1错误率也相对较小;而SE-ShuffleNetV2-164的top-1错误率虽然是最小的,但是FLOPs很大。


ShuffleNetV2在各个FLOPs上的MAP是最好的,同时推理速度也是非常快的。


7.TensorFlow-2.6.0实现网络结构ShuffleNetV2
import os
import keras
import numpy as np
import tensorflow as tf
from tensorflow.keras import layers
from tensorflow.keras.models import Model

# #通道重排
def Shuffle_Channels(inputs):
    #获取图片batch,高度,宽度,通道数
    batch,h,w,c=inputs.shape
    #对通道进行变形
    input_reshape=tf.reshape(inputs,[-1,h,w,2,c//2])
    #退通道进行转置
    input_transposed=tf.transpose(input_reshape,[0,1,2,4,3])
    #再对通道进行变形并且将通道分为两组
    output_reshape=tf.reshape(input_transposed,[2,-1,h,w,c//2])
    return output_reshape[0],output_reshape[1]

#按照原文的意思,将channel split,concat和channel shuffle放在一起
class ShuffleNetV2UnitC(tf.keras.Model):
    def __init__(self,inp,oup):
        super(ShuffleNetV2UnitC, self).__init__()

        inp = inp//2
        output_channel=oup-inp

        self.conv11=layers.Conv2D(oup//2,kernel_size=[1,1],strides=[1,1],padding='same')
        self.conv11BN=layers.BatchNormalization()
        self.conv11relu=layers.Activation('relu')

        self.depthwise=layers.DepthwiseConv2D(kernel_size=[3,3],strides=[1,1],padding='same')
        self.depthwiseBN=layers.BatchNormalization()

        self.conv22=layers.Conv2D(output_channel,kernel_size=[1,1],strides=[1,1],padding='same')
        self.conv22BN=layers.BatchNormalization()
        self.conv22relu=layers.Activation('relu')

    def call(self,inputs,training=None):

        x0,x1=Shuffle_Channels(inputs)

        x=self.conv11(x1)
        x=self.conv11BN(x)
        x=self.conv11relu(x)

        x=self.depthwise(x)
        x=self.depthwiseBN(x)

        x=self.conv22(x)
        x=self.conv22BN(x)
        x=self.conv22relu(x)

        x_out=tf.concat([
            x0,x
        ],axis=3)
        return x_out


class ShuffleNetV2UnitD(tf.keras.Model):
    def __init__(self,inp,oup):
        super(ShuffleNetV2UnitD, self).__init__()

        output_channel=oup-inp

        #right
        self.conv11=layers.Conv2D(oup//2,kernel_size=[1,1],strides=[1,1],padding='same')
        self.conv11BN=layers.BatchNormalization()
        self.conv11relu=layers.Activation('relu')

        self.depthwiseright=layers.DepthwiseConv2D(kernel_size=[3,3],strides=[2,2],padding='same')
        self.depthwiserightBN=layers.BatchNormalization()

        self.conv22=layers.Conv2D(output_channel,kernel_size=[1,1],strides=[1,1],padding='same')
        self.conv22BN=layers.BatchNormalization()
        self.conv22relu=layers.Activation('relu')

        #left
        self.depthwiseleft=layers.DepthwiseConv2D(kernel_size=[3,3],strides=[2,2],padding='same')
        self.depthwiseleftBN=layers.BatchNormalization()

        self.conv11left=layers.Conv2D(inp,kernel_size=[1,1],strides=[1,1],padding='same')
        self.conv11leftBN=layers.BatchNormalization()
        self.conv11leftrelu=layers.Activation('relu')

    def call(self,inputs,training=None):

        xright=self.conv11(inputs)
        xright=self.conv11BN(xright)
        xright=self.conv11relu(xright)

        xright=self.depthwiseright(xright)
        xright=self.depthwiserightBN(xright)

        xright=self.conv22(xright)
        xright=self.conv22BN(xright)
        xright=self.conv22relu(xright)

        xleft=self.depthwiseleft(inputs)
        xleft=self.depthwiseleftBN(xleft)

        xleft=self.conv11left(xleft)
        xleft=self.conv11leftBN(xleft)
        xleft=self.conv11leftrelu(xleft)

        x_out=tf.concat([
            xleft,xright
        ],axis=3)
        return x_out

class ShuffleNetV2(tf.keras.Model):
    def __init__(self,inputFilter,numlayers,StageFilter):
        super(ShuffleNetV2, self).__init__()
        self.conv11=layers.Conv2D(inputFilter,kernel_size=[3,3],strides=[2,2],padding='same')
        self.conv11BN=layers.BatchNormalization()
        self.conv11relu=layers.Activation('relu')

        self.maxpool=layers.MaxPool2D(pool_size=[3,3],strides=[2,2],padding='same')

        self.stage2=self.StageX(numlayers[0],inputFilter,StageFilter[0],2)
        self.stage3 = self.StageX(numlayers[1],StageFilter[0] ,StageFilter[1],3)
        self.stage4 = self.StageX(numlayers[2],StageFilter[1] ,StageFilter[2],4)

        self.conv55=layers.Conv2D(1024,kernel_size=[1,1],strides=[1,1],padding='same')

        self.globalAvgeragePool=layers.GlobalAveragePooling2D()
        self.dense=layers.Dense(1000)
        self.dropout=layers.Dropout(0.5)
        self.softmax=layers.Activation('softmax')


    def StageX(self,numlayers,inputFilter,outFilter,i):
        stage=keras.Sequential([],name='stage'+str(i))
        stage.add(
            ShuffleNetV2UnitD(inp=inputFilter,oup=outFilter)
        )
        for i in range(numlayers):
            inputFilter = outFilter
            stage.add(
                ShuffleNetV2UnitC(inp=inputFilter,oup=outFilter)
            )
        return stage

    def call(self,inputs,training=None):
        x=self.conv11(inputs)
        x=self.conv11BN(x)
        x=self.conv11relu(x)

        x=self.maxpool(x)

        x=self.stage2(x)
        x=self.stage3(x)
        x=self.stage4(x)

        x=self.globalAvgeragePool(x)
        x=self.dense(x)
        x=self.dropout(x)
        x=self.softmax(x)

        return x


model_shufflenetv2=ShuffleNetV2(inputFilter=24,numlayers=[3,7,3],StageFilter=[116,232,464])
model_shufflenetv2.build(input_shape=(None,224,224,3))
model_shufflenetv2.summary()


if __name__ == '__main__':
    print('Pycharm')

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/langs/942652.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-05-18
下一篇2022-05-18

发表评论

登录后才能评论

评论列表(0条)

    保存