8强化学习:导论(第2版)

规划与学习

模型 기반 RL

动态规划启发式搜索模型学习

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第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:从真实经验中学习价值函数/策略

模型学习:从真实经验中学习环境的转移模型

规划:使用学习到的模型生成模拟经验,进一步改进价值函数/策略

python
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)的核心思想:不要均匀地规划所有状态,而是优先规划那些价值变化最大的状态。

python
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.Q

8.4 当模型不准确时

如果环境发生变化(非平稳环境),之前学到的模型可能不再准确。Dyna-Q 的问题在于它假设模型是确定性的,一旦学到错误的转移,就会持续产生错误的模拟经验。解决方案包括:

  • 定期用新经验更新模型
  • 使用置信度加权的模型
  • 结合 model-based 和 model-free 方法

三、实战案例

3.1 案例:Dyna-Q 在不同规划步数下的表现

python
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(处理随机环境)

实现优先级扫描并与均匀规划对比

8/11规划与学
0%