返回专辑
·Johan·16 分钟阅读

约束 MDP 的代价估计偏差:安全层看起来在工作

区分期望约束、条件风险与硬屏蔽,解释代价 critic 偏差如何让可行集虚胖;用约束违反轨迹占比、拉格朗日乘子轨迹和安全层旁路注入验收。

约束 MDP 的代价估计偏差:安全层看起来在工作

一套机器人策略在仿真里通过了约束曲线:平均回报上升,平均代价压在阈值以下,安全层日志也显示大多数动作没有被改写。上线前做人工回放,轨迹看起来保守;可一旦换到稍窄的通道或更高摩擦的地面,策略会连续贴边,偶发碰撞之后拉格朗日乘子才开始抬升。这个现象很容易被误读成「安全层响应慢」或「惩罚系数还不够大」。更常见的根因是:训练目标只保证期望代价,代价 critic 又系统性低估了策略在关键状态里的未来代价,于是算法相信自己站在可行集内部,实际只是踩在估计误差撑出的气泡上。

这篇只讨论约束强化学习栈里的统计与优化问题,不替代工程硬护栏。关节限位、速度夹紧、急停和碰撞屏蔽仍应在策略外独立存在。这里关心的是另一层:当团队说「我们用了 CMDP,安全约束已经进训练目标」时,应该问的不是曲线是否好看,而是代价定义是什么、风险目标是什么、critic 是否乐观、乘子是否被偏差喂成了错误信号,以及旁路掉安全层时策略本体是否已经学会规避危险。

1. CMDP 约束的是期望,不是每一步都安全

普通 MDP 写成 。约束 MDP 额外定义代价 和阈值 ,典型目标是

这个式子里的约束是轨迹分布上的平均量。若 是一次碰撞记一分,阈值 表示长跑平均碰撞代价不超过某个预算;它并不推出「任意一条轨迹都不碰」。如果任务里有大量容易场景和少量窄门场景,平均代价可以被容易场景稀释。对仓储车、机械臂或无人机来说,少量尾部事故往往正是验收最关心的部分。

有限 horizon 中还要写清终止语义。碰撞后直接终止并给一次代价,和碰撞后进入吸收状态持续累计代价,得到的约束强度不同。前者容易让短 episode 的危险行为看起来便宜:策略高速冲过去,失败轨迹只贡献几个时间步,成功轨迹贡献完整 reward。若日志只画每步平均 cost,短失败会被进一步摊薄。因此代价统计至少同时报告每轨迹累计 、是否发生过违反的指示量 ,以及按场景切片的分布。

2. 拉格朗日法把安全问题变成两个估计问题

工程里常见的 Lagrangian PPO、SAC-Lag 或近似 CPO 会把约束并入对偶目标:

实现上通常交替更新策略和乘子:

这一步看似简单,实际把安全性托付给两个估计器: 是否准确,策略梯度里使用的代价优势 是否给出了正确方向。若 cost critic 低估危险动作的长期代价,乘子会被告知「还没超预算」,策略更新也会觉得当前动作可行;等真实 rollout 暴露违反时,策略已经在边界附近形成高 reward 行为。

更麻烦的是,乘子只按批次平均代价更新。一个 batch 中九成轨迹完全安全,一成轨迹严重违反,平均值可能仍低于阈值;乘子不升,actor 继续追 reward。于是 dashboard 上看到的是 平稳、平均 cost 合格、安全层很少介入,现场看到的是稀有场景里策略突然失控。乘子不是安全监控器,它只是对当前估计目标的对偶变量。

3. 代价 critic 为什么会乐观

代价 critic 学的是

TD 目标通常写成

低估来源很多。第一是稀有事件采样不足。碰撞、越界、电流过载本来就是低频尾部,replay buffer 按均匀采样时,critic 大部分梯度来自零代价样本。网络在危险边界附近只有少量正样本,平滑拟合会把尖峰抹平。

第二是 bootstrap 延迟。一次危险动作的代价可能在几步后才出现,比如高速转向先让底盘姿态恶化,随后才撞到货架。若 已经偏低,当前目标也偏低,误差沿时间反向传播得很慢。奖励 critic 的乐观会诱导策略更积极,代价 critic 的乐观却会让约束更新失明。

