Skip to content

Latest commit

 

History

History
1090 lines (737 loc) · 44.8 KB

File metadata and controls

1090 lines (737 loc) · 44.8 KB

3.4 DP、MC、TD

本节导读

核心内容

  • 掌握 DP、MC、TD 三类价值估计方法分别需要什么信息、何时更新、误差来自哪里。
  • 理解从已知模型到采样轨迹,再到一步自举更新的演进逻辑。
  • 学会用 TD Error 连接价值评估、Q-Learning、Critic 训练等后续算法。

前面三节已经搭好了这章最重要的地基。3.1 的两台老虎机让我们看到 RL 最小的矛盾:动作要靠试出来,既要探索,也要利用。3.2 把这个问题推广到有状态变化的世界,用 MDP 描述“当前是什么状态、能做什么动作、动作之后会发生什么、拿到什么奖励”。3.3 又进一步引入状态价值函数 $V^\pi(s)$ 和贝尔曼方程,告诉我们:当前价值 = 眼前奖励 + 折扣后的下一状态价值

3.3 的最后已经埋下了这个问题:有了贝尔曼方程,并不等于我们已经会算价值。 贝尔曼方程告诉我们价值应该满足什么关系,矩阵形式也给出了一个理论上的解析解。但解析解依赖两个前提:状态数量不能太大,而且我们要知道环境的完整规则,也就是转移概率 $P$ 和奖励函数 $R$

现实任务往往卡在这两个前提上。CartPole 的物理规律也许还能写出来,围棋的状态已经多到无法穷举,大模型生成的 token 组合更是天文数字。更常见的情况是:环境像一个黑盒,智能体只能进去试一试,看到这一步拿了多少奖励、跳到了哪个下一状态,却拿不到完整的“世界说明书”。

于是问题变成了:不知道 $P$$R$,只靠智能体自己在环境里一步步试错,怎么把 $V(s)$ 估算出来?

强化学习历史上出现了三代经典方法来回答这个问题。它们不是三个平行的备选方案,而是一条逼近现实的演进线——每一代都在解决上一代的硬伤:

  • DP(动态规划):已知模型时直接代入贝尔曼方程迭代,精确但需要知道一切 → 现实中做不到。
  • MC(蒙特卡洛):抛开模型,跑完整局再看总分,回头修正每一步的判断 → 不用模型,但一局太长就得一直等。
  • TD(时序差分):走一步就结合“眼前真实奖励”和“下一状态的猜测”修正当前估计 → 既不用模型,也不用等结束,是现代 RL 的绝对主力。

理解了这条演进线,许多后续设计就不再神秘:PPO 的 Critic 为什么用 TD 而不是 MC?DQN 为什么需要经验回放池?这三代方法是后续所有算法的基因。

::: info 核心概念 DP、MC、TD 的根本区别在于更新价值时用了多少信息:DP 需要完整的环境模型($P$ 和 $R$),MC 需要一整局结束后才能回头修正,TD 只需要走一步就能更新。从 DP 到 MC 到 TD,是一条“已知信息越来越少,但还能继续学习”的演进线。现代 RL 的主力算法几乎都以 TD 为基础。 :::

核心公式

