工作台课程

CS221 · 人工智能:原理与技术

Lecture 7 · 马尔可夫决策过程

Lecture 7 · 马尔可夫决策过程

CS221: Artificial Intelligence — Principles and Techniques · Stanford · Autumn 2025
📅 Oct 13 · 💻 mdp.py


承上启下:从确定性到随机性

前两讲(Lecture 5 · 搜索算法 ILecture 6 · UCS 与 A 搜索)研究的是搜索问题:给定起始状态、后继函数(动作、代价、下一状态)和终止判断,找一条总代价最小的动作序列。这套框架有一个隐含的关键假设——动作的结果是确定性的(deterministic):在状态 $s$ 执行动作 $a$,永远到达同一个状态 $\text{Succ}(s,a)$。所以 UCS/A 才能放心地输出一条固定的路径作为解。

本讲把这个假设去掉:动作的结果是随机的(stochastic)。执行同一个动作,可能以不同概率到达不同状态——就像掷骰子,或者出门时无法预知的交通状况。这一泛化得到的模型就是马尔可夫决策过程(Markov Decision Process, MDP)

动机例子:你要去超市,选步行、骑车还是开车?开车平均最快,但可能堵车、可能找不到停车位;步行最慢但时间确定。理性的决策不能只看「最好情况」或「最坏情况」,而要在不确定性下最大化期望收益——这正是 MDP 的用武之地。

本讲假设我们完全知道环境的运作规律(转移概率与奖励);下一讲 Lecture 8 · 强化学习 将进一步去掉这个假设,讨论环境是黑盒时如何从交互中学习。


1. 马尔可夫决策过程(MDP)

1.1 历史与命名

MDP 起源于 1950 年代的运筹学(Operations Research),奠基性工作是 Bellman 1957 年的《Dynamic Programming》。这个名字的三个词各有含义:

  • 马尔可夫(Markov):来自马尔可夫链,指马尔可夫性质——给定当前状态,未来与过去条件独立:

$$\mathbb{P}(s_{t+1} \mid s_t, a_t, s_{t-1}, a_{t-1}, \dots, s_0) = \mathbb{P}(s_{t+1} \mid s_t, a_t)$$

即当前状态 $s_t$ 已经编码了预测未来所需的全部历史信息。这不是对世界的断言,而是对「状态怎么定义」的要求:如果历史信息影响未来,就应该把它塞进状态里。
- 决策(Decision):与马尔可夫链不同,这里有一个智能体在每一步主动选择动作,而不是被动地看链条随机演化。
- 过程(Process):事情随时间一步一步顺序发生。

提示

MDP = 马尔可夫链 + 决策。马尔可夫链回答「世界会怎样随机演化」,MDP 额外回答「我该怎么行动才能把这个随机演化引向对我有利的方向」。搜索问题则是 MDP 在「转移概率全为 1」时的退化特例。

1.2 形式化定义

一个 MDP 由以下部分组成(沿用课程代码接口):

class MDP:
    def start_state(self) -> Any:
        """起始状态"""
        raise NotImplementedError

    def successors(self, state: Any) -> list[Step]:
        """从 state 出发的后继:(动作, 概率, 奖励, 新状态)"""
        raise NotImplementedError

    def is_end(self, state: Any) -> bool:
        """是否为终止状态"""
        raise NotImplementedError

    def discount(self) -> float:
        """折扣因子 γ ∈ [0,1]"""
        raise NotImplementedError

@dataclass(frozen=True)
class Step:
    action: Any       # 动作
    prob: float       # 到达该状态的概率
    reward: float     # 获得的奖励
    state: Any        # 到达的状态

