Scaling Humanoid Whole-Body Control with One Differentiable World Model
目前训练"通用"人形全身控制器的主流路线叫动作跟踪(motion tracking):给策略一段人类动作捕捉(mocap)片段当参考,策略输出每个关节的目标位置,奖励就是机器人当前姿态和参考动作像不像。代表工作有 DeepMimic、PHC、BeyondMimic 和 SONIC。经验规律是:动作库越大越多样,训出来的控制器越强。
代价是算力。SONIC 用无模型强化学习(PPO)直接在仿真器里暴力训练,用了 128 张 L40 跑 7 天,约 21,000 GPU 小时。作者指出问题所在:PPO 把仿真器当黑箱,对每条动作都要靠大量随机采样去"重新摸索"动作的后果;而所有动作共享的那套刚体动力学结构,PPO 从来没有显式利用。于是扩展动作库等价于扩展算力。
既然动力学是共享的,就专门学一个神经网络 \(f_\omega\) 来表示它,并且这个网络是可微的。这样策略改进就不必再靠采样估计梯度,而是把"想象中的累积回报"对策略参数直接求导。训练是一个三阶段循环,三部分同时更新,而不是先学好模型再学策略:
世界模型由一个动力学网络和奖励、观测、终止三个输出头组成,每个头要么学出来、要么直接解析计算:
拟合损失(论文公式 1):从记录的状态 \(s_0\) 出发,按记录的动作往前滚 8 步,让每一步的状态、奖励、观测、终止都贴近仿真结果:
8 步自回归训练让模型学会修正自己的误差,所以虽然只在 8 步窗口上拟合,实际滚动 32 步甚至无限长也保持稳定。
每次策略更新时模型参数冻结。从回放缓冲区的真实状态和它记录的 ξ 出发,模型每步给出去噪观测,加回传感器噪声,策略 \(a_t=\pi_\theta(\hat{\bar o}_t+\eta_t,\,g_t)\) 出动作。目标函数(论文公式 2)是 32 步折扣奖励和,加上 critic 对剩余回报的估计:
整条链路可微,所以 BPTT 通过模型对 \(\hat s_t\) 和 \(a_t\) 的雅可比就能算出一阶梯度 \(\nabla_\theta\hat J\)。critic 用只在想象转移上计算的 GAE 回报拟合(公式 3),没有目标网络。奖励沿用 BeyondMimic 的动作跟踪奖励,去掉自碰撞惩罚。
这个结构接近 SHAC 的短视野 actor-critic,差别是:用学到的模型代替可微仿真器;用软存活权重代替硬终止;critic 没有 EMA 目标网络。以前用解析梯度做动作控制的工作(SuperTrack、ControlVAE)往往不用 critic,只优化 H 步奖励和,作者在第 7 节说明这会让策略短视。
| SONIC 公开 checkpoint | SuperMimic 1×H100, 3 h | SuperMimic 2×H100, 9.8 h | |
|---|---|---|---|
| 训练方式 | PPO,无模型 | BPTT 穿过学到的世界模型 | |
| 硬件 / 时长 | 128 × L40,168 h | 1 × H100,3 h | 2 × H100,9.8 h |
| GPU 小时 | ≈21,000 | 3 | ≈20 |
| 动作数据 | 611 h | 264.56 h(BONES-SEED 公开子集) | |
| 失败动作 / 129,514 | 179 | 25 | 0 |
作者对公平性的说明。两边数据量差 2.3 倍,H100 对 L40 的峰值算力最多差 5.5 倍,这两项加起来解释不了 1000 倍的差距。3 小时这个数字取得保守:从 3 小时起之后每个 checkpoint 都领先 SONIC 至少一半差距。基准上的 129,514 条都是训练集动作,所以失败数衡量的是覆盖率而非泛化。
SuperMimic 单卡 1.2 小时达到回报 0.82。PPO-Sim 在 18 小时预算内都没达到:1 卡最终 0.60,4 卡 0.70,8 卡 0.72,从 4 卡到 8 卡几乎没有提升。
回到 Figure 4(b, c)。没有 critic 时 H ≤ 16 训练失败;有 critic 时 H = 16 就能和无 critic 的 H = 32 打平,H = 32 最快到达 0.82。把 H 加到 128(H100 上能放下的最长视野)反而耗时近三倍、最终回报更低。
更有意思的是行为差异。在仿真里让机器人保持 T 字姿势并施加 0.5 到 4 m/s 的推力:带 critic 的策略 0.5 到 2.4 秒内站稳;无 critic 的策略在 0.5 m/s 就一直踉跄,4 m/s 时摔倒。原因是恢复平衡的奖励常常落在 32 步窗口之外,只有 critic 看得到。
世界模型只从仿真数据学,但它条件在域随机化参数 ξ 上,策略在想象中就见过不同摩擦和质心,所以零样本迁移到真机。项目主页另有"世界模型 vs 仿真器"的并排预测视频,包括关闭策略后摔倒和摩擦系数 0.3 的滑地面两种情况。
同一个世界模型还能训练别的目标接口。作者做了一个初步实验:一个速度指令编码器,与动作跟踪接口共享解码器和世界模型。两种训练方式对比:
从读者角度补两点。第一,这篇文章的新意不在奖励或网络结构,而在于把"共享可微动力学模型 + 短视野 BPTT + critic 自举"这套基于模型的 RL 配方第一次在大规模人形动作跟踪上做通并上真机,相关思路在 SuperTrack、ControlVAE、SuperDyno 等角色动画工作中已有雏形。第二,截至写作时项目主页未给出代码和数据下载链接,复现需要自己搭 MuJoCo Warp 管线和 BONES-SEED 数据。
先把后面公式里反复出现的概念用白话讲一遍,再给一张符号总表。读第三部分遇到不认识的符号,回来查这张表就行。
状态 \(s_t\) 是时刻 \(t\) 机器人在仿真里的完整物理状况:所有关节的角度和角速度、骨盆在世界里的位置姿态和速度等。知道了 \(s_t\) 和接下来的动作,物理定律就能唯一决定 \(s_{t+1}\)。
观测 \(o_t\) 是策略实际"看得到"的那部分。真机上没有动捕,机器人只知道自己的关节编码器、IMU 读数,不知道自己在世界里的绝对位置。所以 \(o_t\) 比 \(s_t\) 少很多信息,这就是"部分可观"(POMDP 里的 PO)。论文里 \(o_t\) 是 93 维,策略看最近 10 帧。
动作 \(a_t\) 是策略的输出,29 维,对应 G1 的 29 个关节。它不是直接的力矩,而是经过 \(q^{\mathrm{sp}}=\alpha a_t+q^0\) 变成关节目标位置,再由低增益 PD 控制器转成力矩。
策略就是"看到观测、给出动作"的函数,\(\theta\) 是它的神经网络参数。这里的策略是确定性的:同样的输入永远给同样的输出。PPO 用的是随机策略(输出一个高斯分布再采样),两者在求梯度的方式上完全不同,后面会讲。
策略还接收目标 \(g_t\):对动作跟踪来说就是"接下来 10 帧参考动作长什么样"。写成 \(a_t=\pi_\theta(o_t,g_t)\)。
奖励 \(r_{t+1}\) 是每一步得到的分数,这里主要是"身体各部分和参考动作有多像",越像越接近 1。
回报 \(J\) 是把未来所有奖励加起来,但远处的奖励打折:\(J=\sum_t\gamma^t r_{t+1}\)。折扣 \(\gamma=0.99\) 的意思是每往后一步奖励的价值乘 0.99;100 步(2 秒)之后的奖励只值当前的 37%。这样和既让策略看长远,又让总和有限。
强化学习的目标就是调 \(\theta\) 让回报的期望最大。所有方法的区别都在"怎么估计回报对 \(\theta\) 的梯度"。
摔倒或偏离参考太远,回合就终止,\(d_t=1\),之后没有奖励。为了让这件事可以进公式,定义存活权重 \(c_t\):开始为 1,每一步乘以 \((1-d_{t+1})\),终止后变 0。于是回报可以写成 \(\sum_t\gamma^t c_t r_{t+1}\),终止后的项自动归零。
在世界模型里,终止是预测出来的概率 \(\hat d_t\in[0,1]\) 而不是 0/1,于是 \(\hat c_t\) 是连续量,能对策略参数求导。这就是论文说的"软存活权重"。
\(V_\psi(s)\) 回答一个问题:从状态 \(s\) 出发,按现在的策略走下去,期望还能拿多少折扣回报?\(\psi\) 是这个网络的参数。它叫 critic(评论家),因为它只打分不行动。
它的用途是自举(bootstrap):当我们只愿意往前算 32 步时,第 32 步之后的回报用 \(V_\psi(\hat s_{32})\) 一个数代替。没有它,策略只会在乎 32 步内的奖励,叫"短视"。
MuJoCo 这样的仿真器是用物理方程写出来的:给它当前状态和动作,它算碰撞、求解约束、积分一步,给出下一个状态。它很准,但两点不好用:一是慢(接触求解贵),二是不可微,意思是你没法问它"动作改一点点,下一状态会怎么变",因为接触的开关是不连续的。
世界模型是一个神经网络 \(f_\omega\),用仿真器产生的大量 \((s_t,a_t)\to s_{t+1}\) 数据训练,学会模仿仿真器:"给我当前状态、动作和这回合的物理参数 \(\xi\),我预测下一个状态。" \(\omega\) 是它的参数。它是仿真器的替身,神经网络天然可微、而且一次前向能并行算几万个环境,比仿真器快 10 到 60 倍。
论文里的"世界模型"除了 \(f_\omega\) 还配了三个头:预测奖励 \(\hat r\)、预测观测 \(\hat{\bar o}\)、预测终止概率 \(\hat d\)。四个一起,就能在不碰仿真器的情况下"想象"一整段轨迹。
想象你调策略参数 \(\theta\) 一点点,动作 \(a_0\) 变了一点,于是 \(s_1\) 变一点,\(a_1\) 跟着变,\(s_2\) 变……最后回报变了多少?如果每一环都可导,链式法则就能把"回报对 \(\theta\) 的导数"精确算出来,这叫解析梯度。
PPO 做不到这点,因为仿真器是黑箱。它只能让策略随机试很多动作,看哪些动作之后回报高就加大其概率,这是似然比估计:\(\nabla_\theta\log\pi_\theta(a\mid o)\cdot\hat A\)。它的噪声很大,需要海量样本。
所以世界模型的价值不只是快,更在于它让"直接求导"成为可能。Figure 5(b) 的消融就是证明:同样在世界模型里训练,直接求导比 PPO 少 64 倍样本。
把 32 步想象展开成一条长链:\(s_0\to a_0\to \hat s_1\to a_1\to\hat s_2\to\cdots\to\hat s_{32}\)。每个箭头要么是策略网络、要么是世界模型网络。回报是这条链上所有 \(\hat r\) 的加权和。
反向传播从链尾出发,一步步把"回报对 \(\hat s_{32}\) 的导数"传给 \(\hat s_{31}\),再传给 \(a_{31}\) 和 \(\hat s_{30}\)……每经过一次 \(f_\omega\) 就乘一次它的雅可比矩阵 \(\partial\hat s_{t+1}/\partial\hat s_t\)。这就是 BPTT,和训练 RNN 一模一样。
问题也一样:32 个雅可比连乘很容易爆炸或消失。论文的应对是两道梯度裁剪、短视野 \(H=32\) 加 critic 补尾巴、8 步自回归训练让模型平滑。
想象 rollout 就是在世界模型里跑出的一段轨迹,相对于在仿真器里跑的"真实 rollout"。
回放缓冲区 \(\mathcal B\) 是一个大队列,存仿真器里发生过的转移。它有两个用途:训练世界模型的数据来源;想象 rollout 的起点来源(起点是真实状态,模型只负责往后推 32 步,不必从零编造姿态)。
这种"真实数据训模型,从真实状态出发在模型里想象,用想象更新策略"的套路叫 Dyna(Sutton 1990)。
训练序列模型有两种喂法。Teacher forcing:每一步都给真实的 \(s_t\) 作输入,只预测一步。自回归:只给起点 \(s_0\),之后每一步输入模型自己上一步的预测 \(\hat s_t\)。
论文用自回归 8 步。好处是模型在训练时就见过"自己的误差",学会纠偏,所以在想象里滚 32 步不会漂走。
域随机化是 sim-to-real 的常规手段:每回合随机抽摩擦系数、质心偏置等物理参数,让策略在各种"版本"的机器人上都能工作,真机就落在这个范围里。
对世界模型来说,这些参数如果不告诉它,同一个 \((s,a)\) 在不同回合会得到不同的 \(s'\),模型只能学个平均。论文把这些参数拼成向量 \(\xi\) 直接喂给模型,于是模型对每个回合都是确定的函数。这就是"随机性来源已知,所以模型可以是确定性的"。
要训 \(V_\psi\),得有"正确答案"。一步目标 \(\hat r_{t+1}+\gamma V_{t+1}\) 偏差大、方差小;直接把 32 步奖励加起来偏差小、方差大。GAE(\(\lambda\)) 用 \(\lambda=0.95\) 在两者之间做几何加权平均,递推式就是第 14 节的 \(\hat R_t\) 公式:从链尾 \(\hat R_H=V_H\) 开始,每往前一步把下一步的目标和 critic 估值按 \((\lambda,1-\lambda)\) 混合再加当前奖励。
约定:带帽子 \(\hat{\ }\) 的量是世界模型预测的版本,不带帽子的是仿真器里的真实值;带横线 \(\bar{\ }\) 的观测是去掉传感器噪声的版本;下标 \(t\) 是时刻,\(j\) 是世界模型训练窗口内的步数,\(b\) 是刚体编号。
| 符号 | 读法 | 含义 |
|---|---|---|
| 状态与观测 | ||
| \(s_t\) | s | 仿真器的完整物理状态。 |
| \(\hat s_t\) | s hat | 世界模型预测的状态。想象里第 0 步 \(\hat s_0=s_0\) 取真实值。 |
| \(o_t\) | o | 带噪观测,策略真正看到的输入(93 维 × 10 帧)。 |
| \(\bar o_t\) | o bar | 去噪观测,\(o_t=\bar o_t+\eta_t\)。 |
| \(\hat{\bar o}_t\) | o bar hat | 世界模型预测的去噪观测,再加噪声后喂给策略。 |
| \(\eta_t\sim\rho_\eta\) | eta | 传感器噪声,按 Table A.1 的均匀分布采样,仿真和想象里都加。 |
| \(g_t\) | g | 目标。动作跟踪里是未来 10 帧参考动作;速度接口里是速度指令。 |
| \(\xi\) | xi | sim context:该回合的摩擦、关节标定偏移、躯干质心偏置。 |
| 动作与策略 | ||
| \(a_t\) | a | 策略输出的 29 维动作,映射为关节设定点。 |
| \(\epsilon_t\sim\rho_\epsilon\) | epsilon | 只在仿真采数据时加的动作噪声,\(\mathcal N(0,0.1^2)\)。缓冲区存 \(a_t+\epsilon_t\)。 |
| \(\pi_\theta\) | pi theta | 策略网络,参数 \(\theta\)。由目标编码器 enc 和共享解码器 dec 组成。 |
| \(z\) | z | 编码器输出的 32 维潜变量,经 tanh 限幅。 |
| \(q,\ \dot q,\ q^0,\ q^{\mathrm{sp}}\) | q | 关节位置、关节速度、默认姿态、关节设定点。 |
| 世界模型 | ||
| \(f_\omega\) | f omega | 动力学网络,\(\hat s_{t+1}=f_\omega(\hat s_t,a_t,\xi)\)。参数 \(\omega\)。 |
| \(r_\omega,\ g_\omega,\ d_\omega\) | 奖励头、观测头、终止头,输出 \(\hat r,\hat{\bar o},\hat d\)。可以是网络也可以是解析公式。 | |
| \(k=8\) | k | 训练世界模型时自回归窗口的长度。 |
| \(\mathcal L_{\mathrm{WM}}\) | L WM | 世界模型损失,公式 (1)。 |
| \(\mathrm{BCE}(\hat d,d)\) | 二元交叉熵 \(-[d\log\hat d+(1-d)\log(1-\hat d)]\),训练终止概率。 | |
| \(\|x\|^2\) | 向量 \(x\) 各分量平方和,即欧氏距离的平方。 | |
| 奖励、回报、终止 | ||
| \(r_{t+1}\) / \(\hat r_{t+1}\) | r | 执行 \(a_t\) 后得到的奖励(真实 / 预测)。下标 \(t+1\) 表示它在 \(s_{t+1}\) 时结算。 |
| \(d_t\) / \(\hat d_t\) | d | 终止标志。真实值是 0 或 1,预测值是概率。 |
| \(c_t\) / \(\hat c_t\) | c | 存活权重,\(c_0=1,\ c_{t+1}=c_t(1-d_{t+1})\)。 |
| \(\gamma=0.99\) | gamma | 折扣因子。 |
| \(H=32\) | H | 想象视野,每次想象 rollout 的步数。 |
| \(J(\theta)\) / \(\hat J(\theta)\) | J | 策略目标:真实的期望折扣回报 / 在世界模型里算出的 32 步代理目标,公式 (2)。 |
| \(\mathbb E[\cdot]\) | 期望 | 对随机量取平均。实现里就是对一个 batch 求均值。下标写明对什么取平均,如 \(\mathbb E_{\mathcal B}\) 是对缓冲区采样。 |
| \(\rho_0\) | rho 0 | 想象起点的分布,即从缓冲区采样起点的方式。 |
| critic | ||
| \(V_\psi(\hat s_t,g_t,\xi)\) | V psi | 价值网络,估计从该状态起的剩余折扣回报。参数 \(\psi\)。 |
| \(V_t\) | \(V_\psi(\hat s_t,g_t,\xi)\) 的简写。 | |
| \(\hat R_t\) | R hat | GAE 回报目标,critic 的回归标签,公式 (3)。 |
| \(\lambda=0.95\) | lambda | GAE 的混合系数。 |
| \(\mathcal L_V\) | L V | critic 的均方误差损失。 |
| \(\hat A\) | A hat | 优势估计,PPO 公式里用,表示某动作比平均好多少。 |
| 奖励公式里的量 | ||
| \(\mathcal T\) | T | 被跟踪刚体的集合,\(|\mathcal T|\) 是其个数。 |
| \(\mathbf p_b,\ R_b,\ \mathbf v_b,\ \boldsymbol\omega_b\) | 刚体 \(b\) 的世界系位置、旋转矩阵、线速度、角速度。上标 des 是参考动作里的目标值。 | |
| \(\log(R^{\mathrm{des}}R^{\top})\) | 两个旋转之间的相对旋转取对数映射,得到一个 3 维轴角向量,其范数就是旋转角误差。 | |
| \(\exp(-e^2/\sigma^2)\) | 把误差 \(e\) 变成 0 到 1 的分数,\(\sigma\)(如 0.3、0.4、1.0)控制多大误差算"差"。 | |
| \(e_{p,z},\ e_{\mathrm{tilt}}\) | 终止用的根部高度误差和倾斜误差。 | |
| \(\Delta t=0.02\,\mathrm s\) | 控制周期(50 Hz),每一项奖励都乘它。 | |
| 优化 | ||
| \(\theta,\ \omega,\ \psi\) | theta, omega, psi | 策略、世界模型、critic 三组网络参数。 |
| \(\alpha_\theta,\ \alpha_\omega\) | alpha | 对应的学习率。 |
| \(\nabla_\theta\) | nabla | 对 \(\theta\) 求梯度。 |
| \(\partial\hat s_{t+1}/\partial\hat s_t\) | 世界模型的雅可比矩阵,BPTT 每一步要乘它。 | |
以下内容整理自论文第 3 节和附录 A 到 G。标注 训练 的是该阶段有梯度更新的参数,冻结 的是该阶段只做前向、不更新的参数。凡论文没有明说、属于我的推断的地方,都放在橙色提示框里并注明。
整个系统只有一个外层循环。每一次迭代依次做三件事,三件事共享同一个回放缓冲区 B。没有"先预训练世界模型再训练策略"的分期,世界模型 ω 和策略 θ 每次迭代都更新;ω 只在策略的反向传播里被停梯度(Algorithm A.1 的说明原文)。
| 阶段 | 做什么 | 训练 | 冻结 / 只前向 | 数据来自 | 损失 |
|---|---|---|---|---|---|
| 1 采数据 | 策略在 MuJoCo Warp 里跑,转移写入 B | 无 | \(\pi_\theta\)、仿真器 | 4096 个并行环境,起点按 return-based reset 采样 | 无 |
| 2 拟合世界模型 | 从 B 抽 8 步窗口,自回归预测并回归仿真结果 | \(f_\omega\)(以及若有学习头:\(r_\omega\)、\(g_\omega\)、\(d_\omega\)) | 无(不涉及策略) | B 中任意时期的转移,含旧策略数据 | \(\mathcal L_{\mathrm{WM}}\),公式 (1) |
| 3a 改进策略 | 从 B 的真实状态出发在 \(f_\omega\) 里想象 32 步,BPTT | \(\pi_\theta\)(含目标编码器与共享解码器) | \(f_\omega\)(停梯度)、\(V_\psi\)(作为终端值,是否停梯度见第 23 节) | B 中的起点状态与其记录的 ξ | −\(\hat J(\theta)\),公式 (2) |
| 3b 拟合 critic | 对同一批想象轨迹算 GAE 目标,回归 | \(V_\psi\) | \(f_\omega\)、\(\pi_\theta\)(轨迹已生成) | 阶段 3a 刚生成的想象轨迹 | \(\mathcal L_V\),公式 (3) |
flowchart LR
subgraph S1["阶段 1 · 采数据"]
SIM["MuJoCo Warp
4096 envs"] -->|"(s_t, a_t+ε_t, ξ, r, d, o)"| B[("回放缓冲区 B
1000 步/env")]
PI0["π_θ 前向"] --> SIM
end
subgraph S2["阶段 2 · 拟合世界模型"]
B -->|"8 步窗口 ×2048 ×10 批"| FW["f_ω 自回归 8 步"]
FW --> LWM["L_WM = Σ ‖ŝ−s‖² + (r̂−r)² + ‖ô−ō‖² + BCE(d̂,d)"]
LWM -.->|"∇_ω"| FW
end
subgraph S3["阶段 3 · 改进策略与 critic"]
B -->|"起点 s_0, ξ ×4096"| IM["想象 32 步
f_ω 冻结, π_θ 出动作"]
IM --> J["Ĵ = Σ γ^t ĉ_t r̂_t+1 + γ^H ĉ_H V_ψ"]
J -.->|"∇_θ (BPTT 穿过 f_ω)"| IM
IM --> GAE["GAE 目标 R̂_t"]
GAE --> LV["L_V = Σ (V_t − R̂_t)²"]
LV -.->|"∇_ψ"| GAE
end
实线是数据流,虚线是梯度流。下面逐阶段展开。
回合重置到动作库里的某个时间点,倾向选策略当前回报低的地方。做法:
注意这影响两处:仿真回合的起点,以及(通过 B)想象 rollout 的起点分布 \(\rho_0\)。Figure 4 的"训练回报"因此和 checkpoint 评估分数不一致,论文自己也指出了。
为什么用 8 步自回归而不是单步:模型学会在自己的预测误差上继续预测,因此在 32 步想象乃至更长的滚动里保持稳定(§3.3 原文)。
和 PPO 的根本差别:PPO 需要随机策略并用 \(\nabla_\theta\log\pi_\theta(a\mid o)\cdot\hat A\) 做似然比估计;这里策略是确定性的,梯度是 \(\frac{\partial\hat J}{\partial a_t}\cdot\frac{\partial a_t}{\partial\theta}\) 沿链式法则一路回传。
critic 为什么必要:没有它只优化 32 步奖励和时,训练在 H ≤ 16 失败,并且策略对推力的恢复行为很差,因为恢复奖励在窗口之外(§4.3)。
策略接收最近 10 帧观测。每帧内容和逐维传感器噪声 \(\eta_t\) 如下,η 服从均匀分布:
| 项 | 维度 | 噪声 \(\eta_t\) | 单位 |
|---|---|---|---|
| 关节位置 \(q-q^0\)(相对默认姿态) | 29 | U[−0.01, 0.01] | rad |
| 关节速度 \(\dot q\) | 29 | U[−0.5, 0.5] | rad/s |
| 基座角速度(IMU) | 3 | U[−0.2, 0.2] | rad/s |
| 投影重力(基座系) | 3 | U[−0.05, 0.05] | – |
| 上一步动作 \(a_{t-1}\) | 29 | – | – |
合计每帧 93 维,10 帧 930 维。观测里没有根部位置和线速度,是纯本体感知。
动作跟踪接口的目标是参考动作的未来 K = 10 帧序列(BeyondMimic 用单帧目标,这里改成序列)。编码器输入就是这 10 帧。
29 维,映射到关节设定点 \(q^{\mathrm{sp}}=\alpha\,a_t+q^0\)。沿用 BeyondMimic 的低 PD 增益,设定点实际是关节力矩的代理而不是真要到达的位置。α 的数值论文未给。
动作跟踪奖励沿用 BeyondMimic(Table A.2),改动两处:去掉自碰撞惩罚(它防的是随机策略的奖励黑客,这里策略在想象中是确定性的,没必要);关节限位惩罚权重从 −10 降到 −2。每一项都乘以控制周期 Δt = 0.02 s。T 是被跟踪刚体的集合。
| 项 | 公式 | 权重 |
|---|---|---|
| 跟踪项 | ||
| 刚体位置 | \(\exp\!\Big(-\tfrac{1}{|\mathcal T|}\sum_{b\in\mathcal T}\|\mathbf p_b^{\mathrm{des}}-\mathbf p_b\|^2\big/0.3^2\Big)\) | 1.0 |
| 刚体姿态 | \(\exp\!\Big(-\tfrac{1}{|\mathcal T|}\sum_{b\in\mathcal T}\|\log(R_b^{\mathrm{des}}R_b^{\top})\|^2\big/0.4^2\Big)\) | 1.0 |
| 刚体线速度 | \(\exp\!\Big(-\tfrac{1}{|\mathcal T|}\sum_{b\in\mathcal T}\|\mathbf v_b^{\mathrm{des}}-\mathbf v_b\|^2\big/1.0^2\Big)\) | 1.0 |
| 刚体角速度 | \(\exp\!\Big(-\tfrac{1}{|\mathcal T|}\sum_{b\in\mathcal T}\|\boldsymbol\omega_b^{\mathrm{des}}-\boldsymbol\omega_b\|^2\big/3.14^2\Big)\) | 1.0 |
| 根部位置 | \(\exp\!\big(-\|\mathbf p_{\mathrm{root}}^{\mathrm{des}}-\mathbf p_{\mathrm{root}}\|^2/0.3^2\big)\) | 0.5 |
| 根部姿态 | \(\exp\!\big(-\|\log(R_{\mathrm{root}}^{\mathrm{des}}R_{\mathrm{root}}^{\top})\|^2/0.4^2\big)\) | 0.5 |
| 正则项 | ||
| 动作平滑 | \(\|\mathbf a_t-\mathbf a_{t-1}\|^2\) | −0.1 |
| 关节限位 | \(\sum_j\big[\max(0,\,q_j-q_j^{\max})+\max(0,\,q_j^{\min}-q_j)\big]\) | −2 |
归一化回报 = 整回合每步奖励之和 ÷ 该和的最大可能值。跟踪项全是 exp 形式,上界是 1,所以最大值可以直接算。
终止:\(|e_{p,z}|>0.25\,\mathrm m\) 或 \(e_{\mathrm{tilt}}>0.8\)(见第 12 节)。仿真记录硬终止 0/1,世界模型预测概率 \(\hat d\)。
| 参数 | 采样分布(每回合) |
|---|---|
| 摩擦系数 μ | U[0.3, 1.2] |
| 关节标定偏移 Δ\(q_j\)⁰ | U[−0.01, 0.01] rad,逐关节 |
| 躯干质心偏移 | Δx ~ U[−0.025, 0.025] m,Δy ~ U[−0.05, 0.05] m,Δz ~ U[−0.05, 0.05] m |
这三组量拼成 ξ,作为 \(f_\omega\)、\(g_\omega\) 和 \(V_\psi\) 的输入。去掉了 BeyondMimic 的根部速度扰动(随机推),因为推力是状态的不连续跳变,确定性世界模型无法表示;作者发现只随机化物理参数就够上真机。
| 网络 | 输入 | 输出 | 结构(隐层宽度) |
|---|---|---|---|
| 动力学 \(f_\omega\) | \(\hat s\)_t, \(a_t\), ξ | \(\hat s_{t+1}\) | MLP (2048, 2048, 2048) |
| 动作编码器 \(\mathrm{enc}_{\theta,\mathrm{motion}}\) | K = 10 帧未来参考 | z | MLP (2048, 1024, 512, 256) |
| 速度编码器 \(\mathrm{enc}_{\theta,\mathrm{vel}}\)(附录 G) | 归一化指令 (\(v_x\), \(v_y\), \(\omega_z\)) + 最近 10 帧 o 的特征 | z | MLP (256, 256) |
| 共享解码器 \(\mathrm{dec}_\theta\) | z, 最近 10 帧 o | \(a_t\) | MLP (2048, 1024, 512, 256) |
| critic \(V_\psi\) | \(\hat s\)_t, \(g_t\), ξ | 标量 | MLP (256, 128),每个接口一个 critic |
潜变量 z 为 32 维,经 tanh 限幅。策略 = 编码器 + 解码器:\(z=\mathrm{enc}_{\theta,i}(g)\),\(a=\mathrm{dec}_\theta(z,o)\)。激活函数、层归一化、初始化论文均未给出。
| 组 | 超参 | 值 |
|---|---|---|
| 世界模型 | 窗口长度 k | 8 |
| 优化器 | Adam | |
| 学习率 \(\alpha_\omega\) | 单卡 2×10⁻⁴;双卡 √2 × 2×10⁻⁴ | |
| 批量 | 每次更新 10 批,每批 2048 个窗口 | |
| 梯度裁剪 | 全局范数 1.0 | |
| 想象与策略 | 想象视野 H | 32 |
| 优化器 | Adam,β = (0.9, 0.95) | |
| 学习率 \(\alpha_\theta\) | 同 \(\alpha_\omega\) | |
| 批量 | 每次更新 4096 条想象 rollout | |
| 梯度裁剪 | 全局范数 1.0,然后逐元素 0.1 | |
| 折扣 γ | 0.99 | |
| 传感器噪声 η | Table A.1(第 15 节) | |
| critic | GAE λ | 0.95 |
| 优化器 | Adam | |
| 学习率 | 同 \(\alpha_\omega\) | |
| 梯度裁剪 | 全局范数 1.0 | |
| 每次更新的 epoch | 5 个 epoch,每个 4 个打乱的 minibatch | |
| 数据采集 | 每 GPU 仿真环境数 | 4096 |
| 动作噪声 ε | N(0, 0.1²) 逐维 | |
| 回放缓冲区 | 每环境 1000 步 | |
| 调度 | 更新数据比(UTD) | 0.4 |
| 每次世界模型更新对应的策略更新数 | 1 | |
| 学习率调度 | 余弦衰减到 0.03 倍,历时 250,000 次迭代,之后恒定 | |
| 训练长度 | 320,000 次迭代 | |
| 多卡 | 数据并行,跨卡平均梯度 |
PPO-Sim 基线用 BeyondMimic 的 PPO 超参,每 GPU 8192 个环境,是 SuperMimic 的两倍。PPO-WM 消融(Figure 5b)在世界模型上跑 PPO,四种批量里 16k × 32 最好。
训练集是 BONES-SEED 的 G1 重定向公开子集(HuggingFace bones-studio/seed),是 SONIC 数据的公开部分。原始 142,220 条,两道过滤后 129,514 条,合计 264.56 小时。
| 阶段 | 条数 |
|---|---|
| 原始 BONES-SEED(G1 重定向) | 142,220 |
| 名称过滤后 | 129,667 |
| 可行性过滤后 | 129,514 |
Figure 4 的受控实验用 LAFAN1 的 40 条片段(2.45 小时);Figure 5b 用其中走、跑、冲刺;数据量扫描用 1、7、40、264 小时的子集。
| 速度跟踪奖励项(Table A.6) | 公式 | 权重 |
|---|---|---|
| 线速度 | \(\exp\!\big(-\|\mathbf v_{xy}-\mathbf v_{xy}^{\mathrm{cmd}}\|^2/0.5^2\big)\) | 2.0 |
| 偏航角速度 | \(\exp\!\big(-(\omega_z-\omega_z^{\mathrm{cmd}})^2/0.5\big)\) | 2.0 |
| 动作平滑 | \(\|\mathbf a_t-\mathbf a_{t-1}\|^2\) | −0.3 |
| 关节限位 | 同 Table A.2 | −2 |
| 躯干侧倾 | \(\sin^2\phi_{\mathrm{torso}}\)(前倾不罚) | −4 |
| 躯干滚转、俯仰角速度 | \(\omega_{\mathrm{roll}}^2+\omega_{\mathrm{pitch}}^2\) | −0.05 |
阶跃测试:从站立接收固定指令 5 s,每个指令 4 次试验,稳态误差取最后 2 s 平均速度误差的范数除以指令范数,取未摔倒试验的中位数。三个平面各 15 × 15 网格,去掉站立指令共 672 个。RL 编码器中位相对误差 0.16,蒸馏 0.49。
下面是按你"让 agent 照规格复现效果不好"的问题,我认为最可能出偏差的点,按重要性排序。
这一页只讲原理,有三个目标:把 PPO 从策略梯度定理一路讲到裁剪目标;讲清楚基于模型的强化学习(MBRL)是什么、有哪几种用法、难在哪里;最后看 SuperMimic 怎样把 MBRL 用在人形动作跟踪上,以及它为什么比 PPO 快。超参数只在影响性能的地方点一句。
全文用同一个任务、同一个瞬间做例子。每个公式都有编号,下面紧跟一栏"其中",把这个公式里新出现的符号逐个讲清楚。页尾有本页全部符号的索引。
任务取论文 Figure 4(a) 的受控实验。Unitree G1 人形机器人有 29 个关节,要跟踪 LAFAN1 数据集里的 40 条走、跑片段,控制频率 50 Hz。PPO 和 SuperMimic 用完全相同的观测、目标、动作、奖励、终止条件和网络大小,唯一的区别是怎样算出"策略参数该往哪里改"。结果是 SuperMimic 单卡 1.2 小时达到归一化回报 0.82;PPO 单卡 18 小时只到 0.60,八卡 18 小时也只到 0.72。
某条片段的第 137 步,参考动作正处于跑步的摆动相:右脚离地,膝盖前摆。此刻机器人的右膝比参考低了 2 cm,其余部位基本对上。如果右膝追不上,右脚会提前触地,步幅变短,接下来几十步的跟踪奖励都会下降。
策略要学的东西很具体:在这种状态下,29 维动作里右膝那一维应该再大一点,右髋可能也要配合。后面每一节都会回到这个问题:PPO 怎样发现这件事,SuperMimic 又怎样发现这件事。
训练就是不停地回答一个问题:"演员的每个关节,现在应该多动一点还是少动一点?"每次都能答对,舞就会越跳越好。难就难在动作和分数之间隔着物理:动作改了,身体会怎么变,要靠物理才知道。PPO 和 SuperMimic 的区别,就在于回答这个问题的办法不同:PPO 靠"随便试试,看分数",SuperMimic 靠"让预言家直接算"。
用梯度上升优化 \(J\),需要知道梯度 \(\nabla_\theta J\),也就是每个参数往哪个方向动一点,\(J\) 会变大。难点在于 \(J\) 是隔着物理依赖 \(\theta\) 的:参数决定动作,动作经过物理决定下一个状态,状态又决定奖励和下一个动作。下图画出了这条链,以及两种方法在链上的根本区别。
先看确定性策略的情形。Silver 等(2014)的确定性策略梯度定理把梯度写成:
严格地说,外面还有一个状态分布的折扣权重。这里省略,不影响理解。
(G2) 把梯度拆成两半。后一半 \(\partial\pi_\theta/\partial\theta\) 只涉及神经网络,是容易的。前一半 \(\nabla_aQ\) 回答"在这个状态下,动作该往哪边改",它牵涉物理,是难的。回到例子,第 137 步的 \(\nabla_aQ\) 应该在右膝那一维为正。各种强化学习方法的根本区别,就在于怎样得到 \(\nabla_aQ\):
| 方法 | 怎样得到 \(\nabla_aQ\) | 性质 |
|---|---|---|
| PPO 等策略梯度方法 | 随机扰动动作,看回报变好还是变坏,做统计相关(第 4 节) | 零阶估计:只用回报的数值,不用导数 |
| DDPG、TD3、SAC 等 | 学一个 Q 网络,对它求导 | 一阶,但 Q 网络对动作的导数常常不准 |
| SuperMimic,以及 SVG、SHAC | 学一个可微的动力学模型,往前展开若干步再接价值函数,链式求导(第 12 节) | 一阶,称为路径导数 |
本页其余部分就是把第一行和第三行讲透。
PPO(Schulman 等 2017)是目前人形动作跟踪的主流算法,BeyondMimic 和 SONIC 都用它。它建立在一个很巧妙的数学恒等式上:不需要知道物理,也能得到无偏的策略梯度。代价是方差。
PPO 不懂物理,那它怎么知道该往哪边改?它的办法像小朋友学投篮:每次都随手换一点点姿势去投。投进了,就记住"刚才那个姿势要多用";没投进,就"少用"。这一节的数学只证明了一件事:这样做,平均下来方向是对的,而且完全不需要知道物理是怎么回事。
直觉。不知道物理,就让策略自己"掷骰子"。策略不输出一个确定的动作,而是输出一个概率分布,按分布采样执行。哪次碰巧回报高,就提高那次所选动作的概率;回报低,就降低。整个过程只需要知道"某个动作的概率对参数的导数",这完全在我们自己的网络里,和物理无关。
随机策略。连续动作下,PPO 用高斯分布:
一条轨迹出现的概率。把一个回合记成轨迹 \(\tau=(s_0,a_0,s_1,a_1,\dots)\)。它出现的概率是一连串因子的乘积:
一条轨迹是一连串依次发生的事:先抽到起点,再选动作,再由物理给出下一个状态,再选动作……一连串事情同时发生的概率,等于每一件事在前面已经发生的条件下的概率连乘。这和"连掷两次骰子都是 6 的概率是 \(\tfrac16\times\tfrac16\)"是同一个道理。
对数导数技巧。记整条轨迹的回报为 \(R(\tau)=\sum_t\gamma^tr_{t+1}\),于是 \(J(\theta)=\mathbb E_{\tau\sim p_\theta}[R(\tau)]\)。利用恒等式 \(\nabla p=p\,\nabla\log p\):
第 1 步:期望就是加权平均。\(J\) 是"每条轨迹的回报,按它出现的概率加权平均"。积分可以理解成"对所有可能的轨迹求和":
\(\theta\) 只出现在概率 \(p_\theta\) 里。一条轨迹一旦确定,它的回报 \(R(\tau)\) 就是一串固定的数,和 \(\theta\) 无关。
第 2 步:把求导挪进积分。对一个和求导,等于对每一项求导再相加,所以求导可以挪进积分里。\(R\) 和 \(\theta\) 无关,当作常数留在原处:
第 3 步:对数导数恒等式。\(\log x\) 的导数是 \(1/x\),配合链式法则,\(\log p\) 的导数是 \(\nabla p/p\)。两边乘以 \(p\):
第 4 步:变回期望。代进 (P3.2),积分里又出现了"\(p_\theta\) 乘以某个量",按 (P3.1) 的定义,这就是那个量的期望:
为什么要绕这一圈。(P3.2) 没法用采样来算:\(\nabla p\) 不是概率,不能按它抽样本。(P3.4) 的形式是"按 \(p_\theta\) 抽轨迹,再把方括号里的量取平均",在仿真器里跑轨迹就能估计。
把 (P2) 取对数,乘积变成求和。只有策略那一项含 \(\theta\),起始分布和物理那两项求导后都是零:
环境对策略梯度的贡献是零。它从不告诉策略"动作改一点,结果会怎么变",(P4) 里物理那一项求导为零说的就是这件事。
环境唯一提供的是奖励,而且只是一个数值。梯度全部来自策略自己:策略做出某个动作,拿到奖励,这个奖励作为权重反馈给 \(\nabla_\theta\log\pi_\theta(a_t\mid s_t)\),也就是"让这个动作更可能"的方向。奖励越高,往这个动作推得越多;减去基线以后(第 3 节),比平均差的动作会被往反方向推。
这一步是无模型强化学习的精髓:物理 \(P\) 在求导中消失了。我们从来不需要知道"动作改一点,下一个状态怎么变"。代价是,关于物理的全部信息只能从"哪些动作碰巧带来了高回报"中统计出来。
代回 (P3),就得到策略梯度定理,也叫 REINFORCE(Williams 1992):
读法:每一步都往"让刚才那个动作更可能"的方向推,推多少由整条轨迹有多好决定。
下面是 (P5) 最核心的 PyTorch 写法,省略了批处理细节和优势估计。真实的 PPO 代码在此基础上把回报 \(R\) 换成优势 \(\hat A\)(第 3 节),再加上概率比和裁剪(第 5 节)。
# ---- 采样:只要动作和奖励的数值,不需要梯度 ----
with torch.no_grad():
mu = policy(obs) # μ_θ(s)
a = mu + sigma * torch.randn_like(mu) # a = μ + σε,即 (P1)
obs_next, r, done = env.step(a) # 仿真器只返回数值,没有计算图
buffer.add(obs, a, r)
# ---- 更新:对一整批数据 ----
obs_b, a_b, R_b = buffer.get() # R_b:每一步对应的回报,只是数
mu = policy(obs_b) # 重新前向,这次建立计算图
dist = torch.distributions.Normal(mu, sigma)
logp = dist.log_prob(a_b).sum(-1) # log π_θ(a_t | s_t),29 维相加
loss = -(logp * R_b).mean() # (P5) 取负号:最大化变成最小化
loss.backward() # 梯度只经过 logp,等于 R · ∇θ log π
optimizer.step()
no_grad 里,动作 a 和环境返回的 r 都只是普通的数,和 \(\theta\) 之间没有任何可求导的连接。这就是"环境那一项为零"在代码里的样子:从回报出发做反向传播,根本找不到回到 \(\theta\) 的路。logp 是一个随 \(\theta\) 变化的数。dist.log_prob(a_b) 算的是"按当前网络,选到这个已经执行过的动作的对数概率"。动作 a_b 是固定的数据,均值 mu 由网络算出,所以这个值随 \(\theta\) 变化,可以求导。R_b 是常数,backward 只对 logp 求导,结果正好是 \(R\cdot\nabla_\theta\log\pi_\theta\)。取负号是因为优化器做的是最小化。对高斯策略,log_prob 有显式公式,可以看清"提高 log π"具体在做什么:
.sum(-1)。读法:执行过的动作离均值越近,\(\log\pi\) 越大。所以在固定 \(\sigma\) 时,最大化 \(R\cdot\log\pi\) 就是把均值 \(\mu\) 往实际执行过的动作 \(a\) 那边挪,挪多少由回报 \(R\) 决定。这相当于一次加权回归:标签是执行过的动作,权重是回报。对 \(\mu\) 求导得到 \((a_i-\mu_i)/\sigma^2\),正是第 4 节 (P9) 里的那一项。
上一节的办法有个大毛病:太吵。一支舞跳了 300 步,最后得了一个总分,可这个总分是 300 个动作一起挣来的,凭什么全算到第 137 步的右膝头上?这一节做三件事来"去掉不相干的功劳":动作发生之前的分数不算给它;只看"这次比平时好多少",不看总分多少;只看接下来十几步,更远的交给裁判估。去掉的越多,判断越准,需要试的次数就越少。
方差大是什么意思。(P5) 是一个期望,实际只能拿有限条轨迹的平均去估计。换一批轨迹,估出来的梯度差别很大,就叫方差大。方差越大,需要的样本越多,训练越慢。
乘在第 \(t\) 步 score 前面的权重,本来只该回答一个问题:这个动作让结果比预期好了多少。但整条轨迹的回报 \(R(\tau)\) 里,除了这个答案,还混着三样和这个动作无关的东西。为了看清结构,这里先不打折扣,把它拆开:
| 项 | 和这个动作有关吗 | 例子中的量级 | 谁来处理,怎么处理 |
|---|---|---|---|
| ① 之前的奖励 | 无关:动作发生前就定了 | 约 110 | 第一步。已知,直接删掉 |
| ② 平时水平 \(V(s_t)\) | 无关:选什么动作都有这一份 | 约 130 | 第二步。不知道,训练 critic 估计出来再减掉 |
| ③ 动作本身的贡献 \(A\) | 唯一有关的 | 约 0.3 | 保留,这就是信号 |
| ④ 之后的运气 | 无关:来自之后的随机性 | 约 ±1 | 第三步。无法精确减掉,把远处的真实奖励换成 critic 给的平均值,大部分就被抹掉了 |
为什么去掉 ①②④ 不改变梯度的期望。这三项都不是 \(a_t\) 造成的。它们乘上 score 以后,按 (P6) 的道理平均为零:它们从来没有为梯度提供过方向,只是让每个样本推得更猛、更随机。例子里,真正有用的 ③ 只有 0.3 左右,却和两百多的无关部分加在一起,只能靠海量样本去平均。
三步就是依次把它们拿掉:
最后剩下的主要就是 ③,也就是 (P7) 里的优势 \(A(s_t,a_t)\)。
贯穿这一节的数字。一个回合 300 步,每步奖励约 0.8,所以整条轨迹的回报 \(R(\tau)\) 约 240。我们关心第 137 步的右膝动作。按 (★) 拆开,① 约 110,② 约 130,③ 只有约 0.3,④ 约 ±1。下图画出每一步之后权重还用到了哪些东西。
删的是哪一段。把整条轨迹的回报 \(R(\tau)\) 在第 \(t\) 步处切成两段:
R = sum(r[:t]) + sum(r[t:]),第一步就是只保留 sum(r[t:])。为什么要删。拿第 137 步的右膝动作来说。前 137 步的奖励在它之前就已经拿到了,右膝这一次怎么动都改变不了它们。可是按 (P5),它们照样乘在右膝动作的 score 上:前面碰巧跟踪得好,这次右膝动作就被多推一把;碰巧跟踪得差,就被少推一把。这一推一拉和右膝动作的好坏毫无关系,纯粹是噪声。
为什么删了不影响结果。对第 137 步来说,前面的奖励已经是一个定数,记作 \(c\),例子里约 110。右膝动作是从高斯分布里随机抽出来的,有时比均值大,有时比均值小。\(c\) 乘上 score 这一项的效果是:
两种情况出现的机会一样多,平均下来正好抵消,所以这一项对梯度的平均贡献是零。第二步的 (P6) 会给出一般的证明。但在有限的一批样本里,大和小不会恰好一样多,抵消不干净,剩下的就是噪声,而且 \(c\) 越大噪声越大。所以把它删掉:平均的梯度不变,噪声变小。
删完得到什么。每一步的权重只剩"从这一步起的回报":
例子。第 137 步的权重从约 240 降到约 130,与右膝无关的约 110 被去掉了。
问题。\(G_t\) 仍然很大,而且永远是正的。假设环境 A 的 \(G_{137}=131\),环境 B 的是 129。两次的右膝动作都会被往"更可能"的方向推,只是 A 推得稍多。真正有用的信息只在 131 和 129 的差里,推的幅度却是 130 的量级。score 的方向是随机的,这么大的权重乘上随机的方向,在有限样本里很难互相抵消。
做法。从 \(G_t\) 里减去一个只依赖状态、不依赖动作的量 \(b(s_t)\),叫基线。
为什么期望不变。多减掉的那部分是 \(\nabla_\theta\log\pi_\theta(a_t\mid s_t)\cdot b(s_t)\)。\(b(s_t)\) 不依赖动作,可以提到"对动作求平均"的外面,剩下的就是 score 对动作的平均。它恒为零:
直观地说,所有动作的概率加起来永远是 1。提高一些动作的概率,就必须降低另一些。所以"让某个动作更可能"的方向,按策略自己的分布平均下来,正好互相抵消。第一步删掉"之前的奖励",用的也是这个道理。于是:
选什么做基线。最自然的选择是"在这个状态下,按当前策略平均能拿到的回报",也就是状态价值 \(V(s_t)\)。这时权重 \(G_t-V(s_t)\) 的含义变成"这次比平时好多少"。在给定 \(s_t\) 和 \(a_t\) 的条件下,\(G_t\) 的平均就是 \(Q(s_t,a_t)\),所以 \(G_t-V(s_t)\) 是下面这个优势函数的单样本估计:
第 1 步:分两层求平均。(P6a) 里的期望是对整条轨迹的随机性求平均。可以先固定第 \(t\) 步的状态和动作,只对之后发生的事求平均;再对 \(s_t,a_t\) 求平均。结果一样。这就像先算每个班的平均分、再按人数加权,和直接算全校平均分是一回事。
第 2 步:里层的平均。固定 \(s_t,a_t\) 以后,score 和 \(V(s_t)\) 都是定值,只有 \(G_t\) 还随之后的随机性变化。\(G_t\) 的平均按定义就是 \(Q\):
第 3 步:代回去。
所以用 \(G_t-V(s_t)\) 和用 \(A\) 算出的梯度,平均值相同。\(G_t-V(s_t)\) 是 \(A\) 的一个单样本估计:平均是对的,但每一次都带着运气 ④。
例子。设 \(V(s_{137})=130\)。环境 A 的权重从 131 变成 +1,环境 B 从 129 变成 −1。A 的右膝动作被往"更可能"推,B 的被往"更不可能"推,两者一致地告诉策略"右膝该加大"。权重的量级从 130 降到了 1。
\(V\) 接近最优基线:\(G_t\) 的平均就是 \(V\),减去平均能让权重的平方平均最小。严格的最优基线还要按 score 的大小加权,实际差别不大。
问题一:\(V\) 不知道。于是训练一个网络 \(V_\phi\) 去近似它,方法是普通的回归:
固定一个状态,看这个状态下拿到的很多个回报 \(G\)。要找一个数 \(v\),让平方误差的平均最小。把平方展开:
这是关于 \(v\) 的一条开口向上的抛物线,最低点在导数为零处:
critic 网络对每个状态都在做这件事,只是用梯度下降一点点逼近这个最低点。
问题二:\(G_t-V(s_t)\) 还是很吵。\(G_{137}\) 包含之后 163 步的奖励,这 163 步里策略每一步都在加随机噪声,环境 A 和 B 之后的经历完全不同。第 137 步右膝那一点影响,被埋在 163 步的随机性里。办法是:只用接下来几步的真实奖励,更远的部分换成 critic 的估计。
为什么要引入 TD 误差。我们真正要的是优势 \(A(s_t,a_t)\),用它给每个动作打分。第二步的 \(G_t-V(s_t)\) 能估计 \(A\),但它把之后一百多步的运气都带了进来,太吵。TD 误差是估计 \(A\) 的另一种办法:只用一步真实奖励,其余部分交给 critic,所以噪声小得多,而它的平均正好等于优势,推导见下面的框。后面的 \(n\) 步估计和 GAE,都是用 TD 误差拼出来的。
只看一步:TD 误差。用一步真实奖励,加上 critic 对下一个状态的估值,再减去对当前状态的估值:
\(Q\) 是"做完这个动作后平均能拿到的回报",可以拆成两段:这一步的奖励,加上从下一个状态起平均能拿到的回报。后一段正是 \(V(s_{t+1})\):
这叫贝尔曼方程。两边同时减去 \(V(s_t)\),左边是优势,右边是 \(\delta_t\) 的平均:
前提是 \(V\) 准确。critic 不准时这个等式只近似成立,这就是偏差的来源。
例子。右膝动作让膝盖更接近参考,所以 \(r_{138}\) 略高;critic 看到新状态里膝盖位置更好,\(V_\phi(s_{138})\) 也略高。于是 \(\delta_{137}\) 为正,而且完全不受之后 163 步随机动作的影响。
看 n 步。也可以用 \(n\) 步真实奖励再接 critic。展开以后,中间的 \(V\) 两两抵消,正好等于 \(n\) 个 TD 误差之和:
这里把 \(V_\phi\) 简写成 \(V\)。把两个 TD 误差按定义写开:
第一个括号里的 \(+\gamma V(s_{t+1})\) 和第二个括号乘开后的 \(-\gamma V(s_{t+1})\) 正好抵消:
这正是 \(n=2\) 时 (P7c) 的左边:两步真实奖励,接 critic,减去起点的估值。\(n\) 更大时,每一对相邻的 \(\delta\) 都这样抵消,只剩头和尾,这种求和叫"裂项相消"。
\(n\) 越小,用到的随机奖励越少,方差越小,但越依赖 critic,偏差越大;\(n\) 越大则反过来。\(n\) 一直取到回合结束,就退回第二步的 \(G_t-V(s_t)\)。
GAE:把所有的 n 一起用上。与其挑一个 \(n\),不如把 \(n=1,2,3,\dots\) 的估计按 \((1-\lambda)\lambda^{n-1}\) 加权平均。整理之后形式很简洁,这就是 GAE(广义优势估计,Schulman 等 2016):
要算的是:把所有 \(n\) 步估计按权重加起来以后,每个 \(\delta_{t+l}\) 前面的总系数是多少。会用到等比数列求和,\(\lambda\) 在 0 和 1 之间时:
第 1 步:看 \(\delta_t\)。按 (P7c),每一个 \(n\) 步估计都含 \(\delta_t\),系数是 1。所以它的总系数就是全部权重之和:
第 2 步:看 \(\delta_{t+1}\)。只有 \(n\ge2\) 的估计含它,系数都是 \(\gamma\):
第 3 步:一般的 \(\delta_{t+l}\)。只有 \(n\ge l+1\) 的估计含它,系数都是 \(\gamma^l\):
把所有 \(\delta\) 按这个系数加起来,就是 (P8) 右边的 \(\sum_l(\gamma\lambda)^l\delta_{t+l}\)。
读法。从第 \(t\) 步往后,每一步的 TD 误差都算进来,但每远一步,权重就乘一次 \(\gamma\lambda\)。\(\gamma=0.99\)、\(\lambda=0.95\) 时 \(\gamma\lambda\approx0.94\),权重衰减到一半大约要 11 步,有效长度约 \(1/(1-\gamma\lambda)\approx17\) 步,也就是约 0.34 秒。所以 GAE 主要看接下来十几步的真实奖励,更远的部分交给 critic。
| \(\lambda\) | 实际用到什么 | 方差 | 偏差 |
|---|---|---|---|
| 0 | 一步真实奖励加 critic,即 \(\delta_t\) | 最小 | critic 不准时最大 |
| 0.95(常用) | 以接下来约 17 步的真实奖励为主,其余靠 critic | 中 | 中 |
| 1 | 全部真实奖励,即 \(G_t-V_\phi(s_t)\) | 最大 | 无,critic 只当基线 |
代码里怎么算。GAE 可以从回合末尾往回一步步递推,一次扫描就算完:
把 (P8) 展开,从第二项起提出一个公共因子 \(\gamma\lambda\),括号里剩下的正好是下一步的 \(\hat A_{t+1}\):
所以从最后一步往回算,每一步只需要一次乘加。如果回合在第 \(t+1\) 步终止,之后没有任何 \(\delta\),括号里应该是零,所以乘上 \((1-d_{t+1})\) 把它清零。
# r[t] 是第 t 步动作之后拿到的奖励,即 r_{t+1}
# V 比 r 多一项:V[T] 是最后一个状态的估值
adv = 0
for t in reversed(range(T)): # 从最后一步往回
not_done = 1 - done[t] # 1 − d_{t+1}
delta = r[t] + gamma * V[t+1] * not_done - V[t] # (P7b)
adv = delta + gamma * lam * not_done * adv # (P8a)
A[t] = adv
R_target = A + V[:T] # critic 的回归目标,见 (P13)
注意 critic 在 PPO 里的角色:它只用来降方差,一是当基线,二是替代远处的回报。策略梯度从不对 \(V_\phi\) 求导。到第 15 节会看到,SuperMimic 里的 critic 角色完全不同。
| 阶段 | 乘在 score 上的权重 | 还混着的无关项 | 无关部分 vs 信号 ③ |
|---|---|---|---|
| 原始 (P5) | \(R(\tau)\) | ① + ② + ④ | 约 240 vs 0.3 |
| 第一步 (P5c) | \(G_t\) | ② + ④ | 约 130 vs 0.3 |
| 第二步 (P7) | \(G_t-V(s_t)\) | ④,之后 163 步的运气 | 约 ±1 vs 0.3 |
| 第三步 (P8) | \(\hat A_t\) | 少量 ④(约 17 步)+ critic 误差 | 约 ±0.3 vs 0.3 |
量级是示意。④ 来自之后各步的独立随机性,大致按步数的平方根缩小:\(\sqrt{17/163}\approx0.3\),所以从 ±1 降到约 ±0.3。
前两步完全不改变期望,只去掉无关项。第三步引入了一点偏差,换来大幅度的降方差。
但它仍然只是一个数。三步做完以后,\(\hat A_{137}\) 依然是一个标量,里面还混着第 137 步其余 28 维动作噪声的影响,以及之后十几步的随机性。下一节会看到,这一个标量怎样决定梯度的方向。
PPO 的"随手换一点点姿势"具体是这样的:演员先想好一个动作,再在上面加一点随机抖动,抖动的方向是随便抽的。这次抖完如果比平时好,就把动作往抖的方向挪一点;比平时差,就往反方向挪。所以 PPO 每试一次,只知道"往这一个随机方向抖,是好是坏"。29 个关节到底谁的功劳,要试很多很多次才分得清。
把 (P1) 代进 score。把一次采样写成 \(a=\mu_\theta(s)+\sigma\varepsilon\),其中 \(\varepsilon\) 是标准高斯噪声:
只用到三条规则:对数的性质、平方的求导、链式法则。先看一维,再推广到 29 维。
第 0 步:要算的是什么。动作 \(a\) 是已经执行过的一个固定的数。\(\theta\) 只通过均值 \(\mu\) 影响概率,\(\sigma\) 这里当作常数。要回答的问题是:\(\theta\) 变一点,"选到 \(a\) 的对数概率"变多少。
第 1 步:写出一维高斯分布的密度。
注意:根号里的 \(\pi\) 是圆周率 3.14…,和策略 \(\pi_\theta\) 不是同一个东西,只是碰巧用了同一个字母。\(\exp(x)\) 就是 \(e^x\)。
第 2 步:取对数。用两条规则:乘积的对数等于对数之和,\(\log e^{x}=x\)。
后两项里没有 \(\mu\),对 \(\mu\) 求导时都是零,只需要管第一项。
第 3 步:对 \(\mu\) 求导。用两条规则:\(x^2\) 的导数是 \(2x\);里层的 \((a-\mu)\) 对 \(\mu\) 的导数是 \(-1\),这就是链式法则的"里层"。
含义:如果执行的动作 \(a\) 比均值 \(\mu\) 大,结果为正,说明把 \(\mu\) 往 \(a\) 那边挪,选到 \(a\) 的概率会变大;比均值小时则反过来。
第 4 步:用链式法则,从 \(\mu\) 传到 \(\theta\)。对数概率依赖 \(\mu\),\(\mu\) 又依赖 \(\theta\),所以把两段导数相乘:
\(\partial\mu_\theta/\partial\theta\) 是"参数变一点,网络输出的均值变多少",深度学习框架的自动求导会算。
第 5 步:推广到 29 维。29 个关节各自独立,所以对数概率就是 29 项相加,见 (P5a)。对每一维分别做第 3 步,得到 29 个数 \((a_i-\mu_i)/\sigma^2\)。再对每一维做第 4 步,然后加起来:
第 6 步:代入 \(a=\mu+\sigma\varepsilon\)。这时 \(a-\mu=\sigma\varepsilon\),于是
这就是 (P9) 里的第二个等号。
用右膝的数验算一下。取 \(\mu=0.41\)、\(\sigma=0.05\),抽到 \(\varepsilon=0.6\),实际执行的动作就是 \(a=0.44\)。两种算法结果一样:
一句话。score 指向"把均值往实际执行过的动作那边挪"的方向,力度是 \(\varepsilon/\sigma\)。抽到的噪声越大、\(\sigma\) 越小,力度就越大。
于是 (P7) 里每个样本贡献的梯度是:
第一个式子:代入。(P7) 里每个样本是"score 乘以优势"。把 score 换成 (P9) 的结果,把优势换成它的估计 \(\hat A_t\):
\(\hat A_t\) 是一个数,放在哪里都一样,挪到 \(\varepsilon_t/\sigma\) 前面就是 (P10) 的写法。
第二个式子:为什么"随机扰动乘以结果"的平均等于梯度。看一维。"被高斯模糊过的 \(Q\)",就是把 \(Q\) 在 \(\mu\) 附近按高斯分布加权平均:
对 \(\mu\) 求导时,只有高斯密度含 \(\mu\)。按 (P9.3) 的算法,高斯密度对 \(\mu\) 的导数等于密度本身乘以 \((x-\mu)/\sigma^2\):
代进去,再把 \(x\) 写回 \(\mu+\sigma\varepsilon\),\((x-\mu)/\sigma^2\) 就变成 \(\varepsilon/\sigma\),和 (P9.6) 一样:
这其实又是 (P3) 的对数导数技巧,只不过这次是在动作空间里,对高斯分布用。
为什么 \(\hat A\) 可以代替 \(Q\)。\(\hat A\) 和 \(Q\) 差一个 \(V(s)\),它不随噪声 \(\varepsilon\) 变化,而噪声的平均是零:
所以减掉 \(V\) 不改变平均,只减小噪声,和第 3 节的基线是同一个道理。
把 (P10) 和 (G2) 对照:PPO 用 \(\hat A_t\,\varepsilon_t/\sigma\) 充当 \(\nabla_aQ\)。具体说,它在 29 维动作空间里随机选一个方向 \(\varepsilon_t\) 扰动一下,看结果比平均好还是坏,好就往这个方向推,坏就往反方向推。这是单样本的随机有限差分,和进化策略是同一个思想,只不过发生在动作空间里。
由此有三个直接后果:
试一批要花很多工夫,PPO 舍不得只用一次,想在同一批数据上多学几遍。可学得太猛会出事:同一个好动作被反复加强,演员可能一下子变得面目全非。裁剪就是给每一轮学习装一个"限速器":某个动作被选中的可能性已经提高到原来的 1.2 倍,这一批数据就不再推它了。
(P7) 只在采样所用的那组参数 \(\theta_{\text{old}}\) 处成立。走一步梯度之后,手里的数据就"来自另一个策略"了。最朴素的做法是采一批、走一步、扔掉,非常浪费。步子迈大了还可能让策略崩掉,而崩掉的策略采到的数据又很差,难以恢复。
重要性采样。用新旧策略的概率比给旧数据重新加权,得到一个代理目标:
为什么乘概率比能用旧数据。想求新策略下的平均,手里却只有旧策略的样本。在积分里乘一个 1,再重新分组:
也就是:用旧策略的样本,每个乘上概率比再平均,就能估计新策略下的平均。新策略更爱选的动作,权重变大。这只修正了动作这一层,状态分布的变化被忽略了,所以新旧策略要靠得近。
为什么在 \(\theta_{\text{old}}\) 处梯度就是 (P7)。分母是采样时记下的旧概率,是固定的数,求导时不动:
在 \(\theta=\theta_{\text{old}}\) 这一点,分子分母是同一个策略,于是用 (P3.3):
所以 \(\nabla L^{\text{IS}}=\mathbb E[\nabla_\theta\log\pi_\theta\cdot\hat A]\),正是 (P7)。离开 \(\theta_{\text{old}}\) 以后两者不再相等,所以只能走小步。
先说 \(\rho\) 是什么。对同一个已经执行过的动作,新策略选到它的概率是旧策略的几倍。\(\rho=1\) 表示没变,\(\rho=1.2\) 表示新策略选它的可能性高了 20%。
不裁剪会怎样。设想这批数据里,环境 A 把右膝多给了 0.03,算出的优势 \(\hat A=+0.3\)。同一批数据要反复用好几轮,每一轮优化器都会想"这个动作好,再把它的概率提高一点"。如果没有限制(下面的数字是示意):
裁剪怎么做。\(\rho\) 一旦超出 \([1-\epsilon_{\text{clip}},\,1+\epsilon_{\text{clip}}]\),比如 \([0.8,\,1.2]\),目标就变平,梯度为零。右膝这个动作的概率提高到原来的 1.2 倍以后,这一批数据就不再推它了;要继续提高,等下一批新数据再说。这相当于给每一轮更新划了一个"信任区域",和 TRPO 用 KL 散度约束是同一个目的,只是实现更简单。
这个代理目标只在新策略离旧策略不远时才靠得住,因为它忽略了"策略变了以后会访问到不同的状态"。TRPO 用 KL 散度约束新旧策略的距离;PPO 换成了更简单的裁剪:
读图:优势为正时,提高这个动作的概率有好处,但概率比超过 \(1+\epsilon_{\text{clip}}\) 之后目标变平,不再奖励继续推高。优势为负时同理,降到 \(1-\epsilon_{\text{clip}}\) 就停。如果某次更新把概率比推向了错误的一边,取最小值会保留未裁剪的那一项,梯度仍在,能把它拉回来。
\(\min\) 每次只选两项中较小的那一项,梯度就来自被选中的那一项。\(\operatorname{clip}(\rho)\) 在区间内等于 \(\rho\),在区间外是常数 \(1\pm\epsilon_{\text{clip}}\),常数的导数是零。逐个情况看:
| \(\hat A\) 的符号 | \(\rho\) 的位置 | min 选中 | 梯度 |
|---|---|---|---|
| 任意 | 区间内 | 两项相等,都是 \(\rho\hat A\) | 有,正常更新 |
| 正 | 超过 \(1+\epsilon_{\text{clip}}\) | \((1+\epsilon_{\text{clip}})\hat A\),常数 | 零:已经提得够多,不再提 |
| 正 | 低于 \(1-\epsilon_{\text{clip}}\) | \(\rho\hat A\) | 有:方向推错了,拉回来 |
| 负 | 低于 \(1-\epsilon_{\text{clip}}\) | \((1-\epsilon_{\text{clip}})\hat A\),常数 | 零:已经压得够低,不再压 |
| 负 | 超过 \(1+\epsilon_{\text{clip}}\) | \(\rho\hat A\) | 有:方向推错了,拉回来 |
以第三行为例:\(\hat A\) 为正时,\(\rho\) 越小,\(\rho\hat A\) 越小;\(\rho\) 低于 \(1-\epsilon_{\text{clip}}\) 时,\(\rho\hat A\) 比 \((1-\epsilon_{\text{clip}})\hat A\) 还小,所以被 min 选中,梯度保留。
有了这层保护,同一批数据可以做几轮小批量梯度下降,然后丢弃,重新采样。PPO 实际最小化的总损失是:
三项分别负责训练策略、训练 critic、保持探索,合成一个数一起反向传播,一次更新两个网络。优化器做的是最小化:想让它变大的 \(L^{\text{CLIP}}\) 和熵前面加负号,想让它变小的 critic 误差前面是正号。\(c_v\)、\(c_e\) 用来平衡三项的量级。
把前面几节串起来,PPO 每一轮就是四步:几千个机器人带着随机抖动同时跳一小段;裁判估一估每一步"比平时好多少";在这批数据上按带限速器的规则学几遍;然后把这批数据全部扔掉,重新跳。
例子。第 137 步那样的状态在一批里会出现很多次。某个环境碰巧把右膝多给了 0.03,之后回报略高于 critic 的预期,\(\hat A>0\),于是右膝均值被往上推一点。另一个环境碰巧少给了 0.03,回报略低,\(\hat A<0\),同样把均值往上推。但这两个 \(\hat A\) 里还混着其余 28 维的噪声和之后几十步的随机性,它们的影响往往比 3 毫米的膝盖差异更大。只有把成千上万个这样的样本平均起来,"右膝该加大"这个信号才会从噪声里浮出来。
PPO 慢,归根到底是因为它在"蒙着眼睛下山":每次只能随便迈一小步,感觉一下是高了还是低了。29 个关节、几百步、一大堆随机因素,要试非常多次才能判断准;试完的数据只能用一次;每次试都要真的跑一遍仿真;多加 GPU 也只能让判断准一点,走不快。下面六小节分别讲这几件事。
下面六条原因可以分成四类:前三条说的是"每个样本提供的信息少、噪声大",第四条是"数据用得浪费",第五条是"造数据贵",第六条是"堆算力也救不回来"。每一条都用右膝的例子来说明。
每试一次只得到一个"好"或"不好",29 个关节各自的功劳分不清。
一个样本里到底有什么。按 (P10),第 \(t\) 步的样本给出的"该往哪边改"是 \(\hat A_t\,\varepsilon_t/\sigma\)。其中 \(\hat A_t\) 只是一个数,回答"这次比平时好还是差";方向 \(\varepsilon_t\) 则完全由噪声随机决定,和问题本身无关。换句话说,一个样本只告诉你:沿着某个随机方向扰动一下,结果变好了多少。
打个比方。面前有 29 个旋钮,你同时把它们各随手拧一下,分数涨了 0.3。是哪个旋钮的功劳?单看这一次根本分不清。只有试很多次,统计"右膝那个旋钮往上拧的时候,分数是不是更常上涨",才能把 29 个旋钮的作用分开。
量化一下。先假设一个最理想的情况:回报对动作是线性的,而且 \(\hat A\) 除了这次扰动以外没有任何别的噪声。即便如此,单个样本的误差也很大:
也就是说,一个样本的误差大约是信号本身的 \(\sqrt{30}\approx5.5\) 倍。平均 \(N\) 个样本,相对误差的平方降到 \((d+1)/N\);要把相对误差压到 10%,即使回报毫无噪声,也要约 3000 个样本。真实情况下回报不是线性的,\(\hat A\) 还带着运气,需要的样本只会更多。下面的小例子里回报是二次函数,所需样本就从约 3000 增加到约 4,600。SuperMimic 一次反向传播直接得到完整的 \(u\),没有这一项代价。
把问题缩到一步决策。动作 \(a\) 是 29 维,回报 \(r(a)=-\|a-a^\star\|^2\),当前均值 \(\mu=0\),最优动作 \(a^\star\) 每一维都是 0.1,噪声 \(\sigma=0.1\)。真梯度每一维都是 0.2。用 (P10) 的方式估计,单样本每一维的标准差,以及把每一维估到 10% 精度所需的样本数如下(Python 模拟 20 万次):
| 估计方式 | 单样本每维标准差 | 所需样本数 |
|---|---|---|
| 不减基线,按 (P5) | 6.1 | 约 94,000 |
| 减去最优常数基线,按 (P7) | 1.4 | 约 4,600 |
| 解析梯度,SuperMimic 的方式 | 0 | 1 |
这还只是一步决策。真实任务里动作要经过几十步动力学才影响奖励,\(\hat A_t\) 的噪声大得多。
关节越多、跳得越久,混进来的运气就越多,越难分清是谁的功劳。
维度。(P14) 已经说明,单样本误差随动作维度 \(d\) 增长。还有一层:第 137 步右膝那一维的信号里,混着同一步其余 28 维噪声的影响。右髋、脚踝也在随机抖动,它们对之后奖励的影响全部算进同一个 \(\hat A_{137}\),对右膝来说都是噪声。维度越高,混进来的"别人的噪声"越多。
时间。\(G_t\) 或 \(\hat A_t\) 是之后很多步奖励的和,而之后每一步策略都在加新的噪声,环境也有自己的随机性。假设每一步带来一份独立的随机波动,它们累积起来的方差是:
\(\gamma=0.99\) 时,\(1/(1-\gamma^2)\approx50\),相当于把约 50 步的随机性叠在了一起。不打折扣时更糟,方差和步数成正比:第 3 节例子里 163 步的运气约 ±1,只用 17 步时约 ±0.3,就是这个道理。GAE 用 critic 截短了时间跨度,代价是引入 critic 的偏差。
为什么动作跟踪特别吃亏。一条动作片段有几百步,机器人有 29 个关节,动作库里还有成千上万条不同的动作。每条动作、每个时刻都需要自己的那一份统计平均。时间长、维度高、种类多,三者叠加在一起。
PPO 必须让动作抖起来才能学,可跳舞要的是准,抖得越多越不准。
为什么非加不可。按 (P9),梯度的方向就是 \(\varepsilon/\sigma\)。没有噪声,所有环境执行同一个动作,没有差别可比;\(\sigma\) 太小,分母上的 \(\sigma\) 又会把 \(\hat A\) 里的噪声成倍放大,梯度的方差大约和 \(1/\sigma^2\) 成正比。所以 \(\sigma\) 不能太小。
噪声带来三个代价。
两难。想要精度就得减小 \(\sigma\),可梯度噪声随之增大;想要梯度稳就得保持 \(\sigma\),精度又上不去。SuperMimic 的策略在想象中是确定性的,梯度不靠噪声,所以没有这个两难。它采数据时加的小噪声只是为了让世界模型见过更多动作,而且是可选的;论文也因此去掉了自碰撞惩罚。
试过的数据学几遍就扔,非常浪费。
为什么必须丢。(P7) 的期望是对"当前策略跑出来的轨迹"求的。更新一次以后,手里的数据就来自旧策略了。重要性采样 (P11) 能修正一点,但只在新旧策略接近时有效。所以 PPO 只能在同一批数据上做几轮更新,用裁剪限制走多远,然后把整批数据丢掉,重新采样。这类算法叫同策略(on-policy)。
为什么说浪费。一条转移"在状态 \(s\) 做动作 \(a\),到了 \(s'\)"描述的是物理。不管是哪个策略采到的,它永远成立。PPO 没有利用这一点,因为它从不学物理,它学的是"在当前策略下哪些动作比平时好"。优势是相对于当前策略的平均水平算的,策略一变,旧的优势就过期了。
量级。PPO 每轮采几十万条转移,用几轮就扔;论文里 PPO 跑 18 小时,粗略算下来要采上百亿条,每条只被短暂使用。SuperMimic 的回放缓冲区每个环境保留 1000 步,4096 个环境就是约 400 万条,每条在很多轮迭代里反复用来拟合世界模型。物理不随策略改变,所以这些旧数据一直有效。
也有能复用数据的无模型算法,比如 SAC 这类异策略方法。但在几千个环境并行、动作维度高的场景下,它们的稳定性和扩展性通常不如 PPO,所以动作跟踪领域的主流仍是 PPO。
每试一次都要真的算一遍物理,很贵;但这还不是最主要的原因。
一步仿真要做什么。MuJoCo Warp 的一个控制步包含 4 个 5 毫秒的子步。每个子步都要检测哪些部位和地面或彼此接触,求解接触力的约束方程,再对 29 个关节加上浮动基座做积分。接触求解需要迭代,是最贵的部分。相比之下,世界模型走一步只是一次神经网络前向,在 GPU 上就是几次矩阵乘法。
差多少。论文 Figure 5(a):在实际使用的批量下,世界模型比 MuJoCo Warp 快 10 倍以上。并行环境到 6.4 万个时快 62 倍,因为仿真器的吞吐量已经饱和,再加环境也快不起来。
为什么说是次要原因。Figure 5(b) 做了一个关键对照:把 PPO 搬进世界模型里跑,叫 PPO-WM,这样 PPO 也享受到了同样快的"仿真"。结果 PPO 仍然需要约 64 倍的想象步数才能追上 SuperMimic。也就是说,即使造数据完全免费,PPO 的梯度估计方式本身就要多花几十倍的样本。造数据贵,是在这个基础上又乘了一个倍数。
合起来看。SuperMimic 的速度优势大致是"每个样本信息多"和"每个样本便宜"两个倍数相乘,再加上 7.4 节的数据复用。这也解释了 SONIC 为什么需要 128 张 GPU:PPO 对样本的需求量大,而每个样本都要花真实的仿真算力。

多加 GPU 只能让判断准一点,每一轮能走的步子还是那么大。
多 GPU 买到的是什么。数据并行时,每张卡跑自己的一批环境,梯度在卡之间平均。卡数翻倍,每轮的样本数也翻倍,梯度噪声的标准差按平方根下降:
8 张卡相比 1 张,噪声只降到约 \(1/\sqrt8\approx0.35\),而不是 1/8。
买不到的是什么。每轮迭代花的时间基本不变,因为每张卡的工作量没变;而每轮能走多远,被裁剪限制住了,概率比超出 \(1\pm\epsilon_{\text{clip}}\) 就不再推。所以梯度一旦"准到够用",再多的样本也不能让策略每轮走得更远,训练速度就卡在"每小时能做多少轮迭代"上。这就像下山时每一步的步长有上限:指南针越准越好,但指南针够准以后,下山的速度只取决于步长和步数。
论文里的证据。Figure 4(a):18 小时后,PPO 单卡 0.60,4 卡 0.70,8 卡 0.72,从 4 卡到 8 卡几乎没有提升。另外,多卡还有通信开销,批量变大以后学习率等超参也要重新调。
对照 SuperMimic。它每个样本的梯度已经很干净,瓶颈不在噪声,而在模型和策略每轮能学到多少。论文用两张卡、把学习率乘以 \(\sqrt2\),失败动作数下降得更快,9.8 小时降到零(附录 Figure A.1)。

PPO 把物理当黑箱。MBRL 反其道而行:先学一个"世界如何运转"的模型,再利用这个模型来学策略。这一部分先讲一般的 MBRL,下一部分再看 SuperMimic 属于其中哪一种。
所谓"模型",就是脑子里的一个预言家:你告诉它"现在身体是这样,我打算这样动",它告诉你"下一刻身体会在哪"。它是照着真实发生过的事,也就是日记本,学出来的。学会以后有三个好处:可以在脑子里无限次地想象,不用每次都真跑;以前的日记永远有用,因为物理不会变;它能求导,可以直接告诉你"动作往哪边改"。
强化学习里说的"模型",指环境的动力学:给定当前状态和动作,预测下一个状态,通常还预测奖励和是否终止。确定性的写法是:
学模型有三个好处:
有了预言家以后,可以怎么用它?有人用它"编故事",造出更多练习数据交给别的算法;有人每做一个动作前,先在它脑子里把几种走法都试一遍,挑最好的;有人在一个压缩过的"梦境"里练;SuperMimic 用的是最后一种:直接问预言家"往哪边改",也就是对它求导。
学到模型以后怎么用,形成了几条主要路线。它们的区别在于模型提供的是数据、计划还是梯度:
| 用法 | 模型提供什么 | 代表工作 |
|---|---|---|
| A. 生成数据(Dyna 式) | 从真实状态出发在模型里滚出短轨迹,当作额外数据交给无模型算法 | Dyna(Sutton 1990)、MBPO(Janner 等 2019);RWM 在模型轨迹上跑 PPO |
| B. 在线规划(MPC) | 每一步都在模型里搜索一段动作序列,只执行第一个动作 | PETS(Chua 等 2018)、TD-MPC(Hansen 等 2022) |
| C. 潜空间想象 | 在学到的潜空间里想象,训练 actor-critic | Dreamer 系列(Hafner 等 2020 起) |
| D. 解析梯度 | 把模型当作可微函数,对回报沿轨迹反向传播 | PILCO、SVG(Heess 等 2015)、SuperTrack、ControlVAE、SuperMimic |
SuperMimic 是 A 和 D 的结合。想象的起点来自回放缓冲区里的真实状态,这是 Dyna 的做法;策略更新用穿过模型的反向传播,这是 D 的做法;再加一个 critic 处理想象窗口之外的回报。论文的 Table 1 和相关工作一节也是这样定位的。

预言家不是完美的。它每一步都有一点小错,连着猜很多步,错会越滚越大;演员很聪明,会专门钻到预言家算错、以为分数很高的地方去;演员变了,会去到预言家没见过的地方;预言家只练了"猜得准",没专门练"变化趋势准"。SuperMimic 的对策是:只想象 32 步,起点都用真实发生过的,并且一直补充新日记,让预言家及时改正。
难题一:误差会累积。模型每一步都有一点误差,而下一步的输入是上一步的预测,误差会被动力学放大。粗略地看:
\(L>1\) 时误差随 \(H\) 指数增长,所以长时间的想象不可信。
难题二:策略会利用模型的错误。策略优化的是模型里的回报。如果模型在某些动作上错误地乐观,优化器会专门往那里钻:想象里表现很好,真实里一塌糊涂。这叫模型利用(model exploitation)。
难题三:分布偏移。模型是在旧策略访问过的状态上训练的。策略一变,就会去到模型没见过的地方。
难题四:目标不一致。模型按预测误差训练,但对控制真正重要的是:沿着策略实际走的轨迹准不准;如果走 D 路线,还要求它的导数准。一个模型完全可能预测值不错,斜率却是错的。
| 难题 | 常见对策 | SuperMimic 的做法 |
|---|---|---|
| 误差累积 | 只做短的想象,并且从真实状态出发(MBPO) | 想象 32 步,起点取自回放缓冲区;用多步自回归训练模型,让它学会在自己的预测上继续预测 |
| 模型利用 | 模型集成、不确定性惩罚(PETS、MBPO、RWM-U) | 没有用集成。靠每轮都用当前策略采新数据、重新拟合模型:策略钻到模型错的地方,这些动作就会在仿真里被执行,数据回到缓冲区,模型随之被纠正 |
| 分布偏移 | 持续交替收集数据和更新 | 采数据、拟合模型、改进策略三步每轮都做;缓冲区先进先出,保留近期数据;按回报采样起点,覆盖难动作 |
| 目标不一致 | 多步损失,在策略数据上训练 | 多步损失;把仿真上下文 \(\xi\) 作为输入,让模型确定化(第 14 节)。公开的可工作复现还用了"根部加关节"的紧凑状态,刚体位姿由可微前向运动学算出 |
为什么这个任务特别适合 MBRL。论文的论点可以概括为四条。第一,所有动作都由同一个机器人执行,物理是共享的,一个模型就能覆盖十几万条动作。第二,跟踪奖励每一步都有,信号稠密。第三,平地上单个铰接刚体的动力学相对好学,而仿真里的随机因素又全部已知。第四,GPU 仿真提供了大量便宜的数据,瓶颈在策略优化而不在数据。反过来,涉及物体交互和复杂地形时,模型更难学,论文也把这列为局限。
这一部分把 MBRL 的 D 路线展开:路径导数是什么,反向传播到底算出了什么,为什么要用学到的模型,以及视野、critic 和软终止这三个让它真正能工作的设计。
既然有一个会算的预言家,演员也不再随机抖动,那么"演员的旋钮 → 动作 → 下一刻的身体 → 分数"就是一条确定的计算链。确定的计算链就能直接求导:顺着这条链倒着走回去,一次就算出每个旋钮该往哪边拧,不用再随机试。这就叫"路径导数"。
直觉。如果手里有一个可微的物理函数,策略又是确定性的,那么从参数到回报就是一个普通的复合函数,直接对它求导就行,用不着掷骰子。
想象一段轨迹。从回放缓冲区取一个真实状态当起点,在模型里滚 \(H\) 步:
一旦起点 \(s_0\)、上下文 \(\xi\) 和噪声 \(\eta_{0:H-1}\) 抽定,\(\hat J\) 就是 \(\theta\) 的一个确定函数,反向传播即可得到 \(\nabla_\theta\hat J\)。再对很多个起点和噪声取平均,这就是路径导数,也叫重参数化梯度。它和变分自编码器里的重参数化技巧是同一回事:把随机性挪到输入里,让从参数到目标的路径变成确定的。
和 PPO 对照:PPO 的随机性在动作上,梯度靠随机性"相关"出来;SuperMimic 的策略是确定的,每个样本都直接给出完整的梯度向量,剩下的随机性只来自抽到了哪些起点,方差小得多。
这一节把 SuperMimic 从头到尾拆开。先看"前向一次"到底算了什么,每个变量从哪来;再按论文 Algorithm A.1 的顺序,看每一轮训练先训练什么、再训练什么;最后对每一个训练步骤,讲清楚反向传播的梯度更新了谁、经过了哪些路径、哪里被冻结、哪里被截断。
一句话先说完
机器人要学跟着视频跳舞。它的办法是:先真的跳一小会儿,把发生的事记进日记;让脑子里的"预言家"照着日记学会物理;然后在脑子里一边想象一边练,因为预言家会算,所以每次想象完都能马上知道"每个关节该往哪边改";最后再让"裁判"学会打分。四件事轮流做,一直重复。
先搞懂"学"这个字:loss.backward() 在干什么
每个神经网络里都有几百万个小旋钮,也就是参数。"学"就是把这些旋钮各拧一点点,让结果变好。问题是:几百万个旋钮,该拧哪个、往哪边拧?
代码里只要两行:loss.backward() 和 optimizer.step()。第一行像是"从考试分数往回追责任":把刚才每一步计算都倒着走一遍,算出每个旋钮对这个分数负多少责任、往哪边拧会更好,这个过程叫反向传播。第二行就是让每个旋钮按自己的责任拧一小步。
还有两个常见的操作,要分清:冻住一个网络,意思是这次不修它的旋钮,但"追责任"的电话线还通着,信号可以穿过它,传到更前面的网络;截断,意思是把电话线剪断,信号到这里就停了。
| 知识点 | 一两句话说清 | 在哪里用到 |
|---|---|---|
| 计算图与自动求导 | 前向计算时,深度学习框架会记下每一步运算,形成一张图。反向传播时,从损失出发按链式法则把导数沿这张图一路传回每个参数。我们只写前向,反向由 loss.backward() 自动完成。 | 四个训练步骤都用 |
| 链式法则与雅可比 | 复合函数的导数等于各层导数相乘。向量对向量的导数是一个矩阵,叫雅可比矩阵,第 \(i\) 行第 \(j\) 列表示"输入第 \(j\) 维变一点,输出第 \(i\) 维变多少"。 | 12.5、12.6 |
| 冻结与截断不是一回事 | 冻结:某个网络的参数这一步不更新,但导数照样穿过它,传给它的输入。截断(detach):导数传到这里就停下,不再往前传。 | 12.6 冻结世界模型和 critic;12.7 截断想象轨迹 |
| 通过时间反向传播(BPTT) | 把一个循环展开成一条长链再做反向传播。循环里每一步用的都是同一个网络,所以这个网络的参数会从每一步各收到一份梯度,再加起来。 | 12.5(展开 8 步)、12.6(展开 32 步) |
| 均方误差与交叉熵 | 均方误差(MSE)是预测值和真实值差的平方,用于回归连续量。二元交叉熵(BCE)用于预测"是或否"的概率。sigmoid 函数把任意实数压到 0 到 1 之间,压之前的数叫 logit。 | 12.5、12.7 |
| 三维朝向的表示 | 四元数用 4 个数表示一个三维朝向。两个朝向之差可以用旋转向量表示:方向是转轴,长度是转过的角度。航向坐标系只保留水平朝向(偏航角),让"朝东跑"和"朝北跑"看起来一样。 | 12.2 的状态和积分 |
| 正运动学(FK) | 已知根部的位置和朝向,以及 29 个关节角,按机器人连杆的结构算出每个身体部位在世界中的位置、朝向和速度。它全部由矩阵乘法组成,所以可以求导。 | 12.2 的奖励头 |
| 欧拉积分 | 新位置 = 旧位置 + 时间步长 × 速度。 | 12.2 的状态更新 |
| 优化器与梯度裁剪 | Adam 是最常用的梯度下降变体。梯度裁剪:梯度太大时按比例缩小,防止一步走飞。 | 12.5 到 12.7 |
| 回放缓冲区、GAE | 见第 8 节和第 3 节。 | 12.4、12.7 |
整个系统里只有四个会学习的"小脑袋",每个都是一个神经网络。另外还有三个不用学习的"计算器",它们是写死的公式。
| 角色 | 它是什么 | 它看什么 | 它说什么 | 会学习吗 |
|---|---|---|---|---|
| 演员 | 策略网络 | 自己身体的感觉(最近 10 帧),以及接下来要模仿的 10 帧舞蹈 | "每个关节现在这样动" | 会 |
| 预言家 | 动力学网络,世界模型的主体 | 现在身体的样子,加上演员给的动作 | "这样动的话,下一刻身体会在这里" | 会 |
| 摔倒预警器 | 终止头,世界模型的一部分 | 下一刻身体歪了多少、比舞蹈低了多少 | "这样下去有多大可能摔倒" | 会 |
| 裁判 | critic | 某一刻的身体样子 | "从这里往后,大概还能拿多少分" | 会 |
| 打分计算器 | 奖励公式加正运动学 | 下一刻身体,和舞蹈同一帧 | "这一步像不像,得几分" | 不会,是公式 |
| 感觉计算器 | 观测公式 | 下一刻身体 | "机器人这时会感觉到什么" | 不会,是公式 |
| 日记本 | 回放缓冲区 | — | 记下真实练习时发生的每一步 | 不是网络 |
演员只有一个。它一开始是随机的,几乎不动。每一轮训练改它一点,改完马上用。在仿真器里练习、在脑子里想象、最后上真机,用的都是这同一个演员。
| 变量 | 维度 | 是什么 | 从哪来 |
|---|---|---|---|
| 状态 \(s_t\) 或 \(\hat s_t\) | 71 | 根部位置 3、根部朝向四元数 4、29 个关节角、根部线速度 3、根部角速度 3、29 个关节角速度 | 不带帽子的来自仿真器,带帽子的是世界模型预测的(复现的做法) |
| 网络输入特征 \(\mathrm{feat}(\hat s_t)\) | 71 | 把状态转到航向坐标系,丢掉水平位置,按固定尺度缩放 | 由 \(\hat s_t\) 直接算出,没有参数 |
| 仿真上下文 \(\xi\) | 33 | 摩擦系数 1、29 个关节的编码器偏差、躯干质心偏移 3 | 每个回合随机抽取,随数据存进缓冲区 |
| 动作 \(a_t\) | 29 | 每个关节目标角度的归一化值 | 策略网络输出 |
| 观测 \(o_t\) | 93 × 10 帧 | 每帧:关节角(含编码器偏差)29、关节角速度 29、IMU 角速度 3、投影重力 3、上一步动作 29 | 观测头从 \(\hat s_t\) 算出,再加传感器噪声 \(\eta_t\) |
| 目标 \(g_t\) | 640 | 未来 10 帧参考动作,每帧:关节角 29、关节角速度 29、参考躯干朝向相对机器人躯干的差 6 | 参考动作,加上从 \(\hat s_t\) 算出的机器人躯干朝向(复现的做法) |
| 潜变量 \(z_t\) | 32 | 编码器把目标压缩成的向量,经 tanh 限幅 | 编码器输出 |
| 网络输出 \(y_t\) | 70 | 速度增量和位置修正 | 动力学网络输出 |
| 奖励 \(\hat r_{t+1}\) | 1 | BeyondMimic 的跟踪奖励 | 正运动学加奖励公式 |
| 终止概率 \(\hat d_{t+1}\)、存活权重 \(\hat c_{t+1}\) | 1、1 | 这一步摔倒的概率;到这一步还没摔倒的概率 | 终止头;按 (S4) 连乘 |
| 价值 \(V_\psi\) | 1 | 从这个状态往后的期望回报 | critic 网络 |
需要学习的网络只有四个:策略(编码器加解码器,参数 \(\theta\))、动力学网络和终止头(合起来是世界模型,参数 \(\omega\))、critic(参数 \(\psi\))。观测头、奖励头和正运动学都是固定公式,没有参数,但可以求导。
"想象一步"就是不用仿真器,全靠这几个角色在脑子里算出下一刻会发生什么。
写成代码,大概是这样。这里的变量名直接用中文,方便看懂,真实代码里是英文:
def 想象一步(现在的身体, 身体感觉, 要模仿的舞蹈):
动作 = 演员(身体感觉, 要模仿的舞蹈) # 演员决定每个关节怎么动
下一刻身体 = 预言家(现在的身体, 动作) # 预言家猜下一刻身体在哪
分数 = 打分计算器(下一刻身体, 要模仿的舞蹈) # 像不像,得几分
摔倒可能 = 摔倒预警器(下一刻身体) # 会不会摔
新的感觉 = 感觉计算器(下一刻身体) + 一点点噪声 # 模拟真实传感器不准
return 下一刻身体, 分数, 摔倒可能, 新的感觉
两个小细节。第一,预言家不是凭空画出下一刻的身体,而是在"按现在的速度继续往前走"的基础上做修正。物理里能直接算的部分直接算,它只负责学"速度会怎么变"。第二,"一点点噪声"是故意加的,因为真机上的传感器读数也不准,这样练出来的演员不会太娇气。
"前向一次"指想象中的一步:从第 \(t\) 步的状态出发,算出第 \(t+1\) 步的状态、奖励、终止概率和新的观测。把这一步重复 \(H=32\) 次,就是一条想象轨迹。下图是一步的完整数据流。
下面按图中的顺序,把这一步拆成六个小步。
第 1 小步:拼出策略的输入。策略看两样东西:最近 10 帧的观测 \(o_{t-9},\dots,o_t\),共 930 维;以及目标 \(g_t\),即未来 10 帧参考动作,共 640 维。在想象的第 0 步,观测历史由缓冲区里记录的前 10 帧真实状态算出;之后每一步都用模型预测的状态算出。
第 2 小步:策略前向,得到动作。编码器把目标压成 32 维的 \(z_t\),解码器把 \(z_t\) 和观测历史拼在一起,输出 29 维动作 \(a_t\)。想象中不给动作加噪声,策略是确定性的。
第 3 小步:动力学网络前向,得到下一个状态。先把 \(\hat s_t\) 转成网络输入特征:去掉水平位置,把速度和朝向转到航向坐标系,按固定尺度缩放。这样"朝东跑"和"朝北跑"在网络眼里是同一件事,网络不必把同一套物理学两遍。然后:
为什么不让网络直接输出下一个状态,而是输出增量再积分?因为"位置等于旧位置加上速度乘以时间"这部分物理是确定的,用欧拉积分直接算出来就行,网络只需要学"速度怎么变"以及积分之外的小修正。复现里网络最后一层初始化为零,所以训练一开始模型就等于"匀速运动",比从零学起容易得多。这些都是复现的做法,论文只说动力学网络是 2048×3 的 MLP。
第 4 小步:奖励头。对 \(\hat s_{t+1}\) 做正运动学,得到 14 个被跟踪身体部位的位置、朝向和速度,再和参考动作同一帧比较,按 Table A.2 的公式算出跟踪奖励 \(\hat r_{t+1}\)。动作平滑项要用 \(a_t\) 和 \(a_{t-1}\),关节限位项要用 \(\hat s_{t+1}\) 里的关节角。整个奖励头是公式,没有参数(复现的做法;论文允许它是网络,也允许是公式)。
第 5 小步:终止头。从 \(\hat s_{t+1}\) 算出两个量:根部高度和参考相差多少、倾斜程度和参考相差多少,也就是论文终止条件里的那两个量。把它们输入一个小网络(复现里是 64×64 的 MLP),输出 logit,经 sigmoid 得到终止概率 \(\hat d_{t+1}\),再按 (S4) 更新存活权重 \(\hat c_{t+1}=\hat c_t(1-\hat d_{t+1})\)。终止头是学出来的,因为一个光滑的概率比硬阈值更适合求导,见第 16 节。
第 6 小步:观测头,准备下一步。从 \(\hat s_{t+1}\) 按 Table A.1 直接算出 93 维的去噪观测:关节角要加上 \(\xi\) 里的编码器偏差,再减去默认姿态;投影重力由根部朝向算出;最后一段是这一步的动作 \(a_t\)。再加上采样的传感器噪声 \(\eta_{t+1}\),放进观测历史,挤掉最老的一帧。目标也更新到 \(g_{t+1}\):参考动作往后移一帧,其中"参考躯干朝向相对机器人躯干的差"要用预测的状态重新算。
把这六小步重复 32 次,就得到一条想象轨迹:\(\hat s_0,\dots,\hat s_{32}\)、\(a_0,\dots,a_{31}\)、\(\hat r_1,\dots,\hat r_{32}\)、\(\hat c_0,\dots,\hat c_{32}\)。最后用 critic 估计 \(V_\psi(\hat s_{32})\),按 (S1) 算出代理目标 \(\hat J\)。每次策略更新同时想象 4096 条这样的轨迹,起点都是缓冲区里抽出的真实状态。
整个训练循环写成代码,骨架就这么几行:
演员, 预言家, 摔倒预警器, 裁判 = 随机初始化()
日记本 = 空的
先在仿真器里随便跑几十步,写进日记本 # 让日记本里有东西可学
while 还没练完:
真实练习(演员, 日记本) # ① 4096 个机器人同时跑 2~3 步
预言家复习(预言家, 摔倒预警器, 日记本) # ② 改预言家和预警器
想象 = 演员在脑子里练(演员, 预言家, 裁判, 日记本) # ③ 改演员
裁判学打分(裁判, 想象) # ④ 改裁判
为什么是这个顺序。先让预言家复习最新的日记,演员在脑子里练的时候用的就是最新的物理。演员每轮只改一次,免得它跑到预言家还不熟悉的地方去。裁判最后学,用的正好是演员刚刚做完的那批想象。
论文的 Algorithm A.1 是一个循环。开始前,先在仿真器里跑几十步,把缓冲区填到至少能切出 8 步窗口。之后每一轮迭代按固定顺序做四件事:
| 顺序 | 做什么 | 前向算什么 | 损失 | 反向传播更新谁 | 冻结或截断 | 每轮次数 |
|---|---|---|---|---|---|---|
| ① 采数据 | 策略在仿真器里跑 | 策略 | 无 | 无 | 完全不求导 | 平均 2.5 个仿真步,4096 个环境并行 |
| ② 拟合世界模型 | 在 8 步窗口上回归仿真结果 | 动力学网络、终止头,展开 8 步 | (S6) | \(\omega\) | 不涉及策略 | 10 个小批量,每个 2048 个窗口 |
| ③ 更新策略 | 想象 32 步,最大化 \(\hat J\) | 策略、世界模型、critic,展开 32 步 | (S8) | \(\theta\) | \(\omega\)、\(\psi\) 冻结,但导数穿过它们 | 1 次 |
| ④ 拟合 critic | 回归想象轨迹上的 GAE 目标 | critic | (S9) | \(\psi\) | 想象轨迹被截断 | 5 轮,每轮 4 个小批量 |
为什么是这个顺序。先更新模型,策略用的就是最新的物理;策略只更新一次,是为了不让策略跑到模型还没见过的地方去;critic 在策略之后,用的是同一批想象轨迹。论文的"更新数据比 0.4"意思是每采 1 步新数据做 0.4 次更新,换算过来就是每轮平均采 2.5 步。这几个次数都影响性能:模型更新太少时,策略会开始利用模型的错误。
① 真实练习:不学,只记日记
def 真实练习(演员, 日记本):
with 不追责任(): # 这一步谁也不改
动作 = 演员(仿真器给的感觉, 要模仿的舞蹈)
动作 = 动作 + 一点点随机抖动 # 让预言家见识更多不同的动作
下一刻 = 仿真器.走一步(动作)
日记本.记下(现在的身体, 动作, 下一刻, 有没有摔倒, 这回合的地面摩擦等)
每个机器人一开始不从站立姿势开始,而是直接摆成舞蹈里某一帧的姿势,带着那一帧的速度。所以哪怕演员一开始很笨,记下的也是"跳舞跳到一半摔倒"的真实物理,正好是预言家要学的东西。日记本只保留每个机器人最近的 1000 步,旧的会被挤掉。
当前策略在 MuJoCo Warp 里跑,起点按第 13 节说的"按回报采样"挑选。仿真器实际执行的是 \(a_t+\epsilon_t\),\(\epsilon_t\sim\mathcal N(0,0.1^2)\)。每一步存进缓冲区的是:
缓冲区每个环境保留最近 1000 步,新的挤掉最老的。这一步只是前向,不算任何梯度。动作噪声只为了让模型见过策略动作附近的各种动作,这样它学出来的雅可比 \(F^a\) 才准。
② 预言家复习:连着猜 8 步,猜错就改
def 预言家复习(预言家, 摔倒预警器, 日记本):
for 第几次 in range(10): # 每轮复习 10 次
片段 = 日记本.随机抽(2048 段, 每段 8 步)
猜的 = 片段.第0步的真实身体
误差 = 0
for 第几步 in range(8):
猜的 = 预言家(猜的, 片段.第几步的动作) # 注意:用自己上一步猜的接着猜
误差 += (猜的 − 片段.第几步的真实下一刻) 的平方
误差 += 摔倒预警器猜得准不准
误差.backward() # 追责任:只追到预言家和预警器的旋钮
优化器.step() # 它们的旋钮各拧一点
为什么要"用自己猜的接着猜"。就像背课文:如果每背一句都看一眼书,你永远不知道自己合上书会不会背着背着就跑偏。连着猜 8 步,第 8 步猜错了,追责任时会一路追到第 1 步,于是预言家学会的是"连续猜很多步也不跑偏"。演员后面要在它脑子里连走 32 步,这一点非常重要。
这一步演员完全不参与,只改预言家和摔倒预警器。
前向。从缓冲区抽 2048 个长度为 8 的连续窗口。每个窗口给出真实状态 \(s_0,\dots,s_8\)、执行过的动作 \(a_0,\dots,a_7\)、上下文 \(\xi\) 和终止标签。从真实的 \(s_0\) 出发,用模型自回归地往前滚:\(\hat s_1\) 由 \(s_0\) 和 \(a_0\) 算出,\(\hat s_2\) 由 \(\hat s_1\) 和 \(a_1\) 算出……注意第二步开始,输入的是模型自己上一步的预测,不是真实状态。
损失。每一步都和真实状态比较:
反向传播:梯度更新了谁。只更新世界模型的参数 \(\omega\),也就是动力学网络和终止头。策略不参与这一步。
反向传播:梯度经过了哪些路径。8 步里用的是同一个动力学网络。第 \(j\) 步的预测 \(\hat s_j\) 既直接依赖 \(\omega\),也通过上一步的预测 \(\hat s_{j-1}\) 间接依赖 \(\omega\):
读法:第 8 步的误差不只怪第 8 步的网络调用,也会怪第 1 步到第 7 步的调用,因为前面的误差一路传了过来。于是模型学到的是"让后面的预测也保持准确",而不只是"单步准"。这就是多步自回归训练能让模型在 32 步想象里不漂走的原因。它的另一个副作用是:多步误差取决于雅可比的连乘,所以这种训练也在间接地让雅可比变准(这一点是我的推断)。终止头的梯度只来自 BCE 项,路径是"终止特征 → 终止头",终止特征又依赖预测的根部位姿,所以 BCE 也会给动力学网络一点梯度。
优化。每个小批量做一次 Adam 更新,梯度按全局范数裁剪到 1.0。每轮做 10 个小批量。
③ 演员在脑子里练:最关键的一步
def 演员在脑子里练(演员, 预言家, 裁判, 日记本):
预言家.冻住(); 裁判.冻住() # 不修它们,但电话线通着
身体 = 日记本.随机抽(4096 个真实的身体) # 起点是真实发生过的
总分 = 0; 还没摔倒 = 1
for 第几步 in range(32):
身体, 分数, 摔倒可能, 感觉 = 想象一步(身体, 感觉, 舞蹈)
总分 += 分数 × 还没摔倒 # 摔倒以后的分数不算
还没摔倒 = 还没摔倒 × (1 − 摔倒可能)
总分 += 裁判(身体) × 还没摔倒 # 32 步以后的分数,请裁判估
(−总分).backward() # 追责任:从总分一路追回演员
优化器.step() # 只拧演员的旋钮,拧一次
return 这次想象的全部记录
追责任的信号是怎么走回演员的。总分是 32 步的分数加起来。每一步的分数取决于下一刻的身体;下一刻的身体是预言家根据动作猜出来的;动作又是演员给的。因为预言家和打分计算器都是能求导的计算,追责任的信号就能顺着这条链一路倒着走回去:分数 → 下一刻的身体 → 预言家 → 动作 → 演员的旋钮。这就是为什么只能冻住预言家,不能截断它:一截断,信号就到不了演员了。
拿右膝举个例子。第 137 步,右膝比舞蹈低了 2 厘米。追责任时,打分计算器说:"膝盖再高一点,分数会更高。"预言家说:"右膝的动作加大一点,下一刻膝盖就会高一点。"两句话合起来,结论就是"右膝的动作该加大"。29 个关节一次全部算出来,不需要随机去试。PPO 做不到这一点,它只能让很多机器人随机多动一点、少动一点,再统计哪边分数高。
为什么只想象 32 步,最后交给裁判。预言家猜得越远越不准,追责任的信号走得太远也会乱掉。所以只走 32 步,之后"还能拿多少分"交给裁判估。这样演员既不会只顾眼前,也不会被太远处不准的猜测带偏。
前向。从缓冲区抽 4096 个真实状态作起点,带上各自的 \(\xi\) 和前 10 帧历史,按 12.2 想象 32 步,最后接 critic,算出 (S1) 的代理目标 \(\hat J\)。损失取它的相反数,再除以视野长度:
反向传播:梯度更新了谁。只更新策略参数 \(\theta\)。世界模型 \(\omega\) 和 critic \(\psi\) 被冻结:它们的参数不变,但导数会穿过它们,传给它们的输入。这和截断不同:如果截断了世界模型,梯度就到不了前面的动作,整个方法就失效了。
反向传播:哪些量是常数。起点状态 \(s_0\)、上下文 \(\xi\)、传感器噪声 \(\eta_t\)、参考动作都是给定的数,梯度不会传到它们身上。正是因为把随机性都挪成了这些"给定的输入",从 \(\theta\) 到 \(\hat J\) 的路径才是确定的,可以直接求导,这就是第 11 节的路径导数。
反向传播:梯度经过了哪些路径。\(\theta\) 出现在每一步的策略调用里。第 \(t\) 步的动作 \(a_t\) 通过下面几条路影响 \(\hat J\):
把这些路径整理成公式,就是下面的伴随递推。先简化记号:把观测头并入策略,记 \(a_t=\pi_\theta(\hat s_t)\),并暂时去掉存活权重。定义
伴随向量从第 \(H\) 步开始,一步步往回传。每一步的动作梯度由它给出,最终的参数梯度是:
方括号里的东西和 (P10) 里的 \(\hat A_t\,\varepsilon_t/\sigma\) 扮演同一个角色,都在回答"动作该往哪边改"。区别在于这里它是算出来的:"动作怎样改变下一状态"乘以"下一状态的每个分量值多少钱"。29 个维度同时得到,没有随机扰动。完整的版本还要加上存活权重那一路:\(\hat c\) 乘在之后所有奖励上,它对状态的导数把"离摔倒更近,之后的回报全打折"也算进 \(\boldsymbol\nu\)。
回到例子。第 138 步的伴随向量 \(\boldsymbol\nu_{138}\) 在"右膝高度"这一维上有一个明显的正值:奖励希望膝盖再高 2 cm,之后的落脚时机也取决于它。\(F^a_{137}\) 告诉我们"右膝的动作加大,下一步右膝会升高多少"。两者相乘,右膝那一维的动作梯度为正。如果右髋的配合也影响后续落脚,\(\boldsymbol\nu\) 里与髋相关的分量会把这个信息带到右髋那一维上。协调是自动出现的,不需要哪个环境恰好试出来。
(S2) 的递推里反复乘以 \(F^s_t+F^a_tK_t\),也就是闭环系统的雅可比。连乘 32 次以后,结果可能爆炸,也可能消失。这就是第 15 节要处理的问题,也是下面要做两道梯度裁剪的原因。
优化。梯度先按全局范数裁剪到 1.0,再把每个元素裁剪到 \(\pm0.1\),然后做一次 Adam 更新,\(\beta=(0.9,0.95)\)。复现里如果梯度出现无穷大或非数,就跳过这一步,防止一个坏样本毁掉参数。
④ 裁判学打分:把刚才的想象当"录像"看
def 裁判学打分(裁判, 想象):
with 不追责任(): # 把想象当成录像:只看,不改
目标分数 = 从录像最后一步往前倒着算 # 每一步"往后还能拿多少分"的参考答案
for 第几轮 in range(5):
for 一小份 in 把录像打乱分成(4 份):
误差 = (裁判(一小份的身体) − 目标分数) 的平方
误差.backward() # 只追到裁判的旋钮
优化器.step()
为什么要当录像看。裁判学打分的时候,不能让它去改演员或预言家。不然就像裁判为了自己打分方便,反过来去改选手的动作。所以先把刚才的想象"剪断电话线",变成一段只能看的录像,再让裁判对着录像学。参考答案的算法就是第 3 节讲过的 GAE,只是数据换成了想象出来的。
前向,并且截断。拿刚才那 4096 条想象轨迹,把所有状态、目标、终止概率都截断(detach),也就是当作普通的数,不再和策略、模型有任何梯度联系。然后用当前的 critic 给每一步估值,得到 \(V_0,\dots,V_{32}\)。
目标。从第 32 步往回递推 GAE 回报,和 (P8a) 是同一个递推,只是数据换成了想象轨迹,终止换成了预测的终止概率:
反向传播。只更新 critic 的参数 \(\psi\)。因为输入和目标都已截断,梯度到 critic 的输入就停了,策略和模型都不受影响。把 4096×32 个样本打乱,分成 4 个小批量,做 5 轮,每个小批量一次 Adam 更新,梯度按全局范数裁剪到 1.0。
为什么要截断。critic 的任务只是学会"给状态打分"。如果不截断,critic 的回归损失会把梯度传回策略和模型,等于让它们去迁就 critic 的误差,而不是去提高真实的回报。
一个时间上的细节。第 ③ 步算 \(V_\psi(\hat s_H)\) 时用的是上一轮的 critic,第 ④ 步才把 critic 更新。所以策略总是用"上一轮的打分标准"来改进自己,这和 PPO 先算优势、再更新 critic 是同一个习惯。
| 哪件事 | 谁在学 | 谁在帮忙(冻住,但信号能穿过) | 电话线在哪剪断 |
|---|---|---|---|
| ① 真实练习 | 没有人 | — | 全部,不追责任 |
| ② 预言家复习 | 预言家、摔倒预警器 | — | 日记里的真实数据只是参考答案 |
| ③ 演员在脑子里练 | 演员 | 预言家、摔倒预警器、裁判,以及三个计算器 | 起点的真实身体、噪声、舞蹈本身 |
| ④ 裁判学打分 | 裁判 | — | 整段想象当录像 |
| 步骤 | 被更新的参数 | 梯度的来源 | 梯度穿过、但不更新的 | 梯度被挡住的地方 |
|---|---|---|---|---|
| ① 采数据 | 无 | 无 | 无 | 全程不求导 |
| ② 世界模型 | \(\omega\):动力学网络、终止头 | 8 步状态误差和终止 BCE | 积分公式、终止特征公式 | 真实的 \(s_0\)、动作、\(\xi\) 都是数据 |
| ③ 策略 | \(\theta\):编码器、解码器 | 32 步想象回报加末端价值 | 动力学网络、终止头、正运动学、奖励公式、观测公式、critic | \(s_0\)、\(\xi\)、\(\eta\)、参考动作 |
| ④ critic | \(\psi\) | GAE 目标的均方误差 | 无 | 想象轨迹整体截断 |
一句话:每一轮先用一点真实数据把物理教给模型,再让策略在模型里练 32 步并直接求导,最后教 critic 给想象中的状态打分。
第 12 节讲原理,这一节只讲"照着做"。四个网络都从随机开始:机器人在仿真里每走一步,就把发生的事写进日记;每走 5 步,就停下来学 2 次。每次学习,先让预言家对着日记练 10 下,再让演员在预言家的脑子里练 1 下,最后让裁判练 20 下。这样循环 32 万步,世界模型和策略就一起练出来了。
本节以目前最完整、最贴近论文的公开复现 dhmbb2/supermimic_vibe(commit d0934d0,基于 mjlab)为准。它在一张 RTX A6000 上训练 LAFAN1 的 40 条片段,1.34 小时达到归一化回报 0.833,11.4 小时最好达到 0.947、0/40 失败。下面每个数字都来自它的代码,方括号里注明出处。
一次迭代就是 4096 个机器人在仿真里各走一步。更新数据比是 0.4,所以每 5 次迭代做 2 次更新。每次更新依次是:世界模型 10 步梯度、策略 1 步梯度、critic 20 步梯度。总共 32 万次迭代,约 12.8 万次更新。三者学习率都是 \(2\times10^{-4}\),在前 25 万次迭代里按余弦降到 0.03 倍,之后保持不变。每 4000 次迭代按论文附录 F 的协议完整评估一次〔train.py:TrainCfg、run〕。
| 世界模型 | 策略 | critic | |
|---|---|---|---|
| 训练数据 | 从日记随机抽 2048 段连续 8 步的片段,跨回合的片段丢弃 | 从日记随机抽 4096 个真实状态作起点,在世界模型里想象 32 步 | 刚才那 4096 条 32 步的想象 |
| 每次更新的梯度步数 | 10 | 1 | 5 轮 × 4 个小批量 = 20 |
| 目标 | 连续预测 8 步,每步与真实状态比,误差归一化后取平方 | 32 步折扣总分 + 第 32 步的 critic 估值;损失取负再除以 32 | 想象轨迹上的 λ-回报(\(\gamma=0.99\),\(\lambda=0.95\)) |
| 优化器 | Adam | Adam,\(\beta=(0.9,0.95)\) | Adam |
| 梯度裁剪 | 全局范数 1.0 | 全局范数 1.0,再逐元素 0.1 | 全局范数 1.0 |
| 谁被冻结 | — | 世界模型冻结,梯度穿过它 | 输入当作数据,不回传 |
出处:train.py 的 TrainCfg、update_wm、update_policy、update_critic。
它学什么。输入是当前状态(根部和 29 个关节的位置、速度,共 71 个数)、动作、仿真上下文 ξ(摩擦、关节编码器偏差、躯干质心偏移)。状态会先转成航向坐标系下的特征,其中包括用正运动学算出的身体部位位置和速度。输出是"下一步各量变了多少",再用公式加回当前状态〔networks.py:WorldModel;notes/IMPLEMENTATION.md〕。
怎么练。从片段第一步的真实状态出发,用日记里记录的带噪动作连续猜 8 步,每一步都输入自己上一步的预测。每一步的误差,除以"这一维一步通常变多少"(运行统计,每 5 次更新刷新一次),平方后取平均,8 步相加〔train.py:wm_window_loss〕。
它不学奖励和观测。奖励、观测和摔倒判断都由公式从预测的状态直接算出,所以损失里只有状态这一项。日记只存最近 1000 步,模型学到的始终是"当前策略附近"的物理。
起点。从日记随机抽真实状态,连同它前 10 帧的观测历史和上一步动作〔replay.py:sample_starts〕。
想象 32 步。每一步:策略看"带噪观测历史 + 目标"给出动作;世界模型算出下一个状态;公式算出奖励、新观测(再加传感器噪声),以及一个"软"的摔倒概率。软摔倒概率是在两个终止阈值附近用 sigmoid 抹平,温度分别是 2 厘米和 0.04〔task.py:termination〕。存活概率逐步连乘。参考片段走到头就停止计分,也不再请 critic 估值〔train.py:update_policy〕。
一步更新。总分 = 每步"折扣 × 存活 × 奖励"之和,加上第 32 步"折扣 × 存活 × critic 估值"。损失取总分平均值的负数再除以 32。反向传播时世界模型参数冻结,但梯度穿过它;critic 的估值也把梯度传回第 32 步的状态。然后做一次 Adam 更新。
采数据时用的也是它。真实练习时,策略输出加上标准差 0.1 的高斯抖动再执行〔train.py:collect〕。
update_critic〕。这些地方论文没写清,或者复现做了取舍〔notes/IMPLEMENTATION.md〕:
仿真器本身也是一种物理,为什么不直接对它求导?因为脚碰地面这类接触是"突然"发生的:没碰到时一点力都没有,一碰到就是很大的力。对这种突变求导,答案要么是零,要么大得离谱,没法用。神经网络学出来的预言家是"圆滑"的,它给出的方向更好用,算得也快得多。
演员要听预言家说"往哪边改",所以预言家不光要猜得准,变化的趋势也要准。几个设计都是为这个服务的:所有舞蹈都是同一个身体跳的,所以一个预言家就够用;把地面摩擦这些随机因素直接告诉它,它就不用瞎猜;让它连着猜 8 步来练习,它就不容易越猜越偏;它和演员一起学,演员去哪,它就跟着学哪。
走 D 路线时,模型不仅要预测得准,雅可比 \(F^s\)、\(F^a\) 也要准,因为 (S3) 用的就是它们。下面几个设计各自对应这个要求:

在脑子里想象多少步合适?太多了,预言家越猜越不准,"往哪边改"的信号也会越传越乱;太少了,演员只看眼前。比如被推了一下,恢复平衡的好处要过一会儿才显现,只看眼前就看不到。办法是只想象 32 步,32 步以后"还能拿多少分"请裁判估。这里的裁判比 PPO 里的更重要:它的意见会直接变成演员"往哪边改"的一部分。
想象视野 \(H\) 两头为难:
解决办法是在截断处接一个价值函数,也就是 (S1) 最后那一项 \(\gamma^H\hat c_HV_\psi(\hat s_H)\)。按 (S2),反向传播从 \(\boldsymbol\nu_H=\nabla_sV_\psi(\hat s_H)\) 开始,critic 把窗口之外的长期价值以"状态价格"的形式交给反向传播。于是 \(H\) 只需要长到模型还准的程度,再往后交给 critic。这一思路来自 SVG(H)(Heess 等 2015)和 SHAC(Xu 等 2022,用的是可微仿真器)。
和 PPO 对照:PPO 的 critic 只用来降方差,策略梯度不对它求导;SuperMimic 的 critic 是目标函数的一部分,它对状态的梯度直接进入策略梯度,所以 critic 的质量直接决定策略的上限。论文没有明说梯度是否穿过 critic,SHAC 是穿过的。
critic 本身用想象轨迹上的 GAE 目标做回归,公式和 (P8) 同构,只是数据换成了模型里的想象轨迹。
论文 Figure 4(b, c) 给出了证据。没有 critic 时,\(H\le16\) 训练直接失败;有 critic 时,\(H=16\) 就能和无 critic 的 \(H=32\) 打平,\(H=32\) 最快。\(H=128\) 耗时近三倍,最终回报还更低。被推时,有 critic 的策略 0.5 到 2.4 秒内站稳,没有 critic 的一直踉跄。
仿真里的摔倒是"一刀切":倾斜超过某个角度就算摔倒,没超过就完全没事。这种一刀切没法告诉演员"你离摔倒还有多远"。所以让摔倒预警器给出一个"摔倒的可能",从 0 慢慢升到 1。演员越接近摔倒,之后的分数就被打越多的折扣,它自然就学会远离摔倒。
真实的终止判断是一个台阶函数:倾斜误差超过阈值就终止,否则继续。台阶函数的导数几乎处处为零,在阈值处又是无穷大,梯度没法告诉策略"你离摔倒还有多远"。SuperMimic 让模型预测终止的概率 \(\hat d\),它是光滑的:
因为 \(\hat c\) 乘着之后所有的奖励,\(\hat J\) 对状态的导数里会多出一项:"之后的回报"乘以"终止概率对状态的导数"。这一项的方向是远离会摔倒的状态。
终止概率有多"陡"会影响性能。太陡就退回到台阶函数,没有梯度,训练也不稳定。公开的可工作复现用的是学出来的终止头;我们第一版用解析的陡峭 sigmoid,效果明显更差。
这几项设置决定了练得快不快、稳不稳:想象多少步,预言家复习得够不够勤,追责任的信号要不要限速,预言家连着猜几步来练习,以及身体状态怎么表示。
一轮迭代的完整过程见第 12 节。这里只列出对速度和稳定性影响最大的几个设置。
PPO 是蒙着眼睛试,每次只知道"这样抖一下是好是坏";SuperMimic 是先学会物理,再直接算出每个关节该往哪边改。所以 SuperMimic 每次学到的东西多得多,想象又比仿真快,日记还能反复用,几样乘在一起就快了上千倍。代价是:它只和预言家一样准。
| 维度 | PPO | SuperMimic |
|---|---|---|
| \(\nabla_aQ\) 从哪来 | 随机扰动加相关,\(\hat A_t\varepsilon_t/\sigma\) | 穿过模型链式求导,(S3) 的方括号 |
| 每个样本的信息量 | 一个标量 | 完整的 29 维向量 |
| 误差的性质 | 无偏,方差大 | 方差小,但以模型为准,有偏 |
| 策略 | 随机策略,必须加噪声 | 确定性策略,噪声只用于拟合模型 |
| critic 的角色 | 基线,只降方差 | 目标的一部分,提供窗口外的价值和梯度 |
| 数据复用 | 同策略,几轮更新后丢弃 | 物理不随策略变,缓冲区数据反复用于拟合模型 |
| 每一步的代价 | 真实仿真,要求解接触 | 一次网络前向,快 10 到 62 倍 |
| 跨动作的共享 | 没有显式共享 | 一个模型覆盖全部动作 |
| 主要风险 | 样本需求大 | 模型错误被利用、梯度爆炸、短视 |
| 适用边界 | 任何能采样的环境 | 动力学要能学准;平地上的单个机器人效果好 |
论文给出的三条证据正好对应三个因素,它们是相乘的关系:

两段代码最关键的区别只在一个地方:PPO 学习时,追责任的信号只经过"这个动作被选中的可能性",仿真器在计算之外;SuperMimic 学习时,信号穿过了 32 次预言家。
# 随机策略:均值网络 mu + 标准差 sigma
loop:
# 1. 采样(同策略)
for t in rollout:
a = mu(s) + sigma * randn() # 必须加噪声
s2, r, d = sim.step(a) # 真实仿真
batch.add(s, a, logp(a), r, d)
# 2. 优势
A, R = gae(batch, critic) # (P8)
# 3. 几轮更新,然后丢掉数据
for mb in minibatches(batch, epochs):
ratio = exp(logp_new - logp_old)
loss = -min(ratio*A, clip(ratio)*A) # (P12)
loss += c_v*(critic(s) - R)**2 - c_e*entropy
step(loss) # 梯度只经过 log pi
batch.clear()# 确定性策略 pi;模型 f;critic V
loop:
# 1. 采数据(旧数据保留)
a = pi(o, g)
s2, r, d = sim.step(a + small_noise)
buffer.add(s, a, xi, r, d)
# 2. 拟合模型:多步自回归 (M1)
s_hat = s0
for j in window:
s_hat = f(s_hat, a[j], xi) # 输入自己的预测
loss_f += |s_hat - s[j+1]|**2 + ...
step(loss_f)
# 3. 想象 H 步,反向传播 (S1)-(S3)
s_hat = buffer.sample_starts() # 真实起点
for t in range(H):
a = pi(obs_head(s_hat) + eta, g[t])
J += gamma**t * c * r_head(s_hat, a)
c *= 1 - term_head(s_hat) # (S4)
s_hat = f(s_hat, a, xi) # f 冻结但可导
J += gamma**H * c * V(s_hat)
step(-J) # 梯度穿过 f
# 4. critic 回归想象轨迹上的 GAE 目标最该盯住的差别:PPO 的 step(loss) 里,梯度只经过 logp,仿真器在计算图之外;SuperMimic 的 step(-J) 里,梯度穿过 \(H\) 次模型调用,仿真器被一个可导的替身取代。
点公式编号可以跳回这个符号第一次出现的地方。
| 符号 | 含义 | 首次出现 |
|---|---|---|
| \(\theta\) | 策略网络的参数 | (G1) |
| \(t,\ T\) | 时间步;回合长度 | (G1) |
| \(r_{t+1}\) | 第 \(t\) 步动作之后得到的奖励 | (G1) |
| \(\gamma\) | 折扣因子 | (G1) |
| \(\mathbb E[\cdot]\) | 期望,对随机因素求平均 | (G1) |
| \(J(\theta)\) | 期望折扣回报,优化目标 | (G1) |
| \(\nabla_\theta J\) | \(J\) 对参数的梯度 | (G2) |
| \(s_t\) | 状态(本页常把观测和目标合并写成状态) | (G2) |
| \(\pi_\theta(s)\) | 确定性策略 | (G2) |
| \(Q(s,a)\) | 动作价值 | (G2) |
| \(\nabla_aQ\) | 动作梯度:动作每一维改一点,未来回报变多少 | (G2) |
| \(\partial\pi_\theta/\partial\theta\) | 策略输出对参数的雅可比 | (G2) |
| \(\pi_\theta(a\mid s)\) | 随机策略的概率密度 | (P1) |
| \(\mu_\theta(s),\ \sigma\) | 高斯策略的均值、标准差 | (P1) |
| \(\tau\) | 一条轨迹 | (P2) |
| \(\rho_0\) | 起始状态分布 | (P2) |
| \(P(s'\mid s,a)\) | 环境的转移概率,即物理 | (P2) |
| \(R(\tau)\) | 整条轨迹的折扣回报 | (P3) |
| \(\nabla_\theta\log\pi_\theta(a\mid s)\) | score:让该动作更可能的参数方向 | (P5) |
| \(a_i,\ \mu_{\theta,i}\) | 第 \(i\) 个关节的动作、均值 | (P5a) |
| ①②③④ | 回报的四个组成部分:之前的奖励、平时水平、动作本身的贡献、之后的运气 | (★) |
| \(k\)(求和下标) | 求和用的时间下标 | (P5b) |
| \(G_t\) | 从第 \(t\) 步起的回报 | (P5c) |
| \(b(s)\) | 基线 | (P6) |
| \(V(s)\) | 状态价值 | (P7) |
| \(A(s,a)\) | 优势:比平均好多少 | (P7) |
| \(V_\phi,\ \phi\) | PPO 的 critic 及其参数 | (P7a) |
| \(\delta_t\) | TD 误差 | (P7b) |
| \(n,\ \hat A^{(n)}_t\) | 真实奖励的步数;n 步优势估计 | (P7c) |
| \(\hat A_t\) | 优势的估计 | (P8) |
| \(\lambda\) | GAE 系数 | (P8) |
| \(d_{t+1}\) | 是否终止,终止为 1 | (P8a) |
| \(\varepsilon\) | 标准高斯噪声 | (P9) |
| \(\widehat{\nabla_\theta J}_t\) | 单个样本的梯度估计 | (P10) |
| \(\theta_{\text{old}}\) | 采样时的策略参数 | (P11) |
| \(\rho_t(\theta)\) | 新旧策略的概率比 | (P11) |
| \(\epsilon_{\text{clip}}\) | 裁剪范围 | (P12) |
| \(\hat R_t\) | critic 的回归目标 | (P13) |
| \(\mathcal H[\pi]\) | 策略分布的熵 | (P13) |
| \(c_v,\ c_e\) | 损失中 critic 项、熵项的权重 | (P13) |
| \(u,\ \hat u\) | 真实动作梯度 \(\nabla_aQ\) 的简写;它的单样本估计 | (P14) |
| \(d\) | 动作维度,G1 是 29 | (P14) |
| \(\zeta_l,\ \sigma_\zeta^2\) | 之后每一步的随机波动及其方差 | (P15) |
| \(N\) | 每轮估计梯度用的样本数 | (P16) |
| \(f_\omega,\ \omega\) | 动力学模型及其参数 | (M1) |
| \(\hat s\) | 模型预测的状态 | (M1) |
| \(\mathcal D\) | 真实转移的数据集 | (M1) |
| \(e,\ L\) | 单步误差上界;Lipschitz 常数 | (M2) |
| \(H\) | 想象视野 | (M2) |
| \(\xi\) | 仿真上下文:摩擦、标定偏移、质心偏移 | (S1) |
| \(\hat{\bar o}_t\) | 模型预测的去噪观测 | (S1) |
| \(\eta_t\) | 传感器噪声 | (S1) |
| \(g_t\) | 目标:参考动作的未来 10 帧 | (S1) |
| \(\hat r_{t+1}\) | 模型给出的奖励 | (S1) |
| \(\hat c_t\) | 软存活权重 | (S1) |
| \(V_\psi,\ \psi\) | SuperMimic 的 critic 及其参数 | (S1) |
| \(\hat J(\theta)\) | 模型里的代理目标 | (S1) |
| \(\boldsymbol\nu_t\) | 伴随向量:状态各分量的"价格" | (S2) |
| \(F^s_t,\ F^a_t\) | 模型对状态、对动作的雅可比 | (S2) |
| \(K_t\) | 策略对状态的雅可比(反馈增益) | (S2) |
| \(y_t\) | 动力学网络的 70 维输出:速度增量和位置修正 | (S5) |
| \(p,\ \mathsf q,\ j,\ v,\ w,\ \dot j\) | 状态的六部分:根部位置、根部朝向、关节角、根部线速度、根部角速度、关节角速度 | (S5) |
| \(\Delta t,\ R_h\) | 控制步长 0.02 秒;航向旋转 | (S5) |
| \(\ominus,\ \sigma_{\text{err}}\) | 状态之差(朝向用旋转向量);各物理量的名义误差尺度 | (S6) |
| \(\mathcal L_\pi\) | 策略的损失,即 \(-\hat J/H\) | (S8) |
| \(\hat d_{t+1}\) | 模型预测的终止概率 | (S4) |
| \(k\) | 多步自回归训练的窗口长度 | 第 14 节 |