观测归一化:连续控制默认项
关节角、速度、力矩量纲混在一起,MLP 很难学;running mean/std 归一化是 SB3 标配,train/eval 统计必须一致。

1. 位置弧度、速度 rad/s、力牛顿混喂,梯度被大尺度维主导
MuJoCo obs 可能同时有位置(弧度)、速度(rad/s)、接触力(N),数值范围差几个数量级。直接喂 MLP,大尺度维的梯度淹没小尺度维——policy 看起来在训,实际只学 force 忽略 angle,deploy 行为偏科。上次 reach 任务关掉 VecNormalize 同 seed 完全训不动,打开后 1M step 收敛——normalize 不是 optional 预处理,是连续 control 的默认契约,与 model weight 同级打包 deploy。
2. VecNormalize 机制
SB3 VecNormalize 对 obs 和/或 reward 维护 running mean 和 std,训练时 normalize。norm_obs=True、norm_reward=True 常见:
from stable_baselines3.common.vec_env import VecNormalize
env = VecNormalize(env, norm_obs=True, norm_reward=True)Fixed scaling 适合已知物理范围:角度除以 ,速度除以 max_vel——不依赖 running stats,deploy 更简单,sim2real 时 domain 固定则优先 fixed。某一维 std 极小,normalize 会放大噪声;可对 std 设 clip=1e-3 下限。clip_obs 默认 10,contact force 偶发 spike 被截断会丢信息——clip 前单独 log raw 便于诊断。
3. Train vs eval:load stats 是 deploy 门禁
Eval 必须 load 训练时的 running stats,设 training=False——否则 obs 分布突变,policy 表现像随机。训练结束除了 model.save,还要 vec_normalize.save("vecnormalize.pkl");评估时 VecNormalize.load(path, env)。我踩过坑:eval 忘了 load,一夜白训——MLOps 要把 normalize 与 model 绑成 pair artifact。
Eval 用全新 stats 在线更新、与 train 不一致,是常见 deploy bug。Fine-tune 新 domain(新相机、新光照)时 running stats 过期——training=True 再跑几千 step 更新 stats,或 frozen stats 接受一定 domain gap。Sim2real 常在 real 上重新估 stats 或用 sim stats + domain gap 接受一定 loss——两种都试,文档写选了哪种。
4. 视觉 vs 向量分开
像素输入 CNN 前 /255 或 ImageNet normalize;不要对像素用 running mean/std 除非 domain 固定。Image obs 别和 proprioception 共用一个 running std——CNN branch 各自处理;有时只对 proprioception 部分 VecNormalize,视觉走 batch norm。视觉任务有时只做 /255.0;低维 state 更依赖 running std。
Export deploy 时 normalize 参数(mean/std/count)与 weight 一起打包;MLOps 校验 count 避免未收敛 stats——count 太小 mean/std 不可靠。Reward normalize 后改 env reward,stats 过期——改 reward 要 reset 或重训 stats。
5. 失败模式
Eval 用全新 stats 在线更新。极端 outlier 污染 running mean——单 step 异常 force 拉偏 std。Image 与 proprioception 共用 running std 导致 CNN 输入 scale 错乱。Deploy 只 load model 不 load pkl——最常见 deploy 事故之一。
6. Fine-tune 与 domain shift
Fine-tune 新 domain 时 training=True 再跑几千 step 更新 stats 是 pragmatic 做法;完全 frozen stats 有时 domain gap 太大。Real 上只更新 running stats 不重训 policy 试一把——有时够用,有时必须 fine-tune,两种都试并文档化。Normalize stats 导出 JSON(mean/std/count)进 model bundle,部署校验 count 阈值。
7. 案例:eval 未 load stats 像随机
Train 20M step,eval 脚本只 model.load,忘 VecNormalize.load。Eval return 接近 random,团队怀疑过拟合。加 load pkl 并 training=False 后 eval return 与 train 曲线一致——根因是 obs 空间变了,不是 policy 坏了。Fix 一行,省一周 debug。
8. 验收
- Log obs 各维 min/max/mean;关掉 normalize 同 seed 是否训不动。
- Save/load checkpoint 时同时 save VecNormalize.pkl。
- Eval 脚本显式 load stats 并设
training=False。 - Sim2real 文档写 stats 来源。
- Deploy bundle 含 normalize JSON,count 阈值校验。
9. 与 reward normalize 的联动
Obs 与 reward normalize 常同时开——改 env reward 后 reward stats 过期,Q 与 policy 仍按旧 scale 训会 oscillation。改 reward 与改 normalize 一起 code review。Fixed scaling(角度/)与 VecNormalize 可混用:已知范围的维 fixed,其余 running std。Deploy bundle:model + vecnormalize.pkl + stats JSON(mean/std/count),MLOps 校验 count>1000。Sim2real 时在 real 上重估 stats 或 frozen sim stats 接受 gap——两种都试,文档写选了哪种。Eval 脚本模板强制 VecNormalize.load + training=False,缺一行 review 打回——我见过太多「eval 像 random」实际是未 load pkl。
10. 录包与 deploy bundle
Deploy bundle 三件套:model、vecnormalize.pkl、stats JSON(mean/std/count);MLOps 校验 count>1000。Log obs 各维 min/max/mean 进 train 摘要,deploy 异常时对比。Fine-tune 新 domain:training=True 再跑几千 step 更新 stats,或 frozen sim stats 接受 gap——文档写选了哪种。改 reward 后 reward stats 过期,与 obs normalize 一起 code review。Image 与 proprioception 分开:像素 /255,向量 VecNormalize,共用 running std 是常见 bug。连续控制里 obs normalize 是默认项——关掉 normalize 同 seed 训不动是典型症状。Deploy 表现像 random 时先查 eval 是否 load pkl,一行 fix 省一周 debug。Reward normalize 后改 env reward 要 reset stats;fine-tune 新 domain 时 training=True 再跑几千 step 更新 stats 是 pragmatic 做法。VecNormalize 与 model 配对 artifact,MLOps checklist 与 weight 同级;某一维 std 极小设 clip 下限防噪声放大。
11. 收束
VecNormalize 与 model 是配对 deploy artifact,缺 pkl 表现像 random。Eval 强制 load stats 并 training=False;MLOps bundle 含 stats JSON 与 count 阈值。连续 control 默认 normalize,不是 optional;改 reward 与 normalize 一起 review,stats 过期会 train/eval 分布断裂。Visual 与 proprioception 分开 normalize 是 CNN+MLP 混合 obs 的标准做法。关掉 normalize 同 seed 训不动,是连续 control 的典型症状,先 normalize 再谈 network 宽度。Deploy bundle 缺 pkl 是最常见 deploy 事故之一,eval 脚本模板强制 load 并设 training=False。Reward normalize 与 obs normalize 常同时开,改 reward 后 stats 过期会导致 Q 与 policy oscillation,与改 reward 一起 review。Fixed scaling 适合已知物理范围,running std 适合分布漂移的维——两者可混用。
相关
也可以看看
johan's blog