ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MOT Challenge评估指标与TrackEval实战全解析

MOT Challenge评估指标与TrackEval实战全解析 做多目标跟踪MOT这几年我几乎每天都要跟评估指标打交道。尤其是交论文或者跑比赛的时候最后那道坎永远是同一个怎么让官方评估代码老老实实跑通并且跑出来的分数是别人能复现的。这个项目标题其实涵盖了多目标跟踪领域一个非常核心的问题——如何理解并使用整个社区公认的“一把尺子”。今天就把我在这上面踩过的坑、理清的原理、摸透的参数全部梳理一遍。不多废话直接开始。1. 评估代码到底是什么为什么绕不开MOT Challenge1.1 没有统一评估MOT算法就只剩“自说自话”多目标跟踪这个方向有一个很尴尬的历史算法五花八门数据集来源也杂乱。早期很多人用自己采集的视频自己标注一部分轨迹然后自己定义评估方法结果就是论文里各种“效果提升明显”却没人能横向对比。整个领域迫切需要一套公认的基准测试。MOT Challenge就是在这种背景下成为事实标准的。它提供了一个完整的数据集划分、目标标注格式以及一套官方评估工具。你提交跟踪结果官方代码算出一系列指标——MOTA、IDF1、HOTA、MT、ML、IDSW等等——大家用同一套代码、同一套GT谁高谁低一目了然。后面这些指标的定义和计算过程直接写进了大量论文的实验章节甚至成了期刊审稿人判断工作价值的第一道筛子。所以不只是“比赛”要用这个评估代码很多做研究、做落地评估的团队也会在自建数据上仿照MOT Challenge的格式做评测。它就是领域内的一把通用尺子。1.2 官方评估代码的“两个时代”接触这个评估代码的人通常会遇到两个版本。早期官方维护的是一套Matlab工具包配合MOT16、MOT17数据集使用里面包含evaluateTracking之类的核心函数。这套代码很经典很多老论文里的实验数据都是它跑出来的。后来官方逐渐迁移到了基于Python的TrackEvalGitHub上以TrackEval为名的独立仓库维护它支持MOT Challenge、KITTI、BDD100X等多类数据集同时也支持MOT16/17/20的结果评估。新的比赛基本都转向了这套Python实现官方服务器也要求提交结果后通过这套工具计算最终排名。我个人的建议是新项目一律直接用TrackEval别在旧Matlab代码上浪费时间。虽然老代码也有参考价值但新代码活跃维护、修复了很多历史指标计算的边界情况而且以后往新数据集扩展也更方便。不过要理解核心原理两套代码都可以读一读。2. 核心细节解析输入格式、GT格式与类别体系2.1 跟踪结果文件格式MOT Challenge的结果文件是文本格式每行表示一个检测框字段顺序不能乱。标准格式如下frame, id, bb_left, bb_top, bb_width, bb_height, conf, x, y, zframe帧号从1开始递增id目标ID同一个目标跨帧保持一致bb_left,bb_top边界框左上角坐标像素bb_width,bb_height边界框宽和高像素conf检测置信度一般取0到1之间的浮点数。对于跟踪结果来说官方在评估时通常不会用conf做阈值过滤但字段不能留空x, y, z3D信息用于3D MOT2D评估里统一填-1举个例子一行的典型内容是1, 3, 102.5, 201.3, 72.1, 198.6, 0.95, -1, -1, -1这个格式很多人第一次接触会漏掉逗号或者顺序错位后续我会单独说排查方法。2.2 GT文件的差异MOT16、MOT17与MOT20GT文件的“长相”和结果文件很像但多了几列通常是frame, id, bb_left, bb_top, bb_width, bb_height, conf, class, visibility这里的class是目标类别代号。比如7代表行人其他数值代表车辆、自行车等。visibility是目标的可见程度取值范围0到1评估时官方会用它来筛选“可评估目标”。MOT16和MOT17还有一个关键差异MOT17提供了三种检测器DPM、FRCNN、SDP对应的检测结果因此GT序列里包含多个子集而MOT16更基础不区分检测器来源。到了MOT20场景密度大幅提高GT的标注模式又有些细节变化。但有一点是通用的评估工具依赖GT文件中的visibility和class来决定哪些目标进入指标计算。比如官方评估中通常只对class为7行人且visibility大于等于某个阈值的目标计算指标。如果你提交的结果文件里混入了别的类别目标或者GT里某些行有特殊的ignore标记最后的分数字段就会出现莫名其妙的偏差。2.3 私检测与公检测MOT Challenge评估里还有一个重要的概念public detection和private detection。Public detection使用官方提供的检测结果作为输入跟踪器只负责数据关联和轨迹管理。Private detection跟踪器使用自己的检测器结果文件中可以出现比GT更多的检测框。评估代码会按你选择的“detection mode”区别对待。如果跑private模式MOTA等指标往往更高因为更好的检测结果能提升整体关联效果。但在论文里必须标注清楚否则审稿人一看就知道你玩的是“作弊对比”。TrackEval里对应的是--DO_PREPROCESS和是否对检测置信度做阈值处理的配置。如果你只是简单跑通默认是private detection模式不会限制你提交的检测框数量。3. 评估指标详解MOTA、IDF1、HOTA是怎么算出来的3.1 MOTA一票否决式的准确度MOTA的全称是Multiple Object Tracking Accuracy它关心的是三个错误的总量FP误检、FN漏检、IDSW身份切换。计算公式MOTA 1 - (FN FP IDSW) / GT总数这个公式简洁但很“无情”。如果一个目标跟丢了又找回来IDSW立刻增加MOTA下降。所以MOTA对跟踪器连续性的惩罚非常直接。但它也有一个被吐槽多年的缺陷它没有直接区分跟踪质量因为IDSW在整体分母中占比很小。当场景中有大量行人且检测漏检很多时FN会主导MOTAIDSW的影响反而被稀释。这也是后来IDF1和HOTA被推出的原因之一。实操中很多深度学习方法在MOT17上能跑到MOTA 75甚至80但IDF1可能只有70出头说明存在大量ID切换。3.2 IDF1谁才是真正的“身份保持之王”IDF1的思想是把跟踪任务看成二分类问题对每一个GT轨迹和每一个预测轨迹求得最佳匹配使得匹配上的ID数量最多。然后用匹配上的ID数量除以GT与预测ID数量的平均得到F1分数。IDF1 2 * IDTP / (2 * IDTP IDFP IDFN)这里的IDTP、IDFP、IDFN都在“ID级别”上统计。和MOTA相比IDF1对轨迹碎片和ID切换的惩罚更均衡。两个指标一起看基本能判断一个跟踪器是“检测强关联弱”还是“关联强检测弱”。我自己看实验时有个习惯MOTA高但IDF1低说明你跟踪器老是丢ID然后又重新起ID两者都高才是真正的好模型。某些场景甚至会出现MOTA和IDF1互相矛盾的情况这时候一般要综合HOTA再判断。3.3 HOTA兼顾检测与关联的综合指标HOTAHigher Order Tracking Accuracy是较新的指标核心思路是对每一个匹配上的GT-预测框对同时计算检测相似度Detection Similarity和关联相似度Association Similarity最终通过几何平均整合。HOTA sqrt(DetA * AssA)DetA检测准确率基于匹配上的检测框与GT的IoUAssA关联准确率基于轨迹ID匹配的一致性HOTA比MOTA更精细的地方在于它能区分错误发生在“检测”还是“关联”而且不会像MOTA那样被FP/FN的数量主导。官方评估中HOTA已经成了综合排名的首选指标论文里三个指标一起列也是标配。TrackEval在计算HOTA时会使用不同的IoU阈值从0.05到0.95步长0.05最后取平均。这也是为什么HOTA计算特别慢的原因——它要对每个阈值都做一次匹配。3.4 其余指标MT、ML、Frag、IDSW一个都不能少MOTA、IDF1、HOTA是三大核心但论文里通常还要列这些辅助指标MTMostly Tracked轨迹被跟踪到80%以上的GT轨迹占比MLMostly Lost跟踪到20%以下的GT轨迹占比Frag轨迹断裂次数同一个GT轨迹被切断成多段的总次数IDSWID切换总次数这些指标没有计算复杂性但从不同角度反映了跟踪器的短板。比如MT高说明你的跟踪器能保持长轨迹Frag高说明经常发生目标被短暂遮挡又重现关联模块处理得不好。4. 实操过程完整跑通一次MOT Challenge评估4.1 环境准备与代码获取以TrackEval为例。老规矩先克隆代码git clone https://github.com/JonathonLuiten/TrackEval.git cd TrackEval依赖项主要是numpy、scipy、opencv-python以及可选的pandas。如果你还需要可视化结果那建议安装matplotlib。用Python 3.7以上版本基本都能跑通。建议创建一个独立虚拟环境别污染全局环境。python3 -m venv mot_eval source mot_eval/bin/activate pip install numpy scipy opencv-python4.2 数据目录结构TrackEval要求一个固定的目录结构。以MOT17为例默认数据根目录是data它下面要有data/ gt/ mot_challenge/ MOT17/ train/ MOT17-02-SDP/ gt/ gt.txt gt_ignore.txt seqinfo.ini test/ ... trackers/ mot_challenge/ MOT17/ train/ 你的方法名/ MOT17-02-SDP/ data/ 你的结果文件.txt test/ 你的方法名/ 你提交的文件.txt第一次配置这个结构的时候很多人会被gt_ignore.txt搞蒙。其实这个文件就是用来存放“评估时需要忽略的gt目标”的比如静止的人、站立不动的人等。它在MOT17里是自动生成的但如果你自建数据集要确保这个文件存在且格式正确。seqinfo.ini是序列信息文件包含序列名、帧宽高、帧率等信息。一般是数据集自带的不需要自己创建除非你在处理自建数据。4.3 运行评估TrackEval提供了命令行接口。最简单的运行方式python scripts/run_mot_challenge.py --BENCHMARK MOT17 --TRACKERS_TO_EVAL 你的方法名 --SPLIT_TO_EVAL train参数含义--BENCHMARK MOT17评测基准选择--TRACKERS_TO_EVAL要评估的方法名对应trackers目录下的文件夹名--SPLIT_TO_EVAL train评估训练集--METRICS HOTA CLEAR Identity指定要计算的指标集合如果要评估测试集并打包提交需要加上--SPLIT_TO_EVAL test --TRACKERS_FOLDER等参数。我自己习惯在本地训练集上先快速跑一遍验证格式再跑测试集。4.4 结果文件内容跑完之后终端会打印各序列的指标也会在data/trackers/...下生成汇总的文件。常见输出包括每序列的MOTA、IDF1、HOTA等以及全局平均值。如果你的目标是在线提交官方服务器接受的最终结果就是你的结果文件本身而不是评估代码的打印输出。4.5 一个完整的实战记录以我最近跑的一个简单SORT基线为例。我的结果文件名叫sort_test.txt放在data/trackers/mot_challenge/MOT17/test/sort_test/data/下。命令执行python scripts/run_mot_challenge.py --BENCHMARK MOT17 --TRACKERS_TO_EVAL sort_test --SPLIT_TO_EVAL test --METRICS HOTA CLEAR Identity Count输出片段如下MOT17-02: MOTA: 42.3, IDF1: 45.1, HOTA: 36.2 MOT17-04: MOTA: 56.4, IDF1: 50.8, HOTA: 41.5 ... COMBINED: MOTA: 49.0, IDF1: 48.5, HOTA: 39.8这个结果在MOT17上属于“简单基线”水平但能看到整个流程能正确跑通。关键不在于分数高而在于格式正确、指标可复现。5. 常见问题与排查技巧实录5.1 结果文件格式错误最常见的错误是逗号、空格、Tab混用或者conf列写成了NaN。我见过最“经典”的坑是文件编码带了BOM头评估程序解析第一行时读到了\ufeff1结果直接报错。排查方法很简单用head -n 3查看前几行再用file查看编码。如果看到UTF-8 with BOM建议重新导出或者用命令去掉BOMsed -i 1s/^\xEF\xBB\xBF// 你的结果文件.txt5.2 轨迹ID不连续有些跟踪器为了实现在线逻辑会频繁删掉ID再新建ID导致结果文件里的ID不是从1开始的连续整数。某些评估工具对这种ID序列处理正常但自建脚本或老版本工具可能把ID为0的目标当成无效目标直接丢弃。我开始做MOT时就在这上面吃过大亏离线评估时MOTA高得离谱提交到服务器却狂掉分。后来一查原来我的ID从0开始而官方GT里目标ID是从1开始的。所以强烈建议在结果导出时统一做一次ID重映射保证ID从1开始、连续递增。哪怕只是自己离线评估这一步也能避免很多不必要的困惑。5.3 检测框坐标与图像边界评估代码不会对超界框做硬性裁剪但如果你把所有检测框坐标都限幅在图像尺寸内MOTP可能更高一点部分指标也更稳定。原因是GT框本身在标注时不会超出图像边界如果预测框跑到图像外面去了IoU计算会受到影响部分阈值下的匹配就会失败。我的建议是在保存结果文件之前把坐标clip到[0, width]和[0, height]范围内同时保证宽高为正数。5.4 类别id不匹配前面说了GT里class为7代表行人。如果你的跟踪器只跟踪行人结果文件的conf之后那几列一般填-1没问题。但如果你的结果文件里出现了class列且填了其他数字评估工具可能会把它当作其他类别然后默认忽略掉你的指标就只剩“部分目标”的分数结果看起来不高不低非常可疑。所以在跑官方评估前一定确认你的结果文件是纯跟踪结果不是检测结果。检测结果通常有类别列项目里经常有人把检测器的txt输出直接当成跟踪结果提交结果自然是“稀奇古怪”。5.5 训练集测试集混淆这是老生常谈但每届比赛都能看到有人提交错划分。MOT17的训练集和测试集都有GT但测试集GT不对最终用户开放。如果你在本地跑测试集评估只能通过官方服务器进行TrackEval默认不会对测试集输出GT指标。它会识别你跑的是test split然后做“空评估”或者直接报错提示。所以如果你想检查自己的格式是否正确建议先跑trainsplit用训练集GT确认评估流程通顺再提交测试集。5.6 Python依赖版本问题TrackEval依赖scipy的linear_sum_assignment老版本的scipy在新版numpy下会出现numpy.float不存在之类的兼容问题。具体报错一般是AttributeError: module numpy has no attribute float这个报错通常发生在scipy 1.6配合numpy 1.24时。解决办法是升级scipy到1.10以上或者锁定numpy版本在1.23左右。如果你的项目里还依赖其他深度学习框架建议在评估环境里单独装一份依赖不要和训练环境混在一起。5.7 小目标与极端宽高比官方评估代码在计算IoU的时候没有对小目标做特殊处理所以如果你的目标框小于10x10像素一个小偏移就可能导致IoU降到阈值以下从而变成FPFN。这种问题在密集行人场景特别明显。我的经验是结果文件里不要保留那些数值上明显不合理的微小框比如宽或高只有1像素的框多半是检测器输出的spurious box。保留它们只会无端增加FP拉低MOTA。在输出结果时加一个最小宽高限制比如5像素在很多场景下能稳定提升指标0.2-0.5个点。5.8 多序列提交文件命名最后分享一个非常隐蔽的坑TrackEval要求你的方法文件夹下每个序列子文件夹的名字必须和GT序列文件夹名字完全一致。MOT17这种多检测器结构尤其容易出错比如你把结果都放到了MOT17-02但GT里是MOT17-02-SDP、MOT17-02-DPM、MOT17-02-FRCNN评估时找不到前者的结果就会直接跳过序列最终汇总分数把你搞迷糊。解决方法是提交前先用一个小脚本检查tracker结果的序列列表与GT序列列表是否一致。这个脚本本身很简单但能省下大量排查时间。6. 模型评估参数代码自定义数据集与超参配置6.1 修改参数适配自建数据实际项目里很少直接用MOT17原版数据更多是自建数据集。你只需要仿照MOT Challenge的格式组织GT和结果文件然后在TrackEval里新增一个benchmark配置即可。具体来说你需要在track_eval/datasets/mot_challenge_2d_box.py里注册一个新数据集类或修改现有类中的gt_folder和tracker_folder路径。保证gt.txt和seqinfo.ini存在。在评测时保证序列文件夹的命名一致。我建议在自建数据集上先跑一个极小的子集比如2-3个序列确认GT格式无误后再扩展到全量。6.2 常用参数速查我自己调试时经常用的参数模板python scripts/run_mot_challenge.py \ --BENCHMARK MOT17 \ --TRACKERS_TO_EVAL my_tracker \ --SPLIT_TO_EVAL train \ --METRICS HOTA CLEAR Identity \ --USE_PARALLEL True \ --NUM_PARALLEL_CORES 4--USE_PARALLEL True多进程加速序列多的时候体感很明显--NUM_PARALLEL_CORES并行核心数--METRICS选择指标集合。只跑HOTA和Identity会比全跑快得多如果你只想快速验证格式跑一个序列就够用--SEQMAP_FILE指定seqmap或者直接在data/gt/mot_challenge/MOT17/train目录下删掉多余的序列文件夹临时验证即可。6.3 理解不同指标下的优化目标最后想说一个角度问题不同指标导向的优化策略完全不同。如果你关注MOTA那就把检测器做得更准降低FP/FN往往是关键如果你关注IDF1那么数据关联的平滑性和长时记忆更重要哪怕检测器偶尔漏掉一两帧也要通过关联模型保持轨迹的稳定。HOTA则会带来一个折中的优化目标要求检测质量和关联质量同时达到不错水平。所以我强烈建议在调模型的时候把评估代码的指标输出当成一个“多通道反馈”而不是只看一个总和。我见过大量团队天天刷MOTA结果到了挑战赛上因为IDF1不达标被刷下来回头抱怨“MOTA明明很高啊”其实就是没搞明白这两个指标各自的侧重。这是我跑MOT评估这些年最深刻的体会。官方评估代码的价值不只是一个“打分器”它逼着你把跟踪问题的本质拆开——检测好坏归检测关联好坏归关联HOTA还会进一步细化到不同IoU阈值下的表现让你知道自己的模型到底是“检测不济”还是“关联稀烂”。把这几块理解透了才算真正会用这套工具。
返回列表