FairMOT 这篇论文在目标跟踪圈子里火了好一阵了,但网上大多数解读都停在“它是 anchor-free 的”这个层面,然后就开始讲结构图。真正动手做过 MOT 的人都知道,FairMOT 的价值远不止换了个检测头这么简单,它其实是把“检测”和“重识别”这两个原本互相打架的任务,用一种相对干净的方式焊在了同一个网络里。本文我想从实际应用和复现的角度,把这篇论文真正讲透:它解决什么问题、各个模块为什么这么设计、训练和推理的完整链路长什么样、以及我在落地过程中踩过的那些坑。
1. 一个被“框”住的领域:Anchor-Based 方法在 MOT 上的尴尬
1.1 从“两段式”到“一段式”演进中的核心矛盾
多目标跟踪(MOT)的任务说起来很简单:给定一帧图像,你要把里面所有感兴趣的目标框出来,并且在连续的视频帧里保持同一个目标的 ID 不变。早年主流的 DeepSORT 路线是“先检测、后跟踪”,也就是检测器全速跑一遍,把框给到跟踪模块,再单独用一个 ReID 网络提取每个框的衣着特征,靠特征相似度做帧间匹配。
这种两段式方案很稳,但缺陷摆在那里:检测和 ReID 是两套网络,无法共享特征,速度上不去。于是业界开始尝试把检测和 ReID 塞进同一个网络,最具代表性的就是 JDE——它把 ReID 分支直接挂在检测器的特征图上。这个思路方向是对的,但 JDE 的效果一直不算理想,尤其是 ID Switch 这个指标很难看,原因就藏在 anchor-based 检测器的结构里。
1.2 Anchor 框带来的“特征错位”问题,到底错在哪
JDE 这类方法通常会沿用带 anchor 的检测器(比如 YOLO 变体)。检测器在训练时会把每个真实目标框和它匹配的 anchor 框绑定,然后对 anchor 框内的特征做分类和回归。问题来了:一个目标在图像里是离散的、有边界的一个像素区域,而 anchor 框是网格化生成的一堆预设矩形,两者并不是天然对齐的。
具体到特征层面,anchor 框内部的 RoI 特征或多或少会挤入背景像素,或者把目标的一部分边缘给切掉。对于分类任务来说,这事影响不大——类别判断本来就允许一定鲁棒性;但对于 ReID 任务来说,特征里混入背景、或者目标被裁掉一截,意味着这个目标的“身份指纹”被污染了。你用一个被污染的指纹去做帧间匹配,匹配结果当然时好时坏。
更麻烦的是,anchor 本身的超参数(长宽比、尺度、数量)需要针对具体场景反复调。而且 anchor 匹配规则是“和 ground truth 交并比大者得”,一个真实目标可能匹配到多个 anchor,或者一个 anchor 同时负责多个目标,这都把同一个目标的 ReID 特征给打散了。
FairMOT 的核心动机正是冲着这个问题去的:与其在 anchor 的不对齐框架里修修补补,不如直接从 anchor-free 的思路出发,让目标的位置和 ReID 特征严格落在同一个点上。
1.3 为什么 CenterNet 式的方法天然适合跟踪场景
CenterNet 是典型的 anchor-free 检测器,它不预测框和 anchor 的偏移,而是直接预测目标中心点的 heatmap,再回归出中心点到四条边的距离。FairMOT 直接继承了这套思路:你在 heatmap 上找到一个显著的峰值点,这个点就是目标的中心。目标中心点天然表达了“单个目标自身”的位置,不需要绑任何先验框。在这个点上提取的特征,几乎就是目标正中心的特征,干净、紧凑、背景污染极小。
这时候 ReID 分支也是基于同一个中心点去提取特征,检测框和身份特征就构成了严格的锚定关系。FairMOT 的名字里“Fair”这个单词,最重要的含义就在这里:检测任务和 ReID 任务从起点开始就被公平地放置在了同一个特征位置上,谁也不会为谁做妥协。
2. FairMOT 整体设计拆解:两条分支如何在一个网络里共生
2.1 总体架构:一个骨干网络、两个头部
FairMOT 的总体结构其实很简洁:输入一帧图,经过一个改良过的 ResNet-34 骨干网络,接一个 DLA 特征融合结构,输出一个 1/4 分辨率的特征图。在这个特征图上,分出三个头:heatmap 头、box 回归头、ReID 头。
三个头各司其职:
- heatmap 头负责找到目标中心点的位置,输出分辨率是 H/4 x W/4,每个位置预测一个分数,表示这里是目标中心的置信度;
- box 头对每个中心点输出目标框的宽高和中心点的偏移量(因为下采样之后中心点坐标会有小数误差);
- ReID 头输出每个中心点的身份嵌入向量,默认维度是 128 维。
这个设计和 FPN 式的多尺度检测思路不同:FairMOT 自始至终只用一张 1/4 分辨率的融合特征图,没有多尺度分支。这一选择也是刻意为之——多尺度特征在 anchor-based 检测里很有用,但多尺度会意味着同一个目标在不同尺度特征图上被重复预测,对于跟踪来说,你还得头疼这些重复预测之间怎么合并,反而破坏了“一个目标一个特征”的干净性。
2.2 Backbone 选型:为什么是 ResNet-34 而不是 ResNet-50
很多人在复现的时候会不假思索地换成 ResNet-50 或者更深的主干,认为模型越大效果越好。但 FairMOT 用 ResNet-34 是有明确理由的。
MOT 任务中目标往往很小(比如行人远距离时甚至只有几十个像素),你需要的不是非常“深”的语义特征,而是足够“细”的空间分辨率。ResNet-34 在维持不错语义信息的同时,计算开销比 ResNet-50 小得多,能够让你在第 4 层(也就是 1/4 分辨率)的特征图上继续保持细节。用更深的网络,理论上语义更强,但下采样层数多、有效步幅大,小目标的中心点往往糊在 heatmap 里找不到,这就是典型的“检测精度被网络深度反噬”。
另一个容易被忽视的点:MOT 数据本身的规模很小。MOT17 训练集里只有几千张标注帧,你要是拿个大 backbone 去 train from scratch,几乎必然过拟合。FairMOT 采用 ResNet-34 配合在 COCO 上的预训练权重,再通过 DLA 结构进行跨层融合,算是在“数据有限”和“精度需求”之间拿捏了一个很务实的平衡点。
2.3 DLA 融合结构到底在融合什么
DLA 全称 Deep Layer Aggregation,简单说就是把不同层级的特征进行迭代式的融合,让高层语义信息和低层细节信息互相渗透。FairMOT 用的是 DLA 的一个变体,具体结构是在 ResNet-34 的基础上,把第 2、3、4、5 层的特征做类似 FPN 的自上而下融合,但融合方式比 FPN 多加了几层树状聚合,融合之后每一个位置的特征同时包含丰富的语义和空间细节。
说直白一点:单靠最后一层特征图做检测和 ReID,小目标的细节早就丢光了;单靠浅层特征,又缺少全局语义,行人抠出来都不知道是人还是柱子。DLA 的迭代融合在不大幅增加计算量的前提下,把这两部分信息拧在了一起。实际消融也证明,去掉 DLA 融合之后 MOTA 和 IDF1 双双下跌,说明在 anchor-free 结构里,特征融合的质量比网络深度更值钱。
2.4 “Fair”的第三重含义:任务之间也要公平
FairMOT 对“公平”还有一个维度的理解:两个任务在训练时的地位应该是平等的,你不能让检测任务把 ReID 任务压得没有梯度空间,也不能反过来。这个细节主要体现在两个地方。
一是 ReID 分支从特征图上的每个像素位置都提取特征,而不是只从 GT 中心点提取。训练时你会看见它在全图上对所有位置产生梯度,这就保证了 ReID 分支不仅仅是“中心点那几个像素”的学习,而是整个语义特征图都有身份判别能力。二是损失函数里,ReID 分支使用了难样本三元组损失和 ID 分类损失的加权组合,并且在全图上随机采样一部分位置参与 ReID 训练。这样 ReID 头不会因为训练对某个目标过拟合,而是学会了一个通用的“身份判别表征”。
3. 损失函数与训练策略:三分支损失如何协同
3.1 Heatmap 损失:一个中心点由谁说了算
FairMOT 的 heatmap 分支输出每个位置是目标中心的置信度,训练时用的损失函数是 Focal Loss 的一个变体,这个变体在 CenterNet 里就已经验证过:对于正样本点,鼓励它输出接近 1;对于中心点周围的高斯半径内的点,不简单当成负样本硬压,而是按照与中心点的距离衰减惩罚力度;远离中心的点则正常按负样本处理。
为什么不能用简单的二分类交叉熵?因为目标中心点下采样到 1/4 分辨率之后,标注的整数坐标可能偏向某一个像素,旁边那一个像素同样是有效的目标中心。如果我们硬把所有非中心点的像素都当成负样本,网络会在这些邻近像素上产生大量冲突信号,训练很不稳定。高斯掩码本质上是在告诉网络:“你这几个像素都可以响应这个目标,但越靠近真实中心,响应应该越强。”这保证了预测时 heatmap 上能形成尖锐的、可区分的峰,而不是一片糊。
训练中一个值得注意的细节是,分辨率造成的中心点偏移还会通过 box 头单独回归一个 offset,类别是中心点附近的连续偏移量。这个小回归头看似不起眼,但对于后续的中心点追踪是必须的——你不可能靠四分之一分辨率的粗略坐标去跟踪一个移动的小目标。
3.2 Box 损失和 ReID 损失的平衡艺术
Box 回归头和 CenterNet 一样,直接回归中心点到上、下、左、右四个边界的距离,使用 L1 loss。这部分损失权重比较小,通常在实验中占 0.1 左右。
ReID 损失是 FairMOT 比较讲究的地方。它对每个标注目标提取对应的 128 维特征,然后同时使用三元组损失和 ID 分类损失。ID 分类损失就是把每个训练目标当作一个类别去分类,简单粗暴地逼迫网络按 ID 区分目标;三元组损失则是把同一个 ID 的目标特征拉近、不同 ID 的目标特征推开。两个损失加在一起,让特征空间既具备类别可分性,又具备度量空间里的判别性。
消融实验里,去掉三元组损失后 IDF1 明显下滑,因为单独的 ID 分类损失往往让网络学到的是“粗分类”能力,而不是细粒度的“距离远近”。真正到了推理阶段,我们对比的是特征向量的余弦距离,这个距离在训练时必须有显式约束——这正是三元组损失不可替代的原因。
3.3 总损失权重为什么要设为 1:0.1:1
FairMOT 的总损失基本是:heatmap loss + 0.1 x box loss + 1 x ReID loss。很多人复现时会觉得 box loss 这么小是不是不够,上来就调成 0.5,结果把框训得稳稳的,但 ReID 特征反而不好了。这里背后有个很实际的逻辑:
Box 回归结果是给跟踪提供空间位置的,但它不是身份判别的唯一依据。框架里后续匹配时,主要靠空间距离和 ReID 特征同时作用。让 box 损失占大头,会迫使网络把大部分容量用去精修边框,而这些精修对跟踪的 ID 保持几乎没有帮助。反过来,ReID 损失占大头,能确保同一目标的特征在不同姿态、不同遮挡下依然非常接近,这才是 MOT 里最珍惜的能力。
我见过不少团队自己调参,把 box 权重提到 0.5,发现框确实更紧致,但 ID Switch 暴涨。原因就是特征空间失衡了。所以 FairMOT 给出的这组权重,不是一个随手填的参数,而是经过反复实验得到的平衡状态。
3.4 训练细节:EMA 和数据增强怎么配合
FairMOT 训练时的数据增强组合是:随机水平翻转、缩放、裁剪、颜色抖动。这里面比较关键的是颜色抖动。MOT 视频序列里,同一个目标在相邻帧之间光照变化通常不大,但跨相机、跨时段之后颜色差异会非常大。你如果不在训练时把颜色抖动做强,模型很容易依赖“颜色一致性”来做匹配,一碰到光照突变,ReID 特征就崩了。
EMA(指数移动平均)在 FairMOT 的训练里也很重要。作者在推理时使用的不是训练末尾那一版参数,而是所有历史参数的指数滑动平均,这个技巧老实说对 BN 层比较敏感的任务帮助很大,因为它能平滑掉训练后期的震荡,让模型输出的特征更稳定。我实测下来,打开 EMA 和不开 EMA,在 IDF1 上能差 1 到 2 个点,这个收益在 MOT 这种对稳定性要求极高的任务上非常可观。
4. 推理流程与数据关联:在线 MOT 的匹配策略
4.1 从 heatmap 峰值到目标框:一整套后处理流程
推理阶段,把一张图喂给网络,得到三个头的输出。第一步不是直接用阈值过滤,而是在 heatmap 上做 3x3 的最大值池化,然后把那些同时是 3x3 邻域最大值的点挑出来。这个“局部峰值抑制”是一个关键细节,目的是保证同一个目标只对应一个中心点。
拿到候选中心点之后,先把置信度低于某个阈值的点丢掉。然后根据 box 头输出的宽高和偏移量,还原出每个中心点对应的完整目标框。这一步完成之后,你可以得到这一帧所有的目标框和每个框对应的 128 维 ReID 向量。
4.2 轨迹状态机:从检测框到连续 ID 的关联
跟踪器内部维护着一个轨迹集合,每条轨迹包含:历史 ReID 特征(通常保留最近若干帧的均值)、轨迹中心点位置、连续丢失帧数、轨迹状态(暂定、确认、结束)。算法流程大概是:
每一帧检测完,先把新检测框和所有存活轨迹做匹配。匹配的依据是一个综合距离,包括检测框中心点和轨迹预测中心点的欧氏距离,以及检测 ReID 特征和历史轨迹 ReID 特征的余弦距离。两个距离按一定权重相加,得到一个代价矩阵,再用匈牙利算法做全局最优匹配。
匹配时要设两个阈值,一个是空间距离阈值,一个是 ReID 距离阈值。超过阈值的配对直接不允许。为什么要两个阈值同时起作用?空间距离阈值保证了匹配不会乱来——就算特征很像,也不可能瞬间从画面左边跳到右边;ReID 阈值则保证了即使目标短暂消失后重新出现,只要特征还够近,依然能接得上。这套组合比 DeepSORT 那种纯 IoU + 级联匹配要灵活很多,尤其是在遮挡后重现的场景下,FairMOT 的优势非常明显。
4.3 轨迹的出生、确认与消亡机制
FairMOT 的轨迹管理借鉴了经典跟踪器的设计:新检测框如果暂时没有和任何轨迹匹配上,不会立刻生成一个“正式轨迹”,而是先作为“暂定轨迹”存在。只有当这个暂定轨迹在连续几帧内都稳定地匹配上了新的检测框,才被提升为确认轨迹并分配 ID。
反过来,一条确认轨迹如果连续丢失超过阈值(比如 30 帧),就直接销毁;如果只是偶尔丢一帧,还可以保留,等待下一帧的特征匹配。这套机制就是为了挡掉误检带来的噪声轨迹——毕竟 anchor-free 检测器偶尔还是会输出一些置信度不低的背景误检,如果一出现误检马上就给 ID,你的整个跟踪结果里会到处是闪烁的“幽灵目标”。
5. 实验结果与消融分析:每个模块到底贡献了什么
5.1 公开数据集上的硬指标变化
在 2D MOT 2016 / 2017 的公开测试集上,FairMOT 相比当时主流的单阶段方法,MOTA 和 IDF1 都有明显提升,尤其是 IDF1 和 ID Switch 提升幅度非常可观。这说明 FairMOT 的真正优势并不在于“把框得更准”这件事,而在于“把 ID 保持得更久”。MOTA 这个指标衡量的是检测精度和跟踪一致性的结合,IDF1 则更看重 ID 分配的准确性,后者提升,说明 ReID 特征在匹配中的价值被真正发挥出来了。
论文里还特意和 JDE 做了对比,重点强调的一个结论是:JDE 之类方法里 ReID 特征其实没有充分发挥作用,因为 anchor-based 结构中的特征错位问题,让 ReID 分支学到的东西本身就有缺陷。FairMOT 在差不多的计算量下,IDF1 比 JDE 高出十几个百分点。
5.2 消融实验里最值得记住的几个数字
论文的消融实验有几个结论很典型:
- 把 anchor-free 换成 anchor-based,MOTA 和 IDF1 同时显著下跌,说明 anchor-free 结构是公平训练的基石;
- 不做特征对齐(即不在中心点直接提取 ReID),IDF1 掉得特别明显,说明中心点锚定是 ReID 发挥作用的必要条件;
- 去掉 DLA 特征融合,IDF1 也会下滑,说明跨层特征融合对中小目标的特征表达至关重要;
- ReID 特征维度从 128 降到 64,IDF1 微降,但速度小幅提升,这是一个可以按算力取舍的选项。
这些消融实验用一句话概括就是:FairMOT 的三个关键设计——anchor-free 检测、中心点特征对齐、DLA 融合——每一层都在为“ID 保持”服务,缺一个都会导致跟踪质量的明显退化。
5.3 速度与精度的实际权衡
FairMOT 在单卡上大概能跑到 25 FPS 左右(以 DLA-34 配置为准),配合轻量后端在嵌入式设备上也能跑出现实可用的帧率。这个速度在当年已经是非常领先的单阶段 MOT 方案了,它证明了“检测 + ReID 单网络”这条路是行得通的。那种认为“MOT 必须上两段式,否则 ReID 质量一定拉胯”的旧观念,基本上被这组实验结果给否掉了。
不过也要说句公道话:FairMOT 的高效建立在“目标尺度变化不大”的前提上,它默认同一类目标的中心点尺度分布相对集中。一旦遇到大尺度和极小尺度目标混在一起,单尺度的 heatmap 表达就会吃力,这点在 MOT20 这种密集场景里体现得比较明显。
6. 复现与落地:我在实践中踩过的坑
6.1 预训练权重与骨干网络细节
复现 FairMOT 时最容易翻车的地方是预训练权重。论文里的 baseline 用的是 DLA-34 结构,而这个结构在常见框架里没有现成的 ImageNet 预训练权重,很多复现者会直接把 backbone 换成一个现成的 ResNet-34 权重,但这其实牺牲了 DLA 结构本身带来的融合优势。
我自己的做法是:参照论文给的 DLA-34 定义,把层次聚合结构搭出来之后,用 ImageNet 预训练的 ResNet-34 权重来初始化 backbone 部分,其中 DLA 新增的聚合层用随机初始化。实践下来,模型需要先跑几个 epoch 让聚合层适应,然后 loss 才能稳定下降,这个过程看起来会让人误以为代码写错了,其实是正常的。如果你直接用 COCO 预训练的 CenterNet 权重来初始化,效果会好很多,但注意其输出通道数和 FairMOT 的三个头并不一致,加载时要做好裁剪。
6.2 ReID 特征到底要不要做归一化
FairMOT 的 ReID 头输出的 128 维特征在推理时要和轨迹的历史特征做余弦距离。余弦距离本身对向量长度不敏感,但训练时如果网络输出的特征向量长度差异过大,会导致梯度更新不稳定。
实践中我发现一个细节:如果在 ReID 头后面加一个 L2 归一化层,训练时三元组损失里距离度量用归一化后的特征,效果往往比不归一化或者只在推理时归一化要好。因为归一化让网络在训练时就知道“特征最终会被压到单位超球面上”,它会主动调整特征分布的紧凑性。FairMOT 原始论文里对这个问题提得不多,但很多第三方实现都会在推理阶段做一次 L2 归一化,这是经过实操验证的有效做法。
6.3 阈值调参:IDF1 和 MOTA 之间的隐形成本
FairMOT 推理阶段有一个很关键的阈值参数:检测置信度阈值。阈值调得低,召回率上去了,但误检增多,最终表现为 MOTA 微升、IDF1 大跌——因为误检目标会随机匹配到轨迹,把整个 ID 分配搅乱。阈值调得高,ID Switch 确实减少,但漏检目标变多,轨迹断裂频繁,IDF1 又往下掉。
我在实际调参中建议不要只看 MOTA,而是一起看 IDF1 和 ID Switch。这三个指标本质上在互相对抗。更合理的做法是:根据你的业务场景先定一个可接受的误检率,再反推阈值。比如做商场客流统计,误检一个不存在的行人比漏掉一个行人更致命,那阈值就应该往高调;做安防巡逻,漏报可能更严重,那就适当降低阈值、靠后续的轨迹管理机制来过滤噪声。
6.4 距离权重的影响比你想的大
FairMOT 匹配时综合距离 = 空间距离 + 权重 x ReID 余弦距离。这个权重的取值对跟踪效果影响巨大,但它恰恰是最容易被复现者忽视的参数。权重太高,模型过度信任 ReID 特征,在遮挡之后可能把 A 的身份误配给 B;权重太低,模型又几乎退化成“纯位置跟踪”,一旦目标快速移动或短暂丢帧,就会断链。
我的调参经验是:先固定检测置信度阈值,把权重按数量级去试(0.1、0.5、1、2),每改一次都统计 IDF1 和 ID Switch。多数场景下 0.5 到 1 这个区间是比较稳的,但如果你的相机有抖动、目标运动速度快,权重应该往 0.5 以下调,多信任空间信息;如果你的场景里目标外观差异明显但空间上容易混淆,就往 1 以上调,多信任 ReID。
6.5 训练轮数和 BN 层统计量的问题
FairMOT 论文里训练用的是 30 个 epoch 左右。我复现的时候最开始只跑了 15 个 epoch,ReID 分支明显没有收敛,IDF1 差了一大截,你甚至能观察到特征向量之间的余弦距离完全没有区分度。后来我把训练拉长到和论文近似,并且特别留意 BN 层的 running mean 和 running variance 是否更新稳定——这个问题在小 batch 训练时特别明显。
如果你用的是比较小的 batch size(比如 4 或 6),BN 的统计量噪声会很大,模型输出的特征在推理时会出现“训练和推理分布不一致”的典型症状。两个可落地的解法:一是增大 batch size(一般建议 12 以上);二是如果显存不够,可以在训练到一半时冻结 BN 的统计量更新,只把 BN 当成一个固定的归一化层来用,这样推理时会稳定不少。这个技巧在 FairMOT 这种特征敏感型模型上尤其有用。
6.6 密集场景和遮挡场景的落地建议
FairMOT 在密集场景下的表现比大家预想的要弱一些,因为相邻目标的中心点太近,heatmap 峰值很容易互相压制。如果你想把它用到商场、车站这类人流密集的场景,我建议做两件事:一是在训练时增加一个针对“中心点冲突”的数据增强,比如随机将一个目标的中心点附近像素扣掉,逼迫模型学会在遮挡情况下依然维持正确的中心点响应;二是在推理时把局部峰值抑制的邻域从 3x3 扩大到 5x5,代价是会丢掉一些相邻的密集目标,但换来的是更稳定的 ID 分配。
如果你面对的是相机运动较强的场景,FairMOT 原生的中心点方案会有点吃力,因为背景整体移位之后,空间距离约束容易被破坏。一个有效缓解手段是引入轻量的背景运动估计,在送进跟踪器之前先做一次全局对齐。这个做法不会影响 ReID 特征本身的提取,但对于空间距离匹配的帮助非常明显,能在不重训模型的前提下挽回几个点的 IDF1。
写在最后
FairMOT 给我最大的启发不是它用了 anchor-free,而是在这个框架里,“检测框”和“身份特征”被真正拧成了同一条绳。这种设计上的干净,让工程上很多脏活累活都变得可控了。如果你正准备在业务里落地一个实时 MOT 系统,或者只是在学习阶段想把检测和跟踪的边界摸清楚,从 FairMOT 的结构和训练细节入手,会是一个非常好的起点。实话说,它并不是论文里那种“只能看不能跑”的架构——只要你把预训练权重、EMA、阈值平衡这几关过了,它给你的回报会相当可观。