SiamRPN++

  • 论文来源
  • 论文背景
    • 什么是目标跟踪
    • 什么是孪生网络结构
    • Siamese的局限
    • 解决的问题
  • 论文分析
    • 创新点一:空间感知策略
    • 创新点二:ResNet-50深层网络
    • 创新点三:多层特征融合
    • 创新点四:深层互相关
  • 代码分析
    • 整体代码简述
    • (1)特征提取网络
    • (2)逐层的特征融合(Layerwise Aggregation)
    • (3)深层互相关Depthwise Cross Correlation
    • 总结
  • 论文翻译
    • Abstract
    • 1. Introduction
    • 2. Related Work
    • 3. Siamese Tracking with Very Deep Networks
    • 3.1. Analysis on Siamese Networks for Tracking
    • 3.2. ResNet-driven Siamese Tracking
    • 3.3. Layer-wise Aggregation
    • 3.4. Depthwise Cross Correlation
    • 4. Experimental Results
    • 4.1. Training Dataset and Evaluation
    • 4.2. Implementation Details
    • 4.3. Ablation Experiments
    • 4.4. Comparison with the state-of-the-art
    • 5. Conclusions

论文来源

论文链接
代码链接

本文参考:
如何理解SiamRPN++?
SiamRPN++理解
SiamRPN++: 基于深度网络的孪生视觉跟踪的进化
【SOT】Siamese RPN++ 论文和代码解析
SiamRPN++算法详解

论文背景

什么是目标跟踪

使用视频序列第一帧的图像(包括bounding box的位置),来找出目标出现在后序帧位置的一种方法。

什么是孪生网络结构

在进入到正式理解SiamRPN++之前,为了更好的理解这篇论文,我们需要先了解一下孪生网络的结构。
孪生网络是一种度量学习的方法,而度量学习又被称为相似度学习。

孪生网络结构被较早地利用在人脸识别的领域(《Learning a Similarity Metric Discriminatively, with Application to Face Verification》)。其思想是将一个训练样本(已知类别)和一个测试样本(未知类别)输入到两个CNN(这两个CNN往往是权值共享的)中,从而获得两个特征向量,然后通过计算这两个特征向量的的相似度,相似度越高表明其越可能是同一个类别。在上面这篇论文中,衡量这种相似度的方法是L1距离。

在目标领域中,最早利用这种思想的是SiamFC,其网络结构如上图。输入的z 是第一帧ROI(也就是在手动在第一帧选择的bounding box),x则是后一帧的图片。两者分别通过两个CNN,得到两张特征图。再通过一次卷积操作(L=(22-6)/1+1=17),获得最后形状为17×17×1的特征图。在特征图上响应值越高的位置,代表其越可能有目标存在。其想法就类似于对上面的人脸识别孪生网络添加了一次卷积操作。获得特征图后,可以获得相应的损失函数。y={-1,+1}为真实标签,v是特征图相应位置的值。下列第一个式子是特征图上每一个点的loss,第二个式子是对第一个式子作一个平均,第三个式子就是优化目标。

However, both Siamese-FC and CFNet are lack of boundingbox regression and need to do multi-scale test which makesit less elegant. The main drawback of these real-time track-ers is their unsatisfying accuracy and robustness comparedto state-of-the-art correlation filter approaches.但是,Siamese-FC和CFNet都没有边界框回归,因此需要进行多尺度测试,这使得它不太美观。这些实时跟踪器的主要缺点是,与最新的相关滤波器方法相比,它们的精度和鲁棒性不令人满意。

为了解决SiamFC的这两个问题,就有了SiamRPN。可以看到前半部分Siamese Network部分和SiamFC一模一样(通道数有变化)。区别就在于后面的RPN网络部分。特征图会通过一个卷积层进入到两个分支中,在Classification Branch中的4×4×(2k×256)特征图等价于有2k个4×4×256形状的卷积核,对20×20×256作卷积操作,于是可以获得17×17×2k的特征图,Regression Branch操作同理。而RPN网络进行的是多任务的学习。17×17×2k做的是区分目标和背景,其会被分为k个groups,每个group会有一层正例,一层负例。最后会用softmax + cross-entropy loss进行损失计算。17×17×4k同样会被分为k groups,每个group有四层,分别预测dx,dy,dw,dh。而k的值即为生成的anchors的数目。而关于anchor box的运行机制,可以看下方第二张图(来自SSD)。


correlation操作详解(涉及到后续改进):以分类分支为例,在RPN中,分类分支需要输出一个通道数为 2k 的特征图( k 为anchor个数),SiamFC中使用的correlation只能提供通道数为1的响应图,无法满足要求。所以我们换了一个想法,把correlation层当成一个卷积层,template分支提取的特征作为卷积核,detection分支提取的特征作为卷积层的input,这样只需要改变卷积核的形状就可以达到输出2k通道数的目的。具体做法为使用了两个不同的卷积层,template分支的卷积层负责升维,把通道数提升到 256*2k ,为了保持对齐,detection分支也增加了一个卷积层,不过保持通道数不变。之后进行correlation操作(卷积),得到最终的分类结果。