第三是动作分布漂移。采样数据来自旧策略或带安全层的执行动作,更新后的 actor 在同一状态下提出更激进的动作。critic 被要求评价这些动作,但数据覆盖不足。若执行日志只存 post-filter 动作 ,却用 actor 原始动作 更新策略,critic 实际学到的是「被安全层修正后的代价」,并不能评价旁路安全层时的策略本体。

第四是标签设计把严重程度压扁。许多项目把任何碰撞都记为 ,轻擦和高速撞击同价;或者把越界后终止造成的丢失信息当成零后续代价。critic 会学到一个过于温和的风险地形,策略自然贴着边界走。代价不一定要无限大,但必须和验收关心的事故等级同向。

4. 可行集虚胖:估计误差如何改变约束边界

把策略参数空间里满足真实约束的集合记为

训练时看到的是估计集合

,其中 是低估偏差,那么一大片真实不可行的策略会落入 。这就是可行集虚胖。它比普通 reward overfitting 更危险,因为优化器会主动贴着估计边界寻找高 reward;边界一旦向外膨胀,最优点往往正落在虚假的外壳上。

从曲线看,虚胖有几个典型信号。平均 cost 在阈值下方小幅震荡,但违反轨迹占比不降; 长期贴近零或只在事故 batch 后尖峰;安全层改写率低,但改写集中在少数场景;eval 时 deterministic policy 比 stochastic policy 更贴边,因为它选择了 critic 认为最划算的尖动作。单看均值会漏掉这些结构。

更隐蔽的版本发生在多约束场景。比如同时约束碰撞、关节速度和电流,三个 cost critic 的误差方向不同。actor 会寻找被某个 critic 低估的缝隙:避开碰撞代价,却用高加速度擦过电流阈值;或满足速度均值,却在转角处产生短时尖峰。多约束不是把三个曲线画在一张图上就结束,必须看联合违反模式。

5. 期望约束、CVaR 与硬屏蔽不是同一个目标

期望约束适合管理长期预算,例如平均能耗、平均舒适度、可恢复的小幅越界。它的优点是优化稳定,样本需求相对可控;缺点是对尾部事故不敏感。若业务语义是「偶发事故也不可接受」,期望约束只是辅助训练信号。

条件风险价值(CVaR)把目标转向尾部。对轨迹累计代价 ,可写为

它关注最坏那部分轨迹的平均代价,比单纯 更贴近「少数危险场景不能被多数安全场景稀释」的诉求。但 CVaR 也不是硬保证:它仍是分布目标,依赖采样覆盖和尾部估计;若仿真没有生成窄门、湿滑地面或传感器延迟,CVaR 只会在已见尾部上认真。

硬屏蔽或安全过滤器处理的是逐步可行动作集合。若有已知状态约束 ,屏蔽层可以把 actor 输出投影成

这类机制回答「当前动作是否允许」,不是「策略分布长期代价是否低」。它能挡住已建模的硬边界,却可能改变训练分布:actor 学会持续提出越界动作,反正过滤器会拉回。于是硬屏蔽必须和 CMDP 指标分开验收:一个保证执行动作不越界,另一个评估策略本体是否在学习可行行为。

6. 日志要同时保存原始动作、执行动作和代价来源

如果只记录环境实际执行的 ,训练后无法判断是策略安全,还是过滤器替它擦屁股。最小日志应包含 raw_actionsafe_action、过滤原因、过滤幅度、代价事件、场景标签和策略版本。critic 更新也要明确到底学习哪个动作的代价:学习 raw action 的潜在风险,还是学习 post-filter 执行后的结果。两者都可有用,但不能混在一个标量里。

python
def log_step(obs, raw_action, safe_action, cost, info, writer):
    delta = safe_action - raw_action
    writer.write({
        "scenario": info["scenario_id"],
        "raw_action": raw_action.tolist(),
        "safe_action": safe_action.tolist(),
        "shield_norm": float((delta ** 2).sum() ** 0.5),
        "shield_reason": info.get("shield_reason", "none"),
        "cost_collision": float(cost["collision"]),
        "cost_limit": float(cost["joint_limit"]),
        "terminated_by_violation": bool(info.get("violation_stop", False)),
    })

这段代码不复杂,关键在契约。若代价来自环境传感器,过滤器来自控制栈,二者时间戳必须对齐;否则会把过滤后的安全动作错配到过滤前的危险观测。对于延迟系统,还要保存 action 生效时间和 cost 触发时间,避免 critic 把几步后的事故归咎给错误动作。

