ResNeXt

ResNeXt

背景介绍

  ResNeXt:2017年发表于CVPR的一个模型,是ResNet网络的升级版本。和Inception-ResNet类似,Inception-ResNet可以认为是Inception模型的基础上吸收ResNet残差思想,而ResNext则可以认为是ResNet模型的基础上吸收Inception分块合并思想。

ResNeXt

ResNeXt特点

  网络结构和ResNet相同,根据ResNet50,则可以修改为ResNeXt50,根据ResNet101,则可以修改为ResNeXt101,等等
  引入Inception模型分块合并思想,将ResNet中Conv Block和Identity Block中的普通卷积变成GroupConv分组卷积。提出了**cardinality(基数)**名词,基数为32,相当于分组卷积的组数为32,最后将32组卷积结果合并

Group Convolution

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

ResNeXt50图像分析

ResNeXt

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
from functools import reduce
import tensorflow as tf
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 GroupConv(keras.layers.Layer):
def __init__(self, filters, kernel_size, strides, g_num, name='groupconv'):
super(GroupConv, self).__init__()
self._name = name
self.g_num = g_num
self.groupconv = [keras.layers.Conv2D(filters // g_num, kernel_size, strides, padding='same', name='{}{}'.format(name, i + 1)) for i in range(g_num)]
self.concatenate = keras.layers.Concatenate(name='{}_concatenate'.format(name))

def call(self, inputs, **kwargs):
x_split = tf.split(inputs, self.g_num, axis=-1)
x = [self.groupconv[i](x_split[i]) for i in range(self.g_num)]
output = self.concatenate(x)

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
if name.find('group') == -1:
self.conv = keras.layers.Conv2D(filters, kernel_size, strides, padding)
else:
self.conv = GroupConv(filters, kernel_size, strides, 32)
self.bn = keras.layers.BatchNormalization()
self.relu = keras.layers.ReLU()

def call(self, inputs, **kwargs):
conv = self.conv(inputs)
bn = self.bn(conv)
output = self.relu(bn)

return output


class Conv_Bn(keras.layers.Layer):
def __init__(self, filters, kernel_size, strides, padding, name):
super(Conv_Bn, self).__init__()
self._name = name
self.conv = keras.layers.Conv2D(filters, kernel_size, strides, padding)
self.bn = keras.layers.BatchNormalization()

def call(self, inputs, **kwargs):
conv = self.conv(inputs)
output = self.bn(conv)

return output


def res_block(x, filters, strides, type, name):
shortcut = x
x = compose(Conv_Bn_Relu(filters // 2, (1, 1), (1, 1), padding='same', name='{}{}_conv_bn_relu1'.format(type, name)),
Conv_Bn_Relu(filters // 2, (3, 3), strides, padding='same', name='{}{}_groupconv_bn_relu2'.format(type, name)),
Conv_Bn(filters, (1, 1), (1, 1), padding='same', name='{}{}_conv_bn3'.format(type, name)))(x)
if type == 'conv_block':
shortcut = keras.layers.Conv2D(filters, (1, 1), strides, name='{}{}_shortcut'.format(type, name))(shortcut)
x = keras.layers.Add(name='{}{}_add'.format(type, name))([x, shortcut])
x = keras.layers.ReLU(name='{}{}_relu3'.format(type, name))(x)

return x


def resnext50(input_shape):
input_tensor = keras.layers.Input(input_shape, name='input')
x = input_tensor
x = compose(keras.layers.ZeroPadding2D((3, 3), name='zeropadding'),
keras.layers.Conv2D(64, (7, 7), (2, 2), name='conv1'),
keras.layers.BatchNormalization(name='bn'),
keras.layers.ReLU(name='relu'),
keras.layers.MaxPool2D((3, 3), (2, 2), padding='same', name='maxpool'))(x)
filters = [256, 512, 1024, 2048]
strides = [(1, 1), (2, 2), (2, 2), (2, 2)]
times = [3, 4, 6, 3]
for i in range(len(times)):
x = res_block(x, filters[i], strides[i], 'conv_block', i + 1)
for j in range(times[i] - 1):
x = res_block(x, filters[i], (1, 1), 'identity_block{}_'.format(i + 1), j + 1)
x = compose(keras.layers.GlobalAveragePooling2D(name='global_averagepool'),
keras.layers.Dense(1000, activation='softmax', name='dense'))(x)
model = keras.Model(input_tensor, x, name='ResNeXt50')

return model


if __name__ == '__main__':

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

ResNeXt

ResNeXt小结

  ResNeXt是一种非常有效的特征提取网络,ResNeXt参数量为25M,和相同结构的ResNet几乎相同,但是效果优于ResNet,因此是一种实用的特征提取网络。

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