Siamese的局限

作者的实验发现,较深的网络,如 ResNet, 无法带来跟踪精度提升的原因在于:

  1. 更深的网络的填充会破坏严格的平移不变性(Strict translation invariance)
    padding会破坏这种性质
    现代化网络:随着何铠明等提出残差网络以后,网络的深度得到了巨大的释放,通常物体检测和语义分割的baseline backbone都采用ResNet50的结构。为了保证网络具有适当/整齐的分辨率,几乎所有的现代网络backbone都需要执行padding操作。而ResNet网络中具有padding操作,即该网络肯定不具备严格的平移不变性,padding的引入会使得网络输出的响应对不同位置有了不同的认知。而我们进行进一步的训练是希望网络学习到如何通过物体的表观特征来分辨回归物体,这就限制了深网络在tracking领域的应用。

  2. RPN要求分类和回归具有不对称特征
    即如果将搜索区域图像和模板区域图像进行互换,输出的结果应该保持不变。(因为是相似度,所以应该有对称性)。
    由于SiamRPN不再是进行相似度计算,而是通过计算回归的偏移量和分类的分数来选择最终的目标,这将使得该网络不再具有对称性。因而在SiamRPN的改进中需要引入非对称的部件,如果完全Siamese的话没法达到目的,这一点主要会引导后面的correlation设计。

文中作者引入空间感知策略来克服第一个困难,并对第二个问题进行讨论。

针对第一个问题作者认为Strict translation invariance只存在没有padding的网络中例如Alexnet,并且假设违反了这种限制将导致空间的倾斜(spatial bias)。
作者发现如下的几个参数,对跟踪结果的影响,非常巨大:the receptive field size of neurons; network stride; feature padding。

具体来说,感受野决定了用于计算 feature 的图像区域。较大的感受野,提供了更好的 image context 信息,而一个较小的感受野可能无法捕获目标的结构信息;

网络的步长,影响了定位准确性的程度,特别是对小目标而言;与此同时,它也控制了输出 feature map 的大小,从而影响了 feature 的判别性和检测精度。

此外,对于一个全卷积的结构来说,feature padding 对卷积来说,会在模型训练中,引入潜在的位置偏移,从而使得当一个目标移动到接近搜索范围边界的时候,很难做出准确的预测。这三个因素,同时造成了 Siamese Tracker 无法很好的从更顶尖的模型中收益。

解决的问题

该论文主要解决的问题是将深层基准网络ResNet、Inception等网络应用到基于孪生网络的跟踪网络中。在SiameseFC算法之后,尽管已经有很多的基于孪生网络的跟踪算法,但是大家可能会注意到一个问题是,这些网络都使用浅层的类AlexNet做为基准特征提取器。其实在这之前,也有学者们尝试着使用深层的网络,但是发现直接使用预训练好的深层网络反而会导致跟踪算法精度的下降,因此,这成为了一个基于孪生网络的跟踪器需要解决的一个关键问题!

论文分析

创新点一:空间感知策略

1.平移不变性的问题
作者认为Strict translation invariance只存在没有padding的网络中例如Alexnet,并且假设违反了这种限制将导致空间的倾斜(spatial bias)。作者发现如下的几个参数,对跟踪结果的影响,非常巨大:the receptive field size of neurons; network stride; feature padding。具体来说,感受野决定了用于计算 feature 的图像区域。较大的感受野,提供了更好的 image context 信息,而一个较小的感受野可能无法捕获目标的结构信息;网络的步长,影响了定位准确性的程度,特别是对小目标而言;与此同时,它也控制了输出 feature map 的大小,从而影响了 feature 的判别性和检测精度。此外,对于一个全卷积的结构来说,feature padding 对卷积来说,会在模型训练中,引入潜在的位置偏移,从而使得当一个目标移动到接近搜索范围边界的时候,很难做出准确的预测。这三个因素,同时造成了 Siamese Tracker 无法很好的从更顶尖的模型中收益。

2.如果破坏了网络的平移不变性,具体会带来什么问题呢?
如果现代化网络的平移不变性被破坏以后,带来的弊端就是会学习到位置偏见:按照SiamFC的训练方法,正样本都在正中心,网络逐渐会学习到这种统计特性,学到样本中正样本分布的情况。即简而言之,网络会给图像的中心位置分配更大的权重。

