LECTURE 16

基于模型的强化学习算法

动力学模型已经学到手了——接下来到底怎么用它?规划、把梯度反传进策略、还是让它给无模型算法造数据。

讲师:Sergey Levine UC Berkeley 原始材料:lec-16.pdf(38 页)

0. 本讲导读

上一讲我们建立了「学一个模拟器」这件事的基本框架:从环境里收集转移数据 $\mathcal{D}=\{(\mathbf{s}_i,\mathbf{a}_i,\mathbf{s}_i')\}$,用监督学习拟合一个动力学模型(dynamics model)$f(\mathbf{s},\mathbf{a})=\mathbf{s}'$ 或者概率形式 $p_\phi(\mathbf{s}_{t+1}|\mathbf{s}_t,\mathbf{a}_t)$,然后「在这个模拟器里跑你喜欢的 RL 算法」。上一讲也已经指出这条路上的两块大石头:分布漂移(distributional shift)——策略一旦被优化,就会跑到模型没见过的状态去;以及模型不确定性(model uncertainty)——我们需要贝叶斯神经网络或 bootstrap 集成来知道「模型自己也不知道」的地方在哪。

这一讲把那个含糊的第三步「run your favorite RL method inside the simulator」拆开,认真回答:拿到模型之后到底怎么做决策。整讲分三大块:

  • Part 1 · 用模型做规划(planning):完全不要策略,直接在模型里搜索一串动作。从确定性开环问题出发,讲清楚开环 / 闭环的区别、随机打靶法(random shooting)与交叉熵方法(CEM)、模型预测控制(MPC),以及怎么在集成模型的不确定性下做规划。
  • Part 2 · 用模型学策略(policy learning):把梯度直接反传穿过模型进策略参数(pathwise 梯度),它为什么在长时域下会炸;改用策略梯度为什么更稳;再到长 rollout 的 $\mathcal{O}(\epsilon H^2)$ 误差诅咒,以及 Dyna 风格的「短分支 rollout + off-policy RL」解法(MBA / MVE / MBPO)。
  • Part 3 · 模型怎么表示(representing the model):当观测是图像时,直接在像素上建模和规划都很痛苦,于是引入潜在状态空间模型(latent state space model):观测模型 + 动力学模型 + 奖励模型 + 编码器联合训练,最后长成 SLAC / Dreamer 这样的算法。

下一讲会转向离线强化学习(offline RL)。你会发现那里的核心矛盾——「值函数在没见过的动作上过高估计,策略去利用这个错误」——和本讲里的 model exploitation 是同一个病的两种表现,只不过一个发生在动力学模型上,一个发生在值函数上。带着这个对应关系读,两讲会互相点亮。

全讲统一记号:$\mathbf{s}_t$ 是状态(满足马尔可夫性),$\mathbf{o}_t$ 是观测(可能只是状态的部分信息),$\mathbf{a}_t$ 是动作,$r(\mathbf{s}_t,\mathbf{a}_t)$ 是奖励,$\pi_\theta(\mathbf{a}|\mathbf{s})$ 是策略,$p(\mathbf{s}_{t+1}|\mathbf{s}_t,\mathbf{a}_t)$ 是真实转移,$\hat p_\phi$ 或 $f_\phi$ 是学到的模型,$\tau$ 是轨迹,$\gamma$ 是折扣,$H$ 是规划时域(planning horizon),$T$ 是任务时域。

核心结论
  • 规划 ≠ 学策略。规划是在当前状态下现场解一个优化问题,输出一串动作;学策略是离线把决策函数固化到网络参数里。前者不需要训练、天然能利用模型的即时信息,但只能做开环、维度上限极低。
  • 开环规划在随机环境下必然次优,因为它在看到未来状态之前就把所有动作都定死了。修复方法有两条:要么学闭环策略,要么每一步重新规划(MPC)——后者才是模型基 RL 在真实机器人上真正管用的原因。
  • 规划器是模型误差的对手(adversary)。$\argmax$ 会主动去找模型高估奖励的地方,所以必须用不确定性感知的模型。用 bootstrap 集成时,正确做法是对回报取平均 $J=\frac1N\sum_i\sum_t r(\mathbf{s}_{t,i},\mathbf{a}_t)$,而不是对预测取平均——两者差别巨大。
  • 把梯度反传穿过 $H$ 步模型会乘上 $H$ 个雅可比矩阵,数值上等价于训练一个没有门控机制的 RNN,梯度非爆即消;用策略梯度在模型里做「无模型优化」反而更稳。
  • 长 rollout 的误差以 $\mathcal{O}(\epsilon H^2)$ 累积。解法是 Dyna 风格:从真实 replay buffer 里取状态作起点,只做极短(哪怕 1 步)的分支 rollout,把生成的转移喂给 off-policy 的无模型算法。这就是 MBPO 的骨架,也是目前最实用的模型基 RL 配方。
  • 图像输入时不要在像素上建模型。学一个潜在空间 $\mathbf{s}_t$,同时训练观测模型 $p(\mathbf{o}_t|\mathbf{s}_t)$、动力学 $p(\mathbf{s}_{t+1}|\mathbf{s}_t,\mathbf{a}_t)$、奖励模型 $p(r_t|\mathbf{s}_t)$ 与编码器 $q_\psi(\mathbf{s}_t|\mathbf{o}_t)$,目标函数就是这个隐变量模型的 ELBO。

1. 路线图:模型基 RL 的版本进化史

Levine 讲模型基 RL 有一套经典的「版本号」讲法:从一个显然会失败的朴素算法出发,每次只补一个洞,一路升级到真正能用的算法。这条动机链条本身就是本讲最值钱的东西,我们先把它整个铺开,后面各节再逐个填细节。

上一讲的名义「学到的模拟器」算法三步,以及本讲要展开的两个分支
上一讲留下的骨架:① 用基准策略 $\pi_\beta$ 收数据;② 用平方误差 $\min_f\sum_i\|f(\mathbf{s}_i,\mathbf{a}_i)-\mathbf{s}_i'\|^2$ 拟合「模拟器」;③ 在模拟器里跑 RL。黄色标注给出本讲的两条主线:右上——用不确定性感知的模型来对付分布漂移;下方——第三步「可以跑我们学过的任一算法,但这里有更好的选择」,那个「更好的选择」就是规划。

版本 0.5:收集 → 拟合 → 规划

最朴素的写法只有三步,没有任何循环:

model-based RL version 0.5:
  1. 用基准策略 pi_0(a|s)(例如均匀随机策略)收集数据 D = {(s, a, s')_i}
  2. 学动力学模型 f(s,a),最小化 sum_i || f(s_i,a_i) - s'_i ||^2
  3. 用 f(s,a) 规划出动作序列,执行它

这套东西在系统辨识(system identification)里其实很有用:如果你已经知道动力学的参数化形式(比如机器人的连杆长度、质量、摩擦系数未知,但方程形式已知),用随机激励采一批数据、拟合几个参数、然后用最优控制求解,是几十年来的标准做法。它失败的地方在于:$\pi_0$ 采出来的状态分布 $p_{\pi_0}(\mathbf{s})$,和你规划出来的策略访问的状态分布 $p_{\pi_f}(\mathbf{s})$ 完全不是一回事。

这就是上一讲反复强调的分布漂移。给一个具体图景:你在一片山地上随机游走采数据,模型在你走过的低矮区域拟合得很好;但奖励是「海拔越高越好」,于是规划器发现「一直往右走海拔就一直涨」,因为在训练数据的边缘处模型的外推恰好是一条上升直线。你照做,实际上右边是悬崖。模型在数据分布内几乎完美,在数据分布外错得离谱,而规划器专门往数据分布外跑——因为那里才有模型幻想出来的高奖励。

版本 1.0:把执行时的数据收回来(DAgger 式修补)

模仿学习那一讲教过一个思路:既然训练分布和测试分布不一致,那就把测试分布也变成训练分布。DAgger 是让专家来标注策略访问过的状态;这里更简单——环境本身就是「专家」,我们执行完计划后自然会观测到真实的 $\mathbf{s}'$,直接把它加回数据集就行,不需要任何人工标注。

model-based RL version 1.0:
  1. 用基准策略 pi_0(a|s) 收集数据 D = {(s, a, s')_i}
  2. 学动力学模型 f(s,a),最小化 sum_i || f(s_i,a_i) - s'_i ||^2
  3. 用 f(s,a) 规划动作序列
  4. 执行这些动作,把真实观测到的 (s, a, s') 追加进 D
     —— 回到第 2 步

循环 2→3→4→2 就是 DAgger 的模型基版本。它确实让训练分布逐步逼近了策略访问的分布,但还留着一个致命问题:第 3 步规划出的是一整条长度 $H$ 的开环动作序列,第 4 步要把它全部执行完才回头更新模型。如果模型在第 3 步就把你带到了悬崖,你会在收集到「悬崖是坏的」这条数据之前先摔下去。

版本 1.5:MPC —— 只执行第一个动作,然后重新规划

model-based RL version 1.5:
  1. 用基准策略 pi_0(a|s) 收集数据 D = {(s, a, s')_i}
  2. 学动力学模型 f(s,a)
  3. 用 f(s,a) 规划动作序列 a_1 ... a_H
  4. 只执行第一个动作 a_1,观测到真实的 s'(这就是 MPC)
  5. 把 (s, a_1, s') 追加进 D
     —— 回到第 3 步(每 N 步回到第 2 步重训模型)

注意这里出现了两层循环:内层 3→4→5→3 是每个时间步都重新规划一次,外层每 N 步才重新训练一次模型。这个「重规划」的动作看起来只是工程上的小改动,实际上一石二鸟:(a)它把开环规划变成了事实上的闭环控制,因为每一步的规划都基于刚刚真实观测到的状态;(b)模型只需要在一步之内是对的,因为一步之后我们就会用真实状态把累积误差清零。代价是计算量——每个控制周期都要现场解一次优化问题。

版本 2.0:把梯度反传进策略

MPC 每步都要重规划,在高频控制(比如 1 kHz 的力矩控制)下算不过来;而且规划器只能做开环,处理不了「等看到结果再决定」这类需求。于是自然的下一步是:既然模型是可微的神经网络,那就把 $H$ 步 rollout 的总奖励对策略参数 $\theta$ 求导,直接训练一个闭环策略 $\pi_\theta(\mathbf{a}|\mathbf{s})$。

model-based RL version 2.0:
  1. 用基准策略 pi_0(a|s) 收集数据 D = {(s, a, s')_i}
  2. 学动力学模型 f(s,a)
  3. 通过 f(s,a) 把梯度反传进策略 pi_theta(a|s),优化 sum_t r(s_t, a_t)
  4. 运行 pi_theta(a|s),把访问到的 (s, a, s') 追加进 D
     —— 回到第 2 步

这一版才是「真正的模型基策略学习」。它的问题在第 8 节展开:反传穿过 $H$ 步模型意味着连乘 $H$ 个雅可比矩阵,数值性质和训练一个没有 LSTM 门控的深层 RNN 一样糟。

版本 2.5 与 3.0:本讲新增的两级

本讲在 2.0 之上又加了两级。版本 2.5 把第 3 步的「反传」换成「在模型里跑策略梯度」——把学到的模型当成一个廉价模拟器,用 REINFORCE / PPO 这类无模型算法去优化策略,从而避开雅可比连乘。版本 3.0 再解决 2.5 遗留的长 rollout 误差累积问题:不从初始状态出发做长 rollout,而是从真实 replay buffer 里随机取状态作起点,只做极短的分支 rollout,再用 off-policy 算法同时消化真实数据和模型数据。

版本怎么用模型闭环?抗分布漂移手段主要弱点
0.5规划一次否无分布漂移直接致命
1.0规划 + 回收数据否DAgger 式数据回收执行整条计划才更新,容易先摔死
1.5MPC:每步重规划是(事实上)数据回收 + 每步纠偏在线计算量大;只能开环规划
2.0反传穿过模型学策略是数据回收雅可比连乘导致梯度爆炸/消失
2.5模型内跑策略梯度是数据回收长 rollout 误差 $\mathcal{O}(\epsilon H^2)$
3.0短分支 rollout + off-policy RL是短 rollout + 真假数据混训模型数据的状态分布仍有偏
直觉

把这条进化线一句话总结:每一级升级,都是在缩短「我们信任模型的时长」。0.5 版信任模型整整 $H$ 步且永不修正;1.0 版信任 $H$ 步但会事后补数据;1.5 版只信任 1 步;3.0 版索性只让模型往前走 1~5 步。模型基 RL 的全部工程智慧,几乎都可以归结为「别让模型自己走太远」。

2. 确定性情形:开环规划的数学形式

先把最干净的情形写清楚:动力学是确定性的,$\mathbf{s}_{t+1}=f(\mathbf{s}_t,\mathbf{a}_t)$,初始状态 $\mathbf{s}_1$ 已知。此时「决策」这件事退化成一个纯粹的确定性优化问题:

$$ \mathbf{a}_1,\dots,\mathbf{a}_T=\argmax_{\mathbf{a}_1,\dots,\mathbf{a}_T}\sum_{t=1}^{T}r(\mathbf{s}_t,\mathbf{a}_t)\quad\text{s.t.}\quad \mathbf{s}_{t+1}=f(\mathbf{s}_t,\mathbf{a}_t) $$
确定性开环规划:智能体在脑内用模型推演,一次性把整串动作发给世界
确定性开环规划的通信图景。世界只告诉智能体一件事:初始状态 $\mathbf{s}_1$。智能体在自己脑子里(云朵中的那个小世界=学到的模型)把整条轨迹推演一遍,选出总奖励最高的动作序列 $\mathbf{a}_1,\dots,\mathbf{a}_T$,然后一次性发出去。之后信息就是单向的了——世界不再向智能体反馈任何东西。

三点值得注意。

第一,这里没有策略。输出是一串具体的动作向量,不是一个函数。这就是「规划(planning)」与「学策略(policy learning)」的本质分界:规划的解只对这一个初始状态有效,换个 $\mathbf{s}_1$ 要重解一遍;策略的解对所有状态都有效,但需要训练。

第二,约束可以被消掉。把 $\mathbf{s}_{t+1}=f(\mathbf{s}_t,\mathbf{a}_t)$ 递归代入,$\mathbf{s}_t$ 就是 $\mathbf{s}_1,\mathbf{a}_1,\dots,\mathbf{a}_{t-1}$ 的确定性函数,于是目标变成只关于动作序列的无约束函数:

$$ J(\mathbf{a}_1,\dots,\mathbf{a}_T)=r(\mathbf{s}_1,\mathbf{a}_1)+r\big(f(\mathbf{s}_1,\mathbf{a}_1),\mathbf{a}_2\big)+r\Big(f\big(f(\mathbf{s}_1,\mathbf{a}_1),\mathbf{a}_2\big),\mathbf{a}_3\Big)+\cdots $$

这种「把状态全部用动作表达掉」的写法叫 shooting 形式(打靶法),下一节的 random shooting、CEM 都建立在它之上。它的好处是变量少(只有动作)、无约束;坏处是数值上极其病态——$\mathbf{a}_1$ 的微小改变会经过 $T-1$ 层 $f$ 的复合放大到 $\mathbf{s}_T$,而 $\mathbf{a}_T$ 只影响最后一项。这正是后面梯度方法会炸掉的根源。(与之相对的 collocation 形式把 $\mathbf{s}_t$ 也当作优化变量、把动力学作为等式约束,条件数好得多,但要上带约束优化求解器。)

第三,这个问题在真实的深度 RL 场景里通常是非凸的。$f$ 是神经网络,$r$ 可能有稀疏项,$J$ 的地形坑坑洼洼。所以我们不会指望梯度下降找到全局最优,而是接受「找到一个够好的局部解」。这也是为什么本讲花大力气讲无梯度的随机优化。

常见误区

「确定性动力学 ⇒ 开环最优」这个论断只在初始状态已知且模型完全正确时成立。真实情况下模型有误差、执行有噪声,即使物理系统本身是确定性的,开环执行照样会漂掉。所以后面即使是确定性模型,我们也照样用 MPC 重规划。

3. 随机动力学:开环为什么必然次优

现在把 $f$ 换成随机转移 $p(\mathbf{s}_{t+1}|\mathbf{s}_t,\mathbf{a}_t)$。给定一串动作,轨迹分布是

$$ p_\theta(\mathbf{s}_1,\dots,\mathbf{s}_T\mid \mathbf{a}_1,\dots,\mathbf{a}_T)=p(\mathbf{s}_1)\prod_{t=1}^{T}p(\mathbf{s}_{t+1}\mid\mathbf{s}_t,\mathbf{a}_t) $$

开环规划要解的问题变成:

$$ \mathbf{a}_1,\dots,\mathbf{a}_T=\argmax_{\mathbf{a}_1,\dots,\mathbf{a}_T}\ \E\left[\sum_t r(\mathbf{s}_t,\mathbf{a}_t)\ \Big|\ \mathbf{a}_1,\dots,\mathbf{a}_T\right] $$

Levine 在这里问了一句:这为什么是次优的?答案藏在条件里——期望是在给定整串动作的条件下取的,也就是说,$\mathbf{a}_5$ 必须在还没看到 $\mathbf{s}_2,\mathbf{s}_3,\mathbf{s}_4$ 的时候就定死。而最优决策显然应该依赖于中途观测到的状态。

Levine 的考试比喻

他讲课时用的例子很传神:明天你要参加一场考试,考题可能是数学题也可能是化学题,各 50%。闭环的情形是:你明天到考场,看到题目再作答——你只要两门都会,就能拿满分。开环的情形是:今天就要把答案写好交上去,明天直接照抄。你只能赌一门,期望得分立刻腰斩。注意这里两种情形的「能力」完全一样,差的只是信息何时到达。

一个可以算出来的两步 MDP

把上面的比喻写成数字。设 $t=1$ 时只有一个状态 $\mathbf{s}_1$ 和一个无关紧要的动作;转移到 $t=2$ 时,以概率 $0.5$ 进入 $\mathbf{s}_2^A$(数学题),以概率 $0.5$ 进入 $\mathbf{s}_2^B$(化学题),且与动作无关。第二步有两个动作 $\alpha,\beta$:

$a_2=\alpha$$a_2=\beta$
$\mathbf{s}_2^A$(概率 0.5)$r=10$$r=0$
$\mathbf{s}_2^B$(概率 0.5)$r=0$$r=10$

开环最优:$\mathbf{a}_2$ 只能是常数。选 $\alpha$ 得 $0.5\times10+0.5\times0=5$;选 $\beta$ 同理得 $5$。开环最优值 $J^{\text{open}}=5$。

闭环最优:$\pi(\mathbf{a}_2|\mathbf{s}_2)$ 可以在 $\mathbf{s}_2^A$ 选 $\alpha$、在 $\mathbf{s}_2^B$ 选 $\beta$,得 $0.5\times10+0.5\times10=10$。闭环最优值 $J^{\text{closed}}=10$。

整整差了 2 倍,而且这个差距完全来自「决策时能不能看到状态」,跟模型准不准毫无关系。把这个结构串联 $T$ 步,差距会随 $T$ 指数级放大。

闭环与开环的通信结构对比图
「loop」到底指什么。左边闭环:智能体按 $\mathbf{a}_t\sim\pi(\mathbf{a}_t|\mathbf{s}_t)$ 发动作,世界回传 $\mathbf{s}_t$,信息在两者之间循环往复形成闭合回路。右边开环:世界只在 $t=1$ 时告诉智能体 $\mathbf{s}_1$,之后智能体单向地把整串 $\mathbf{a}_1,\dots,\mathbf{a}_T$ 推出去,回路是断开的。红字点出关键——「只在 $t=1$ 发送一次,之后就是单向的了」。

闭环情形下我们优化的是策略而不是动作序列,轨迹分布里多了 $\pi$ 这一项:

$$ p(\mathbf{s}_1,\mathbf{a}_1,\dots,\mathbf{s}_T,\mathbf{a}_T)=p(\mathbf{s}_1)\prod_{t=1}^{T}\pi(\mathbf{a}_t|\mathbf{s}_t)\,p(\mathbf{s}_{t+1}|\mathbf{s}_t,\mathbf{a}_t),\qquad \pi=\argmax_{\pi}\ \E_{\tau\sim p(\tau)}\left[\sum_t r(\mathbf{s}_t,\mathbf{a}_t)\right] $$
开环规划闭环策略
决策对象动作序列 $\mathbf{a}_{1:T}$函数 $\pi(\mathbf{a}|\mathbf{s})$
随机环境下严格次优可达最优
是否需要训练否,现场求解是,离线优化参数
换初始状态要重解直接复用
典型方法random shooting、CEM、LQR、MCTS策略梯度、Q 学习、actor-critic
与模型误差的关系误差沿 $H$ 步累积误差在训练时被平均掉一部分

那既然闭环这么好,为什么本讲 Part 1 还要花这么多篇幅讲开环?两个理由:一是开环规划不需要任何训练,模型一更新立刻就能用,这在数据极少的真实机器人场景里价值巨大;二是配上 MPC 重规划之后,开环规划在实践中就变成了闭环控制——每一步的开环解都基于最新的真实状态。这是第 5 节的主题。

直觉

把开环规划想成「闭眼睛把整套动作背下来再做」,闭环策略想成「睁着眼睛边看边做」。MPC 则是一个折中的怪招:每睁一次眼,就重新闭眼把剩下的动作背一遍,但只做第一个。虽然每次规划都是「闭眼」的,但因为睁眼极其频繁,整体行为几乎与闭环无异。

4. 随机优化:random shooting 与 CEM

现在把规划问题彻底抽象掉。不管你是确定性开环、随机开环还是别的什么,最后都归结为:找一个向量 $\mathbf{A}=(\mathbf{a}_1,\dots,\mathbf{a}_H)$ 使某个黑箱函数最大。

$$ \mathbf{A}=\argmax_{\mathbf{A}}J(\mathbf{A}) $$

Levine 特意在 $J$ 底下标了一句「don't care what this is」——我们只需要能求值(在模型里 rollout 一遍算总奖励),完全不需要梯度。这一节的方法全是无梯度的。

随机优化的抽象形式与 random shooting 方法
把最优控制/规划整个抽象成 $\mathbf{A}=\argmax_\mathbf{A}J(\mathbf{A})$,其中 $\mathbf{A}$ 是整条动作序列。最朴素的解法「猜与验证(guess & check)」,在控制文献里叫随机打靶法(random shooting method):① 从某个分布(例如动作空间上的均匀分布)里独立采 $N$ 条候选序列 $\mathbf{A}_1,\dots,\mathbf{A}_N$;② 全部在模型里 rollout 算出 $J(\mathbf{A}_i)$,取 $\argmax_i J(\mathbf{A}_i)$。

random shooting 能有多差?算一算

假设「足够好的动作序列」在采样分布下占的概率质量是 $p$。采 $N$ 个独立样本至少命中一次的概率是 $1-(1-p)^N$。要达到 $90\%$ 的命中率,需要

$$ N\ \ge\ \frac{\log 0.1}{\log(1-p)}\ \approx\ \frac{2.30}{p}\quad (p\ \text{很小时}) $$

若 $p=10^{-3}$,$N\approx 2300$,还能接受。问题在于 $p$ 随维度指数衰减:假设动作序列有 $d$ 个标量分量,每个分量都必须落在自己取值范围的某个 $10\%$ 区间里才算「好」,那么 $p=0.1^{d}$。$d=4$ 时 $p=10^{-4}$,$N\approx2.3\times10^4$,勉强可行;$d=10$ 时 $p=10^{-10}$,$N\approx2.3\times10^{10}$,彻底没戏。

而动作序列的维度是 $d=\dim(\mathbf{a})\times H$。一个 6 自由度机械臂、规划时域 $H=30$,就是 $d=180$。这就是「非常苛刻的维度上限」的来历——纯随机打靶实际能对付的通常只有几十维,也就是低维动作 + 短时域。

CEM:把采样分布也一起优化

random shooting 的浪费之处是:它采完一轮就丢掉了所有信息,下一轮还从同一个均匀分布采。交叉熵方法(cross-entropy method, CEM)的想法是把采样分布 $p(\mathbf{A})$ 本身迭代地往好区域挪。

CEM 算法四步与精英重拟合示意图
CEM 的核心是把 random shooting 第一步里那个「从某个分布采样」画红圈标出来——分布本身可以被优化。图中横轴是候选 $\mathbf{A}$,竖线是各候选的 $J$ 值(橙色为淘汰者、绿色为精英),两条钟形曲线是前后两轮的高斯采样分布:新分布明显向精英聚集的位置收窄并平移。右侧说明实践中一般用高斯分布,并提到 CMA-ES 相当于「带动量的 CEM」。

连续动作下的 CEM 就四步,反复迭代:

cross-entropy method (continuous):
  1. 从 p(A) 采样 A_1, ..., A_N
  2. 求值 J(A_1), ..., J(A_N)
  3. 取值最高的 M 个精英 A_{i_1}, ..., A_{i_M}   (M < N)
  4. 用这 M 个精英重新拟合 p(A),回到第 1 步

「重新拟合」在高斯情形下就是求精英的样本均值和样本(对角)方差:

$$ \mu\leftarrow\frac{1}{M}\sum_{j=1}^{M}\mathbf{A}_{i_j},\qquad \sigma^2\leftarrow\frac{1}{M}\sum_{j=1}^{M}(\mathbf{A}_{i_j}-\mu)^2 $$
推导

为什么这个算法叫「交叉熵」?CEM 原本是从稀有事件估计推出来的。设想我们想让采样分布 $p$ 尽量贴近「最优解附近」的目标分布 $q^\ast(\mathbf{A})\propto \mathbb{1}[J(\mathbf{A})\ge\gamma_k]$($\gamma_k$ 是第 $k$ 轮精英的阈值,也就是第 $M$ 名的分数)。用交叉熵(等价于 KL 散度)作为距离:

$$ \min_{p}\ \KL\big(q^\ast\,\|\,p\big)=\min_p\ \E_{q^\ast}[\log q^\ast]-\E_{q^\ast}[\log p(\mathbf{A})] \ \Longleftrightarrow\ \max_p\ \E_{q^\ast}\big[\log p(\mathbf{A})\big] $$

第一项与 $p$ 无关可丢掉。用从上一轮 $p$ 采出的、且满足 $J\ge\gamma_k$ 的样本(正是那 $M$ 个精英)做蒙特卡洛近似,就得到 $\max_p\frac{1}{M}\sum_j\log p(\mathbf{A}_{i_j})$——这正是在精英集合上做极大似然估计。对高斯而言,MLE 的闭式解就是样本均值和样本方差。所以第 4 步「refit to elites」并不是随手的启发式,而是一个 KL 最小化步骤。

典型超参:$N=200\sim1000$ 条候选,$M$ 取 $N$ 的 $10\%$,迭代 $3\sim8$ 轮。CEM 相比 random shooting 的提升是实打实的,但它没有改变维度上限的本质——只是把可行维度从十几维推到几十维。原因很简单:对角高斯无法表达动作维度之间的相关性,而好的轨迹恰恰高度相关(比如「先加速再刹车」)。CMA-ES 通过维护完整协方差矩阵和演化路径(相当于动量)部分缓解了这一点,代价是 $\mathcal{O}(d^2)$ 的存储和 $\mathcal{O}(d^3)$ 的分解。

规划方法需要梯度动作空间典型可行维度并行友好备注
random shooting否连续/离散~十几维极好三行代码,可作 baseline
CEM否连续~几十维极好模型基 RL 的默认选择
CMA-ES否连续~上百维好带协方差与动量的 CEM
MCTS否离散为主—中等树搜索,AlphaGo/MuZero 的骨架
LQR / iLQR / DDP是(需一二阶导)连续很高差(串行)轨迹优化,要求动力学光滑
RRT 等运动规划否连续高中等擅长几何约束、避障

随机优化类方法(random shooting / CEM)的账很好算:优点——① 并行化后极快,$N$ 条候选可以在 GPU 上一个 batch 全推完,$N=1000$、$H=30$ 也不过是 30 次前向;② 极其简单,没有梯度、没有二阶导、对 $f$ 和 $r$ 的形式毫无要求,甚至可以是不可微的仿真器。缺点——① 维度上限苛刻;② 只能做开环规划。这两条缺点分别由 MPC(第 5 节)和策略学习(第 8 节起)来补。

5. MPC:用重规划把开环变成闭环

模型预测控制(model predictive control, MPC)是本讲最重要的一个工程思想,可惜它简单到经常被低估。它只做一件事:规划长度 $H$ 的动作序列,但只执行第一个,然后在新观测到的状态上从头再规划一次。

MPC 控制循环(版本 1.5 的第 3~5 步):
  while True:
      A = plan(model, s_now, horizon=H)     # 用 CEM 解一次开环问题
      a = A[0]                              # 只取第一个动作
      s_next, r = env.step(a)               # 在真实世界里执行一步
      D.append((s_now, a, s_next))          # 回收真实数据
      s_now = s_next
      每 N 步: model.fit(D)                  # 重训模型

为什么这么小的改动能起这么大作用?分三层看。

第一层:它把开环变成了事实上的闭环。回顾第 3 节那个两步 MDP:开环之所以只能拿 5 分,是因为 $\mathbf{a}_2$ 必须在看到 $\mathbf{s}_2$ 之前定死。MPC 里 $\mathbf{a}_2$ 是在真正到达 $\mathbf{s}_2$ 之后、基于 $\mathbf{s}_2$ 重新规划出来的,所以能拿满 10 分。虽然每一次调用规划器解的都是开环问题,但由于 $\mathbf{a}_2$ 实际使用的那一版是以 $\mathbf{s}_2$ 为初始状态求出来的,整体行为等价于一个(计算量很大的)闭环策略 $\pi_{\text{MPC}}(\mathbf{a}|\mathbf{s})=\big[\text{plan}(\mathbf{s})\big]_1$。

第二层:它把模型误差的累积窗口压到了一步。假设模型每步引入的状态误差量级是 $\epsilon$。开环执行 $H$ 步,末端误差是 $\mathcal{O}(\epsilon H)$ 甚至更糟(第 9 节细算)。MPC 每步都用真实状态把预测误差清零重置,所以实际执行轨迹上的误差始终是 $\mathcal{O}(\epsilon)$ 量级。代价是规划出来的「后 $H-1$ 步」永远不会被真的执行,它们只起「让第一步的选择考虑到长远后果」的作用——所以后面几步不准也无所谓。

第三层:它让「差模型」变得可用。这是 Levine 反复强调的实践观察:模型基 RL 在真实机器人上能工作,很大程度上不是因为模型学得多好,而是因为 MPC 极其宽容。一个只在一步预测上靠谱的模型,配上每步重规划,就足以完成相当复杂的操作任务。

真实机器人上的模型基 RL:多指灵巧手在盒子里操作物体
模型基 RL 在真实世界的代表性例子(Nagabandi et al. 2019, Deep Dynamics Models for Learning Dexterous Manipulation):一只多指灵巧手在真实硬件上完成物体重定向等操作任务。它的配方几乎就是本讲版本 1.5 加上第 6 节的集成模型——学一个概率集成动力学模型,用 CEM/MPPI 做 MPC,每步重规划、每隔一段时间用新收集的真实数据重训模型。这类任务用无模型方法在真机上采样开销高到不现实,而模型基方法只用几小时真机数据就能跑通。

三个必须知道的工程细节

(1)热启动(warm start)。每步都从零开始跑 CEM 太浪费。标准做法是把上一步规划出的 $\mu=(\mathbf{a}_1,\dots,\mathbf{a}_H)$ 左移一格,末尾补零或补随机值,作为这一步 CEM 的初始均值。因为相邻两步的最优计划高度相似,热启动往往能把 CEM 迭代轮数从 8 轮降到 2~3 轮。

(2)规划时域 $H$ 的取舍。$H$ 太短会短视(比如为了避免立刻的负奖励而错过后面的大奖励);$H$ 太长则(a)搜索维度 $\dim(\mathbf{a})\times H$ 线性增长,CEM 效率崩塌;(b)模型在深处的预测已经完全不可信,规划器会去优化纯噪声。实践中 $H$ 通常取 $10\sim30$ 步。一个更好的做法是用学到的值函数截断:$J=\sum_{t=1}^{H}\gamma^{t-1}r(\mathbf{s}_t,\mathbf{a}_t)+\gamma^{H}V(\mathbf{s}_{H+1})$,让值函数负责 $H$ 步之后的一切。这是 TD-MPC、MuZero 这类方法的核心结构,也是把 Part 1(规划)和 Part 2(学策略)缝在一起的最自然接口。

(3)奖励函数从哪来。规划需要能对任意 $(\mathbf{s},\mathbf{a})$ 求 $r$。很多机器人任务的奖励是我们自己写的解析函数(比如「手指尖到目标点的距离」),这时直接调用即可;否则就得像第 11 节那样额外学一个奖励模型 $\hat r_\phi(\mathbf{s},\mathbf{a})$,这时奖励模型的误差也会被规划器利用。

注意

MPC 不是万能药。它无法处理需要长期承诺的任务:如果最优行为要求「先花 50 步搭一个梯子,才能拿到墙后面的东西」,而你的规划时域只有 20 步,那么在时域内搭梯子只有代价没有收益,MPC 永远不会开始搭。这类问题必须靠值函数(把长期收益压缩进 $V$)或分层结构来解决。另外,MPC 的在线计算量是硬约束——每个控制周期要跑 $N\times H$ 次模型前向,1 kHz 的控制回路基本不可能。

6. 在模型不确定性下规划:集成与期望回报

规划器是模型误差的对手

先把问题说透。假设我们训练好了模型 $\hat f$,然后拿它去解 $\max_\mathbf{A}\hat J(\mathbf{A})$。写出真实回报和模型回报的关系:

$$ \hat J(\mathbf{A})=J(\mathbf{A})+\underbrace{\big[\hat J(\mathbf{A})-J(\mathbf{A})\big]}_{\text{模型误差}\ \delta(\mathbf{A})} $$

规划器求的是 $\argmax_\mathbf{A}\big[J(\mathbf{A})+\delta(\mathbf{A})\big]$。即便 $\delta$ 在数据分布下均值为零、方差不大,$\argmax$ 也会系统性地偏向 $\delta$ 取大正值的那些 $\mathbf{A}$。这就是「优化者的诅咒(optimizer's curse)」,在模型基 RL 里叫 model exploitation(模型利用/模型套利)。

数值感受一下这个偏差有多大:设有 $N$ 个候选动作序列,真实回报都相等($J\equiv 0$),模型误差 $\delta_i\sim\mathcal{N}(0,\sigma^2)$ 独立。则 $\E[\max_i\delta_i]\approx\sigma\sqrt{2\ln N}$。取 $\sigma=1$、$N=1000$,期望的「幻觉收益」约为 $3.7$;$N=10^6$ 时约为 $5.3$。你搜索得越狠,被模型骗得越惨——这是模型基 RL 里最反直觉、也最重要的一条定律。它意味着单纯提升规划器的优化能力,反而可能让性能下降。

两种不确定性,只有一种有用

上一讲区分过两类不确定性,这里再压缩复述一遍,因为它直接决定了本节的算法设计。

偶然不确定性 aleatoric / statistical认知不确定性 epistemic / model
来源环境本身随机(掷骰子、传感器噪声)数据不够,我们不知道该信哪个模型
数学位置$p(\mathbf{s}'|\mathbf{s},\mathbf{a},\theta)$ 的熵$p(\theta|\mathcal{D})$ 的熵
数据变多会不变趋于零
能否防 model exploitation不能能

为什么「输出熵」(网络直接吐出的高斯方差)不够?因为在数据分布之外,网络对自己的错误答案非常自信:它会以很小的方差预测一个完全离谱的下一状态。这正是「模型对数据很确定,但我们对模型不确定」这句话的含义。要捕捉后者,我们必须表达 $p(\theta|\mathcal{D})$。

做法一:贝叶斯神经网络

把每个权重看成随机变量,用变分推断近似后验。最常见的平均场近似把后验分解成各权重独立的高斯:

$$ p(\theta\mid\mathcal{D})\ \approx\ \prod_i \mathcal{N}\big(\mu_i,\ \sigma_i^2\big) $$

其中 $\mu_i$ 是权重的期望值、$\sigma_i$ 刻画我们对这个权重有多不确定。训练时用重参数化技巧采样权重、优化 ELBO(Blundell et al. 的 Bayes-by-Backprop;Concrete Dropout 则用可学习的 dropout 率隐式实现类似效果)。理论上漂亮,实践中调参痛苦、方差估计偏保守,而且平均场假设忽略了权重之间的强相关性。

做法二:bootstrap 集成(实践中的胜者)

用 $N$ 个独立训练的模型 $\theta_1,\dots,\theta_N$ 构成一个粒子近似:

$$ p(\theta\mid\mathcal{D})\ \approx\ \frac{1}{N}\sum_{i=1}^{N}\delta(\theta-\theta_i) \quad\Longrightarrow\quad \int p(\mathbf{s}'|\mathbf{s},\mathbf{a},\theta)\,p(\theta|\mathcal{D})\,d\theta\ \approx\ \frac{1}{N}\sum_{i=1}^{N}p(\mathbf{s}'|\mathbf{s},\mathbf{a},\theta_i) $$

关键是让这 $N$ 个模型「独立」。经典 bootstrap 的做法是从 $\mathcal{D}$ 有放回地重采样 $N$ 个数据集 $\mathcal{D}_i$(每个大小仍为 $|\mathcal{D}|$,平均包含约 $63.2\%$ 的不重复样本)分别训练。深度学习里有两个实用观察:

  • 模型个数通常很小($N\lt 10$,常见 $5\sim7$),所以这是对后验非常粗糙的近似;但它对 model exploitation 的抑制作用已经够用。
  • 有放回重采样常常不是必需的——随机初始化 + SGD 的随机性(不同的 minibatch 顺序)已经让不同成员足够不同。省掉重采样还能让每个成员用上全部数据。

用集成做规划:对回报取平均,不是对预测取平均

在不确定性下规划:从对单模型求和改为对模型集合求平均回报
规划目标的改写。上方「Before」是确定性单模型:$J(\mathbf{a}_1,\dots,\mathbf{a}_H)=\sum_{t=1}^{H}r(\mathbf{s}_t,\mathbf{a}_t)$,其中 $\mathbf{s}_{t+1}=f(\mathbf{s}_t,\mathbf{a}_t)$。下方「Now」是集成版本:$J=\frac1N\sum_{i=1}^{N}\sum_{t=1}^{H}r(\mathbf{s}_{t,i},\mathbf{a}_t)$,其中 $\mathbf{s}_{t+1,i}=f_i(\mathbf{s}_{t,i},\mathbf{a}_t)$——注意每个成员 $i$ 维护自己独立的一条状态轨迹。箭头注释点明这是「一个关于确定性模型的分布」。再下面是完全一般的四步流程,适用于任何后验表示。

写清楚这个式子:对每个候选动作序列 $\mathbf{a}_1,\dots,\mathbf{a}_H$,

$$ J(\mathbf{a}_1,\dots,\mathbf{a}_H)=\frac{1}{N}\sum_{i=1}^{N}\sum_{t=1}^{H}r(\mathbf{s}_{t,i},\mathbf{a}_t), \qquad \mathbf{s}_{t+1,i}=f_i(\mathbf{s}_{t,i},\mathbf{a}_t),\quad \mathbf{s}_{1,i}=\mathbf{s}_1 $$

更一般地(后验不是集成、模型不是确定性时):

对每个候选动作序列 a_1, ..., a_H:
  Step 1: 采一个模型  theta ~ p(theta | D)
  Step 2: 逐步采下一状态  s_{t+1} ~ p(s_{t+1} | s_t, a_t, theta)
  Step 3: 算这条轨迹的回报  R = sum_t r(s_t, a_t)
  Step 4: 重复 Step 1~3 多次,取回报的平均值作为 J
核心结论

必须先在每个模型里 rollout 出完整轨迹、算出回报,最后再平均回报;绝不能先把各模型的预测平均成一个「平均状态」再算奖励。数学上就是 $\frac1N\sum_i r(f_i(\mathbf{s},\mathbf{a}))\ \ne\ r\!\left(\frac1N\sum_i f_i(\mathbf{s},\mathbf{a})\right)$。

数值例子:状态是一维标量,奖励 $r(s)=1-s^2$(在 $s=0$ 最好)。模型 A 预测 $s'=+1$,模型 B 预测 $s'=-1$。先平均预测:$\bar s'=0$,$r=1$——看起来是完美动作。先算奖励再平均:$\frac12\big[r(1)+r(-1)\big]=\frac12(0+0)=0$——如实反映「两个模型严重分歧,这个动作的期望结果并不好」。前者会让规划器一头扎进模型分歧最大的区域,正是我们最想避免的。

为什么期望回报能抑制 model exploitation

直觉是这样的:在数据分布内,各成员的预测几乎一致,平均后与单模型无异;在数据分布外,各成员的外推方向天差地别,于是它们的轨迹会散开到状态空间的各处。只要奖励函数不是到处都高(真实任务里高奖励区域总是很窄的),散开的轨迹里绝大多数会落在低奖励区,平均回报自然被拉低。模型分歧大的地方,期望回报自动变低——这是一个「免费」的悲观正则化项,不需要我们手写任何惩罚。

用上一讲的图景说:一个高方差的预测,其期望奖励远低于同均值的低方差预测。假设奖励在状态上呈钟形 $r(s)=\exp(-s^2/2)$,模型预测 $s'\sim\mathcal N(0,\sigma^2)$,则 $\E[r]=\frac{1}{\sqrt{1+\sigma^2}}$:$\sigma=0$ 时为 $1$,$\sigma=1$ 时降到 $0.71$,$\sigma=3$ 时只剩 $0.32$。均值完全没变,仅仅因为不确定性大,期望回报就掉了三分之二。

注意

Levine 明确列了几条 caveat:期望值 $\ne$ 悲观值,也 $\ne$ 乐观值。真正的悲观(用于离线 RL)应该取 $\min_i$ 或 $\text{mean}-\lambda\cdot\text{std}$;真正的乐观(用于探索)应该取 $\max_i$ 或 $\text{mean}+\lambda\cdot\text{std}$——而且我们确实需要探索才能把模型变好,一味悲观会卡在原地。期望值只是一个「通常不错的起点」,它足以防止最灾难性的模型套利,同时不会过度扼杀探索。真要精调,就在 $J$ 里显式加上 $-\lambda\cdot\mathrm{std}_i\big[\sum_t r(\mathbf{s}_{t,i},\mathbf{a}_t)\big]$ 这样的分歧惩罚项。

另外,除了采样近似还有 矩匹配(moment matching):不采轨迹,而是把每步的状态分布用高斯近似,解析地传播均值和协方差(PILCO 用高斯过程做的就是这件事)。它方差更小、无需大量采样,但只在分布近似高斯时才准,遇到多峰(比如「物体可能倒向左边也可能倒向右边」)就会失效。

7. 最小实现:概率集成动力学模型 + CEM-MPC

把前六节的东西拼成一份能跑的代码。三个部件:单个高斯动力学网络(捕捉偶然不确定性)、集成(捕捉认知不确定性)、CEM 规划器 + MPC 循环。

(1)单个概率动力学模型

两个细节值得强调:预测状态增量 $\Delta\mathbf{s}$ 而不是 $\mathbf{s}'$ 本身(因为相邻状态通常很接近,直接预测 $\mathbf{s}'$ 会让网络把力气全花在学恒等映射上);用高斯负对数似然而不是 MSE(这样网络能表达「这一步我不太确定」)。

import torch, torch.nn as nn, torch.nn.functional as F, numpy as np

class GaussianDynamics(nn.Module):
    """预测 p(s' | s, a) = N(s + mu(s,a), diag(exp(logvar(s,a))))"""
    def __init__(self, ds, da, hidden=256):
        super().__init__()
        self.ds = ds
        self.net = nn.Sequential(
            nn.Linear(ds + da, hidden), nn.SiLU(),
            nn.Linear(hidden, hidden),  nn.SiLU(),
            nn.Linear(hidden, 2 * ds))
        # 可学习的 logvar 上下界(PETS 的做法),防止方差塌到 0 或爆炸
        self.max_logvar = nn.Parameter(torch.full((ds,),  0.5))
        self.min_logvar = nn.Parameter(torch.full((ds,), -10.0))

    def forward(self, s, a):
        out = self.net(torch.cat([s, a], dim=-1))
        delta, logvar = out[..., :self.ds], out[..., self.ds:]
        # 软裁剪:保证 min_logvar < logvar < max_logvar 且处处可导
        logvar = self.max_logvar - F.softplus(self.max_logvar - logvar)
        logvar = self.min_logvar + F.softplus(logvar - self.min_logvar)
        return s + delta, logvar

    def nll(self, s, a, s_next):
        mu, logvar = self(s, a)
        inv_var = torch.exp(-logvar)
        # 高斯 NLL(去掉常数项):((mu-y)^2 / var + log var)
        loss = ((mu - s_next) ** 2 * inv_var + logvar).mean()
        # 轻微惩罚,把上下界拉紧
        return loss + 0.01 * (self.max_logvar.sum() - self.min_logvar.sum())

(2)bootstrap 集成

class Ensemble(nn.Module):
    def __init__(self, n_models, ds, da):
        super().__init__()
        self.members = nn.ModuleList([GaussianDynamics(ds, da)
                                      for _ in range(n_models)])
        self.n = n_models

    def fit(self, S, A, S2, epochs=40, bs=256, lr=1e-3, bootstrap=True):
        N = S.shape[0]
        for m in self.members:
            opt = torch.optim.Adam(m.parameters(), lr=lr)
            # 每个成员用一份自己的(有放回重采样的)数据集
            idx = torch.randint(0, N, (N,)) if bootstrap else torch.arange(N)
            for _ in range(epochs):
                perm = idx[torch.randperm(N)]
                for k in range(0, N, bs):
                    b = perm[k:k + bs]
                    loss = m.nll(S[b], A[b], S2[b])
                    opt.zero_grad(); loss.backward(); opt.step()

    @torch.no_grad()
    def step(self, s, a, member):
        """在第 member 个模型里往前走一步(采样,而非取均值)"""
        mu, logvar = self.members[member](s, a)
        return mu + torch.randn_like(mu) * torch.exp(0.5 * logvar)

(3)CEM 规划器:对回报取平均

注意内层的双重循环——外层遍历集成成员、内层遍历时间步,每个成员维护自己那份 $N$ 条候选轨迹的状态;奖励在成员内累加,最后才除以成员数。这就是第 6 节强调的「先算回报再平均」。

@torch.no_grad()
def cem_plan(model, s0, reward_fn, a_dim, H=15, N=400, n_elite=40,
             iters=5, a_low=-1.0, a_high=1.0, mu_init=None):
    dev = s0.device
    mu  = torch.zeros(H, a_dim, device=dev) if mu_init is None else mu_init.clone()
    std = torch.full((H, a_dim), 0.5 * (a_high - a_low), device=dev)

    for _ in range(iters):
        # 1) 从当前高斯采 N 条候选动作序列
        A = (mu + std * torch.randn(N, H, a_dim, device=dev)).clamp(a_low, a_high)

        # 2) 每条候选在每个集成成员里各推演一遍,回报累加后取平均
        J = torch.zeros(N, device=dev)
        for m in range(model.n):
            s = s0.unsqueeze(0).expand(N, -1).contiguous()
            for t in range(H):
                J = J + reward_fn(s, A[:, t])
                s = model.step(s, A[:, t], m)
        J = J / model.n

        # 3) 取精英,重拟合高斯(即在精英集上做 MLE)
        elite = A[J.topk(n_elite).indices]
        mu, std = elite.mean(dim=0), elite.std(dim=0) + 1e-6

    return mu          # mu[0] 是本步要执行的动作

(4)MPC 主循环(版本 1.5)

def mbrl_loop(env, model, reward_fn, a_dim, total_steps=20000, refit_every=1000):
    S, A, S2 = [], [], []
    # 第 1 步:随机策略采一批种子数据
    s = env.reset()
    for _ in range(1000):
        a = env.action_space.sample()
        s2, _, done, _ = env.step(a)
        S.append(s); A.append(a); S2.append(s2)
        s = env.reset() if done else s2
    to_t = lambda x: torch.as_tensor(np.array(x), dtype=torch.float32)
    model.fit(to_t(S), to_t(A), to_t(S2))

    s, warm = env.reset(), None
    for step in range(total_steps):
        plan = cem_plan(model, to_t(s), reward_fn, a_dim, mu_init=warm)
        a = plan[0].cpu().numpy()
        s2, r, done, _ = env.step(a)               # 只执行第一个动作
        S.append(s); A.append(a); S2.append(s2)    # 回收真实转移
        # 热启动:把计划左移一格,末尾补零,作为下一步 CEM 的初值
        warm = torch.cat([plan[1:], torch.zeros(1, a_dim)], dim=0)
        s, warm = (env.reset(), None) if done else (s2, warm)
        if (step + 1) % refit_every == 0:          # 每 N 步重训模型
            model.fit(to_t(S), to_t(A), to_t(S2))

这份代码就是 PETS(Chua et al. 2018)的骨架。真正跑起来还需要补的东西:状态/动作的输入归一化(对模型精度影响极大)、若奖励未知则再训一个 $\hat r_\phi$、以及把成员内层循环向量化成一次 batched 前向(把 $N$ 条候选 $\times$ $N_{\text{model}}$ 个成员合并成一个大 batch,GPU 上能快一个数量级)。

直觉

model.step 里为什么要采样而不是直接返回均值?两个原因:一是这样才如实反映了偶然不确定性(环境本身的随机性);二是均值传播在多步之后会系统性地偏向「分布的中心」,让轨迹显得比实际更平滑、更可控,规划器于是高估自己的控制能力。PETS 还有一个更细的技巧叫 TS∞(trajectory sampling):让同一条候选轨迹在整个 $H$ 步里始终使用同一个集成成员(正如上面的代码),而不是每步重新抽一个成员——后者会把认知不确定性错误地当成偶然噪声给「平均掉」。

8. 用模型学策略:pathwise 梯度 vs 策略梯度

Part 2 换一条路:不再每步现场规划,而是把模型当作可微(或可采样)的模拟器,训练一个闭环策略 $\pi_\theta(\mathbf{a}_t|\mathbf{s}_t)$。目标就是第 3 节写过的闭环目标:

$$ \theta^\star=\argmax_\theta\ \E_{\tau\sim p_\theta(\tau)}\left[\sum_t r(\mathbf{s}_t,\mathbf{a}_t)\right],\qquad p_\theta(\tau)=p(\mathbf{s}_1)\prod_{t}\pi_\theta(\mathbf{a}_t|\mathbf{s}_t)\,\hat p_\phi(\mathbf{s}_{t+1}|\mathbf{s}_t,\mathbf{a}_t) $$

唯一的区别是转移用的是学到的 $\hat p_\phi$。这样一来,采样几乎免费——不用碰真实机器人,在 GPU 上想采多少条采多少条。剩下的问题只有一个:怎么对 $\theta$ 求梯度?有两个截然不同的答案。

策略梯度估计式与反传(pathwise)梯度的完整链式法则展开
同一个目标的两种梯度。上面是策略梯度(似然比估计量):$\nabla_\theta J(\theta)\approx\frac1N\sum_{i=1}^{N}\sum_{t=1}^{H}\nabla_\theta\log\pi_\theta(\mathbf{a}_{i,t}|\mathbf{s}_{i,t})\hat Q^\pi_{i,t}$——只需要对策略的对数概率求导,模型只被当作黑箱采样器。下面是反传(pathwise)梯度:把链式法则一路穿过动力学展开,出现了对时间的连乘。底部两条要点:如果样本足够多,策略梯度可能更稳定,因为它不需要连乘一大堆雅可比矩阵;并给出 Parmas et al. '18 (PIPP) 作为深入分析的参考。

做法一:pathwise(反传)梯度 —— 版本 2.0

如果 $\hat f_\phi$、$r$、$\pi_\theta$ 全都可微(策略的随机性用重参数化技巧处理,$\mathbf{a}_t=\pi_\theta(\mathbf{s}_t,\xi_t)$,$\xi_t$ 是与 $\theta$ 无关的噪声),那整条 rollout 就是一个巨大的可微计算图,直接 loss.backward() 即可。展开写出来是:

$$ \nabla_\theta J(\theta)=\sum_{t=1}^{H}\frac{d\mathbf{a}_t}{d\theta}\frac{d\mathbf{s}_{t+1}}{d\mathbf{a}_t} \left(\sum_{t'=t+1}^{H}\frac{dr_{t'}}{d\mathbf{s}_{t'}} \prod_{t''=t+2}^{t'}\left(\frac{d\mathbf{s}_{t''}}{d\mathbf{a}_{t''-1}}\frac{d\mathbf{a}_{t''-1}}{d\mathbf{s}_{t''-1}}+\frac{d\mathbf{s}_{t''}}{d\mathbf{s}_{t''-1}}\right)\right) $$
推导

逐项解释这个式子怎么来的。$\theta$ 影响 $J$ 的路径是:$\theta\to\mathbf{a}_t\to\mathbf{s}_{t+1}\to(\mathbf{a}_{t+1},\mathbf{s}_{t+2})\to\cdots\to r_{t'}$。

  • 最外层 $\sum_{t=1}^{H}$:$\theta$ 在每个时间步都产生一次动作,每次都是一条独立的影响路径,梯度全部相加。
  • $\frac{d\mathbf{a}_t}{d\theta}\frac{d\mathbf{s}_{t+1}}{d\mathbf{a}_t}$:参数先影响动作,动作再影响下一状态。这里的第二项就是动力学模型的雅可比。
  • 中间的 $\sum_{t'=t+1}^{H}\frac{dr_{t'}}{d\mathbf{s}_{t'}}$:状态被扰动后,会影响它之后所有时刻的奖励,所以对未来所有 $t'$ 求和。
  • 最内层的连乘 $\prod_{t''=t+2}^{t'}$:这是「扰动从 $\mathbf{s}_{t+1}$ 传播到 $\mathbf{s}_{t'}$」的转移矩阵。每传播一步有两条通路并联:直接通过动力学 $\frac{d\mathbf{s}_{t''}}{d\mathbf{s}_{t''-1}}$(状态本身的自然演化),以及绕道策略 $\frac{d\mathbf{s}_{t''}}{d\mathbf{a}_{t''-1}}\frac{d\mathbf{a}_{t''-1}}{d\mathbf{s}_{t''-1}}$(状态变了 → 策略选的动作变了 → 下一状态变了)。两条并联所以相加,然后逐步相乘。

换句话说,把每步的「闭环雅可比」记作 $\mathbf{M}_{t}=\frac{\partial \mathbf{s}_{t+1}}{\partial\mathbf{s}_{t}}+\frac{\partial\mathbf{s}_{t+1}}{\partial\mathbf{a}_{t}}\frac{\partial\mathbf{a}_{t}}{\partial\mathbf{s}_{t}}$,则梯度里出现的是 $\mathbf{M}_{t'-1}\mathbf{M}_{t'-2}\cdots\mathbf{M}_{t+1}$ 这样的矩阵连乘。

为什么这个梯度会炸

矩阵连乘的病理和训练 RNN 时的梯度爆炸/消失一模一样。设 $\mathbf{M}_t$ 的谱半径大致是 $\lambda$,则 $H$ 步之后梯度的量级是 $\lambda^{H}$:

$\lambda$(每步闭环增益)$H=20$$H=50$$H=100$
$1.1$(轻微不稳定)$6.7$$1.2\times10^{2}$$1.4\times10^{4}$
$1.3$$1.9\times10^{2}$$5.0\times10^{5}$$2.5\times10^{11}$
$0.9$(收敛系统)$0.12$$5.2\times10^{-3}$$2.7\times10^{-5}$

更糟的是,$\lambda$ 通常在轨迹的不同段落取不同值:接触发生的瞬间(脚落地、手指碰到物体)雅可比会突然变得巨大甚至不连续,而在自由飞行段几乎是 $1$。所以梯度既爆炸又消失,还在同一条轨迹里混着来。这就是 Parmas et al. 说的「混沌的诅咒(curse of chaos)」——真实物理系统对初值敏感,$\frac{d\mathbf{s}_{t'}}{d\mathbf{s}_{t}}$ 本身就随 $t'-t$ 指数发散。

常见误区

「这不就是 BPTT 吗?RNN 里我们用 LSTM/GRU 和梯度裁剪就解决了。」——不行。RNN 的转移矩阵是我们自己的参数,可以设计门控结构、可以正交初始化、可以约束谱范数。而这里的 $\mathbf{M}_t$ 是物理系统(或拟合它的模型)强加给我们的,我们没有任何设计自由度:真实的倒立摆就是不稳定的,$\lambda$ 就是大于 1。梯度裁剪能防止数值溢出,但裁剪后的方向已经不是真实梯度方向了。这也是为什么 Levine 说「如果样本够多,策略梯度可能更稳定」——注意他用的是「可能」,这不是定理,是经验判断。

做法二:在模型里跑策略梯度 —— 版本 2.5

既然连乘雅可比是病根,那就用完全不需要模型雅可比的估计量。策略梯度(似然比 / REINFORCE 估计量)正是如此:

$$ \nabla_\theta J(\theta)\approx\frac{1}{N}\sum_{i=1}^{N}\sum_{t=1}^{H}\nabla_\theta\log\pi_\theta(\mathbf{a}_{i,t}|\mathbf{s}_{i,t})\,\hat Q^{\pi}_{i,t} $$

这里唯一的求导对象是 $\log\pi_\theta$,模型只负责产生样本——它甚至可以是不可微的、离散的、带 if-else 的。$\hat Q^\pi_{i,t}=\sum_{t'\ge t}\gamma^{t'-t}r_{i,t'}$ 只是一堆数字,不参与求导。于是就有了:

模型基 RL 版本 2.5:在学到的模型里用策略梯度优化策略
版本 2.5 的完整算法框:① 用基准策略 $\pi_0$(比如随机策略)收集 $\mathcal{D}=\{(\mathbf{s},\mathbf{a},\mathbf{s}')_i\}$;② 学动力学模型 $f(\mathbf{s},\mathbf{a})$ 最小化 $\sum_i\|f(\mathbf{s}_i,\mathbf{a}_i)-\mathbf{s}_i'\|^2$;③ 用 $f$ 配合当前策略 $\pi_\theta$ 生成轨迹 $\{\tau_i\}$;④ 用这些轨迹以策略梯度改进 $\pi_\theta$;⑤ 在真实环境里运行 $\pi_\theta$,把访问到的 $(\mathbf{s},\mathbf{a},\mathbf{s}')$ 追加进 $\mathcal{D}$。左侧绿色箭头显示两层循环:内层 ③④ 反复迭代(在同一个模型里多次改进策略),外层 ②→⑤ 每轮重训模型。底部黄框留了个问题:这个做法有什么潜在问题?——答案就是下一节的长 rollout 误差累积。
pathwise(反传)梯度策略梯度(似然比)
是否需要 $\hat f$ 可微需要不需要(只需能采样)
梯度的偏差无偏(对模型而言)无偏(对模型而言)
方差通常较低……较高,需要大 $N$ 压方差
数值稳定性差:$H$ 个雅可比连乘好:不含雅可比连乘
对时域 $H$ 的敏感度指数级恶化方差随 $H$ 大致线性/平方增长
对不连续动力学(接触)崩溃照常工作
样本效率(在模型内)高低,但模型采样便宜

「方差通常较低」那一栏之所以打省略号,是因为这是低维光滑问题上的经验;一旦系统混沌,pathwise 梯度的方差反而会爆掉——Parmas et al. 的 PIPP 正是分析并混合这两种估计量的工作。

直觉

一个记忆点:pathwise 梯度问「如果我把动作往这个方向挪一点点,回报会怎么变」;策略梯度问「哪些采出来的动作恰好回报高,就提高它们的概率」。前者需要精确知道系统的局部导数结构(模型必须在导数层面也准确,这比在值层面准确难得多);后者只需要模型在分布层面大致对。学到的神经网络动力学模型往往预测值还行、导数却噪声很大,这是策略梯度在模型里更稳的一个实际原因。

9. 长 rollout 的诅咒与短 rollout 方案

版本 2.5 遗留的问题是:第 ③ 步「用 $f$ 生成轨迹」到底要生成多长?如果按任务时域 $T$ 生成完整轨迹,模型误差会一路累积。

训练轨迹与模型内轨迹随时间发散,误差累积为 O(epsilon H^2)
长 rollout 的诅咒。三维图的横轴是时间、纵轴是状态维度(100 维)、竖直方向是状态值。黑线是用真实动力学运行 $\pi_\theta$ 得到的轨迹,红线是用学到的模型运行同一个 $\pi_\theta$ 得到的轨迹。两者在开头几乎重合,但随时间推移逐渐分道扬镳,到后半段已经完全是两个不同的行为。右侧给出误差累积的量级:$\mathcal{O}(\epsilon H^2)$。
推导

$\mathcal{O}(\epsilon H^2)$ 是怎么来的?这与模仿学习那一讲里 behavior cloning 的 $\mathcal{O}(\epsilon T^2)$ 分析完全同构,只是「犯错的人」从策略换成了模型。

设模型的一步预测误差用总变差距离刻画:对数据分布覆盖到的任意 $(\mathbf{s},\mathbf{a})$,

$$ D_{\mathrm{TV}}\big(\hat p_\phi(\cdot|\mathbf{s},\mathbf{a}),\ p(\cdot|\mathbf{s},\mathbf{a})\big)\le\epsilon $$

第一步:分布误差随时间线性增长。记 $p_t$ 和 $\hat p_t$ 分别是真实动力学和模型在 $t$ 时刻的状态分布,起点相同 $p_1=\hat p_1$。用「一步耦合」论证:$\hat p_{t+1}$ 与 $p_{t+1}$ 的差异有两个来源——(a) 输入分布本来就不同($\hat p_t$ vs $p_t$),(b) 即使输入相同,转移核也差 $\epsilon$。由三角不等式与 TV 距离在马尔可夫核下的非扩张性:

$$ D_{\mathrm{TV}}(\hat p_{t+1},p_{t+1})\ \le\ D_{\mathrm{TV}}(\hat p_t,p_t)+\epsilon \quad\Longrightarrow\quad D_{\mathrm{TV}}(\hat p_t,p_t)\le (t-1)\epsilon $$

第二步:把分布误差换算成回报误差。设奖励有界 $|r|\le R_{\max}$。对任意有界函数,期望之差不超过 $2R_{\max}$ 乘以 TV 距离:

$$ \Big|\E_{\hat p_t}[r]-\E_{p_t}[r]\Big|\ \le\ 2R_{\max}\,D_{\mathrm{TV}}(\hat p_t,p_t)\ \le\ 2R_{\max}(t-1)\epsilon $$

第三步:对时间求和。

$$ \big|\hat J-J\big|\ \le\ \sum_{t=1}^{H}2R_{\max}(t-1)\epsilon\ =\ 2R_{\max}\epsilon\cdot\frac{H(H-1)}{2}\ =\ \mathcal{O}(\epsilon H^2) $$

数值感受:$\epsilon=0.01$(每步 1% 的分布误差,已经算相当好的模型了)、$R_{\max}=1$。$H=10$ 时误差界约 $0.9$;$H=100$ 时约 $99$——已经远超整条轨迹能拿到的总奖励 $H\cdot R_{\max}=100$,界完全失效(意味着模型内的回报估计毫无意义)。而 $H=1$ 时界是 $0$,$H=5$ 时只有 $0.2$。这就是「短 rollout」三个字的全部理由。

顺带一提,这还是乐观的估计。如果动力学有 Lipschitz 常数 $L\gt 1$(混沌系统),误差是 $\epsilon\frac{L^{H}-1}{L-1}$,即指数级而非平方级。

三种 rollout 方案的取舍

三种模型 rollout 方式的对比:长 rollout、短 rollout、从 buffer 状态出发的短分支 rollout
三张图对应三种做法。左:从初始状态出发做完整长度的 rollout——「巨大的累积误差」。中:从初始状态出发只走很短几步——「误差低得多」,但「永远看不到靠后的时间步」,策略学不会后期该干什么。右:黑线是 replay buffer 里的真实轨迹,橙色圆点是从真实轨迹上随机挑的起点,红色短线是从这些点出发的短模型 rollout(分支 rollout, branched rollout)——「误差低得多」「所有时间步都能看到」,但「状态分布是错的」。
方案rollout 起点长度模型误差覆盖后期状态状态分布
长 rollout初始状态 $\mathbf{s}_1$$T$(完整)$\mathcal{O}(\epsilon T^2)$ 极大覆盖(但全是幻觉)正确(若模型准)
短 rollout初始状态 $\mathbf{s}_1$$k$ 很小$\mathcal{O}(\epsilon k^2)$ 很小不覆盖正确但只有开头
分支 rolloutbuffer 中任意真实状态$k$ 很小$\mathcal{O}(\epsilon k^2)$ 很小覆盖有偏(是旧策略的分布)

第三种方案的「状态分布是错的」具体指什么?我们从 buffer 里采起点,而 buffer 里的状态是历史上各个旧策略访问过的状态,不是当前 $\pi_\theta$ 的稳态分布 $d^{\pi_\theta}$。所以我们实际在优化的是一个 off-policy 的目标。这也正是为什么第 4 步必须用off-policy 的无模型算法(Q 学习、SAC 之类)而不是 on-policy 的策略梯度——策略梯度要求样本来自当前策略,而这里显然不是。

把这三点合起来就是版本 3.0:

model-based RL version 3.0:
  1. 用基准策略 pi_0(a|s) 收集数据 D = {(s, a, s')_i}
  2. 学动力学模型 f(s,a),最小化 sum_i || f(s_i,a_i) - s'_i ||^2
  3. 从 D 里挑一批状态 s_i,用 f(s,a) 从它们出发做【短】rollout
  4. 用【真实数据和模型数据一起】,以【off-policy RL】改进 pi_theta(a|s)
  5. 运行 pi_theta(a|s),把访问到的 (s, a, s') 追加进 D
     —— 内层回到第 3 步,外层回到第 2 步
核心结论

版本 3.0 用三个改动同时解决了三个问题:短 rollout 压住 $\mathcal{O}(\epsilon H^2)$ 的误差累积;从 buffer 采起点保证策略能见到所有阶段的状态;用 off-policy 算法容忍由此带来的状态分布偏差,并顺便让真实数据和模型数据可以混在一起训练。这三件事缺一不可,去掉任何一件整个方案都不成立。

10. Dyna 风格:用模型给无模型算法造数据

版本 3.0 的思想其实是 Sutton 在 1990 年就提出的 Dyna 架构的现代重述。Dyna 的一句话概括:每和真实环境交互一步,就在脑子里(模型里)多做 $K$ 步「想象中的」更新。

原始 Dyna

Sutton 的原始 Dyna 算法:在线 Q 学习加上 K 次模型内 Q 更新
Sutton 的原始 Dyna(表格式):一个「用模型做无模型 RL」的在线 Q 学习算法。① 在状态 $s$ 用探索策略选动作 $a$;② 观测真实的 $s'$ 和 $r$,得到转移 $(s,a,s',r)$;③ 用它更新模型 $\hat p(s'|s,a)$ 和 $\hat r(s,a)$;④ 用真实转移做一次 Q 更新 $Q(s,a)\leftarrow Q(s,a)+\alpha\E_{s',r}[r+\max_{a'}Q(s',a')-Q(s,a)]$;⑤ 重复 $K$ 次:⑥ 从过去访问过的状态动作缓冲 $\mathcal{B}$ 里采一个 $(s,a)$,⑦ 用模型给出的 $s',r$ 再做一次同样的 Q 更新。

这里有两个设计值得细看。

第一,第 ⑥ 步从 $\mathcal{B}$ 采样,而不是从模型里自由漫游。$\mathcal{B}$ 是「过去真的访问过的 $(s,a)$」,所以每次模型内更新的起点都是真实的。这正是上一节说的「从 buffer 采起点」。

第二,rollout 只有一步。第 ⑦ 步用模型采一个 $s'$ 就立刻做 Q 更新,不继续往下走。按上一节的分析,$k=1$ 时误差界正比于 $\epsilon\cdot 1^2$,几乎不受模型误差累积影响。那这样还有什么价值?价值在于 Q 值的信息传播:真实交互每步只能更新一个 $(s,a)$ 的 Q 值,而模型内的 $K$ 次更新可以把奖励信息迅速沿着状态空间反向扩散。在经典的 Dyna-Maze 实验里,加上 $K=50$ 次规划更新能把找到最优路径所需的真实回合数减少一个数量级以上。

一般化的 Dyna 风格配方

一般化的 Dyna 风格模型基 RL 配方八步,以及分支 rollout 示意图
把 Dyna 推广到深度 RL 的通用配方。① 收集一批真实转移 $(s,a,s',r)$;② 学模型 $\hat p(s'|s,a)$(可选地也学 $\hat r(s,a)$);③ 重复 $K$ 次:④ 从 buffer 采 $s\sim\mathcal{B}$;⑤ 选动作 $a$(可以来自 $\mathcal{B}$、来自当前策略 $\pi$、或纯随机);⑥ 用模型仿真 $s'\sim\hat p(s'|s,a)$(以及 $r=\hat r(s,a)$);⑦ 用 $(s,a,s',r)$ 做一次无模型 RL 更新;⑧(可选)在模型里再往前走 $N$ 步。右图画出这个结构:黑线是 buffer 里的真实轨迹,橙点是采出的起点,红色短线是从起点岔出去的短模型 rollout。底部两条优点:只需要极短(少至一步)的模型 rollout;同时仍然能看到多样的状态。

第 ⑤ 步「动作从哪来」是一个被低估的设计维度:

动作来源效果适合的下游算法
从 $\mathcal{B}$ 取(历史动作)完全 off-policy,最保守,模型只做「重放」Q 学习
从当前策略 $\pi_\theta$ 采on-policy 的动作分布,能评估当前策略的价值SAC / actor-critic(MBPO 的选择)
纯随机覆盖面最广,但很多动作毫无意义,样本浪费探索阶段

模型加速的 off-policy RL:完整的系统图

模型加速 off-policy RL 的五进程系统结构图
把 Dyna 塞进现代 off-policy RL 框架后的完整系统,一共五个可以异步跑的进程。进程 1(数据收集):用 $\pi(\mathbf{a}|\mathbf{s})$(如 $\epsilon$-greedy)与真实世界交互,把 $(\mathbf{s},\mathbf{a},\mathbf{s}',r)$ 存进 replay buffer,并淘汰旧数据。进程 4(模型训练):从 replay buffer 取数据训练动力学模型。进程 5(模型数据收集):以 $\mathbf{s}\sim\mathcal{B}$ 为起点,用模型生成转移,存进单独的「模型转移缓冲」。注意右上角的垃圾桶——模型一变,这个缓冲里的数据就要全部丢弃,因为它们是旧模型的幻觉。进程 3(Q 函数回归):同时从真实 buffer 和模型 buffer 取批次做 Bellman 回归,更新当前参数 $\phi$。进程 2(目标更新):把 $\phi$ 缓慢拷贝到目标参数 $\phi'$。

两个 buffer 分开是关键工程设计:真实数据是「事实」,可以长期保留;模型数据是「意见」,随模型更新而失效,必须及时清空。实践中通常还会控制两者在每个 batch 里的比例(例如 $5\%$ 真实 + $95\%$ 模型,或者反过来),这个比例是最重要的超参之一。

MBA / MVE / MBPO

MBA、MVE、MBPO 三个方法共享的六步骨架
三个代表性方法共享的骨架:① 执行动作 $\mathbf{a}_i$,观测 $(\mathbf{s}_i,\mathbf{a}_i,\mathbf{s}_i',r_i)$ 加入 $\mathcal{B}$;② 从 $\mathcal{B}$ 均匀采一个 minibatch;③ 用这个 batch 更新模型 $\hat p(\mathbf{s}'|\mathbf{s},\mathbf{a})$;④ 从 $\mathcal{B}$ 采一批起始状态 $\{\mathbf{s}_j\}$;⑤ 对每个 $\mathbf{s}_j$,用当前策略 $\mathbf{a}=\pi(\mathbf{s})$ 在模型里做 rollout;⑥ 用 rollout 上的所有转移 $(\mathbf{s},\mathbf{a},\mathbf{s}',r)$ 更新 Q 函数。右图再次强调分支结构。底部两个问题:为什么这是好主意?为什么这是坏主意?

为什么是好主意:(a)样本效率暴涨——每条真实转移可以「繁殖」出几十上百条模型转移供 Q 函数消化,在 MuJoCo 连续控制基准上,这类方法达到 SAC 同等性能所需的真实交互步数常常能少一个数量级;(b)起点来自真实 buffer,状态覆盖有保证;(c)rollout 很短,误差可控。

为什么是坏主意:(a)Q 函数会被模型误差污染,而且这个污染会通过 Bellman 自举不断循环放大——错误的 $Q(\mathbf{s}',\mathbf{a}')$ 被用作目标去训练 $Q(\mathbf{s},\mathbf{a})$,一轮轮传播;(b)策略又会去 $\argmax$ 这个被污染的 Q,重演 model exploitation,只不过这次被利用的是值函数里的模型误差;(c)计算量大——每个真实步要跑几百次模型前向和几十次梯度更新,wall-clock 时间可能比纯无模型方法还长(虽然真实样本少得多)。

三者的区别

MBA(Gu et al. 2016, Continuous Deep Q-learning with Model-based Acceleration):最直接的 Dyna——把模型生成的转移加进 replay buffer 喂给 Q 学习(他们用的是 NAF)。

MVE(Feinberg et al. 2018, Model-Based Value Expansion):不是造数据,而是改造 Bellman 目标。标准的一步目标是 $y=r_0+\gamma Q_{\phi'}(\mathbf{s}_1,\mathbf{a}_1)$;MVE 用模型往前展开 $k$ 步再接上 Q:

$$ y^{(k)}=\sum_{t=0}^{k-1}\gamma^{t}\hat r(\hat{\mathbf{s}}_t,\hat{\mathbf{a}}_t)+\gamma^{k}Q_{\phi'}(\hat{\mathbf{s}}_k,\hat{\mathbf{a}}_k), \qquad \hat{\mathbf{s}}_{t+1}\sim\hat p_\phi(\cdot|\hat{\mathbf{s}}_t,\hat{\mathbf{a}}_t),\ \hat{\mathbf{a}}_t\sim\pi_\theta(\cdot|\hat{\mathbf{s}}_t) $$

这样做的收益是降低目标的偏差:$k$ 步真实(模型)奖励替代了 $k$ 步的自举,Q 的估计误差被 $\gamma^k$ 压缩。代价是引入了 $k$ 步模型误差。$k$ 就是在「自举偏差」和「模型偏差」之间的旋钮。后续的 STEVE 进一步用集成的不确定性对不同 $k$ 的目标做加权平均,让算法自动决定该信任模型多少步。

MBPO(Janner et al. 2019, When to Trust Your Model):本节最完整的方案,也是当前的实用基线。它的三个决定性选择是:概率集成模型(第 6 节那套)、从真实 buffer 出发的 $k$ 步分支 rollout、SAC 作为底层 off-policy 优化器,且真实数据与模型数据按固定比例混合采样。论文的理论部分给出形如

$$ \eta[\pi]\ \ge\ \hat\eta[\pi]-\underbrace{C(\epsilon_m,\epsilon_\pi,k)}_{\text{随}\ \epsilon_m\ \text{和}\ k\ \text{增大}} $$

的下界:$\hat\eta$ 是模型内估计的回报,$\epsilon_m$ 是模型的泛化误差,$\epsilon_\pi$ 度量当前策略与数据收集策略的差距。这个界告诉我们,只要模型误差足够小、rollout 长度 $k$ 足够短,模型内的改进就能保证真实回报的改进。

直觉

rollout 长度 $k$ 的取舍是 MBPO 的灵魂。$k$ 增大:好处是模型数据能覆盖到离 buffer 更远的状态(对当前策略更 on-policy),Q 学习的目标偏差更小;坏处是误差按 $\mathcal{O}(\epsilon k^2)$(甚至指数)累积。$k$ 减小到极限 $k=1$ 时,模型只做「一步外推」,几乎不会出错,但生成的状态几乎全在 buffer 分布内,价值有限。实践中 $k$ 通常小到 $1$,最多也就几步;有的实现会随训练进行(模型变准)从 $1$ 逐步增大。请记住:模型基 RL 里的默认答案是「非常短」,长 rollout 需要额外理由才能被采用。

分支 rollout 的最小实现

@torch.no_grad()
def branched_rollouts(model, real_buf, model_buf, policy, reward_fn,
                      k=1, B=100000):
    """MBPO 的核心:从真实 buffer 采起点,用模型走 k 步,产物存进模型 buffer。"""
    s = real_buf.sample_states(B)                    # (B, ds) 全是真实状态
    for _ in range(k):
        a = policy.sample(s)                         # 用【当前】策略选动作
        # 每条轨迹随机挑一个集成成员,整段 rollout 用同一个成员
        m = int(torch.randint(0, model.n, (1,)))
        s2 = model.step(s, a, m)
        r  = reward_fn(s, a)                         # 或用学到的奖励模型
        model_buf.add(s, a, r, s2)                   # 存进【模型】buffer
        s = s2                                       # 从新状态继续分支

def mbpo_epoch(env, model, policy, real_buf, model_buf,
               k=1, n_env_steps=1000, grad_steps_per_step=20, p_real=0.05):
    for _ in range(n_env_steps):
        # (1) 真实环境走一步
        real_buf.add(*env_step(env, policy))
        # (2) 重训模型(实践中每隔几百步做一次即可)
        model.fit(*real_buf.all())
        # (3) 清空旧的模型数据,重新生成分支 rollout
        model_buf.clear()
        branched_rollouts(model, real_buf, model_buf, policy, reward_fn, k=k)
        # (4) 用混合 batch 做多次 SAC 更新
        for _ in range(grad_steps_per_step):
            n_real = int(p_real * BATCH)
            batch = concat(real_buf.sample(n_real),
                           model_buf.sample(BATCH - n_real))
            sac_update(policy, batch)

注意 grad_steps_per_step=20:每走一步真实环境,做 20 次梯度更新。这个「更新-数据比(update-to-data ratio, UTD)」正是模型基方法能把样本效率提上去的直接机制——模型提供了足够多的(伪)数据,让我们敢于在每条真实数据上榨取更多梯度步而不至于立刻过拟合。

11. 高维观测:潜在空间模型

Part 3 回头处理一件之前一直被跳过的事:模型的输入输出到底是什么。前面所有讨论都默认我们有低维的马尔可夫状态 $\mathbf{s}_t$(关节角、速度、物体位姿)。可现实里我们常常只有 $64\times64\times3$ 的图像 $\mathbf{o}_t$。直接在像素上学 $\hat p(\mathbf{o}_{t+1}|\mathbf{o}_t,\mathbf{a}_t)$ 有三重困难:

  • 维度太高:$12288$ 维的输出,模型容量和训练开销都很吓人。
  • 观测不是状态:单帧图像不含速度信息,也可能有遮挡——$\mathbf{o}_t$ 不满足马尔可夫性,$p(\mathbf{o}_{t+1}|\mathbf{o}_t,\mathbf{a}_t)$ 这个式子本身就不成立。
  • 规划在像素上做不了:CEM 在像素空间里没有意义,奖励函数也很难定义在像素上。

解法是引入一个低维的潜在状态(latent state) $\mathbf{s}_t$(有些文献写作 $\mathbf{z}_t$),让动力学发生在潜在空间,图像只是它的一个「渲染」。

潜在状态空间模型的图模型结构、先验、解码器与编码器
潜在状态空间模型的结构。上方是三帧 Atari 图像与它们在潜在空间中对应的点 $\mathbf{z}_1\to\mathbf{z}_2\to\mathbf{z}_3\to\mathbf{z}_4$——高维图像被压到一个低维流形上,动力学在这个流形上演化。中间是图模型:潜变量链 $\mathbf{z}_1\to\mathbf{z}_2\to\mathbf{z}_3$ 由 $p(\mathbf{z}_{t+1}|\mathbf{z}_t,\mathbf{a}_t)$ 连接,每个 $\mathbf{z}_t$ 生成一个观测 $\mathbf{o}_t$;箭头注明「我们并不处在部分可观测的设定下」——意思是 $\mathbf{z}$ 被定义为马尔可夫的,部分可观测性被吸收进了「$\mathbf{o}$ 只是 $\mathbf{z}$ 的一个含噪投影」。右侧把它对应到标准 VAE 的三件套:先验 $p(\mathbf{z})=p(\mathbf{z}_1)\prod_t p(\mathbf{z}_{t+1}|\mathbf{z}_t,\mathbf{a}_t)$($p(\mathbf{z}_1)=\mathcal{N}(0,\mathbf{I})$,转移是学出来的)、解码器 $p_\theta(\mathbf{o}|\mathbf{z})=\prod_t p(\mathbf{o}_t|\mathbf{z}_t)$、编码器 $q_\phi(\mathbf{z}|\mathbf{o})=\prod_t q_\phi(\mathbf{z}_t|\mathbf{o}_{1:t})$。

三个组件的对应关系值得反复品味:普通 VAE 的先验是固定的 $\mathcal{N}(0,\mathbf{I})$,而这里的先验是一个带动作输入的时序模型——它就是我们要的动力学模型。换句话说,学动力学模型这件事,被表述成了「学一个序列 VAE 的先验」。解码器就是一个反卷积网络,把 $\mathbf{z}_t$ 画成图像;编码器可以是逐帧 CNN,也可以是吃整段历史的 LSTM/Transformer——写成 $q_\phi(\mathbf{z}_t|\mathbf{o}_{1:t})$ 时它就是一个滤波后验(filtering posterior),能从多帧里恢复出速度这类单帧看不到的信息。

三个要学的模型

状态空间模型的图结构与两种训练目标(全可观测 vs 潜在空间)
完整的状态空间模型。图中每个潜在状态 $\mathbf{s}_t$ 同时向三个方向发射:向上生成观测 $\mathbf{o}_t$、向右在动作 $\mathbf{a}_t$ 作用下生成 $\mathbf{s}_{t+1}$、向下生成奖励 $r_t$。对应三个待学的分布:观测模型 $p(\mathbf{o}_t|\mathbf{s}_t)$、动力学模型 $p(\mathbf{s}_{t+1}|\mathbf{s}_t,\mathbf{a}_t)$、奖励模型 $p(r_t|\mathbf{s}_t,\mathbf{a}_t)$。下半部分对比两种训练目标:全可观测时就是普通的最大似然 $\max_\phi\frac1N\sum_i\sum_t\log p_\phi(\mathbf{s}_{t+1,i}|\mathbf{s}_{t,i},\mathbf{a}_{t,i})$;潜在空间时状态不可见,必须对后验取期望并加上熵项。

全可观测时目标简单得几乎不用想:

$$ \max_\phi\ \frac{1}{N}\sum_{i=1}^{N}\sum_{t=1}^{H}\log p_\phi(\mathbf{s}_{t+1,i}\mid\mathbf{s}_{t,i},\mathbf{a}_{t,i}) $$

潜在空间时 $\mathbf{s}_t$ 是未知的,我们只能对某个近似后验 $q_\psi$ 取期望:

$$ \max_{\phi,\psi}\ \frac{1}{N}\sum_{i=1}^{N}\sum_{t=1}^{H} \E_{q}\Big[\log p_\phi(\mathbf{s}_{t+1,i}|\mathbf{s}_{t,i},\mathbf{a}_{t,i})+\log p_\phi(\mathbf{o}_{t,i}|\mathbf{s}_{t,i})\Big] +\mathcal{H}\big(q_\psi(\mathbf{s}_t,\mathbf{s}_{t+1}\mid \mathbf{o}_{1:H,i},\mathbf{a}_{1:H,i})\big) $$

期望是对 $(\mathbf{s}_t,\mathbf{s}_{t+1})\sim q_\psi(\mathbf{s}_t,\mathbf{s}_{t+1}|\mathbf{o}_{1:H,i},\mathbf{a}_{1:H,i})$ 取的。

推导

这个式子不是拍脑袋写的,它就是证据下界(ELBO)。从观测序列的对数似然出发(为简洁省去下标 $i$,并把 $\mathbf{a}_{1:H}$ 视为给定的条件):

$$ \log p_\phi(\mathbf{o}_{1:H}\mid\mathbf{a}_{1:H})=\log\int p_\phi(\mathbf{o}_{1:H},\mathbf{s}_{1:H}\mid\mathbf{a}_{1:H})\,d\mathbf{s}_{1:H} $$

乘除一个任意的 $q_\psi(\mathbf{s}_{1:H}|\mathbf{o}_{1:H},\mathbf{a}_{1:H})$,再用 Jensen 不等式($\log$ 是凹函数)把 $\log$ 换到期望里面:

$$ =\log\E_{q_\psi}\!\left[\frac{p_\phi(\mathbf{o}_{1:H},\mathbf{s}_{1:H}|\mathbf{a}_{1:H})}{q_\psi(\mathbf{s}_{1:H}|\mathbf{o}_{1:H},\mathbf{a}_{1:H})}\right] \ \ge\ \E_{q_\psi}\big[\log p_\phi(\mathbf{o}_{1:H},\mathbf{s}_{1:H}|\mathbf{a}_{1:H})\big]-\E_{q_\psi}\big[\log q_\psi\big] $$

第二项按定义就是熵 $\mathcal{H}(q_\psi)$。第一项按图模型的因子分解展开:

$$ \log p_\phi(\mathbf{o}_{1:H},\mathbf{s}_{1:H}|\mathbf{a}_{1:H})=\sum_{t}\log p_\phi(\mathbf{o}_t|\mathbf{s}_t)+\sum_{t}\log p_\phi(\mathbf{s}_{t+1}|\mathbf{s}_t,\mathbf{a}_t)+\log p(\mathbf{s}_1) $$

代回去,忽略与参数无关的 $\log p(\mathbf{s}_1)$,就得到

$$ \text{ELBO}=\sum_{t=1}^{H}\E_{q_\psi}\Big[\log p_\phi(\mathbf{s}_{t+1}|\mathbf{s}_t,\mathbf{a}_t)+\log p_\phi(\mathbf{o}_t|\mathbf{s}_t)\Big]+\mathcal{H}(q_\psi) $$

与幻灯片上的式子完全一致。三项各司其职:第一项逼着潜在空间里的动力学要可预测(这是我们真正想要的模型);第二项逼着潜在状态要保留足够信息重建出图像(防止 $\mathbf{s}_t$ 塌缩成常数——那样动力学项会完美但毫无用处);熵项防止后验塌缩成一个点,保持不确定性的表达能力。注意由于每一项只涉及 $(\mathbf{s}_t,\mathbf{s}_{t+1})$,我们其实只需要成对的边缘后验 $q_\psi(\mathbf{s}_t,\mathbf{s}_{t+1}|\cdot)$,不需要整条链的联合后验——这正是幻灯片写成那个形式的原因。

后验(编码器)怎么选

三种近似后验的选择及其精度与复杂度权衡
近似后验的三种选择。完整平滑后验 $q_\psi(\mathbf{s}_t,\mathbf{s}_{t+1}|\mathbf{o}_{1:H},\mathbf{a}_{1:H})$:用整段序列(包括未来)来推断当前状态,最准确,但最复杂。单步编码器 $q_\psi(\mathbf{s}_t|\mathbf{o}_t)$(红圈):只看当前这一帧,最简单,也最不准确。红圈下写着「我们先讲这一个」——因为它最容易实现,且在很多任务上够用。中间还有一档是滤波后验 $q_\psi(\mathbf{s}_t|\mathbf{o}_{1:t},\mathbf{a}_{1:t})$,只用过去而不用未来,适合在线控制。
后验形式用到的信息准确度实现复杂度能否在线用
$q_\psi(\mathbf{s}_t|\mathbf{o}_t)$ 单步当前一帧最低最低(一个 CNN)可以
$q_\psi(\mathbf{s}_t|\mathbf{o}_{1:t},\mathbf{a}_{1:t})$ 滤波过去全部中中(RNN/Transformer)可以
$q_\psi(\mathbf{s}_t|\mathbf{o}_{1:H},\mathbf{a}_{1:H})$ 平滑过去 + 未来最高最高(双向序列模型)只能离线训练用

取单步编码器时,目标简化成(期望改为对 $\mathbf{s}_t\sim q(\mathbf{s}_t|\mathbf{o}_t)$、$\mathbf{s}_{t+1}\sim q(\mathbf{s}_{t+1}|\mathbf{o}_{t+1})$ 取):

$$ \max_{\phi,\psi}\ \frac{1}{N}\sum_{i}\sum_{t}\E_{q}\Big[\log p_\phi(\mathbf{s}_{t+1,i}|\mathbf{s}_{t,i},\mathbf{a}_{t,i})+\log p_\phi(\mathbf{o}_{t,i}|\mathbf{s}_{t,i})\Big]+\mathcal{H}\big(q(\mathbf{s}_t|\mathbf{o}_t)\big) $$
单步编码器目标与确定性编码器的特例化推导
单步编码器的目标(上),以及它的确定性特例(下)。黄框两次追问「为什么这类编码器可能是个坏主意?」。下半部分做了一个重要的简化:令 $q(\mathbf{s}_t|\mathbf{o}_t)=\delta\big(\mathbf{s}_t=g_\psi(\mathbf{o}_t)\big)$ 是确定性编码器,于是 $\mathbf{s}_t=g_\psi(\mathbf{o}_t)$ 直接由网络算出、熵项退化为常数可以丢掉,期望符号也消失了,目标变成一个完全普通的、可以直接反传的确定性损失。

确定性编码器下的目标是:

$$ \max_{\phi,\psi}\ \frac{1}{N}\sum_{i=1}^{N}\sum_{t=1}^{H}\log p_\phi\big(g_\psi(\mathbf{o}_{t+1,i})\,\big|\,g_\psi(\mathbf{o}_{t,i}),\mathbf{a}_{t,i}\big)+\log p_\phi\big(\mathbf{o}_{t,i}\,\big|\,g_\psi(\mathbf{o}_{t,i})\big) $$
常见误区

两个「为什么可能是坏主意」的答案。

(1)单步编码器 $q(\mathbf{s}_t|\mathbf{o}_t)$ 的问题:一帧图像通常不足以确定状态。速度、角速度从静止的一帧里根本读不出来;被遮挡的物体位置也读不出来。此时真实后验 $p(\mathbf{s}_t|\mathbf{o}_{1:t})$ 会显著窄于 $p(\mathbf{s}_t|\mathbf{o}_t)$,用后者做变分族相当于人为丢掉了历史信息,ELBO 的间隙很大,学到的「潜在动力学」被迫去建模那些其实可以从历史推断出来的不确定性,预测因此变得模糊。常见的粗暴补丁是把最近 $k$ 帧堆叠成一个「观测」,但那只是把滤波后验硬编码成固定窗口。

(2)确定性编码器的问题:它彻底放弃了表达状态不确定性的能力。$\delta$ 分布的微分熵是 $-\infty$,熵项被丢掉之后,目标里就没有任何东西阻止编码器把不同的真实状态映射到同一个 $\mathbf{s}$(信息塌缩),也没有任何机制让模型说出「这一帧我看不清」。在观测几乎完全决定状态的任务(比如固定视角、无遮挡、动作缓慢)里它工作得很好,也确实是最容易实现的版本;一旦有真正的部分可观测性,实践中的主流方法都会换成随机编码器或更复杂的滤波/平滑后验。

加上奖励模型,得到完整目标

潜在空间模型的三项完整训练目标:动力学、图像重建、奖励模型
把奖励模型也加进来后的完整图模型与目标。图中 $\mathbf{s}_1=g_\psi(\mathbf{o}_1)$ 的虚线箭头表示确定性编码;每个 $\mathbf{s}_t$ 同时产出 $\mathbf{o}_t$ 和 $r_t$,并在 $\mathbf{a}_t$ 作用下推进到 $\mathbf{s}_{t+1}$。下方的目标函数由三项组成,各自被标注出来:潜在空间动力学 $\log p_\phi(g_\psi(\mathbf{o}_{t+1,i})|g_\psi(\mathbf{o}_{t,i}),\mathbf{a}_{t,i})$、图像重建 $\log p_\phi(\mathbf{o}_{t,i}|g_\psi(\mathbf{o}_{t,i}))$、奖励模型 $\log p_\phi(r_{t,i}|g_\psi(\mathbf{o}_{t,i}))$。底部黄框提醒:很多实用方法会使用随机编码器,或者更复杂的平滑后验(尤其在真正部分可观测时)。
$$ \max_{\phi,\psi}\ \frac{1}{N}\sum_{i=1}^{N}\sum_{t=1}^{H} \underbrace{\log p_\phi\big(g_\psi(\mathbf{o}_{t+1,i})|g_\psi(\mathbf{o}_{t,i}),\mathbf{a}_{t,i}\big)}_{\text{潜在动力学}} +\underbrace{\log p_\phi\big(\mathbf{o}_{t,i}|g_\psi(\mathbf{o}_{t,i})\big)}_{\text{图像重建}} +\underbrace{\log p_\phi\big(r_{t,i}|g_\psi(\mathbf{o}_{t,i})\big)}_{\text{奖励模型}} $$

奖励模型不可或缺:规划器必须能对模型幻想出来的潜在状态求奖励,而我们手上没有那些状态对应的真实奖励值。

装进 MPC 循环

带潜在状态的模型基 RL 完整算法,含 MPC 重规划与每 N 步重训
把潜在空间模型接回版本 1.5 的 MPC 框架。① 用基准策略 $\pi_0(\mathbf{a}_t|\mathbf{o}_t)$ 收集 $\mathcal{D}=\{(\mathbf{o},\mathbf{a},\mathbf{o}')_i\}$——注意数据里存的是观测,不是状态;② 学四个组件 $p_\phi(\mathbf{s}_{t+1}|\mathbf{s}_t,\mathbf{a}_t)$、$p_\phi(r_t|\mathbf{s}_t)$、$p(\mathbf{o}_t|\mathbf{s}_t)$、$q_\psi(\mathbf{s}_t|\mathbf{o}_t)$;③ 在模型里规划(编码当前观测得到 $\mathbf{s}$,然后在潜在空间做 CEM);④ 执行计划里的第一个动作,观测到新的 $\mathbf{o}'$(MPC);⑤ 把 $(\mathbf{o},\mathbf{a},\mathbf{o}')$ 追加进 $\mathcal{D}$。左侧标注「every N steps」——内层是每步重规划,外层每 N 步才重训模型。

关键在第 ③ 步:规划整个发生在低维潜在空间里。CEM 采样的是动作序列,rollout 用的是 $p_\phi(\mathbf{s}_{t+1}|\mathbf{s}_t,\mathbf{a}_t)$,奖励用的是 $p_\phi(r_t|\mathbf{s}_t)$——从头到尾都不需要解码出图像。解码器只在训练时起作用(提供重建损失这个学习信号),推理时可以完全不用。这就把第 4 节说的「维度上限」问题绕过去了:动作维度不变,状态维度从 $12288$ 降到几十。PlaNet 就是这个算法的一个完整实现。

注意

图像重建损失有个众所周知的毛病:它按像素面积分配注意力,而不是按任务相关性。画面里那面占了半屏的墙比那个 5 像素的小球贡献大得多的重建损失,于是潜在状态会优先编码墙的纹理,而把决定成败的小球位置当成噪声丢掉。缓解手段包括:加大奖励模型损失的权重、用对比学习目标替代重建、或者干脆彻底丢掉重建项只保留「动力学 + 奖励 + 值函数」的一致性(TD-MPC 一类的做法)。这不是本讲的内容,但在实践中往往是成败的分水岭。

12. 表征学习 + actor-critic:把三条线合起来

最后一步升级:MPC 每步重规划太贵,而且第 5 节说过它处理不了长期承诺。既然我们已经有了一个能把图像编码成低维状态的编码器 $q_\psi$,为什么不直接在这个潜在空间里跑一个标准的 actor-critic?

带学习表征的 actor-critic 算法七步,与 replay buffer 的交互
「带学习表征的 actor-critic」。① 用 $\mathbf{a}\sim\pi_\theta(\mathbf{a}|\mathbf{o})$ 与环境交互一步得到 $(\mathbf{o}_i,\mathbf{a}_i,\mathbf{o}_i')$,存入 replay buffer;② 从 buffer 取一批 $\{(\mathbf{o}_i,\mathbf{a}_i,\mathbf{o}_i')\}_{i=1}^{B}$,更新表征模型的四个组件 $p_\phi(\mathbf{s}_{t+1}|\mathbf{s}_t,\mathbf{a}_t),p_\phi(r_t|\mathbf{s}_t),p(\mathbf{o}_t|\mathbf{s}_t),q_\psi(\mathbf{s}_t|\mathbf{o}_t)$;③ 用编码器推断潜在状态 $\mathbf{s}_i\sim q_\psi(\mathbf{s}_i|\mathbf{o}_i)$、$\mathbf{s}_i'\sim q_\psi(\mathbf{s}_i'|\mathbf{o}_i')$;④ 算 Bellman 目标 $y_i=r(\mathbf{s}_i,\mathbf{a}_i)+\gamma\E_{\mathbf{a}'\sim\pi_\theta(\mathbf{a}'|\mathbf{o}_i')}[\hat Q^\pi_\phi(\mathbf{s}_i',\mathbf{a}')]$;⑤ 用 $\nabla_\phi\sum_i\|\hat Q^\pi_\phi(\mathbf{s}_i,\mathbf{a}_i)-y_i\|^2$ 更新评论家;⑥ 用 $\nabla_\theta J(\theta)\approx\sum_i\E_{\mathbf{a}\sim\pi_\theta(\mathbf{a}|\mathbf{o}_i)}[\nabla_\theta\log\pi_\theta(\mathbf{a}|\mathbf{o}_i)\hat Q^\pi_\phi(\mathbf{s}_i,\mathbf{a})]$ 算演员梯度;⑦ $\theta\leftarrow\theta+\alpha\nabla_\theta J(\theta)$。右下两个黄框问:为什么我们会想用这个?为什么可能更偏好完整的滤波/平滑后验?

为什么我们会想用这个?注意这个算法里模型根本没有被用来生成数据——第 ④⑤⑥ 步用的全是真实转移。模型(准确说是表征学习目标)唯一的作用是给编码器提供一个额外的、密集的训练信号。从像素直接学 Q 函数时,唯一的学习信号是稀疏的奖励,卷积编码器要靠这点信号从零学出视觉表征,极其低效;而「预测下一帧、预测奖励、预测潜在动力学」这些自监督任务提供的梯度密集得多。这就是表征学习作为辅助任务的思路,也是本节标题里「learned representations」的含义。

为什么偏好完整的滤波/平滑后验?因为 Q 函数和策略都定义在 $\mathbf{s}$ 上,$\mathbf{s}$ 必须是马尔可夫的才能让 Bellman 方程成立。如果 $\mathbf{s}_i$ 只来自单帧 $\mathbf{o}_i$,那它可能不含速度,$Q(\mathbf{s},\mathbf{a})$ 就是在一个非马尔可夫的表征上做自举,收敛性质完全没有保证。用 $q_\psi(\mathbf{s}_t,\mathbf{s}_{t+1}|\mathbf{o}_{1:t+1},\mathbf{a}_{1:t})$ 这类滤波后验,$\mathbf{s}_t$ 汇总了整段历史,才更接近真正的马尔可夫状态。

最后一步:把 Dyna 加回来

actor-critic 加模型基 RL:在潜在空间里额外生成模拟数据
在上一个算法里插入第 ④ 步「用 $p_\phi$ 仿真额外的数据」,其余步骤顺延。左侧的绿色箭头显示现在有三层循环:最内层是「仿真数据 → 评论家更新 → 演员更新」,中层回到第 ④ 步再仿真一批,最外层才回到第 ① 步与真实环境交互。右下角的分支 rollout 图和第 10 节完全一样——只不过现在这些分支发生在潜在空间里。底部还给了一条重要的实践建议:通常更希望做完整的潜在空间学习,即策略定义在潜在状态上 $\pi_\theta(\mathbf{a}|\mathbf{s})$,而对观测的策略由边缘化得到 $\pi_\theta(\mathbf{a}|\mathbf{o})=\E_{\mathbf{s}\sim q_\psi(\mathbf{s}|\mathbf{o})}[\pi_\theta(\mathbf{a}|\mathbf{s})]$。

这一步把本讲的三条线全部合流了:Part 3 的潜在空间模型提供了低维马尔可夫表征,Part 2 的 Dyna 风格短 rollout 在这个表征里生成廉价数据,无模型 actor-critic 消化这些数据。Dreamer 系列正是这个结构的完整工程实现——它在潜在空间里做长达十几步的「想象」rollout,并用 pathwise 梯度(因为潜在动力学是自己学的、可微且被正则化得相当光滑,第 8 节的雅可比问题在这里没那么致命)反传训练演员。

$\pi_\theta(\mathbf{a}|\mathbf{o})=\E_{\mathbf{s}\sim q_\psi(\mathbf{s}|\mathbf{o})}[\pi_\theta(\mathbf{a}|\mathbf{s})]$ 这个式子也值得多看一眼:它说策略本质上只依赖潜在状态,面对观测时只需先编码、再决策。好处是策略和模型共享同一套表征,模型内的想象 rollout 里策略可以直接作用于 $\mathbf{s}$,不需要每步都解码出图像再编码回去——那样会慢得离谱,也会引入解码器的误差。

表征学习与模型基 RL 的两个代表性工作:SLAC 与 Dreamer
两个代表性成果。左:SLAC(Stochastic Latent Actor-Critic, Lee et al.),上排是真实 rollout、下排是模型在潜在空间里生成后解码出的样本——两者在多个 DeepMind Control 任务上几乎难以区分,说明潜在动力学确实学到了物理。右:Dreamer(Dream to Control: Learning Behaviors by Latent Imagination, Hafner et al.),示意在潜在空间里根据动作 $\mathbf{a}_1,\mathbf{a}_2$ 想象出未来的观测 $\mathbf{o}_1,\mathbf{o}_2,\mathbf{o}_3$,整个策略学习完全在「梦」里进行。
核心结论

从像素做模型基 RL 的现代配方基本定型为四件事:(1)用序列 VAE 学一个低维潜在状态空间,同时训练观测、动力学、奖励三个头;(2)编码器至少是滤波级别的(RNN 或多帧堆叠),保证潜在状态近似马尔可夫;(3)在潜在空间里做短的想象 rollout,训练 actor-critic 或做 MPC;(4)永远保留一部分真实数据参与训练,并持续用新数据更新模型。SLAC、PlaNet、Dreamer、TD-MPC 都是这个模板的不同实例,差别只在于第 (3) 步用规划还是用 actor-critic、以及第 (1) 步要不要保留重建损失。

本讲小结

一页速查表:

问题症状解法出现在
分布漂移规划器把你带到模型没见过的状态执行后回收数据(版本 1.0)第 1 节
整条计划才更新一次模型骗你走向悬崖,来不及纠正MPC 每步重规划(版本 1.5)第 5 节
随机环境下开环次优动作在看到状态前就定死闭环策略,或 MPC 近似闭环第 3 节
规划维度上限random shooting 命中概率 $q^{d}$ 指数衰减CEM / CMA-ES;短时域 + 值函数截断第 4、5 节
model exploitation$\argmax$ 专挑模型高估的地方,$\E[\max\delta]\approx\sigma\sqrt{2\ln N}$集成 + 对回报取平均第 6 节
反传梯度爆炸/消失$H$ 个雅可比连乘,$\lambda^{H}$改用策略梯度(版本 2.5)第 8 节
长 rollout 误差累积$\mathcal{O}(\epsilon H^2)$,混沌时指数短分支 rollout + off-policy RL(版本 3.0)第 9、10 节
Q 被模型误差污染自举把错误循环放大短 $k$、真假数据混合、集成不确定性加权(STEVE)第 10 节
图像观测像素高维、非马尔可夫、无法规划潜在空间模型(ELBO:动力学 + 重建 + 奖励 + 熵)第 11 节
稀疏奖励下表征学不出来卷积编码器缺少学习信号模型学习作为辅助任务喂给 actor-critic第 12 节

要点清单

  • 规划 vs 学策略:规划是现场解优化问题、输出动作序列、不需训练但只能开环且维度受限;学策略是离线固化决策函数、闭环、可扩展但需要训练且会被模型误差腐蚀。MPC 是二者之间最实用的桥。
  • 开环在随机环境下严格次优,两步 MDP 的例子里差了整整一倍;根源是「决策时看不到状态」,与模型准不准无关。
  • random shooting 是三行代码的强 baseline;CEM 只是把采样分布也一起优化,其「重拟合精英」这一步等价于最小化 $\KL(q^\ast\|p)$,即在精英集上做 MLE。
  • 不确定性只有认知不确定性有用。输出熵(偶然不确定性)在数据分布外会给出「自信的错误答案」。bootstrap 集成用 $p(\theta|\mathcal{D})\approx\frac1N\sum_i\delta(\theta-\theta_i)$ 做粒子近似,$N\lt 10$ 就够,随机初始化 + SGD 通常已经提供了足够的多样性。
  • 永远先 rollout 再平均回报:$\frac1N\sum_i r(f_i(\mathbf{s},\mathbf{a}))\ne r(\frac1N\sum_i f_i(\mathbf{s},\mathbf{a}))$。反例:$r(s)=1-s^2$,两个模型分别预测 $\pm1$,平均预测给 $1$ 分,平均回报给 $0$ 分。
  • 期望值不是悲观值也不是乐观值,但通常是个不错的起点;真要做离线 RL 就得显式加分歧惩罚,要做探索就得反过来加分歧奖励。
  • pathwise 梯度 vs 策略梯度:前者需要模型在导数层面准确、且要连乘 $H$ 个雅可比;后者只需要模型在分布层面大致对、不含连乘。样本够多时后者往往更稳。
  • $\mathcal{O}(\epsilon H^2)$ 的三步推导:一步 TV 误差 $\epsilon$ → 分布误差线性累积 $(t-1)\epsilon$ → 回报误差按 $2R_{\max}$ 换算并对 $t$ 求和。$\epsilon=0.01$、$H=100$ 时界已完全失效。
  • Dyna 的三个要件:从真实 buffer 采起点、只走极短 rollout、用 off-policy 算法容忍状态分布偏差。MBPO 是它的现代实现(概率集成 + $k$ 步分支 + SAC);MVE 换了个角度,把模型用在 Bellman 目标的 $k$ 步展开上。
  • 潜在空间模型 = 序列 VAE,其中先验就是我们要的动力学。目标是 ELBO:潜在动力学项 + 图像重建项 + 奖励项 + 后验熵。单步编码器最简单但丢历史信息;确定性编码器最好实现但无法表达状态不确定性。
  • 规划发生在潜在空间,解码器只在训练时用;这样维度上限的问题被绕开了。
  • 模型学习可以只作为表征学习的辅助任务(不生成任何数据),也可以进一步用来生成想象数据;SLAC / Dreamer / PlaNet / TD-MPC 是这条线的代表。

延伸阅读

规划与不确定性感知模型

用模型学策略、Dyna 与短 rollout

  • Integrated Architectures for Learning, Planning, and Reacting Based on Approximating Dynamic Programming (Sutton, 1990) — Dyna 的原始论文,第 10 节整节的思想源头,短得可以一小时读完。
  • Continuous Deep Q-Learning with Model-based Acceleration (MBA, 2016) — 把 Dyna 搬到连续控制的早期尝试,同时提出了 NAF 这个把 Q 学习用于连续动作的巧妙参数化。
  • Model-Based Value Estimation for Efficient Model-Free RL (MVE, 2018) — 不生成数据,而是用模型把 Bellman 目标展开 $k$ 步,是「自举偏差 vs 模型偏差」权衡的最清晰案例。
  • When to Trust Your Model: Model-Based Policy Optimization (MBPO, 2019) — 本讲版本 3.0 的标准答案。理论部分给出了 rollout 长度 $k$ 与模型误差的显式权衡界,实验部分给出了「$k$ 应该非常短」的经验证据。
  • PIPP: Flexible Model-Based Policy Search Robust to the Curse of Chaos (Parmas et al., 2018) — 第 8 节「为什么反传梯度会炸」的正式分析,并给出混合 pathwise 与似然比估计量的方案。
  • Aggressive Driving with Model Predictive Path Integral Control (Williams et al., 2016) — MPPI 的原始工作,是 CEM 之外另一种在真实机器人上广泛使用的采样式 MPC 求解器。

潜在空间模型与从像素学习