CS221 · 人工智能:原理与技术
Lecture 7 · 马尔可夫决策过程
源文件:lecture-07.md
Lecture 7 · 马尔可夫决策过程
CS221: Artificial Intelligence — Principles and Techniques · Stanford · Autumn 2025
📅 Oct 13 · 💻mdp.py
承上启下:从确定性到随机性
前两讲(Lecture 5 · 搜索算法 I、Lecture 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。
--- ## 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 ) ```注意
效用是随机变量,价值是它的期望。单次 rollout 的效用忽高忽低,不能用来断言策略好坏;策略的「好坏」由下一节定义的期望效用 $V_\pi$ 刻画。混淆这两者是初学最常见的错误。
### 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)提示
Q 值是「向前看一步」的算子:它把「未来所有状态的估价 $V$」压缩成「现在这个动作的估价」。策略评估和价值迭代都只是围绕这一个算子做文章——前者把 $V$ 递归地定义成自己的一步展开,后者在展开时额外做一次 $\max$。
终止"] --> 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 ```--- ## 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说明
每轮迭代用 $\max_s |V^{(t)}(s) - V^{(t-1)}(s)|$ 度量变化,小于容差(如 $10^{-5}$)即停。单轮复杂度为 $O(|S| \cdot |S’|)$(每个状态展开其后继),总代价乘以迭代轮数 $T$。相比蒙特卡洛,这是确定性的精确计算,没有采样方差——代价是必须知道完整的 $T$ 和 $\text{Reward}$。另外,求解 $V_\pi$ 本质上是解 $|S|$ 元线性方程组,也可以直接用线性代数求精确解,迭代法胜在实现简单、可随时截断。
### 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 例子:手算骰子游戏提示
为什么「逐状态各自贪心」就能得到全局最优策略?因为 $V^(s’)$ 已经概括了从 $s’$ 出发之后所有未来的最优结果——马尔可夫性质保证未来只依赖于当前状态。所以在 $s$ 处做决策时,不需要展开整棵未来树,只需比较「一步奖励 + 落点的 $V^$」。这正是动态规划「最优子结构」思想在随机环境下的版本,与 Lecture 6 · UCS 与 A* 搜索 中「最短路的子路径也是最短路」一脉相承。
### 4.5 收敛性例子
贝尔曼最优方程($\gamma = 1$):
XMATHXPLACEHOLDERX12XENDX
假设 stay 是最优的,解方程 $V = 4 + \tfrac{2}{3} V$,得 $V = 12$。验证:$12 > 10$,假设成立。所以
XMATHXPLACEHOLDERX13XENDX
直觉:一直玩下去平均能拿 \$12(平均玩 3 轮 × 每轮 \$4),高于立刻退出的 \$10,所以最优策略是 stay。注意期望值 12 并不出现在任何单次游戏里——单次可能只拿 \$4,也可能拿 \$20+,12 是长期平均。--- ## 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["传播阶段说明
当 $\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$,价值会发散。
信息从终止状态扩散"] --> 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})$ 缓慢提升,这正是已知模型时改用贝尔曼迭代的动机。
参考资料
- 💻 mdp.py
- 📄 Dynamic Programming (Bellman, 1957) — 价值迭代的起源
- 📖 Sutton & Barto: Reinforcement Learning (2nd ed.) — 第 3 章:有限 MDP;第 4 章:动态规划