NumPy 应用:使用像素玩 Pong 的深度强化学习
文章目录
注意
由于底层的 gym 和 atari-py 依赖存在许可和安装问题,本文尚未经过测试。请通过减少依赖的方式来改进本文!
本教程演示了如何使用 NumPy 从头开始实现一个深度强化学习(RL)代理程序,该程序使用策略梯度方法来学习使用屏幕像素作为输入来玩 Pong 视频游戏。你的 Pong 代理将使用人工神经网络作为其策略来获得实时经验。
Pong 是一个 1972 年的 2D 游戏,两个玩家使用“球拍”进行乒乓球的形式游戏。每个玩家在屏幕上上下移动球拍,并试图通过触碰球来将球打向对手的方向。目标是将球打过对手的球拍(对手未接到球)。根据规则,如果一个玩家达到 21 分,他们就赢了。在 Pong 中,学会与对手对战的 RL 代理显示在右侧。

这个例子基于 Andrej Karpathy 在 2017 年加州大学伯克利分校的 深度 RL 训练营 上开发的 代码。他在 2016 年的 博文 中还提供了有关 Pong RL 中使用的机制和理论的更多背景知识。
先决条件
- OpenAI Gym:为了帮助处理游戏环境,你将使用 Gym —— 一个由 OpenAI 开发的开源 Python 接口,它可以在支持许多模拟环境的同时执行 RL 任务。
- Python 和 NumPy:读者应该对 Python、NumPy 数组操作和线性代数有一定的了解。
- 深度学习和深度 RL:你应该熟悉 深度学习 的主要概念,这些概念在 2015 年由 Yann LeCun、Yoshua Bengio 和 Geoffrey Hinton 发表的 深度学习 论文中有详细解释,他们被认为是该领域的先驱之一。本教程将尝试指导你了解深度 RL 的主要概念,并为你提供各种文献,其中包含了原始来源的链接,以方便你阅读。
- Jupyter 笔记本环境:由于 RL 实验可能需要较高的计算能力,你可以使用 Binder 或 Google Colaboratory 在云端免费运行本教程(后者提供免费的有限 GPU 和 TPU 加速)。
- Matplotlib:用于绘制图像。请查看 安装 指南以在你的环境中进行设置。
本教程也可以在本地的隔离环境中运行,例如 Virtualenv 和 conda。
目录
- 关于 RL 和深度 RL 的说明
- 深度 RL 术语表
- 设置 Pong
- 预处理帧(观察)
- 创建策略(神经网络)和前向传递
- 设置更新步骤(反向传播)
- 定义折扣奖励(预期回报)函数
- 对代理进行 3 个回合的训练
- 下一步
- 附录
- 关于 RL 和深度 RL 的注释
- 如何在 Jupyter 笔记本中设置视频播放
关于 RL 和深度 RL 的说明
在 RL 中,你的代理通过与环境交互,使用所谓的策略进行试错学习。在采取一种行动之后,代理会收到关于其奖励(可能得到或可能不得到)和环境的下一个观察的信息。然后它可以继续采取另一种行动。这在一定数量的回合中发生,直到任务被认为完成为止。
代理的策略通过将代理的观察映射到其行动上来工作 —— 也就是说,将代理观察到的内容与所需的行动进行关联。总体目标通常是优化代理的策略,使其最大化每个观察的预期奖励。
有关 RL 的详细信息,请参阅 Richard Sutton 和 Andrew Barton 的 入门书籍。
有关更多信息,请参阅本教程末尾的附录。
深度 RL 术语表
以下是深度 RL 术语的简明词汇表,你可能会在本教程的其余部分中发现它们有用:
- 在有限时间段内的世界中,例如 Pong 游戏,学习代理可以在一个回合内探索(和利用)环境。代理通常需要多个回合来学习。
- 代理使用行动与环境进行交互。
- 在采取行动之后,代理通过奖励(如果有的话)接收一些反馈,这取决于它采取的行动和它所处的状态。状态包含有关环境的信息。
- 代理的观察是状态的部分观察 —— 这是本教程更喜欢的术语(而不是状态)。
- 代理可以根据累积奖励(也称为价值函数)和策略选择行动。累积奖励函数估计代理使用其策略访问观察的质量。
- 策略(由神经网络定义)输出行动选择(作为(对数)概率),应该最大化代理所处状态的累积奖励。
- 对于观察的预期回报,条件是采取的行动,称为行动值函数。为了更重视短期奖励而不是长期奖励,通常使用一个折扣因子(通常是 0.9 到 0.99 之间的浮点数)。
- 代理通过每个策略“运行”期间的行动和状态(观察)序列,有时被称为轨迹 —— 这样的序列产生奖励。
你将通过使用策略梯度的“on-policy”方法来训练你的 Pong 代理 —— 这是一种属于基于策略方法家族的算法。策略梯度方法通常使用梯度下降来更新策略的参数,梯度下降在机器学习中被广泛使用。而且,由于目标是最大化函数(奖励),而不是最小化函数,该过程也被称为梯度上升。换句话说,你使用策略让代理采取行动,目标是最大化奖励,你通过计算梯度并使用它们来更新策略(神经)网络中的参数。
设置 Pong
1. 首先,你应该安装 OpenAI Gym(使用 pip install gym[atari] - 此软件包目前在 conda 上不可用),并导入 NumPy、Gym 和必要的模块:
import numpy as np
import gym
Gym 可以使用 Monitor 包装器来监视和保存输出:
from gym import wrappers
from gym.wrappers import Monitor
2. 为 Pong 游戏实例化一个 Gym 环境:
env = gym.make("Pong-v0")
3. 让我们来看看 Pong-v0 环境中有哪些可用的行动:
print(env.action_space)
print(env.get_action_meanings())
有 6 个行动。然而,LEFTFIRE 实际上是 LEFT,RIGHTFIRE 是 RIGHT,NOOP 是 FIRE。
为了简化起见,你的策略网络将只有一个输出 —— “向上移动” 的(对应于 2 或 RIGHT)(对数)概率。另一个可用的行动将在 3(“向下移动” 或 LEFT)。
4. Gym 可以将代理的学习保存为 MP4 格式的视频 —— 通过运行以下代码将 Monitor() 包装器包装在环境周围:
env = Monitor(env, "./video", force=True)
虽然你可以在 Jupyter 笔记本中执行各种 RL 实验,但在训练后将 Gym 环境的图像或视频渲染出来以可视化代理玩 Pong 游戏的方式可能会相当具有挑战性。如果你想在笔记本中设置视频播放,你可以在本教程末尾的附录中找到详细信息。
预处理帧(观察)
在本节中,你将设置一个函数来预处理输入数据(游戏观察),以使其适合神经网络处理,神经网络只能处理浮点类型的张量(多维数组)形式的输入。
你的代理将使用 Pong 游戏的帧 —— 屏幕帧中的像素 —— 作为策略网络的输入观察。游戏观察告诉代理球在哪里,然后将其(通过前向传递)馈入神经网络(策略)。这类似于 DeepMind 的 DQN 方法(在附录中进一步讨论)。
Pong 屏幕帧是 210x160 像素,有 3 个颜色维度(红色、绿色和蓝色)。这些数组使用 uint8(或 8 位整数)进行编码,并且这些观察结果存储在一个 Gym Box 实例中。
1. 检查 Pong 的观察结果:
print(env.observation_space)
在 Gym 中,代理的行动和观察可以是 Box(n 维)或 Discrete(固定范围整数)类的一部分。
2. 你可以查看一个随机的观察结果 —— 一个帧 —— 的方法是:
1) 在初始化之前设置随机的 `seed`(可选)。
2) 调用 Gym 的 `reset()` 来重置环境,它会返回一个初始观察结果。
3) 使用 Matplotlib 来显示 `render` 后的观察结果。
(您可以参考 OpenAI Gym 核心 API 了解有关 Gym 核心类和方法的更多信息。)
import matplotlib.pyplot as plt
env.seed(42)
env.reset()
random_frame = env.render(mode="rgb_array")
print(random_frame.shape)
plt.imshow(random_frame)
为了将观测输入到策略(神经)网络中,您需要将它们转换为 1D 灰度向量,其中包含 6,400(80x80x1)个浮点数组。(在训练过程中,您将使用 NumPy 的 np.ravel() 函数将这些数组展平。)
3. 设置一个帮助函数来进行帧(观测)预处理:
def frame_preprocessing(observation_frame):
# 裁剪帧。
observation_frame = observation_frame[35:195]
# 对帧进行二次采样。
observation_frame = observation_frame[::2, ::2, 0]
# 移除背景并应用其他增强。
observation_frame[observation_frame == 144] = 0 # 擦除背景(类型 1)。
observation_frame[observation_frame == 109] = 0 # 擦除背景(类型 2)。
observation_frame[observation_frame != 0] = 1 # 将物体(球拍、球)设置为 1。
# 将预处理后的帧作为 1D 浮点数组返回。
return observation_frame.astype(float)
4. 对之前的随机帧进行预处理以测试该函数 —— 策略网络的输入是一张 80x80 的 1D 图像:
preprocessed_random_frame = frame_preprocessing(random_frame)
plt.imshow(preprocessed_random_frame, cmap="gray")
print(preprocessed_random_frame.shape)
创建策略(神经网络)和前向传播
接下来,您将定义策略作为一个简单的前馈网络,它以游戏观测作为输入并输出一个动作的对数概率:
- 对于 输入,它将使用 Pong 视频游戏帧 —— 经过预处理的 1D 向量,包含 6,400(80x80)个浮点数组。
- 隐藏层 将使用 NumPy 的点积函数
np.dot()计算输入的加权和,然后应用 非线性激活函数,例如 ReLU。 - 然后,输出层 将再次执行权重参数和隐藏层输出的矩阵乘法(使用
np.dot()),并通过 softmax 激活函数 将该信息发送出去。 - 最后,策略网络将为代理输出一个动作的对数概率 —— Pong 环境中索引为 2 的动作(“向上移动球拍”的概率)。
1. 让我们为输入、隐藏和输出层实例化一些参数,并开始设置网络模型。
首先,为实验创建一个随机数生成器实例(用于可重现性的种子):
rng = np.random.default_rng(seed=12288743)
然后:
- 设置输入(观测)的维度 —— 经过预处理的屏幕帧:
D = 80 * 80
- 设置隐藏层神经元的数量。
H = 200
- 将策略(神经)网络模型实例化为空字典。
model = {}
在神经网络中,权重 是通过前向和后向传播数据来微调的重要可调参数。
2. 使用一种称为 Xavier 初始化 的技术,使用 NumPy 的 Generator.standard_normal() 设置网络模型的初始权重,该函数返回标准正态分布的随机数,以及 np.sqrt():
model["W1"] = rng.standard_normal(size=(H, D)) / np.sqrt(D)
model["W2"] = rng.standard_normal(size=H) / np.sqrt(H)
3. 策略网络通过随机初始化权重并将输入数据(帧)从输入层通过隐藏层向前传递到输出层。这个过程称为 前向传播 或 前向传递,并在函数 policy_forward() 中进行了概述:
def policy_forward(x, model):
# 使用点积函数将权重与输入在唯一的隐藏层中进行矩阵乘法。
h = np.dot(model["W1"], x)
# 使用 ReLU 进行非线性激活。
h[h < 0] = 0
# 在外层计算“点”积。
# sigmoid 函数的输入称为 logit。
logit = np.dot(model["W2"], h)
# 应用 sigmoid 函数(非线性激活)。
p = sigmoid(logit)
# 返回动作 2(“向上移动”)的对数概率
# 和反向传播所需的隐藏“状态”。
return p, h
请注意,有两个用于确定输入和输出之间非线性关系的 激活函数。这些 非线性函数 应用于层的输出:
- 修正线性单元(ReLU):在上面的
h[h<0] = 0中定义。它对于负输入返回 0,并对正输入返回相同的值。 - Sigmoid:在下面的
sigmoid()中定义。它“包装”了最后一层的输出,并返回(0,1)范围内的动作对数概率。
4. 单独使用 NumPy 的 np.exp() 定义 sigmoid 函数以计算指数:
def sigmoid(x):
return 1.0 / (1.0 + np.exp(-x))
设置更新步骤(反向传播)
在深度强化学习算法中的学习过程中,您使用动作对数概率(给定观测)和折扣回报(例如 Pong 中的 +1 或 -1)执行 反向传播 或 反向传递,以更新参数 —— 策略网络的权重。
1. 让我们定义反向传递函数(policy_backward()),借助 NumPy 的数组乘法模块 —— np.dot()(矩阵乘法)、np.outer()(外积计算)和 np.ravel()(将数组展平为 1D 数组):
def policy_backward(eph, epdlogp, model):
dW2 = np.dot(eph.T, epdlogp).ravel()
dh = np.outer(epdlogp, model["W2"])
dh[eph <= 0] = 0
dW1 = np.dot(dh.T, epx)
# 返回策略网络的新“优化”权重。
return {"W1": dW1, "W2": dW2}
使用网络的中间隐藏“状态”(eph)和动作对数概率的梯度(epdlogp)来自一个 episode,policy_backward 函数通过策略网络将梯度向后传播,并更新权重。
2. 在代理训练期间应用反向传播时,您需要为每个 episode 保存多个变量。让我们实例化空列表来存储它们:
# 该 episode 的所有预处理观测。
xs = []
# 该 episode 的所有隐藏“状态”(来自网络)。
hs = []
# 该 episode 的所有概率动作的梯度
# (相对于观测)。
dlogps = []
# 该 episode 的所有回报。
drs = []
在训练结束后,您将手动重置这些变量,并使用 NumPy 的 np.vstack() 进行重塑。这在教程末尾的训练阶段中进行了演示。
3. 接下来,在优化代理的策略时,通常使用深度学习 优化器(您正在使用梯度进行优化)。在本例中,您将使用 RMSProp —— 一种自适应优化 方法。让我们为优化器设置一个折扣因子 —— 一个衰减率:
decay_rate = 0.99
4. 在训练过程中,您还需要存储梯度(借助 NumPy 的 np.zeros_like())以进行优化步骤:
- 首先,保存在一个批次中累积梯度的更新缓冲区:
grad_buffer = {k: np.zeros_like(v) for k, v in model.items()}
- 其次,为优化器存储 RMSProp 内存以进行梯度上升:
rmsprop_cache = {k: np.zeros_like(v) for k, v in model.items()}
定义折扣回报(预期回报)函数
在本节中,您将设置一个计算折扣回报(discount_rewards())的函数 —— 从观测中得到的预期回报 —— 它使用一个包含奖励的 1D 数组作为输入(借助 NumPy 的 np.zeros_like() 函数)。
为了对短期奖励给予更多权重,而不是长期奖励,您将使用一个 折扣因子(gamma),通常是 0.9 到 0.99 之间的浮点数。
gamma = 0.99
def discount_rewards(r, gamma):
discounted_r = np.zeros_like(r)
running_add = 0
# 从最后一个回报到第一个...
for t in reversed(range(0, r.size)):
# ...重置回报总和
if r[t] != 0:
running_add = 0
# ...计算折扣回报
running_add = running_add * gamma + r[t]
discounted_r[t] = running_add
return discounted_r
对代理进行一定数量的 episode 进行训练
Pong 游戏的策略梯度方法的伪代码如下:
- 实例化策略 - 你的神经网络 - 并随机初始化策略网络中的权重。
- 初始化一个随机观察。
- 随机初始化策略网络中的权重。
- 重复以下步骤一定次数的回合:
- 将观察输入到策略网络中,并输出代理的动作概率(前向传播)。
- 代理根据每个观察采取动作,观察获得的奖励,并收集状态-动作经验的轨迹(在预定义的回合数或批次大小上)。
- 计算交叉熵(带正号,因为你需要最大化奖励而不是最小化损失)。
- 对于每个批次的回合:
- 使用交叉熵计算动作对数概率的梯度。
- 计算累积回报,并使用折扣因子 discount 来给短期回报和长期回报赋予不同的权重。
- 将动作对数概率的梯度乘以折扣回报(“优势”)。
- 执行梯度上升(反向传播)来优化策略网络的参数(权重)。
- 最大化导致高回报的动作的概率。

