ResNeXt两点: (1)Inception v4的所有分支结构统一 (2)inception block前后增加shortcut分支
分支结构统一与shortcut
左边是Inception结构,不同分支用不同的结构。 右边是ResNeXt的Block,每个分支都是1x1,3x3,1x1的BottleNeck结构,所以叫统一分支。 (1)为什么要统一分支?因为左边的结构是人为设计的,所以想用稍微自然一点的结构。(有ablation study吗?) 增加shortcut发现效果更好
具体实现
看右边的结构,第一个1x1卷积直接把256维降为4维了,是不是很离谱? 实际这里并不是这么做的,整体是用分组卷积实现的,而且只有中间的3x3卷积是分组卷积 具体实现如下
class ResNeXtBottleneck(nn.Module):
def __init__(self, in_channels, out_channels, stride, cardinality, base_width, widen_factor):
super(ResNeXtBottleneck, self).__init__()
width_ratio = out_channels / (widen_factor * 64.)
D = cardinality * int(base_width * width_ratio)
self.conv_reduce = nn.Conv2d(in_channels, D, kernel_size=1, stride=1, padding=0, bias=False)
self.bn_reduce = nn.BatchNorm2d(D)
self.conv_conv = nn.Conv2d(D, D, kernel_size=3, stride=stride, padding=1, groups=cardinality, bias=False)
self.bn = nn.BatchNorm2d(D)
self.conv_expand = nn.Conv2d(D, out_channels, kernel_size=1, stride=1, padding=0, bias=False)
self.bn_expand = nn.BatchNorm2d(out_channels)
self.shortcut = nn.Sequential()
if in_channels != out_channels:
self.shortcut.add_module('shortcut_conv',
nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride, padding=0,
bias=False))
self.shortcut.add_module('shortcut_bn', nn.BatchNorm2d(out_channels))
可以看到,第一个和第三个(1x1)卷积并没有分组,只有第二个(3x3)卷积有分组,所以实际上是这样的结构: 第一个卷积负责降维到128,第二个卷积分组得到特征,第三个卷积还原通道。 但是我们仔细想想,这种实现方式和上面那张图实际上是等价的 第一层:128个1x1卷积对相同的输入进行运算,得到128个输出特征图 第二层,128个3x3卷积做分组卷积,实际上就是分成32组,每组4个卷积核,实际上就是把第一层输出的128个特征图分成32组,每组用4个卷积核卷积。也就是说,这32组,每组4个和第一层的128卷积核是一一对应的。也就是说,上面那一层可以看作是将原来的256维的特征图用32组卷积核降维到每组4维,共128维,也就是这张图: 每组4维的特征图再用该组的4个1x1卷积核做一次变换,然后再concat在一起到128维,最后用1个1x1卷积升维。 当然,最后的1x1卷积也可以看作32组1x1卷积,每组升维到256,再最后加起来,如图(a) 实际上应该是(a)->(b)->?的路线
值得注意的是
这里第一块并不是把256维的特征降维到4维的意思,而是降维到32x4维,然后分发给32个分支,每个分支4维的意思 为什么这么说?且看论文中的推导ResNeXt详解 - 大师兄的文章 - 知乎 https://zhuanlan.zhihu.com/p/51075096
ResNeXt的思想其实是由全连接神经网络来的 实际上可以分为3个步骤: (1)拆分:将
x
\bold{x}
x拆分为
x
i
x_i
xi? (2)映射:对于每个
x
i
x_i
xi?乘上对应的权重 (3)聚合:将所有分支映射后的结果相加 实际上可以用一个公式表示: 如果将映射推广,这里只不过是内积(乘法),如果是神经网络呢?
因此,最初的设计: (1)拆分:将输入特征(降维后)拆分成多组 (2)映射:分组使用3x3卷积映射 (3)聚合:将映射后的结果(升维后)相加 于是就有了如下结构: 我们从最下层看,每组卷积的输入channel是4,输出channel是256,共32组,实际上可以先将32组输入feature concat在一起,也就是(32x4,h,w)再使用256个(1,1,32x4)的卷积核升维是等价的。 我们画个图来看看:
|