4. 蒙特卡洛方法:从随机采样到智能决策

蒙特卡洛方法,名字听着挺唬人,其实说白了就是——用大量随机采样来估计真实值。我在做机器人轨迹规划时,经常遇到环境模型不明确的情况,这时候蒙特卡洛就成了我的救命稻草。

你想想看,强化学习的核心问题是什么?是评估一个策略好不好,以及怎么找到更好的策略。蒙特卡洛方法恰好能解决这两个问题,而且思路非常直观。

核心思想:通过完整episode的采样,用平均回报来估计状态值或动作值。不需要环境模型,完全靠经验。

4.1 蒙特卡洛预测:如何评估一个策略?

预测问题,就是给定一个策略π,求它的状态值函数Vπ(s)或动作值函数Qπ(s, a)。

蒙特卡洛的做法很简单:跑很多次完整的episode,记录每个状态后的累计回报,然后取平均。

举个例子,假设你在迷宫里走,从起点到终点算一次episode。你记录了每次经过某个路口后,最终获得的奖励总和。跑100次后,把这些总和平均一下,就是这个路口的价值估计。

我习惯用首次访问型MC和每次访问型MC来区分实现方式:

  • 首次访问型:每个episode中,只记录状态第一次出现时的回报
  • 每次访问型:每个episode中,记录状态每次出现时的回报

理论上两者都会收敛到真实值,但首次访问型在数学上更容易分析。我在项目中一般用首次访问型,因为它的方差更小。

# 首次访问型MC预测伪代码
def mc_prediction(env, policy, num_episodes, gamma=0.9):
    returns = {}  # 存储每个状态的回报列表
    V = {}        # 状态值函数
    
    for _ in range(num_episodes):
        episode = generate_episode(env, policy)
        G = 0
        visited_states = set()
        
        # 从后往前遍历
        for t in range(len(episode)-1, -1, -1):
            state, reward = episode[t]
            G = gamma * G + reward
            
            # 首次访问检查
            if state not in visited_states:
                visited_states.add(state)
                returns[state].append(G)
                V[state] = np.mean(returns[state])
    
    return V

我的经验:实际应用中,episode数量至少需要1000以上才能得到稳定估计。如果状态空间很大,可以考虑用函数近似来替代查表。

4.2 蒙特卡洛控制:从评估到优化

光会评估还不够,我们得找到最优策略。蒙特卡洛控制的核心是广义策略迭代:

  1. 策略评估:用MC方法估计当前策略的Q值
  2. 策略改进:基于Q值贪心地选择动作

这里有个关键问题:探索与利用的平衡。如果一直贪心,你可能永远发现不了更好的路径。

我早期做迷宫导航时就踩过这个坑。机器人只走已知的短路径,结果错过了另一条更优的路线。后来我用了ε-贪心策略,才解决了这个问题。

# ε-贪心策略
def epsilon_greedy(Q, state, epsilon):
    if np.random.random() < epsilon:
        return np.random.choice(env.action_space.n)  # 探索
    else:
        return np.argmax(Q[state])  # 利用

我曾经犯过的错:ε值设得太大会导致策略永远无法收敛,设得太小又探索不够。我建议从0.5开始,随着训练逐渐衰减到0.01左右。

4.3 探索性初始化:一个简单但有效的技巧

探索性初始化,说白了就是让每个episode从不同的状态-动作对开始。这样能保证所有状态都被访问到,避免某些区域永远被忽略。

为什么需要这个?你想想看,如果机器人每次都在同一个起点出发,它可能永远不知道地图另一侧有什么。探索性初始化强制它从不同位置开始,相当于给了它一张全局地图。

具体做法:

  • 每个episode开始时,随机选择一个状态作为起点
  • 随机选择一个动作作为第一步
  • 然后按照当前策略继续执行

但要注意,探索性初始化只适用于可重置的环境。在真实机器人上,你不能每次都把它搬到不同位置重新开始。这时候就需要用在线探索的方法了。

4.4 在迷宫导航中的应用

好了,我们把上面这些串起来,看看怎么用蒙特卡洛方法解决迷宫导航问题。

迷宫导航的设定:

  • 状态:机器人在迷宫中的坐标 (x, y)
  • 动作:上、下、左、右
  • 奖励:到达终点+10,撞墙-1,每步-0.1
  • 目标:找到从起点到终点的最短路径

我实际做过一个5x5的迷宫项目,用蒙特卡洛控制训练了5000个episode。下面是核心流程:

def mc_control_maze(env, num_episodes, gamma=0.9, epsilon=0.1):
    Q = defaultdict(lambda: np.zeros(env.action_space.n))
    policy = defaultdict(lambda: np.random.choice(env.action_space.n))
    
    for episode in range(num_episodes):
        # 探索性初始化
        state = env.reset_random()
        episode_data = []
        
        # 生成一个episode
        done = False
        while not done:
            action = epsilon_greedy(Q[state], epsilon)
            next_state, reward, done = env.step(action)
            episode_data.append((state, action, reward))
            state = next_state
        
        # 反向计算回报并更新Q值
        G = 0
        visited = set()
        for t in range(len(episode_data)-1, -1, -1):
            state, action, reward = episode_data[t]
            G = gamma * G + reward
            
            if (state, action) not in visited:
                visited.add((state, action))
                Q[state][action] = update_mc(Q[state][action], G, t)
        
        # 策略改进
        for state in Q:
            policy[state] = np.argmax(Q[state])
    
    return policy, Q

效果对比:经过训练后,机器人从起点到终点的平均步数从最初的50步降到了12步,接近最优解。而且策略对噪声有很好的鲁棒性。

4.5 蒙特卡洛方法的优缺点

优点 缺点
不需要环境模型 只能用于episodic任务
实现简单直观 方差较大,需要大量采样
对状态空间无偏估计 无法处理连续状态空间
容易并行化 探索效率较低

我个人觉得,蒙特卡洛方法最适合小规模、离散状态空间的问题。如果你的任务有明确的终止条件(比如迷宫有终点),而且状态数不超过几千个,那蒙特卡洛是个好选择。

但如果状态空间很大,或者任务没有明确的episode边界,那就得考虑时序差分学习了。嗯,那是下一章的内容了。

避坑指南:我曾经在连续控制任务上硬套蒙特卡洛方法,结果跑了三天三夜都没收敛。后来才意识到,蒙特卡洛只适合离散episode的任务。选对方法比调参更重要。

蒙特卡洛方法知识体系 蒙特卡洛方法 MC预测 MC控制 探索性初始化 首次访问型 每次访问型 策略评估 策略改进 随机起点 随机动作 迷宫导航应用 路径规划 避障决策 策略优化 预测 控制 探索 应用

这张图把蒙特卡洛方法的三个核心模块串起来了。预测是基础,控制是目标,探索性初始化是保障。三者结合,就能在迷宫导航这类任务中发挥作用。

最后说一句,蒙特卡洛方法虽然简单,但它是理解更复杂强化学习算法(比如Q-learning、SARSA)的基石。把这块啃透了,后面的路就好走了。

可可精品资料库