ShuffleNet-V2

ShuffleNet-V2

背景介绍

  ShuffleNet-V2:2018年发表于ECCV,是一种高效的轻量级深度学习模型,在同等复杂度下,ShuffleNet-V2比ShuffleNet和MobileNet更准确

ShuffleNet_V2

ShuffleNet-V2特点

  借鉴了AlexNet分组卷积的概念,引入了通道分离通道洗牌,在减少参数量的同时,增加了通道之间的联系,并且对最后的结果进行通道合并,完成特征融合
  在Block中使用深度可分离卷积思想,减少模型参数量

Group Convolution

ShuffleNet_V2
  Group Convolution(分组卷积)传统卷积是采用一种卷积全连接的思想,特征图中的每一个像素点都结合了图像中所有通道的信息。而分组卷积特征图像每一个像素点只利用到一部分原始图像的通道
  主要作用是大大降低网络的参数量。如果一个64x64x256的图像,经过5x5的卷积核后变为64x64x256的图像,经过普通卷积的参数量为256x(256x5x5+1)=1638656,而分成32组的分组卷积的参数量为256x(8*5x5+1)=51456,参数量缩小了约32倍,当组数变成通道数时,则类似于Depthwise Convolution深度卷积

Separable Convolution

Xception
  Separable Convolution(深度可分离卷积):是上面两个卷积合二为一的卷积操作。
  第一步:DepthwiseConv,对每一个通道进行卷积
  第二步:PointwiseConv,对第一步得到的结果进行1x1卷积,实现通道融合
  主要作用是
大大降低网络的参数量
,并且可以调整为任意合适的通道数。第一步的目的是减少参数量,第二步是调整通道数,因此将两个卷积操作结合,组成深度可分离卷积。

不同尺寸ShuffleNet-V2网络结构

ShuffleNet_V2

ShuffleNet-V2图像分析

ShuffleNet_V2

TensorFlow2.0实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
from functools import reduce
import tensorflow.keras as keras


def compose(*funcs):
if funcs:
return reduce(lambda f, g: lambda *a, **kw: g(f(*a, **kw)), funcs)
else:
raise ValueError('Composition of empty sequence not supported.')


class Channel_Split(keras.layers.Layer):
def __init__(self, name):
super(Channel_Split, self).__init__()
self._name = name

def call(self, inputs, **kwargs):

return inputs[..., :inputs.shape[-1] // 2], inputs[..., inputs.shape[-1] // 2:]


class Channel_Shuffle(keras.layers.Layer):
def __init__(self, name):
super(Channel_Shuffle, self).__init__()
self._name = name

def call(self, inputs, **kwargs):
channel = inputs.shape[-1]
output = compose(keras.layers.Reshape((inputs.shape[1], inputs.shape[2], 2, channel // 2)),
keras.layers.Permute([1, 2, 4, 3]),
keras.layers.Reshape((inputs.shape[1], inputs.shape[2], channel)))(inputs)

return output


class Conv_Bn_Relu(keras.layers.Layer):
def __init__(self, filters, kernel_size, strides, padding, name):
super(Conv_Bn_Relu, self).__init__()
self._name = name
self.block = keras.Sequential()
if name.find('depthwise') == -1:
self.block.add(keras.layers.Conv2D(filters, kernel_size, strides, padding=padding))
else:
self.block.add(keras.layers.DepthwiseConv2D(kernel_size, strides, padding=padding))
self.block.add(keras.layers.BatchNormalization())
if name.find('relu') != -1:
self.block.add(keras.layers.ReLU())

def call(self, inputs, **kwargs):

return self.block(inputs)


def block(x, out_channel, strides, name):
if strides == (1, 1):
split1, split2 = Channel_Split(name='{}_channel_split'.format(name))(x)
else:
split1 = split2 = x

split1 = compose(Conv_Bn_Relu(out_channel // 2, (1, 1), (1, 1), 'same', name='{}_part1_conv_bn_relu1'.format(name)),
Conv_Bn_Relu(None, (3, 3), strides, 'same', name='{}_part1_depthwiseconv_bn'.format(name)),
Conv_Bn_Relu(out_channel // 2, (1, 1), (1, 1), 'same', name='{}_part1_conv_bn_relu2'.format(name)))(split1)

if strides == (2, 2):
split2 = compose(Conv_Bn_Relu(None, (3, 3), strides, 'same', name='{}_part2_depthwiseconv_bn'.format(name)),
Conv_Bn_Relu(out_channel // 2, (1, 1), (1, 1), 'same', name='{}_part2_conv_bn_relu'.format(name)))(split2)

x = keras.layers.Concatenate(name='{}_concatenate'.format(name))([split1, split2])
x = Channel_Shuffle(name='{}_channel_shuffle'.format(name))(x)

return x


def add_block(x, filters, n, name):
x = block(x, filters, (2, 2), name='{}_1'.format(name))
for i in range(n):
x = block(x, filters, (1, 1), name='{}_{}'.format(name, i + 2))

return x


def shufflenet_v2(input_shape):
input_tensor = keras.layers.Input(input_shape, name='input')
x = input_tensor

x = compose(keras.layers.Conv2D(24, (3, 3), (2, 2), padding='same', activation='relu', name='conv_relu1'),
keras.layers.MaxPool2D((3, 3), (2, 2), padding='same', name='maxpool'))(x)

x = add_block(x, 116, 3, name='block1')
x = add_block(x, 232, 7, name='block2')
x = add_block(x, 464, 3, name='block3')

x = compose(keras.layers.Conv2D(1024, (1, 1), (1, 1), padding='same', activation='relu', name='conv_relu2'),
keras.layers.GlobalAveragePooling2D(name='global_averagepool'),
keras.layers.Dense(1000, activation='softmax', name='dense'))(x)

model = keras.Model(input_tensor, x, name='ShuffleNet-V2')

return model


if __name__ == '__main__':

model = shufflenet_v2(input_shape=(224, 224, 3))
model.build(input_shape=(None, 224, 224, 3))
model.summary()

ShuffleNet_V2

ShuffleNet-V2小结

  ShuffleNet-V2是一种有效的轻量级深度学习网络,参数量只有2M,其从AlexNet中借鉴了分组卷积的思想,并且运用了通道分离洗牌的思想,不但可以大大降低模型参数量,并且可以提高模型的鲁棒性

-------------本文结束感谢您的阅读-------------
0%