神經網絡學習小記錄39——MobileNetV3(small)模型的復現詳解
學習前言
不知道咋地,就是突然想把small也一起寫了。
什麼是MobileNetV3
最新的MobileNetV3的被寫在了論文《Searching for MobileNetV3》中。
它是mobilnet的最新版,據說效果還是很好的。
作爲一種輕量級網絡,它的參數量還是一如既往的小。
它綜合了以下四個特點:
1、MobileNetV1的深度可分離卷積(depthwise separable convolutions)。
2、MobileNetV2的具有線性瓶頸的逆殘差結構(the inverted residual with linear bottleneck)。
3、輕量級的注意力模型。
4、利用h-swish代替swish函數。
代碼下載
https://github.com/bubbliiiing/classic-convolution-network
large與small的區別
其實MobileNetV3中的large與small模型沒有特別大的區別,主要的區別是通道數的變化與bneck的次數。
MobileNetV3(small)的網絡結構
1、MobileNetV3(small)的整體結構
如上爲MobileNetV3(small)的表,與MobileNetV3(large)相比,bneck的次數與通道數都有一定的下降。
如何看懂這個表呢?我們從每一列出發:
第一列Input代表mobilenetV3每個特徵層的shape變化;
第二列Operator代表每次特徵層即將經歷的block結構,我們可以看到在MobileNetV3中,特徵提取經過了許多的bneck結構;
第三、四列分別代表了bneck內逆殘差結構上升後的通道數、輸入到bneck時特徵層的通道數。
第五列SE代表了是否在這一層引入注意力機制。
第六列NL代表了激活函數的種類,HS代表h-swish,RE代表RELU。
第七列s代表了每一次block結構所用的步長。
2、MobileNetV3特有的bneck結構
bneck結構如下圖所示:
它綜合了以下四個特點:
a、MobileNetV2的具有線性瓶頸的逆殘差結構(the inverted residual with linear bottleneck)。
即先利用1x1卷積進行升維度,再進行下面的操作,並具有殘差邊。
b、MobileNetV1的深度可分離卷積(depthwise separable convolutions)。
在輸入1x1卷積進行升維度後,進行3x3深度可分離卷積。
c、輕量級的注意力模型。
這個注意力機制的作用方式是調整每個通道的權重。
d、利用h-swish代替swish函數。
在結構中使用了h-swishj激活函數,代替swish函數,減少運算量,提高性能。
網絡實現代碼
由於keras代碼沒有預訓練權重,所以只是把網絡結構po出來。
from keras.layers import Conv2D, DepthwiseConv2D, Dense, GlobalAveragePooling2D,Input
from keras.layers import Activation, BatchNormalization, Add, Multiply, Reshape
from keras.models import Model
from keras import backend as K
alpha = 1
def relu6(x):
# relu函數
return K.relu(x, max_value=6.0)
def hard_swish(x):
# 利用relu函數乘上x模擬sigmoid
return x * K.relu(x + 3.0, max_value=6.0) / 6.0
def return_activation(x, nl):
# 用於判斷使用哪個激活函數
if nl == 'HS':
x = Activation(hard_swish)(x)
if nl == 'RE':
x = Activation(relu6)(x)
return x
def conv_block(inputs, filters, kernel, strides, nl):
# 一個卷積單元,也就是conv2d + batchnormalization + activation
channel_axis = 1 if K.image_data_format() == 'channels_first' else -1
x = Conv2D(filters, kernel, padding='same', strides=strides)(inputs)
x = BatchNormalization(axis=channel_axis)(x)
return return_activation(x, nl)
def squeeze(inputs):
# 注意力機制單元
input_channels = int(inputs.shape[-1])
x = GlobalAveragePooling2D()(inputs)
x = Dense(int(input_channels/4))(x)
x = Activation(relu6)(x)
x = Dense(input_channels)(x)
x = Activation(hard_swish)(x)
x = Reshape((1, 1, input_channels))(x)
x = Multiply()([inputs, x])
return x
def bottleneck(inputs, filters, kernel, up_dim, stride, sq, nl):
channel_axis = 1 if K.image_data_format() == 'channels_first' else -1
input_shape = K.int_shape(inputs)
tchannel = int(up_dim)
cchannel = int(alpha * filters)
r = stride == 1 and input_shape[3] == filters
# 1x1卷積調整通道數,通道數上升
x = conv_block(inputs, tchannel, (1, 1), (1, 1), nl)
# 進行3x3深度可分離卷積
x = DepthwiseConv2D(kernel, strides=(stride, stride), depth_multiplier=1, padding='same')(x)
x = BatchNormalization(axis=channel_axis)(x)
x = return_activation(x, nl)
# 引入注意力機制
if sq:
x = squeeze(x)
# 下降通道數
x = Conv2D(cchannel, (1, 1), strides=(1, 1), padding='same')(x)
x = BatchNormalization(axis=channel_axis)(x)
if r:
x = Add()([x, inputs])
return x
def MobileNetv3_small(shape = (224,224,3),n_class = 1000):
inputs = Input(shape)
# 224,224,3 -> 112,112,16
x = conv_block(inputs, 16, (3, 3), strides=(2, 2), nl='HS')
# 112,112,16 -> 56,56,16
x = bottleneck(x, 16, (3, 3), up_dim=16, stride=2, sq=True, nl='RE')
# 56,56,16 -> 28,28,24
x = bottleneck(x, 24, (3, 3), up_dim=72, stride=2, sq=False, nl='RE')
x = bottleneck(x, 24, (3, 3), up_dim=88, stride=1, sq=False, nl='RE')
# 28,28,24 -> 14,14,40
x = bottleneck(x, 40, (5, 5), up_dim=96, stride=2, sq=True, nl='HS')
x = bottleneck(x, 40, (5, 5), up_dim=240, stride=1, sq=True, nl='HS')
x = bottleneck(x, 40, (5, 5), up_dim=240, stride=1, sq=True, nl='HS')
# 14,14,40 -> 14,14,48
x = bottleneck(x, 48, (5, 5), up_dim=120, stride=1, sq=True, nl='HS')
x = bottleneck(x, 48, (5, 5), up_dim=144, stride=1, sq=True, nl='HS')
# 14,14,48 -> 7,7,96
x = bottleneck(x, 96, (5, 5), up_dim=288, stride=2, sq=True, nl='HS')
x = bottleneck(x, 96, (5, 5), up_dim=576, stride=1, sq=True, nl='HS')
x = bottleneck(x, 96, (5, 5), up_dim=576, stride=1, sq=True, nl='HS')
x = conv_block(x, 576, (1, 1), strides=(1, 1), nl='HS')
x = GlobalAveragePooling2D()(x)
x = Reshape((1, 1, 576))(x)
x = Conv2D(1024, (1, 1), padding='same')(x)
x = return_activation(x, 'HS')
x = Conv2D(n_class, (1, 1), padding='same', activation='softmax')(x)
x = Reshape((n_class,))(x)
model = Model(inputs, x)
return model
if __name__ == "__main__":
model = MobileNetv3_small()
model.summary()