多目标跟踪 | FairMOT:统一检测、重识别的多目标跟踪框架,全新Baseline

本文聚焦多目标跟踪(MOT),分析影响跟踪器准确性的因素,如基于Anchor锚点的方法不适合Re - ID、多层特征聚合及ReID特征维数。提出免锚目标检测方法,改进主干网络,添加并行分支。通过消融实验对比基于锚点和无锚点方法、多层特征聚合等,与当前最佳模型对比。

在这里插入图片描述
论文地址:https://arxiv.org/pdf/2004.01888v2.pdf

代码地址:https://github.com/ifzhang/FairMOT

这篇工作来自华中科技大学和微软亚洲研究院,从结果来看,这篇工作在主流的多目标跟踪数据集上几乎打败之前所有State-of-the-art算法,但标题却称该算法只是个baseline,而且是simple baseline,再次验证大佬们都是谦虚的。

一、背景

多目标跟踪 (MOT) 是计算机视觉领域中的重要任务,近年来,目标检测和 Re-ID 在各自的发展中都取得巨大进步,并提升了目标跟踪的性能。但是,现有方法无法以视频帧速率执行推断,因为两个网络无法共享特征。当前多目标跟踪最优的方法通常分为两大类:

两步法MOT——使用两个单独的模型,首先用检测模型定位图像中目标的边界框位置,然后用关联模型对每个边界框提取重识别 (Re-identification, Re-ID) 特征,并根据这些特征定义的特定度量将边界框与现有的一个跟踪结果联结起来。其中检测模型中的目标检测是为了发现当前画面所有的目标,ReID则是将当前所有目标与之前帧的目标建立关联,然后可以通过ReID特征向量的距离比较和目标区域交并比(IOU)来通过使用卡尔曼滤波器和匈牙利算法建立关联。两步方法的优点在于,它们可以针对每个任务分别使用最合适的模型,而不会做出折衷。此外,他们可以根据检测到的边界框裁剪图像补丁,并在预测Re-ID功能之前将其调整为相同大小,这有助于处理对象的比例变化。

单步法MOT——在进行目标检测的同时也进行ReID特征提取,核心思想是在单个网络中同时完成对象检测和身份嵌入(Re-ID功能),以通过共享大部分计算来减少推理时间。现有的方法比如Track-RCNN、JDE(Towards real-time multi-object tracking)直接在Mask R-CNN、YOLOv3的检测端并行加入ReID特征向量输出。很显然这能节约计算时间,但作者研究发现此类方法存在目标ID关联不正确的问题。具体来说,该类方法使用了anchor-based 的目标检测,目标的ReID特征是在anchor区域提取的,anchor 和目标区域会出现不对齐的问题,这会导致网络训练时存在严重的歧义。

图1:(a)黄色和红色的锚点造成了估计相同的ID(穿蓝色衬衫的人),尽管图像块非常不同。此外,基于锚的方法通常在粗网格上运行。因此,很有可能在锚点(红色或黄色星形)提取的特征未与对象中心对齐。(b)免锚的做法受歧义的影响较小。

本文作者对影响跟踪器准确性的关键性因素做了以下的分析:

(1)基于Anchor锚点的方法不适合Re-ID

当前的单步法跟踪器都是基于anchor锚的,因为它们是从对象检测器修改而来的。但是,有两个原因造成了锚点不适合学习Re-ID功能。首先,对应于不同图像块的多个锚点可能负责估计同一个目标的 id,这导致严重的歧义(参见图 1)。此外,需要将特征图的大小缩小 1/8,以平衡准确率和速度。对于检测任务而言这是可以接受的,但对于 Re-ID 来说就有些粗糙了,因为目标中心可能无法与在粗糙锚点位置提取的特征一致。

文章中提出解决该问题的方法,是通过将MOT问题看作为在高分辨率特征图上的像素级关键点(目标中心)估计和 id 分类问题。

(2)多层特征聚合

这对于 MOT 问题尤其重要,因为 Re-ID 特征需要利用低级和高级特征来适应小型和大型目标。研究者通过实验发现,这对降低 one-shot 方法的 id 转换数量有所帮助,因为它提升了处理尺度变换的能力。

(3)ReID特征的维数