3.空间感知策略
要想使用更深的特征提取网络,就要避免对目标产生强烈的中心偏移,我们采用空间感知采样策略训练了具有ResNet-50骨干网的SiamRPN。在训练过程中,我们不再把正样本块放在图像正中心,而是按照均匀分布的采样方式让目标在中心点附近进行偏移。随着偏移的范围增大,深度网络可以由刚开始的完全没有效果逐渐变好。
具体的效果如下图所示:当我们将shift设置为0时,网络只会关注图像中心的位置,对应到图中就是只有中心位置具有较大的响应值;而当我们将shift设置为16时,网络开始关注更多的图像范围,对应到图中就是响应的范围会扩大,颜色由深变浅;而当我们将shift设置为32时,网络会关注更大额图像范围,对应到图中就是响应的范围变得更大,颜色也更加多样化。

所以说,通过均匀分布的采样方式让目标在中心点附近进行偏移,可以缓解网络因为破坏了严格平移不变性带来的影响,即消除了位置偏见,让现代化网络可以应用于跟踪算法中。

4.为什么该问题在检测任务和语义分割任务中并不存在?
因为对于物体检测和语义分割而言,训练过程中,物体本身就是在全图的每个位置较为均匀的分布。我们可以很容易的验证,如果在物体检测网络只训练标注在图像中心的样本,而边缘的样本都不进行训练,那么显然,这样训练的网络只会对图像的中心位置产生高响应,边缘位置就随缘了,不难想象这种时候边缘位置的性能显然会大幅衰减。而更为致命的是,按照SiamFC的训练方式,中心位置为正样本,边缘位置为负样本。那么网络只会记录下边缘永远为负,不管表观是什么样子了。这完全背离了我们训练的初衷。

创新点二:ResNet-50深层网络

我们主要的实验实在ResNet-50上做的。现代化网络一般都是stride32,但跟踪为了定位的准确性,一般stride都比较小(Siamese系列一般都为8),所以我们把ResNet最后两个block的stride去掉了,同时增加了dilated convolution,一是为了增加感受野,二是为了能利用上预训练参数。论文中提到的MobileNet等现代化网络也是进行了这样的改动。如上图所示,改过之后,后面三个block的分辨率就一致了。

在训练过程中采用了新的采样策略后,我们可以训练ResNet网络了,并且能够正常跟踪一些视频了。(之前跟踪过程中一直聚集在中心,根本无法正常跟踪目标)。对backbone进行finetune以后,又能够进一步得到一些性能提升。

由于所有层的填充都保持不变,模板特征的空间大小增加到15,这给相关模块带来了沉重的计算负担。因此,作者裁剪中心7×7区域作为模板特征,其中每个特征单元仍然可以捕获整个目标区域。

创新点三:多层特征融合

在以前的工作中,仅使用像AlexNet这样的浅层网络,多层特性不能提供非常不同的作用。然而,考虑到感受野的变化,ResNet中的不同层更有意义。浅层的特征主要集中在低层次的信息上,如颜色、形状等,对于定位是必不可少的,而缺乏语义信息;深层的特征具有丰富的语义信息,在某些挑战场景(如运动模糊、形变等)中是有益的。使用这种丰富的层次信息有助于跟踪。


在多层使用siamrpn的好处
如创新点2中的图所示,我们会观察到作者分别在conv3_3、conv4_6和conv5_3的分支上使用siamrpn网络,并将前面siamrpn的结果输入到后面的siamrpn网络中,该思路类似于cvpr2019值的C-RPN算法,通过多级级联具有两个优点:

  1. 可以通过多个siamrpn来选择出多样化的样本或者具有判别性的样本块,第一个siamrpn可以去除掉一些特别简单的样本块,而后面的网络进一步进行滤除,最终剩余一些hard negative sample,这样其实有利于提升网络的判别能力。
  2. 由于使用了多级回归操作,因此可以获得一个更加准确的BB。

创新点四:深层互相关

互相关模块是嵌入两个分支信息的核心操作。SiamFC利用互相关层获得用于目标定位的单信道响应图。在SiamRPN中,通过添加一个巨大的卷积层来缩放信道,扩展了互相关以嵌入更高级别的信息,例如anchors。重上行信道模块使得参数分布严重不平衡,使得SiamRPN训练优化困难。
  作者提出了一个轻量级的互相关层,称为深度互相关,以实现有效的信息关联。深度互相关层包含的参数比SiamRPN中使用的互相关层少10倍,性能与之相当。
  为了达到这个目标,作者采用一个 conv-bn block 来调整特征,来适应跟踪任务。Bounding box prediction 和 基于 anchor 的分类都是非对称的 (asymmetrical)。为了编码这种不同,the template branch 和 search branch 传输两个 non-shared convolutional layers。然后,这两个 feature maps 是有相同个数的 channels,然后一个 channel 一个 channel 的进行 correlation operation。另一个 conv-bn-relu block,用于融合不同 channel 的输出。最终,最后一个卷积层,用于输出 classification 和 regression 的结果。

