资讯详情

资讯详情

轨迹预测评估新思路:用场景驱动替代平均误差指标

做轨迹预测评估这些年我越发觉得leaderboard 上那个光鲜的 ADE 数字有时候更像一种安慰剂。模型报到 0.3、0.4名次好看但一跑到高架出口、交叉口左转、大车切入这类场景行为就像刚拿驾照的新手——该刹车的时候犹豫该让行的时候硬挤。问题不一定出在模型身上更多时候是我们拿来量它的那把尺子钝了。这篇内容想聊的就是一把更靠谱的尺子场景驱动评估Scenario-Driven Evaluation。它不是要推翻 ADE、FDE 这些传统指标而是换一种统计视角把平均成绩拆成一本场景账本告诉你在哪类场景下模型真的可靠、在哪类场景下它在裸泳。下面这些内容都是我在实际项目中踩过坑、改过版、最后沉淀下来的经验。1. 先搞清楚我们到底在测什么1.1 ADE/FDE 的数学本质与视觉错觉自动驾驶里评估轨迹预测器最常见的两个指标就是 ADEAverage Displacement Error和 FDEFinal Displacement Error。从数学形式上并不复杂ADE预测轨迹与真值轨迹在每一时刻的平均点误差公式可以写成[ ADE \frac{1}{N} \sum_{i1}^{N} \frac{1}{T} \sum_{t1}^{T} \left| \hat{p}_i(t) - p_i(t) \right|_2 ]FDE只看终点时刻 T 的误差[ FDE \frac{1}{N} \sum_{i1}^{N} \left| \hat{p}_i(T) - p_i(T) \right|_2 ]这套指标本身没有错错的是我们使用它的方式。它会带来一个很隐蔽的视觉错觉数字越小模型越好。但所有样本的平均误差小完全不能等价于在关键场景下模型安全。举个我做过的对比测试。同样的预测模型在直行跟车场景里 ADE 能做到 0.12 米几乎贴着真值走但换成一次突然的 cut-in 场景车辆要在极短时间内重新规划目标车道同一个模型的 ADE 直接飙到 1.6 米而且预测轨迹有两次明显的方向跳变。两者平均之后ADE 可能仍然在 0.35 左右看起来还不错可这正是问题所在——平均过程把最危险的现象稀释掉了。1.2 平均指标掩盖了什么一个具体的失败案例我在某次版本迭代中遇到过一件特别典型的事。两个候选模型模型 A 和模型 B在同一个验证集上的全局 ADE 几乎一致A 是 0.321B 是 0.325肉眼几乎看不出差别。如果按传统评估流程工程师很可能直接选 A 上线因为在 leaderboard 上 A 排名更高。但当我把验证集按道路拓扑和交互对象拆开统计后分布完全不同场景子集样本占比模型A ADE模型B ADE高速巡航55%0.080.11城市路口直行20%0.250.24交叉口左转15%0.430.31匝道汇入7%0.580.36公交站台附近3%1.220.52模型 A 之所以全局分数好看是因为它在占比最大的高速巡航场景里优势明显可到了公交站台、匝道汇入这类安全敏感场景它的表现是灾难级的。如果只看全局 ADE这个问题根本不会被发现一旦落地到包含公交站台的城市道路模型 A 可能频繁触发舒适性制动甚至出现误判。这就是我想强调的第一件事评估粒度决定你发现问题的能力。全局平均指标是体检报告上的总胆固醇只能告诉你偏高还是偏低却说不清到底是哪一环出了问题。2. 场景驱动评估框架的设计思路2.1 场景驱动评估的核心主张场景驱动评估的基本主张可以用一句话概括先把测试集切成有意义的场景片段再在每个片段内单独计算指标最后按安全重要度加权汇总。为什么要切片因为轨迹预测模型本质上是输入历史状态输出未来分布的条件模型它对不同上下文的条件化能力差异很大。一个模型可能对道路跟着直走非常自信对旁边有一辆大车正在压线靠近却无能为力。把这两类样本放在同一个统计池子里等于把模型的能力缺陷平均掉了。切片的粒度也不是越细越好。按照我在项目里的经验场景定义至少要满足两个条件语义上可解释。每一类场景都能说清楚为什么是重要的比如雨天高架出口、无保护左转、行人近距离穿越。统计上可计算。每个场景类别在测试集里要有足够样本量否则单独指标没有置信度。如果切得过细每类样本只有十几个算出来的 ADE 波动会非常大反而失去参考价值。我们后来定的原则是每个场景类别在验证集中至少有 50 个预测片段否则就合并到其他交互类。2.2 场景目录怎么建规则分类与数据驱动聚类场景目录的构建行业内大致有两条技术路线规则分类和数据驱动聚类。规则分类是老派但可靠的办法。工程师根据地图拓扑、动态障碍物状态、天气与光照、道路属性这些字段用 if-else 去划分场景类型。比如如果自车道所在 link 的 curvature 变化率超过阈值就归为弯道如果车辆所在区域与道路 boundary 距离小于 1 米且相对横摆角大于 10 度可能标记为车道紧急修正如果目标障碍物是行人且距离小于 5 米则归为近距离 VRU 交互。这种方法的优点是逻辑清晰、可审计出问题的时候能顺着规则直接溯源缺点是规则之间容易重叠同一个样本可能同时满足匝道汇入和变道超车的分类条件需要人工定义优先级。数据驱动聚类正好相反。你可以把每个预测时刻周围的环境编码成向量例如使用自车位置、速度、加速度、周围障碍物的结构化状态甚至拉一个已经训练好的场景编码器然后用 k-means 或 HDBSCAN 聚类再给每个簇人工命名。这种方法的优点是能自动发现一些没被规则定义过的长尾场景比如前车两秒内连续三次点刹这种微妙模式缺点是聚类结果不稳定语料变化后簇的分布会漂移命名也非常依赖人工判读。我在实践中的做法是两者结合先用规则分类作为主骨架保证每一类场景都稳定可靠再用聚类结果作为场景雷达不定期发现潜在的新类型人工审核后补充进规则库。这样既有可解释性又能不断扩展场景覆盖。2.3 赋权策略不是所有场景都该被一视同仁场景驱动评估的第二步是加权。加权逻辑很简单不同场景对安全的影响等级不同统计损失时不能简单平均。我们的默认权重由三部分乘出来风险等级 R由安全团队给每个场景打 1 到 5 分5 分代表存在高概率碰撞风险或已发生碰撞判定。逆频率系数 I频率越低的场景权重越高避免长尾场景被大头样本淹没。具体可以取[ I_c \log\left(\frac{N}{\ n_c}\right) ]再归一化。任务优先级 T如果近期版本重点要解决路口左转的缺陷那这个场景的临时权重可以上调。最终评估分数是[ E_{总} \frac{\sum_{c} R_c \cdot I_c \cdot T_c \cdot E_c}{\sum_{c} R_c \cdot I_c \cdot T_c} ]这样算出来的总分和传统全局指标比会更贴近安全关键场景的实际表现。它的另一层价值在于当我们向管理层或合作方汇报模型能力时可以直接给出在风险等级 4 以上的场景FDE 平均是 0.57 米其中三个版本下降了 22%这类有业务含义的数字比一个冷冰冰的全局 ADE 更有说服力。3. 实操搭建一套可复用的场景驱动评估流水线3.1 数据准备与场景标签构建以我在 Waymo Open Motion Dataset 和 nuScenes 上的实践为例。这两套数据都有比较丰富的语义标注特别是 Waymo 的 sdc_scenario 结构里直接带有场景类型标签可以直接作为起步。我会先做一步场景标签扩展把原始标签、地图结构化信息和动态目标状态拼成一张宽表每一行是一个预测目标的一个历史片段列包括scenario_id、object_id、预测时刻scenario_type原始标签或自定义规则结果自车与目标车的速度、加速度、转向角最近障碍物距离、相对速度所在道路曲率、限速、车道数是否包含行人、自行车天气与光照如果数据里有人工标注的风险等级这一步其实最花时间。我的建议是先把场景标签判定逻辑写成独立模块输出一份 CSV再做评估时直接 join不要每跑一次实验都重新生成一遍标签。标签不稳定是评估结果反复横跳的最大来源之一。3.2 分场景指标的代码实现下面这个代码片段是我项目里的一个简化版评估器核心功能是输入预测结果、真值轨迹和场景标签输出每个场景的 ADE/FDE、样本数和置信区间。import numpy as np from scipy import stats class ScenarioEvaluator: def __init__(self, scenario_names, weightsNone): self.scenario_names scenario_names self.weights weights or {} staticmethod def mean_ci(data, confidence0.95): data np.asarray(data, dtypenp.float64) n len(data) if n 2: return float(np.mean(data)), 0.0 mean np.mean(data) se np.std(data, ddof1) / np.sqrt(n) h se * stats.t.ppf((1 confidence) / 2, n - 1) return float(mean), float(h) def _compute_ade_fde(self, preds, gts, horizon1.0): ade_list, fde_list [], [] for pred, gt in zip(preds, gts): err np.linalg.norm(pred - gt, axis-1) ade_list.append(np.mean(err)) fde_list.append(err[-1]) return np.array(ade_list), np.array(fde_list) def evaluate(self, preds, gts, scenario_ids): results {} ade_all, fde_all [], [] for sid in self.scenario_names: mask (scenario_ids sid) count int(mask.sum()) if count 0: results[sid] {count: 0} continue ade_arr, fde_arr self._compute_ade_fde( preds[mask], gts[mask] ) ade_mean, ade_ci self.mean_ci(ade_arr) fde_mean, fde_ci self.mean_ci(fde_arr) results[sid] { count: count, ADE: ade_mean, ADE_ci: ade_ci, FDE: fde_mean, FDE_ci: fde_ci, } ade_all.extend(ade_arr) fde_all.extend(fde_arr) # 全局值仍然保留用于对照 results[global] { count: len(ade_all), ADE: float(np.mean(ade_all)), FDE: float(np.mean(fde_all)), } return results这段代码的逻辑很直接通过布尔掩码把每个场景类别下的样本筛出来分别计算误差和置信区间。实际工程中我还会把结果封装成 DataFrame方便下一步绘图和对比多个模型版本。需要特别提醒预测轨迹长度和采样间隔必须一致否则 ADE 和 FDE 在不同模型之间没有可比性。我遇到过不少团队在评估脚本里把 6 秒轨迹和 3 秒轨迹混在一起算最后得到的提升其实是长度差异造成的假象。3.3 安全关键场景的专项评估分场景指标只是第一步。真正让这套评估产生决策价值的是安全关键场景专项评估。这类场景我会单独定义一份清单不参与全局加权而是作为硬性门禁条件。比如我心中的安全关键场景至少包括四类切入博弈cut-in旁车道目标在预测时段内跨越车道线并自车前方形成冲突点。无保护左转unprotected left turn对向直行车流持续左转车辆需要判断可穿越间隙。弱势道路使用者交互VRU interaction行人或骑行者位于自车轨迹走廊内距离小于 3 米。拥堵收窄congestion merge多车道汇入一车道存在频繁加减速和礼让博弈。对这四类场景评估标准不能只看 ADE我会额外关注两个行为合理性指标碰撞距离余量distance margin和百公里期望危险事件数expected hazard rate。前者计算预测轨迹与目标物未来最近距离的中位数后者统计预测轨迹与真值差异导致危险事件的比例。只有当场景样本量和置信区间都达标模型版本才被允许进入下一轮实车测试。用这种方式做专项评估之后我们内部对模型是否 Ready的讨论明确了很多。以前是A 版本比 B 版本分数低 0.02A 上现在变成A 版本在 cut-in 场景的危险事件率是 3.1‰B 版本是 1.7‰A 不能上。这两个讨论的性质完全不一样。3.4 结果可视化与分析评估结果一定要可视化。别小看这一步一张好的场景 x 模型热力图往往比十页指标表格更能说服协作团队。我常用的做法是画一张矩阵热力图行场景类别按风险等级从低到高排序列模型版本比如 v1.0、v1.1、v2.0单元格颜色归一化后的 FDE 或危险事件率。推荐用 matplotlib 的 imshow 或 seaborn 的 heatmap 都能快速实现。每周跑完评估后直接把这张图挂在 CI 展示墙上工程师一眼就能看出新版本到底改善了哪个场景、又在哪里退步了。可视化还有一个隐藏价值它能帮我们跟数据团队对齐场景定义是否合理。如果某个场景类别的直方图非常稀疏就说明场景标签太严格或样本采集不足如果某个类别的误差分布呈现双峰说明这一类内部还混着两种不同性质的子场景需要再往下拆。4. 踩坑实录与常见问题排查4.1 场景定义漂移同一场景在不同实验下不一致这是我最先踩到的坑。第一版评估脚本里场景标签写在评估函数内部每迭代一次代码标签判定逻辑就跟着改一点。结果同一个模型在两个 commit 下的intersection场景 ADE 一次是 0.31一次是 0.26我一开始还以为是模型优化有效后来发现只是标签判断条件变了。解决办法只有一个把场景目录与标签定义固定成独立的配置文件纳入版本管理。任何想调整场景定义的动作都必须走正式变更流程并在评估报告里标明 taxonomy_version。只有这样跨版本、跨模型的横向对比才有意义。类似的坑还包括不同线程里加载数据顺序不一致导致场景 ID 错位真值轨迹坐标没有统一到同一坐标系预测输出的频率和真值采样频率不一致。这些我都会在评估流程里加入显式的 assert 检查宁可报错也不让脏数据悄悄通过。4.2 长尾场景样本太少怎么统计场景驱动评估做深之后一定会遇到长尾场景样本不足的问题。比如夜间横穿马路的行人这类高风险场景整个验证集里可能就只有 20 来个片段算出来的 FDE 置信区间非常大没法和其他场景并列比较。我处理这种情况有三个策略只报告不排名。样本量少于 30 的场景只展示平均值和样本数不参与模型间排名免得被噪声误导。Bootstrap 重采样。对每个长尾场景做 1000 次有放回抽样输出误差分布的分位数而不是单点均值。案例库沉淀。把表现差的典型样本做成固定回归测试集虽然数量少但每个都经过人工确认专门用来捕捉模型在特定长尾场景的退化。这三个策略可以组合使用。不要因为样本太少就直接放弃长尾场景的评估它们往往恰恰是安全性差异最大的地方。4.3 权重设定的敏感性与公平对比权重策略引入后又一个现实问题出现了同一个模型在权重方案 A 下排名第一在权重方案 B 下却排到第三。怎么办权重本质上是评估者的价值判断没有标准答案但我们可以让判断过程更透明。我每次发评估报告时会附带一个简单的敏感性分析表格展示权重在正负 30% 范围内波动时模型总排名的变化。如果排名对权重非常敏感说明两个模型的优势场景正好错开这时候不能只看总分而要看安全关键场景专项结果谁更好。如果排名对权重不敏感那说明排名结果比较稳健可以放心使用。常见现象可能原因解决方式同一模型两次实验分数波动大场景标签判定逻辑变化冻结 taxonomy 配置文件纳入版本管理长尾场景指标时高时低样本量不足噪声主导使用置信区间或 bootstrap 分位数样本少于 30 只报告不排名加权总分与全局 ADE 排名矛盾不同模型优势场景错开披露权重来源与敏感性分析参考安全专项结果两个模型 FDE 接近但安全表现差异大平均指标掩盖极端异常增加碰撞距离余量和危险事件率等行为指标可视化分布出现双峰场景内部混有未拆分子类型继续拆分场景或重新定义边界4.4 关于这套方法我的一些实话用场景驱动评估将近一年后我的总体感受是它把模型好不好这个问题从模糊的玄学变成了可以讨论的工程问题。但我也要泼一盆冷水。场景驱动评估不是银弹它解决的是测量粒度的问题不能解决测试分布本身不够的问题。如果验证集里本身就没有暴雨、没有故障车、没有宠物突然窜出那再精细的评估也无济于事该补数据还是要补数据。另外也不要一开始就追求大而全的场景目录。我见过一个团队第一版就定了 40 多个场景类别结果每个类别样本都稀稀拉拉统计结果完全没法用。更好的节奏是先定 8-10 个你最关心的高风险场景跑通流程让评估结果真正影响一次模型版本决策再逐步扩展目录。少而准好过多而乱。最后分享一个实操中的小技巧在评估流水线里保留原始预测 json 和场景标签的 join 字段比如 scenario_id 和 object_id。这个习惯看似平凡却能让很多事后分析变得可能——当你发现某个版本在某个场景下退步时能直接溯源到具体预测样本而不是面对一堆聚合后无法拆解的数字。真正做到测量什么就改进什么。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →