使用简单,提点结构块!南开大学 Res2net! 看完直接上手!
论文链接:https://arxiv.org/abs/1904.01169
1.来先看看摘要
对于许多视觉任务来说,在多个尺度上表示特征是非常重要的。卷积神经网络(CNNs)不断发展,并显示出较强的多尺度表示能力,在广泛的应用中一致取得了性能的提升。然而,大多数现有的方法以分层的方式表示多尺度特性。在本文提出了一种新的CNNs模块,即Res2Net,通过在一个残差块内构造分层的类残差连接。Res2Net以粒度级别表示多尺度特性,并增加了每个网络层的感受野范围。本文提出的Res2Net块可以插入到最先进的CNN主干模型中,例如ResNet、ResNeXt和DLA。我们在所有这些模型上评估了Res2Net模块,在广泛使用的数据集(如CIFAR-100和ImageNet)上相比基准模型性能都得到了提升。针对典型的计算机视觉任务,如目标检测、分类和显著性目标检测,模型简化测试和实验结果进一步验证了Res2Net相对于最先进的基准方法的优越性。源代码和经过训练的模型将会公开。
2,好了,废话不多说,上结构
可以看到 res2net策略使用了一个新的维度,即scale(res2net块中的特征组数量)
作为除了现有的深度维度、宽度维度和基数维度之外的一个重要因素。
结构中x1,x2,x3,x4即表示scale维度值为4
结构解析:
举个栗子:送入此结构的feature map经过1x1卷积后feature map为8 channel ,宽高(w,h),根据通道编号1~8,
x1表示channel编号为1,2的feature map,x2表示channel编号为3,4的feature map,依次表示 ,总共四组feature map
k2为x2 经过3x3 卷积 之后,k3,k4, 依次表示,
y1 等于x1,
y2 等于K2 ,
y3 等于 K2 融合 X3(元素相加)后经过3X3卷积所输出的 K3
y4 等于 K3 融合 X4(元素相加)后经过3X3卷积所输出的 K4
最后,y1,y2, y3, y4 经行 concat(又变回8channel),然后再经过1X1卷积输出。
理解完毕!
3,这个模块提取特征效果
可以看到,红色**区域确实好!
4,代码
def res2net_bottleneck_block(x, f, s=4, expansion=4, use_se_block=False):
"""
Arguments:
x: input tensor
f: number of output channels
s: scale dimension
"""
#获取输入特征的通道
num_channels = int(x._keras_shape[-1])
input_tensor = x
# Conv 1x1
x = BatchNormalization()(x)
x = Activation('relu')(x)
x = Conv2D(f, 1, kernel_initializer='he_normal', use_bias=False)(x)
# Conv 3x3
#定义一个数组来存放 y1,y2,y3,y4模块
subset_x = []
n = f
#w表示将通道分成几组后,每组通道数
w = n // s
#分几组循环几次
for i in range(s):
#实现通道数分组,将x的最后一个维度进行切分
slice_x = Lambda(lambda x: x[..., i*w:(i+1)*w])(x)
#当第0组是时,x1不计算,直接保存特征到subset_x,
#当第1组时,x2经过3X3卷积后产生K2,保存到subset_x
#当第2组时,x3和从subset_x取出的K2,进行融合,再经过3X3卷积后产生K3,保存到subset_x
#当第3组时,同上步
if i > 1:
slice_x = Add()([slice_x, subset_x[-1]])
if i > 0:
slice_x = BatchNormalization()(slice_x)
slice_x = Activation('relu')(slice_x)
slice_x = Conv2D(w, 3, kernel_initializer='he_normal', padding='same', use_bias=False)(slice_x)
subset_x.append(slice_x)
#将subset_x中保存的 y1,y2, y3, y4 经行 concat
x = Concatenate()(subset_x)
# Conv 1x1
x = BatchNormalization()(x)
x = Activation('relu')(x)
x = Conv2D(f*expansion, 1, kernel_initializer='he_normal', use_bias=False)(x)
#下面是res2net结合senet块,可用可不用
if use_se_block:
x = se_block(x)
# Add
if num_channels == f*expansion:
skip = input_tensor
else:
skip = input_tensor
skip = Conv2D(f*expansion, 1, kernel_initializer='he_normal')(skip)
out = Add()([x, skip])
return out
def se_block(input_tensor, c=16):
num_channels = int(input_tensor._keras_shape[-1]) # Tensorflow backend
bottleneck = int(num_channels // c)
se_branch = GlobalAveragePooling2D()(input_tensor)
se_branch = Dense(bottleneck, use_bias=False, activation='relu')(se_branch)
se_branch = Dense(num_channels, use_bias=False, activation='sigmoid')(se_branch)
out = Multiply()([input_tensor, se_branch])
return out
5,结果,看下论文,在识别,检测,分割领域都有提升
基于Faster Rcnn
基于DeeplabV3+