你可以随时停止训练,或者在 /video 目录中检查保存的游戏录像。你可以根据你的设置选择适当的最大回合数。
1. 为了演示目的,让我们将训练的最大回合数限制为 3。如果你使用硬件加速(CPU 和 GPU),可以将回合数增加到 1,000 或更多。作为对比,Andrej Karpathy 的原始实验大约进行了 8,000 个回合。
max_episodes = 3
2. 设置批次大小和学习率的值:
- 批次大小 决定模型何时(在回合中)执行一次参数更新。它是代理可以收集状态-动作轨迹的次数。在收集结束时,可以执行动作概率乘法的最大化。
- 学习率 有助于限制权重更新的幅度,以防止过度修正。
batch_size = 3
learning_rate = 1e-4
3. 设置 Gym 的 render 方法的游戏渲染默认变量(用于显示观察结果,可选,但在调试过程中可能有用):
render = False
4. 通过调用 reset() 方法设置代理的初始(随机)观察:
observation = env.reset()
5. 初始化先前的观察:
prev_x = None
6. 初始化奖励变量和回合计数:
running_reward = None
reward_sum = 0
episode_number = 0
7. 为了模拟帧之间的运动,将策略网络的单个输入帧(x)设置为当前和先前预处理帧之间的差异:
def update_input(prev_x, cur_x, D):
if prev_x is not None:
x = cur_x - prev_x
else:
x = np.zeros(D)
return x
8. 最后,开始训练循环,使用预定义的函数:
:tags: [output_scroll]
while episode_number < max_episodes:
# (用于渲染)
if render:
env.render()
# 1. 预处理观察(游戏帧)并使用 NumPy 的 `ravel()` 进行扁平化。
cur_x = frame_preprocessing(observation).ravel()
# 2. 为策略网络实例化观察
x = update_input(prev_x, cur_x, D)
prev_x = cur_x
# 3. 使用观察(预处理帧作为输入)通过策略网络进行前向传递,并在每个回合过程中存储动作对数概率和隐藏的“状态”(用于反向传播)。
aprob, h = policy_forward(x, model)
# 4. 如果动作索引为 `2`("向上移动"),则将其概率作为动作
# 如果它高于随机采样值,则使用动作 `3`("向下移动")。
action = 2 if rng.uniform() < aprob else 3
# 5. 在单独的变量中缓存观察和隐藏的“状态”(来自网络)以进行反向传播。
xs.append(x)
hs.append(h)
# 6. 计算动作对数概率的梯度:
# - 如果动作是“向上移动”(索引为 `2`):
y = 1 if action == 2 else 0
# - 交叉熵:
# `y*log(aprob) + (1 - y)*log(1-aprob)`
# 或者 `log(aprob)` 如果 y = 1,否则:`log(1 - aprob)`。
# (回想一下:你使用了 sigmoid 函数(`1/(1+np.exp(-x))`)输出了 `aprob` 动作概率。)
# - 然后梯度:`y - aprob`。
# 7. 追加动作对数概率的梯度。
dlogps.append(y - aprob)
# 8. 执行动作并使用 Gym 的 `step()` 函数更新参数;获得新的观察。
observation, reward, done, info = env.step(action)
# 9. 更新总奖励的总和。
reward_sum += reward
# 10. 追加先前动作的奖励。
drs.append(reward)
# 完成一个回合后:
if done:
episode_number += 1
# 11. 使用 `np.vstack()` 收集和重塑存储的值:
# - 观察帧(输入),
epx = np.vstack(xs)
# - 隐藏的“状态”(来自网络),
eph = np.vstack(hs)
# - 动作对数概率的梯度,
epdlogp = np.vstack(dlogps)
# - 过去回合的获得奖励。
epr = np.vstack(drs)
# 12. 为新回合重置存储的变量:
xs = []
hs = []
dlogps = []
drs = []
# 13. 使用之前定义的辅助函数折扣过去回合的奖励...
discounted_epr = discount_rewards(epr, gamma)
# ...并对其进行归一化,因为它们具有较高的方差
# (下面会解释)。
discounted_epr -= np.mean(discounted_epr)
discounted_epr /= np.std(discounted_epr)
# 14. 将折扣奖励乘以动作对数概率的梯度(“优势”)。
epdlogp *= discounted_epr
# 15. 使用梯度执行反向传播和梯度上升。
grad = policy_backward(eph, epdlogp, model)
# 16. 将策略梯度保存在缓冲区中。
for k in model:
grad_buffer[k] += grad[k]
# 17. 使用 RMSProp 优化器在每个批次大小(默认为每 10 个回合)上执行策略网络参数(权重)更新。
if episode_number % batch_size == 0:
for k, v in model.items():
# 梯度。
g = grad_buffer[k]
# 使用 RMSProp 折扣因子。
rmsprop_cache[k] = (
decay_rate * rmsprop_cache[k] + (1 - decay_rate) * g ** 2
)
# 使用学习率和 RMSProp 优化器更新策略网络(因此没有负号)
model[k] += learning_rate * g / (np.sqrt(rmsprop_cache[k]) + 1e-5)
# 在最后重置梯度缓冲区。
grad_buffer[k] = np.zeros_like(v)
# 18. 测量总折扣奖励。
running_reward = (
reward_sum
if running_reward is None
else running_reward * 0.99 + reward_sum * 0.01
)
print(
"重置 Pong 环境。回合总奖励:{} 平均奖励:{}".format(
reward_sum, running_reward
)
)
# 19. 通过调用 Gym 的 `reset()` 函数设置代理的初始观察
# 为下一个回合,并将奖励总和重置为 0。
reward_sum = 0
observation = env.reset()
prev_x = None
# 20. 在训练过程中显示输出。
if reward != 0:
print(
"回合 {}: 游戏结束。奖励:{}...".format(episode_number, reward)
+ ("" if reward == -1 else " 正奖励!")
)
一些建议:
- 如果之前运行过实验并希望重复运行,你的
Monitor实例可能仍在运行,这可能会导致下次尝试训练代理时出错。因此,你应该先通过取消注释并运行下面的单元格来关闭Monitor:
# env.close()
- 在 Pong 游戏中,如果玩家没有将球击回,他们会收到负奖励(-1),而另一个玩家会得到 +1 的奖励。代理通过玩 Pong 获得的奖励具有显着的方差。因此,最佳做法是使用相同的均值(使用
np.mean())和标准差(使用 NumPy 的np.std())对其进行归一化。 - 当仅使用 NumPy 时,深度强化学习训练过程(包括反向传播)涉及多行代码,可能看起来相当长。其中一个主要原因是你没有使用具有自动微分库的深度学习框架,这通常简化了此类实验。本教程展示了如何从头开始执行所有操作,但你也可以使用许多基于 Python 的框架,其中包含“autodiff”和“autograd”,你将在教程末尾了解到。
下一步操作
策略梯度方法可以在给予足够时间的情况下学习任务,而强化学习中的优化是一个具有挑战性的问题。训练智能体学会玩乒乓球或其他任务可能会导致样本效率低下,并且需要大量的回合。您可能还会注意到,在训练输出中,即使经过数百个回合,奖励也可能具有很高的方差。
此外,与许多基于深度学习的算法一样,您应该考虑到您的策略需要学习的大量参数。在乒乓球中,这个数量加起来超过了100万,隐藏层中有200个节点,输入维度的大小为6,400(80x80)。因此,增加更多的CPU和GPU来辅助训练总是一个选择。
您可以使用更先进的基于策略梯度的算法,可以帮助加快训练速度,提高对参数的敏感性,并解决其他问题。例如,有一种名为Proximal Policy Optimization (PPO)的“自我对弈”方法,由John Schulman等人于2017年开发,用于训练OpenAI Five智能体在10个月内达到竞争水平的Dota 2游戏。当然,如果将这些方法应用于较小的Gym环境,训练时间应该只需要几个小时,而不是几个月。
总的来说,强化学习存在许多挑战和可能的解决方案,您可以在《强化学习,快与慢》一文中探索其中一些解决方案,该文由Matthew Botvinick、Sam Ritter、Jane X. Wang、Zeb Kurth-Nelson、Charles Blundell和Demis Hassabis(2019)撰写。
如果您想了解更多关于深度强化学习的知识,可以查看以下免费教育资料:
- Spinning Up in Deep RL:由OpenAI开发。
- 由DeepMind和UC Berkeley的从业者讲授的深度强化学习讲座。
- 由David Silver(DeepMind,UCL)讲授的强化学习讲座。
从头开始使用NumPy构建神经网络是学习更多关于NumPy和深度学习的好方法。然而,对于实际应用,您应该使用专门的框架,如PyTorch、JAX、TensorFlow或MXNet,它们提供类似NumPy的API,具有内置的自动微分和GPU支持,并且专为高性能数值计算和机器学习而设计。
附录
关于强化学习和深度强化学习的注释
- 在用于图像识别、语言翻译或文本分类等任务的监督学习深度学习中,您更有可能使用大量标记数据。然而,在强化学习中,智能体通常不会接收到直接的明确反馈,指示正确或错误的动作 - 它们依赖于其他信号,如奖励。
- 深度强化学习将强化学习与深度学习相结合。该领域在2013年取得了首次重大成功,更复杂的环境中,如视频游戏,这是在计算机视觉领域的AlexNet突破之后的一年。DeepMind的Volodymyr Mnih等人发表了一篇名为《Playing Atari with deep reinforcement learning》(2013年)(更新于2015年),展示了他们能够训练一个能够以人类水平玩几个经典游戏的智能体。他们的强化学习算法 - 称为深度Q网络(DQN) - 在近似Q学习的神经网络中使用了卷积层,并使用了经验回放。
- 与您在此示例中使用的简单策略梯度方法不同,DQN使用一种“离策略”基于值方法(近似Q学习),而原始的AlphaGo使用策略梯度和蒙特卡洛树搜索。
- 2000年,Richard Sutton等人撰写了关于带有函数近似(如神经网络)的策略梯度的论文。他们受到了许多先前工作的影响,包括统计梯度跟随算法,如REINFORCE(Ronald Williams,1992),以及反向传播(Geoffrey Hinton,1986),它有助于深度学习算法的学习。在20世纪90年代,Gerald Tesauro(Temporal difference learning and td-gammon,1995)在IBM工作,与IBM合作开发了一个学会玩backgammon的智能体,并引入了使用神经网络函数近似的强化学习方法,以及Long-Ji Lin(Reinforcement learning for robots using neural networks,1993)的研究。
- 自2013年以来,研究人员已经提出了许多值得注意的方法,用于使用深度强化学习学习解决复杂任务,例如AlphaGo(David Silver等人,2016年),AlphaZero通过自我对弈掌握围棋、国际象棋和将棋(David Silver等人,2017-2018年),OpenAI Five通过自我对弈(OpenAI,2019年)训练Dota 2智能体,以及AlphaStar通过使用演员-评论家算法、经验回放、自我模仿学习和策略蒸馏(Oriol Vinyals等人,2019年)训练StarCraft 2智能体。此外,还有其他实验,例如由Electronic Arts/DICE的工程师开发的Battlefield 1的深度强化学习。
- 视频游戏在深度强化学习研究中受欢迎的原因之一是,与真实世界的实验(例如使用遥控直升机进行的强化学习)相比,虚拟模拟可以提供更安全的测试环境。
- 如果您对深度强化学习对其他领域(如神经科学)的影响感兴趣,可以参考Matthew Botvinick等人(2020年)的一篇论文。
如何在Jupyter笔记本中设置视频播放
- 如果您使用的是Binder - 一个免费的基于Jupyter笔记本的工具 - 您可以设置Docker镜像,并将
freeglut3-dev、xvfb和x11-utils添加到apt.txt配置文件中以安装初始依赖项。然后,在binder/environment.yml中的channels下添加gym、pyvirtualdisplay和其他可能需要的内容,例如python=3.7、pip和jupyterlab。有关更多信息,请查看以下文章。 - 如果您使用的是Google Colaboratory - 另一个免费的基于Jupyter笔记本的工具 - 您可以通过安装和设置X虚拟帧缓冲/Xvfb、X11、FFmpeg、PyVirtualDisplay、PyOpenGL和其他依赖项来启用游戏环境的视频播放,如下所示。
-
如果您使用的是Google Colaboratory,请在笔记本单元格中运行以下命令以帮助进行视频播放:
# 安装Xvfb和X11依赖项。 !apt-get install -y xvfb x11-utils > /dev/null 2>&1 # 为了使用视频,安装FFmpeg。 !apt-get install -y ffmpeg > /dev/null 2>&1 # 安装PyVirtualDisplay以进行可视反馈和其他库/依赖项。 !pip install pyvirtualdisplay PyOpenGL PyOpenGL-accelerate > /dev/null 2>&1 -
然后,添加以下Python代码:
# 导入虚拟显示模块。 from pyvirtualdisplay import Display # 从IPython中导入ipythondisplay和HTML以进行图像和视频渲染。 from IPython import display as ipythondisplay from IPython.display import HTML # 在400x300(可调整大小)上初始化虚拟缓冲区。 # 使用Xvfb时,应将`visible=False`。 display = Display(visible=False, size=(400, 300)) display.start() # 检查是否没有显示。 # 如果没有显示,预期输出是`:0`。 !echo $DISPLAY # 定义一个辅助函数以在Jupyter笔记本中显示视频。 #(来源:https://star-ai.github.io/Rendering-OpenAi-Gym-in-Colaboratory/) import sys import math import glob import io import base64 def show_any_video(mp4video=0): mp4list = glob.glob('video/*.mp4') if len(mp4list) > 0: mp4 = mp4list[mp4video] video = io.open(mp4, 'r+b').read() encoded = base64.b64encode(video) ipythondisplay.display(HTML(data='''<video alt="test" autoplay loop controls style="height: 400px;"> <source src="data:video/mp4;base64,{0}" type="video/mp4" /> </video>'''.format(encoded.decode('ascii')))) else: print('Could not find the video!')
-
如果你想在 Jupyter 笔记本中查看最后一次(非常快速)的游戏过程,并且之前已经实现了
show_any_video()函数,可以在一个单元格中运行以下代码:show_any_video(-1) -
如果你正在 Linux 或 macOS 的本地环境中按照本教程的说明操作,你可以将大部分代码添加到一个名为 Python(
.py) 的文件中。然后,你可以在终端中通过python your-code.py运行你的 Gym 实验。要启用渲染,你可以按照官方 OpenAI Gym 文档中的说明使用命令行界面(确保你已经安装了 Gym 和 Xvfb,如指南中所述)。
更多推荐
所有评论(0)