7. 三条验收曲线比一条平均 cost 更有用

第一条是约束违反轨迹占比。定义 或按具体事故定义 。报告

以及按场景、初始状态、负载和随机种子的切片。它直接回答「多少条轨迹出过事」,不会被短失败或长成功平均掉。若平均 cost 降而违反占比不降,说明策略把事故变轻或变短了,但没有真正避开。

第二条是拉格朗日乘子轨迹。健康的 不一定单调,也不一定越大越安全;它应该随真实估计误差有可解释的响应。若 持续数个评估窗口而 不动,更新链路有问题。若 尖峰后迅速归零,可能是 batch 方差太大或学习率过高。若 长期很大但违反仍高,actor 可能找不到可行方向,或 cost critic 梯度在关键状态上无效。

第三条是安全层旁路注入。不是把真机护栏关掉,而是在仿真或离线回放里构造受控实验:记录同一批状态,让策略输出 raw action;分别走正常过滤、轻度放宽过滤、完全旁路的仿真分支,比较代价和动作分布。若正常过滤合格、旁路后立刻违反,说明 CMDP 没让策略本体学会安全,只是过滤器在工作。若轻度放宽就出现大量边界动作,说明策略贴着屏蔽层内侧,部署裕度不足。

8. 旁路注入要做成可重复实验

旁路注入最怕变成冒险演示。正确做法是只在仿真、数字孪生或离线动力学模型里运行,并固定初始状态、随机种子和场景集。每次训练里程碑都跑同一套注入集,输出四列:baseline filter、relaxed filter、bypass filter、expert 或规则策略。指标包括违反占比、累计代价分位数、raw 与 safe 的距离、以及违反前若干步的 预测。

如果旁路分支违反前,cost critic 仍预测低代价,这就是估计偏差证据;如果 critic 已经预测高代价但 actor 仍选择该动作,则问题在策略更新或乘子权重;如果只有特定场景失败,说明训练分布缺口大于算法问题。这样的归因比「再把 penalty 乘十」可靠。

还可以加一个反事实检查:把真实发生违反的轨迹回放给 critic,沿时间画 。理想情况是危险前逐步升高;若直到碰撞前一两步才升高,critic 没有学到早期预警。对于需要提前减速的系统,晚预警等价于无预警,因为控制器已经没有制动距离。

9. 修偏不是只调惩罚系数

发现代价 critic 乐观后,第一反应常是增大 或把 cost scale 放大。它能让训练看起来更保守,却不一定修复低估。若 critic 在危险状态上没有数据,乘子再大也只是放大错误梯度。更直接的动作包括:对违反轨迹过采样;按场景分层采样 replay;用 n-step return 加快迟到代价传播;为稀有事故保留单独 buffer;在评估集里加入固定 adversarial scenario;对 cost critic 使用 ensemble,取高分位或上置信界作为策略更新信号。

保守代价估计也要有边界。把 一律抬高会让策略过度保守,甚至认为所有高 reward 路径都不可行。更好的方式是在数据稀疏区域或屏蔽层频繁介入区域提高不确定性惩罚。可以把过滤幅度 shield_norm 当作风险特征,而不是只在发生事故后才给 cost。这样 actor 会在接近硬边界前就收到梯度。

对 CVaR 训练,尾部样本量是核心。若每个评估窗口只有少数违反轨迹,CVaR 估计会非常抖。应固定场景集并扩大尾部覆盖,报告置信区间或 bootstrap 区间;不能只给一条平滑曲线。对多约束任务,还要检查 tail 是否由同一种事故支配,否则优化会追着当前最大事故转移,留下另一类事故。

10. 校准与切片:先审 critic,再信策略

cost critic 不应只用训练 loss 验收。TD loss 下降只说明它拟合了自己的 bootstrap 目标,不说明它对真实累计代价校准。更可靠的做法是保留一组固定回放集,其中包含正常场景、边界场景和历史事故场景;对每个时间步保存 ,再用后缀真实累计代价

做校准图。把样本按 分桶,比较每桶的预测均值与真实均值;若高风险桶长期低估,说明 critic 在最需要保守的区域失真。还要看排序能力:危险前的状态是否排在安全巡航状态之上。一个均方误差不高的 critic,仍可能因为大量零代价样本占主导而把危险样本排错。

