CS285 深度强化学习(七):Model-based RL(上)——学会想象
前面我们学习的算法——策略梯度、Actor-Critic、DQN——都属于 Model-free(无模型) 方法。它们直接从经验中学习策略或价值函数,不需要了解环境是如何工作的。但这种"不理解环境"的学习方式有一个代价:样本效率低。DQN 需要数百万帧才能学会一个 Atari 游戏,而人类玩几分钟就能上手。Model-based RL(基于模型的强化学习) 采用不同的策略:先学习环境的模型,再用模
CS285 深度强化学习(七):Model-based RL(上)——学会想象
0. 写在前面
本篇概要
前面我们学习的算法——策略梯度、Actor-Critic、DQN——都属于 Model-free(无模型) 方法。它们直接从经验中学习策略或价值函数,不需要了解环境是如何工作的。
但这种"不理解环境"的学习方式有一个代价:样本效率低。DQN 需要数百万帧才能学会一个 Atari 游戏,而人类玩几分钟就能上手。
Model-based RL(基于模型的强化学习) 采用不同的策略:先学习环境的模型,再用模型进行规划。这就像人类可以在脑海中"想象"行动的后果,不需要真的去尝试。
这一篇我们将学习:
- Model-free vs Model-based 的本质区别
- 如何学习环境模型
- 如何用模型进行规划(MPC)
- 模型误差的问题与应对
系列导航
| 序号 | 标题 | 状态 |
|---|---|---|
| 1 | 开篇:强化学习的世界观 | ✅ 已完成 |
| 2 | 模仿学习:让 AI 学会"抄作业" | ✅ 已完成 |
| 3 | 策略梯度(上):从直觉到公式 | ✅ 已完成 |
| 4 | 策略梯度(下):让训练更稳定 | ✅ 已完成 |
| 5 | Actor-Critic:双剑合璧 | ✅ 已完成 |
| 6 | Value-based 方法:DQN 家族 | ✅ 已完成 |
| 7 | Model-based RL(上):学会想象(本文) | 📖 当前 |
| 8 | Model-based RL(下):Dyna 与 MBPO | ⏳ 待更新 |
| … | … | … |
前置知识
- 阅读过本系列前几篇
- 了解 MDP 的基本概念
- 了解神经网络的基本训练
预计阅读时间
约 30 分钟
1. 为什么需要模型?
1.1 Model-free 的样本效率问题
让我们回顾一下 Model-free 方法的训练流程:
问题:每一次学习都需要与真实环境交互。
- Atari DQN:需要约 2 亿帧(约 38 天的游戏时间)
- 机器人学习:真实机器人交互慢、昂贵、可能损坏
- 自动驾驶:不可能让 AI 在真实道路上随意"试错"
1.2 人类是怎么学的?
想象你要学习打台球。你会怎么做?
不是这样:随机挥杆,看结果,重复百万次。
而是这样:
- 观察物理规律(球怎么滚动、反弹)
- 在脑海中"模拟"不同的击球方式
- 选择看起来最好的方案
- 执行,观察结果,修正模型
人类通过建立世界模型,可以在"想象"中进行大量试验,而不需要真实尝试。
1.3 Model-based 的核心思想
Model-based RL 模仿这种学习方式:
优势:
- 用真实数据学习模型
- 在模型中进行大量"想象"交互
- 真实交互次数大大减少
2. 环境模型的定义
2.1 什么是环境模型?
环境模型(也叫动态模型、转移模型)描述了环境如何响应智能体的动作:
s t + 1 = f ( s t , a t ) s_{t+1} = f(s_t, a_t) st+1=f(st,at)
或者更一般地(考虑随机性):
s t + 1 ∼ p ( s t + 1 ∣ s t , a t ) s_{t+1} \sim p(s_{t+1} | s_t, a_t) st+1∼p(st+1∣st,at)
有时候我们也需要学习奖励模型:
r t = g ( s t , a t ) r_t = g(s_t, a_t) rt=g(st,at)
2.2 模型的类型
2.3 确定性模型 vs 随机模型
| 类型 | 输出 | 优点 | 缺点 |
|---|---|---|---|
| 确定性 | 单一的 s ′ s' s′ | 简单、快速 | 无法表示不确定性 |
| 随机 | s ′ s' s′ 的分布 | 可以表示不确定性 | 更复杂、训练更难 |
什么时候需要随机模型?
- 环境本身是随机的(如骰子游戏)
- 状态观测不完整(部分可观测)
- 需要量化模型的不确定性
3. 学习环境模型
3.1 数据收集
首先,我们需要数据来训练模型。数据的形式是转移元组:
D = { ( s t , a t , s t + 1 ) } \mathcal{D} = \{(s_t, a_t, s_{t+1})\} D={(st,at,st+1)}
数据来源:
- 随机策略探索
- 当前策略执行
- 专家演示
- 混合策略
3.2 模型训练:监督学习
学习模型本质上是一个监督学习问题:
- 输入: ( s t , a t ) (s_t, a_t) (st,at)
- 输出: s t + 1 s_{t+1} st+1(或 Δ s = s t + 1 − s t \Delta s = s_{t+1} - s_t Δs=st+1−st)
- 损失函数:均方误差
L ( θ ) = E ( s , a , s ′ ) ∼ D [ ∥ f θ ( s , a ) − s ′ ∥ 2 ] L(\theta) = \mathbb{E}_{(s,a,s') \sim \mathcal{D}} \left[ \| f_\theta(s, a) - s' \|^2 \right] L(θ)=E(s,a,s′)∼D[∥fθ(s,a)−s′∥2]
3.3 预测 s ′ s' s′ 还是 Δ s \Delta s Δs?
预测下一状态 s ′ s' s′:
s ^ ′ = f θ ( s , a ) \hat{s}' = f_\theta(s, a) s^′=fθ(s,a)
预测状态变化 Δ s \Delta s Δs:
s ^ ′ = s + f θ ( s , a ) \hat{s}' = s + f_\theta(s, a) s^′=s+fθ(s,a)
为什么预测 Δ s \Delta s Δs 更好?
- 更容易学习:状态变化通常比状态本身小,更容易拟合
- 天然的残差连接:类似 ResNet 的思想
- 保持状态结构:当动作很小时, Δ s ≈ 0 \Delta s \approx 0 Δs≈0
3.4 PyTorch 实现
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
class DynamicsModel(nn.Module):
"""确定性动态模型"""
def __init__(self, obs_dim, act_dim, hidden_dim=256):
super().__init__()
self.net = nn.Sequential(
nn.Linear(obs_dim + act_dim, hidden_dim),
nn.ReLU(),
nn.Linear(hidden_dim, hidden_dim),
nn.ReLU(),
nn.Linear(hidden_dim, obs_dim)
)
def forward(self, obs, action):
"""预测状态变化 Δs"""
x = torch.cat([obs, action], dim=-1)
delta = self.net(x)
return delta
def predict(self, obs, action):
"""预测下一状态"""
delta = self.forward(obs, action)
return obs + delta
class ModelTrainer:
"""模型训练器"""
def __init__(self, model, lr=1e-3):
self.model = model
self.optimizer = optim.Adam(model.parameters(), lr=lr)
def train_step(self, obs, action, next_obs):
"""单步训练"""
# 预测
pred_next = self.model.predict(obs, action)
# 损失
loss = nn.functional.mse_loss(pred_next, next_obs)
# 更新
self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()
return loss.item()
def train_epoch(self, dataset, batch_size=256):
"""训练一个 epoch"""
indices = np.random.permutation(len(dataset['obs']))
total_loss = 0
n_batches = 0
for i in range(0, len(indices), batch_size):
batch_idx = indices[i:i+batch_size]
obs = torch.FloatTensor(dataset['obs'][batch_idx])
action = torch.FloatTensor(dataset['actions'][batch_idx])
next_obs = torch.FloatTensor(dataset['next_obs'][batch_idx])
loss = self.train_step(obs, action, next_obs)
total_loss += loss
n_batches += 1
return total_loss / n_batches
3.5 数据归一化
训练神经网络时,输入输出的归一化非常重要:
class Normalizer:
"""在线归一化器"""
def __init__(self, shape):
self.mean = np.zeros(shape)
self.std = np.ones(shape)
self.count = 0
def update(self, data):
"""更新统计量"""
batch_mean = np.mean(data, axis=0)
batch_std = np.std(data, axis=0)
batch_count = len(data)
# 增量更新
total_count = self.count + batch_count
delta = batch_mean - self.mean
self.mean = self.mean + delta * batch_count / total_count
self.std = np.sqrt(
(self.std**2 * self.count + batch_std**2 * batch_count) / total_count
+ delta**2 * self.count * batch_count / total_count**2
)
self.count = total_count
def normalize(self, data):
return (data - self.mean) / (self.std + 1e-8)
def denormalize(self, data):
return data * self.std + self.mean
4. 用模型进行规划
有了模型之后,我们如何用它来选择动作?
4.1 规划的基本思想
规划(Planning):在模型中"想象"不同的行动序列,评估它们的结果,选择最好的。
4.2 随机打靶(Random Shooting)
最简单的规划方法:随机采样一堆动作序列,选择最好的。
算法:Random Shooting
输入:当前状态 s, 模型 f, 规划步数 H, 采样数 K
1. 采样 K 个动作序列:
A_k = (a_0^k, a_1^k, ..., a_{H-1}^k), k = 1, ..., K
2. 对每个序列,用模型预测轨迹:
s_0 = s
for t = 0 to H-1:
s_{t+1} = f(s_t, a_t^k)
R_k += r(s_t, a_t^k)
3. 选择回报最高的序列:
k* = argmax_k R_k
4. 返回第一个动作 a_0^{k*}
优点:简单、可并行
缺点:效率低,需要大量采样
4.3 交叉熵方法(CEM)
CEM(Cross-Entropy Method) 是随机打靶的改进版,通过迭代优化采样分布:
算法:CEM (Cross-Entropy Method)
输入:当前状态 s, 模型 f, 规划步数 H
参数:采样数 K, 精英数 E, 迭代次数 I
1. 初始化采样分布:μ = 0, σ = 1(每个时间步的动作分布)
2. for i = 1 to I:
a) 从 N(μ, σ) 采样 K 个动作序列
b) 用模型评估每个序列的回报
c) 选择回报最高的 E 个序列(精英)
d) 用精英序列更新分布:
μ = mean(精英动作)
σ = std(精英动作)
3. 返回 μ 的第一个动作
4.4 PyTorch 实现 CEM
class CEMPlanner:
"""交叉熵方法规划器"""
def __init__(
self,
model,
action_dim,
action_low,
action_high,
horizon=20,
n_samples=500,
n_elite=50,
n_iterations=5
):
self.model = model
self.action_dim = action_dim
self.action_low = action_low
self.action_high = action_high
self.horizon = horizon
self.n_samples = n_samples
self.n_elite = n_elite
self.n_iterations = n_iterations
def plan(self, state, reward_fn):
"""
规划最优动作
Args:
state: 当前状态
reward_fn: 奖励函数 r(s, a)
Returns:
最优动作
"""
# 初始化分布
mean = np.zeros((self.horizon, self.action_dim))
std = np.ones((self.horizon, self.action_dim))
state = torch.FloatTensor(state)
for _ in range(self.n_iterations):
# 采样动作序列 (n_samples, horizon, action_dim)
noise = np.random.randn(self.n_samples, self.horizon, self.action_dim)
actions = mean + std * noise
# 裁剪到动作范围
actions = np.clip(actions, self.action_low, self.action_high)
# 评估每个序列
returns = self._evaluate_sequences(state, actions, reward_fn)
# 选择精英
elite_idx = np.argsort(returns)[-self.n_elite:]
elite_actions = actions[elite_idx]
# 更新分布
mean = np.mean(elite_actions, axis=0)
std = np.std(elite_actions, axis=0) + 1e-6
# 返回第一个动作
return mean[0]
def _evaluate_sequences(self, init_state, action_sequences, reward_fn):
"""评估动作序列的回报"""
n_samples = action_sequences.shape[0]
returns = np.zeros(n_samples)
with torch.no_grad():
for i in range(n_samples):
state = init_state.clone()
total_reward = 0
for t in range(self.horizon):
action = torch.FloatTensor(action_sequences[i, t])
# 计算奖励
reward = reward_fn(state.numpy(), action.numpy())
total_reward += reward
# 预测下一状态
state = self.model.predict(
state.unsqueeze(0),
action.unsqueeze(0)
).squeeze(0)
returns[i] = total_reward
return returns
5. 模型预测控制(MPC)
5.1 MPC 的核心思想
MPC(Model Predictive Control):在每个时间步重新规划,只执行规划结果的第一个动作。
5.2 为什么要重新规划?
- 模型不完美:长期预测会累积误差,短期更准确
- 获取新信息:执行动作后,观察到真实的下一状态
- 适应变化:环境可能是非平稳的
5.3 MPC 的优势
| 优势 | 说明 |
|---|---|
| 样本效率高 | 充分利用模型,减少真实交互 |
| 无需训练策略 | 直接用模型规划,不需要策略网络 |
| 可解释性 | 可以分析规划过程 |
| 鲁棒性 | 重新规划可以纠正偏差 |
5.4 MPC 的局限
| 局限 | 说明 |
|---|---|
| 计算开销大 | 每步都要规划 |
| 短视 | 规划步数有限,可能错过长期收益 |
| 依赖模型质量 | 模型差则规划差 |
| 需要奖励函数 | 不像 model-free 可以从标量奖励学习 |
5.5 完整的 MPC 实现
class MPCAgent:
"""基于模型预测控制的智能体"""
def __init__(
self,
obs_dim,
act_dim,
action_low,
action_high,
horizon=20,
n_samples=500
):
self.obs_dim = obs_dim
self.act_dim = act_dim
self.action_low = action_low
self.action_high = action_high
# 动态模型
self.model = DynamicsModel(obs_dim, act_dim)
self.model_trainer = ModelTrainer(self.model)
# 数据集
self.dataset = {
'obs': [],
'actions': [],
'next_obs': []
}
# 规划器
self.planner = CEMPlanner(
model=self.model,
action_dim=act_dim,
action_low=action_low,
action_high=action_high,
horizon=horizon,
n_samples=n_samples
)
def add_data(self, obs, action, next_obs):
"""添加经验数据"""
self.dataset['obs'].append(obs)
self.dataset['actions'].append(action)
self.dataset['next_obs'].append(next_obs)
def train_model(self, epochs=50):
"""训练动态模型"""
dataset = {
'obs': np.array(self.dataset['obs']),
'actions': np.array(self.dataset['actions']),
'next_obs': np.array(self.dataset['next_obs'])
}
for epoch in range(epochs):
loss = self.model_trainer.train_epoch(dataset)
if epoch % 10 == 0:
print(f"Epoch {epoch}, Model Loss: {loss:.6f}")
def select_action(self, obs, reward_fn):
"""选择动作(使用 MPC)"""
return self.planner.plan(obs, reward_fn)
def train_mpc(env, agent, reward_fn, n_episodes=100, n_init_random=5):
"""训练 MPC 智能体"""
episode_rewards = []
for episode in range(n_episodes):
obs = env.reset()
episode_reward = 0
done = False
while not done:
# 前几个 episode 用随机策略收集数据
if episode < n_init_random:
action = env.action_space.sample()
else:
action = agent.select_action(obs, reward_fn)
next_obs, reward, done, info = env.step(action)
# 存储数据
agent.add_data(obs, action, next_obs)
episode_reward += reward
obs = next_obs
episode_rewards.append(episode_reward)
# 每个 episode 后重新训练模型
if episode >= n_init_random - 1:
agent.train_model(epochs=50)
print(f"Episode {episode}, Reward: {episode_reward:.2f}")
return episode_rewards
6. 模型误差问题
6.1 模型不完美的后果
在真实应用中,学习的模型永远不会完美。模型误差会导致:
短期影响:单步预测有小误差
长期影响:误差累积,多步预测严重偏离
6.2 模型利用问题(Model Exploitation)
更严重的问题:优化器会利用模型的错误。
当我们用 CEM 等方法优化动作序列时,优化器可能会找到让模型预测给出高回报、但真实环境效果很差的动作。
例子:
- 模型错误地认为某个动作会带来高奖励
- 优化器发现了这个"漏洞"
- 执行后真实效果很差
这就像找到了考试的"作弊方法",但这个方法只在模型的"虚假世界"里有效。
6.3 应对策略
策略 1:限制规划步数
- 只规划短期(如 10-30 步)
- 短期预测更准确
策略 2:使用模型集成
- 训练多个模型
- 用分歧来估计不确定性
- 保守地对待高不确定性区域
策略 3:不断重新规划(MPC)
- 执行一步后用真实状态重新规划
- 自然地纠正偏差
策略 4:在线学习
- 持续收集新数据
- 不断更新模型
7. 模型集成(Ensemble)
7.1 为什么使用集成?
单个模型无法告诉我们它有多"确定"。训练多个模型,如果它们:
- 预测一致:模型比较确定
- 预测不一致:模型不确定
7.2 集成的实现
class EnsembleDynamicsModel(nn.Module):
"""集成动态模型"""
def __init__(self, obs_dim, act_dim, hidden_dim=256, n_models=5):
super().__init__()
self.n_models = n_models
# 创建多个独立的模型
self.models = nn.ModuleList([
DynamicsModel(obs_dim, act_dim, hidden_dim)
for _ in range(n_models)
])
def forward(self, obs, action, model_idx=None):
"""
预测状态变化
Args:
obs: 观测
action: 动作
model_idx: 指定使用哪个模型(None 则使用全部)
Returns:
predictions: 预测结果
"""
if model_idx is not None:
return self.models[model_idx](obs, action)
# 使用所有模型
predictions = [model(obs, action) for model in self.models]
return torch.stack(predictions) # (n_models, batch, obs_dim)
def predict_with_uncertainty(self, obs, action):
"""预测并返回不确定性"""
predictions = self.forward(obs, action) # (n_models, batch, obs_dim)
mean = predictions.mean(dim=0) # 均值预测
std = predictions.std(dim=0) # 不确定性
return obs + mean, std
def sample_prediction(self, obs, action):
"""随机选择一个模型进行预测"""
idx = np.random.randint(self.n_models)
delta = self.models[idx](obs, action)
return obs + delta
class EnsembleTrainer:
"""集成模型训练器"""
def __init__(self, ensemble_model, lr=1e-3):
self.ensemble = ensemble_model
self.optimizers = [
optim.Adam(model.parameters(), lr=lr)
for model in ensemble_model.models
]
def train_step(self, obs, action, next_obs):
"""训练所有模型"""
losses = []
for i, (model, optimizer) in enumerate(
zip(self.ensemble.models, self.optimizers)
):
# 预测
delta = model(obs, action)
pred_next = obs + delta
# 损失
loss = nn.functional.mse_loss(pred_next, next_obs)
# 更新
optimizer.zero_grad()
loss.backward()
optimizer.step()
losses.append(loss.item())
return np.mean(losses)
7.3 使用不确定性进行规划
在规划时,可以利用不确定性来做更保守的决策:
def pessimistic_reward(self, state, action, reward_fn, penalty_coef=1.0):
"""悲观奖励:奖励 - 不确定性惩罚"""
state_t = torch.FloatTensor(state).unsqueeze(0)
action_t = torch.FloatTensor(action).unsqueeze(0)
_, uncertainty = self.ensemble.predict_with_uncertainty(state_t, action_t)
uncertainty = uncertainty.mean().item()
base_reward = reward_fn(state, action)
return base_reward - penalty_coef * uncertainty
8. Model-based vs Model-free 对比
8.1 总体对比
| 特性 | Model-free | Model-based |
|---|---|---|
| 样本效率 | 低 | 高 |
| 计算开销 | 低(训练时) | 高(规划时) |
| 渐近性能 | 通常更高 | 可能受限于模型质量 |
| 适用环境 | 通用 | 可预测的环境 |
| 调试难度 | 较难 | 可以检查模型 |
8.2 什么时候用 Model-based?
适合 Model-based:
- 样本获取成本高(机器人、真实世界)
- 环境动态相对简单、可预测
- 需要快速适应新任务
- 有领域知识可以辅助建模
适合 Model-free:
- 环境复杂、难以建模
- 可以大量采样(仿真环境)
- 追求最高的渐近性能
- 环境包含不可预测的元素
8.3 融合两者
现代方法越来越多地结合 Model-free 和 Model-based:
下一篇我们将详细讨论这些混合方法。
9. 论文延伸
核心论文
-
- 经典的 MBRL 入门论文
- 展示了 MPC + 神经网络模型的有效性
-
- PETS 算法
- 使用概率集成模型
-
- 元学习 + Model-based
- 快速适应新环境
推荐阅读
-
Benchmarking Model-Based Reinforcement Learning (Wang et al., 2019)
- MBRL 方法的系统比较
- 很好的综述
-
When to Trust Your Model: Model-Based Policy Optimization (Janner et al., 2019)
- MBPO 论文
- 分析模型误差的影响
10. 思考题
Q1: 模型误差的传播
假设模型的单步预测误差为 ϵ \epsilon ϵ。如果我们做 H H H 步的规划:
- 误差如何累积?是线性的还是指数的?
- 这对规划步数的选择有什么启示?
考虑误差传播的两种情况:
- 加性误差: s ^ t + 1 = s t + 1 + ϵ \hat{s}_{t+1} = s_{t+1} + \epsilon s^t+1=st+1+ϵ
- 乘性误差: s ^ t + 1 = s t + 1 ⋅ ( 1 + ϵ ) \hat{s}_{t+1} = s_{t+1} \cdot (1 + \epsilon) s^t+1=st+1⋅(1+ϵ)
在神经网络模型中,误差传播通常更复杂,可能是指数级的。
Q2: 模型的泛化
模型在训练数据的分布上表现很好,但在新的状态-动作对上可能很差。
- 这和监督学习中的什么问题类似?
- 有什么方法可以提高模型的泛化能力?
关键词:分布偏移(distribution shift)
思考:
- 如何确保探索到多样的状态?
- 集成如何帮助检测 OOD(out-of-distribution)输入?
Q3: 奖励函数的设计
MPC 需要一个奖励函数 r ( s , a ) r(s, a) r(s,a) 来评估规划。
- 如果我们只有稀疏的 episode 结束奖励(如游戏输赢),MPC 还能用吗?
- 有什么方法可以处理稀疏奖励?
考虑:
- 学习一个奖励模型
- 使用 shaping rewards
- 结合 model-free 方法(如学习一个价值函数作为终端奖励)
参考资料
- [1] CS 285 Fall 2023 Lecture 10-12: Model-Based Reinforcement Learning
- [2] Nagabandi, A., et al. (2018). Neural Network Dynamics for Model-Based Deep RL with Model-Free Fine-Tuning. ICRA.
- [3] Chua, K., et al. (2018). Deep Reinforcement Learning in a Handful of Trials using Probabilistic Dynamics Models. NeurIPS.
- [4] Berkeley Deep RL Bootcamp: Model-based RL
下一篇预告:CS285 深度强化学习(八):Model-based RL(下)——Dyna 与 MBPO
我们将学习如何将 Model-based 和 Model-free 方法结合,包括经典的 Dyna 架构、现代的 MBPO 算法。同时会解析 HW4 的核心思路。
📝 本文基于 UC Berkeley CS 285 Fall 2023 课程内容整理
如有错误或建议,欢迎在评论区指出!
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐
所有评论(0)