2. 马尔可夫决策过程

马尔可夫决策过程,简称 MDP。说实话,这是整个强化学习的基石。你想想看,不管多复杂的决策问题,最后都能抽象成 MDP 这套框架。我在做机器人路径规划时,一开始总想着直接写规则,结果代码越写越乱。后来老老实实搭了个 MDP 模型,问题一下子就清晰了。

2.1 什么是马尔可夫决策过程

MDP 说白了,就是一个数学框架。它用来描述智能体与环境交互的过程。智能体观察当前状态,做出一个动作,环境反馈一个奖励,然后状态发生转移。就这么简单。

但这里有个关键点——马尔可夫性。什么意思呢?就是未来的状态只取决于当前状态和当前动作,跟过去的历史没关系。嗯,这个假设在实际中不一定完全成立,但大多数情况下够用了。

核心五元组:MDP 可以用 (S, A, P, R, γ) 来表示。

  • S:状态空间,所有可能状态的集合
  • A:动作空间,所有可能动作的集合
  • P:状态转移概率,P(s'|s,a)
  • R:奖励函数,R(s,a,s')
  • γ:折扣因子,γ ∈ [0,1]

2.2 状态与动作

状态,就是智能体感知到的环境信息。动作,就是智能体可以做的事情。这两个概念看起来简单,但实际设计时坑很多。

我习惯把状态设计成充分统计量。什么意思?就是状态里包含的信息,要能让你做出最优决策。比如下围棋,当前棋盘上的棋子分布就是状态,你不需要知道之前每一步是怎么下的。

动作空间分两种:离散和连续。离散动作空间,比如上下左右四个方向。连续动作空间,比如方向盘转多少度。我个人建议,能用离散就别用连续。连续动作空间处理起来复杂得多,我在自动驾驶项目中吃过这个亏。

我的经验:设计状态时,多问自己一句——这个信息对决策有用吗?如果没用,就别放进去。状态维度太高,学习会非常慢。

2.3 奖励函数

奖励函数,就是告诉智能体什么行为是好的。这是 MDP 里最需要花心思设计的地方。

奖励函数设计有几个原则:

  • 稀疏 vs 密集:稀疏奖励只在目标达成时给,比如赢了给+1,输了给-1。密集奖励每一步都给,比如离目标近了给正奖励。密集奖励学得快,但容易让智能体钻空子。
  • 延迟奖励:有些动作的后果要很久才显现。比如下棋,一步好棋可能要到几十步后才赢。这时候就需要折扣因子来帮忙。
  • 奖励塑形:可以给一些中间奖励来引导学习。但要注意,不要改变最优策略。我曾经在某个项目中,给了一个看似合理的中间奖励,结果智能体学会了原地转圈刷分。

避坑指南:我曾经设计过一个奖励函数,让智能体既要快又要稳。结果它学会了走一步停一步,因为这样既不会太快摔倒,又能拿到时间奖励。后来我改成只奖励最终目标,问题才解决。

2.4 状态转移概率

转移概率 P(s'|s,a),描述的是在状态 s 下执行动作 a,转移到状态 s' 的概率。这个概率反映了环境的随机性。

比如你让机器人往前走一步,它可能因为地面打滑,只走了半步。这个不确定性,就体现在转移概率里。

转移概率有两种情况:

  • 已知模型:比如棋盘游戏,规则是确定的,转移概率就是 0 或 1。这种情况可以用动态规划求解。
  • 未知模型:大多数实际问题,转移概率是未知的。这时候就需要通过采样来估计,或者用无模型方法。

我个人习惯,先假设模型已知,用动态规划跑一遍,看看最优策略长什么样。然后再切换到未知模型,用强化学习去逼近。这样心里有个底。

2.5 折扣因子

折扣因子 γ,是 MDP 里一个微妙但重要的参数。它决定了智能体有多「短视」。

  • γ = 0:只看眼前,只关心即时奖励。
  • γ = 1:无限远视,所有未来奖励同等重要。
  • γ 在 0 到 1 之间:越靠近 1,越看重长期收益。

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

  1. 数学上保证收敛:无限时间步的奖励求和可能发散,折扣因子让求和收敛。
  2. 模拟不确定性:未来的奖励不如现在的可靠,打个折扣更合理。
  3. 控制策略偏好:调整 γ 可以改变智能体的行为风格。

实际经验:我一般从 γ=0.9 开始调。如果智能体太短视,就增大到 0.95 或 0.99。如果训练不稳定,就减小一些。没有固定值,全靠试。

2.6 核心逻辑流程图

下面这张图,展示了 MDP 的核心交互逻辑。我每次讲课时都会画一遍,因为它太重要了。

智能体 Agent 环境 Environment 动作 a 状态 s 奖励 r 循环直到终止状态 转移概率 P(s'|s,a) 折扣因子 γ ∈ [0,1]

你看这个循环:智能体观察状态 s,做出动作 a,环境反馈奖励 r 并转移到新状态 s'。然后重复这个过程。这就是 MDP 的全部。

2.7 一个简单的代码示例

下面我用 Python 实现一个极简的 MDP 环境。这个环境只有两个状态,两个动作。代码很简单,但能帮你理解 MDP 的结构。

import numpy as np

class SimpleMDP:
    def __init__(self, gamma=0.9):
        self.states = [0, 1]          # 两个状态
        self.actions = [0, 1]         # 两个动作
        self.gamma = gamma            # 折扣因子
        
        # 转移概率 P(s'|s,a)
        # P[state][action][next_state]
        self.P = {
            0: {0: {0: 0.8, 1: 0.2},   # 状态0,动作0
                1: {0: 0.3, 1: 0.7}},  # 状态0,动作1
            1: {0: {0: 0.6, 1: 0.4},   # 状态1,动作0
                1: {0: 0.1, 1: 0.9}}   # 状态1,动作1
        }
        
        # 奖励函数 R(s,a,s')
        self.R = {
            0: {0: {0: 1, 1: -1},      # 状态0,动作0
                1: {0: -1, 1: 2}},     # 状态0,动作1
            1: {0: {0: 0, 1: 1},       # 状态1,动作0
                1: {0: -2, 1: 3}}      # 状态1,动作1
        }
    
    def step(self, state, action):
        # 根据转移概率采样下一个状态
        probs = self.P[state][action]
        next_state = np.random.choice(
            self.states, p=[probs[s] for s in self.states]
        )
        # 获取奖励
        reward = self.R[state][action][next_state]
        return next_state, reward
    
    def value_iteration(self, theta=1e-6):
        # 值迭代求解最优价值函数
        V = np.zeros(len(self.states))
        while True:
            delta = 0
            for s in self.states:
                v = V[s]
                # Bellman 最优方程
                max_value = float('-inf')
                for a in self.actions:
                    value = 0
                    for s_next in self.states:
                        prob = self.P[s][a][s_next]
                        reward = self.R[s][a][s_next]
                        value += prob * (reward + self.gamma * V[s_next])
                    max_value = max(max_value, value)
                V[s] = max_value
                delta = max(delta, abs(v - V[s]))
            if delta < theta:
                break
        return V

# 使用示例
mdp = SimpleMDP(gamma=0.95)
optimal_V = mdp.value_iteration()
print("最优价值函数:", optimal_V)

小提示:代码里的值迭代算法,就是不断用 Bellman 最优方程更新价值函数,直到收敛。这是求解已知 MDP 的标准方法。

2.8 实际应用中的注意事项

最后,分享几个我在项目中积累的经验:

  • 状态空间爆炸:状态太多时,转移概率矩阵会变得巨大。这时候需要用函数近似,比如神经网络。
  • 奖励设计是艺术:我见过太多项目,算法没问题,但奖励设计不合理导致学不出来。多花时间在奖励设计上,绝对值得。
  • 折扣因子不是玄学:它直接影响策略。任务周期短,γ 可以小一些。任务周期长,γ 要大一些。
  • 先跑通再优化:我习惯先用小规模问题验证 MDP 模型是否正确,再扩展到大规模。这样能快速发现设计缺陷。

再次提醒:我曾经在一个项目中,把状态设计得过于复杂,包含了大量无关信息。结果算法跑了三天都没收敛。后来精简状态,只保留关键信息,半天就出结果了。少即是多,在 MDP 设计中尤其适用。

好了,MDP 的核心内容就这些。状态、动作、奖励、转移概率、折扣因子,这五个要素构成了强化学习的基石。理解透它们,后面的算法学起来就轻松多了。