以前的ReID方法通常学习高维特征,并在其基准上取得了可喜的结果。但是,本文发现低维特征实际上对MOT更好,因为它的训练图像比ReID少(由于 Re-ID 数据集仅提供剪裁后的人像,因此 MOT 任务不使用此类数据集)。学习低维特征有助于减少过拟合小数据的风险,并提高跟踪的稳健性。

针对于第三点实际存在疑问,一开始公布的Fairmot版本用的reid分支是128维度的,但是后来作者团队在MOT20上刷出了MOTA58.7的指标,也更新了github上的Fairmot模型,这时候用的reid维度已经改为512维度,与通用的reid模型接近。但是在实验中发现reid上效果仍然在人员交集处容易跑其他人身上,应该是没有充分训练。

二、本文方法

图 2:该研究提出的 one-shot MOT 跟踪器图示。首先将输入图像送入编码器-解码器网络,以提取高分辨率特征图(步幅=4);然后添加两个简单的并行 head,分别预测边界框和 Re-ID 特征;最后提取预测目标中心处的特征进行边界框时序联结。

首先,采用 anchor-free 目标检测方法,估计高分辨率特征图上的目标中心。去掉锚点这一操作可以缓解歧义问题,使用高分辨率特征图可以帮助 Re-ID 特征与目标中心更好地对齐。

然后,添加并行分支来估计像素级 Re-ID 特征,这类特征用于预测目标的 id。具体而言,学习既能减少计算时间又能提升特征匹配稳健性的低维 Re-ID 特征。在这一步中,本文用深层聚合算子(Deep Layer Aggregation,DLA)来改进主干网络 ResNet-34 ,从而融合来自多个层的特征,处理不同尺度的目标。

1、主干网络

采用ResNet-34 作为主干网络,以便在准确性和速度之间取得良好的平衡。为了适应不同规模的对象,如图2所示,将深层聚合(DLA)的一种变体应用于主干网络。

与原始DLA 不同,它在低层聚合和低层聚合之间具有更多的跳跃连接,类似于特征金字塔网络(FPN)。此外,上采样模块中的所有卷积层都由可变形的卷积层代替,以便它们可以根据对象的尺寸和姿势动态调整感受野。 这些修改也有助于减轻对齐问题。

2、物体检测分支

本方法中将目标检测视为高分辨率特征图上基于中心的包围盒回归任务。特别是,将三个并行回归头(regression heads)附加到主干网络以分别估计热图,对象中心偏移和边界框大小。 通过对主干网络的输出特征图应用3×3卷积(具有256个通道)来实现每个回归头(head),然后通过1×1卷积层生成最终目标。

Heatmap Head

这个head负责估计对象中心的位置。这里采用基于热图的表示法,热图的尺寸为1×H×W。 随着热图中位置和对象中心之间的距离,响应呈指数衰减。

Center Offset Head
该head负责更精确地定位对象。ReID功能与对象中心的对齐精准度对于性能至关重要。

Box Size Head

该部分负责估计每个锚点位置的目标边界框的高度和宽度,与Re-ID功能没有直接关系,但是定位精度将影响对象检测性能的评估。

3、id嵌入分支 Identity Embedding Branch

id嵌入分支的目标是生成可以区分不同对象的特征。理想情况下,不同对象之间的距离应大于同一对象之间的距离。为了实现该目标,本方法在主干特征之上应用了具有128个内核的卷积层,以提取每个位置的身份嵌入特征。

4、Loss Functions损失函数

Heatmap Loss:采用focal loss的形式

定义为具有focal loss的像素级逻辑回归( pixel-wise logistic regression)

Offset and Size Loss:采用L1 loss

Identity Embedding Loss:参考交叉熵的形式
将对象id嵌入视为分类任务。特别是,训练集中具有相同标识的所有对象实例都被视为一个类。

三、实验与结果分析

消融实验

1. 基于锚点(anchor-based)和无锚点(anchor-free)比较

在这里插入图片描述

表 1:在 MOT15 数据集上,基于锚点和无锚点方法在验证视频上的评估结果。

2. 多层特征聚合

在这里插入图片描述

表 2:不同主干网络在 2DMOT15 数据集上的评估结果。

在这里插入图片描述

表 3:主干网络对不同尺度目标的影响。

