Project Overview: UE5 Rendering Fidelity vs E2E Autonomous Driving, Part 2
三档对比里,UE4 基线(baseline)回答”模型在它被训练的渲染域里到底什么水平”。没有这个原点,UE5 侧的任何分数都无法解释——56.35 是高是低,完全取决于和 49.59 的差。
SimLingo 的 UE4 基线 DS = 49.59(10 条对齐路线,Bench2Drive 评分口径)。官方论文在 220 条全量上是 85.07±0.95,本项目数值低很多,原因是对齐路线集短、路口密集、且含模型已知的薄弱场景(如 (72,24) 路口偏航 7m 卡死属于模型真实弱点)——所以只有同一路线集内部的跨引擎对比才有意义,绝对分数不可跨口径引用。
同一模型、同一路线集、同一评分链,重跑一次,分数可以再差 12.91。来源不是评分器抖动,而是闭环行为本身的混沌:个别路线在”卡死”与”完成”之间随机翻转,一次卡死(罚分终止)和一次完成的 DS 差可达 40–60 分。
这给出一条硬约束:任何 UE4→UE5 的分数变化,必须先和重跑噪声比大小。变化小于噪声时,统计上无法区分”真提升”和”运气”。这正是后续引入 MDE(最小可检测效应)概念的动机。
开环(给定输入回放日志)下模型是确定函数;闭环里模型的每个动作改变世界状态,世界再反馈回模型——背景交通的相位、物理引擎的浮点细节、TM 的行为树决策都会被前期微小差异放大。闭环评测的噪声因此是结构性的,只能用重复测量刻画,不能消除。
MDE(Minimum Detectable Effect,最小可检测效应)回答的问题是:给定样本量和噪声水平,这个实验最小能可靠分辨多大的效应? 它是把统计功效(statistical power)换算回效应量单位的门槛。
对 条配对路线的均值差检验,经典口径下:
SimLingo:实测变化 ,而 MDE = 20.63——实验只能可靠分辨 20 分以上的变化,6.76 远在门槛之下。结论的正确表述是”未观察到显著变化”,而不是”提升了”也不是”证明没影响”。
MDE 这么大有两个来源:
对照 AutoMoT:噪声极小(重跑零差异),MDE 只有 1.89,实测变化 +32.90 远超门槛,统计显著。同一张实验台,检验力相差一个数量级——MDE 由模型自身的行为稳定性决定。
| 项 | 值 |
|---|---|
| UE4 基线 | 67.10(噪声 1.90) |
| UE5 zero-shot | 100.00(40 条评测全部完成,零碰撞、零违章、零超时) |
| 变化 | +32.90,MDE 1.89,显著 |
| UE5 侧重跑 | 逐路线零差异,完全确定 |
UE4 侧的两类丢分模式——超时(timeout)与偏航(yaw 偏离出界)——在 UE5 侧全部消失。这是全项目效应量最大、同时最干净的一组结果:不是因为路线变简单(同一批路线),而是因为失败模式没有复现。
一个合理机制:5.6B 双专家模型见过更大规模的视觉预训练分布,UE5 更真实的光影更接近它预训练时的真实照片统计,特征激活更”在域内”;而 1B 纯视觉的 SimLingo 更依赖 UE4 微调时记住的特定画面风格。这与”帮助/伤害/无差别”三假设里”预训练分布更近”的解释一致,但注意这只是机制猜测,实验本身只确认了现象。
UE5 侧 40 条重跑逐路线零差异,说明这条自建评测链(路线执行 + 评分 + socket 通信)本身高度可复现。这使 AutoMoT 成为整条链的 sanity anchor:后续任何环节改动,先跑 AutoMoT 对照,结果变了就是链坏了而不是模型变了。
把 SimLingo 的重跑差异拆到逐路线,会看到高度非高斯的结构:
均值噪声 12.91 几乎全部由这两条路线贡献。画成分布不是钟形曲线,而是”大量近零 + 两个远端尖峰”的混合分布。
对照 AutoMoT 是另一个极端:UE5 侧重跑逐路线零差异,完全确定。两个模型在同一实验台上给出两种噪声形态——这本身是发现:模型能力与行为稳定性正相关。弱模型的决策在临界场景(路口拥堵、对向车流间隙)处于”走/停”的决策边界上,前期微小扰动就能把行为推过阈值;强模型决策裕度大,行为可复现。
如果噪声真是高斯的, 的均值检验还能用 分布近似;二元翻转噪声下:
这也直接约束了微调档的评估方式:微调有没有用,要看卡死路线的翻转率是否改善,均值差在 MDE 之内时说明不了问题。
闭环系统是反馈回路:模型动作 → 世界状态 → 新的模型输入。临界场景下,初始条件(背景车相位、物理浮点、TM 行为种子)的微小差异被回路放大,最终落在不同的吸引子上——“卡死”和”通过”就是两个吸引子。这类噪声只能靠重复测量 + 逐路线配对刻画,无法用单次评测消除。
设计里有一条漂亮的归因链:KID 量化了每条路线的域差距,DS 变化量化了模型受的影响——如果两者相关(KID 大的路线掉分多),就能把”掉分”归因给渲染差异,而不只是”观察到同期发生”。
对路线级 KID 与逐路线 DS 变化做 Spearman 秩相关(Spearman’s rank correlation,对单调但非线性的关系稳健、对异常值不敏感):
| 模型 | Spearman | p 值 | 判定 |
|---|---|---|---|
| SimLingo | -0.382 | 0.28 | 不显著 |
| AutoMoT | -0.044 | 0.90 | 不显著 |
两个系数都为负(方向甚至和”域差距越大掉分越多”的预期一致),但 p 值远大于 0.05, 条路线下无法拒绝”无相关”的原假设。
更根本的问题是:归因分析需要被归因的对象存在。相关性分析的前提是 DS 有真实的、跨路线变化的信号;而实测 zero-shot 无人掉分(SimLingo +6.76 不显著,AutoMoT +32.90 是整体抬升而非按路线恶化),DS 里大部分是噪声,噪声与 KID 自然不相关。归因链断在第一环——“KID 大 → 掉分多”需要伤害才有故事,这里没有伤害。
最终口径定为受控条件下的存在性案例证据——这是负结果场景下学术表述的标准做法:报告检验力边界,不外推。
LoRA(Low-Rank Adaptation,低秩适配)冻结预训练权重 ,把增量约束为两个低秩矩阵的乘积:
是秩(常取 8–64), 是缩放系数。可训练参数从 降到 。SimLingo 上实测 LoRA 可训练参数 17,596,416(官方口径占 2.72%),训练步显存峰值约 19.6 GiB——这就是 5090 32GB 能微调 1B VLM 的原因。推理时 LoRA 分支可以 merge 回原权重(merge_and_unload),零额外延迟。
| 环节 | 规模/做法 |
|---|---|
| 采集 | UE5 侧 TM autopilot 专家,549,852 帧 / 3,511 路线 / 110GB |
| 净化 | 碰撞传感器真值打分 + 官方 filter → 834 条干净路线(专家碰撞污染是主要剔除原因) |
| 训练集 | 751 train + 83 val,106,244 样本 |
| 格式 | 转 SimLingo 官方数据集格式(纯驾驶 bucket) |
关键工程约束是格式逐位对齐:模型在评测时吃的是官方管线的字段(target point、图像裁剪、坐标系),训练数据若有任何字段语义不同,LoRA 会把模型掰向错误分布。这个约束后来正是失败的核心,见”排除法与负结果的价值”一篇。
三版结果一致:训练侧正常、闭环侧行为崩溃——见下一篇的悖论剖析。
SimLingo UE5 微调三个版本(v1/v2/v4)症状完全一致:
行为克隆的 loss 只度量一件事:模型在训练分布的输入上复现专家动作的能力。它不度量、也无法度量两件事:
所以”loss 收敛但行为崩溃”不是悖论,而是分布错配场景下的必然:分布错得越系统,拟合得越好,崩得越干净。
满油门 + 死舵且长时间不变,是权重级行为崩溃的稳定签名——模型仍在跑、链路仍通,但输出层被推到饱和区不再随输入变化。它区别于”链路假成功”(输出太平滑像 autopilot)和”模型不想走”(油门为零)。同族案例是 AutoMoT 的 BEV 层随机初始化事故:throttle≈1 且 speed≈0 主导,即”想走但看不见障碍”。诊断时先按(throttle, speed)逐帧分类,可以快速区分权重损坏与链路损坏。
微调崩溃后,按嫌疑逐项排查,七项被实证排除:
| # | 嫌疑 | 判定 | 关键证据 |
|---|---|---|---|
| 1 | 权重导出剥前缀错 | 排除 | 992 keys 与官方完全同构,strict 加载零多缺 |
| 2 | 数据教坏(专家常停车) | 排除 | 训练集速度中位 3.74 m/s,<0.5 m/s 仅占 9% |
| 3 | target_point 语义错配 | 实锤(但非唯一) | check 模式 86–96% 帧目标点差异 >0.3 m |
| 4 | 评测链坏 | 排除 | 官方 zero-shot 权重同链对照正常完赛——最有决定性的一步 |
| 5 | 权重导出方式 | 排除 | 手动导出 vs 官方 zero_to_fp32,抽样 200 keys 相对差 2e-4(精度噪声级) |
| 6 | LoRA 没挂上 | 排除 | 训练日志可训练参数 17,596,416,与官方口径一致 |
| 7 | 训练图像不裁底 | 嫌疑(v4 已修) | 评测链裁底部约 30%,v1/v2 训练裁 0% |
| 8 | 子集多样性过低 | 嫌疑(v4 已修) | 快速档 250 条 = 10 条几何路线循环 |
v2 修复第 3 项、v4 修复第 7/8 项后仍崩,可验证假设穷尽,剩余嫌疑指向帧同步、坐标系这类更隐性的生成约定。最终口径是”未完成验证”而非”微调无效”。
target_point 是给模型的导航条件输入(前方路线上要去的点)。两套算法语义不同:
run_step 每次贪心 pop 距本车 ≤7.5 m 的路线点,target 取剩余路线的第 1 个点(约 7.5–10 m 外);pop 状态跨帧顺序演进,有记忆;后果:目标点系统性偏移 3–5 m,转弯处几何发散;LoRA(小数据短训练)恰好足以把模型掰向自写算法的分布,又不足以对两种分布都鲁棒。教训的一般形式:训练数据的每个字段必须与评测链逐位同源——字段级对齐清单(target point / 裁剪 / 坐标系 / 帧同步)要逐一核对,“看起来合理”的自实现都是嫌疑。
Windows NAT/容器栈成段保留 TCP 端口,CARLA server bind 落在保留段上抛 WSAEACCES,异常未捕获直接崩(0xe06d7363)。保留段大重启后动态漂移,迁好的端口会被再次吞掉。
netsh int ipv4 show excludedportrange tcp # 启动失败先查这个,再查显卡
crash XML 里的 AdapterName 字段会误导成显卡问题——实际与 adapter 无关。最终端口迁到保留段间隙才稳定。
AutoMoT 发布权重与当代代码键名不一致:BEV 投影层在 ckpt 里叫 transfuser_proj.*,代码查表叫 bev_encoder_proj.*,shape 完全一致。流式加载器按名匹配不上 → 该层静默随机初始化 → 64 个 BEV token 全是垃圾 → 车满油门顶死障碍物(18 路线 mean DS 27.5 vs 官方 87.34)。加载器只打印 missing/unexpected,不报错。
通用防御:任何 ckpt 装载后把 missing/unexpected keys 当硬错误检查,零多零缺才放行;修复用显式的前缀重映射表,且”从文件读 key”与”向模型查 key”必须分离,混用会触发 SafetensorError。
车满油门顶在障碍物上不动,整条评测链没有任何异常——loader 和 executor 都只当”模型在开车”。这是闭环基建的普遍盲区:崩溃有报错,行为错误没有。对策是行为级监控:逐帧(throttle, speed)统计,throttle≈1 且 speed≈0 持续即判异常;blocked 检测(速度持续 <0.5 m/s 罚分终止)兜底。
模型进程与 UE5 仿真进程 socket 分离架构,12 轮迭代的代表:
save_path 格式),脱离 evaluator 复用必须逐层伪造;所有坑的共性教训:报错 + 解法完整留档是资产——失败记录本身就是可复用的工程知识。