1. MobilenetV1介绍
MobilenetV1是Google提出的用于移动和嵌入式应用的高效网络模型。MobilenetV1基于一种流线型的架构,使用深度可分离卷积来构建轻量级的深度神经网络。
2. 深度可分离卷积
MobileNetV1模型基于深度可分卷积这是一种分解卷积的形式,它将一个标准卷积分解为深度卷积和一个1×1卷积,1x1卷积又叫称为点卷积。
标准的卷积是输入一个 D F D_{F} DF x D F D_{F} DF x M M M 的特征图,输出一个 D F D_{F} DF x D F D_{F} DF x N N N 的特征图。
标准的卷积层的参数是 D K D_{K} DK x D K D_{K} DK x M M M x N N N ,其中 D K D_{K} DK是卷积核的小大, M M M是输入通道数, N N N是输出通道数。
所以标准的卷积计算量如下:
D
K
D_{K}
DK x
D
K
D_{K}
DK x
M
M
M x
N
N
N x
D
F
D_{F}
DF x
D
F
D_{F}
DF
深度可分离卷积由两层组成:深度卷积和点卷积。
我们使用深度卷积来为每个输入通道(输入深度)应用一个过滤器。然后使用点态卷积(一个简单的1×1卷积)创建深度层输出的线性组合。
其计算量为: D K D_{K} DK x D K D_{K} DK x M M M x D F D_{F} DF x D F D_{F} DF
相对于标准卷积,深度卷积是非常有效的。然而,它只过滤输入通道,并没有将它们组合起来创建新的功能。因此,为了生成这些新特征,需要一个额外的层,通过1 × 1的卷积计算深度卷积输出的线性组合。
其计算量为: M M M x N N N x D F D_{F} DF x D F D_{F} DF
例子:
标准卷积:
假设有一个56 x 56 x 16的特征图,其卷积核是3 x 3大小,输出通道为32,计算量大小就是56 x56 x16 x 32 x 3 x 3 = 14450688
深度可分离卷积:
假设有一个56 x 56 x 16的特征图,输出通道为32,先用16个3 x 3大小的卷积核进行卷积,接着用32个1 x 1大小的卷积核分别对这用16个3 x 3大小的卷积核进行卷积之后的特征图进行卷积,计算量为3 x 3 x 16 x 56 x 56 + 16 x 32 x 56 x 56 = 2057216
从而可见计算量少了许多。
3. 网络结构
其中其中Conv dw就是深度可分离卷积
4. 实现代码
from os import name
import numpy as np
import tensorflow as tf
from tensorflow.keras.layers import (
Activation, BatchNormalization, Conv2D, DepthwiseConv2D, Dropout,
GlobalAveragePooling2D, Input, Reshape
)
from tensorflow.keras.models import Model
from plot_model import plot_model
from tensorflow.keras import backend as K
def relu6(x):
return K.relu(x, max_value=6)
def Conv2D_block(inputs, filters, alpha, kernel_size=(3, 3), strides=(1, 1)):
filters = int(filters * alpha)
x = Conv2D(
filters=filters, kernel_size=kernel_size, padding='same',
use_bias=False, strides=strides, name='Conv'
)(inputs)
x = BatchNormalization(name='Conv_bn')(x)
x = Activation(relu6, name='Conv_relu6')(x)
return x
def Depthwise_Conv2D_block(inputs, filters, alpha, depth_multiplier=1, strides=(1, 1), block_id=1):
filters = int(filters * alpha)
x = DepthwiseConv2D(kernel_size=(3, 3), padding='same', depth_multiplier=depth_multiplier, strides=strides,
use_bias=False, name=f'Conv2D_dw_{block_id}')(inputs)
x = BatchNormalization(name=f'Conv2D_dw_bn{block_id}')(x)
x = Activation(relu6, name=f'Conv2D_dw_relu6{block_id}')(x)
x = Conv2D(filters=filters, kernel_size=(1, 1), padding='same', strides=(1, 1), use_bias=False, name=f'Conv2D_1x1{block_id}')(x)
x = BatchNormalization(name=f'Conv2D_pw_bn{block_id}')(x)
x = Activation(relu6, name=f'Conv2D_pw_relu6{block_id}')(x)
return x
def MobilenetV1(inputs, alpha=1.0, depth_multiplier=1, dropout=1e-3, classes=17):
x = Conv2D_block(inputs=inputs, filters=32, alpha=alpha, strides=(2, 2))
x = Depthwise_Conv2D_block(inputs=x, filters=64, alpha=alpha, depth_multiplier=depth_multiplier, block_id=1)
x = Depthwise_Conv2D_block(inputs=x, strides=(2, 2), filters=128, alpha=alpha, depth_multiplier=depth_multiplier, block_id=2)
x = Depthwise_Conv2D_block(inputs=x, filters=128, alpha=alpha, depth_multiplier=depth_multiplier, block_id=3)
x = Depthwise_Conv2D_block(inputs=x, strides=(2, 2), filters=256, alpha=alpha, depth_multiplier=depth_multiplier, block_id=4)
x = Depthwise_Conv2D_block(inputs=x, filters=256, alpha=alpha, depth_multiplier=depth_multiplier, block_id=5)
x = Depthwise_Conv2D_block(inputs=x, strides=(2, 2), filters=512, alpha=alpha, depth_multiplier=depth_multiplier, block_id=6)
x = Depthwise_Conv2D_block(inputs=x, filters=512, alpha=alpha, depth_multiplier=depth_multiplier, block_id=7)
x = Depthwise_Conv2D_block(inputs=x, filters=512, alpha=alpha, depth_multiplier=depth_multiplier, block_id=8)
x = Depthwise_Conv2D_block(inputs=x, filters=512, alpha=alpha, depth_multiplier=depth_multiplier, block_id=9)
x = Depthwise_Conv2D_block(inputs=x, filters=512, alpha=alpha, depth_multiplier=depth_multiplier, block_id=10)
x = Depthwise_Conv2D_block(inputs=x, filters=512, alpha=alpha, depth_multiplier=depth_multiplier, block_id=11)
x = Depthwise_Conv2D_block(inputs=x, strides=(2, 2), filters=1024, alpha=alpha, depth_multiplier=depth_multiplier, block_id=12)
x = Depthwise_Conv2D_block(inputs=x, filters=1024, alpha=alpha, depth_multiplier=depth_multiplier, block_id=13)
x = GlobalAveragePooling2D()(x)
shape = (1, 1, int(1024 * alpha))
x = Reshape(shape, name='reshape_1')(x)
x = Dropout(dropout, name='dropout')(x)
x = Conv2D(classes, (1, 1),padding='same', name='conv_preds')(x)
x = Activation('softmax', name='act_softmax')(x)
x = Reshape((classes,), name='reshape_2')(x)
return x
if __name__ == '__main__':
is_show_picture = False
inputs = Input(shape=(224,224,3))
classes = 1000
model = Model(inputs=inputs, outputs=MobilenetV1(inputs=inputs, classes=classes))
model.summary()
print(len(model.layers))
for i in range(len(model.layers)):
print(i, model.layers[i])
if is_show_picture:
plot_model(model,
to_file='./nets_picture/MobilenetV1.png',
)
print("plot_model------------------------>")