https://arxiv.org/abs/1801.04381
https://mydreamambitious.blog.csdn.net/article/details/124560414
(1)没有使用残差连接;
(2)很多Depthwise convolutions之后训练出来的很多都是0;
注:MobileNetV2首先使用1x1卷积进行升维,再使用3x3卷积进行Depthwise convolutions(深度卷积),最后再使用1x1卷积进行降维,输出使用线性激活函数。
解释:ResNet首先使用1x1卷积进行降维,降维之后进行3x3卷积,最后使用1x1卷积进行升维;使用的激活函数是ReLU函数;全部使用非线性激活;连接的是两个高维的张量。
MobileNetV2首先使用1x1卷积进行升维,升维之后进行3x3深度卷积,最后使用1x1卷积进行降维;使用的激活函数是ReLU6函数;在最后的时候使用线性激活;连接的是两个低维的张量。
思考:MobileNetV2中为什么需要先Expansion layer(通道数扩充)?
答:由于深度卷积是不会改变通道数的,所以最终的通道数输入的是多少,那么输出的通道数就是多少;如果通道数很少的话,进行DepthWise时就是在低维度上进行深度可分离卷积,导致最终的效果不是很好,所以使用1x1卷积进行升维,在DepthWise之前使用1x1卷积将通道数升维到原来的6倍(t=6),再在一个高维度的空间进行深度可分离卷积操作
。
注:上面三幅图表示的是同样的意思。
解释:之所以MobileNetV2将激活函数换成是ReLU6,主要因为如果小于0的数经过ReLU激活函数之后为0,而如果大于0呢,那么输出不变,但是输出的值很可能很大;而且在低精度的情况下ReLU6激活函数比ReLU激活函数的鲁棒性更好。
图片和一部分内容来源:
https://zhuanlan.zhihu.com/p/33075914
https://machinethink.net/blog/mobilenet-v2/
可以看到MobileNetV2在和其他模型的参数量相当的情况下,运算量更少,并且在CPU上推断的时间更短。说明了MobileNetV2相对其他的模型是非常的轻量化的。
在COCO目标检测集上的实验对比:MobileNetV2+SSDLite实现了更少的参数量,更少的运算量,实现在CPU上更少的推断。这些实验已经很好的说明了MobileNetV2是一个轻量化的网络模型。
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')