1. 为什么2024年还要回头读一篇2016年的跟踪论文
如果你现在去跟踪领域翻论文,大概率会陷入一种迷惑:满屏都是Transformer、分割掩码、基础大模型,甚至SAM都被魔改成了跟踪器。但只要你往引用链深处扒一层,几乎每篇方法论文的Related Work里都会出现同一篇名字——SiamFC,全卷积孪生网络用于目标跟踪。
我第一次认真读这篇论文是几年前,当时感觉就是一个AlexNet套了个互相关层,结构简单到不像能发CVPR的样子。后来自己动手复现、踩坑、调参,再回头对比后来的SiamRPN、DaSiamRPN、SiamRPN++这一整条技术线,才发现SiamFC真正厉害的地方不是精度,而是它把“目标跟踪”这个原本依赖在线更新、手工特征、相关滤波器的领域,重新拉回到了一条极度简洁、可以直接离线端到端训练的深度学习路线上。它不追速度,不当SOTA,但它给后来所有孪生跟踪器画好了骨架。
这篇文章适合三类人看:刚入门跟踪领域、想快速建立全局认知的研究生;想复现SiamFC做基线实验的工程师;以及已经在用后来方法、但一直没搞懂孪生网络为什么要“权值共享”的开发者。我会把网络结构、训练配方的逻辑、在线跟踪的细节,以及我实际复现过程中踩过的坑全部讲清楚。读完之后你再去看SiamRPN系列,会发现很多设计都是顺着SiamFC的痛点长出来的。
2. 网络结构拆解:模板分支和搜索分支到底在算什么
2.1 孪生结构两个字:共享
孪生网络这个名词听起来玄乎,其实形容得挺直白。SiamFC把输入组织成两路:一路是模板图像z,也就是视频第一帧里框住目标的那一小块;另一路是搜索图像x,一般是当前帧以目标上一帧位置为中心裁出来的一块更大的区域。两路输入各自过一个完全相同的卷积网络φ,网络参数一套,两边共用,这就是“孪生”二字的来源。
为什么非要共享参数?因为跟踪本质上要做的是“相似度比较”,而比较的前提是两边得落在同一个特征空间里。如果模板和搜索区域各用一套参数,那它们映射出来的特征根本不在一个坐标系下,匹配结果没有可比性。共享参数等价于告诉网络:你提取的是普适的目标外观特征,而不是针对某一路输入的专属特征。这是一切后续孪生跟踪器的根基。
两路特征提完之后,SiamFC不是像分类网络那样把特征向量拼起来再接全连接层,而是做了一次互相关操作,输出一张响应图。响应图上每个位置的值,代表搜索区域对应位置上的特征与模板特征有多相似。值越大,相似度越高,目标就越可能出现在那个位置。
2.2 全卷积的含义:取消全连接带来的自由
很多初学者会把全卷积理解成“网络里没有全连接层”这么一句废话,但实际上它带来了两个非常关键的工程性质。
第一个性质是输入尺寸不再固定。全连接层要求输入特征铺平后维度严格一致,而全卷积网络里全是卷积、池化、ReLU这类对空间尺寸天然鲁棒的操作,任何尺寸的输入都能算,只是输出特征图大小不同。SiamFC利用这一点,让模板分支输入127×127,搜索分支输入255×255,两者不需要裁剪成同一个大小,因为最后互相关操作允许两个空间尺寸不同的特征图直接配对。
第二个性质是位置信息的保留。全连接会把空间结构彻底打散,而卷积操作天然保持局部空间关系。响应图上的坐标可以与搜索图像的像素位置一一对应,这就让“找目标”变成了“找响应峰”,而不是“做全局分类”。我后面讲跟踪流程时你会看到,这种空间对应关系是所有后续处理的基础。
2.3 互相关层:把相似度比较换算成一次卷积
SiamFC的核心公式定义如下:
f(z, x) = φ(z) ⋆ φ(x) + b
这里的⋆不是数学上严格的卷积,而是互相关。模板特征图为6×6×128,搜索特征图为22×22×128,在通道维度上逐通道做滑窗匹配,最后把所有通道的响应累加起来,得到一张17×17的响应图。
为什么模板是6×6,搜索是22×22,输出是17×17?因为主干网络的步长是8。搜索区域255×255经过步长8的降采样后,特征图尺寸大约是(255-127)/8+1=17?其实严格的尺寸计算和padding有关,后面我会专门在踩坑部分展开。这里先记住一个核心结论:响应图的每一个像素对应搜索图像上一块约8×8像素的感受野区域,峰值位置乘以8,大致就是目标在搜索图像中的偏移量。
实现上有一个很实用的技巧:互相关完全可以等价地用卷积运算实现。把模板特征图当作卷积核,在搜索特征图上做滑窗卷积,输出正好就是响应图。PyTorch里用F.conv2d一行代码就能搞定,很多开源复现也是这么写的。如果不加padding,输出尺寸就是(22-6)/1+1=17,和理论完全吻合。
3. 训练配方拆解:数据、损失函数和收敛技巧
SiamFC能work,训练配方占了很大功劳。论文里的很多细节,比如正负样本怎么定义、损失怎么归一化、模板帧和目标帧之间隔多少帧取,都直接影响了最终跟踪效果的稳定性。
3.1 训练数据怎么构造:同一个视频里的“相似”与“不相似”
SiamFC用的训练集是ImageNet VID,这是一个视频目标检测数据集。训练时,从同一个视频里随机抽两帧,一帧作为模板z,另一帧作为搜索x。两帧里都包含同一个目标,但目标可能发生了位移、形变、遮挡或光照变化,因此它们构成一个“相似”样本对。
但光有相似样本还不够,网络还需要知道什么是不相似。SiamFC的做法比较聪明:它不制造跨视频的负样本对,而是在同一张搜索图内部定义正负位置。换句话说,模板z确实来自另一帧,搜索x来自当前帧;搜索图上以目标真实中心为中心、半径16像素以内的位置,对应的响应图区域标为正样本;超出这个范围的位置全部标为负样本。这个设计让搜索分支每次输入一张大图,网络同时学到“目标在这里”和“目标不在那里”的空间判别力。
这里有个容易忽略的细节:模板帧和搜索帧不能隔得太近。如果两帧紧挨着,目标几乎没动,网络很容易走捷径,靠“复制粘贴”式的低级特征匹配来蒙混过关。如果隔得太远,目标外观又可能变化过大。原论文的做法是从同一个视频里随机抽两帧,且保证两帧之间间隔在一定范围内,实际复现时很多代码把这个间隔定在10帧以内。太近模型会退化,这是一个我在实验里真实遇到过的坑,后面会展开讲。
3.2 logistic损失与正负样本定义
SiamFC的损失函数用的是logistic损失,也就是二分类交叉熵的连续版。对响应图上的每个位置u,定义标签y[u]∈{+1, -1},模型对每个位置输出一个得分v。损失写为:
ℓ(y, v) = log(1 + exp(-yv))
整个响应图所有位置的损失取平均,构成一个batch的损失。注意是“取平均”而不是“求和”,这个细节对学习率设置影响很大。不同batch大小下,取平均能让单样本梯度的量级保持一致,训练稳定性明显更好。原论文用32个序列作为batch,每个序列固定提取8组样本对,效果已经很稳。
正样本半径R=16是一个手工设定的超参数。它的物理含义是:在255×255的搜索图像上,目标中心偏移16像素以内都算“匹配成功”。因为响应图stride是8,16像素大约对应响应图上2个像素的范围。这个值定得太小,正样本太少,训练难收敛;定得太大,正样本区域宽松,跟踪结果会偏模糊。论文选择16是一个平衡经验值,后来很多复现实验也都沿用。
3.3 学习率安排:从1e-2到1e-5的对数衰减
SiamFC训练时使用SGD优化器,momentum设为0.9,weight decay设为5e-4。学习率从1e-2起步,在训练过程中呈对数衰减,收敛到1e-5。我第一眼看到1e-2的时候挺意外,这比常规分类任务的学习率大得多。原因在于模型用了logistic损失加平均归一化,梯度量级被控制得比较稳定,而网络本身又是一个偏浅的AlexNet式结构,前几层的学习率还有单独的缩放。
论文给了一个小细节:主干网络前两层的学习率乘以0.1,后几层保持全速。这个做法的逻辑是,浅层的卷积核更接近通用边缘、纹理检测器,如果学太快容易把预训练提供的底层特征破坏掉;高层特征更任务相关,可以放心大胆地学。我在复现时对比过这个学习率分层,去掉它之后模型依然能收敛,但训练初期的loss下降波动明显变大,最终跟踪精度也略有下滑。
3.4 训练阶段的结构细节:修正版AlexNet
SiamFC的主干网络长得像AlexNet,但做了几个关键改动。第一,去掉了所有全连接层,只保留5个卷积层加2个池化层,所以叫“fully-convolutional”。第二,去掉了conv1到conv4的padding,只保留conv5的padding,这样可以保持严格的位置映射关系。第三,每个卷积层后面接了ReLU,但只有前两层有局部响应归一化LRN层。
这里值得多说一句padding的问题。标准AlexNet的卷积层默认带padding,“SAME”式填充会让特征图每个位置都融合边界补零信息,特征图尺寸不好反推。对跟踪这种需要像素级定位的任务来说,空间映射关系的可解释性比特征表达力更重要,所以SiamFC采用了“valid”卷积——没有padding,特征图逐层缩小,最终模板特征6×6、搜索特征22×22,响应图17×17,每一步尺寸变化都能精确推算。
如果非要用带padding的版本会怎样?我在实验里试过,响应图空间对应关系会被边界效应污染,特别是目标靠近搜索区域边缘时,响应峰值会出现系统性偏移。所以这个改动不是实现细节,而是核心设计。
4. 在线跟踪流程:固定模板、多尺度搜索与边界约束
训练完成之后,网络参数就冻结了。SiamFC在做在线跟踪时并不会根据当前帧微调网络,这和后来的MDNet、ATOM等在线更新流派有本质区别。整个跟踪过程可以被压缩成三个动作:初始化模板、生成搜索区域、寻找响应峰值。
4.1 固定模板与“不更新”的哲学
SiamFC在第一帧里裁剪目标区域作为模板,然后在整个视频跟踪过程中,这张模板一直不变。这个策略后来被大量论文诟病,因为目标一旦发生剧烈形变、旋转或光照变化,初始模板的参考价值就会下降。但SiamFC之所以坚持不更新,有两个实际考量。
第一是速度。在线更新意味着每帧都要额外计算梯度并更新网络,这会显著降低跟踪帧率,破坏深度学习跟踪器“离线训练、在线前向”的干净分工。第二是稳定。更新模板存在一个经典风险:一旦某一帧跟踪漂移,拿错误的目标外观去更新模板,误差会随着时间累积,最终彻底丢失目标。SiamFC选择不做任何更新,相当于用“我能容忍外观缓慢变化”来换“我绝不因错误反馈而加速崩溃”。
实际跟踪中如果目标外观变化太剧烈,SiamFC确实会失效,这是它的明确短板。但反过来,固定模板也让它对轻微遮挡和短暂丢失更鲁棒——因为模型不会因为最近几帧的遮挡信息就把自己的判断带偏。在理解后续SiamRPN++和TransT等方法时,你会发现它们很多也在“更新与否”之间做折中,比如保存多个历史模板或使用一个内存模块,本质上都是在找稳定性和适应性的平衡点。
4.2 多尺度搜索:一个尺度算三遍
目标在视频中的尺寸会变化。为应对这个问题,SiamFC在每次跟踪时并不是只算一次响应图,而是对搜索区域做三个尺度的缩放,分别计算响应图。三个尺度因子是1.0375的幂次方:{1.0375^(-1), 1, 1.0375},大约相当于0.964、1.0、1.037三档。
具体操作流程是这样的:以上一帧目标中心为中心,裁出255×255的搜索区域;对该区域分别乘以三个尺度因子做缩放;把三张缩放后的图分别送进搜索分支,得到三张17×17的响应图;找出全局响应最大的尺度,其最大峰值位置就是目标的新位置。
这里有一个很工程化的细节:响应图本来对应的是缩放后的图像坐标系,要得到原始图像坐标,需要把响应峰值位置乘以尺度因子做换算。也就是先放缩,再乘以对应尺度的步长,才能映射回原始帧坐标。很多人第一次写代码会漏掉这个“乘回去”的步骤,导致跟踪框抖动甚至直接跳飞。
4.3 余弦窗惩罚:抑制边界跳变
只靠多尺度搜索还不够。搜索区域是以上一帧位置为中心裁的,但目标不可能永远保持同样的运动速度。如果目标快速移动,它可能出现在搜索区域边缘;这时响应图边缘如果有一个偏高的噪声峰,跟踪器就会误判。
SiamFC的解决办法是给响应图乘一个余弦窗。余弦窗的中心值最大,向四周递减到接近0,相当于在位置先验上做一个软约束:假定目标大概率出现在搜索区域中心附近,越靠边越不可信。所有尺度算出来的响应图都要乘上这个余弦窗,然后再做峰值搜索。实测下来,这个处理能有效减少边界跳变问题,尤其是处理快速运动的视频时,稳定性提升非常明显。
4.4 尺度惩罚与目标尺寸平滑
多尺度搜索还会带来另一个问题:目标尺度估计容易抖。相邻两帧目标尺寸本来应该接近,但三档尺度的响应峰值可能有跳变,比如上一帧选中了0.964倍尺度,这一帧突然选中1.037倍尺度,视觉上跟踪框的边长会闪跳。
SiamFC在论文和实现里采用了一个尺度惩罚因子:对响应图的峰值乘上一个与尺度变化幅度相关的惩罚,变化越大惩罚越重。同时最终的跟踪框尺寸会做一个时间上的平滑,不会直接采用单帧的极端估计。这两个设计叠加之后,跟踪框的尺度变化会比较缓和,更接近真实目标的连续变化规律。
这种“响应图找位置、多尺度找大小、平滑稳输出”的三段式流程,是SiamFC推理阶段的全貌。它没有什么在线学习成分,全部计算就是几次前向卷积,所以在当时CPU上都能跑到几十FPS,GPU上更是可以实时运行。
5. 复现SiamFC最容易翻车的5个细节
这一节写的是我实际动手复现、以及帮同学调代码时遇到的高频问题。论文里不会明说,但任何一个都会让效果忽好忽坏。
5.1 padding对全卷积映射关系的破坏
很多人都知道SiamFC的卷积层不带padding,但实现时还是会手滑加上。问题在于如果conv1带了padding,输入255×255和127×127的特征图尺寸就不再是整齐的22×22和6×6,互相关输出的尺寸也对应改变,更麻烦的是响应图中心与搜索区域中心的对齐关系会被破坏。
复现时建议在模型定义里显式传入padding=0,并且写一段单元测试:随机输入一个固定大小的张量,打印每个卷积层输出尺寸,和论文标称值逐层对照。这种尺寸自检能省下大量调试时间。
5.2 互相关到底是“相关”还是“卷积”
数学上,卷积核需要翻转180度,互相关不翻转。但在深度学习的实现框架里,卷积层的前向过程实际执行的就是互相关运算,因为框架并不对卷积核做翻转。所以在PyTorch中把模板特征图当作卷积核,用F.conv2d去卷积搜索特征图,得到的结果就是论文定义的互相关结果,不需要额外做翻转操作。
有一类常见错误是把模板特征图reshape成N个6×6×1的“小图”分别去做匹配,再累加,这样在纯Python循环下慢到没法看。正确做法是一次性把模板特征图作为卷积核参数传入,让底层cuDNN处理,又快又准。
5.3 模板帧和搜索帧的采样间隔
训练数据采样时,模板帧和搜索帧之间的间隔非常影响模型的“时间鲁棒性”。间隔太小,网络学到的是相邻帧的像素几乎不变,推理时稍微遇到一点运动模糊就懵;间隔太大,目标外观变化太大,正样本对匹配难度过高,训练loss下不去。
实操经验是:先在同一个视频上随机采样两帧,计算它们的帧号差值;如果差值落在5到20帧之间就保留,否则重采。这个策略能保证模型见过各种运动幅度,又不至于让训练数据过于极端。原始论文里没有给出特别严格的间隔分布,但绝大多数开源复现都采用了类似做法,效果稳定。
5.4 损失归一化:平均还是求和
前面提到SiamFC的损失是对响应图所有位置取平均。这个选择直接影响学习率设置的量级。如果实现时不小心改成求和,总损失会比平均版大几百倍,1e-2的学习率会直接发散。
一个稳妥的验证方法是:训练前挑一个小batch迭代几步,观察loss数量级是否在1附近并缓慢下降。如果一开始就是几千上万,多半是归一化出了问题。另外还要注意,响应图的每个位置损失权重是一致的,不要对不同空间位置加权,这一点和语义分割里的像素级损失权重设计不同。
5.5 推理阶段模板分支的BN统计量
SiamFC原始主干没有使用BatchNorm,但很多复现为了收敛更快会自行加上。加完之后会引入一个隐蔽问题:训练时BN的均值方差是随batch统计的,推理时如果模板分支也走BN层,它用的running stats是在训练数据分布上统计的。而模板分支每次只输入一张图,且这张图来自测试视频的第一帧,分布可能与训练集差异较大,此时BN层会把模板特征强行归一化到训练分布上,干扰特征表达。
一个通用的解决办法是:推理阶段固定BN参数,或者干脆在主干中不用BatchNorm,改用GroupNorm、InstanceNorm这类不依赖batch统计量的归一化方式。后者在跟踪这种“单样本前向”场景下明显更稳。
6. 从SiamFC看后继者:SiamRPN、DaSiamRPN与SiamRPN++
如果只把SiamFC当做一个孤立模型,它的价值就已经兑现了。但更有意思的是顺着它看后续演进,你会发现后续方法的每一个设计,都是在补SiamFC的短板。
6.1 区域提议分支:把“在哪”升级为“在哪且多大”
SiamFC的跟踪框尺度只来自三档离散缩放,而且纵横比固定,这就导致目标长宽比变化剧烈时,跟踪框贴合度很差。SiamRPN的贡献是在Siamese骨架后面加了一个区域提议网络Region Proposal Network,输出多个锚框的分类得分与回归偏移。这样一来,目标的位置和尺度可以连续预测,不需要再做粗糙的多尺度搜索。
这个设计本质上是把跟踪任务从“相似度匹配”升级为“匹配+回归”。回归分支给出的框坐标修正,解决的不只是尺度,还有纵横比和中心偏移。如果没有SiamFC提供的全卷积特征和互相关匹配框架,SiamRPN的RoW(Response of Region Proposal)结构也无从谈起。
6.2 DaSiamRPN:把训练数据范围扩开
SiamFC在ImageNet VID上训练,数据量天然受限,而且主要是单目标视频。DaSiamRPN主要改进之一是引入更丰富的训练数据组合,包括ImageNet Detection、COCO、YouTube-BB等,用跨数据集的目标检测框来补充正样本,同时构造难负样本参与训练。
这个思路的根基在于SiamFC奠定的离线训练框架——只要网络是离线可训练的,数据集的扩充就是水到渠成的事。如果采用在线微调策略,每次跟踪都重新调参,数据集再大也烧不起推理时的计算成本。
6.3 深网络的退化与SiamRPN++的解决思路
很多人在SiamFC之后尝试把主干换成ResNet-50,结果跟踪精度反而下降。SiamRPN++的作者发现,这是因为深层网络中padding导致的位置偏差破坏了严格平移等变性,而孪生网络的匹配任务对平移等变性要求极高。
SiamRPN++的解法包括:在互相关操作中加入多层特征融合,用深度可分离互相关减少参数;更关键的是在训练同类别的样本对时做了分层采样,打破深层网络对目标类别和位置的偏见。这个问题的源头依然可以追溯到SiamFC对“严格空间对应关系”的强调——没有SiamFC建立的这套“空间一致性”标准,后人也不会意识到深层网络在这里会翻车。
7. 写在复现完SiamFC之后
我个人的体会是,SiamFC这篇论文适合反复读,但读的时候一定要配合动手跑代码。只看论文你会觉得它简单到不行,但真正动手,数据采样、尺寸对齐、余弦窗参数、尺度惩罚系数,每一步都会教你做人。当年我用PyTorch从零实现,第一版跟踪同一个视频不到20帧就丢了目标,最后定位到问题是模板帧和搜索帧采样间隔太近,训练出来的模型只会做“像素级拷贝匹配”。
如果你现在准备复现,给你一条可行路线:先下载一份开源实现跑通流程,但不要只做“跑通”,把每个超参数在代码里找到对应位置,对照论文理解它为什么存在;然后改动一个你可以理解的变量,比如正样本半径R,观察跟踪框在遮挡和快速运动下的表现差异。这种实验做两三轮,SiamFC在你的知识体系里就不再是一篇引用文献,而是一个真正理解过的系统。
后续不管你是走SiamRPN++的离线路线,还是转向Transformer跟踪器,SiamFC提供的“离线训练、在线前向、互相关匹配”这三个核心范式都会反复出现。把这篇学透,等于给整个孪生跟踪家族打了一个扎实的地基。