MobileNetV2轻量化网络结构(TensorFlow-2.6.0实现结构)

1.论文地址

https://arxiv.org/abs/1801.04381[0]

2.关于MobileNetV1博客

https://mydreamambitious.blog.csdn.net/article/details/124560414[0]

3.MobileNetV1的局限性

(1)没有使用残差连接;
(2)很多Depthwise convolutions之后训练出来的很多都是0;

4.模型之间的对比

(1)MobileNetV1和MobileNetV2


注:MobileNetV2首先使用1×1卷积进行升维,再使用3×3卷积进行Depthwise convolutions(深度卷积),最后再使用1×1卷积进行降维,输出使用线性激活函数。

(2)ResNet和MobileNetV2




解释:ResNet首先使用1×1卷积进行降维,降维之后进行3×3卷积,最后使用1×1卷积进行升维;使用的激活函数是ReLU函数;全部使用非线性激活;连接的是两个高维的张量。
MobileNetV2首先使用1×1卷积进行升维,升维之后进行3×3深度卷积,最后使用1×1卷积进行降维;使用的激活函数是ReLU6函数;在最后的时候使用线性激活;连接的是两个低维的张量。

思考:MobileNetV2中为什么需要先Expansion layer(通道数扩充)?
答:由于深度卷积是不会改变通道数的,所以最终的通道数输入的是多少,那么输出的通道数就是多少;如果通道数很少的话,进行DepthWise时就是在低维度上进行深度可分离卷积,导致最终的效果不是很好,所以使用1×1卷积进行升维,在DepthWise之前使用1×1卷积将通道数升维到原来的6倍(t=6),再在一个高维度的空间进行深度可分离卷积操作。


5.MobileNetV2采用了新的激活函数ReLU6



解释:之所以MobileNetV2将激活函数换成是ReLU6,主要因为如果小于0的数经过ReLU激活函数之后为0,而如果大于0呢,那么输出不变,但是输出的值很可能很大;而且在低精度的情况下ReLU6激活函数比ReLU激活函数的鲁棒性更好。

图片和一些内容来源:
https://zhuanlan.zhihu.com/p/33075914
https://machinethink.net/blog/mobilenet-v2/[0][1]

6.MobileNetV2网络结构



注:使用ReLU6激活函数。

7.为什么很多Depthwise convolutions之后训练出来的很多都是0呢?


8.实验结果对比


可以看到MobileNetV2在和其他模型的参数量相当的情况下,运算量更少,并且在CPU上推断的时间更短。说明了MobileNetV2相对其他的模型是非常的轻量化的。


在COCO目标检测集上的实验对比:MobileNetV2+SSDLite实现了更少的参数量,更少的运算量,实现在CPU上更少的推断。这些实验已经很好的说明了MobileNetV2是一个轻量化的网络模型。

9.TensorFlow实现网络结构

import os
import keras
import numpy as np
import tensorflow as tf
import tensorflow.keras.applications.mobilenet_v2
from tensorflow.keras import layers


class InvertedBottleNeckLayers(tf.keras.Model):
    def __init__(self,in_fitler,expansion_rate,stride):
        super(InvertedBottleNeckLayers, self).__init__()
        self.in_filter=in_fitler
        self.stride=stride
        self.expansion_rate=expansion_rate

        self.conv1=layers.Conv2D(expansion_rate*in_fitler,kernel_size=[1,1],strides=[1,1],padding='same')
        self.conv1Batch=layers.BatchNormalization()
        self.conv1relu6=layers.Activation('relu6')

        self.Dwise=layers.DepthwiseConv2D(kernel_size=[3,3],strides=stride,padding='same')
        self.Dwisebatch=layers.BatchNormalization()
        self.Dwiserelu6=layers.Activation('relu6')


    def call(self,inputs,training=None):
        x=self.conv1(inputs)
        x=self.conv1Batch(x)
        x=self.conv1relu6(x)

        x=self.Dwise(x)
        x=self.Dwisebatch(x)
        x=self.Dwiserelu6(x)
        DwiseChannel=np.shape(x)[-1]

        #如果步长为1且经过深度卷积输出的通道数和输入的通道数相同,
        #则进行shortcut,否则直接输出
        conv22 = layers.Conv2D(self.in_filter, kernel_size=[1, 1], strides=[1, 1], padding='same')(x)
        conv22batch = layers.BatchNormalization()(conv22)
        if self.stride==1 and DwiseChannel==np.shape(inputs)[-1]:
            x_out=tf.add(conv22batch,inputs)
        else:
            x_out=conv22batch
        return x_out

class MobileNetV2(tf.keras.Model):
    def __init__(self,in_fitler=32,num_classes=1000):
        super(MobileNetV2, self).__init__()
        self.conv11=layers.Conv2D(in_fitler,kernel_size=[3,3],strides=[2,2],padding='same')
        self.conv11batch=layers.BatchNormalization()
        self.conv11relu=layers.Activation('relu6')

        #self.bottleneck(t,c,n,c,i)这里的i表示第几个bottleneck
        self.bottleneck1=self.bottleneck(1,16,1,1,1)
        self.bottleneck2 = self.bottleneck(1, 24, 2, 2,2)
        self.bottleneck3 = self.bottleneck(6, 32, 3, 2,3)
        self.bottleneck4 = self.bottleneck(6, 64, 4, 2,4)
        self.bottleneck5 = self.bottleneck(6, 96, 3, 1,5)
        self.bottleneck6 = self.bottleneck(6, 160, 3, 2,6)
        self.bottleneck7 = self.bottleneck(6, 320, 1, 1,7)

        self.conv22=layers.Conv2D(1280,kernel_size=[1,1],strides=[1,1],padding='same')
        self.conv22batch=layers.BatchNormalization()
        self.conv22relu=layers.Activation('relu6')

        self.avgpool=layers.GlobalAveragePooling2D()
        self.dense=layers.Dense(num_classes)
        self.softmax=layers.Activation('softmax')



    def bottleneck(self,t,c,n,s,i):
        bottle=keras.Sequential([],name='bottleneck'+str(i))
        bottle.add(InvertedBottleNeckLayers(c,t,stride=s))
        for i in range(n-1):
            bottle.add(InvertedBottleNeckLayers(c,t,1))
        return bottle

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

        x = self.bottleneck1(x)
        x = self.bottleneck2(x)
        x = self.bottleneck3(x)
        x = self.bottleneck4(x)
        x = self.bottleneck5(x)
        x = self.bottleneck6(x)
        x = self.bottleneck7(x)

        x = self.conv22(x)
        x = self.conv22batch(x)
        x = self.conv22relu(x)
        x = self.avgpool(x)
        x = self.dense(x)
        x = self.softmax(x)

        return x

model_mobilenetv2=MobileNetV2()
model_mobilenetv2.build(input_shape=(None,224,224,3))
model_mobilenetv2.summary()

# def load_MobileNetV2():
#     model_mobilenetv2=tensorflow.keras.applications.mobilenet_v2.MobileNetV2(weights='imagenet',include_top=True)
#     return model_mobilenetv2
#
# model=load_MobileNetV2()
# model.summary()

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


文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
青葱年少的头像青葱年少普通用户
上一篇 2022年5月8日
下一篇 2022年5月8日

相关推荐