☰
AI for Beginners 实战:用 OpenAI Gym 训练 Mountain Car 逃出山谷(Policy Gradients 与 Actor-Critic 迁移实战)
2026/10/6 2:13:12 网站建设 项目流程
  • 教程
  • 人工智能
  • 机器学习
  • 深度学习

【免费下载链接】AI-For-Beginners

12 Weeks, 24 Lessons, AI for All!

项目地址:https://gitcode.com/GitHub_Trending/ai/AI-For-Beginners
点击查看免费下载

本文基于 AI for Beginners 课程第 22 课(Deep RL)的配套实验(实验任务书)展开,讲解如何把课程中在 CartPole 上训练好的深度强化学习(Deep RL)算法,迁移到另一个经典控制环境 Mountain Car(山地小车)。读完本文,你将掌握 OpenAI Gym 的统一环境接口(reset / step / render / close),学会复用 Policy Gradients 与 Actor-Critic 两类算法在新环境上完成训练,并理解"把任意 Gym 环境作为参数传入 RL 算法"这一课程核心结论。

实验任务:让 RL 智能体驾驭小车冲出山谷

本实验来自 AI for Beginners 课程的第 22 课(Deep Reinforcement Learning)。在完成 CartPole 平衡实验之后,你的下一个目标是:训练一个 RL 智能体,在 OpenAI Gym 的 Mountain Car 环境中控制小车,使它成功逃离被困的山谷并到达旗帜位置。

实验任务书原文见 lessons/6-Other/22-DeepRL/lab/README.md,其要点如下:

  • 任务:训练 RL 智能体控制 OpenAI 环境中的 Mountain Car;
  • 环境构成:一辆被困在谷底的小车,你的目标是跳出山谷并抵达旗帜;
  • 可执行动作:向左加速、向右加速、或什么都不做;
  • 可观测信息:小车在 x 轴上的位置,以及它的速度;
  • 起步方式:打开实验自带的 MountainCar.ipynb 开始编码。

认识 Mountain Car 环境:状态、动作与奖励

Mountain Car 是 OpenAI Gym 中classic_control系列的代表环境之一。与课程主笔记中讲解的 CartPole(CartPole 环境与随机策略演示)相比,两者物理过程完全不同,但它们的编程接口完全一致,这正是本实验要验证的核心思想。

按照任务书的描述,Mountain Car 环境的关键特征如下:

维度说明
任务目标小车通过前后加速积蓄动能,冲上右侧山顶并触达旗帜
观测(Observation)小车沿 x 轴的位置、小车的速度(连续值)
动作(Action)向左加速、向右加速、不做任何动作
终止条件小车到达旗帜位置,实验回合结束
奖励信号每个时间步由env.step返回即时奖励,训练目标是最大化累计奖励

需要注意的是,CartPole 的观测是一个 4 维向量(小车位置、小车速度、杆的倾角、杆的角速度),且每个时间步奖励 +1(详见 CartPole-RL-TF.ipynb 中的观测打印与说明);而 Mountain Car 的观测只有位置与速度两个维度,动作也变成了三个离散选项。观测维度和动作数量不同,意味着策略网络的第一层输入维度和最后一层输出维度需要相应调整,但算法本身无需改动。

从起步 Notebook 开始实验

