第8章:规划与学习(Planning and Learning with Tabular Methods)
一、导读
1.1 本章学习目标
第8章探讨如何将规划(planning)与学习(learning)结合起来。在 tabular 方法中,Dyna 架构是最经典的框架:智能体既通过与环境的真实交互来学习,又通过与内部模型的模拟交互来规划。
本章核心学习目标:
- 理解 Dyna 架构:结合直接学习和规划的统一框架
- 掌握 Dyna-Q 算法:最简单的 Dyna 方法
- 理解模型学习:从经验中学习环境的转移模型
- 了解优先级扫描(Priority Sweeping):专注于重要状态的规划
- 理解真实样本 vs 模拟样本的区别
1.2 为什么本章如此重要
Dyna 架构揭示了强化学习的一个深刻洞察:学习到的模型可以用于"想象"未来,从而加速学习。这个思想在现代强化学习中无处不在:模型预测控制(MPC)、基于模型的策略优化(MBPO)、世界模型(World Models)等都是 Dyna 思想的现代延伸。
1.3 核心问题引导
规划与学习的区别是什么?它们如何互补?
Dyna-Q 如何在没有额外环境交互的情况下加速学习?
当环境模型不准确时,Dyna 方法会出什么问题?
优先级扫描如何提高规划效率?
二、核心概念详解
8.1 Dyna 架构
Dyna 架构的核心思想:
直接 RL:从真实经验中学习价值函数/策略
模型学习:从真实经验中学习环境的转移模型
规划:使用学习到的模型生成模拟经验,进一步改进价值函数/策略
import numpy as np
from collections import defaultdict
class MazeEnv:
def __init__(self, size=6):
self.size = size
self.start = (size-1, 0)
self.goal = (0, size-1)
self.blocks = set()
for i in range(1, size-1):
self.blocks.add((i, 2))
self.actions = [(-1,0),(1,0),(0,-1),(0,1)]
def reset(self):
self.state = self.start
return self.state
def step(self, action):
dr, dc = self.actions[action]
r, c = self.state
nr, nc = r+dr, c+dc
if 0 <= nr < self.size and 0 <= nc < self.size and (nr,nc) not in self.blocks:
self.state = (nr, nc)
reward = 0 if self.state == self.goal else -1
done = self.state == self.goal
return self.state, reward, done
class DynaQ:
def __init__(self, env, n_planning_steps=5, alpha=0.5, gamma=0.95, epsilon=0.1):
self.env = env
self.n_planning = n_planning_steps
self.alpha = alpha
self.gamma = gamma
self.epsilon = epsilon
self.Q = defaultdict(lambda: np.zeros(4))
self.model = {}
def select_action(self, state):
if np.random.random() < self.epsilon:
return np.random.randint(4)
return np.argmax(self.Q[state])
def real_experience(self, state, action, reward, next_state):
best_next = np.max(self.Q[next_state])
target = reward if next_state == self.env.goal else reward + self.gamma * best_next
self.Q[state][action] += self.alpha * (target - self.Q[state][action])
self.model[(state, action)] = (reward, next_state)
def planning(self):
for _ in range(self.n_planning):
if not self.model:
break
keys = list(self.model.keys())
idx = np.random.randint(len(keys))
state, action = keys[idx]
reward, next_state = self.model[(state, action)]
best_next = np.max(self.Q[next_state])
target = reward if next_state == self.env.goal else reward + self.gamma * best_next
self.Q[state][action] += self.alpha * (target - self.Q[state][action])
def run_dyna_q(n_planning_steps=5, n_episodes=100):
env = MazeEnv(size=6)
agent = DynaQ(env, n_planning_steps=n_planning_steps)
steps_per_episode = []
for ep in range(n_episodes):
state = env.reset()
steps = 0
done = False
while not done:
action = agent.select_action(state)
next_state, reward, done = env.step(action)
agent.real_experience(state, action, reward, next_state)
agent.planning()
state = next_state
steps += 1
steps_per_episode.append(steps)
return steps_per_episode
for n_plan in [0, 5, 50]:
steps = run_dyna_q(n_planning_steps=n_plan, n_episodes=50)
print(f"Dyna-Q (n={n_plan}): avg steps (last 10) = {np.mean(steps[-10:]):.1f}")8.2 模型学习
在 Dyna-Q 中,模型是确定性的:对于每个 (state, action) 对,存储观察到的 (reward, next_state)。如果环境是随机的,需要维护转移概率的统计。
8.3 优先级扫描
优先级扫描(Priority Sweeping)的核心思想:不要均匀地规划所有状态,而是优先规划那些价值变化最大的状态。
class PrioritySweeping:
def __init__(self, env, theta=0.01, alpha=0.5, gamma=0.95):
self.env = env
self.theta = theta
self.alpha = alpha
self.gamma = gamma
self.Q = defaultdict(lambda: np.zeros(4))
self.model = {}
self.predecessors = defaultdict(set)
def update(self, state, action):
best_next = np.max(self.Q[state]) if state != self.env.goal else 0
target = -1 + self.gamma * best_next
old_q = self.Q[state][action]
self.Q[state][action] = target
return abs(target - old_q)
def solve(self, max_iterations=1000):
for iteration in range(max_iterations):
state = self.env.reset()
done = False
while not done:
action = np.random.randint(4)
next_state, reward, done = self.env.step(action)
self.model[(state, action)] = (reward, next_state)
self.predecessors[next_state].add((state, action))
priority = self.update(state, action)
if priority > self.theta:
queue = [(priority, state, action)]
while queue:
queue.sort(key=lambda x: -x[0])
_, s, a = queue.pop(0)
_, ns = self.model.get((s, a), (0, s))
for pred_s, pred_a in self.predecessors[s]:
p = self.update(pred_s, pred_a)
if p > self.theta:
queue.append((p, pred_s, pred_a))
state = next_state
return self.Q8.4 当模型不准确时
如果环境发生变化(非平稳环境),之前学到的模型可能不再准确。Dyna-Q 的问题在于它假设模型是确定性的,一旦学到错误的转移,就会持续产生错误的模拟经验。解决方案包括:
- 定期用新经验更新模型
- 使用置信度加权的模型
- 结合 model-based 和 model-free 方法
三、实战案例
3.1 案例:Dyna-Q 在不同规划步数下的表现
def dyna_comparison():
planning_steps = [0, 5, 50, 100]
results = {}
for n_plan in planning_steps:
all_steps = []
for run in range(5):
steps = run_dyna_q(n_planning_steps=n_plan, n_episodes=30)
all_steps.append(steps)
avg_steps = np.mean(all_steps, axis=0)
results[n_plan] = avg_steps
print("Dyna-Q comparison (avg steps per episode):")
for n_plan, steps in results.items():
print(f" n={n_plan}: first={steps[0]:.0f}, "
f"last={steps[-1]:.0f}, "
f"mean={np.mean(steps):.1f}")
dyna_comparison()四、本章小结
Dyna 架构:结合直接学习和规划,利用模型生成模拟经验
Dyna-Q:最简单的 Dyna 方法,使用确定性模型
优先级扫描:优先规划价值变化大的状态
模型准确性:当环境变化时,需要更新模型
五、思考与练习
思考题
Dyna 方法在什么情况下可能比纯 model-free 方法更差?
优先级扫描的时间复杂度是多少?如何高效实现?
如何将 Dyna 思想应用到连续状态空间?
编程练习
在 Maze 环境中比较不同规划步数的 Dyna-Q
实现带随机模型的 Dyna(处理随机环境)
实现优先级扫描并与均匀规划对比