squeeze excitation模块,替换ResNet bottleneck block

之前的 文章 很详细的写了bottleneck block的实现。
先回顾一下block的意义,网络的性能归根结底是对特征提取的优秀程度,对特征提取越好,后面的分类,检测,分割等等,就会有更好的性能。
但是很深的网络会非常难训练,一方面是因为参数巨大,容易过拟合,但是resnet实验中证明并不是因为过拟合导致的准确率下降,因为在训练阶段的准确率也下降了,这并不是过拟合的结果。
另一方面,梯度的反向传播经过非常多次相乘会变得很小,这就导致了梯度消失现象,因此性能饱和,无法进一步优化。
所以resnet不是一味的加深网络层数而提高性能的。resnet的基本假设是,对于一个效果很好的网络,我在后面加上和前面层一模一样的层的时候,模型的性能至少应该是和之前的浅层网络持平,
squeeze excitation模块,替换ResNet bottleneck block_第1张图片
在下图中可以看到一个叫building block的基本结构,block设计的初衷就是为了让x有两条路走,第一条就是identify mapping, 第二条就是resduial mapping,identify mapping确保了已经学到的信息被完整的保存下来,如果需要有改进,那么就在resduial mapping中进行,也就是F(x)中进行,F(x)学到的是block的输出相对于block输入的变化,如果F(x)什么都学不到,那么模型保持之前x状态的性能,即不会变差。
说了这么多,可以发现,F(x)对block之间的信息变化学的越好,那么模型的性能就越好。因此如何设计F(x) 成了一个热门点。
在最初的resnet中,设计了两种F(x),也可以说是设计了两种block,左边叫build block,右边叫bottleneck block。一般有4个block和两个全连接层(早期用),可以构造为
resnet 18=2+(2+2+2+2)*2 ;
resnet 34=2+(3+4+6+3)*2;
resnet 50=2+(3+4+6+3)*3;
resnet 101=2+(3+4+23+3)*3;
resnet 152=2+(3+8+36+3)*3;

squeeze excitation模块,替换ResNet bottleneck block_第2张图片
大多数简单任务,使用resnet 18即可。
以上所说,统称为resent v1,发表在Deep Residual Learning for Image Recognitio中,
在resnet v2,主要是引入了BN,如下图右边。图中的resduial mapping都换到了右侧。
squeeze excitation模块,替换ResNet bottleneck block_第3张图片
关于为什么使用full preactivation结构的解释,准备结合上一篇文章一起解释(两篇合一篇),以及不同channel的处理,在写完se block 和 gc block之后整理。

写了这么多前言,正题开始了,先讲Squeeze Excitation Net。

SE Net的核心思想是通过channel信息的attention机制(重新分布权重)来达到更好的学习效果。
squeeze excitation模块,替换ResNet bottleneck block_第4张图片
squeeze excitation模块,替换ResNet bottleneck block_第5张图片
squeeze excitation模块,替换ResNet bottleneck block_第6张图片

def se_block(bottom, ratio=16):
    weight_initializer = tf.contrib.layers.variance_scaling_initializer()
    bias_initializer = tf.constant_initializer(value=0.0)

    # Bottom [N,H,W,C]
    # Global average pooling
    #with tf.variable_scope("se_block"):

    channel = bottom.get_shape()[-1]
    se = tf.reduce_mean(bottom, axis=[1,2], keep_dims=True)
    #squeeze过程 Global average pooling

    assert se.get_shape()[1:] == (1,1,channel)
    se = tf.layers.dense(se, channel//ratio, activation=tf.nn.relu,
                         kernel_initializer=weight_initializer,
                         bias_initializer=bias_initializer)
    assert se.get_shape()[1:] == (1,1,channel//ratio)
    se = tf.layers.dense(se, channel, activation=tf.nn.sigmoid,
                         kernel_initializer=weight_initializer,
                         bias_initializer=bias_initializer)
    assert se.get_shape()[1:] == (1,1,channel)
    top = bottom * se

    return top 
def res_block2(bottom, filters, training, use_bn, use_se_block, strides=1, downsample=False):
    path_2=bottom

    #conv 3x3
    path_1 = conv_layer(bottom, filters[0], kernel_size=3,strides=strides)
    path_1 = norm_layer(path_1, training, use_bn)
    path_1 = relu(path_1)
    # conv 3x3
    path_1 = conv_layer(path_1, filters[1], kernel_size=3)
    path_1 = norm_layer(path_1, training, use_bn)
    path_1 = relu(path_1)

    if use_se_block:
        path_1=se_block(path_1)
    if downsample:
        path_2 = conv_layer(path_2, filters[1], kernel_size=1, strides=strides)
        path_2 = norm_layer(path_2, training, use_bn)
    top = path_1 + path_2
    top = relu(top)
    return top

参考
https://zhuanlan.zhihu.com/p/32702350
https://blog.csdn.net/u014380165/article/details/78006626

你可能感兴趣的:(算法,Deep,Learning)