题目:《A Backbone network for Object Detection》
基于CNN的目标检测器可以分为两类:单阶段(one-stage)检测器,如YOLO、SSD、RetinaNet,以及双阶段(two-stage)检测器,典型的如Faster-RCNN、R-FCN、FPN。这两类检测器都要使用到在ImageNet上预训练的分类网络作为骨干网。这也正是被作者认为有缺陷的地方。图像分类毕竟不同于目标检测,它既不需要识别目标的语义特征,也不需要对目标进行定位。具体说来,使用分类网络作为目标检测的骨干网有两个问题:1). 最近提出的检测器如FPN,它为了能够检测不同尺寸的目标增加了额外的阶段。我们经常使用ResNet或VGG作为骨干网,像ResNet50它总共包含5个阶段(C1-C5),但是FPN在此基础上增加了一个阶段,而增加的这个阶段是没有经过预训练的。2). 传统的骨干网采用更大的降采样率产生更大的感受野,这对于图像分类来说有效,即牺牲了空间分辨率,不利于精确定位大的目标和识别小的目标。
DetNet兼顾以上两大问题,具体说来,首先为了识别不同尺寸的目标,DetNet也像FPN一样增加了额外的阶段,但即使增加了额外的阶段DetNet也保持了高分辨率。但是,高分辨显然会带来计算和内存的开销,为了不降低DetNet网络的效率,作者采用了一种低复杂度的扩张的瓶颈层(a low complexity dilated bottleneck structure),what?
上图是应用于FPN的不同骨干网络,其中(A)为含传统骨干网的特征金字塔网络,(B)为用于图像分类的传统骨干网,(C)为DetNet提出的骨干网。这里作者受限于版面大小,没有将第一阶段(stage 1,with stride 2)展示出来。从图(C)也可以明显地看出DetNet的不同之处,首先相比(B)这一传统的分类网络,DetNet增加了一个阶段P6,FPN也同样是增加了P6,目的是为了识别更大的目标。其次作者所说的保持空间分辨率,也就是在最后三个阶段(P4,P5,P6)分辨率一直保持着16X。
DetNet的结构是在ResNet50的基础上进行改进,因为ResNet50本身性能优异,经常用来作为目标检测的骨干网。为了和ResNet50进行PK,DetNet的前4个阶段(1,2,3,4)保持了和ResNet50一致。在上文中也有所提及,要为目标检测设计一下高效且有用的骨干网络要面对两方面的挑战。首先,你要为深层网络维持高的空间分辨率,但高分辨率势必又会带来时间和内存上的巨大的成本。其次,如果减小降采样率也就相当于减小了有效的感受野,这对于图像分类和语义分割来说都是有害的。那面对这两项挑战,DetNet是如何做的呢?
通过下面这幅图来分析DetNet59的细节,它是由ResNet50扩展而来。
1. 引入一个新的阶段P6,随后将用于FPN中的目标检测。同时,从第4阶段开始,固定空间分辨率为输入图像的16X降采样;
2. 因为自第4阶段后空间尺寸被固定,为了增加一个新的阶段P6,作者引入了dilated bottleneck(如下图B)。
3. 使用dilated bottleneck作为一个基础的网络块有效地扩大的感受野。由于dilated convolution仍然是耗时的,所以第5阶段和第6阶段保持和第4阶段一样的通道数(256 input channels for bottleneck block)。这一点也不同于传统骨干网,后者在后一个阶段会将通道数翻倍。
B类型的BottleNeck网络结构,借用下面的代码可能理解得更为清楚。
class BottleneckB(nn.Module):
expansion = 4
def __init__(self, inplanes, planes, stride=1, downsample=None):
super(BottleneckB, self).__init__()
assert inplanes == (planes * 4), 'inplanes != planes * 4'
assert stride == 1, 'stride != 1'
assert downsample is None, 'downsample is not None'
# 1x1 Conv
self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False) # inplanes = 1024, planes = 256
self.bn1 = nn.BatchNorm2d(planes)
# 3x3, dilate 2, Conv
self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=1, dilation=2,
padding=2, bias=False) # stride = 1, dilation = 2
self.bn2 = nn.BatchNorm2d(planes)
# 1x1 Conv
self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False)
self.bn3 = nn.BatchNorm2d(planes * 4)
self.relu = nn.ReLU(inplace=True)
self.downsample = downsample
self.stride = stride
# 1x1 Conv
self.extra_conv = nn.Sequential(
nn.Conv2d(inplanes, planes * 4, kernel_size=1, bias=False),
nn.BatchNorm2d(planes * 4)
)
def forward(self, x):
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
out = self.relu(out)
out = self.conv3(out)
out = self.bn3(out)
residual = self.extra_conv(x)
# downsample always is None, because stride=1 and inplanes=expansion * planes
if self.downsample is not None:
residual = self.downsample(x)
out += residual
out = self.relu(out)
return out
很容易将DetNet和目标检测器(无论是否饮食特征金字塔结构)结合,下图是基于DetNet的FPN结构。因为DetNet只是改变了FPN的骨干网络,所以固定除骨干网以外的其它结构。而且,因为没有减少ResNet-50自第4阶段后的空间尺寸,所以只简单地沿从顶向下的方向(in top-down path way)将这些阶段的输出进行相加。
作者使用8张Pascal TITAN XP GPU进行端到端的训练,权重衰减因子为0.0001,动量为0.9。每个mini-batch含2张图片,因为有8卡张并行训练,所有有效batch-size是16。为了避免过高的内存消耗,图象大小做了调整,短边统一为800像素,长边为1333像素。训练过程使用经典的"2x"方法,初始学习率设置为0.02,在120k和160k次迭代时乘以一下0.1的衰减因子,最终在180k次迭代后终止。此外,在最开始的500个迭代,使用学习率为0.02 x 0.3进行预热。
上面是在FPN中使用三种不同的骨干网进行测试的对比结果。相比ResNet-59,DetNet-50新增一个阶段P6,很自然地会认为DetNet-50之所以表现优于ResNet-59完全是得意于更多的参数,但和ResNet-101的对比可以看出,虽然ResNet-101的参数远多于DetNet-50,表现却弱于DetNet50,所以说并不是参数多少决定的,只能说DetNet-50在设计上更加合理。
为了进一步检验DetNet在目标检测上的性能,基于DetNet-59和ResNet-50的FPN从头进行训练,也就是说不使用ImageNet上的预训练参数。结果(见上表)表明DetNet-59的mAP高出ResNet-50约1.8个点,这也就进一步证明了DetNet更适合于目标检测。