通过用 Depthwise correlation 替换掉 cross-correlation,我们可以很大程度上降低计算代价和内存使用。通过这种方式,template 和 search branch 的参数数量就会趋于平衡,导致训练过程更加稳定。

如上图所示,图中(a)互相关层预测方法是取自SiamFC中目标模板和搜索图像之间的单通道相似性映射。模板图像和搜索图像在经过网络后输出通道数相同的featuremap,两者逐通道相互卷积,最后取平均值。
图中(b)上行信道互相关层,取自SiamRPN,模板图像和搜索图像经过特征提取网络生成feature map后,输入RPN网络,分类分支和回归分支分别经过非权值共享的卷积层后在相互卷积,参数量十分巨大。
图中(c)深度互相关层预测模板和搜索图像之间的多通道相关性特征,取自SiamRPN++,模板图像经过卷积层后并不像SiamRPN那样将通道数增加2k倍(每个grid生成k个anchors),而是保持不变,同时搜索图像也与模板图像保持一致,两者逐通道相互卷积,之后接一个1×1的卷积层,再改变通道数,这样在保持精度的同时减少了参数量。如下第一张图,输入两个从ResNet获得的特征层(channel数目是一致的),先同样分别通过一个卷积层(由于需要学的任务不同,所以卷积层参数不共享),再分别进行DW卷积操作,然后两分支再分别通过一层不同的卷积层改变通道数以获得想要的分类和回归结果。这样一来,就可以减少计算量,且使得两个分支更加平衡。

通道数的研究:
下图说明了一个有趣的现象。同一类别的对象在同一信道(148信道的car、222信道的person和226信道的face)上具有高响应,而其余信道的响应被抑制。这一特性可以理解为,由深度互相关产生的信道方向特征几乎是正交的,每个信道代表一些语义信息。我们还分析了当使用上通道互相关时的热图,而响应图的解释性较差。

上图为conv4中的深度相关输出通道。conv4共有256个通道,但只有少数信道在跟踪过程中有较高的响应。因此,选择第148、222、226个通道作为演示,它们是图中的第2、3、4行。第一行包含来自OTB数据集的六个相应的搜索区域。不同的通道表示不同的语义,第148通道对汽车的响应较高,而对人和脸的响应较低。第222频道和第226频道分别对人和脸有很高的响应。

代码分析

整体代码简述

整体代码主要分成两个部分,第一个是初始帧标记的目标box为temple,第二个部分为追踪过程中的track目标。

代码首先将初始帧进行处理成127×127的形式然后进入主体网络(包括resnet50(经过resnet50后输出三个feature_map大小分别是[1, 512, 15, 15]、[1, 1024, 15, 15]、[1, 2048, 15, 15])和adjustnect)框架中,并将输出的3个feature_map,其大小分别是[1, 128, 7, 7]、[1, 256, 7, 7]、[1, 512, 7, 7]进行保存。
追踪过程(始终保持上一帧目标位置的中心),根据上一帧目标位置的得分判断是否进行长期追踪(长期追踪围绕中心位置搜索的面积较大写,短期追踪围绕中心面积搜索的范围较小,是否进行长期追踪主要是根据上上衣目标帧的得分决定),按照追踪形式将上一帧范围内的像素进行剪裁填充以及resize成255×255,将剪裁过后的图片输入到网络框架中(包括resnet50经过resnet50后输出三个feature_map大小分别是[1, 512, 31, 31]、[1, 1024, 31, 31]、[1, 2048, 31, 31])和adjustnect),并输出三个feature_map大小分别为[1, 128, 31, 31]、[1, 256, 31, 31]、[1, 512, 31, 31]
最后temple和track中分别输出的feature_map进入rpn网络中,按照索引一一对应进行卷积操,之后将templw中的feature展开成[temple.size[0]*temple.size[1], 1,temple.size[2],temple. zise[3]]的形式作为kernel,track同样改变通道数作输入层进行卷积操作,之后将三次卷积操作的结果按照一定权重(网络中训练出来的权重参数)相加最后输出。rpn中有两个分支一个用来预测是否是目标类别cls输出的tensor大小[1, 10, 25,25](选择的anchor数为5,预测为2分类因此第二个维度上的通道数为10),另一个分支预测位置的偏移量loc,输出的tensor大小为[1, 20, 25, 25](选择的anchor为5,预测四个位置上的偏移[x, y, w, h])。
根据cls何loc的输出预测是否是目标以及目标位置,将cls何loc分别展开成[3125, 2], [3125, 4]的形式,cls经过softmax分类器,并取最后一列最为目标位置的预测概率。anchor box结合回归结果得出bounding box。最后使用余弦窗和尺度变化惩罚来对cls中经过softmax处理过后的最后一列进行排序,选最好的。余弦窗是为了抑制距离过大的,尺度惩罚是为了抑制尺度大的变化

