4、蒙特卡洛方法:蒙特卡洛预测、蒙特卡洛控制、探索与利用的平衡(ε-贪婪策略),以及首次访问与每次访问MC的区别

4.1 蒙特卡洛方法:不靠模型,靠采样

蒙特卡洛方法,说白了就是「用大量随机采样来逼近真实值」。在强化学习里,它不依赖环境模型,直接通过完整的 episode 来估计状态价值或动作价值。

我个人习惯把蒙特卡洛方法理解为「事后诸葛亮」——你先把一整局游戏打完,然后回头看看每个状态到底得了多少回报,用这些回报的平均值来更新价值估计。嗯,听起来很直觉,对吧?

我在项目中遇到过这样一个场景:一个机器人导航任务,环境太复杂,根本建不出准确的转移概率模型。这时候蒙特卡洛方法就派上用场了——让机器人自己跑,跑完一局算一局,慢慢就能学会怎么走。

核心思想: 用大量完整轨迹的回报均值,逼近真实的价值函数。

4.2 蒙特卡洛预测:如何估计状态价值?

预测问题,就是给定一个策略 π,估计每个状态的价值 v_π(s)。蒙特卡洛的做法很简单:

  1. 从状态 s 开始,按照策略 π 走完一个 episode
  2. 记录下这个 episode 中 s 之后获得的累计回报 G_t
  3. 重复多次,取 G_t 的平均值作为 v_π(s) 的估计

你想想看,这其实就是一个大数定律的应用。采样次数越多,估计越准。

# 蒙特卡洛预测的伪代码
def mc_prediction(episodes, gamma):
    returns = defaultdict(list)
    V = defaultdict(float)
    
    for episode in episodes:
        G = 0
        # 从后往前计算回报
        for t in range(len(episode)-1, -1, -1):
            state, reward = episode[t]
            G = gamma * G + reward
            returns[state].append(G)
            V[state] = mean(returns[state])
    return V
我的经验: 从后往前计算回报是个小技巧,这样只需要一次遍历就能算出所有时间步的 G_t。我第一次写的时候从前往后算,代码又长又慢,后来才意识到这个优化。

4.3 首次访问 vs 每次访问:一个关键区别

这两个概念,我刚开始学的时候也绕了一阵。它们的区别其实就一句话:

类型 做法 特点
首次访问 MC 一个 episode 中,状态 s 第一次出现时才更新 无偏估计,方差稍大
每次访问 MC 一个 episode 中,状态 s 每次出现都更新 有偏估计(但偏差渐近消失),方差更小

为什么会这样?因为首次访问只取第一次出现的回报,而每次访问把所有出现都算进去。理论上首次访问更「干净」,但实践中每次访问往往收敛更快。

我曾经在一个棋类游戏中对比过两者,发现每次访问 MC 在相同 episode 数下,价值估计的波动更小。嗯,当然这跟任务性质有关,不能一概而论。

注意: 首次访问 MC 在理论分析中更常用,因为它满足独立同分布假设。每次访问 MC 虽然好用,但严格证明收敛性要复杂一些。

4.4 蒙特卡洛控制:从预测到决策

预测学会了,接下来就是控制——找到最优策略。蒙特卡洛控制的核心是「广义策略迭代」:

  1. 策略评估: 用 MC 方法估计当前策略的动作价值 q(s, a)
  2. 策略改进: 对每个状态,选择 q 值最大的动作

但这里有个坑:如果你总是选当前最好的动作,那些没试过的动作就永远没机会被评估了。这就是探索与利用的平衡问题。

4.5 ε-贪婪策略:给探索留一扇窗

我建议用 ε-贪婪策略来解决这个问题。它的逻辑很简单:

  • 以概率 1-ε 选择当前最优动作(利用)
  • 以概率 ε 随机选择一个动作(探索)
def epsilon_greedy(Q, state, epsilon, n_actions):
    if random.random() < epsilon:
        return random.randint(0, n_actions-1)  # 探索
    else:
        return argmax(Q[state])  # 利用

ε 的取值很关键。我刚开始做项目时,把 ε 设成 0.1 就扔在那不管了,结果算法到后期还在乱探索,收敛很慢。后来我改成 ε 随时间衰减,效果好了很多。

避坑指南: 我曾经把 ε 设得太大(0.5),结果智能体一直在随机游走,根本学不到东西。建议初始 ε 在 0.1~0.3 之间,然后逐步衰减到 0.01 左右。

4.6 蒙特卡洛控制完整流程

把上面这些拼起来,就是完整的蒙特卡洛控制算法:

def mc_control(episodes, gamma, epsilon):
    Q = defaultdict(lambda: zeros(n_actions))
    returns = defaultdict(list)
    
    for episode in episodes:
        G = 0
        # 从后往前
        for t in range(len(episode)-1, -1, -1):
            state, action, reward = episode[t]
            G = gamma * G + reward
            # 首次访问 MC
            if (state, action) not in episode[:t]:
                returns[(state, action)].append(G)
                Q[state][action] = mean(returns[(state, action)])
        # 策略改进:ε-贪婪
        for state in Q:
            best_action = argmax(Q[state])
            for a in range(n_actions):
                if a == best_action:
                    policy[state][a] = 1 - epsilon + epsilon/n_actions
                else:
                    policy[state][a] = epsilon/n_actions
    return Q, policy
关键点: 蒙特卡洛控制不需要环境模型,只需要能采样完整的 episode。这在很多实际场景中是个巨大优势。

4.7 本章知识体系

下面这张图总结了蒙特卡洛方法的核心脉络:

蒙特卡洛方法知识体系 蒙特卡洛方法 MC 预测 MC 控制 首次访问 MC 每次访问 MC 回报均值估计 策略评估 策略改进 ε-贪婪策略 核心优势:不需要环境模型,直接从经验中学习 适用场景:有完整 episode 可采样的任务

蒙特卡洛方法虽然简单,但非常实用。它不需要环境模型,只需要能采样完整轨迹。我个人觉得,对于初学者来说,这是理解强化学习「从经验中学习」这一核心理念的最佳切入点。

嗯,记住一点:蒙特卡洛方法只适用于有终止状态的 episodic 任务。如果你的任务没有明确的终止点,那就要考虑其他方法了——不过那是后面章节的内容了。

本章小结:
  • 蒙特卡洛预测:用完整 episode 的回报均值估计价值
  • 首次访问 vs 每次访问:更新时机不同,各有优劣
  • 蒙特卡洛控制:策略评估 + 策略改进的迭代
  • ε-贪婪策略:在利用和探索之间找平衡
可可精品资料库