尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Sparse4Dv3复现实战:自动驾驶3D检测的稀疏时序融合解析

Sparse4Dv3复现实战:自动驾驶3D检测的稀疏时序融合解析 过去这半年我陆续复现过几类视觉模型从 3DGS3D高斯泼溅到 FixMatch 这类半监督分类方法再到 AdaLoRA 这种参数高效微调方案。这些项目各有各的门道但要说把工程底子练得最扎实的还是最近完成的 Sparse4Dv3 代码复现。Sparse4Dv3 是面向自动驾驶视觉中心 3D 检测与跟踪的稀疏 4D 感知算法核心思路很直接用一组可学习的稀疏查询取代传统 BEV 特征图从多视角图像中采样时序信息直接输出 3D 检测框和跟踪结果。它在 nuScenes 数据集上的 NDS 指标能做到 0.61 附近在同类稀疏感知方案里非常有代表性。这篇笔记不是我复现过程的流水账而是想把这套经验完整写下来从环境配置、数据处理、模型结构拆解到多卡训练、指标核对、问题排查全部覆盖。如果你正准备入坑自动驾驶感知方向的项目复现或者正在调 Sparse4D 系列代码但遇到 loss 不稳、指标偏低的情况这份记录应该能帮你少走不少弯路。需要提前说明的是项目里的具体参数我会给出参考值但更建议以官方仓库的 config 为准毕竟环境和个人硬件差异很大。1. 复现前最重要的事先吃透 Sparse4Dv3 改了什么很多人拿到开源代码第一件事就是配环境、跑训练结果跑到一半发现模型结构理解错了改来改去浪费大量时间。我的习惯是先把论文和代码结构过一遍把关键设计点抄在纸上再动手。Sparse4Dv3 不是那种换了个损失函数就能涨点的简单工作它的三个核心创新点和代码结构是强绑定的理解不到位后续排查问题会非常痛苦。1.1 三代 Sparse4D 到底在解决什么问题Sparse4D 系列是地平线在纯视觉 3D 检测方向的一组代表性工作。第一代最核心的贡献是把主流 BEV 方案里的“稠密网格”换成了“稀疏查询”。BEV 类方案需要先把多视角图像投影到统一的俯视图网格上再在网格上用 transformer 解码网格分辨率一高算力和显存就成倍上涨。Sparse4D 的做法是直接用一组低维的稀疏锚点作为查询通过可变形注意力去透视图像上采样特征整个流程不再依赖显式的 BEV 体素特征。这个思路在当时非常前沿直接拉低了视觉中心 3D 检测的计算成本。到了 Sparse4Dv2作者把时序信息加了进来。它把上一帧的对齐特征和当前帧查询做融合又增加了第二轮 refine 阶段让每一帧的检测都能借助历史帧的信息来提精度。v2 的性能已经很不错但那时它做时序融合的粒度还偏向特征级对不同时间尺度、不同目标状态的表达能力有限。Sparse4Dv3 的主要工作就是沿着“时序”和“稀疏”这两条线继续深挖。它提出实例级时序融合让每个检测实例直接聚合它在历史帧中的特征与位置又提出解耦注意力把采样点拆成定位组和内容组让查询同时拥有全局定位和局部精修的能力再加上一个质量估计器用预测的 3D IoU 去修正分类置信度。这几个设计每个都对最终指标有实打实的贡献代码里也都有对应的模块。1.2 复现的目标是“跑准”不是“跑通”做代码复现常见误区是以为“能跑起来”就等于“复现成功”。对 Sparse4Dv3 这种训练依赖很重的模型来说跑通只是第一步能不能把 nuScenes val 上的 NDS 拉到接近论文水平才是真正考验人的地方。为什么难首先是训练时间长8 卡起步十几二十个小时是常态其次是数据预处理细节多nuScenes 的 infos 文件怎么生成、can_bus 怎么关联、多视角相机参数怎么对齐都会直接影响模型输入再者Sparse4Dv3 里有大量“不加就掉点”的训练技巧比如 GT 采样、EMA、长时序训练、混合精度等等。这些技巧在论文里可能只是两三句话但在复现时每一项都可能拉开好几个点的差距。我在动手前把论文和官方 README 各读了两遍然后把关键超参数抄到便签上再开始配环境。这个习惯救了我很多次因为很多问题其实是前期设计没想清楚而不是代码写错了。接下来我就按实际复现的顺序把这套流程完整展开。2. 环境准备与 nuScenes 数据预处理Sparse4Dv3 是典型的重依赖项目环境稍有不对光编译就能耗掉你半天。这一节先讲版本组合怎么选再说 nuScenes 数据怎么准备最后是加载性能优化。这些都是“看不见”但特别影响体验的环节。2.1 版本组合怎么选最稳官方代码基于 MMDetection3D 生态所以版本匹配是第一道坎。我这次采用的组合如下整体比较稳Python 3.8.8PyTorch 1.11.0CUDA 11.3mmcv-full 1.5.3mmdet 2.25.1mmengine 0.6.0mmdet3d 1.0.0rc4numpy 1.21.5这组版本不是唯一答案但它是社区里跑通率比较高的一套组合。mmcv-full 编译起来比较耗时能用预编译的 wheel 就直接用不要自己从源码编。如果用的是比较新的显卡比如 RTX 4090 或者更高型号还要注意驱动自带的 CUDA 版本和 PyTorch 的 CUDA runtime 是否兼容否则会在模型前向时报奇怪的算子错误。还有个小经验建好 conda 环境后先写一段简单的脚本import torch、mmcv、mmdet3d确认都能正常加载再进入下一步。这不花多少时间但能帮你把“环境杂症”和“业务代码问题”隔离开。我在这个环节吃过亏一度以为模型代码有问题查了半天发现是 mmcv 编译时没带 CUDA 算子。2.2 nuScenes 数据制备与 infos 生成Sparse4Dv3 在 nuScenes 数据集上训练和评估。你需要先从官网注册下载完整数据包括 camera、lidar、map、can_bus 这几大类。很多人会漏掉 can_bus但它恰恰是 ego 运动信息的来源Sparse4Dv3 的时序对齐非常依赖它。数据准备的核心是生成 infos 文件。官方仓库通常会提供 create_data.py 之类的脚本把原始数据转成包含图像路径、标定参数、位姿信息、GT 标注的 pkl 文件。这个转换过程比较耗时建议先用 nuScenes mini 集把整个流程跑通确认 pkl 正常生成了再处理全量数据。全量数据生成的 pkl 文件可能有几个 GB磁盘空间要预留足够。生成完之后至少手动检查三处每个 sample 的相机数量是否一致、can_bus 时间戳能否对上、图像尺寸是否统一。我在第一次生成时发现部分场景因为传感器异常少了几帧相机数据如果不过滤训练时会直接导致 batch 维度对不齐。2.3 数据加载性能优化数据加载慢是这类项目的通病尤其是 nuScenes 这种图片数量很大的数据集。GPU 利用率上不去、训练时显存占用波动大很多时候问题就出在 DataLoader 上。优先把数据放在 SSD 上机械硬盘读图的速度会直接拖垮训练。其次num_workers 不是越大越好我试过 8 个 worker 反而比 4 个更慢因为在老版本 mmdet3d 的 DataLoader 里有内存锁问题。我最终用 4 到 6 个 worker 配合 pin_memoryTrue整体吞吐最稳定。如果你的内存足够大也可以考虑把 pkl 里的图像预先解码缓存到内存能省掉不少随机的 IO 时间但要注意内存占用不要挤占训练进程。3. 核心代码结构与关键模块拆解环境搭好、数据准备完接下来就是重头戏读懂代码结构。Sparse4Dv3 的代码量不算小但核心模块相对集中。这一节我按“结构概览、时序融合、注意力与质量估计”三块来讲这也是我在复现时认为最值得花时间的部分。3.1 官方仓库的结构以及我建议的阅读顺序Sparse4Dv3 的官方实现基于 MMDetection3D 框架核心代码通常放在 projects/Sparse4Dv3 目录下。按模块来划分大致包括模型主体、检测头、注意力模块、时序融合模块、质量估计模块和若干工具方法。我建议的阅读顺序是先看模型主体搞清楚 backbone、neck、head 是怎么串起来的然后看 head理解 proposal 生成和 refine 阶段的关系接着读注意力模块弄明白可变形采样是怎么做的最后再看时序融合和质量估计器。不要一上来就钻进某个算子细节里先把数据流走通知道每个模块输入输出是什么再深入细节。我自己在复现的时候会先跑一次前向用几个断点打印出查询的数量、特征维度、坐标范围对照论文里的描述检查是否一致。这一步看似简单但能非常有效地帮你建立起“代码变量”和“公式符号”之间的对应关系。3.2 实例级时序融合代码中最精彩的部分实例级时序融合是 Sparse4Dv3 相比前两代变化最大的地方。它的目标很直白当前帧的每一个稀疏查询都应该去历史帧里找到属于同一个目标的“自己”然后把历史帧里的特征拿过来用。这样即使当前帧目标被遮挡、模糊模型也能凭借历史信息给出更稳的预测。代码里做这件事大致分三步。第一步把历史帧的查询位置投影到当前帧坐标系下投影过程需要用到外参、内参和 ego 运动轨迹第二步在当前帧查询和历史帧查询之间做特征聚合可以是基于距离的最近邻匹配也可以是可学习的注意力打分第三步把聚合后的历史特征和当前帧特征做融合再输入后续的解码层。整个过程中有一个类似记忆池的结构用来缓存若干帧的查询特征和对应时间戳。这个模块最坑的地方在于坐标变换。我在第一次实现时把外参矩阵的行列顺序弄错了结果模型训练出来 NDS 非常低排查了很久才发现是历史帧查询投影偏了一个方向。如果你也在复现这个模块建议单独写个可视化脚本把投影后的历史锚点画到当前帧图像上看看位置合不合理这一步能省下大量盲调时间。3.3 解耦注意力与质量估计器解耦注意力是在可变形注意力基础上的改动。标准可变形注意力会让每个查询在所有采样点上学一组偏移然后采特征。Sparse4Dv3 的思路是把采样点拆成两组一组负责“位置”偏移范围可以大一些主要用于搜索目标在空间中的大致位置另一组负责“内容”偏移范围小一些集中在目标附近做精细特征提取。这样做的好处是同一个查询既能看全局又能看细节而计算量并没有成倍增加。质量估计器则是在 head 里多接了一个小分支用来预测预测框与真实框之间的 3D IoU。推理时把这个预测的 IoU 乘到分类分数上作为最终置信度。这一招解决的是分类分数和定位质量不匹配的问题有些框分类很自信但位置偏了单纯按分类分数排序会把这类框排上去乘上 IoU 预测之后质量差的框会被压下去。代码里这个模块通常不复杂但对最终指标影响很明显。我建议把质量估计器的预测结果单独打印出来和真实 IoU 对比一下如果两者相关性很弱说明这个分支没有训练好需要考虑调整损失权重或者给这个分支更长的训练轮数。4. 训练复现的关键环节与多卡调参数据和模型都准备好了就进入最耗资源的训练环节。Sparse4Dv3 的多卡训练有很多细节稍不注意就是训练两天发现指标离论文差一大截。这一节我把整套训练流程、涨分技巧和显存优化经验都写出来。4.1 完整训练流程与超参数参考Sparse4Dv3 的训练通常是分阶段走的。第一阶段先训练一个不含长时序的 base 模型用来初始化模型参数第二阶段再加载 base 权重用连续多帧的时序数据进行长时序训练。这样做的原因是实例级时序融合对查询的初始质量很敏感如果一开始就上长时序模型容易不稳定。我参考社区里的常见配置用如下一组参数输入图像尺寸短边 900长边 1600优化器AdamW初始学习率 2e-4weight decay 0.01学习率调度cosine 衰减训练轮数24 epochbatch size88 卡每卡 1 个样本混合精度fp16EMAdecay 设为 0.9998GT 采样开启这组配置下在 8 卡 A100 上大概要训练一天左右如果换 V100 或者加长时序时间还要翻倍。训练过程中要定期保存 checkpoint最好每 1 到 2 个 epoch 存一次同时记录验证集上的 NDS 和 mAP用最好的那版做最终评估。4.2 涨分关键的三个 Trick第一个是 GT 采样。自动驾驶场景的类别分布很不均衡行人和车辆这种常见类别样本多但一些少见类别如工程车、拖车就很少。GT 采样的做法是从数据集中随机挑选其他帧的真实 3D 框以合理的位置和朝向复制到当前训练样本里增加少见类别的样本量。这个 trick 在 Sparse4Dv3 里非常影响 NDS我试过不加的情况下指标能掉 3 到 5 个点影响非常直观。第二个是 EMA。对模型参数做指数移动平均可以减少训练后期参数的剧烈震荡尤其在检测头部分效果明显。推理时加载 EMA 版本的权重通常比直接用最后一轮权重更稳。这个技巧实现的代码量很小但性价比很高。第三个是长时序训练。第二阶段用连续多帧数据做实例级时序融合是 Sparse4Dv3 最终涨到高指标的关键。时序帧数不是越多越好帧数增加会带来显存和训练时间的线性增长我试过 5 帧和 8 帧8 帧在远距离小目标上的收益最明显但训练成本也更高。建议根据自己的卡量和时间预算来选择。4.3 显存、训练时长与 checkpoint 策略显存是训练这类模型最现实的问题。把 fp16 打开是最基本的操作如果还是 OOM可以先把输入图像尺寸降下来等代码完全跑通之后再调回去。另一个办法是开启梯度累计比如每 2 个 step 累加一次梯度等效于扩大 batch size但不会增加单卡显存压力。Sparse4Dv3 的时序融合模块在长时序训练时会缓存多帧特征这部分比较吃显存可以考虑用 gradient checkpointing 换取显存空间。训练时长要提前做好心理预期。我自己的经验是第一阶段 base 模型训练相对快一些第二阶段长时序训练才是真正的大头。如果中途因为环境问题或数据问题中断最好能保存 ckpt 支持无缝恢复不然重来一次的成本太高。checkpoint 保存建议同时保留普通权重和 EMA 权重这样即使普通权重后期过拟合也能用 EMA 版本兜底。5. 复现过程中的常见问题与排查实录这一节把我在实际复现中踩过的坑和排查思路整理出来按环境、数据、训练三块分类。每一条都是真实经历希望能帮你省下一些调 bug 的时间。5.1 环境配置阶段的坑mmcv-full 编译报错是重灾区。最常见的错误是 CUDA 版本和 PyTorch 的 CUDA runtime 不一致导致编译时找不到 cu 头文件。解决办法就是官方找一版匹配的组合直接用预编译 wheel不要自己编。另一个常见问题是用多卡训练时deformable attention 的自定义算子报“CUDA error: invalid device function”。这个问题多数是 mmcv 编译时没有开启对应显卡的算力支持重新安装匹配当前显卡的 mmcv 版本即可。印象里这种报错特别迷惑人因为单卡跑得通一上多卡就崩。5.2 数据准备阶段的坑生成 infos 文件时出现 KeyError 指向 can_bus一般是 data 目录里少了 can_bus 文件夹或者路径配置不对。还有可能是时间戳对齐出问题这会导致后续时序融合时找不到对应的位姿信息。另一个常见问题是数据增强后图像尺寸不一致比如随机翻转或者 resize 没有按照统一尺度会导致 backbone 输出的特征图尺寸和 head 预期不匹配。我也遇到过训练时 GPU 利用率只有 30% 左右的情况排查下来是 num_workers 设太高导致 DataLoader 内部发生内存争抢。把 num_workers 降到 4 并打开 pin_memory 之后GPU 利用率恢复到 90% 以上。数据加载这个环节看着不起眼但确实能影响整个训练节奏。5.3 训练过程中的坑训练时 loss 变成 NaN是最让人头疼的问题。我遇到的情况有两种一种是初试学习率太高导致 loss 直接爆掉调低学习率后恢复另一种是某张训练图像是全黑帧经过归一化之后出现异常值在数据加载时做一下过滤就好了。排查方式很简单写一段代码单独加载几个 batch跑一次前向和反传看看 NaN 出现在哪里再做针对处理。还有一种奇怪现象训练 loss 正常下降但验证时 NDS 和 mAP 几乎为 0。这种情况十有八九不是模型没学会而是推理后处理时坐标转换出了问题。比如把网络输出的归一化坐标还原到 ego 坐标系时用错了外参顺序或者忽略了翻转增强。先拿一张图可视化预测框和标注框通常一眼就能看出问题在哪。5.4 常见问题速查表我整理了一张速查表方便你遇到问题时快速定位方向。现象可能原因解决方式mmcv 编译失败CUDA 版本与 PyTorch 不匹配换预编译 wheel 或调整 CUDA 版本多卡训练报 invalid devicemmcv 未适配当前显卡算力重装匹配显卡的 mmcv 全量版本infos 生成报 can_bus 缺失数据目录缺少 can_bus 或路径错误下载完整 nuScenes 数据并检查路径图像尺寸不一致数据增强未按统一 scale检查 transform 配置固定短边/长边GPU 利用率很低num_workers 设置过高降到 4~6打开 pin_memoryloss 为 NaN学习率过高或异常输入帧降低学习率检查并过滤黑帧/坏帧训练 loss 正常但指标为 0后处理坐标转换错误可视化预测框核对坐标变换流程显存溢出输入尺寸或时序帧数过大开启 fp16、梯度累计或 gradient checkpointing指标比论文低几个点缺少 GT 采样或 EMA补齐训练技巧加载 EMA 权重评估这张表不是万能答案但它覆盖了我复现过程中绝大多数问题。遇到新问题的时候建议先把变量隔离是环境问题是数据问题还是模型结构问题确定了方向再动手改。整个 Sparse4Dv3 复现下来我最大的感受是这类工程项目的坑往往不在模型定义而在数据 pipeline 和多卡训练的配合。建议刚上手的人先下载官方预训练权重在 mini 集上把整个推理和评估流程跑通确认指标和预期一致再上多卡做全量训练。我自己就是因为跳过了这步第一次全量训练结束后才发现 infos 文件里 can_bus 对齐方式写错了白白浪费了两天算力。Sparse4Dv3 后续往端到端方向扩展的空间很大如果有余力建议把 v3 的代码结构和下一代改进位置一起对照着读收获会更多。希望这份复现笔记对你有些帮助。
返回列表