特征提取网络: 改进的残差网络ResNet-50
逐层特征融合(Layerwise Aggregation)
Depthwise Cross Correlation
接下来按照分成的三部分进行详细说明。

(1)特征提取网络

论文中提到

The original ResNet has a large stride of 32 pixels, which is not suitable for dense Siamese network prediction. As shown in Fig.3, we reduce the effective strides at the last two block from 16 pixels and 32 pixels to 8 pixels by modifying the conv4 and conv5 block to have unit spatial stride, and also increase its receptive field by dilated convolutions. An extra 1 x 1 convolution layer is appended to each of block outputs to reduce the channel to 256.

作者的意思就是嫌弃原来ResNet的stride过大,从而在conv4和conv5中将stride=2改动为stride=1。但是同时为了保持之前的感受野,采用了空洞卷积。其代码定义如下:

class ResNetPP(nn.Module):def __init__(self, block, layers, used_layers):self.inplanes = 64super(ResNetPP, self).__init__()self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=0,  # 3bias=False)self.bn1 = nn.BatchNorm2d(64)self.relu = nn.ReLU(inplace=True)self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)self.layer1 = self._make_layer(block, 64, layers[0])self.layer2 = self._make_layer(block, 128, layers[1], stride=2)self.feature_size = 128 * block.expansionself.used_layers = used_layerslayer3 = True if 3 in used_layers else Falselayer4 = True if 4 in used_layers else Falseif layer3:self.layer3 = self._make_layer(block, 256, layers[2],stride=1, dilation=2)  # 15x15, 7x7self.feature_size = (256 + 128) * block.expansionelse:self.layer3 = lambda x: x  # identityif layer4:self.layer4 = self._make_layer(block, 512, layers[3],stride=1, dilation=4)  # 7x7, 3x3self.feature_size = 512 * block.expansionelse:self.layer4 = lambda x: x  # identityfor m in self.modules():if isinstance(m, nn.Conv2d):n = m.kernel_size[0] * m.kernel_size[1] * m.out_channelsm.weight.data.normal_(0, math.sqrt(2. / n))elif isinstance(m, nn.BatchNorm2d):m.weight.data.fill_(1)m.bias.data.zero_()def _make_layer(self, block, planes, blocks, stride=1, dilation=1):downsample = Nonedd = dilationif stride != 1 or self.inplanes != planes * block.expansion:if stride == 1 and dilation == 1:downsample = nn.Sequential(nn.Conv2d(self.inplanes, planes * block.expansion,kernel_size=1, stride=stride, bias=False),nn.BatchNorm2d(planes * block.expansion),)else:if dilation > 1:dd = dilation // 2padding = ddelse:dd = 1padding = 0downsample = nn.Sequential(nn.Conv2d(self.inplanes, planes * block.expansion,kernel_size=3, stride=stride, bias=False,padding=padding, dilation=dd),nn.BatchNorm2d(planes * block.expansion),)layers = []layers.append(block(self.inplanes, planes, stride,downsample, dilation=dilation))self.inplanes = planes * block.expansionfor i in range(1, blocks):layers.append(block(self.inplanes, planes, dilation=dilation))return nn.Sequential(*layers)def forward(self, x):x = self.conv1(x)x = self.bn1(x)x_ = self.relu(x)x = self.maxpool(x_)p1 = self.layer1(x)p2 = self.layer2(p1)p3 = self.layer3(p2)p4 = self.layer4(p3)out = [x_, p1, p2, p3, p4]out = [out[i] for i in self.used_layers]if len(out) == 1:return out[0]else:return out

从上述代码中的这段代码可以看出

if layer3:self.layer3 = self._make_layer(block, 256, layers[2],stride=1, dilation=2)  # 15x15, 7x7self.feature_size = (256 + 128) * block.expansionelse:self.layer3 = lambda x: x  # identityif layer4:self.layer4 = self._make_layer(block, 512, layers[3],stride=1, dilation=4)  # 7x7, 3x3self.feature_size = 512 * block.expansion

作者在原来的ResNet-50的基础上进行了一些修改,包括stride=1与空洞卷积的使用。

(2)逐层的特征融合(Layerwise Aggregation)

熟悉目标检测的FPN网络的小伙伴们一定不会对Layerwise Aggregation陌生。一般而言,浅层的网络包含的信息更多有关于物体的颜色、条纹等,深层的网络包含的信息更多包含物体的语义特征。正如作者文中提到的那样:

Features from earlier layers will mainly focus on low level information such as color, shape, are essential for localization, while lacking of semantic information
使用特征融合可以弥补浅层信息和深层信息的不足,更有助于单目标追踪。

本文中的Siamese RPN++利用了ResNet-50在conv3、conv4、conv5的输出作为输入。如下图所示。