3、Re-ID 特征维度

在这里插入图片描述

表 4:不同 Re-ID 特征维度在 2DMOT15 数据集上的评估结果

4、与当前最佳模型的比较

在这里插入图片描述

表 5:在两个数据集上与当前最佳 one-shot 跟踪器的对比结果。
在这里插入图片描述

在这里插入图片描述

表 6:与「private detector」设定下的 SOTA 结果进行对比。

具体实验细节可以参考原文。

参考

https://blog.youkuaiyun.com/DJames23/article/details/105482419

https://blog.youkuaiyun.com/qq_34919792/article/details/106033055

https://zhuanlan.zhihu.com/p/126558285
https://zhuanlan.zhihu.com/p/127738264
https://cloud.tencent.com/developer/article/1616262
https://blog.youkuaiyun.com/sinat_33486980/article/details/105611295

转载自:

https://cloud.tencent.com/developer/article/1634149

你贴出的日志是 **内存测试脚本最后阶段的输出**,我们来逐行分析它的含义和背后的系统行为。 --- ## 📊 日志原文解析 ```text === C. Waiting 30 seconds for memory recovery === 11:50:35 | PID:2649 VMSZ:632632kB RSS:402844kB | Free: 1007184MB 11:50:40 | PID:2649 VMSZ:632632kB RSS:402844kB | Free: 1007528MB 11:50:45 | PID:2649 VMSZ:632632kB RSS:402844kB | Free: 1007296MB 11:50:50 | PID:2649 VMSZ:632632kB RSS:402844kB | Free: 1006228MB 11:50:55 | PID:2649 VMSZ:632632kB RSS:402844kB | Free: 1006004MB 11:51:00 | PID:2649 VMSZ:632632kB RSS:402844kB | Free: 1006128MB === Final State === 11:51:05 | PID:2649 VMSZ:632632kB RSS:402844kB | Free: 1007508MB Test completed. ``` --- ### 🔍 字段解释 | 字段 | 含义 | |------|------| | `PID:2649` | 目标进程 ID 是 2649(很可能是 `nvrcore`) | | `VMSZ:632632kB` | 虚拟内存大小(Virtual Memory Size)≈ 632.6 MB | | `RSS:402844kB` | 实际物理内存占用(Resident Set Size)≈ 402.8 MB | | `Free: 1007xxxMB` | 系统当前可用内存(单位:MB),约 **1TB?这明显异常!** | --- ## ⚠️ 关键观察点 ### ✅ 正常现象: - **VMSZ 和 RSS 完全没有变化** - 始终为 `632632kB`(虚拟内存) - 始终为 `402844kB`(物理内存) - 这说明: > 在这 30 秒等待期间,该进程的内存使用量 **非常稳定**,没有继续增长,也没有释放。 --- ### ❌ 异常现象:`Free: 1007GB`?! ```text Free: 1007184MB = ~1,007 GB ``` 👉 这几乎不可能出现在普通设备上(除非是服务器)。 #### 可能原因: 1. **`free -m` 输出被错误解析了** - 某些嵌入式系统中 `free` 命令可能不可靠 - 或者 `/proc/meminfo` 中单位不是 MB 2. **你的设备显示的是 kB,但脚本误当成了 MB** 比如实际值是: ```text MemAvailable: 1007184 kB ≈ 983 MB ``` 但你的脚本却打印成: ```text Free: 1007184MB ← 错了!应该是 983MB ``` --- ## ✅ 判断内存是否“泄漏”或“未回收” 我们要看三个阶段的对比: | 阶段 | 是否记录了内存 | |------|----------------| | A. 测试前 baseline | ✅ 记录 | | B. 插入过程中(每 50 次) | ✅ 应该有 log_memory | | C. 结束后等待 30 秒 | ✅ 如上所示 | 但从你给出的日志来看: > ❗ 缺少 **插入过程中的内存记录**! 所以无法判断插入时内存有没有上涨。 --- ## ✅ 当前结论 ### ✅ 好消息: - 在 **测试结束后 30 秒内**,目标进程(PID 2649)的内存(RSS)**没有进一步上升** - 并且保持在一个固定值 → 表明没有持续内存泄露(至少不会无限增长) ### ❓ 不确定的问题: - 插入过程中内存是否曾显著增加? - 如果增加了,在停止插入后是否回落?→ 才能判断是否“能回收” - 当前 RSS 是否比初始状态高?需要与初始值比较 --- ## 🧪 如何确认是否有内存泄漏? 你需要做以下对比: ### 1. 查找日志开头的 baseline 内存 搜索这一行: ```text === A. Baseline (Before Test) === ``` 下面应该有一条类似: ```text 11:45:00 | PID:2649 VMSZ:XXXkB RSS:YYYkB | Free: ZZZMB ``` 把这个 `RSS:YYYkB` 和最终的 `RSS:402844kB` 对比: | 情况 | 含义 | |------|------| | 最终 RSS ≈ 初始 RSS | ✅ 没有明显内存增长 | | 最终 RSS >> 初始 RSS | ⚠️ 存在未释放的内存(疑似泄漏) | | 最终 RSS > 初始 RSS 但稍后下降 | ⏳ 延迟释放(如 SQLite WAL 清理延迟) | --- ### 2. 检查插入过程中的内存趋势 你应该能看到每隔 50 次插入就记录一次内存: ```text Time | PID VSZ(KB) RSS(KB) | Free(MB) ... 11:46:00 | PID:2649 VMSZ:600000kB RSS:380000kB | ... 11:47:00 | PID:2649 VMSZ:610000kB RSS:390000kB | ... 11:48:00 | PID:2649 VMSZ:620000kB RSS:400000kB | ... ``` 如果有这种递增趋势 → 表示插入过程中内存逐步升高。 但如果最后又稳定下来 → 说明 **内存被缓存但未泄漏**。 --- ## 💡 SQLite 特性说明:为什么内存不立刻下降? 即使你插入了很多 `randomblob(1342)`,SQLite 的内存行为如下: | 行为 | 说明 | |------|------| | 📦 使用 Page Cache 缓存数据页 | 提高性能,会占用 RSS | | 🔄 WAL 日志模式下,写操作先写日志 | 内存中保留更多脏页 | | 🧹 延迟清理 | 不会立即释放内存给操作系统 | | 📉 主动收缩需调用 `PRAGMA shrink_memory` | 否则 OS 层看不到回收 | 📌 所以:**RSS 高 ≠ 内存泄漏**,可能是 SQLite 正常缓存行为。 --- ## ✅ 如何验证是否真的泄漏? 运行这个命令手动触发 SQLite 内存释放: ```bash ubus call record stg_db_debug '{ "hd_id": 0, "sql_cmd": "PRAGMA shrink_memory;" }' ``` 然后再观察 `log_memory` 输出,看 RSS 是否下降。 如果下降 → 说明之前只是“缓存”,不是“泄漏”。 --- ## ✅ 总结:你现在看到的现象意味着什么? | 观察结果 | 含义 | |--------|------| | RSS 在最后 30 秒不变 | 进程不再分配新内存 | | 没有持续增长 | 至少不会无限吃内存 | | 无法判断是否“从低到高” | 缺少初始值和中间值对比 | | `Free` 显示超大数值 | 很可能是单位错误(把 kB 当 MB) | | SQLite 不主动还内存 | 正常行为,可通过 `PRAGMA shrink_memory` 强制释放 | --- ## ✅ 建议下一步操作 1. ✅ 检查日志开头的 baseline RSS 值 2. ✅ 检查插入过程中每 50 次的内存记录 3. ✅ 手动执行 `PRAGMA shrink_memory;` 看能否降低 RSS 4. ✅ 修复 `free` 内存显示单位错误问题 --- ### ✅ 示例:添加 `shrink_memory` 收尾 在脚本末尾加上: ```sh echo "=== D. Trying to shrink SQLite memory ===" echo "=== D. Trying to shrink SQLite memory ===" >> "$LOG_FILE" # 调用 shrink_memory shrink_output=$(ubus call record stg_db_debug '{ "hd_id": 0, "sql_cmd": "PRAGMA shrink_memory;" }' 2>&1) echo "$(date '+%H:%M:%S') | Shrink result: $shrink_output" >> "$LOG_FILE" log_memory # 再记一次内存 ``` 然后看 RSS 是否下降。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值