SENet

SENet

背景介绍

  SENet:2017年发表在CVPR上的一个模型,也是最后一届ImageNet 2017竞赛分类任务的冠军,其创新点是引入了注意力Squeeze-and-Excitation (SE)模块

SENet

SENet特点

  引入了注意力Squeeze-and-Excitation(SE)模块
  由于SE模块简单有效,因此可以很容易的和其他模型耦合,和ResNet耦合变成SE-ResNet,和Inception-V3耦合变成SE-Inception-V3等等

Squeeze-and-Excitation

SENet
  Squeeze-and-Excitation:又称为特征重标定卷积,或者注意力机制。具体来说,就是通过学习的方式来自动获取到每个特征通道的重要程度,然后依照这个重要程度去提升有用的特征并抑制对当前任务用处不大的特征
  首先是 Squeeze操作,先进行全局池化,具有全局的感受野,并且输出的维度和输入的特征通道数相匹配,它表征着在特征通道上响应的全局分布。
  然后是Excitation操作通过全连接层为每个特征通道生成权重,建立通道间的相关性输出的权重看做是进过特征选择后的每个特征通道的重要性,然后通过乘法逐通道加权到先前的特征上,完成在通道维度上的对原始特征的重标定。

SE-ResNet50图像分析

SENet

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
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 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.conv = keras.layers.Conv2D(filters, kernel_size, strides, padding)
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 // 4, (1, 1), (1, 1), padding='same', name='{}{}_conv_bn_relu1'.format(type, name)),
Conv_Bn_Relu(filters // 4, (3, 3), strides, padding='same', name='{}{}_conv_bn_relu2'.format(type, name)),
Conv_Bn(filters, (1, 1), (1, 1), padding='same', name='{}{}_conv_bn3'.format(type, name)))(x)
x = se_block(x, filters, name='{}{}_se'.format(type, name))
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 se_block(x, filters, name):
shortcut = x
x = compose(keras.layers.GlobalAveragePooling2D(name='{}_global_averagepool'.format(name)),
keras.layers.Dense(filters // 16, name='{}_dense1'.format(name)),
keras.layers.ReLU(name='{}_relu'.format(name)),
keras.layers.Dense(filters, name='{}_dense2'.format(name)),
keras.layers.Activation('sigmoid', name='{}_sigmoid'.format(name)),
keras.layers.Reshape((1, 1, filters), name='{}_reshape'.format(name)))(x)
x = keras.layers.Multiply(name='{}_multiply'.format(name))([x, shortcut])

return x


def se_resnet50(input_shape):
input_tensor = keras.layers.Input(input_shape, name='input')
x = input_tensor
x = compose(keras.layers.ZeroPadding2D((3, 3), name='zeropadding'),
Conv_Bn_Relu(64, (7, 7), (2, 2), padding='valid', name='conv_bn_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='SE_ResNet50')

return model


if __name__ == '__main__':

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

SENet

SENet小结

  SENet是一种非常好的思路,其模型参数需要根据选择的耦合模型确定,如果耦合模型为SE-ResNet50,则参数量为28M,其注意力机制非常有效,为MobileNet-V3的发展,EfficientNetGhostNet等网络起到了推动作用。

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