也就是这三个输出分别都有各自的RPN网络,并不是通过堆叠或者相加进行融合。这在代码中是这样体现的

self.features = resnet50(**{'used_layers': [2, 3, 4]})

先使用改进后的resnet50作为特征提取网络,返回输出的层 id 为[2,3,4],其实就是conv3、conv4、conv5的输出。

然后分别对template图像和需要detection的图像分别进行特征提取。

        zf = self.features(template)xf = self.features(detection)

事实上,zf 和 xf 并不是单一的特征图,而是一个列表,每个列表中包含了三个特征图。

接着,在特征提取结束后,需要对提取的特征进行调整,代码中是这样实现的:

     zf = self.neck(zf)xf = self.neck(xf)

那么这个neck的定义如下:

AdjustAllLayer(**{'in_channels': [512, 1024, 2048], 'out_channels': [256, 256, 256]})

AdjustAllLayer定义如下:

class AdjustLayer(nn.Module):def __init__(self, in_channels, out_channels):super(AdjustLayer, self).__init__()self.downsample = nn.Sequential(nn.Conv2d(in_channels, out_channels, kernel_size=1, bias=False),nn.BatchNorm2d(out_channels),)def forward(self, x):x = self.downsample(x)if x.size(3) < 20:l = 4r = l + 7x = x[:, :, l:r, l:r]return xclass AdjustAllLayer(nn.Module):def __init__(self, in_channels, out_channels):super(AdjustAllLayer, self).__init__()self.num = len(out_channels)if self.num == 1:self.downsample = AdjustLayer(in_channels[0], out_channels[0])else:for i in range(self.num):self.add_module('downsample'+str(i+2),AdjustLayer(in_channels[i], out_channels[i]))def forward(self, features):if self.num == 1:return self.downsample(features)else:out = []for i in range(self.num):adj_layer = getattr(self, 'downsample'+str(i+2))out.append(adj_layer(features[i]))return out

可见上面的AdjustAllLayer的作用就是对特征提取网络输出的三个特征图分别做1x1卷积,调整所有特征图的通道数为256。论文中也有提及:

An extra 1 x 1 convolution layer is appended to each of block outputs to reduce the channel to 256

上述代码中有个很有趣的forward,即

 def forward(self, x):x = self.downsample(x)if x.size(3) < 20:l = 4r = l + 7x = x[:, :, l:r, l:r]return x

这么做是为了什么呢?我们发现原文中提到:

Since the paddings of all layers are kept, the spatial size of the template feature increases to 15, which imposes a heavy computational burden on the correlation module. Thus we crop the center 7 x 7 regions as the template feature where each feature cell can still capture the entire target region.

原来是因为降低运算量所以对template的特征图进行了裁剪,恍然大悟呀!原来原文中的细节都在代码中有所描述,所以我十分推荐大家结合代码看论文,这是非常容易进行理解的。

特征提取以及所有通道都压缩至256后,紧接着代码实现如下:

  cls, loc = self.head(zf, xf)

其中head定义如下:

 self.head = MultiRPN(**{'anchor_num': 5, 'in_channels': [256, 256, 256], 'weighted': True})

其中MultiRPN定义如下:

class MultiRPN(RPN):def __init__(self, anchor_num, in_channels, weighted=False):super(MultiRPN, self).__init__()self.weighted = weightedfor i in range(len(in_channels)):self.add_module('rpn'+str(i+2),DepthwiseRPN(anchor_num, in_channels[i], in_channels[i]))if self.weighted:self.cls_weight = nn.Parameter(torch.ones(len(in_channels)))self.loc_weight = nn.Parameter(torch.ones(len(in_channels)))def forward(self, z_fs, x_fs):cls = []loc = []for idx, (z_f, x_f) in enumerate(zip(z_fs, x_fs), start=2):rpn = getattr(self, 'rpn'+str(idx))c, l = rpn(z_f, x_f)cls.append(c)loc.append(l)if self.weighted:cls_weight = F.softmax(self.cls_weight, 0)loc_weight = F.softmax(self.loc_weight, 0)def avg(lst):return sum(lst) / len(lst)def weighted_avg(lst, weight):s = 0for i in range(len(weight)):s += lst[i] * weight[i]return sif self.weighted:return weighted_avg(cls, cls_weight), weighted_avg(loc, loc_weight)else:return avg(cls), avg(loc)

(3)深层互相关Depthwise Cross Correlation

Siamese RPN++中选用了Depth-wise Cross Correlation Layer对Template和Search Region进行卷积运算。这么做是为了降低计算量。具体实现过程如下

