有脚就行头像
关注

第02篇-马尔可夫决策过程-强化学习的数学语言

【Agentic RL 智能体强化学习】第 02 篇:马尔可夫决策过程 — 强化学习的数学语言

本系列定位:从强化学习数学基础出发,系统讲解 LLM 对齐(RLHF/DPO/GRPO)到 Agentic RL(多轮智能体强化学习)的全链路,以 OpenRLHF 框架为实战主线。


本篇你将学到

  • 马尔可夫决策过程(MDP)五元组 (S, A, P, R, γ) 的精确定义
  • 状态转移、奖励函数、折扣因子的数学含义
  • 如何用 ChatGPT 对齐场景类比 MDP——抽象数学的直觉化理解
  • 贝尔曼方程的推导与直觉
  • 用 Python 实现一个完整的 GridWorld MDP 环境

学完本篇,你将掌握强化学习最核心的数学框架,后续所有算法(PPO、GRPO 等)都在这个框架上构建。


一、为什么需要 MDP

在第 01 篇中,我们看到 ε-贪心策略通过"记住经验并利用"超越了随机策略。但那只解决了一个极简问题(多臂老虎机)。

真实的强化学习问题远比老虎机复杂:

  • AlphaGo:棋盘上有 361 个位置,每步有数百种合法落子,要考虑几十步之后的影响
  • ChatGPT:上下文窗口里有上千个 token,每个 token 的选择都影响后续生成的质量
  • AI Agent:每一步可以选择生成文字、调用工具、执行代码,交互可能持续几十轮

这些场景有一个共同的数学结构——马尔可夫决策过程(Markov Decision Process, MDP)。MDP 是所有强化学习算法的基础语言。理解了它,你就理解了 RL 的"语法"。


二、MDP 五元组:定义 RL 世界的物理定律

2.1 形式化定义

一个 MDP 由五元组 ( S , A , P , R , γ ) (S, A, P, R, \gamma) (S,A,P,R,γ) 定义:

符号名称含义LLM 类比
S S S状态空间 (State Space)所有可能状态的集合上下文窗口中的 token 序列
A A A动作空间 (Action Space)所有可能动作的集合词汇表中所有可选的 token
P P P状态转移 (Transition) P ( s ′ ∣ s , a ) P(s' | s, a) P(ss,a):在状态 s s s 执行动作 a a a 后转到 s ′ s' s 的概率选了 token a a a 后,新的上下文(状态)是确定的
R R R奖励函数 (Reward) R ( s , a ) R(s, a) R(s,a) R ( s , a , s ′ ) R(s, a, s') R(s,a,s):在状态 s s s 执行 a a a 的即时奖励奖励模型对该回答的打分
γ \gamma γ折扣因子 (Discount) γ ∈ [ 0 , 1 ] \gamma \in [0, 1] γ[0,1]:未来奖励的重要性权重对长对话中远处奖励的重视程度

2.2 马尔可夫性质

MDP 的"马"字来自马尔可夫性质(Markov Property):

未来只与现在有关,与过去无关。

数学表述:

P ( s t + 1 ∣ s t , a t , s t − 1 , a t − 1 , … , s 0 , a 0 ) = P ( s t + 1 ∣ s t , a t ) P(s_{t+1} | s_t, a_t, s_{t-1}, a_{t-1}, \ldots, s_0, a_0) = P(s_{t+1} | s_t, a_t) P(st+1st,at,st1,at1,,s0,a0)=P(st+1st,at)

翻译成大白话:给定当前状态 s t s_t st 和当前动作 a t a_t at,未来状态 s t + 1 s_{t+1} st+1 的概率分布与所有历史无关

为什么这个假设合理? 以 LLM 为例:当前状态就是"上下文窗口中的全部 token",它已经包含了所有历史信息。下一步生成什么 token,只取决于当前上下文,不需要知道"这个上下文是怎么一步步生成的"。Transformer 的自注意力机制天然满足马尔可夫性质。

2.3 用 ChatGPT 场景理解 MDP

让我们把 ChatGPT 的对齐过程映射到 MDP:

ChatGPT RLHF 的 MDP 模型

策略 π(A|S)

状态转移 P(S'|S,A)

每个 token 或回答结束

反馈

状态 S
用户问题 + 上下文

动作 A
模型生成的 token

新状态 S'
问题 + 已生成的 token

奖励 R
RM 对回答的评分

具体映射关系:

MDP 要素ChatGPT RLHF 中的对应
状态 s t s_t st[BOS] 用户:怎样学Python? [SEP] 助手:
动作 a t a_t at下一个 token,如 "Python"
状态转移确定性的:新状态 = 旧状态 + 新 token
奖励 R R R回答结束后,奖励模型对完整回答打分
策略 π ( a ∣ s ) \pi(a|s) π(as)LLM 的 token 概率分布 π θ ( ⋅ ∣ s t ) \pi_\theta(\cdot | s_t) πθ(st)
折扣因子 γ \gamma γ通常设为 1(不折扣,整个回答视为一个 Episode)

💡 关键洞察:LLM 生成文本的过程,本质上就是一个 MDP。每个 token 的选择是一个"动作",模型就是一个"策略"。这就是为什么 RL 可以用来训练 LLM。


三、回合、回报与折扣因子

3.1 Episode(回合)

RL 中的一次完整交互称为一个 Episode(回合/轨迹)。从初始状态出发,经过一系列状态-动作-奖励,到达终止状态。

Episode = ( s 0 , a 0 , r 1 , s 1 , a 1 , r 2 , s 2 , … , s T ) \text{Episode} = (s_0, a_0, r_1, s_1, a_1, r_2, s_2, \ldots, s_T) Episode=(s0,a0,r1,s1,a1,r2,s2,,sT)

场景Episode 示例
AlphaGo一盘棋:从空棋盘到终局
CartPole一次游戏:从平衡开始到杆子倒下
ChatGPT 单轮一次生成:从 prompt 到完整回答
Agentic RL一次任务:从接收指令到调用工具完成任务

3.2 Return(回报)

回报 G t G_t Gt 是从时刻 t t t 开始到 Episode 结束的累积奖励

G t = r t + 1 + γ r t + 2 + γ 2 r t + 3 + ⋯ = ∑ k = 0 ∞ γ k r t + k + 1 G_t = r_{t+1} + \gamma r_{t+2} + \gamma^2 r_{t+3} + \cdots = \sum_{k=0}^{\infty} \gamma^k r_{t+k+1} Gt=rt+1+γrt+2+γ2rt+3+=k=0γkrt+k+1

折扣因子 γ \gamma γ 的作用是让近期的奖励比远期的更重要:

γ \gamma γ含义效果
γ = 0 \gamma = 0 γ=0只看眼前完全贪心,只关心下一步奖励
γ = 0.9 \gamma = 0.9 γ=0.9适度远视未来 10 步的奖励权重降到 35%
γ = 0.99 \gamma = 0.99 γ=0.99高度远视未来 10 步的奖励权重还有 90%
γ = 1.0 \gamma = 1.0 γ=1.0无折扣所有未来奖励同等重要

在 LLM RLHF 中, γ \gamma γ 通常设为 1.0——因为一次生成被视为一个完整的 Episode,中间步骤没有独立的奖励信号(奖励只在回答结束时给出)。

3.3 折扣因子的数学意义

为什么需要折扣因子?三个原因:

  1. 数学便利:保证回报 G t G_t Gt 是有限的(当 Episode 无限长时)
  2. 不确定性:远期奖励更难预测,应该权重更低
  3. 人类偏好:生物本能上更偏好即时满足

对于有限长度的 Episode(如 LLM 生成), γ = 1 \gamma=1 γ=1 是合理的。对于可能无限延续的环境(如 Agent 与工具的持续交互), γ < 1 \gamma < 1 γ<1 是必要的。


四、策略与价值函数

4.1 策略 (Policy)

策略 π ( a ∣ s ) \pi(a|s) π(as) 是状态到动作概率分布的映射:

π ( a ∣ s ) = P ( A t = a ∣ S t = s ) \pi(a|s) = P(A_t = a | S_t = s) π(as)=P(At=aSt=s)

它回答了一个核心问题:在状态 s s s 下,应该以多大概率选择动作 a a a

策略类型定义示例
确定性策略 π ( s ) = a \pi(s) = a π(s)=a在状态 s s s 永远选固定的动作 a a a
随机性策略 π ( a ∣ s ) ∈ [ 0 , 1 ] \pi(a|s) \in [0,1] π(as)[0,1]以一定概率分布选择动作

LLM 天然是随机性策略——在给定上下文下,它输出词汇表上的概率分布。采样温度(temperature)控制这个分布的"随机程度"。

4.2 状态价值函数 V π ( s ) V_\pi(s) Vπ(s)

状态价值函数回答:从状态 s s s 出发,遵循策略 π \pi π,预期能获得多少回报?

V π ( s ) = E π [ G t ∣ S t = s ] = E π [ ∑ k = 0 ∞ γ k r t + k + 1 ∣ S t = s ] V_\pi(s) = \mathbb{E}_\pi \left[ G_t | S_t = s \right] = \mathbb{E}_\pi \left[ \sum_{k=0}^{\infty} \gamma^k r_{t+k+1} \bigg| S_t = s \right] Vπ(s)=Eπ[GtSt=s]=Eπ[k=0γkrt+k+1 St=s]

直觉: V π ( s ) V_\pi(s) Vπ(s) 高 → 当前状态"好"; V π ( s ) V_\pi(s) Vπ(s) 低 → 当前状态"差"。

4.3 动作价值函数 Q π ( s , a ) Q_\pi(s, a) Qπ(s,a)

动作价值函数回答:在状态 s s s 执行动作 a a a,之后遵循策略 π \pi π,预期获得多少回报?

Q π ( s , a ) = E π [ G t ∣ S t = s , A t = a ] Q_\pi(s, a) = \mathbb{E}_\pi \left[ G_t | S_t = s, A_t = a \right] Qπ(s,a)=Eπ[GtSt=s,At=a]

V V V Q Q Q 的关系:

V π ( s ) = ∑ a π ( a ∣ s ) ⋅ Q π ( s , a ) V_\pi(s) = \sum_a \pi(a|s) \cdot Q_\pi(s, a) Vπ(s)=aπ(as)Qπ(s,a)

翻译成大白话:状态 s s s 的价值 = 所有可能动作的价值按策略概率加权求和。

4.4 价值函数的直觉

用一个简单例子来理解。假设你在迷宫中:

右走

上走

下走

状态A
起点

状态B
分岔口

状态C
死路
R=-1

状态D
宝藏
R=+10

在这个迷宫中:

  • V ( C ) = − 1 V(C) = -1 V(C)=1(到达死路,获得 -1 奖励)
  • V ( D ) = + 10 V(D) = +10 V(D)=+10(到达宝藏,获得 +10 奖励)
  • V ( B ) = 0.5 × V ( C ) + 0.5 × V ( D ) = 0.5 × ( − 1 ) + 0.5 × 10 = 4.5 V(B) = 0.5 \times V(C) + 0.5 \times V(D) = 0.5 \times (-1) + 0.5 \times 10 = 4.5 V(B)=0.5×V(C)+0.5×V(D)=0.5×(1)+0.5×10=4.5(如果上下各 50% 概率)
  • 在 B 点, Q ( B , 下走 ) = 10 > Q ( B , 上走 ) = − 1 Q(B, \text{下走}) = 10 > Q(B, \text{上走}) = -1 Q(B,下走)=10>Q(B,上走)=1,所以应该往下走

这就是 RL 的核心思想:通过比较 Q Q Q 值来做出更好的决策


五、贝尔曼方程:RL 的递归灵魂

5.1 直觉

价值函数有一个优美的递归结构——当前状态的价值 = 即时奖励 + 下一状态的价值

就像复利公式:今天的财富 = 今天的收入 + 明天的财富 × 折扣率。

5.2 贝尔曼期望方程

状态价值的贝尔曼方程

V π ( s ) = ∑ a π ( a ∣ s ) ∑ s ′ P ( s ′ ∣ s , a ) [ R ( s , a , s ′ ) + γ V π ( s ′ ) ] V_\pi(s) = \sum_a \pi(a|s) \sum_{s'} P(s'|s,a) \left[ R(s,a,s') + \gamma V_\pi(s') \right] Vπ(s)=aπ(as)sP(ss,a)[R(s,a,s)+γVπ(s)]

动作价值的贝尔曼方程

Q π ( s , a ) = ∑ s ′ P ( s ′ ∣ s , a ) [ R ( s , a , s ′ ) + γ ∑ a ′ π ( a ′ ∣ s ′ ) Q π ( s ′ , a ′ ) ] Q_\pi(s,a) = \sum_{s'} P(s'|s,a) \left[ R(s,a,s') + \gamma \sum_{a'} \pi(a'|s') Q_\pi(s',a') \right] Qπ(s,a)=sP(ss,a)[R(s,a,s)+γaπ(as)Qπ(s,a)]

用文字解读这个方程:

拆解为

求和

V(s) = 当前状态的价值

对所有动作 a 求期望
按策略概率 π(a|s) 加权

对于每个动作 a
考虑所有可能的下一状态 s'

即时奖励 R(s,a,s')
+ 下一状态价值 γV(s')

5.3 贝尔曼最优方程

当我们想找到最优策略 π ∗ \pi^* π 时,对应的贝尔曼最优方程:

V ∗ ( s ) = max ⁡ a ∑ s ′ P ( s ′ ∣ s , a ) [ R ( s , a , s ′ ) + γ V ∗ ( s ′ ) ] V^*(s) = \max_a \sum_{s'} P(s'|s,a) \left[ R(s,a,s') + \gamma V^*(s') \right] V(s)=amaxsP(ss,a)[R(s,a,s)+γV(s)]

Q ∗ ( s , a ) = ∑ s ′ P ( s ′ ∣ s , a ) [ R ( s , a , s ′ ) + γ max ⁡ a ′ Q ∗ ( s ′ , a ′ ) ] Q^*(s,a) = \sum_{s'} P(s'|s,a) \left[ R(s,a,s') + \gamma \max_{a'} Q^*(s',a') \right] Q(s,a)=sP(ss,a)[R(s,a,s)+γamaxQ(s,a)]

注意:最优方程把期望 ∑ a π ( a ∣ s ) \sum_a \pi(a|s) aπ(as) 换成了 max ⁡ a \max_a maxa——不再"平均",而是"选最好的"。

🔑 后续关联:Q-Learning(第 04 篇)直接利用贝尔曼最优方程来学习 Q ∗ Q^* Q。PPO(第 07 篇)通过策略梯度来逼近最优策略。


六、部分可观测 MDP(POMDP)

6.1 现实中的不完全信息

经典 MDP 假设 Agent 完全知道当前状态。但现实中,Agent 往往只能观察到状态的一部分——这就是 POMDP(Partially Observable MDP)。

POMDP 在 MDP 五元组基础上增加两个要素:

新增要素含义
观测空间 O O OAgent 能感知到的信息
观测函数 Z ( o ∣ s , a ) Z(o|s,a) Z(os,a)执行动作 a a a 后到达状态 s ′ s' s 时,观测到 o o o 的概率

6.2 LLM 与 POMDP

LLM 的生成过程实际上更接近 POMDP 而非标准 MDP:

  • 观测:输入上下文(token 序列),而非"真实状态"
  • 隐藏状态:Transformer 的内部表征(注意力模式、隐藏层激活)
  • 策略:基于观测(token 序列)生成动作(下一个 token)

不过在实际 RLHF 实现中,我们把 token 序列直接当作"状态"来处理,这样就回到了标准 MDP 框架。这是一种合理的简化。


七、实战:GridWorld MDP 环境实现

现在让我们用代码实现一个完整的 MDP 环境。GridWorld 是 RL 教学的经典场景——一个网格世界,Agent 需要从起点走到终点。

7.1 环境设计

"""
AgentRL-Lab v0.1: GridWorld MDP 环境

这是一个标准的网格世界 MDP:
- 4×4 网格,左上角为起点,右下角为终点
- 每步奖励 -1(鼓励快速到达终点)
- 碰墙不动,原地奖励 -1
- 到达终点奖励 +10,游戏结束
"""
import numpy as np
import matplotlib.pyplot as plt
from typing import Tuple, Optional


class GridWorld:
    """GridWorld MDP 环境"""

    # 动作定义
    UP, DOWN, LEFT, RIGHT = 0, 1, 2, 3
    ACTION_NAMES = ["↑ 上", "↓ 下", "← 左", "→ 右"]

    def __init__(self, size: int = 4):
        self.size = size
        self.n_states = size * size
        self.n_actions = 4

        # 终止状态:右下角
        self.terminal_state = self.n_states - 1
        # 起始状态:左上角
        self.start_state = 0

        # 构建状态转移矩阵 P[s][a] = (next_state, reward, done)
        self.P = self._build_transition_matrix()

        self.current_state = self.start_state

    def _state_to_pos(self, state: int) -> Tuple[int, int]:
        """状态编号 → (行, 列)"""
        return state // self.size, state % self.size

    def _pos_to_state(self, row: int, col: int) -> int:
        """(行, 列) → 状态编号"""
        return row * self.size + col

    def _build_transition_matrix(self):
        """
        构建状态转移矩阵
        P[s][a] = (next_state, reward, is_terminal)
        """
        P = {}
        for s in range(self.n_states):
            P[s] = {}
            row, col = self._state_to_pos(s)

            for a in range(self.n_actions):
                if s == self.terminal_state:
                    # 终止状态:保持在原地,奖励 0
                    P[s][a] = (s, 0.0, True)
                    continue

                # 计算下一个位置
                next_row, next_col = row, col
                if a == self.UP:
                    next_row = max(0, row - 1)
                elif a == self.DOWN:
                    next_row = min(self.size - 1, row + 1)
                elif a == self.LEFT:
                    next_col = max(0, col - 1)
                elif a == self.RIGHT:
                    next_col = min(self.size - 1, col + 1)

                next_state = self._pos_to_state(next_row, next_col)

                # 奖励设计
                if next_state == self.terminal_state:
                    reward = 10.0   # 到达终点
                else:
                    reward = -1.0   # 每步惩罚

                P[s][a] = (next_state, reward, next_state == self.terminal_state)

        return P

    def reset(self, seed: Optional[int] = None) -> int:
        """重置环境,返回初始状态"""
        if seed is not None:
            np.random.seed(seed)
        self.current_state = self.start_state
        return self.current_state

    def step(self, action: int) -> Tuple[int, float, bool]:
        """
        执行动作
        返回: (next_state, reward, done)
        """
        next_state, reward, done = self.P[self.current_state][action]
        self.current_state = next_state
        return next_state, reward, done

    def render(self):
        """可视化当前状态"""
        grid = np.full((self.size, self.size), "·")
        grid[0][0] = "S"  # 起点
        grid[self.size - 1][self.size - 1] = "G"  # 终点

        row, col = self._state_to_pos(self.current_state)
        if self.current_state not in (self.start_state, self.terminal_state):
            grid[row][col] = "A"  # Agent

        print("\nGridWorld 状态:")
        print("+" + "--+" * self.size)
        for r in range(self.size):
            row_str = "|"
            for c in range(self.size):
                row_str += f" {grid[r][c]}|"
            print(row_str)
            print("+" + "--+" * self.size)


if __name__ == "__main__":
    env = GridWorld(size=4)

    print(f"状态空间: {env.n_states} 个状态")
    print(f"动作空间: {env.n_actions} 个动作 {env.ACTION_NAMES}")
    print(f"起点: 状态 {env.start_state}, 终点: 状态 {env.terminal_state}")
    print()

    # 展示状态转移矩阵的一部分
    print("=== 状态转移矩阵示例 ===")
    for s in [0, 5, 10]:
        print(f"\n状态 {s} (位置 {env._state_to_pos(s)}):")
        for a in range(env.n_actions):
            next_s, r, done = env.P[s][a]
            print(f"  {env.ACTION_NAMES[a]:>6} → 状态{next_s:2d}, 奖励={r:+.0f}, 结束={done}")

    # 运行一个随机策略的 Episode
    print("\n=== 随机策略 Episode 示例 ===")
    state = env.reset(seed=42)
    env.render()
    total_reward = 0
    steps = 0

    while True:
        action = np.random.randint(env.n_actions)
        next_state, reward, done = env.step(action)
        total_reward += reward
        steps += 1
        print(f"步骤 {steps}: {env.ACTION_NAMES[action]} → 状态{next_state}, 奖励={reward:+.0f}")

        if done or steps >= 50:
            break
        state = next_state

    print(f"\nEpisode 结束: 总步数={steps}, 总奖励={total_reward:+.0f}")

运行后你会看到类似输出:

状态空间: 16 个状态
动作空间: 4 个动作 ['↑ 上', '↓ 下', '← 左', '→ 右']
起点: 状态 0, 终点: 状态 15

=== 状态转移矩阵示例 ===

状态 0 (位置 (0, 0)):
  ↑ 上 → 状态 0, 奖励=-1, 结束=False
  ↓ 下 → 状态 4, 奖励=-1, 结束=False
  ← 左 → 状态 0, 奖励=-1, 结束=False
  → 右 → 状态 1, 奖励=-1, 结束=False

状态 5 (位置 (1, 1)):
  ↑ 上 → 状态 1, 奖励=-1, 结束=False
  ↓ 下 → 状态 9, 奖励=-1, 结束=False
  ← 左 → 状态 4, 奖励=-1, 结束=False
  → 右 → 状态 6, 奖励=-1, 结束=False

7.2 用价值迭代求解最优策略

有了 MDP 环境后,我们可以用价值迭代(Value Iteration)来求解最优策略。这直接利用了贝尔曼最优方程:

"""
GridWorld 价值迭代:用贝尔曼最优方程求最优策略
"""
import numpy as np

def value_iteration(env, gamma=0.99, theta=1e-6, max_iter=1000):
    """
    价值迭代算法
    直接利用贝尔曼最优方程 V*(s) = max_a Σ P(s'|s,a)[R + γV*(s')]

    Args:
        env: GridWorld 环境
        gamma: 折扣因子
        theta: 收敛阈值
        max_iter: 最大迭代次数

    Returns:
        V: 最优状态价值
        policy: 最优策略
    """
    V = np.zeros(env.n_states)

    for iteration in range(max_iter):
        delta = 0
        for s in range(env.n_states):
            # 跳过终止状态
            if s == env.terminal_state:
                continue

            # 计算每个动作的价值
            action_values = []
            for a in range(env.n_actions):
                next_s, reward, done = env.P[s][a]
                # Q(s,a) = R + γV(s')(GridWorld 是确定性的)
                q_value = reward + gamma * V[next_s] * (not done)
                action_values.append(q_value)

            # 贝尔曼最优方程:取最大的 Q 值
            old_v = V[s]
            V[s] = max(action_values)
            delta = max(delta, abs(old_v - V[s]))

        if delta < theta:
            print(f"价值迭代在第 {iteration + 1} 轮收敛")
            break

    # 从最优价值函数提取最优策略
    policy = np.zeros(env.n_states, dtype=int)
    for s in range(env.n_states):
        if s == env.terminal_state:
            continue
        action_values = []
        for a in range(env.n_actions):
            next_s, reward, done = env.P[s][a]
            q_value = reward + gamma * V[next_s] * (not done)
            action_values.append(q_value)
        policy[s] = np.argmax(action_values)

    return V, policy


def print_value_function(env, V):
    """以网格形式打印价值函数"""
    print("\n最优状态价值函数 V*(s):")
    print("+" + "--------+" * env.size)
    for r in range(env.size):
        row_str = "|"
        for c in range(env.size):
            s = r * env.size + c
            row_str += f" {V[s]:+6.2f} |"
        print(row_str)
        print("+" + "--------+" * env.size)


def print_policy(env, policy):
    """以网格形式打印策略"""
    arrows = ["↑", "↓", "←", "→"]
    print("\n最优策略 π*(s):")
    print("+" + "----+" * env.size)
    for r in range(env.size):
        row_str = "|"
        for c in range(env.size):
            s = r * env.size + c
            if s == env.terminal_state:
                row_str += "  G |"
            elif s == env.start_state:
                row_str += f" S{arrows[policy[s]]} |"
            else:
                row_str += f"  {arrows[policy[s]]} |"
        print(row_str)
        print("+" + "----+" * env.size)


if __name__ == "__main__":
    from gridworld import GridWorld

    env = GridWorld(size=4)

    # 价值迭代求解
    V, policy = value_iteration(env, gamma=0.99)

    print_value_function(env, V)
    print_policy(env, policy)

    # 用最优策略走一遍
    print("\n=== 最优策略 Episode ===")
    state = env.reset(seed=42)
    total_reward = 0
    steps = 0

    while True:
        action = policy[state]
        next_state, reward, done = env.step(action)
        total_reward += reward
        steps += 1
        action_name = env.ACTION_NAMES[action]
        print(f"  步骤 {steps}: 状态{state:2d}{action_name} → 状态{next_state:2d}, 奖励={reward:+.0f}")
        state = next_state
        if done or steps >= 20:
            break

    print(f"\n最优策略: 总步数={steps}, 总奖励={total_reward:+.0f}")
    print("💡 对比随机策略(通常需要 20-50 步),最优策略只需要 6 步!")

预期输出:

价值迭代在第 22 轮收敛

最优状态价值函数 V*(s):
+--------+--------+--------+--------+
|  +4.80 |  +5.86 |  +6.93 |  +7.92 |
+--------+--------+--------+--------+
|  +5.86 |  +6.93 |  +7.92 |  +8.92 |
+--------+--------+--------+--------+
|  +6.93 |  +7.92 |  +8.92 |  +9.50 |
+--------+--------+--------+--------+
|  +7.92 |  +8.92 |  +9.50 |  +0.00 |
+--------+--------+--------+--------+

最优策略 π*(s):
+----+----+----+----+
| S→ |  → |  ↓ |  ↓ |
+----+----+----+----+
|  ↓ |  ↓ |  ↓ |  ↓ |
+----+----+----+----+
|  ↓ |  ↓ |  ↓ |  ↓ |
+----+----+----+----+
|  → |  → |  → |  G |
+----+----+----+----+

=== 最优策略 Episode ===
  步骤 1: 状态 0 → →右 → 状态 1, 奖励=-1
  步骤 2: 状态 1 → →右 → 状态 2, 奖励=-1
  步骤 3: 状态 2 → ↓下 → 状态 6, 奖励=-1
  步骤 4: 状态 6 → ↓下 → 状态10, 奖励=-1
  步骤 5: 状态10 → ↓下 → 状态14, 奖励=-1
  步骤 6: 状态14 → →右 → 状态15, 奖励=+10

最优策略: 总步数=6, 总奖励=+5
💡 对比随机策略(通常需要 20-50 步),最优策略只需要 6 步!

注意价值函数的规律:离终点越近的状态价值越高,这完全符合直觉——离目标越近,预期回报越大。

7.3 代码解读

价值迭代算法的核心是一个两步循环

初始化 V(s)=0

对每个状态 s
计算所有动作的 Q 值

V(s) = max_a Q(s,a)
贝尔曼最优更新

收敛?
Δ < θ

从 V* 提取最优策略

这段代码虽然是为 GridWorld 写的,但它体现的思想是通用的:

  1. 贝尔曼最优方程是可计算的——通过反复迭代,价值函数会收敛到最优
  2. 最优策略可以从最优价值函数提取——在每个状态选 Q 值最大的动作
  3. 折扣因子影响收敛速度和策略——gamma 越大,考虑越远,收敛越慢

在后续文章中,当我们讨论 PPO 和 GRPO 时,你会看到同样的"估计价值 → 选择更好的动作 → 更新策略"的范式,只是从精确计算变成了用神经网络去逼近。


本篇小结

知识点核心内容
MDP 五元组 ( S , A , P , R , γ ) (S, A, P, R, \gamma) (S,A,P,R,γ):状态、动作、转移、奖励、折扣
马尔可夫性质未来只与现在有关: P ( s ′ ∣ s , a ) P(s'|s,a) P(ss,a) 不依赖历史
Return(回报) G t = ∑ k = 0 ∞ γ k r t + k + 1 G_t = \sum_{k=0}^{\infty} \gamma^k r_{t+k+1} Gt=k=0γkrt+k+1:累积折扣奖励
状态价值 V π ( s ) V_\pi(s) Vπ(s)从状态 s s s 出发遵循策略 π \pi π 的预期回报
动作价值 Q π ( s , a ) Q_\pi(s,a) Qπ(s,a)在状态 s s s 执行动作 a a a 后遵循 π \pi π 的预期回报
贝尔曼方程 V ( s ) = ∑ a π ( a ∣ s ) ∑ s ′ P ( s ′ ∣ s , a ) [ R + γ V ( s ′ ) ] V(s) = \sum_a \pi(a|s) \sum_{s'} P(s'|s,a)[R + \gamma V(s')] V(s)=aπ(as)sP(ss,a)[R+γV(s)]:递归结构
价值迭代反复用贝尔曼最优方程更新 V 直到收敛,再提取最优策略

下篇预告

第 03 篇:值函数与贝尔曼方程 — 评估"好不好"的数学工具
深入贝尔曼方程的计算方法——蒙特卡洛估计和时序差分学习。这两种方法是对后续 PPO 和 GRPO 理解的基础。


如果本篇内容对你有帮助,欢迎点赞收藏!有任何疑问,欢迎在评论区交流。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/m0_68987304/article/details/163877913

文章来源转载

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--