很多人初次接触深度强化学习时注意力基本都集中在 DQN、PPO、SAC 这类“无模型”算法上因为这些算法接口清晰、社区资料多跑通一个 CartPole 或 Mujoco 任务就能立刻获得反馈。但研究做深之后就会发现“样本效率”是一个绕不开的问题无模型强化学习面对真实机器人、推荐系统、工业控制这类难以快速试错的环境时训练成本实在太高。这时基于模型的强化学习Model-Based Reinforcement Learning简称 MBRL就体现出了独特价值。本文围绕伯克利 2026 春季深度强化学习课程通常对应本科/研究生阶段的强化学习进阶课程第 15 讲的内容整理一份基于模型强化学习的系统学习笔记与实战教程。文章会先讲清楚 MBRL 的核心思想和解决痛点再拆解主流方法的技术原理最后通过一个可运行的简化示例展示“学环境模型 用模型做规划”的基本流程。无论你是在读论文、复现算法还是准备把强化学习落到真实项目里这部分内容都值得完整看一遍。1. 背景与核心概念1.1 为什么需要基于模型的强化学习先从一个对比入手。假设让一个强化学习智能体学习玩一款赛车游戏无模型算法Model-Free RL的做法是智能体在游戏里不断试错靠大量采样积累经验再用这些经验更新策略网络。这个思路很直接但代价是采样量非常大。比如训练 DQN 玩 Atari 游戏通常需要几千万帧画面如果换到真实机器人上每一步试错都可能造成机械磨损甚至安全事故显然不现实。基于模型的强化学习器采用的路线则更偏“人类思维”。智能体除了和环境交互还会先尝试学习一个环境的“动力学模型”。这个模型描述的是在某个状态 s 下采取动作 a环境会转移到什么新状态 s同时产生多少奖励 r。一旦学习到近似的环境模型智能体就可以不通过真实环境直接在虚拟模型中进行大量“预演”Rollout通过模拟去评估动作的好坏再到真实环境中执行只有少数步骤的策略。换句话说MBRL 的核心优势在于“用模型代替真实环境做模拟”这就显著降低了真实环境交互次数。用更专业的语言来说Model-Free RL直接学习策略 π(s)→a 或价值函数 Q(s,a)不显式建模转移概率 P(s|s,a) 和奖励 R(s,a)。Model-Based RL先学习环境模型 T(s|s,a) 和奖励模型 R(s,a)再利用模型进行规划Planning或策略优化。这种差异带来的直接结果就是MBRL 在样本效率上通常比 MF-RL 高一个量级以上。代价是模型往往存在系统误差一旦模型学偏策略就会在“幻觉”里被优化最终部署到真实环境时可能表现很差。所以现代 MBRL 的核心工作几乎都是在解决“模型误差”和“模型利用”的平衡问题。1.2 MBRL 的典型应用场景MBRL 非常适合以下场景真实机器人运动控制。比如四足机器人、机械臂抓取真实试错成本高优先用 MBRL 获得一个初始策略再结合少量真实数据微调。自动驾驶或仿真器加速。用仿真环境学一个近似模型在大规模并行模拟中评估规划结果。推荐系统与商业决策。真实用户反馈是昂贵的通过离线日志构建用户响应模型在模型上做策略搜索。冷冻、能源调度等连续控制任务。动作空间可能是连续变量状态变化存在明显物理规律适合用模型预测。1.3 MBRL 的两个核心阶段从流程上看MBRL 通常包含两个互相迭代的环节模型学习阶段从历史数据中拟合环境动力学典型监督学习问题。规划/策略优化阶段利用模型做轨迹预测或策略搜索。这两个阶段是耦合的。模型学不准规划结果就没有意义规划搜索效率低模型样本优势又发挥不出来。因此不同 MBRL 算法的差异往往就在这两个阶段的具体设计上。2. 环境准备与版本说明本文的实操示例基于 Python 生态下面列出的是一套常见环境组合。版本需要根据你的项目实际情况调整本文示例以常见环境为例重点演示配置思路。2.1 安装 Python 与虚拟环境建议使用 Python 3.8 及以上版本。为了隔离项目依赖先创建虚拟环境python3 -m venv mbrl_env source mbrl_env/bin/activate # Windows 下使用 mbrl_env\Scripts\activate2.2 安装核心依赖本文示例不需要太多第三方库主要用到PyTorch用于搭建神经网络模型。GymnasiumOpenAI Gym 的继承者提供标准强化学习环境接口。NumPy数值计算。Matplotlib可视化可选。安装命令pip install torch --index-url https://download.pytorch.org/whl/cu118 # 按你的 CUDA 版本调整 pip install gymnasium pip install numpy matplotlib如果你希望在 MuJoCo 连续控制环境上测试更完整的 MBRL 流程还需要安装pip install gymnasium[mujoco]不过这属于额外步骤本文的核心示例用经典 CartPole 环境即可演示“模型学习 在线规划”思想降低环境安装造成的负担。2.3 为什么选择这类环境CartPole 状态空间是四维连续变量动作空间是离散的“左/右”是非常适合展示 MBRL 的环境。它状态维度低学习一个近似动力学模型很快规划流程也能在 CPU 上实时运行。如果你后续要去跑 MuJoCo 的 Humanoid、Walker2d 等高维连续控制任务只需要把模型的输入输出维度调整并匹配连续动作规划即可。3. 核心语法、配置或原理拆解3.1 环境模型的监督学习本质MBRL 的第一步是学一个前向动力学模型Forward Dynamics Model。常见做法是训练一个神经网络把当前状态和动作作为输入预测下一状态。如果环境是离散状态也可以分类输出但大多数真实控制环境是连续状态所以通常使用回归模型。假设状态向量为 s动作向量为 a网络参数 θ那么模型可以表示为fθ(s, a) ≈ s在 PyTorch 中搭建一个简单的两层全连接网络代码结构如下import torch import torch.nn as nn class DynamicsModel(nn.Module): 前向动力学模型输入 [state, action]输出 [delta_state]。 这里预测状态变化量而非直接预测下一时刻状态在很多环境中更容易学习。 def __init__(self, state_dim, action_dim, hidden_dim128): super().__init__() self.net nn.Sequential( nn.Linear(state_dim action_dim, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, state_dim) # 输出维度等于状态维度 ) def forward(self, state, action): x torch.cat([state, action], dim-1) delta_state self.net(x) return delta_state这段代码的输出是状态的增量 Δs。预测时可以用 s s Δs。之所以这样设计是因为很多环境的动力学满足“短时间内的状态增量相对状态本身变化平稳”直接回归下一状态有时会让网络更难以拟合。3.2 一个关键差异学习单步模型还是轨迹模型在实战中模型既可以学习“一步转移”也可以学习“k 步转移”。一步转移模型最有通用性每次迭代时预测下一时刻状态然后递归多步得到整条轨迹。但这种递归预测方式会累积误差单步误差小多步之后偏差可能飞速膨胀。轨迹模型如直接预测未来 T 步的状态变化在特定任务中误差累积更小但适用环境有限。因此现代 MBRL 大多使用“单步模型 多条轨迹采样 / 截断预测长度”的组合。这也就是我们常说的“写实模型”应用策略。3.3 在线规划MPC 与 Random Shooting模型学完以后怎么用于决策最直接的方法是模型预测控制Model Predictive ControlMPC。MPC 的核心思想是在当前状态 s 下规划固定长度 H 的动作序列使模型预测出的未来 H 步累计奖励最大执行第一步动作然后获得真实环境反馈进入下一个时间步重新规划。Random Shooting 是 MPC 里最朴素的规划方法随机采样 N 条长度为 H 的动作序列用模型逐一模拟出每条序列带来的累计奖励选择最优的一条执行它的第一个动作。伪代码如下def random_shooting_plan(model, env, current_state, horizon, num_candidates): # 初始化最优动作序列 best_return -float(inf) best_actions None for _ in range(num_candidates): # 随机采样一条动作序列 actions np.random.uniform(lowenv.action_space.low, highenv.action_space.high, size(horizon, action_dim)) # 用模型做轨迹模拟 state current_state total_return 0.0 for t in range(horizon): pred_state model.predict(state, actions[t]) reward reward_model(pred_state) # 或直接使用环境奖励函数 total_return reward state pred_state if total_return best_return: best_return total_return best_actions actions return best_actions[0]虽然 Random Shooting 很粗糙但在低维控制任务上往往能获得不错效果。后续进阶方案包括Cross-Entropy MethodCEM迭代式采样根据上一轮奖励排名更新采样分布。带引导的策略规划学习一个可行的策略网络再结合 MPC 搜索兼顾效率与稳定性。3.4 数据收集与模型重训练MBRL 不能一开始就依赖模型。最常用的是 Dyna-style 迭代流程初始化策略为空或随机策略。用当前模型或随机策略在真实环境中采样。把真实转移数据加入数据集。用全部历史数据重新训练动力学模型。在模型中进行额外模拟虚拟经验来生成更多数据。回到步骤 2。之所以强调“真实数据与虚拟数据混合”是为了防止模型误差被模型自身产生的幻觉数据进一步放大。这也是 MBRL 工程落地最容易踩的坑。4. 完整实战案例一个简化 Dyna 风格 MBRL下面用一个完整示例演示怎么把“学模型 随机规划”跑起来。示例环境选择 CartPole-v1用 Gymnasium 接口实现全部代码放在单个 Python 文件中方便直接运行。4.1 创建项目结构简易工程结构如下mbrl_demo/ ├── main.py └── README.md代码本身不长一个文件足够。这说明 MBRL 入门并不需要复杂的框架。4.2 完整代码# 文件路径src/mbrl_demo.py # 说明一个基于模型的强化学习最小示例 # 环境CartPole-v1Gymnasium import copy import numpy as np import gymnasium as gym import torch import torch.nn as nn import torch.optim as optim from collections import deque # ---------- 1. 搭建动力学模型 ---------- class DynamicsModel(nn.Module): 输入 [state, action]输出 [delta_state] 由于 CartPole 的动作是离散的我们把动作做 one-hot 处理后拼接到状态里。 def __init__(self, state_dim4, action_dim2, hidden_dim128): super().__init__() self.net nn.Sequential( nn.Linear(state_dim action_dim, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, state_dim) ) def forward(self, state, action_onehot): x torch.cat([state, action_onehot], dim-1) delta_state self.net(x) return delta_state # ---------- 2. 从环境数据构造训练样本 ---------- def prepare_dataset(real_data, device): real_data: 每条记录是 (state, action, next_state, done) 将动作转为 one-hot 向量 states [] actions [] next_states [] for state, action, next_state, done in real_data: state np.asarray(state, dtypenp.float32) next_state np.asarray(next_state, dtypenp.float32) onehot np.zeros(2, dtypenp.float32) onehot[action] 1.0 states.append(state) actions.append(onehot) next_states.append(next_state) states torch.tensor(np.array(states), dtypetorch.float32).to(device) actions torch.tensor(np.array(actions), dtypetorch.float32).to(device) next_states torch.tensor(np.array(next_states), dtypetorch.float32).to(device) delta next_states - states return states, actions, delta # ---------- 3. 训练动力学模型 ---------- def train_dynamics_model(model, data_buffer, device, epochs100, batch_size64, lr1e-3): if len(data_buffer) batch_size: return # 数据不足时暂不训练 states, actions, delta prepare_dataset(data_buffer, device) dataset_size states.shape[0] optimizer optim.Adam(model.parameters(), lrlr) loss_fn nn.MSELoss() model.train() for epoch in range(epochs): # 按批次随机采样 idx np.random.choice(dataset_size, batch_size, replaceTrue) s states[idx].to(device) a actions[idx].to(device) delta_true delta[idx].to(device) delta_pred model(s, a) loss loss_fn(delta_pred, delta_true) optimizer.zero_grad() loss.backward() optimizer.step() model.eval() # ---------- 4. 用模型做随机规划 ---------- def random_shot_plan(model, env, state, horizon20, candidates50, devicecpu): 随机采样 candidates 条动作序列用模型模拟每条序列的回报。 CartPole 动作是离散的因此随机采样指的是在 {0,1} 之间随机选动作 而不是连续动作采样。 state_tensor torch.tensor(state, dtypetorch.float32).unsqueeze(0).to(device) best_action None best_return -float(inf) for _ in range(candidates): # 随机生成一个长度为 horizon 的离散动作序列 actions_seq np.random.choice([0, 1], sizehorizon) total_return 0.0 predicted_state state_tensor # 逐步推演轨迹 for t in range(horizon): onehot np.zeros(2, dtypenp.float32) onehot[actions_seq[t]] 1.0 action_tensor torch.tensor(onehot, dtypetorch.float32).unsqueeze(0).to(device) delta model(predicted_state, action_tensor) predicted_state predicted_state delta # 简化的奖励CartPole 中只要未结束每步 1 # 这里我们用模型的预测状态做简单评分 # 真实环境中通常还需要考虑角度、位置范围等因素 pole_angle predicted_state[0, 2].item() angle_reward 1.0 - abs(pole_angle) # 角度越接近 0 越好 total_return angle_reward if total_return best_return: best_return total_return best_action actions_seq[0] return best_action if best_action is not None else 0 # ---------- 5. 主流程 ---------- def main(): device cuda if torch.cuda.is_available() else cpu env gym.make(CartPole-v1) model DynamicsModel(state_dim4, action_dim2).to(device) # 数据缓冲池只保留最近 5000 条真实转移 data_buffer deque(maxlen5000) num_episodes 100 # 先随机采样少量数据确保模型有初始训练集 warmup_episodes 5 for episode in range(1, num_episodes 1): state, _ env.reset() total_reward 0.0 done False truncated False step 0 while not done and not truncated: # 前 warmup_episodes 个 episode 用随机策略探索 if episode warmup_episodes: action env.action_space.sample() else: # 用学到的模型做规划并执行第一步动作 action random_shot_plan(model, env, state, devicedevice) next_state, reward, done, truncated, info env.step(action) # 尝试做一点奖励塑形方便模型评估 # 这里保留原始 reward 用于真实累计用实际结果判断 data_buffer.append((state, action, next_state, done)) state next_state total_reward reward step 1 # 每 10 步重新训练一次模型 if step % 10 0: train_dynamics_model(model, data_buffer, device, epochs50) print(fEpisode {episode}: total_reward {total_reward}) env.close() if __name__ __main__: main()4.3 运行与预期结果在项目根目录执行python src/mbrl_demo.py由于前几个 episode 完全随机探索初始累计奖励可能很低。随着动力学模型开始学习到 CartPole 的“角度变化规律”随机规划算法会越来越倾向于选择能保持角度稳定的动作后面的 episode 累计奖励会逐步提高。需要说明的是这只是一个最小可视化示例。真实 MBRL 算法想要在 CartPole 上稳定超过经典 PPO 或 DQN还需要在以下方面做更多优化对动作序列做更好的采样例如 CEM。同时训练多个模型构成集成。引入不确定性量化机制。在模型预测的轨迹上计算真实 reward而不是人工设计近似奖励。这个示例的核心价值是把“学习模型 在线规划”的完整闭环展示清楚。你可以在自己的实验环境里替换成连续控制任务改成均匀采样连续动作即可。4.4 结果说明与局限运行脚本时你会发现模型训练过程并不像无模型 RL 那样有一个稳定的策略评估指标而是在“探索数据采样 → 训练模型 → 随机规划”之间循环。因为随机规划本身就有随机性单次轨迹的回报波动很大这是 MBRL 入门时一个很直观的体验。如果模型训练不够好规划器可能给出看起来“正确”但实际执行后效果很差的动作。这正是模型误差带来的典型问题。后续我们可以通过“模型集成 在线模型验证”来缓解。5. 常见问题与排查思路MBRL 在实操中比无模型强化学习更容易“莫名其妙地失败”因为它多了一个模型学习环节误差来源链条更长。下面是几类高频问题及排查顺序。问题现象常见原因解决思路模型训练 loss 很低但规划效果差模型只在数据分布内预测准确规划时进入分布外状态限制规划步数增加真实数据多样性用模型集成估计不确定性规划器一直选择相同动作候选动作随机采样太少或模型奖励函数有问题增加候选数量改用 CEM检查奖励塑形在模型模拟中表现好部署到真实环境崩模型存在系统误差策略被幻觉数据误导加入真实数据重训缩短规划步长执行 MPC 后只执行第一步训练数据不足模型无法拟合复杂动力学先用随机策略采集更多数据使用数据增强模型利用了无效转移比如穿墙数据中没有包含约束信息模拟时对状态边界进行 clip在模型输入中显式加入约束特征每个 episode 的训练时间太长模型训练和规划循环都太慢减小模型规模减少训练 epochs使用 GPU并行采样候选轨迹这几个问题里最值得深入理解的是“模型误差累积”。以 CartPole 为例单步模型预测角度误差可能只有 0.01 弧度但 20 步之后误差可以累积到 0.2此时规划器可能认为“向左转是最好的”真实环境中却可能已经翻车。这种误差导致的问题通常不是模型“没学会”而是“没学会在规划轨迹覆盖的状态下做预测”。排查步骤建议先单独评估模型精度在真实环境中随机采样 5000 条转移数据计算一步预测的 MSE。如果一步误差就很大说明模型表达能力或任务特征化不足。再评估多步预测精度用模型从同一个状态出发预测 10 步轨迹对比真实轨迹。误差增长曲线如果很快爆炸优先缩短规划长度。最后评估规划器固定模型不变只更换规划算法比如从随机采样改成 CEM看性能是否提升。6. 最佳实践与工程建议从论文复现到真实系统落地MBRL 有很多工程细节值得打磨。下面列出几条我认为最重要的建议。6.1 用集成模型量化不确定性单个神经网络模型很难告诉系统“自己什么时候不确定”。工程上最简单可靠的做法是训练多个动力学模型例如 5 个初始化不同训练数据随机排序不同。预测时利用多个模型的均值作为最终预测同时计算预测分布的标准差。当模型标准差过大时说明当前输入状态或动作已经远离数据覆盖区。此时可以在规划器中添加惩罚项比如对超出模型置信区间的轨迹进行折扣。这种方法能显著降低“幻觉策略”带来的风险。6.2 数据和模型同时更新但要冷静MBRL 中最自然的诱惑是每采集一小批数据就立刻重训模型。频繁重训不仅耗时还容易让模型只记住最近的数据发生“灾难性遗忘”。实践做法是维护一个大容量的重放缓冲区覆盖多轮迭代数据。每隔固定步数或固定数据量再重训模型。如果新模型在验证集的 loss 比旧模型差保留旧模型继续使用。6.3 规划器的选择要匹配任务对于低维连续控制任务CEM 往往比随机采样更高效。对于高维动作空间纯随机采样几乎不可行需要使用更强的搜索策略或学习一个策略网络来引导规划。一个常见做法是“策略引导规划”先训练一个快速策略 π规划时不仅随机采样还结合策略网络给出的动作。这样既保留模型预测的准确性又不至于在巨大动作空间里盲目搜索。6.4 奖励模型不等于真实奖励在真实环境不可微分的情况下MBRL 经常需要额外学习奖励模型。标准做法是让奖励模型也作为监督学习的一部分参与训练但不能和动力学模型共用一个网络否则表征耦合会影响两个任务的收敛速度。如果奖励函数本身是已知的比如赛车的赛道边界奖励、机器人的关节限位惩罚尽量使用解析奖励函数避免学习奖励模型带来的额外误差。6.5 安全边界与生产环境注意事项基于模型的规划一旦部署到真实系统安全风险会比无模型强化学习更高因为策略动作可能来源于模型幻觉。工程上需要在模拟器中严格验证后再接入真实系统。真实运行前用随机规划器运行一个“安全性巡检”检查模型是否输出异常偏大的控制信号。对执行的动作做限幅、限速处理。对关键任务设置人工熔断开关当模型不确定性指标超过阈值时切换回保守控制器。详细记录每次规划的模型置信度、预测轨迹和真实轨迹方便事后归因。6.6 从零开始的 MBRL 项目清单如果你要在一个新环境下上手 MBRL推荐按以下顺序推进先用随机策略收集 1000 条单步转移数据。训练一个单步动力学模型并打印验证集 loss 与多步 rollout 误差曲线。用 MPC 在线规划但先限制规划步长比如 5 步确认控制不死。逐步增大规划步长观察真实轨迹与模型预测轨迹的偏差。加入模型集成、数据增强等技巧提升稳定性。引入策略网络引导规划进一步提升样本效率。7. 总结与学习路线这一讲围绕伯克利深度强化学习课程中的基于模型的强化学习展开核心收获可以概括为三点第一MBRL 的关键价值是大幅降低真实环境交互成本第二MBRL 不是一个单一算法而是一套“学习环境模型 基于模型规划/训练策略”的方法论第三模型误差是 MBRL 落地的主要矛盾几乎所有进阶算法都在围绕“如何降低误差、如何对误差保持鲁棒”做文章。如果你刚看完课程第 15 讲下一步可以按这个学习路线继续深入复现一个完整的经典 MBRL 算法例如 PILCO、PETProbabilistic Ensembles with Trajectory Sampling或 TD-MPC。把本文的 CartPole 示例扩展成连续控制环境感受高维动作空间下随机规划的局限再尝试 CEM。阅读模型不确定性建模的论文理解 Bayesian 神经网络、深度集成、Dropout 不确定性估计的区别。学习 Model-Based 与 Model-Free 的混合算法比如把模型生成的数据用于无模型策略训练这是近年来工业界实用度较高的方向。实战时优先关注的不只是模型准确率而是“模型在哪不准确”。下次你在训练 MBRL 时如果遇到规划崩坏不妨先画出多步预测误差曲线很多时候问题根源一眼就能看出来。动手把代码跑通一次比看十遍公式更能建立直觉。如果本文对你理解基于模型的强化学习有帮助可以收藏备用后续我会继续更新深度强化学习系列的笔记与实战案例。