实验的第一步是打开 MountainCar.ipynb。这个 Notebook 已经把环境搭建和"随机试探"的演示代码写好,剩下的训练部分留给你自己完成(Notebook 中甚至预留了## Lost of code here占位单元)。

1. 创建环境

import gym env = gym.make('MountainCar-v0')

2. 观察一次随机实验

在训练任何算法之前,先跑一个"纯随机策略"的回合,看看环境长什么样、实验何时结束:

state = env.reset() while True: env.render() action = env.action_space.sample() state, reward, done, info = env.step(action) if done: break

最后关闭环境:

env.close()

这里的四个接口是 OpenAI Gym 所有环境共通的统一契约:

  • env.reset():开始一次新的实验(回合),返回初始状态;
  • env.step(action):执行一步仿真,输入来自动作空间的 action,返回(新的观测、即时奖励、终止标志 done、附加信息 info);
  • env.render():渲染当前画面(本地运行时会在新窗口中弹出仿真画面);
  • env.action_space.sample():从动作空间中随机采样一个动作,用于构造随机基线策略;
  • env.close():关闭环境、释放渲染资源。

3. 环境依赖

运行本实验需要gym(仓库根目录 requirements.txt 与 binder/requirements.txt 均固定为gym==0.26.2)以及用于渲染的pygame==2.6.0。可参考 CartPole-RL-TF.ipynb 开头的安装方式:

import sys !{sys.executable} -m pip install gym pygame

把 CartPole 学到的 RL 算法迁移到 Mountain Car

课程主笔记(DeepRL 课程 README)在 CartPole 上演示了两类算法,均以"状态、动作、奖励"三个抽象概念为核心,因此可以直接复用到 Mountain Car:

  • Policy Gradients(策略梯度):用一个神经网络直接建模策略 $\pi$,输入状态,输出各动作的概率分布;
  • Actor-Critic(演员-评论家):网络同时输出动作概率分布(actor)与当前状态下的期望累计奖励估计(critic)。

策略网络的定义

参考 CartPole-RL-TF.ipynb,在 CartPole 上网络输入为 4 维观测、输出 2 个动作概率;迁移到 Mountain Car 时只需把num_inputs改为 2(位置 + 速度)、num_actions改为 3(左 / 不动 / 右):

import numpy as np import tensorflow as tf from tensorflow import keras num_inputs = 2 # Mountain Car 观测:位置、速度 num_actions = 3 # Mountain Car 动作:向左、不动、向右 model = keras.Sequential([ keras.layers.Dense(128, activation="relu", input_shape=(num_inputs,)), keras.layers.Dense(num_actions, activation="softmax") ]) model.compile(loss='categorical_crossentropy', optimizer=keras.optimizers.Adam(learning_rate=0.01))

PyTorch 版本可参考 CartPole-RL-PyTorch.ipynb,用torch.nn.Sequential构造同样结构(Linear(2, 128) -> ReLU -> Linear(128, 3) -> Softmax)。

回合采样函数 run_episode

RL 训练依赖反复运行完整回合来采集"轨迹(trace)"——即每个时间步的状态、动作、动作概率和奖励。复用课程 Notebook 中的run_episode(此处按 Mountain Car 的维度参数化):

def run_episode(max_steps_per_episode=10000, render=False): states, actions, probs, rewards = [], [], [], [] state = env.reset() for _ in range(max_steps_per_episode): if render: env.render() action_probs = model(np.expand_dims(state, 0))[0] action = np.random.choice(num_actions, p=np.squeeze(action_probs)) nstate, reward, done, info = env.step(action) if done: break states.append(state) actions.append(action) probs.append(action_probs) rewards.append(reward) state = nstate return np.vstack(states), np.vstack(actions), np.vstack(probs), np.vstack(rewards)

注意:np.random.choice(num_actions, p=...)表示按照策略网络输出的概率分布随机采样动作——这正是课程中"探索与利用(exploration / exploitation)平衡"的具体实现:网络越确定某个动作好,就越倾向于选它;同时保留随机性以继续探索新状态。

折扣奖励 discounted_rewards

Policy Gradients 的关键难点是:回合结束前我们不知道每个动作的好坏。解决思路是构造累计奖励向量,并用折扣系数 $\gamma=0.99$ 削弱早期奖励的作用,最后做归一化作为训练的加权信号。函数实现直接取自课程 Notebook:

eps = 0.0001 def discounted_rewards(rewards, gamma=0.99, normalize=True): ret = [] s = 0 for r in rewards[::-1]: s = r + gamma * s ret.insert(0, s) if normalize: ret = (ret - np.mean(ret)) / (np.std(ret) + eps) return ret

策略梯度训练主循环

课程 Notebook 的训练流程(跑 300 个回合)如下:先跑回合收集轨迹,用"实际执行的动作(one-hot)与预测概率之差"构造梯度方向,再乘以折扣奖励,最后以alpha=1e-4的学习步长把修正后的目标概率喂回网络:

alpha = 1e-4 history = [] for epoch in range(300): states, actions, probs, rewards = run_episode() one_hot_actions = np.eye(num_actions)[actions.T][0] gradients = one_hot_actions - probs # 动作与预测概率的差 dr = discounted_rewards(rewards) # 折扣累计奖励 gradients *= dr # 高奖励步骤产生更大影响 target = alpha * np.vstack([gradients]) + probs model.train_on_batch(states, target) history.append(np.sum(rewards)) if epoch % 100 == 0: print(f"{epoch} -> {np.sum(rewards)}")

在 CartPole 上,这段训练使每回合累计奖励从约 27 一路提升到数百(Notebook 输出显示第 0 回合 29、第 100 回合 135、第 200 回合 484)。迁移到 Mountain Car 后,训练目标从"平衡更久"变为"更快抵达旗帜",但这段主循环代码几乎不需要改动。

Actor-Critic 网络结构

Actor-Critic 是策略梯度的改进版:一个共享隐藏层的网络,同时输出动作概率分布与状态价值估计(CartPole-RL-TF.ipynb 中的模型定义):

num_inputs = 2 num_actions = 3 num_hidden = 128 inputs = keras.layers.Input(shape=(num_inputs,)) common = keras.layers.Dense(num_hidden, activation="relu")(inputs) action = keras.layers.Dense(num_actions, activation="softmax")(common) # actor:动作概率 critic = keras.layers.Dense(1)(common) # critic:价值估计 model = keras.Model(inputs=inputs, outputs=[action, critic])

训练时,critic 用 Huber 损失逼近真实折扣奖励,actor 用-log_prob * (折扣奖励 - critic 估计值)作为损失,使 actor 倾向于选择"实际回报高于 critic 预期"的动作。PyTorch 版本把 actor 与 critic 拆成两个独立模块,并用torch.distributions.Categorical采样动作、以advantage = returns - values计算优势函数,详见 CartPole-RL-PyTorch.ipynb。

更进一步:把环境作为参数传入 RL 算法

任务书的核心结论(Takeaway)指出:由于 OpenAI Gym 对所有环境提供相同接口,RL 算法本身在很大程度上不依赖环境的物理性质,你甚至可以把 Python 代码重构为"将任意环境作为参数传给 RL 算法"的形式。

重构思路非常简单,把run_episode、discounted_rewards和训练主循环中的env改为函数参数即可:

def run_episode(env, model, num_actions, max_steps_per_episode=10000, render=False): ...

这样一来,只需一行gym.make(...)切换环境名,从CartPole-v1换成MountainCar-v0,再同步调整num_inputs与num_actions两个维度常量,同一套 Policy Gradients / Actor-Critic 代码即可在课程演示的两个环境(甚至更多 Gym 环境)之间无缝复用。这也印证了课程的更大视野:只要提供定义"期望状态"的奖励函数,并给智能体充分探索的机会,RL 就能自行学到最优策略(更多讨论见 DeepRL 课程 README 中关于 Atari、AlphaZero 与工业仿真控制等应用方向的介绍)。

实验总结

完成本实验后,你应该掌握三件事:

  1. Mountain Car 环境的建模方式:2 维连续观测(位置、速度)+ 3 个离散动作,目标是把困在谷底的小车送上山顶旗帜;
  2. 算法的环境无关性:Policy Gradients 与 Actor-Critic 只依赖reset / step / render / close这套 Gym 统一接口,换环境只需调整网络的输入输出维度;
  3. 从零训练 RL 智能体的完整流程:创建环境 → 随机基线 → 定义策略网络 → 回合采样 → 折扣奖励 → 训练主循环 → 渲染验证。

在此基础上,你还可以继续挑战任务书最后的扩展方向:尝试在 Gym 提供的其他经典控制环境中复用同一套算法,进一步体会"统一接口 + 参数化环境"带来的迁移能力。

  • 教程
  • 人工智能
  • 机器学习
  • 深度学习

【免费下载链接】AI-For-Beginners

12 Weeks, 24 Lessons, AI for All!

项目地址:https://gitcode.com/GitHub_Trending/ai/AI-For-Beginners
点击查看免费下载

相关推荐

上一篇:虚拟设备驱动:游戏控制器兼容性的跨平台解决方案
下一篇:jsonpath-ng性能优化:提升大规模JSON数据查询效率的5个技巧

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询