文章目录
ResNet
ResNet是著名的神经网络,网络中的残差块更是广为人知,如下图,残差块的公式为 y = F ( x ) + x y=F(x)+x y=F(x)+x, x x x为输入, F ( x ) F(x) F(x)代表的则是卷积操作。
网络图
残差块两种主要模块如下图:左边的是浅层Resnet架构,右边的深层Resnet架构, 1 ∗ 1 1*1 1∗1卷积层负责先降维和升维(考虑到训练时间),这样 3 ∗ 3 3*3 3∗3卷积层的输入和输出可以在一个比较小的维度。下图中,左边输入64维,右边输入256维,但是这两种设计具有相似的时间复杂度。
代码
def resnet_basicblock(l, ch_out, stride):
shortcut = l
l = Conv2D('conv1', l, ch_out, 3, strides=stride, activation=BNReLU)
l = Conv2D('conv2', l, ch_out, 3, activation=get_bn(zero_init=True))
out = l + resnet_shortcut(shortcut, ch_out, stride, activation=get_bn(zero_init=False))
return tf.nn.relu(out)
def resnet_bottleneck(l, ch_out, stride, stride_first=False):
shortcut = l
l = Conv2D('conv1', l, ch_out, 1, strides=stride if stride_first else 1, activation=BNReLU)
l = Conv2D('conv2', l, ch_out, 3, strides=1 if stride_first else stride, activation=BNReLU)
l = Conv2D('conv3', l, ch_out * 4, 1, activation=get_bn(zero_init=True))
out = l + resnet_shortcut(shortcut, ch_out * 4, stride, activation=get_bn(zero_init=False))
return tf.nn.relu(out)
下表是ImageNet的架构,表格中,方括号就是上图类似的块,以及块的数量。
实验结果
ImageNet-1K验证集的Top-1错误率。
该网络在2015年ILSVRC比赛中获得了第一名,有比较好的实用意义。
ResNeXt
ResNeXt是ResNet网络的一个变种,是通过重复一个块构建的,该构建块聚集了一组具有相同拓扑结构的转换,简称聚合转换。
T i ( x ) T_i(x) Ti(x)与一个简单的神经元相似。
y是输出。
网络图
实验结果
ImageNet-1K数据集,Top-1错误率结果。
参数量对比图
总结
该网络使用一种分组卷积的思想,与Resnet网络相结合。该网络在ILSVRC 2016比赛中获得了第二名。
代码
class ResNeXtBottleneck(nn.Module):
def __init__(self, in_channels, out_channels, stride, cardinality, base_width, widen_factor):
""" Constructor
Args:
in_channels: input channel dimensionality
out_channels: output channel dimensionality
stride: conv stride. Replaces pooling layer.
cardinality: num of convolution groups.
base_width: base number of channels in each group.
widen_factor: factor to reduce the input dimensionality before convolution.
"""
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))
def forward(self, x):
bottleneck = self.conv_reduce.forward(x)
bottleneck = F.relu(self.bn_reduce.forward(bottleneck), inplace=True)
bottleneck = self.conv_conv.forward(bottleneck)
bottleneck = F.relu(self.bn.forward(bottleneck), inplace=True)
bottleneck = self.conv_expand.forward(bottleneck)
bottleneck = self.bn_expand.forward(bottleneck)
residual = self.shortcut.forward(x)
return F.relu(residual + bottleneck, inplace=True)
DenseNet
上面说过,ResNet相当于添加了一个跳跃链接,通过恒等函数绕过非线性变换。
x l = F l ( x l − 1 ) + x l