class DepthwiseXCorr(nn.Module):def __init__(self, in_channels, hidden, out_channels, kernel_size=3, hidden_kernel_size=5):super(DepthwiseXCorr, self).__init__()self.conv_kernel = nn.Sequential(nn.Conv2d(in_channels, hidden, kernel_size=kernel_size, bias=False),nn.BatchNorm2d(hidden),nn.ReLU(inplace=True),)self.conv_search = nn.Sequential(nn.Conv2d(in_channels, hidden, kernel_size=kernel_size, bias=False),nn.BatchNorm2d(hidden),nn.ReLU(inplace=True),)self.head = nn.Sequential(nn.Conv2d(hidden, hidden, kernel_size=1, bias=False),nn.BatchNorm2d(hidden),nn.ReLU(inplace=True),nn.Conv2d(hidden, out_channels, kernel_size=1))def forward(self, kernel, search):kernel = self.conv_kernel(kernel)search = self.conv_search(search)feature = xcorr_depthwise(search, kernel)out = self.head(feature) #维度提升return outclass DepthwiseRPN(RPN):def __init__(self, anchor_num=5, in_channels=256, out_channels=256):super(DepthwiseRPN, self).__init__()self.cls = DepthwiseXCorr(in_channels, out_channels, 2 * anchor_num)self.loc = DepthwiseXCorr(in_channels, out_channels, 4 * anchor_num)def forward(self, z_f, x_f):cls = self.cls(z_f, x_f)loc = self.loc(z_f, x_f)return cls, loc

其中xcorr_depthwise定义如下

def xcorr_depthwise(x, kernel):"""depthwise cross correlation"""batch = kernel.size(0)channel = kernel.size(1)x = x.view(1, batch*channel, x.size(2), x.size(3))kernel = kernel.view(batch*channel, 1, kernel.size(2), kernel.size(3))out = F.conv2d(x, kernel, groups=batch*channel)out = out.view(batch, channel, out.size(2), out.size(3))return out

实质上Depthwise Cross Correlation采用的就是分组卷积的思想,分组卷积可以带来运算量的大幅度降低,被广泛用于MobileNet系列网络中。

通过上述代码可以看出,与Siamese RPN网络不同,Siamese RPN++提升网络通道数为2k或者4k的操作是在卷积操作( Cross Correlation)之后,而Siamese RPN网络是在卷积操作之前,这样就减少了大量的计算量了。这在DepthwiseXCorr类中的forward中定义出来了,如下:

    def forward(self, kernel, search):kernel = self.conv_kernel(kernel)search = self.conv_search(search)feature = xcorr_depthwise(search, kernel)out = self.head(feature) #维度提升return out

上面的self.head运算就是升维运算(到2k或者4k),可以看出,其发生在xcorr_depthwise之后。

最后的最后,网络最后输出的3个cls和loc分支进行了按权重融合。这在MultiRPN的forward定义了,如下:

 def forward(self, z_fs, x_fs):cls = []loc = []for idx, (z_f, x_f) in enumerate(zip(z_fs, x_fs), start=2):rpn = getattr(self, 'rpn'+str(idx))c, l = rpn(z_f, x_f)cls.append(c)loc.append(l)if self.weighted:cls_weight = F.softmax(self.cls_weight, 0)loc_weight = F.softmax(self.loc_weight, 0)def avg(lst):return sum(lst) / len(lst)def weighted_avg(lst, weight):s = 0for i in range(len(weight)):s += lst[i] * weight[i]return sif self.weighted:return weighted_avg(cls, cls_weight), weighted_avg(loc, loc_weight)else:return avg(cls), avg(loc)

总结

至此,Siamese RPN++的网络结构就讲解结束了,代码总结如下:

 def forward(self, template, detection):zf = self.features(template) #ResNet-50特征提取xf = self.features(detection)zf = self.neck(zf) #降低维度为256xf = self.neck(xf)cls, loc = self.head(zf, xf) #RPNreturn cls, loc

总体的结构图如下:

图中右侧的支路中

  1. adj系列就是降维为256的1x1卷积
  2. DW_Conv就是Depthwise Cross Correlation操作
  3. Box_Head就是提升维度为4k的1x1卷积
  4. Cls_Head就是提升维度为2k的1x1卷积

至此,Siamese RPN++的结构解析也结束了。

论文翻译

Abstract

1. Introduction

2. Related Work

3. Siamese Tracking with Very Deep Networks

3.1. Analysis on Siamese Networks for Tracking

3.2. ResNet-driven Siamese Tracking

3.3. Layer-wise Aggregation

3.4. Depthwise Cross Correlation

4. Experimental Results

4.1. Training Dataset and Evaluation

4.2. Implementation Details

4.3. Ablation Experiments

4.4. Comparison with the state-of-the-art

5. Conclusions