切片比总表更重要。按场景标签、速度段、负载、传感器遮挡、地面摩擦、起始姿态、过滤器介入原因分别画误差。若总体校准合格,但窄通道切片低估,部署风险仍在窄通道。对于连续控制,还要按 shield_norm 分桶:过滤幅度越大,说明 raw action 离可行集越远;如果这类样本的 仍低,actor 会继续把过滤器当作隐形动力学。

离线回放审计还应区分两种问题。第一种是可识别性不足:数据里几乎没有某类危险状态,critic 没法学会,只能给出不确定性。处理方式是补仿真场景、规则策略采样或人工构造边界初始状态。第二种是标签或折扣错误:数据里已有事故,但 没有传播到早期状态,通常来自终止处理、时间对齐或 cost scale 错。前者要补数据,后者要修管线;二者混在一起会让团队误以为算法不够强。

一个实用门槛是把校准审计接到训练流水线:每次保存候选策略,都在固定回放集上输出 mean_error、高分位低估误差、违反前 步平均预测、以及按场景的最大低估切片。这里的 应按系统制动距离或控制延迟定,不是随便取。若机械臂从高速到停稳需要十个控制周期,碰撞前两步才升高的代价预测不能算可用预警。

最后把审计结果落成归因矩阵。横轴放策略版本,纵轴放场景切片,单元格里同时记录真实违反占比、 低估量、过滤器介入率和 均值。若违反占比高、低估量高、过滤器介入率也高,优先修 critic 与数据覆盖;若违反占比高但 critic 已预警,说明 actor 或乘子没有服从代价梯度;若过滤器介入率高但真实违反低,说明硬屏蔽在兜底,策略本体仍不可直接信任;若只有 高而其他指标低,可能是 cost scale 或阈值语义过紧。这样的矩阵把“安全层看起来在工作”拆成可定位的工程问题,避免所有事故都被归结成一个模糊的安全不稳定。

还要保留失败样本,而不是每轮训练覆盖旧日志。代价偏差常在版本对比中暴露:新策略平均 reward 更高,旧事故场景却重新出现。固定 replay audit 集相当于安全回归测试;候选策略必须通过旧事故、边界初始状态和当前随机场景三组集合。若只能在随机场景上合格,而旧事故复现失败,说明策略不是学会了约束,只是当前采样没有撞到那块薄冰。

11. 发布门禁:把「看起来安全」拆成可证据化条件

一个可审阅的 CMDP 发布门禁至少包含四类证据。第一,目标量声明:当前用的是期望代价、CVaR,还是硬屏蔽;阈值对应轨迹累计、每步平均还是事件占比。第二,估计质量:cost critic 在固定回放集上的校准图、违反前 曲线、ensemble 分歧和场景切片误差。第三,对偶行为: 的同图轨迹,解释为什么它升、降或饱和。第四,旁路注入:正常过滤、放宽过滤和旁路分支的违反占比与动作距离。

如果这些证据互相矛盾,以最坏解释为准。平均 cost 合格但违反占比高,不能发布;正常过滤合格但旁路失败,说明策略依赖安全层,不能把 CMDP 曲线当作安全证明;乘子平稳但 critic 校准差,说明对偶优化正在优化错误信号。真正有价值的结论不是「算法已经安全」,而是明确哪些风险由硬屏蔽承担,哪些由训练目标降低,哪些仍需要补场景、补标签或降级策略覆盖。

灰度发布也要沿用同一套量词。不能训练时看期望代价,灰度时只看运维告警,复盘时又改口说事故概率。每个阶段都应保留相同的 、过滤幅度和场景标签,只是执行环境从仿真到影子模式再到低速小范围。这样一旦发现偏差,可以把真机片段接回同一套 critic 审计和旁路注入实验,而不是重新解释一套指标。指标先对齐。

CMDP 的价值在于把 reward 和 cost 分开,让策略学习时看见工程关心的代价;它的陷阱也在这里:一旦代价估计偏乐观,整套优化会用很严肃的数学把策略推向假的可行集。安全层看起来在工作,可能只是它替策略挡住了训练目标没有学会的部分。验收时把期望、尾部和硬边界拆开,把 raw action 与 safe action 拆开,把平均曲线与轨迹占比拆开,才能知道策略是在变安全,还是只是更擅长躲在估计偏差后面。

← 全部文章

johan's blog