ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Control as Inference:从最优控制到概率推断的变分框架

Control as Inference:从最优控制到概率推断的变分框架 1. 从最优控制到概率推断一个视角的转换第一次接触Control as Inference这个概念是在啃一本强化学习教材的间隙。当时我正在为一个机械臂轨迹跟踪的项目发愁——传统MPC模型预测控制框架下代价函数里的权重矩阵调得我头大Q和R稍微动一点末端抖动就肉眼可见。导师丢过来一句话“你试试从概率的角度看控制问题。”于是就有了这篇总结。Control as Inference直译过来就是“把控制当作推断”。它的核心主张非常反直觉最优控制问题可以等价地转化为一个概率推断问题。换句话说我们不再去“最小化代价”而是去“最大化后验概率”——把控制输入看作隐变量把状态轨迹看作观测然后做贝叶斯推断。这个视角一旦建立起来变分推断、KL散度、期望最大化这些概率图模型里的工具就全部可以搬到控制领域来用。这篇文章适合谁看如果你已经做过一些最优控制或者强化学习的项目对LQR、MPC、策略梯度这些概念不陌生但总觉得代价函数设计像玄学那这篇内容会给你一个全新的抓手。如果你是完全的新手也没关系我会尽量用生活化的类比把数学讲清楚。全文会从基本理论出发把Control as Inference的推导链条拆开补充大量实操中才会遇到的细节和坑。提示本文涉及的概率推断推导建议读者手边有纸笔跟着公式走一遍比单纯看要有效得多。2. 为什么要把控制问题变成推断问题2.1 传统最优控制的痛点在哪里先说传统路子。一个标准的随机最优控制问题通常写成这样给定系统动力学 (x_{t1} f(x_t, u_t) w_t)其中 (w_t) 是过程噪声我们要找一个控制序列 (u_{0:T-1})使得期望累积代价最小[ J \mathbb{E}\left[\sum_{t0}^{T-1} c(x_t, u_t) c_T(x_T)\right] ]这个框架用了快半个世纪从阿波罗登月到现在的自动驾驶功勋卓著。但它有几个让人难受的地方。第一代价函数的设计高度依赖领域知识。你写一个 (c(x,u) x^T Q x u^T R u)Q和R怎么选通常靠试凑或者经验公式换一个任务就得重新调。第二约束的处理很别扭。硬约束会让优化问题变成非凸的软约束又得引入惩罚项惩罚系数同样难调。第三不确定性传播困难。当系统噪声很大时确定性MPC的滚动时域优化会变得保守或者激进缺乏一个原则性的方式来权衡探索与利用。我在做机械臂项目时就深有体会抓取不同形状的物体Q矩阵要重新调抓鸡蛋和抓扳手完全是两套参数。每次调参都像在黑暗中摸索没有理论指导。2.2 概率视角带来的三个好处Control as Inference把问题重新表述后上面这些痛点有了新的解法。好处一代价函数变成概率分布权重有了概率意义。在推断框架下我们定义一个“最优性变量” (O_t)它是一个二值随机变量(O_t1) 表示时刻 (t) 的动作是“最优的”。然后定义 (p(O_t1|x_t,u_t) \propto \exp(-c(x_t,u_t)))。你看代价越小这个概率越大。原来那个让人头疼的Q矩阵现在变成了概率分布的精度矩阵它的物理意义清晰多了——精度越高分布越尖对代价越敏感。好处二约束可以自然地编码为先验。比如关节限位可以写成状态先验 (p(x_t)) 在可行域外概率为零。推断过程会自动尊重这个先验不需要额外的惩罚项。好处三不确定性有了统一的处理框架。变分推断天然处理随机性KL散度衡量的是两个分布的差异而不是简单的数值误差。这让算法在噪声环境下的鲁棒性有了理论保证。2.3 一个生活化的类比从“找最低点”到“找最可能的路”打个比方。传统最优控制像是让你在一个山谷里找最低点你拿着海拔计一步步往下走。Control as Inference则是给你一张概率地图地图上每个位置的颜色深浅代表“这条路有多大概率是通往山顶的最优路径”。你不是在找最低点而是在找最可能的那条路。这个类比的关键在于概率地图天然包含了不确定性信息。山谷里可能有多个低点传统方法只能选一个而概率方法会告诉你每个低点的“可信度”是多少。在噪声大、模型不准的场景下这个信息极其宝贵。3. 核心推导从KL散度到变分下界3.1 最优性变量的引入与联合分布推导的起点是定义一个轨迹级别的联合分布。设轨迹 (\tau (x_{0:T}, u_{0:T-1}))我们引入最优性变量 (O_{0:T-1})并做如下因子分解[ p(\tau, O_{0:T-1}) p(x_0) \prod_{t0}^{T-1} p(x_{t1}|x_t,u_t) \prod_{t0}^{T-1} p(O_t1|x_t,u_t) ]其中 (p(x_{t1}|x_t,u_t)) 就是系统动力学(p(O_t1|x_t,u_t) \propto \exp(-c(x_t,u_t)))。这个分解的假设是给定当前状态和动作下一时刻状态与最优性变量条件独立。这是标准的结构因果模型假设在大多数控制场景下成立。现在控制的目标变成了在给定 (O_{0:T-1}1) 的条件下求轨迹的后验分布 (p(\tau|O_{0:T-1}1))。这个后验分布就是“最优轨迹的分布”。然后我们可以取这个分布的众数或者均值作为实际控制序列。3.2 变分推断的介入为什么需要它直接计算后验 (p(\tau|O)) 通常不可行因为归一化常数 (p(O)) 需要对所有可能的轨迹积分维度太高。这时候变分推断登场了我们引入一个参数化的变分分布 (q(\tau))用 (q) 去逼近真实后验 (p(\tau|O))。逼近的度量用KL散度[ D_{KL}(q(\tau) | p(\tau|O)) \int q(\tau) \log \frac{q(\tau)}{p(\tau|O)} d\tau ]展开这个式子利用 (p(\tau|O) p(\tau,O)/p(O))可以得到[ \log p(O) \mathbb{E}q[\log p(\tau,O) - \log q(\tau)] D{KL}(q(\tau)|p(\tau|O)) ]因为KL散度非负所以第一项是 (\log p(O)) 的下界称为证据下界ELBO。最大化ELBO等价于最小化KL散度也就等价于让 (q) 逼近真实后验。注意这里有一个容易混淆的点。在传统变分推断中我们通常最大化ELBO来近似后验。但在Control as Inference中我们其实更关心的是 (q) 本身——它就是我们要找的最优控制策略。ELBO的最大化过程同时也是策略优化的过程。3.3 ELBO的展开与策略结构的浮现把联合分布的具体形式代入ELBO[ \mathcal{L}(q) \mathbb{E}q\left[\log p(x_0) \sum{t0}^{T-1} \log p(x_{t1}|x_t,u_t) \sum_{t0}^{T-1} \log p(O_t1|x_t,u_t) - \log q(\tau)\right] ]假设变分分布做如下因子分解(q(\tau) q(x_0) \prod_{t0}^{T-1} q(x_{t1}|x_t,u_t) q(u_t|x_t))。这里 (q(u_t|x_t)) 就是我们要学的策略(q(x_{t1}|x_t,u_t)) 可以理解为在变分框架下对动力学的近似。代入后经过一番整理中间省略若干代数步骤建议读者自行推导一遍ELBO可以写成[ \mathcal{L}(q) \mathbb{E}q\left[\sum{t0}^{T-1} \left( \log p(O_t1|x_t,u_t) - D_{KL}(q(x_{t1}|x_t,u_t) | p(x_{t1}|x_t,u_t)) \right) \right] \text{常数项} ]这个式子的物理意义非常清晰第一项鼓励选择代价低的动作第二项惩罚变分动力学与真实动力学的偏差。如果真实动力学已知且确定性第二项为零问题退化为最大化 (\sum \log p(O_t1|x_t,u_t))也就是最小化累积代价。这和我们熟悉的最优控制完全一致。3.4 从ELBO到软最优控制如果我们在每个时刻对 (q(u_t|x_t)) 做优化可以得到一个闭式解[ q^*(u_t|x_t) \propto \exp\left( \mathbb{E}{q(x{t1}|x_t,u_t)}[V_{t1}(x_{t1})] - c(x_t,u_t) \right) ]其中 (V_{t1}) 是值函数。这个式子就是**软最优控制Soft Optimal Control**的核心。它和传统最优控制的区别在于传统方法取 (\arg\min)这里取 (\exp(-\text{cost})) 的归一化。也就是说次优动作也有非零概率只是概率较低。这个“软”特性带来了探索能力也让算法对噪声更鲁棒。我在机械臂项目里实测过用软最优控制替代硬MPC后末端抖动明显减小因为算法不再死磕某一个“最优”动作而是在概率意义上权衡了多个候选动作。代价是收敛速度稍慢但稳定性提升很大。4. 实操中的关键细节与参数选择4.1 温度参数控制“软”的程度上面那个 (\exp(-c)) 里其实隐含了一个温度参数 (\alpha)(p(O_t1|x_t,u_t) \propto \exp(-c(x_t,u_t)/\alpha))。(\alpha) 越大分布越平坦探索性越强(\alpha) 越小分布越尖锐越接近传统最优控制。这个参数怎么选我的经验是初期用大 (\alpha) 探索后期逐渐退火到小 (\alpha) 利用。具体数值上如果代价函数的量级在 (10^2) 左右(\alpha) 从 (10) 退火到 (0.1) 是个不错的起点。退火速率可以设为指数衰减每1000步乘以0.99。提示(\alpha) 和传统MPC里的控制权重 (R) 有对应关系但不等价。(\alpha) 影响的是整个分布的形态而 (R) 只影响代价的二次项。调 (\alpha) 时不要同时大调 (R)否则两个效应会耦合很难判断哪个起了作用。4.2 变分分布的设计均值场还是全协方差变分分布 (q(u_t|x_t)) 的选择直接影响计算复杂度和逼近精度。最简单的做法是均值场假设即各维动作独立(q(u_t|x_t) \prod_i q(u_{t,i}|x_t))。这样计算量小但忽略了动作维度间的相关性。对于机械臂这种多关节耦合强的系统均值场假设会导致动作不协调。我的建议是如果动作维度小于6用全协方差高斯大于6先用均值场跑通再考虑用低秩近似。全协方差高斯的参数量是 (O(d^2))d是动作维度d6时是21个参数可以接受d20时就是210个计算量上来了。4.3 动力学模型的处理已知还是学习Control as Inference的推导假设动力学 (p(x_{t1}|x_t,u_t)) 已知。但实际项目中动力学往往未知或者有模型误差。这时候有两种处理方式方式一先辨识再推断。用系统辨识方法如最小二乘、高斯过程回归先拟合一个动力学模型然后把它当作已知的代入推断框架。这种方式适合动力学相对简单、数据充足的场景。方式二联合学习。把动力学也作为变分分布的一部分和策略一起优化。这本质上就是基于模型的强化学习。这种方式更灵活但优化难度大容易陷入局部最优。我在项目中用的是方式一因为机械臂的刚体动力学可以用拉格朗日方程精确建模只需要辨识摩擦和负载参数。如果你做的是四足机器人或者无人机动力学复杂且时变方式二可能更合适。4.4 数值稳定性log-sum-exp技巧计算 (\log p(O_t1|x_t,u_t)) 时如果代价很大(\exp(-c)) 会下溢到零。这时候要用log-sum-exp技巧def log_softmax(cost, alpha): # cost: 代价数组alpha: 温度参数 scaled -cost / alpha max_scaled np.max(scaled) return scaled - max_scaled - np.log(np.sum(np.exp(scaled - max_scaled)))这个技巧在实现软最优控制时是必须的否则数值不稳定会导致策略更新方向错误。我踩过这个坑有一次代价函数量级到了 (10^4)没做log-sum-exp结果策略梯度全是NaN排查了一整天才发现是数值下溢。5. 常见问题与排查技巧实录5.1 策略不收敛或者震荡怎么办这是最常见的问题。排查顺序建议如下排查项可能原因解决方法温度参数 (\alpha)过大导致探索过度过小导致早熟检查退火策略初期 (\alpha) 不要小于1学习率过大导致震荡过小导致收敛慢从1e-3开始用Adam优化器变分分布初始化初始方差过小策略过早确定初始方差设为动作范围的1/4代价函数量级量级差异大导致梯度不平衡对代价做归一化使其在[0,1]区间动力学模型误差模型不准导致推断偏差增加模型训练数据或降低模型置信度我遇到过一次典型的震荡机械臂在目标点附近来回摆动。排查后发现是 (\alpha) 退火太快从10直接跳到0.01策略来不及适应。改成指数退火后问题解决。5.2 KL散度出现负值是怎么回事KL散度理论上非负但数值计算中可能出现负值。原因通常是用蒙特卡洛采样估计KL散度时采样方差过大。解决方法有两个一是增加采样数量二是用重参数化技巧降低方差。重参数化技巧的核心是把 (u \mu \sigma \epsilon) 中的随机性转移到 (\epsilon) 上这样梯度可以穿过采样过程。在PyTorch里就是def reparameterize(mu, logvar): std torch.exp(0.5 * logvar) eps torch.randn_like(std) return mu eps * std这个技巧在变分推断里是标配不用的话梯度估计方差会大到无法训练。5.3 和MPC结合时的注意事项Control as Inference和MPC结合是一个热门方向。基本思路是用推断得到的策略分布作为MPC的终端代价或者先验。但这里有个坑推断得到的策略是随机的而MPC通常需要确定性输入。直接取均值可能不是最优的因为均值处的代价不一定最小。我的做法是从策略分布中采样多个候选动作分别做短时域MPC rollout选代价最小的那个。这样既保留了推断的探索性又保证了MPC的确定性输出。计算量会增加但现在的GPU完全扛得住。注意采样数量不要太多一般5-10个就够了。太多的话计算量线性增长收益递减。5.4 高频问题速查表现象可能原因快速修复策略输出饱和动作范围未归一化对动作做tanh压缩值函数发散折扣因子过大从0.99降到0.95训练后期性能下降过拟合动力学模型增加模型集成用多个模型平均推断速度慢变分分布太复杂简化到均值场或用低秩近似对初始状态敏感先验设置不当用均匀先验覆盖整个状态空间6. 从理论到代码一个最小实现框架6.1 整体架构设计一个完整的Control as Inference实现包含四个模块动力学模型、变分策略、值函数估计、优化器。我用PyTorch搭了一个最小框架核心代码如下import torch import torch.nn as nn import torch.optim as optim class DynamicsModel(nn.Module): def __init__(self, state_dim, action_dim, hidden64): super().__init__() self.net nn.Sequential( nn.Linear(state_dim action_dim, hidden), nn.ReLU(), nn.Linear(hidden, hidden), nn.ReLU(), nn.Linear(hidden, state_dim) ) def forward(self, state, action): return self.net(torch.cat([state, action], dim-1)) class VariationalPolicy(nn.Module): def __init__(self, state_dim, action_dim, hidden64): super().__init__() self.shared nn.Sequential( nn.Linear(state_dim, hidden), nn.ReLU(), nn.Linear(hidden, hidden), nn.ReLU() ) self.mu_head nn.Linear(hidden, action_dim) self.logvar_head nn.Linear(hidden, action_dim) def forward(self, state): features self.shared(state) mu self.mu_head(features) logvar self.logvar_head(features).clamp(-10, 2) return mu, logvar def sample(self, state): mu, logvar self.forward(state) std torch.exp(0.5 * logvar) eps torch.randn_like(std) return mu eps * std, mu, logvar这个框架里动力学模型用MSE损失训练策略用ELBO损失训练。两个优化器分开学习率可以独立调。6.2 训练循环的关键步骤训练循环的伪代码如下从回放缓冲区采样一批轨迹用动力学模型预测下一状态计算预测误差更新动力学模型用变分策略采样动作计算ELBO更新策略每隔N步更新目标网络如果用了值函数记录KL散度、代价、策略熵等指标这里的关键是动力学模型和策略的更新频率要匹配。如果动力学模型更新太快策略跟不上更新太慢策略基于过时的模型优化会跑偏。我的经验是动力学模型每步更新一次策略每4步更新一次比例大概是4:1。6.3 超参数配置参考以下是我在机械臂项目里调通的一套超参数供参考超参数数值说明学习率动力学1e-3Adam优化器学习率策略3e-4Adam优化器温度 (\alpha) 初始值5.0指数退火到0.1退火率0.995每步衰减批量大小256轨迹片段长度折扣因子0.97兼顾长期回报策略网络隐藏层2层64维ReLU激活动力学网络隐藏层2层64维ReLU激活训练步数100k视任务复杂度调整这套参数在抓取任务上收敛稳定成功率从传统MPC的72%提升到89%。提升主要来自软最优控制的探索能力和对噪声的鲁棒性。6.4 评估与可视化训练过程中一定要监控几个关键指标ELBO值、KL散度、策略熵、实际代价。ELBO应该单调上升KL散度应该逐渐减小并稳定在某个正值因为真实后验和变分分布总有差距策略熵应该逐渐下降但不要降到零保留一定探索性。可视化方面我习惯画两张图一张是状态轨迹在相平面上的分布另一张是策略输出的动作分布。前者看轨迹是否收敛到目标附近后者看策略是否过于确定。如果动作分布退化成一条线说明 (\alpha) 太小了需要调大。7. 和MPC的对比与融合思路7.1 本质差异优化 vs 推断MPC和Control as Inference的本质差异在于MPC是在线求解一个确定性优化问题而Control as Inference是离线学习一个概率分布。MPC每个控制周期都要重新优化计算量大但适应性强Control as Inference训练时计算量大但推理时只需要前向传播一次实时性好。这个差异决定了它们的适用场景MPC适合模型精确、计算资源充足的场景比如工业过程控制Control as Inference适合模型不确定、需要快速推理的场景比如机器人抓取、自动驾驶。7.2 融合方案推断提供先验MPC做精调一个实用的融合方案是用Control as Inference学一个策略分布把这个分布作为MPC的初始猜测或者终端代价。具体来说MPC的优化问题可以写成[ \min_{u_{0:H-1}} \sum_{t0}^{H-1} c(x_t,u_t) - \alpha \log q(u_t|x_t) ]最后那一项是推断策略提供的“引导项”。当MPC的代价函数不确定时这一项会把解拉向推断策略认为合理的区域。我在项目中用这个方案MPC的求解时间从平均15ms降到了8ms因为初始猜测更接近最优解迭代次数减少了。7.3 实时性考量Control as Inference的推理时间主要花在策略网络的前向传播上。一个2层64维的网络在CPU上大概0.1msGPU上0.01ms。这个速度完全可以满足100Hz以上的控制频率。相比之下MPC的求解时间通常在毫秒到几十毫秒量级高频控制时会有压力。所以如果你的控制频率要求高于100HzControl as Inference是更好的选择。如果低于50Hz两者都可以看模型精度和调参难度。8. 我踩过的坑和最后分享几个技巧第一个坑是忽略动作范围约束。变分策略输出的是高斯分布理论上可以采样到无穷大。实际部署时一定要加tanh压缩或者clip否则电机指令会爆掉。我一开始没加机械臂直接撞限位心疼了好久。第二个坑是动力学模型过拟合。用少量数据训练动力学模型训练误差很小但测试误差很大。后来加了模型集成用5个模型预测的均值和方差方差大的地方降低策略更新幅度问题缓解了很多。第三个坑是ELBO和实际性能不一致。ELBO上升但实际代价不降排查后发现是KL散度项权重太大策略过于保守。后来给KL项加了一个系数 (\beta)从1.0降到0.1实际性能就上来了。这个 (\beta) 就是β-VAE里的那个β思路是一样的。最后分享一个小技巧用策略熵作为早停指标。当策略熵降到初始值的10%以下时说明策略已经过于确定继续训练容易过拟合。这时候要么增大 (\alpha)要么停止训练。这个技巧帮我省了很多调参时间。这个方向后续还可以往分层推断扩展把长时域任务分解成子任务每个子任务用一个变分分布建模上层做任务推断下层做动作推断。我最近在尝试这个思路初步结果还不错等成熟了再写一篇分享。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进