SiameseRPN++分析相关推荐

  1. SiameseRPN详解

    SiameseRPN 论文来源 论文背景 一,简介 二,研究动机 三.相关工作 论文理论 注意: 网络结构: 1.Siamese Network 2.RPN 3.LOSS计算 4.Tracking 论 ...

  2. 【Golang源码分析】Go Web常用程序包gorilla/mux的使用与源码简析

    目录[阅读时间:约10分钟] 一.概述 二.对比: gorilla/mux与net/http DefaultServeMux 三.简单使用 四.源码简析 1.NewRouter函数 2.HandleF ...

  3. 2022-2028年中国自动驾驶系统行业现状调研分析报告

    [报告类型]产业研究 [报告价格]4500起 [出版时间]即时更新(交付时间约3个工作日) [发布机构]智研瞻产业研究院 [报告格式]PDF版 本报告介绍了中国自动驾驶系统行业市场行业相关概述.中国自 ...

  4. 2022-2028年中国阻尼涂料市场研究及前瞻分析报告

    [报告类型]产业研究 [报告价格]4500起 [出版时间]即时更新(交付时间约3个工作日) [发布机构]智研瞻产业研究院 [报告格式]PDF版 本报告介绍了中国阻尼涂料行业市场行业相关概述.中国阻尼涂 ...

  5. 2021-2028年中国阻燃装饰行业市场需求与投资规划分析报告

    [报告类型]产业研究 [报告价格]4500起 [出版时间]即时更新(交付时间约3个工作日) [发布机构]智研瞻产业研究院 [报告格式]PDF版 本报告介绍了中国阻燃装饰行业市场行业相关概述.中国阻燃装 ...

  6. 2022-2028年全球与中国漂白吸水棉市场研究及前瞻分析报告

    [报告类型]产业研究 [报告价格]4500起 [出版时间]即时更新(交付时间约3个工作日) [发布机构]智研瞻产业研究院 [报告格式]PDF版 本报告介绍了全球与中国漂白吸水棉行业市场行业相关概述.全 ...

  7. 2022-2028年全球与中国青苔清洗剂市场研究及前瞻分析报告

    [报告类型]产业研究 [报告价格]4500起 [出版时间]即时更新(交付时间约3个工作日) [发布机构]智研瞻产业研究院 [报告格式]PDF版 本报告介绍了全球与中国青苔清洗剂行业市场行业相关概述.全 ...

  8. 2022-2028年全球与中国氢碘化物市场智研瞻分析报告

    [报告类型]产业研究 [报告价格]4500起 [出版时间]即时更新(交付时间约3个工作日) [发布机构]智研瞻产业研究院 [报告格式]PDF版 本报告介绍了全球与中国氢碘化物行业市场行业相关概述.全球 ...

  9. 2022-2028年全球与中国人字拖市场研究及前瞻分析报告

    [报告类型]产业研究 [报告价格]4500起 [出版时间]即时更新(交付时间约3个工作日) [发布机构]智研瞻产业研究院 [报告格式]PDF版 本报告介绍了全球与中国人字拖行业市场行业相关概述.全球与 ...

最新文章

  1. Tomcat 1099端口占用重启无效,查不到进程,改换端口无效解决方案
  2. 21 个HTML网页转RSS Feeds的工具
  3. 利用组策略防止计算机访问共享资源
  4. 使用logminer进行审计 Audit by using logminer
  5. Android中TimePicker时间选择器的使用和获取选择的时和分
  6. 【深度学习】Pytorch编写代码基本步骤思想
  7. Spark记录-Scala语法基础
  8. python笔记1:字符串处理函数
  9. Java入门算法(树篇)
  10. [密码学基础][每个信息安全博士生应该知道的52件事][Bristol52]51.基于ID的加密安全模型,描述IBE方案
  11. 微信小程序入门一:点击事件
  12. 面向对象的JavaScript-007-Function.prototype.bind() 的4种作用
  13. 吴军-《智能时代》-简介
  14. 集成Android SlidingMenu(SlideMenu)
  15. (转)步进电机扭矩计算公式
  16. PMP学习笔记 第5章 项目范围管理
  17. 轻量级web api_API接口管理,这15种开源工具助你管理API
  18. 18个免费替代Photoshop的图像编辑软件
  19. 分析30万条微博评论,看毕业生与翟天临的爱恨情仇|数据会说话
  20. 太离谱了!一行Python代码写的游戏,我能这样玩一天!

热门文章

  1. fstat、stat和lstat 区别
  2. timerfd API使用总结
  3. 进程控制块PCB(进程描述符)
  4. 2019.04.09 电商25 结算功能1
  5. 集合之ArrayList(含JDK1.8源码分析)
  6. 三维空间两直线/线段最短距离、线段计算算法 【转】
  7. 第一类第二类丢失更新
  8. 游戏组件——挑战:创建NextBlock游戏组件
  9. Javascript中的翻转器
  10. 如何push一个docker镜像到DockerHub上