数学记号

  • $\text{Actions}(s)$:状态 $s$ 下所有可用动作的集合;
  • $T(s, a, s')$:转移概率(transition probability),在状态 $s$ 执行动作 $a$ 后到达 $s'$ 的概率;
  • $\text{Reward}(s, a, s')$:这次转移获得的奖励(reward)(可以为负,表示代价);
  • $\gamma \in [0, 1]$:折扣因子(discount factor),控制未来奖励相对当下的重要性;
  • 概率归一化约束:对每个合法的 $(s, a)$,

$$\sum_{s'} T(s, a, s') = 1$$

即执行动作后必然落到某个后继状态,$T(s,a,\cdot)$ 是一个合法的概率分布。

注意

搜索问题里我们最小化代价(cost),MDP 里约定最大化奖励(reward)。两者只是符号翻转:把「耗时 1 分钟」写成奖励 $-1$ 即可。写公式或代码时务必确认方向,否则会把「最优」算成「最差」。

1.3 例子 1:不可靠的电车

Lecture 6 · UCS 与 A* 搜索 里的电车问题加上随机性:

  • 位置编号 $1$ 到 $n$(与搜索版相同),从 $1$ 出发,目标是到达 $n$;
  • 步行:从 $i$ 到 $i+1$,耗时 1 分钟,结果确定;
  • 电车:从 $i$ 到 $2i$,耗时 2 分钟,但有概率 $p$ 故障——花了 2 分钟却停在原地(官方讲义演示用 $n=10$、$p=0.4$);
  • 目标:期望总耗时最短,即最大化期望奖励(奖励 = 负的耗时)。
graph LR
    1 -->|"walk, p=1, r=-1"| 2
    1 -->|"tram 成功, p=0.6, r=-2"| 2B["2 (=2×1)"]
    1 -->|"tram 失败, p=0.4, r=-2"| 1

    style 1 fill:#c8e6c9,stroke:#2e7d32
    style 2B fill:#ffe0b2,stroke:#e65100

注意同一个动作 tram 对应两条出边(成功/失败),概率和为 $0.6 + 0.4 = 1$——这正是「动作的结果是分布」的直观体现。

class FlakyTramMDP(MDP):
    def __init__(self, num_locs: int, failure_prob: float):
        self.num_locs = num_locs
        self.failure_prob = failure_prob

    def start_state(self) -> int:
        return 1

    def successors(self, state: int) -> list[Step]:
        successors = []

        # 步行(确定性)
        if state + 1 <= self.num_locs:
            successors.append(Step(
                action="walk", prob=1.0, reward=-1, state=state + 1
            ))

        # 电车(随机性)
        if 2 * state <= self.num_locs:
            # 成功:到达目标
            successors.append(Step(
                action="tram", prob=1 - self.failure_prob,
                reward=-2, state=2 * state
            ))
            # 失败:停在原地
            successors.append(Step(
                action="tram", prob=self.failure_prob,
                reward=-2, state=state
            ))

        return successors

    def is_end(self, state: int) -> bool:
        return state == self.num_locs

    def discount(self) -> float:
        return 1.0

例子

从状态 $i$ 反复尝试坐电车直到成功,成功前平均需要 $\frac{1}{1-p}$ 次尝试(几何分布的期望),每次花 2 分钟,所以期望耗时 $\frac{2}{1-p}$。当 $p=0.4$ 时约 $3.3$ 分钟就能从 $i$ 跳到 $2i$;而步行需要 $i$ 分钟。$i$ 越大电车越划算——最优策略自然是「位置靠后时坐电车、靠前时步行」,这个权衡会由后面的价值迭代自动算出来,无需手工分析。

1.4 例子 2:骰子游戏

每一轮你二选一:

  • quit(退出):立刻获得 \$10,游戏结束; - 选 **stay**(继续):先获得 \$4,然后对方掷 6 面骰子: - 掷出 1 或 2(概率 $\tfrac{1}{3}$):游戏被强制结束; - 掷出 3–6(概率 $\tfrac{2}{3}$):进入下一轮,可以再次选择。 ```mermaid graph LR In -->|"quit, p=1, r=10"| End In -->|"stay 继续, p=2/3, r=4"| In In -->|"stay 终止, p=1/3, r=4"| End style In fill:#e1f5fe,stroke:#0277bd style End fill:#ffe0b2,stroke:#e65100 ``` ```python class DiceGameMDP(MDP): def start_state(self) -> str: return "in" def successors(self, state: str) -> list[Step]: return [ Step(action="quit", prob=1, reward=10, state="end"), Step(action="stay", prob=1/3, reward=4, state="end"), Step(action="stay", prob=2/3, reward=4, state="in"), ] def is_end(self, state: str) -> bool: return state == "end" def discount(self) -> float: return 1.0 ``` 这个例子状态极少(只有 `in` 和 `end`),却完整体现了 MDP 的所有要素,后面会用它手算最优策略。注意 stay 的 \$4 是**先拿到手**的——无论骰子结果如何,这一步的奖励都是 4。 ### 1.5 MDP vs 搜索问题 | 维度 | 搜索问题 | MDP | |:---|:---|:---| | 起始状态 | ✓ start_state | ✓ start_state | | 终止判断 | ✓ is_end | ✓ is_end | | 后继函数 | ✓ successors | ✓ successors | | 优化目标 | 代价(cost),最小化 | 奖励(reward),最大化期望 | | **核心区别** | 每个动作 → **一个**确定的下一状态 | 每个动作 → 下一状态上的**概率分布** | --- ## 2. 策略(Policy) ### 2.1 定义:为什么解不再是动作序列 **策略(policy)** $\pi$ 是一个从状态到动作的映射函数: XMATHXPLACEHOLDERX2XENDX ```python Policy = Callable[[State], Action] ``` - 搜索问题的解是一条**动作序列**(如 `[walk, tram, walk, tram]`):因为结果确定,未来会经过哪些状态在出发前就已知,提前排好一串动作即可。 - MDP 的解必须是**策略函数** $\pi(s)$:因为结果随机,你无法预知自己会落到哪个状态,所以必须**对每个可能到达的状态都预先备好决策**。

    提示

    策略更像「行为准则」而非「行程单」。行程单(动作序列)在第一个意外发生时就作废了;行为准则(策略)则规定「无论我身处何地,都知道下一步做什么」,天然对随机性免疫。这也是「解的形式」随问题假设变化的一个典型例子:假设变弱(确定 → 随机),解就得变强(序列 → 函数)。

    ### 2.2 示例策略 ```python # 骰子游戏的策略 def always_stay_policy(state: str) -> str: return "stay" def always_quit_policy(state: str) -> str: return "quit" # 电车问题的策略 def always_walk_policy(state: int) -> str: return "walk" def tram_if_possible_policy(mdp: MDP, state: int) -> str: """能坐电车就坐,否则步行""" if state * 2 <= mdp.num_locs: return "tram" else: return "walk" ``` ### 2.3 策略执行(Rollout) 固定一个策略,在 MDP 中实际运行一遍,产生一条随机轨迹,称为一次 **rollout**:智能体查询策略得到动作,环境按 $T(s,a,\cdot)$ 采样下一状态并给出奖励,如此往复直到终止。 ```python def generate_rollout(mdp: MDP, policy: Policy) -> Rollout: """执行策略并返回轨迹""" steps = [] state = mdp.start_state() while not mdp.is_end(state): # 策略决定动作 action = policy(state) # MDP 根据概率分布采样结果 successors = [s for s in mdp.successors(state) if s.action == action] probs = [s.prob for s in successors] choice = np.random.choice(len(successors), p=probs) step = successors[choice] steps.append(step) # 进入下一状态 state = step.state return Rollout(steps=steps, discount=mdp.discount()) ``` 注意分工:**策略负责选动作,环境负责掷骰子**。同一个策略跑两次 rollout,得到的轨迹一般不同——这就是为什么评价策略要看「期望」。 ### 2.4 效用(Utility)与折扣 一条轨迹的**效用(utility)**定义为**折扣奖励之和**。设轨迹依次获得奖励 $r_1, r_2, r_3, \dots$,则 XMATHXPLACEHOLDERX3XENDX 其中 $\gamma$ 是折扣因子:每晚一步到手的奖励,价值就要打一次 $\gamma$ 的折扣。 ```python def compute_utility(steps: list[Step], discount: float) -> float: """计算效用(折扣奖励和)""" rewards = [step.reward * discount ** i for i, step in enumerate(steps)] return sum(rewards) ``` **折扣因子 $\gamma$ 的两种取值**: - $\gamma = 1$:所有奖励等权重,适合**保证有限步终止**的问题(如骰子游戏、电车问题); - $\gamma < 1$:更看重近期奖励。除了体现「现钱比期货值钱」的偏好,它还有关键的数学作用——对可能无限长的轨迹,只要单步奖励有界 $|r_t| \le R_{\max}$,效用就被几何级数控制住: XMATHXPLACEHOLDERX4XENDX 从而期望效用是良定义的,后面的迭代算法也因此保证收敛。

    例子

    轨迹奖励为 $[4, 4, 4, 10]$:

    • $\gamma = 1$:$u = 4 + 4 + 4 + 10 = 22$
    • $\gamma = 0.9$:$u = 4 + 4(0.9) + 4(0.81) + 10(0.729) = 18.13$

    越靠后的奖励缩水越狠——最后那笔 \$10 实际只值 \$7.29。

    注意

    效用是随机变量,价值是它的期望。单次 rollout 的效用忽高忽低,不能用来断言策略好坏;策略的「好坏」由下一节定义的期望效用 $V_\pi$ 刻画。混淆这两者是初学最常见的错误。

    --- ## 3. 策略评估(Policy Evaluation) ### 3.1 问题:给定策略,它有多好? **策略的价值(value)** $V_\pi(s)$:从状态 $s$ 出发、始终按策略 $\pi$ 行动所得效用的**期望**: XMATHXPLACEHOLDERX5XENDX 期望是对所有随机转移取的。策略评估(policy evaluation)就是计算 $V_\pi$。 ### 3.2 方法一:蒙特卡洛估计 最直接的办法:跑很多次 rollout,效用取平均。 ```python def monte_carlo_policy_evaluation(mdp: MDP, policy: Policy, num_rollouts: int) -> float: """通过采样估计策略价值""" utilities = [generate_rollout(mdp, policy).utility for _ in range(num_rollouts)] return np.mean(utilities) ``` 由大数定律,样本均值收敛到真实期望。但收敛速度是 $O(1/\sqrt{n})$——要把估计精度提高 10 倍,样本量得增加 100 倍,又慢方差又大。既然我们**已知** $T$ 和 $\text{Reward}$,就应该有比「反复试」更聪明的精确算法。 ### 3.3 Q 值(Q-value) 先定义一个核心的中间量。给定任意价值函数 $V$,**Q 值**表示「在状态 $s$ 执行动作 $a$,之后的前景按 $V$ 来估价」的期望回报: XMATHXPLACEHOLDERX6XENDX 逐项读:对每个可能的落点 $s’$,按转移概率 $T$ 加权,回报由两部分组成——这一步的即时奖励 $\text{Reward}(s,a,s’)$,加上打过折扣的后续价值 $\gamma V(s’)$。 ```python def compute_q_value(successors: list[Step], discount: float, values: dict) -> float: """计算 Q(s, a, V)""" return sum( step.prob * (step.reward + discount * values[step.state]) for step in successors ) ```

    提示

    Q 值是「向前看一步」的算子:它把「未来所有状态的估价 $V$」压缩成「现在这个动作的估价」。策略评估和价值迭代都只是围绕这一个算子做文章——前者把 $V$ 递归地定义成自己的一步展开,后者在展开时额外做一次 $\max$。

    ### 3.4 贝尔曼方程(策略评估版) 按策略 $\pi$ 行动时,状态 $s$ 的价值满足递归关系(Bellman equation for a fixed policy): XMATHXPLACEHOLDERX7XENDX 含义:**现在的价值 = 执行 $\pi(s)$ 的期望即时奖励 + 折扣后的期望未来价值**。终止状态的价值恒为 $0$(没有未来了)。这不是一个可以直接代入求值的公式——$V_\pi$ 出现在等号两边——而是一个方程组,解法见下。 ### 3.5 策略评估算法:不动点迭代 把贝尔曼方程当作更新规则反复套用,即**自举(bootstrapping)**——用旧的估计值算新的估计值: XMATHXPLACEHOLDERX8XENDX ```python def policy_evaluation(mdp: MDP, policy: Policy, max_iters: int = 100, tolerance: float = 1e-5): """计算策略 π 的价值""" # 初始化:终止状态价值为 0,其余为 -100(悲观的初始猜测,具体数值不影响收敛结果) values = {state: 0 if mdp.is_end(state) else -100 for state in get_states(mdp)} distances = [] for iter in range(max_iters): new_values = {} for state in values: if mdp.is_end(state): new_values[state] = 0 continue # 只考虑策略选择的动作 action = policy(state) successors = get_action_successors(mdp, state)[action] new_values[state] = compute_q_value(successors, mdp.discount(), values) # 检查收敛 distance = max(abs(new_values[s] - values[s]) for s in values) distances.append(distance) if distance < tolerance: break values = new_values return PolicyEvaluationResult(values=values, distances=distances) ``` **自举过程的语义**:$V^{(t)}$ 恰好等于「按策略执行 $t$ 步然后强制终止」的期望效用—— - 第 0 步:立即终止的价值; - 第 1 步:执行策略 1 步再终止的价值; - 第 2 步:执行策略 2 步再终止的价值; - …… - 收敛极限:执行策略到底的真实价值 $V_\pi$。 ```mermaid graph LR V0["V^(0)
    终止"] --> V1["V^(1)
    1步+终止"] V1 --> V2["V^(2)
    2步+终止"] V2 --> V3["V^(3)
    3步+终止"] V3 --> Vn["V^(∞)
    真实价值"] style V0 fill:#ffcdd2,stroke:#c62828 style Vn fill:#c8e6c9,stroke:#2e7d32 ```

    说明

    每轮迭代用 $\max_s |V^{(t)}(s) - V^{(t-1)}(s)|$ 度量变化,小于容差(如 $10^{-5}$)即停。单轮复杂度为 $O(|S| \cdot |S’|)$(每个状态展开其后继),总代价乘以迭代轮数 $T$。相比蒙特卡洛,这是确定性的精确计算,没有采样方差——代价是必须知道完整的 $T$ 和 $\text{Reward}$。另外,求解 $V_\pi$ 本质上是解 $|S|$ 元线性方程组,也可以直接用线性代数求精确解,迭代法胜在实现简单、可随时截断。

    --- ## 4. 价值迭代(Value Iteration) ### 4.1 从评估到优化 - **策略评估**:计算**给定策略**的价值 $V_\pi$; - **价值迭代**:计算**最优策略**的价值 $V^$,并顺带构造最优策略 $\pi^$。 后者看起来难得多(类比:计算 $f(x)$ 在某点的值 vs 求 $\min_x f(x)$),但在 MDP 里两者的算法几乎一模一样——只多一个 $\max$。 ### 4.2 贝尔曼最优方程 **策略评估递归**(固定 $\pi$): XMATHXPLACEHOLDERX9XENDX **贝尔曼最优方程**(Bellman optimality equation, Bellman 1957): XMATHXPLACEHOLDERX10XENDX **唯一区别**:不再照抄固定策略给的动作 $\pi(s)$,而是对所有动作取 **max**。最优策略随之而来——在每个状态选达到最大 Q 值的那个动作: XMATHXPLACEHOLDERX11XENDX

    提示

    为什么「逐状态各自贪心」就能得到全局最优策略?因为 $V^(s’)$ 已经概括了从 $s’$ 出发之后所有未来的最优结果——马尔可夫性质保证未来只依赖于当前状态。所以在 $s$ 处做决策时,不需要展开整棵未来树,只需比较「一步奖励 + 落点的 $V^$」。这正是动态规划「最优子结构」思想在随机环境下的版本,与 Lecture 6 · UCS 与 A* 搜索 中「最短路的子路径也是最短路」一脉相承。

    ### 4.3 价值迭代算法 ```python def value_iteration(mdp: MDP, max_iters: int = 100, tolerance: float = 1e-5): """计算最优价值函数和最优策略""" # 初始化 values = {state: 0 if mdp.is_end(state) else -100 for state in get_states(mdp)} pi = {state: None for state in values} # 最优策略 distances = [] for iter in range(max_iters): new_values = {} for state in values: if mdp.is_end(state): new_values[state] = 0 continue # 对所有动作计算 Q 值,取最大 new_values[state], pi[state] = value_iteration_for_state(mdp, state, values) # 检查收敛 distance = max(abs(new_values[s] - values[s]) for s in values) distances.append(distance) if distance < tolerance: break values = new_values return ValueIterationResult(values=values, pi=pi, distances=distances) def value_iteration_for_state(mdp: MDP, state: Any, values: dict) -> tuple[float, Any]: """计算 V*(state) 和 π*(state)""" # V*(s) = max_a Q(s, a, V*) actions = [] q_values = [] for action, successors in get_action_successors(mdp, state).items(): actions.append(action) q_values.append(compute_q_value(successors, mdp.discount(), values)) # 选择最佳动作 best_value = np.max(q_values) best_action = actions[np.argmax(q_values)] return best_value, best_action ``` ### 4.4 例子:手算骰子游戏

    例子

    贝尔曼最优方程($\gamma = 1$):
    XMATHXPLACEHOLDERX12XENDX
    假设 stay 是最优的,解方程 $V = 4 + \tfrac{2}{3} V$,得 $V = 12$。验证:$12 > 10$,假设成立。所以
    XMATHXPLACEHOLDERX13XENDX
    直觉:一直玩下去平均能拿 \$12(平均玩 3 轮 × 每轮 \$4),高于立刻退出的 \$10,所以最优策略是 stay。注意期望值 12 并不出现在任何单次游戏里——单次可能只拿 \$4,也可能拿 \$20+,12 是长期平均。

    ### 4.5 收敛性

    说明

    当 $\gamma < 1$ 时,贝尔曼更新是一个压缩映射(contraction mapping):对任意两个价值函数 $U, V$,一轮更新后 $\max_s |U’(s) - V’(s)| \le \gamma \max_s |U(s) - V(s)|$——误差每轮至少缩小到 $\gamma$ 倍。由 Banach 不动点定理,迭代从任意初始值出发都指数级收敛到唯一不动点 $V^$(初始化成 $-100$ 还是 $0$ 只影响速度,不影响结果)。
    当 $\gamma = 1$ 时没有这个保证,需要额外条件:所有策略都以概率 1 在有限期望步数内终止(如骰子游戏、电车问题)。若存在能无限循环攒奖励的回路且 $\gamma=1$,价值会发散。

    --- ## 5. 对比与总结 ### 5.1 算法对比 | 算法 | 目标 | 递归关系 | 输出 | |:---|:---|:---|:---| | **策略评估** | 评估给定策略 $\pi$ | $V_\pi(s) = Q(s, \pi(s), V_\pi)$ | $V_\pi$(无策略) | | **价值迭代** | 找最优策略 | $V^(s) = \max_a Q(s, a, V^)$ | $V^$ 与 $\pi^$ | **共同点**: - 都基于贝尔曼式递归做**自举**——用当前估计值更新估计值; - 都迭代至变化量小于容差; - 每轮复杂度 $O(|S| \cdot |A| \cdot |S’|)$(每个状态、每个动作、枚举每个后继),总复杂度再乘迭代轮数 $T$。 ### 5.2 收敛的两个阶段 ```mermaid graph LR A["传播阶段
    信息从终止状态扩散"] --> B["精化阶段
    误差指数衰减 → 0"] style A fill:#e1f5fe,stroke:#0277bd style B fill:#c8e6c9,stroke:#2e7d32 ``` 观察每轮的 distance 曲线,会看到两个阶段: 1. **传播阶段**:初始时只有终止状态的价值是准的,「终点在哪、值多少」的信息每轮向外扩散一步,需要约等于图的直径的轮数才能触达所有状态; 2. **精化阶段**:信息触达后,误差按压缩映射以 $\gamma$ 的比率指数衰减,曲线在对数坐标下呈直线下降。 ### 5.3 MDP 求解流程 ```mermaid flowchart TD A["定义 MDP"] --> B{"有给定策略吗?"} B -->|有| C["策略评估
    Policy Evaluation"] B -->|无| D["价值迭代
    Value Iteration"] C --> E["V_π(s)"] D --> F["V*(s) + π*(s)"] style C fill:#e1f5fe,stroke:#0277bd style D fill:#ffe0b2,stroke:#e65100 ``` --- ## 6. 总结 ```mermaid mindmap root((MDP)) 定义 状态 / 动作 / 转移概率 奖励 / 折扣因子 随机结果 策略 状态 → 动作 Rollout → 效用 价值 V_π = 期望效用 策略评估 计算给定策略的价值 贝尔曼方程(固定 π) 自举迭代 价值迭代 计算最优策略 贝尔曼最优方程(max over a) 同时得到 V* 和 π* ``` **关键要点**: - **MDP** 是搜索问题的随机化推广——动作结果从「一个确定状态」变成「状态上的概率分布」,优化目标从最小化代价变成最大化**期望**效用; - **策略** $\pi(s)$ 是 MDP 的解的形式(类比搜索的动作序列):因为无法预知会落到哪个状态,必须对每个状态备好决策; - **效用**是单条轨迹的折扣奖励和(随机变量),**价值** $V_\pi(s)$ 是它的期望;折扣 $\gamma<1$ 同时表达时间偏好并保证无限期问题良定义; - **Q 值** $Q(s, a, V)$ 是「向前看一步」的算子:即时奖励 + 折扣后继价值的期望; - **策略评估**:以 $V_\pi(s) = Q(s, \pi(s), V_\pi)$ 为更新规则自举迭代; - **价值迭代**:以 $V^
    (s) = \max_a Q(s, a, V^)$ 自举迭代,同时得到 $V^$ 和 $\pi^$; - 两个算法的**唯一区别**:评估用固定的 $\pi(s)$,迭代对动作取 $\max$;$\gamma<1$ 时两者都由压缩映射保证指数收敛。 **下一讲预告**:本讲的一切都建立在「$T$ 和 $\text{Reward}$ 已知」之上。如果环境是黑盒——只能靠试错与它交互、从反馈中学习——怎么办?→ Lecture 8 · 强化学习。 --- ## 复习自测

    题目

    因为转移是随机的,执行动作后落到哪个状态事先未知,一条固定的动作序列在第一次「意外转移」时就失效了。策略对每个可能到达的状态都规定了动作,无论随机性把智能体带到哪里都有决策可用。搜索问题结果确定、路径可预知,所以序列就够了。

    题目

    唯一差别:评估用固定动作 $Q(s,\pi(s),V)$,迭代取 $\max_a Q(s,a,V)$。取 max 意味着每轮更新都在每个状态局部改进动作选择;由马尔可夫性质,$V^(s’)$ 已概括后续所有最优行为,所以逐状态贪心即可拼出全局最优(最优子结构),不需要枚举所有策略。

    题目

    设 stay 最优:$V = 2 + \tfrac{2}{3}V \Rightarrow V = 6$,但 $6 < 10$,假设矛盾。故最优是 quit,$V^(\text{in}) = \max(10,\ 2 + \tfrac{2}{3}\cdot 10) \approx \max(10, 8.67) = 10$,$\pi^(\text{in}) = \text{quit}$。可见最优策略对参数敏感,贝尔曼方程会自动完成这类权衡。

    题目

    $\gamma<1$ 时贝尔曼更新是压缩映射:每轮迭代把任意两个价值函数间的最大差距至少缩小到原来的 $\gamma$ 倍,故从任意初始化出发都指数收敛到唯一不动点 $V^*$。$\gamma=1$ 时压缩性消失,需要所有策略都以概率 1 有限步终止;若存在可无限逗留且累积奖励的回路,价值会发散。

    题目

    效用是随机变量——同一策略每次 rollout 因随机转移得到不同效用;$V_\pi(s)$ 是这个随机变量的期望。单次样本方差可能很大(骰子游戏 always-stay 单次可得 \$4 也可得 \$20+,期望是 \$12),用一次结果比较两个策略相当于用一次抛硬币判断硬币是否公平。蒙特卡洛评估需要大量样本,精度按 $O(1/\sqrt{n})$ 缓慢提升,这正是已知模型时改用贝尔曼迭代的动机。


参考资料