$$ V(s) \leftarrow \sum_a \pi(a\mid s)\left[R(s,a)+\gamma\sum_{s'}P(s'\mid s,a)V(s')\right] \quad \text{(DP 策略评估更新:已知模型时迭代价值)} $$

DP 策略评估更新 (DP Policy Evaluation Update):

  • $\leftarrow$:赋值操作,表示用右边的计算结果来更新左边的旧值。
  • $\sum_a \pi(a\mid s)$:对当前状态下所有可能的动作按策略概率进行加权平均。
  • $\sum_{s'} P(s'\mid s,a)$:对采取动作后所有可能转移到的下一个状态按转移概率进行加权平均。

$$ V(s) \leftarrow V(s)+\alpha\left[G_t-V(s)\right] \quad \text{(MC 价值更新:用完整回报修正估计)} $$

MC 价值更新 (MC Value Update):

  • $\alpha$:学习率(Learning Rate),控制每次更新时向新目标迈进的步长大小($0 \sim 1$)。
  • $G_t$:蒙特卡洛采样的真实完整回报(从当前步一直到这局游戏结束)。

$$ V(s) \leftarrow V(s)+\alpha\left[r+\gamma V(s')-V(s)\right] \quad \text{(TD(0) 价值更新:一步自举更新价值)} $$

TD(0) 价值更新 (TD(0) Value Update):

  • $r$:走这一步真实拿到的即时奖励。
  • $\gamma V(s')$:对未来收益的猜测(打折后的下一个状态的预估价值)。
  • $r + \gamma V(s')$:TD 目标(TD Target),即“眼前的真实奖励 + 对未来的猜测”。

$$ \delta = r+\gamma V(s')-V(s) \quad \text{(TD Error:提供基础学习信号)} $$

TD Error (Temporal Difference Error):

  • $\delta$:时序差分误差(TD Error),表示“现实发生的情况”与“你之前的预测”之间的差距。大于 0 说明有惊喜,小于 0 说明有惊吓。

第一代:动态规划(Dynamic Programming, DP)

核心思想

先看最理想的情况:如果你完全知道环境的转移概率 $P$ 和奖励函数 $R$,就好像你不只是玩家,而是拿到了这个游戏的完整规则表。每个状态有哪些动作、每个动作会通向哪里、会得到多少奖励,全都写得清清楚楚。

在这种情况下,智能体不需要先和环境反复交互来猜规则。它可以直接把这些已知规则代入贝尔曼方程,让价值在状态之间一轮一轮传播。

这套不靠盲目试错,直接在已知模型上反复代公式的方法,就是贝尔曼在 1957 年提出的动态规划 1

先看直觉:现实中什么时候会“完全知道” $P$$R$

答案通常是:当这个世界本来就是你定义出来的。比如一个迷宫小游戏的规则由你编写:“向右走一步扣 1 分,走到终点得 10 分,而且没有随机性,想往右走就一定能走到右边。”在这种场景下,DP 很自然:既然规则已知,就直接用数学递推算价值,不必靠大量试玩去估计平均结果。这也是为什么物流规划、库存管理这类规则明确的问题里,动态规划思想仍然常见。

更新规则

DP 计算价值的核心叫做策略评估(Policy Evaluation)。公式看起来很眼熟:它就是把 3.3 的贝尔曼期望方程,从“真值应该满足的等式”改写成“用旧估计产生新估计”的更新规则:

$$V(s) \leftarrow \sum_a \pi(a|s) \left[ R(s,a) + \gamma \sum_{s'} P(s'|s,a) , V(s') \right] \tag{3.4}$$

反复对所有状态执行这个更新,$V$ 最终会收敛到 $V^\pi$ 的精确值。

:::details 慢慢拆:这个公式从哪来?每一项是什么意思?

1. 它从 3.3 的贝尔曼期望方程来

3.3 里我们已经得到过贝尔曼期望方程:

$$ V^\pi(s)

\sum_a \pi(a\mid s) \left[ R(s,a)

  • \gamma \sum_{s'} P(s'\mid s,a) V^\pi(s') \right] $$

这行公式说的是:如果我们已经知道策略 $\pi$ 的真实价值函数 $V^\pi$,那么状态 $s$ 的价值应该等于:

  • 先看策略 $\pi$ 在状态 $s$ 下可能选择哪些动作;
  • 对每个动作,计算“这一步的奖励 + 下一状态的价值”;
  • 最后按照动作概率加权平均。

问题在于:我们现在并不知道真正的 $V^\pi$。所以不能直接把它当作已知答案来用。

2. 把“真值方程”变成“迭代更新”

DP 的想法很朴素:既然不知道真值,那就先猜一个。

比如一开始把所有状态的价值都设成 $0$

$$ V_0(s)=0 $$

然后用贝尔曼方程右边重新算一遍,得到下一轮估计 $V_1(s)$。再用 $V_1$$V_2$,继续重复。于是原来的“真值方程”就变成了“更新规则”:

$$ V_{k+1}(s) \leftarrow \sum_a \pi(a\mid s) \left[ R(s,a)

  • \gamma \sum_{s'} P(s'\mid s,a) V_k(s') \right] $$

这里:

  • $k$ 表示第几轮迭代。
  • $V_k(s)$ 是这一轮手里的旧估计。
  • $V_{k+1}(s)$ 是用贝尔曼方程算出来的新估计。

所以箭头 $\leftarrow$ 的意思不是“数学恒等”,而是:用右边算出来的新值,覆盖左边旧的估计值。

3. 从内到外拆开每一项

先固定一个动作 $a$。如果在状态 $s$ 下已经决定做动作 $a$,那么这一动作带来的价值是:

$$ R(s,a) + \gamma \sum_{s'} P(s'\mid s,a)V_k(s') $$

它分成两块:

第一块:眼前奖励

$$ R(s,a) $$

这是在状态 $s$ 做动作 $a$ 后,立刻拿到的平均奖励。

第二块:下一状态价值的平均

$$ \sum_{s'} P(s'\mid s,a)V_k(s') $$

做完动作 $a$ 后,环境可能跳到不同的下一状态 $s'$。每个下一状态都有一个旧价值估计 $V_k(s')$,我们按照转移概率 $P(s'\mid s,a)$ 做加权平均。

如果“向右走”有 $90%$ 到达右边格子、$10%$ 滑到下面格子,那这里就在算:

$$ 0.9\times V_k(\text{右边格子}) + 0.1\times V_k(\text{下面格子}) $$

再乘上折扣因子 $\gamma$,表示未来价值要打折:

$$ \gamma \sum_{s'} P(s'\mid s,a)V_k(s') $$

最后,策略 $\pi$ 不一定只选一个动作。它可能 $70%$ 选右,$30%$ 选左。所以还要对所有动作再平均:

$$ \sum_a \pi(a\mid s)[\text{动作 }a\text{ 的价值}] $$

这就得到完整更新式:

$$ V_{k+1}(s) \leftarrow \sum_a \pi(a\mid s) \left[ R(s,a) +\gamma\sum_{s'}P(s'\mid s,a)V_k(s') \right] $$

4. 这行公式在做什么?

可以把一次 DP 更新理解成三句话:

  1. 先用旧价值表 $V_k$ 看下一状态值多少。
  2. 再加上当前动作带来的即时奖励。
  3. 最后按策略 $\pi$ 对动作求平均,得到新价值表 $V_{k+1}$

不断重复这件事,价值会一轮一轮从终点、奖励高的地方、奖励低的地方向周围传播。最后,整张价值表会接近真正的 $V^\pi$

5. 为什么用 $\gamma$

$\gamma$ 是折扣因子,可以先理解成“未来分数要打折”。如果 $\gamma=0.9$,下一步的 1 分在现在看作 $0.9$ 分;两步后的 1 分在现在看作 $0.9^2=0.81$ 分。这样越远的未来权重越小,长期价值也更容易稳定下来。

6. 用微型走廊验证公式的每一步

先画出这个最小例子。3 格走廊只有三个位置:

S  --右走,奖励 -1-->  M  --右走,奖励 -1-->  G
起点                    中间                    终点
  • $S$:起点状态。
  • $M$:中间状态。
  • $G$:终点状态,价值固定为 $0$
  • 策略很简单:每一步都只往右走。
  • 折扣因子 $\gamma=1$,表示未来奖励不打折。

直觉上,$M$ 离终点只差 1 步,所以价值应该是 $-1$;$S$ 离终点差 2 步,所以价值应该是 $-2$。下面用 DP 更新把这个结果算出来。

转移概率:$P(\text{M}|S,\text{右}) = 1$,$P(\text{G}|M,\text{右}) = 1$,其余为 0。

因为策略只有“向右走”一个动作,而且转移是确定的,所以通用 DP 更新式:

$$ V_{k+1}(s)

R(s,\text{右})+\gamma V_k(s_{\text{next}}) $$

在这个走廊里就变成两条具体公式:

$$ V_{k+1}(S)=-1+V_k(M) $$

$$ V_{k+1}(M)=-1+V_k(G) $$

终点不再继续走,所以:

$$ V_k(G)=0 $$

现在再代入每一轮的数值。

迭代 0→1:初始时,所有状态都先猜成 $0$

$$ V_0(S)=0,\qquad V_0(M)=0,\qquad V_0(G)=0 $$

代入上面的两条公式:

$$ V_1(S)=-1+V_0(M)=-1+0=-1 $$

$$ V_1(M)=-1+V_0(G)=-1+0=-1 $$

$$ V_1(G)=0 $$

迭代 1→2

这一轮要用上一轮刚算出的 $V_1$

$$ V_2(S)=-1+V_1(M)=-1+(-1)=-2 $$

$$ V_2(M)=-1+V_1(G)=-1+0=-1 $$

$$ V_2(G)=0 $$

迭代 2→3

$$ V_3(S)=-1+V_2(M)=-1+(-1)=-2 $$

$$ V_3(M)=-1+V_2(G)=-1+0=-1 $$

结果和上一轮一样,不再变化,说明已经收敛。

把上面的迭代过程汇总成表格,就是:

迭代 V(S) V(M) V(G)
0 0 0 0
1 -1 -1 0
2 -2 -1 0
3 -2 -1 0(收敛)

经过 3 次迭代就收敛了,因为 $V(M)$ 更新后会影响下一轮 $V(S)$ 的计算。这种“全状态扫描”的方式,让奖励或代价可以沿着状态连接逐步传播。

这一步的含义是:$V(M)$ 在第 1 轮更新为 -1 之后,参与了第 2 轮 $V(S)$ 的计算。价值不是一次性凭空出现的,而是通过贝尔曼更新从后继状态传回当前状态。 :::

策略迭代(Policy Iteration)

策略评估只是第一步。知道了 $V^\pi$,就可以用它来改进策略——在状态 $s$ 选择让 $Q(s,a)$ 最大的动作:

$$\pi'(s) = \arg\max_a \left[ R(s,a) + \gamma \sum_{s'} P(s'|s,a) V^\pi(s') \right]$$

策略迭代是“评估策略 → 改进策略 → 再评估”的循环,理论上保证收敛到最优策略 $\pi^*$

:::details 慢慢拆:策略改进为什么一定有效?

1. 策略迭代只做两件事

策略迭代由两个交替步骤组成:

  1. 策略评估(Policy Evaluation):给定策略 $\pi$,用公式 3.4 迭代求解 $V^\pi$
  2. 策略改进(Policy Improvement):用 $V^\pi$ 构造一个更好的策略 $\pi'$

关键问题是:为什么第二步真的会让策略变好?

2. 先定义“选某个动作后值多少”

定义:

$$ Q^\pi(s,a)

R(s,a)

  • \gamma \sum_{s'} P(s'\mid s,a) V^\pi(s') $$

这句话的意思是:先在状态 $s$ 选动作 $a$,之后继续按旧策略 $\pi$ 行动,最后平均能拿多少分。

3. 新策略只做一件朴素的事:选分最高的动作

新策略 $\pi'$ 定义为:

$$\pi'(s) = \arg\max_a Q^\pi(s, a)$$

也就是说,在每个状态里,先把每个动作的 $Q^\pi(s,a)$ 算出来,然后选最高分那个动作。

4. 为什么这样不会变差?

因为 $\pi'(s)$ 是最高分动作,所以它至少不会比旧策略原来选的动作差:

$$ Q^\pi(s,\pi'(s))

\max_a Q^\pi(s,a) \geq Q^\pi(s,\pi(s))

V^\pi(s) $$

这行公式只是在说一句很普通的话:从一堆动作里挑最高分,不会比原来那套选择更差。

$Q^\pi(s,\pi'(s))$ 展开,就是:

$$ R(s,\pi'(s))

  • \gamma \sum_{s'} P(s'\mid s,\pi'(s)) V^\pi(s') \geq V^\pi(s) $$

右边的 $V^\pi(s')$ 还假设“下一步以后继续按旧策略走”。如果下一步以后也继续用新策略 $\pi'$,每一步都做同样的“选最高分动作”,价值就不会下降。反复展开这个关系,就得到:

$$ V^{\pi'}(s) \geq V^\pi(s) $$

这就是策略改进定理。

什么时候停止?

$\pi' = \pi$ 时(贪心策略不再改变),意味着对每个状态 $s$

$$\pi(s) = \arg\max_a Q^\pi(s, a)$$

这正是贝尔曼最优方程的条件——$\pi$ 已经是最优策略 $\pi^*$

为什么不直接用贝尔曼最优方程?

可以直接用,这就是值迭代(Value Iteration)。策略迭代只是把同一件事拆成两步:

  1. 先把当前策略评估清楚。
  2. 再根据评估结果改策略。

这样做的好处是思路更清楚:先问“这套走法到底值多少分”,再问“有没有更好的第一步”。值迭代更像是每一步都直接用 $\max$ 推进,策略迭代则把“算分”和“选动作”分开。 :::

小实验:看 V 值一步步收敛

值迭代和策略迭代不是抽象的公式——它们是肉眼可见的迭代过程。用一个 4×4 GridWorld 来展示每一步发生了什么。

import numpy as np

# 4×4 GridWorld
# S(0,0) → ... → G(3,3)
# 每步奖励 -1,4 个动作(上/下/左/右),γ=0.9
GRID = 4
ACTIONS = [(-1, 0), (1, 0), (0, -1), (0, 1)]  # 上下左右
GAMMA = 0.9

def step(state, action):
    """执行动作,返回 (next_state, reward)"""
    nr = state[0] + action[0]
    nc = state[1] + action[1]
    if 0 <= nr < GRID and 0 <= nc < GRID:
        return (nr, nc), -1
    return state, -1  # 撞墙留在原地,照样扣分

def print_V(V, label=""):
    """打印 V 值矩阵"""
    if label:
        print(f"  {label}")
    for r in range(GRID):
        print("  ", end="")
        for c in range(GRID):
            print(f"{V[r][c]:7.2f}", end=" ")
        print()
    print()

# ========== 值迭代 ==========
V = np.zeros((GRID, GRID))
print("===== 值迭代 =====")
print_V(V, "迭代 0(初始)")

for iteration in range(1, 11):
    V_new = np.zeros((GRID, GRID))
    for r in range(GRID):
        for c in range(GRID):
            if (r, c) == (3, 3):
                continue  # 终点 V=0
            values = []
            for a in ACTIONS:
                (nr, nc), reward = step((r, c), a)
                values.append(reward + GAMMA * V[nr][nc])
            V_new[r][c] = max(values)
    V = V_new.copy()
    if iteration in [1, 2, 3, 5, 10]:
        print_V(V, f"迭代 {iteration}")

print_V(V, "最终结果")

预期输出(节选):

===== 值迭代 =====
  迭代 0(初始)
    0.00    0.00    0.00    0.00
    0.00    0.00    0.00    0.00
    0.00    0.00    0.00    0.00
    0.00    0.00    0.00    0.00

  迭代 1
   -1.00   -1.00   -1.00   -1.00
   -1.00   -1.00   -1.00   -1.00
   -1.00   -1.00   -1.00   -1.00
   -1.00   -1.00   -1.00    0.00

  迭代 2
   -1.90   -1.90   -1.90   -1.90
   -1.90   -1.90   -1.90   -1.90
   -1.90   -1.90   -1.90   -1.00
   -1.90   -1.90   -1.00    0.00

  迭代 5
   -4.10   -4.10   -3.44   -2.71
   -4.10   -3.44   -2.71   -1.90
   -3.44   -2.71   -1.90   -1.00
   -2.71   -1.90   -1.00    0.00

  最终结果
   -4.69   -4.10   -3.44   -2.71
   -4.10   -3.44   -2.71   -1.90
   -3.44   -2.71   -1.90   -1.00
   -2.71   -1.90   -1.00    0.00

观察:$V$ 值从终点向外“扩散”。每轮迭代,比较准确的价值会向外多传播一格。这是因为值迭代每轮只把“一步之后”的估计拿回来用:第 1 轮先影响终点附近的格子,第 2 轮再影响离终点更远一格的状态,以此类推。

策略迭代的可视化

策略迭代与值迭代的区别在于:策略迭代交替执行“评估当前策略”和“改进策略”两个步骤,而值迭代直接在每步选最优动作。

# ========== 策略迭代 ==========
V = np.zeros((GRID, GRID))
policy = np.zeros((GRID, GRID), dtype=int)  # 每个状态的动作编号

def policy_evaluation(V, policy, iterations=20):
    """用当前策略迭代计算 V"""
    for _ in range(iterations):
        V_new = np.zeros((GRID, GRID))
        for r in range(GRID):
            for c in range(GRID):
                if (r, c) == (3, 3):
                    continue
                a = ACTIONS[policy[r][c]]
                (nr, nc), reward = step((r, c), a)
                V_new[r][c] = reward + GAMMA * V[nr][nc]
        V = V_new.copy()
    return V

def policy_improvement(V, policy):
    """根据 V 改进策略,返回策略是否发生变化"""
    stable = True
    for r in range(GRID):
        for c in range(GRID):
            if (r, c) == (3, 3):
                continue
            values = []
            for a in ACTIONS:
                (nr, nc), reward = step((r, c), a)
                values.append(reward + GAMMA * V[nr][nc])
            best_action = np.argmax(values)
            if best_action != policy[r][c]:
                stable = False
                policy[r][c] = best_action
    return stable

print("===== 策略迭代 =====")
for iteration in range(1, 10):
    V = policy_evaluation(V, policy)
    stable = policy_improvement(V, policy)
    action_names = ["↑", "↓", "←", "→"]
    policy_str = ""
    for r in range(GRID):
        policy_str += "  "
        for c in range(GRID):
            if (r, c) == (3, 3):
                policy_str += "  G "
            else:
                policy_str += f"  {action_names[policy[r][c]]} "
        policy_str += "\n"
    print(f"第 {iteration} 轮:")
    print(policy_str)
    print_V(V)
    if stable:
        print(f"策略已收敛!共 {iteration} 轮")
        break

预期输出:

===== 策略迭代 =====
第 1 轮:
    ↑   ↑   ↑   ↑
    ↑   ↑   ↑   ↑
    ↑   ↑   ↑   ↓
    ↑   ↑   →    G

   -8.78   -8.78   -8.78   -8.78
   -8.78   -8.78   -8.78   -8.78
   -8.78   -8.78   -8.78   -8.78
   -8.78   -8.78   -8.78    0.00

...

第 7 轮:
    ↓   ↓   ↓   ↓
    ↓   ↓   ↓   ↓
    ↓   ↓   ↓   ↓
    →   →   →    G

  策略已收敛!共 7 轮

策略迭代通常用较少的外层轮次收敛。原因是:策略迭代每轮内部做了多轮策略评估(把当前策略的 $V$ 算得更充分),然后再改进策略。值迭代则每轮只做一步更新,单轮更轻,但需要更多轮把价值传播开。

方法 收敛轮数 每轮计算量 特点
值迭代 ~10 轮 每轮扫描所有状态一次 简单,每步只做一个 max 操作
策略迭代 ~7 轮 每轮需要多次策略评估(内部迭代) 外层更少,但每轮更重

在 4×4 的小格子里差别不大,但在大规模问题中,策略评估的内部循环本身就很昂贵——这就是为什么实践中值迭代更常用,而策略迭代的思想则体现在了后续的 Actor-Critic 架构中(第 6 章)。

局限:知道模型的代价

换个角度看,DP 的强大来自一个很重的前提:它要求我们已经知道环境模型。

现实中,我们根本拿不到完整的说明书。你想让机械臂学会抓杯子,难道要先把机械臂和杯子所有微观物理碰撞的概率分布(转移概率 $P$)全写进公式吗?如果是围棋,状态数更是高达 $10^{170}$,你要怎么算?如果是训练大语言模型,token 组合是天文数字,根本不可能对所有可能状态做一次遍历。

所以,DP 的精确性建立在两个条件上:规则已知,状态规模还算可控。它更像是一个理论基准,告诉我们“如果知道一切,价值应该怎样被算出来”。在实际中,面对那些不知道规则的环境,我们必须让智能体和环境交互,用采样数据来估计价值。

第二代:蒙特卡洛方法(Monte Carlo, MC)

核心思想:试完算总账

DP 的前提是“我知道环境模型”。但现实里更多时候,你并不知道模型。你不知道某个动作之后会转到哪里,也不知道奖励函数到底怎么写。那还能不能学?

蒙特卡洛的答案很直接:不知道规则,就亲自进去玩;玩完整局,再看最后总分。

比如你在玩一个陌生迷宫。站在路口 $s$,你不知道向左走会不会绕远,也不知道向右走会不会撞墙。MC 不试图提前写出完整地图,而是让你真的从这个路口出发,按当前策略一路走到游戏结束。最后你拿到一个总分:可能是 -8,也可能是 +3,也可能是 +10。

这一局从状态 $s$ 出发最终拿到的总分,记作 $G_t$。它不是猜出来的,而是你真的跑完这一局后得到的结果。

如果只跑一次,运气成分太大;但如果从同一个状态 $s$ 出发跑很多次,把这些 $G_t$ 取平均,就能估计这个状态大概值多少分:

$$ V(s) \approx \text{很多次 } G_t \text{ 的平均值} $$

这就是蒙特卡洛方法:用很多次真实试玩的平均结果,替代你原本不知道的环境公式。 它的名字来自赌城 Monte Carlo,因为它本质上就是靠随机采样来估计平均值 2

更新规则

现在的问题是:每跑完一局,难道都要把过去所有总分重新平均一遍吗?

不用。我们只需要保留当前估计 $V(s)$,然后把它朝新看到的总分 $G_t$ 挪一点:

$$V(s) \leftarrow V(s) + \alpha \left[ G_t - V(s) \right] \tag{3.5}$$

这行公式可以这样读:

  1. $V(s)$:我原来以为状态 $s$ 值多少分。
  2. $G_t$:这次真的从 $s$ 出发跑完整局后,实际拿到多少分。
  3. $G_t - V(s)$:现实和预测差了多少。
  4. $\alpha$:每次改多少。它通常是一个小数,比如 0.1,意思是“不要一次全信新样本,只往新结果靠近 10%”。

比如你原本觉得这个路口值 0 分,结果这局从这里出发最后拿了 -10 分。如果 $\alpha = 0.1$,新估计就是:

$$ V(s) \leftarrow 0 + 0.1 \times (-10 - 0) = -1 $$

它不会一下子变成 -10,因为一次试玩可能只是运气不好;但它会先往 -10 的方向挪一点。多试很多次以后,$V(s)$ 就会逐渐靠近真实平均水平。

:::details 慢慢拆:MC 更新公式从哪来?

1. 先从最普通的平均数开始

MC 的核心思想就是:用很多次完整回报的平均值来估计 $V(s)$

假设状态 $s$ 被访问了 $N$ 次,每次得到的回报分别是 $G_1, G_2, \ldots, G_N$。最直接的做法:

$$ V(s) = \frac{1}{N} \sum_{i=1}^{N} G_i $$

这就是学校里最普通的“求平均分”:把所有分数加起来,再除以次数。

2. 再改成不用保存所有历史的写法

问题是,RL 训练会跑很多很多局。如果每来一个新回报 $G_{N+1}$,都把过去所有分数重新加一遍,就太麻烦了。

我们希望只保留当前平均值 $V_N$,然后来了一个新样本 $G_{N+1}$,就直接算出新平均值 $V_{N+1}$

$$ V_{N+1}

\frac{N \cdot V_N + G_{N+1}}{N+1} $$

这里的 $N \cdot V_N$ 就是“前 $N$ 次总分加起来”的结果。

继续整理一下:

$$ V_{N+1}

V_N

  • \frac{1}{N+1}(G_{N+1} - V_N) $$

这行公式很重要。它说明新平均值可以写成:

$$ \text{新估计}

\text{旧估计}

  • \text{步长} \times (\text{新样本} - \text{旧估计}) $$

这就是公式 3.5 的来源。

3. 最后把步长换成学习率 $\alpha$

在严格求平均时,步长是 $\frac{1}{N+1}$。样本越多,步长越小,估计越来越稳定。

但在 RL 中,我们经常把它换成固定学习率 $\alpha$

$$V(s) \leftarrow V(s) + \alpha \left[ G_t - V(s) \right]$$

这样做是因为环境和策略可能一直在变。固定的 $\alpha$ 让智能体不会“学到一半就完全不改了”。代价是它不会像普通平均数那样完全静止,而是会在真实值附近小幅波动。

实际中常见选择是 $\alpha = 0.01$$\alpha = 0.1$,也可以让 $\alpha$ 随训练慢慢变小。

$G_t - V(s)$ 的含义:预测误差

$G_t - V(s)$ 就是“实际回报”和“原来预测”之间的差:

  • $G_t &gt; V(s)$:实际比预测好 → $V$ 上调
  • $G_t &lt; V(s)$:实际比预测差 → $V$ 下调
  • $G_t = V(s)$:预测准确 → 不动

这与人类学习的直觉完全一致——你预测考试能考 80 分($V(s)$),实际考了 90 分($G_t$),下次你就会调高对自己的评估。

为什么 MC 是无偏的?

"无偏"的定义:$\mathbb{E}[V(s)] = V^\pi(s)$,即估计值的期望等于真实值。

MC 用的是真实的完整回报 $G_t = r_t + \gamma r_{t+1} + \gamma^2 r_{t+2} + \cdots$——这是从状态 $s$ 出发,实际跑完整个 episode 拿到的总回报。根据 $V^\pi(s)$ 的定义:

$$V^\pi(s) = \mathbb{E}[G_t | s_t = s]$$

所以 $G_t$ 就是 $V^\pi(s)$ 的无偏估计(它的期望恰好等于 $V^\pi(s)$),用 $G_t$ 的平均值来估计 $V$ 自然也是无偏的。

为什么方差大?

$G_t = r_t + \gamma r_{t+1} + \gamma^2 r_{t+2} + \cdots$ 把从 $t$ 时刻到 episode 结束的所有随机奖励加在了一起。每一步的奖励都是随机的,加在一起,不确定性就累加了——就像掷 100 次骰子的总和,比掷 1 次骰子的方差大得多。

形式化地,如果把每步奖励的波动粗略记作 $\text{Var}(r_i)=\sigma^2$,那么 $G_t$ 的波动会把很多步的不确定性累加起来。折扣因子越接近 1、轨迹越长,完整回报的方差通常越大。这里不用记具体上界,先记住直觉:MC 用的是整条未来,所以它的样本目标很真实,也很容易抖。 :::

特点:无偏,但运气成分太大(高方差)

MC 给出的估算是无偏(Unbiased) 的。什么是无偏?就是说在策略固定、采样足够多的情况下,样本平均会朝真实期望靠近。因为 MC 用的目标就是完整回报 $G_t$,而 $V^\pi(s)$ 本来就是这些完整回报的期望。

但这也藏着 MC 最大的弱点:方差巨大。因为每一局游戏里,有很多后续状态和动作。你这一局结果很好,可能是因为当前状态真的好,也可能是因为后面某一步运气好;你这一局结果很差,也可能是当前状态的问题,也可能只是后面某个随机事件拖累了总分。

还是看迷宫。你站在同一个路口 $s$,按同一个策略往前走。第一局后面连续遇到顺路出口,完整回报是 $G_t=8$;第二局后面绕进死胡同,完整回报是 $G_t=-6$;第三局中间发生一次随机滑倒,完整回报是 $G_t=-10$。这三个数都是真实发生的结果,但它们差得很大。

如果只采样三次,$V(s)$ 会被这些偶然结果带着大幅摇摆。只有采样很多次以后,顺利的轨迹、倒霉的轨迹、普通的轨迹都被平均进去,估计才会稳定下来。

这就是高方差(High Variance):MC 的目标没有系统性偏差,但单次目标很不稳定。 为了让平均值可靠,通常需要大量完整轨迹。

局限:必须等一局结束

还有个很致命的问题:用 MC 方法,你必须等游戏彻底结束,才能算账。 如果是打一局几分钟的游戏,还能接受。但如果是持续运行的推荐系统、机器人控制、网页 Agent 操作,任务可能很长,甚至没有自然终点。真正的问题在于:MC 必须等完整回报出现,才能更新早先状态的价值。 一旦 episode 很长,学习信号就来得太晚;如果任务没有终止状态,完整回报本身就难以定义。

第三代:时序差分学习(Temporal Difference, TD)

核心思想:不用等结束,走一步就更新一次

MC 解决了“不知道环境规则”的问题,但它又带来一个新麻烦:必须等一整局结束,才知道总分 $G_t$

如果一局很短,比如 3 格走廊,等结束还可以接受。但如果任务很长呢?机器人走了 10 秒、网页 Agent 点了 5 次按钮、模型生成了 200 个 token,你不可能每次都等到全部结束再回头改第一个判断。

TD 的想法是:不用等最后总账,先用眼前这一步形成一个临时目标。

走一步以后,你至少知道两件事:

  1. 这一步真实拿到了多少奖励,记作 $r$
  2. 下一状态 $s'$ 现在被你估计值多少分,记作 $V(s')$

于是 TD 说:虽然我还不知道完整结局,但我可以先把“这一步的真实奖励”和“下一站的预估价值”拼起来,得到一个临时目标:

$$ r + \gamma V(s') $$

这个量叫 TD Target。它的意思是:如果我从 $s$ 走到 $s'$,这一步已经真实发生了;从 $s'$ 往后的未来,先暂时相信当前的估计 $V(s')$

举个 3 格走廊的例子:

S
M
G

每走一步奖励 -1,终点 $G$ 的价值是 0。刚开始我们什么都不知道,先设:

$$ V(S)=0,\quad V(M)=0,\quad V(G)=0 $$

$S$ 走一步到 $M$,这一刻你还没到终点,但你已经知道:

  1. 这一步真实奖励是 $r=-1$
  2. 下一站 $M$ 当前估计是 $V(M)=0$

所以 TD Target 是:

$$ r + \gamma V(M) = -1 + 1 \times 0 = -1 $$

而你原来以为 $V(S)=0$。现在看到 TD Target 是 -1,就知道原来的 $V(S)$ 估高了,应该往下调。

下一次,当 $M$ 的估计也被更新成 -1 以后,再从 $S$ 看过去,TD Target 就会变成:

$$ -1 + 1 \times V(M) = -1 + (-1) = -2 $$

信息就这样从终点一步步往前传。TD 不需要等完整回合结束,也不需要知道环境转移概率;它每走一步,就把已经发生的一点真实信息传回当前状态。

这就是自举(Bootstrapping):用“下一状态的当前估计”来帮助修正“当前状态的估计”。它不是乱猜,因为每次更新都至少包含一个真实发生的奖励 $r$

更新规则

把“走一步就更新一次”写成公式,就是 TD(0) 更新:

$$V(s) \leftarrow V(s) + \alpha \underbrace{\left[ r + \gamma V(s') - V(s) \right]}_{\text{TD Error } \delta} \tag{3.6}$$

这行公式可以这样读:

  1. $V(s)$:我原来以为当前状态 $s$ 值多少分。
  2. $r$:这一步真实拿到的奖励。
  3. $V(s')$:下一状态 $s'$ 现在估计值多少分。
  4. $r + \gamma V(s')$:TD Target,也就是“这一步真实奖励 + 打折后的下一站估计”。
  5. $r + \gamma V(s') - V(s)$:TD Error,也就是“新的临时目标”和“旧预测”之间差了多少。
  6. $\alpha$:学习率,控制每次把 $V(s)$ 往 TD Target 挪多远。

所以 TD 更新不是神秘公式,它就是:

$$ \text{新估计}

\text{旧估计}

  • \alpha \times (\text{TD Target} - \text{旧估计}) $$

:::details 慢慢拆:TD 更新公式从哪来?为什么不用等结束?

1. 先回到贝尔曼方程的一步形式

贝尔曼方程的核心意思是:

$$ V^\pi(s)

\mathbb{E}\left[r + \gamma V^\pi(s') \mid s\right] $$

翻译一下:状态 $s$ 的真实价值,等于“走一步得到的奖励 + 下一状态价值”的平均值。

DP 的做法是把所有可能动作、所有可能下一状态都列出来,精确算这个平均值。但这需要知道转移概率 $P$

TD 的做法更现实:我不列所有可能性,我只看这一次真实发生了什么。

实际走一步,观测到 $(r, s')$,就用这一个样本来更新:

$$V(s) \leftarrow V(s) + \alpha \left[ \underbrace{r + \gamma V(s')}_{\text{TD Target}} - V(s) \right]$$

这就是用一次真实采样代替 DP 的全概率求和。不需要知道 $P$,只需要真的走一步。

2. 再和 MC 对比

MC 和 TD 最大的区别在于:它们拿什么当更新目标。

方法 更新目标 需要等到什么时候? 未来怎么处理?
MC $G_t = r_t + \gamma r_{t+1} + \gamma^2 r_{t+2} + \cdots$ 等整局结束 全部用真实奖励
TD $r + \gamma V(s')$ 走一步就能更新 $V(s')$ 暂时代替未来

MC 的目标更“真实”,因为它等到了完整结果;TD 的目标更“及时”,因为它只等一步。

为什么 TD 有偏差?

TD Target 里面的 $V(s')$ 是估计值,不是真实完整回报。所以在训练早期,它可能不准:

$$\mathbb{E}[r + \gamma V(s')] \neq V^\pi(s)$$

这就是 TD 的偏差来源。只有当 $V(s')$ 已经估得很准时,TD Target 才会接近真实价值。

为什么 TD 方差更低?

MC 的 $G_t$ 把从当前时刻到游戏结束的所有随机奖励都加在一起。路径越长,随机性越多,波动就越大。

TD Target 只用了一个真实奖励 $r$,后面的未来先用当前估计 $V(s')$ 接上。它不那么“纯真实”,但波动小很多。

这就是 TD 的核心取舍:牺牲一点无偏性,换来更快、更稳定、更及时的更新。

TD(0) 的收敛性

在表格小环境里,只要每个状态都被访问足够多次,并且学习率逐渐变小,TD(0) 可以收敛到正确的价值函数。常见条件是:

  1. $\sum_{t=0}^{\infty} \alpha_t = \infty$(总学习量足够大)
  2. $\sum_{t=0}^{\infty} \alpha_t^2 &lt; \infty$(学习率衰减足够快)

不用先记证明。先记直觉:每次 TD 更新都不完美,但它不断把真实的一步奖励吸收进价值估计;随着下一状态估计越来越准,当前状态也会被带着越来越准。

一个具体数值例子

还是 3 格走廊:

S
M
G

真实价值是:

$$ V(S)=-2,\quad V(M)=-1,\quad V(G)=0 $$

假设刚开始全是 0,学习率 $\alpha=0.5$

第一次从 $S$ 走到 $M$

$V(S) \leftarrow 0 + 0.5 \times (-1 + 1 \times 0 - 0) = -0.5$

第一次从 $M$ 走到 $G$

$V(M) \leftarrow 0 + 0.5 \times (-1 + 1 \times 0 - 0) = -0.5$

下一轮再从 $S$ 走到 $M$ 时,$V(M)$ 已经不是 0,而是 -0.5,所以:

$$ \text{TD Target} = -1 + 1 \times (-0.5) = -1.5 $$

于是 $V(S)$ 会继续往 -2 靠近。你可以看到,TD 的信息是一格一格往前传的。 :::

TD Error 的定义:预测和目标之间差多少

$$\delta = r + \gamma V(s') - V(s) \tag{3.7}$$

TD Error 就是 TD Target 和旧预测之间的差:

$$ \delta

\underbrace{r + \gamma V(s')}_{\text{TD Target}}

\underbrace{V(s)}_{\text{旧预测}} $$

它的符号很好读:

  1. $\delta &gt; 0$:TD Target 比你原来想的更高,$V(s)$ 应该上调。
  2. $\delta &lt; 0$:TD Target 比你原来想的更低,$V(s)$ 应该下调。
  3. $\delta = 0$:这一步看起来预测正好,不需要改。

:::details 慢慢拆:TD Error 为什么是学习信号?

TD Error 是什么?

如果 $V$ 已经完全正确,那么它应该满足贝尔曼方程。也就是说,当前预测 $V(s)$ 应该等于“即时奖励 + 下一状态价值”的平均值。

单步采样时,我们看不到完整平均值,只看到这一次真实发生的 $(r,s')$。于是可以用下面这个差值来判断当前预测错了多少:

$$ \delta = r + \gamma V(s') - V(s) $$

这就是 TD Error。它可以理解成贝尔曼方程在这一次采样上的“没对齐程度”。

TD Error 的期望

如果对所有可能的下一步取平均,TD Error 的期望是:

$$ \mathbb{E}[\delta]

\mathbb{E}[r + \gamma V(s') \mid s]

  • V(s) $$

$V=V^\pi$ 时,贝尔曼方程成立,右边就等于 0:

$$ \mathbb{E}[\delta] = 0 $$

所以 TD Error 的平均值越接近 0,说明价值函数越接近贝尔曼方程要求的自洽状态。

TD Error vs 梯度下降的关系

TD 更新可以写成:

$$V(s) \leftarrow V(s) + \alpha \cdot \delta$$

如果用神经网络参数化 $V_\theta$,更新变为:

$$\theta \leftarrow \theta + \alpha \cdot \delta \cdot \nabla_\theta V_\theta(s)$$

这看起来很像梯度下降,但它不是真正的梯度下降——因为 TD Target $r + \gamma V(s')$ 中的 $V(s')$ 也依赖于 $\theta$,而 TD 方法在计算梯度时假装 TD Target 是常数。这种“半梯度”方法在实践中工作得很好,但理论分析更复杂。

TD Error 贯穿全书的三重角色

  1. 价值更新的驱动信号:$V(s) \leftarrow V(s) + \alpha \delta$(本节)
  2. 优势函数的最简形式:$\hat{A}(s,a) = \delta = r + \gamma V(s') - V(s)$,衡量“这个动作比平均水平好多少”(第 6 章)
  3. GAE 的构建基石:$A^{\text{GAE}} = \sum_{l=0}^{\infty} (\gamma \lambda)^l \delta_{t+l}$,多个 TD Error 的指数衰减加权和(第 7 章)

这三种角色会在后面反复出现。先把 TD Error 记成一句话:它是价值函数没有对齐贝尔曼目标的那一小段差距。 :::

TD vs MC:一个直觉对比

还是看 3 格走廊:

S
M
G

$S$ 出发,完整回报是:

$$ G_t = -1 + (-1) = -2 $$

MC 必须等你走到 $G$,拿到完整回报 -2,才能更新 $V(S)$

TD 只需要你从 $S$ 走到 $M$,看到一步奖励 -1,再看一眼当前的 $V(M)$,就能立刻更新 $V(S)$

$$ \delta = -1 + \gamma V(M) - V(S) $$

所以 TD 的优势很清楚:它不等最终答案,而是用一步真实结果加下一站估计,边走边修。

:::details 思考题:TD Error 能不能永远为 0? 在确定性环境中,理论上可以:只要所有状态的 $V$ 值都精确满足贝尔曼方程,TD Error 就处处为 0。但在随机环境和函数逼近(神经网络)的情况下,TD Error 通常不会精确为 0。因为环境的随机性导致每次采样到的 $(r, s')$ 不同,而神经网络的参数有限,无法精确表示所有状态的价值。所以在实际训练中,我们追求的是 TD Error 的期望趋近于 0。 :::

三代方法的演进

DP MC TD
需要环境模型?
需要完整轨迹?
自举
偏差 有(但低方差)
方差
代表场景 已知规则的小规模问题 有终止的采样任务 绝大多数无模型在线任务

演进逻辑:DP 精确但不现实 → MC 不需要模型但要等太久 → TD 不需要模型也不需要等。每一代都解决了前一代的核心局限。

本节总结

学完这一节,我们获得了一项关键能力:从数据中估计价值函数。三种方法共享同一个核心——贝尔曼方程——只是“怎么拿到未来信息”的方式不同:

  1. DP(动态规划):需要完美模型。直接用转移概率 $P$ 和奖励 $R$ 精确迭代。它是理论基准,也就是“如果知道一切,价值该怎么算”。
  2. MC(蒙特卡洛):不需要模型,但需要跑完一整局。用完整的真实回报 $G_t$ 更新。它的目标无偏,但方差很大,学习信号来得晚。
  3. TD(时序差分):不需要模型,走一步就更新。用“眼前的奖励 + 对未来的猜测($V(s')$)”来修正当前猜测。它有偏差但方差更低,是实际应用的主力。
  4. 统一视角(GPI):这些算法都可以拆解为“评估当前策略”和“根据价值改进策略”两个交替过程。

局限性与后续引申

如果你是初学者,最应该掌握的是 TD。不是因为它最简单,而是因为它是现代 RL 的主力——PPO、SAC、TD3 等工业界广泛使用的算法都以 TD 为基础。理解了 TD Error 和自举的思想,你就能直接理解后续高级算法的核心。

此外,这条 MC→TD 的演进线不仅出现在价值估计中。在后续的第 5 章,我们将看到完全相同的演进出现在策略优化中:REINFORCE 就是策略版的 MC(跑完一整局用完整回报),Actor-Critic 就是策略版的 TD(走一步就用 TD Error 更新)。

现在,我们已经解决了“如果不知道环境模型,怎么估计 $V(s)$”的问题。但 $V(s)$ 仍然有一个局限:它只告诉你局面好不好,却不直接告诉你该选哪个动作。为了直接做决策,我们需要把 $V(s)$ 扩展到具体的动作上。这就是下一节的内容:

下一节:路线一:Q(s,a)——给每个动作打分

参考文献

Footnotes

  1. Bellman, R. (1957). Dynamic Programming. Princeton University Press.

  2. Metropolis, N., & Ulam, S. (1949). The Monte Carlo method. Journal of the American Statistical Association, 44(247), 335-341.