JavaScript 深度学习(四)(4)

简介: JavaScript 深度学习(四)

JavaScript 深度学习(四)(3)https://developer.aliyun.com/article/1516981

11.3. 价值网络和 Q 学习:蛇游戏示例

我们将使用经典的动作游戏 snake 作为我们深度 Q 学习的示例问题。就像我们在上一节中所做的那样,我们将首先描述 RL 问题及其带来的挑战。在这样做的过程中,我们还将讨论为什么策略梯度和 REINFORCE 对这个问题不会非常有效。

11.3.1. 蛇作为一个强化学习问题

蛇游戏首次出现在 1970 年代的街机游戏中,已经成为一个广为人知的视频游戏类型。tfjs-examples 中的 snake-dqn 目录包含一个简单变体的 JavaScript 实现。您可以通过以下代码查看:

git clone https://github.com/tensorflow/tfjs-examples.git
cd tfjs-examples/snake-dqn
yarn
yarn watch

在由yarn watch命令打开的网页中,你可以看到贪吃蛇游戏的棋盘。你可以加载一个预先训练并托管的深度 Q 网络(DQN)模型,并观察它玩游戏。稍后,我们将讨论如何从头开始训练这样的模型。现在,通过观察,你应该能直观地感受到这款游戏是如何运行的。如果你还不熟悉贪吃蛇游戏,它的设置和规则可以总结如下。

首先,所有动作发生在一个 9×9 的网格世界中(参见图 11.9 的例子)。世界(或棋盘)可以设得更大,但在我们的例子中,9×9 是默认大小。棋盘上有三种类型的方块:蛇、果子和空白。蛇由蓝色方块表示, 只有头部是橙色的,并带有半圆形代表蛇的嘴巴。果子由内部有圆圈的绿色方块表示。空白方块是白色的。游戏按步骤进行,或者按视频游戏术语来说是。在每一帧中,代理必须从三个可能的动作中为蛇选择:直行、左转或右转(原地不动不是选项)。当蛇的头部与果子方块接触时,代理被奖励呈积极反应,这种情况下果子方块将消失(被蛇“吃掉”),蛇的长度会在尾部增加一个。一个新的果子将出现在空白方块中。如果代理在某一步没有吃到果子,它将受到负奖励。游戏终止(蛇“死亡”)是指当蛇的头部离开边界(如图 11.9 的面板 B)或撞到自己的身体时。

图 11.9. 贪吃蛇游戏:一个网格世界, 玩家控制蛇吃果子。蛇的“目标”是通过有效的移动模式尽可能多地吃果子(面板 A)。每次吃一个果子蛇的长度增加 1。游戏结束(蛇“死掉”)是当蛇离开边界(面板 B)或撞到自己的身体(面板 C)时。注意,在面板 B 中,蛇的头部到达边缘位置,然后发生了向上的运动(直行动作),导致游戏终止。简单到达边缘方块并不会导致终止。吃掉每个果子会导致一个很大的正奖励。在没有吃果子的情况下移动一个方块会导致一个较小幅度的负奖励。游戏终止(蛇死亡)也会导致一个负奖励。

蛇游戏中的一个关键挑战是蛇的增长。如果没有这个规则,游戏会简单得多。只需一遍又一遍地将蛇导航到水果,智能体可以获得无限的奖励。然而,有了长度增长规则,智能体必须学会避免撞到自己的身体,随着蛇吃更多的水果和变得更长,这变得更加困难。这是蛇 RL 问题的非静态方面,推车杆环境所缺乏的,正如我们在上一节末尾提到的。

表 11.2 在经典 RL 表述中描述了蛇问题。与推车杆问题的表述(表 11.1)相比,最大的区别在于奖励结构。在蛇问题中,正奖励(每吃一颗水果+10)出现不频繁——也就是说,只有在蛇移动到达水果后,经历了一系列负奖励后才会出现。考虑到棋盘的大小,即使蛇以最有效的方式移动,两个正奖励之间的间隔也可能长达 17 步。小的负奖励是一个惩罚,鼓励蛇走更直接的路径。没有这个惩罚,蛇可以以蜿蜒的间接方式移动,并且仍然获得相同的奖励,这将使游戏和训练过程不必要地变长。这种稀疏而复杂的奖励结构也是为什么策略梯度和 REINFORCE 方法在这个问题上效果不佳的主要原因。策略梯度方法在奖励频繁且简单时效果更好,就像推车杆问题一样。

表 11.2. 在经典 RL 表述中描述蛇游戏问题
抽象 RL 概念 在蛇问题中的实现
环境 一个包含移动蛇和自我补充水果的网格世界。
动作 (离散) 三元选择:直行,左转,或右转。

| 奖励 | (频繁,混合正负奖励)

  • 吃水果——大正奖励 (+10)
  • 移动而不吃水果——小负奖励 (–0.2)
  • 死亡——大负奖励 (–10)

|

观测 (完整状态,离散) 每一步,智能体可以访问游戏的完整状态:即棋盘上每个方块的内容。
蛇的 JavaScript API

我们的 JavaScript 实现可以在文件 snake-dqn/snake_ game.js 中找到。我们只会描述SnakeGame类的 API,并略过实现细节,如果你感兴趣,可以自行学习。SnakeGame类的构造函数具有以下语法:

const game = new SnakeGame({height, width, numFruits, initLen});

这里,棋盘的大小参数,heightwidth,默认值为 9。numFruits是棋盘上任意给定时间存在的水果数量,默认值为 1。initLen,蛇的初始长度,默认值为 2。

game对象暴露的step()方法允许调用者在游戏中执行一步:

const {state, reward, done, fruitEaten} = game.step(action);

step() 方法的参数表示动作:0 表示直行,1 表示向左转,2 表示向右转。step() 方法的返回值具有以下字段:

  • state—动作后立即棋盘的新状态,表示为具有两个字段的普通 JavaScript 对象:
  • s—蛇占据的方块,以 [x, y] 坐标数组形式表示。此数组的元素按照头部对应第一个元素,尾部对应最后一个元素的顺序排列。
  • f—水果占据的方块的 [x, y] 坐标。请注意,此游戏状态的表示设计为高效,这是由 Q 学习算法存储大量(例如,成千上万)这样的状态对象所必需的,正如我们很快将看到的。另一种方法是使用数组或嵌套数组来记录棋盘上每个方块的状态,包括空的方块。这将是远不及空间高效的方法。
  • reward—蛇在步骤中立即执行动作后获得的奖励。这是一个单一数字。
  • done—一个布尔标志,指示游戏在动作发生后是否立即结束。
  • fruitEaten—一个布尔标志,指示蛇在动作中是否吃到了水果。请注意,这个字段部分冗余于 reward 字段,因为我们可以从 reward 推断出是否吃到了水果。它包含在内是为了简单起见,并将奖励的确切值(可能是可调节的超参数)与水果被吃与未被吃的二进制事件解耦。

正如我们将在稍后看到的,前三个字段(staterewarddone)在 Q 学习算法中将发挥重要作用,而最后一个字段(fruitEaten)主要用于监视。

11.3.2. 马尔可夫决策过程和 Q 值

要解释我们将应用于蛇问题的深度 Q 学习算法,首先需要有点抽象。特别是,我们将以基本水平介绍马尔可夫决策过程(MDP)及其基本数学。别担心:我们将使用简单具体的示例,并将概念与我们手头的蛇问题联系起来。

从 MDP 的视角看,RL 环境的历史是通过有限数量的离散状态的一系列转换。此外,状态之间的转换遵循一种特定类型的规则:

下一步环境的状态完全由代理在当前步骤采取的状态和动作决定。

关键是下一个状态仅取决于两件事:当前状态和采取的动作,而不是其他。换句话说,MDP 假设你的历史(你如何到达当前状态)与决定下一步该做什么无关。这是一个强大的简化,使问题更易处理。什么是 非马尔可夫决策过程?这将是一种情况,即下一个状态不仅取决于当前状态和当前动作,还取决于先前步骤的状态或动作,可能一直追溯到情节开始。在非马尔可夫情况下,数学会变得更加复杂,解决数学问题需要更多的计算资源。

对于许多强化学习问题来说,马尔可夫决策过程的要求是直观的。象棋游戏是一个很好的例子。在游戏的任何一步中,棋盘配置(以及轮到哪个玩家)完全描述了游戏状态,并为玩家提供了计算下一步移动所需的所有信息。换句话说,可以从棋盘配置恢复棋局而不知道先前的移动。 (顺便说一句,这就是为什么报纸可以以非常节省空间的方式发布国际象棋谜题的原因。)像贪吃蛇这样的视频游戏也符合马尔可夫决策过程的公式化。蛇和食物在棋盘上的位置完全描述了游戏状态,这就足以从那一点恢复游戏或代理决定下一步行动。

尽管诸如国际象棋和贪吃蛇等问题与马尔可夫决策过程完全兼容,但它们都涉及天文数字级别的可能状态。为了以直观和视觉的方式呈现马尔可夫决策过程,我们需要一个更简单的例子。在 图 11.10 中,我们展示了一个非常简单的马尔可夫决策过程问题,其中只有七种可能的状态和两种可能的代理动作。状态之间的转换受以下规则管理:

  • 初始状态始终为 s[1]。
  • 从状态 s[1] 开始,如果代理采取动作 a[1],环境将进入状态 s[2]。如果代理采取动作 a[2],环境将进入状态 s[3]。
  • 从每个状态 s[2] 和 s[3],进入下一个状态的转换遵循一组类似的分叉规则。
  • 状态 s[4]、s[5]、s[6] 和 s[7] 是终止状态:如果达到任何一个状态,那么该情节结束。
图 11.10. 马尔可夫决策过程(MDP)的一个非常简单具体的例子。状态表示为标有 s[n] 的灰色圆圈,而动作表示为标有 a[m] 的灰色圆圈。由动作引起的每个状态转换的奖励标有 r = x

因此,在这个强化学习问题中,每个阶段都恰好持续三个步骤。在这个强化学习问题中,代理应该如何决定在第一步和第二步采取什么行动?考虑到我们正在处理一个强化学习问题,只有在考虑奖励时,这个问题才有意义。在马尔可夫决策过程中,每个动作不仅引起状态转移,而且还导致奖励。在 图 11.10 中,奖励被描述为将动作与下一个状态连接的箭头,标记为 r = 。代理的目标当然是最大化总奖励(按比例折现)。现在想象一下我们是第一步的代理。让我们通过思考过程来确定我们将选择 a[1] 还是 a[2] 更好的选择。假设奖励折现因子(γ)的值为 0.9。

思考过程如下。如果我们选择动作 a[1],我们将获得–3 的即时奖励并转移到状态 s[2]。如果我们选择动作 a[2],我们将获得 3 的即时奖励并转移到状态 s[3]。这是否意味着 a[2] 是更好的选择,因为 3 大于 –3?答案是否定的,因为 3 和 –3 只是即时奖励,并且我们还没有考虑以下步骤的奖励。我们应该看看每个 s[2] 和 s[3] 的最佳可能结果是什么。从 s[2] 得到的最佳结果是通过采取动作 a[2] 而产生的结果,该动作获得了 11 的奖励。这导致我们从状态 s[1] 采取动作 a[1] 可以期望的最佳折现奖励:

当从状态 s[1] 采取动作 a[1] 时的最佳奖励 = 即时奖励 + 折现未来奖励

同样,从状态 s[3] 得到的最佳结果是我们采取动作 a[1],这给我们带来了–4 的奖励。因此,如果我们从状态 s[1] 采取动作 a[2],我们可以得到的最佳折现奖励是

当从状态 s[1] 采取动作 a[2] 时的最佳奖励 = 即时奖励 + 折现未来奖励

我们在这里计算的折现奖励是我们所说的 Q-values 的示例。 Q-value 是给定状态的动作的预期总累积奖励(按比例折现)。从这些 Q-values 中,很明显 a[1] 是在状态 s[1] 下更好的选择——这与仅考虑第一个动作造成的即时奖励的结论不同。本章末尾的练习 3 将指导您完成涉及随机性的更现实的 MDP 情景的 Q-value 计算。

描述的示例思考过程可能看起来微不足道。但它引导我们得到一个在 Q 学习中起着核心作用的抽象。Q 值,表示为 Q(s, a),是当前状态 (s) 和动作 (a) 的函数。换句话说,Q(s, a) 是一个将状态-动作对映射到在特定状态采取特定动作的估计值的函数。这个值是长远眼光的,因为它考虑了最佳未来奖励,在所有未来步骤中都选择最优动作的假设下。

由于它的长远眼光,Q(s, a) 是我们在任何给定状态决定最佳动作的全部内容。特别是,鉴于我们知道 *Q*(*s*, *a*) 是什么,最佳动作是在所有可能动作中给出最高 Q 值的动作:

方程 11.2.

其中 N 是所有可能动作的数量。如果我们对 Q(s, a) 有一个很好的估计,我们只需在每一步简单地遵循这个决策过程,我们就能保证获得最高可能的累积奖励。因此,找到最佳决策过程的强化学习问题被简化为学习函数 Q(s, a)。这就是为什么这个学习算法被称为 Q 学习的原因。

让我们停下来,看看 Q 学习与我们在小车杆问题中看到的策略梯度方法有何不同。策略梯度是关于预测最佳动作的;Q 学习是关于预测所有可能动作的值(Q 值)。虽然策略梯度直接告诉我们选择哪个动作,但 Q 学习需要额外的“选择最大值”的步骤,因此稍微间接一些。这种间接性带来的好处是,它使得在涉及稀疏正奖励(如蛇)的问题中更容易形成奖励和连续步骤值之间的连接,从而促进学习。

奖励与连续步骤的值之间有什么联系?当我们解决简单的 MDP 问题时,我们已经窥见了这一点,见图 11.10。这种连接可以用数学方式表示为:

方程 11.3.

s[next] 是我们在状态 s[i] 中选择动作 a 后到达的状态。这个方程被称为贝尔曼方程,是我们在简单的早期示例中得到数字 6 和 -0.6 的抽象。简单来说,这个方程表示:

归因于美国应用数学家理查德·E·贝尔曼(1920–1984)。参见他的书 Dynamic Programming,普林斯顿大学出版社,1957 年。

在状态 s[i] 采取动作 a 的 Q 值是两个术语的总和:

  1. 由于 a 而产生的即时奖励,以及

  2. 从下一个状态中获得的最佳 Q 值乘以一个折扣因子(“最佳”是指在下一个状态选择最优动作的意义上)

贝尔曼方程是使 Q 学习成为可能的因素,因此很重要理解。你作为程序员会立即注意到贝尔曼方程(方程 11.3)是递归的:方程右侧的所有 Q 值都可以使用方程本身进一步展开。我们在图 11.10 中解释的示例在两步之后结束,而真实的 MDP 问题通常涉及更多步骤和状态,甚至可能包含状态-动作-转换图中的循环。但贝尔曼方程的美丽和力量在于,它允许我们将 Q 学习问题转化为一个监督学习问题,即使对于大状态空间也是如此。我们将在下一节解释为什么会这样。

11.3.3。深度 Q 网络

手工制作函数Q(s, a)可能很困难,因此我们将让函数成为一个深度神经网络(在本节中之前提到的 DQN),并训练其参数。这个 DQN 接收一个表示环境完整状态的输入张量——也就是蛇板配置,这个张量作为观察结果提供给智能体。正如图 11.11 所示,该张量的形状为[9, 9, 2](不包括批次维度)。前两个维度对应于游戏板的高度和宽度。因此,张量可以被视为游戏板上所有方块的位图表示。最后一个维度(2)是代表蛇和水果的两个通道。特别地,蛇被编码在第一个通道中,头部标记为 2,身体标记为 1。水果被编码在第二个通道中,值为 1。在两个通道中,空方块用 0 表示。请注意,这些像素值和通道数目是或多或少任意的。其他值排列(例如,蛇头为 100,蛇身为 50,或者将蛇头和蛇身分成两个通道)也可能有效,只要它们保持三种实体(蛇头、蛇身和水果)是不同的。

图 11.11。蛇游戏的板状态如何表示为形状为[9, 9, 2]的三维张量

请注意,这种游戏状态的张量表示比我们在上一节中描述的由字段sf组成的 JSON 表示要不太空间有效,因为它总是包含板上的所有方块,无论蛇有多长。这种低效的表示仅在我们使用反向传播来更新 DQN 的权重时使用。此外,在任何给定时间,由于我们即将访问的基于批次的训练范式,这种方式下只存在一小部分(batchSize)游戏状态。

将有效表示的棋盘状态转换为图 11.11 中所示张量的代码可以在 snake-dqn/snake_game.js 的getStateTensor()函数中找到。这个函数在 DQN 的训练过程中会被频繁使用,但我们这里忽略其细节,因为它只是根据蛇和水果的位置机械地为张量缓冲区的元素赋值。

你可能已经注意到这种[height, width, channel]的输入格式恰好是卷积神经网络设计来处理的。我们使用的 DQN 是熟悉的卷积神经网络架构。定义 DQN 拓扑的代码可以在列表 11.5 中找到(从 snake-dqn/dqn.js 中摘录,为了清晰起见删除了一些错误检查代码)。正如代码和图 11.12 中的图示所示,网络由一组 conv2d 层后跟一个 MLP 组成。额外的层包括 batchNormalization 和 dropout 被插入以增加 DQN 的泛化能力。DQN 的输出形状为[3](排除批次维度)。输出的三个元素是对应动作(向左转,直行和向右转)的预测 Q 值。因此,我们对Q(s, a)的模型是一个神经网络,它以状态作为输入,并输出给定该状态的所有可能动作的 Q 值。

图 11.12 作为蛇问题中Q(s, a)函数的近似所使用的 DQN 的图示示意图。在“Online DQN”框中,“BN”代表 BatchNormalization。

列表 11.5 创建蛇问题的 DQN
export function createDeepQNetwork(h, w, numActions) {
  const model = tf.sequential();
  model.add(tf.layers.conv2d({                            ***1***
    filters: 128,
    kernelSize: 3,
    strides: 1,
    activation: 'relu',
    inputShape: [h, w, 2]                                 ***2***
  }));
  model.add(tf.layers.batchNormalization());              ***3***
  model.add(tf.layers.conv2d({
    filters: 256,
    kernelSize: 3,
    strides: 1,
    activation: 'relu'
  }));
  model.add(tf.layers.batchNormalization());
  model.add(tf.layers.conv2d({
    filters: 256,
    kernelSize: 3,
    strides: 1,
    activation: 'relu'
  }));
  model.add(tf.layers.flatten());                         ***4***
  model.add(tf.layers.dense({units: 100, activation: 'relu'}));
  model.add(tf.layers.dropout({rate: 0.25}));             ***5***
  model.add(tf.layers.dense({units: numActions}));
  return model;
}
  • 1 DQN 具有典型的卷积神经网络架构:它始于一组 conv2d 层。
  • 2 输入形状与代理观察的张量表示相匹配,如图 11.11 所示。
  • 3 batchNormalization 层被添加以防止过拟合并提高泛化能力
  • 4 DQN 的 MLP 部分以一个 flatten 层开始。
  • 5 与 batchNormalization 类似,dropout 层被添加以防止过拟合。

让我们停下来思考一下为什么在这个问题中使用神经网络作为函数Q(s, a)是有意义的。蛇游戏具有离散状态空间,不像连续状态空间的车杆问题,后者由四个浮点数组成。因此,Q(s, a)函数原则上可以实现为查找表,即将每个可能的棋盘配置和动作组合映射为Q的值。那么为什么我们更喜欢 DQN 而不是这样的查找表呢?原因在于,即使是相对较小的棋盘尺寸(9×9),可能的棋盘配置也太多了,导致了查找表方法的两个主要缺点。首先,系统 RAM 无法容纳如此庞大的查找表。其次,即使我们设法构建了具有足够 RAM 的系统,代理在 RL 期间访问所有状态也需要耗费非常长的时间。DQN 通过其适度大小(约 100 万参数)解决了第一个(内存空间)问题。它通过神经网络的泛化能力解决了第二个(状态访问时间)问题。正如我们在前面章节中已经看到的大量证据所示,神经网络不需要看到所有可能的输入;它通过泛化学习来插值训练示例。因此,通过使用 DQN,我们一举解决了两个问题。

一个粗略的估算表明,即使我们将蛇的长度限制为 20,可能的棋盘配置数量也至少达到 10¹⁵数量级。例如,考虑蛇长度为 20 的特定情况。首先,为蛇头选择一个位置,共有 81 种可能性(9 * 9 = 81)。然后第一段身体有四个可能的位置,接着第二段有三个可能的位置,依此类推。当然,在一些身体姿势的配置中,可能的位置会少于三个,但这不应显著改变数量级。因此,我们可以估算出长度为 20 的蛇可能的身体配置数量约为 81 * 4 * 3¹⁸ ≈ 10¹²。考虑到每种身体配置有 61 种可能的水果位置,关节蛇-水果配置的估算增加到了 10¹⁴。类似的估算可以应用于更短的蛇长度,从 2 到 19。将从长度 2 到 20 的估算数字求和得到了 10¹⁵数量级。与我们的蛇棋盘上的方块数量相比,视频游戏如 Atari 2600 游戏涉及更多的像素,因此更不适合查找表方法。这就是为什么 DQNs 是解决这类视频游戏使用 RL 的适当技术之一,正如 DeepMind 的 Volodymyr Mnih 及其同事在 2015 年的里程碑式论文中所示的那样。

11.3.4. 训练深度 Q 网络

现在,我们有了一个 DQN,可以在蛇游戏的每一步估计出三个可能行动的 Q 值。为了实现尽可能大的累积奖励,我们只需要在每一步运行 DQN,并选择具有最高 Q 值的动作即可。我们完成了吗?并没有,因为 DQN 还没有经过训练!没有适当的训练,DQN 只会包含随机初始化的权重,它给出的动作不会比随机猜测更好。现在,蛇的强化学习问题已经被减少为如何训练 DQN 的问题,这是我们在本节中要讨论的主题。这个过程有些复杂。但别担心:我们将使用大量的图表以及代码摘录,逐步详细说明训练算法。

深度 Q 网络训练的直觉

我们将通过迫使 DQN 与贝尔曼方程相匹配来训练我们的 DQN。如果一切顺利,这意味着我们的 DQN 将同时反映即时奖励和最优折现未来奖励。

我们该如何做到这一点呢?我们需要的是许多输入-输出对的样本,其中输入是实际采取的状态和动作,而输出是 Q 的“正确”(目标)值。计算输入样本需要当前状态 s[i] 和我们在该状态下采取的动作 a[j],这两者都可以直接从游戏历史中获取。计算目标 Q 值需要即时奖励 r[i] 和下一个状态 s[i][+1],这两者也可以从游戏历史中获取。我们可以使用 r[i]s[i][+1],通过应用贝尔曼方程来计算目标 Q 值,其细节将很快涉及到。然后,我们将计算由 DQN 预测的 Q 值与贝尔曼方程中的目标 Q 值之间的差异,并将其称为我们的损失。我们将使用标准的反向传播和梯度下降来减少损失(以最小二乘的方式)。使这成为可能和高效的机制有些复杂,但基本的直觉却相当简单。我们想要一个 Q 函数的估计值,以便能做出良好的决策。我们知道我们对 Q 的估计必须与环境奖励和贝尔曼方程相匹配,因此我们将使用梯度下降来实现。简单!

回放内存:用于 DQN 训练的滚动数据集

我们的 DQN 是一个熟悉的卷积网络,在 TensorFlow.js 中作为 tf.LayersModel 的一个实例实现。关于如何训练它,首先想到的是调用其 fit()fitDataset() 方法。然而,我们在这里不能使用常规方法,因为我们没有一个包含观察到的状态和相应 Q 值的标记数据集。考虑这样一个问题:在 DQN 训练之前,没有办法知道 Q 值。如果我们有一个给出真实 Q 值的方法,我们就会在马尔科夫决策过程中使用它并完成。因此,如果我们局限于传统的监督学习方法,我们将面临一个先有鸡还是先有蛋的问题:没有训练好的 DQN,我们无法估计 Q 值;没有良好的 Q 值估计,我们无法训练 DQN。我们即将介绍的强化学习算法将帮助我们解决这个先有鸡还是先有蛋的问题。

具体来说,我们的方法是让代理者随机玩游戏(至少最初是如此),并记住游戏的每一步发生了什么。随机游戏部分很容易通过随机数生成器实现。记忆部分则通过一种称为重放内存的数据结构实现。图 11.13 展示了重放内存的工作原理。它为游戏的每一步存储五个项目:

  1. s[i],第 i 步的当前状态观察(棋盘配置)。
  2. a[i],当前步骤实际执行的动作(可以是 DQN 选择的,如图 11.12,也可以是随机选择)。
  3. r[i],在此步骤接收到的即时奖励。
  4. d[i],一个布尔标志,指示游戏在当前步骤后立即结束。由此可见,重放内存不仅仅是为了一个游戏回合。相反,它将来自多个游戏回合的结果连接在一起。一旦前一场游戏结束,训练算法就会简单地开始新的游戏,并将新记录追加到重放内存中。
  5. s[i][+1],如果 d[i] 为假,则是下一步的观察。(如果 d[i] 为真,则存储 null 作为占位符。)
图 11.13. 在 DQN 训练过程中使用的重放内存。每一步都将五条数据推到重放内存的末尾。在 DQN 的训练过程中对这些数据进行抽样。

这些数据片段将用于 DQN 的基于反向传播的训练。回放记忆可以被视为 DQN 训练的“数据集”。然而,它不同于监督学习中的数据集,因为它会随着训练的进行而不断更新。回放记忆有一个固定长度M(在示例代码中默认为 10,000)。当一个记录(s[i], a[i], r[i], d[i], s[i][+1])被推到其末尾时,一个旧的记录会从其开始弹出,这保持了一个固定的回放记忆长度。这确保了回放记忆跟踪了训练中最近M步的发生情况,除了避免内存不足的问题。始终使用最新的游戏记录训练 DQN 是有益的。为什么?考虑以下情况:一旦 DQN 训练了一段时间并开始“熟悉”游戏,我们将不希望使用旧的游戏记录来教导它,比如训练开始时的记录,因为这些记录可能包含不再相关或有利于进一步网络训练的幼稚移动。

实现回放记忆的代码非常简单,可以在文件 snake-dqn/replay_memory.js 中找到。我们不会描述代码的详细信息,除了它的两个公共方法,append()sample()

  • append()允许调用者将新记录推送到回放记忆的末尾。
  • sample(batchSize)从回放记忆中随机选择batchSize个记录。这些记录完全均匀地抽样,并且通常包括来自多个不同情节的记录。sample()方法将用于在计算损失函数和随后的反向传播期间提取训练批次,我们很快就会看到。
epsilon-greedy 算法:平衡探索和利用

一个不断尝试随机事物的智能体将凭借纯运气偶然发现一些好的动作(在贪吃蛇游戏中吃几个水果)。这对于启动智能体的早期学习过程是有用的。事实上,这是唯一的方法,因为智能体从未被告知游戏的规则。但是,如果智能体一直随机行为,它在学习过程中将无法走得很远,因为随机选择会导致意外死亡,而且一些高级状态只能通过一连串良好的动作达到。

这就是蛇游戏中探索与开发的两难境地的体现。我们在平衡摇摆杆的示例中看到了这个两难境地,其中的策略梯度方法通过逐渐增加训练过程中的多项式采样的确定性来解决这个问题。在蛇游戏中,我们没有这个便利,因为我们的动作选择不是基于 tf.multinomial(),而是选择具有最大 Q 值的动作。我们解决这个问题的方式是通过参数化动作选择过程的随机性,并逐渐减小随机性参数。特别地,我们使用所谓的epsilon-greedy 策略。该策略可以用伪代码表示为`

x = Sample a random number uniformly between 0 and 1.
    if x < epsilon:
      Choose an action randomly
    else:
      qValues = DQN.predict(observation)
      Choose the action that corresponds to the maximum element of qValues

这个逻辑在训练的每一步都会应用。epsilon 的值越大(接近 1),选择动作的随机性越高。相反,epsilon 的值越小(接近 0),基于 DQN 预测的 Q 值选择动作的概率越高。随机选择动作可以看作是对环境的探索(“epsilon” 代表 “exploration”),而选择最大 Q 值的动作被称为贪心。现在你明白了 “epsilon-greedy” 这个名字的来历。

如 代码清单 11.6 所示,实现蛇 DQN 示例中 epsilon-greedy 算法的实际 TensorFlow.js 代码与之前的伪代码具有密切的一对一对应关系。该代码摘自 snake-dqn/agent.js。

代码清单 11.6。实现 epsilon-greedy 算法的部分蛇 DQN 代码
let action;
const state = this.game.getState();
if (Math.random() < this.epsilon) {
  action = getRandomAction();                          ***1***
} else {
  tf.tidy(() => {                                      ***2***
    const stateTensor =                                ***2***
        getStateTensor(state,                          ***2***
                       this.game.height,               ***2***
                       this.game.width);               ***2***
    action = ALL_ACTIONS[
        this.onlineNetwork.predict(                    ***3***
            stateTensor).argMax(-1).dataSync()[0]];    ***3***
  });
}
  • 1 探索:随机选择动作
  • 2 将游戏状态表示为张量
  • 3 贪心策略:从 DQN 获取预测的 Q 值,并找到对应于最高 Q 值的动作的索引

epsilon-greedy 策略在早期需要探索和后期需要稳定行为之间保持平衡。它通过逐渐减小 epsilon 的值,从一个相对较大的值逐渐减小到接近(但不完全等于)零。在我们的蛇 DQN 示例中,epsilon 在训练的前 1 × 105 步中以线性方式逐渐减小从 0.5 到 0.01。请注意,我们没有将 epsilon 减小到零,因为在智能体的训练的高级阶段,我们仍然需要适度的探索程度来帮助智能体发现新的智能举动。在基于 epsilon-greedy 策略的 RL 问题中,epsilon 的初始值和最终值都是可调节的超参数,epsilon 的降低时间也是如此。

在 epsilon-greedy 策略设定下的深度 Q 学习算法背景下,接下来让我们详细了解 DQN 的训练细节。

提取预测的 Q 值

尽管我们正在使用一种新方法来解决 RL 问题,但我们仍然希望将我们的算法塑造成监督学习,因为这样可以让我们使用熟悉的反向传播方法来更新 DQN 的权重。这样的制定需要三个要素:

  • 预测的 Q 值。
  • “真实”的 Q 值。请注意,在这里,“真实”一词带有引号,因为实际上并没有办法获得 Q 值的基本真实值。这些值只是我们在训练算法的给定阶段能够得到的Q(s, a)的最佳估计值。因此,我们将其称为目标 Q 值。
  • 一个损失函数,它以预测和目标 Q 值作为输入,并输出一个量化两者之间不匹配的数字。

在这个小节中,我们将看看如何从回放记忆中获取预测的 Q 值。接下来的两个小节将讨论如何获取目标 Q 值和损失函数。一旦我们有了这三个要素,我们的蛇 RL 问题基本上就变成了一个简单的反向传播问题。

图 11.14 说明了如何从回放记忆中提取预测的 Q 值的 DQN 训练步骤。应该将这个图表与实现代码清单 11.7 一起查看,以便更好地理解。

图 11.14. 如何从回放记忆和在线 DQN 中获取预测的 Q 值。这是 DQN 训练算法中监督学习部分的两个部分中的第一个部分。这个工作流的结果,即 DQN 预测的 Q 值actionQs,是将与targetQs一起用于计算 MSE 损失的两个参数之一。查看图 11.15 以了解计算targetQs的工作流程。

特别地,我们从回放记忆中随机抽取batchSize(默认为N = 128)条记录。正如之前所描述的,每条记录都有五个项目。为了获得预测的 Q 值,我们只需要前两个。第一个项目,包括N个状态观察,一起转换成一个张量。这个批处理的观察张量由在线 DQN 处理,它给出了预测的 Q 值(在图表和代码中都是qs)。然而,qs包含的 Q 值不仅包括实际选择的动作,还包括未选择的动作。对于我们的训练,我们希望忽略未选择动作的 Q 值,因为没有办法知道它们的目标 Q 值。这就是第二个回放记忆项发挥作用的地方。

第二项包含实际选择的动作。它们被格式化成张量表示(图和代码中的 actionTensor)。然后使用 actionTensor 选择我们想要的 qs 元素。这一步骤在图中标记为选择实际动作的框中完成,使用了三个 TensorFlow.js 函数:tf.oneHot()mul()sum()(参见清单 11.7 中的最后一行)。这比切片张量稍微复杂一些,因为在不同的游戏步骤可以选择不同的动作。清单 11.7 中的代码摘自 snake-dqn/agent.js 中的 SnakeGameAgent.trainOnReplayBatch() 方法,为了清晰起见进行了些许省略。

清单 11.7. 从回放内存中提取一批预测的 Q 值
const batch = this.replayMemory.sample(batchSize);       ***1***
const stateTensor = getStateTensor(
    batch.map(example => example[0]),                    ***2***
    this.game.height, this.game.width);
const actionTensor = tf.tensor1d(
    batch.map(example => example[1]),                    ***3***
    'int32');
const qs = this.onlineNetwork.apply(                     ***4***
    stateTensor, {training: true})                       ***4***
    .mul(tf.oneHot(actionTensor, NUM_ACTIONS)).sum(-1);  ***5***
  • 1 从回放内存中随机选择一批大小为 batchSize 的游戏记录
  • 2 每个游戏记录的第一个元素是代理的状态观察(参见图 11.13)。它由 getStateTensor() 函数(参见图 11.11)将其从 JSON 对象转换为张量。
  • 3 游戏记录的第二个元素是实际选择的动作。它也被表示为张量。
  • 4 apply() 方法与 predict() 方法类似,但显式指定了“training: true”标志以启用反向传播。
  • 5 我们使用 tf.oneHot()、mul() 和 sum() 来隔离仅针对实际选择的动作的 Q 值,并丢弃未选择的动作的 Q 值。

这些操作给了我们一个名为 actionQs 的张量,其形状为 [N],其中 N 是批次大小。这就是我们寻找的预测的 Q 值,即我们所处的状态 s 和我们实际采取的动作 a 的预测 Q(s, a)。接下来,我们将探讨如何获取目标 Q 值。

提取目标 Q 值:使用贝尔曼方程

获取目标 Q 值比获取预测值稍微复杂一些。这是理论上的贝尔曼方程将被实际应用的地方。回想一下,贝尔曼方程用两个因素描述了状态-动作对的 Q 值:1) 即时奖励和 2) 下一步状态可用的最大 Q 值(通过一个因子折现)。前者很容易获得。它直接作为回放内存的第三项可得到。图 11.15 中的 rewardTensor 用示意图的方式说明了这一点。

图 11.15. 如何从重播记忆和目标 DQN 获取目标 Q 值(targetQs)。此图与图 11.14 共享重播记忆和批量采样部分。应该与列表 11.8 中的代码一起检查。这是进入 DQN 训练算法的监督学习部分的两个部分之一。targetQs在计算中起着类似于前几章中监督学习问题中的真实标签的作用(例如,MNIST 示例中的已知真实标签或 Jena-weather 示例中的已知真实未来温度值)。贝尔曼方程在计算targetQs中起着关键作用。与目标 DQN 一起,该方程允许我们通过形成当前步骤的 Q 值和随后步骤的 Q 值之间的连接来计算targetQs的值。

要计算后者(最大的下一步 Q 值),我们需要来自下一步的状态观察。幸运的是,下一步观察被存储在重播记忆中的第五项。我们取随机抽样批次的下一步观察,将其转换为张量,并通过名为目标 DQN的 DQN 的副本运行它(见图 11.15)。这给了我们下一步状态的估计 Q 值。一旦我们有了这些值,我们沿着最后(动作)维度进行max()调用,这导致从下一步状态中获得的最大 Q 值(在列表 11.8 中表示为nextMaxQTensor)。遵循贝尔曼方程,这个最大值乘以折扣因子(图 11.15 中的γ和列表 11.8 中的gamma)并与即时奖励相结合,产生目标 Q 值(在图和代码中均为targetQs)。

注意,只有当当前步骤不是游戏剧集的最后一步时(即,它不会导致蛇死亡),下一步 Q 值才存在。如果是,那么贝尔曼方程的右侧将仅包括即时奖励项,如图 11.15 所示。这对应于列表 11.8 中的doneMask张量。此列表中的代码摘自 snake-dqn/agent.js 中的SnakeGameAgent.trainOnReplayBatch()方法,为了清晰起见做了一些小的省略。

图 11.8. 从重播记忆中提取一批目标(“真实”)Q 值
const rewardTensor = tf.tensor1d(
    batch.map(example => example[2]));             ***1***
const nextStateTensor = getStateTensor(
    batch.map(example => example[4]),              ***2***
    this.game.height, this.game.width);
const nextMaxQTensor =
    this.targetNetwork.predict(nextStateTensor)    ***3***
    .max(-1);                                      ***4***
const doneMask = tf.scalar(1).sub(
    tf.tensor1d(batch.map(example => example[3]))
        .asType('float32'));                       ***5***
const targetQs =                                   ***6***
    rewardTensor.add(nextMaxQTensor.mul(           ***6***
        doneMask).mul(gamma));                     ***6***
  • 1 重播记录的第三项包含即时奖励值。
  • 2 记录的第五项包含下一状态观察。它被转换为张量表示。
  • 3 目标 DQN 用于下一个状态张量,它产生下一步所有动作的 Q 值。
  • 4 使用max()函数提取下一步可能的最高奖励。这在贝尔曼方程的右侧。
  • 5 doneMask 在终止游戏的步骤上具有值 0,并在其他步骤上具有值 1。
  • 6 使用贝尔曼方程来计算目标 Q 值。

正如你可能已经注意到的,在深度 Q 学习算法中的一个重要技巧是使用两个 DQN 实例。它们分别被称为 在线 DQN 和 目标 DQN。在线 DQN 负责计算预测的 Q 值(参见上一小节的 图 11.14)。它也是我们在 epsilon-greedy 算法决定采用贪婪(无探索)方法时选择蛇行动的 DQN。这就是为什么它被称为“在线”网络。相比之下,目标 DQN 仅用于计算目标 Q 值,就像我们刚刚看到的那样。这就是为什么它被称为“目标”DQN。为什么我们使用两个 DQN 而不是一个?为了打破不良反馈循环,这可能会导致训练过程中的不稳定性。

在线 DQN 和目标 DQN 是由相同的 createDeepQNetwork() 函数创建的(清单 11.5)。它们是两个具有相同拓扑结构的深度卷积网络。因此,它们具有完全相同的层和权重集。权重值周期性地从在线 DQN 复制到目标 DQN(在默认设置的 snake-dqn 中每 1,000 步)。这使目标 DQN 与在线 DQN 保持同步。没有这种同步,目标 DQN 将过时,并通过产生贝尔曼方程中最佳下一步 Q 值的劣质估计来阻碍训练过程。

Q 值预测和反向传播的损失函数

有了预测和目标 Q 值,我们使用熟悉的 meanSquaredError 损失函数来计算两者之间的差异(图 11.16)。在这一点上,我们已经成功将我们的 DQN 训练过程转化为一个回归问题,类似于以前的例子,如波士顿房屋和耶拿天气。来自 meanSquaredError 损失的误差信号驱动反向传播;由此产生的权重更新用于更新在线 DQN。

图 11.16. 将 actionQstargetQs 结合在一起,以计算在线 DQN 的 meanSquaredError 预测误差,从而使用反向传播来更新其权重。这张图的大部分部分已经在 图 11.12 和 11.13 中展示过。新添加的部分是 meanSquaredError 损失函数和基于它的反向传播步骤,位于图的右下部分。

图 11.16 中的示意图包括我们已经在 图 11.12 和 11.13 中展示过的部分。它将这些部分放在一起,并添加了新的框和箭头,用于 meanSquaredError 损失和基于它的反向传播(见图的右下部分)。这完成了我们用来训练蛇游戏代理的深度 Q 学习算法的完整图景。

代码清单 11.9 中的代码与图 11.16 中的图表紧密对应。这是在蛇 DQN / agent.js 中的 SnakeGameAgent 类的 trainOnReplayBatch()方法,它在我们的强化学习算法中发挥着核心作用。该方法定义了一个损失函数,该函数计算预测 Q 值和目标 Q 值之间的 meanSquaredError。然后,它使用tf.variableGrads()函数(附录 B,第 B.4 节包含了有关 TensorFlow.js 的梯度计算函数(如tf.variableGrads())的详细讨论)计算在线 DQN 权重相对于 meanSquaredError 的梯度。通过优化器使用计算出的梯度来更新 DQN 的权重。这将促使在线 DQN 朝着更准确的 Q 值估计方向移动。重复数百万次后,这将导致 DQN 能够引导蛇达到不错的性能。对于下面的列表,已经展示了负责计算目标 Q 值(targetQs)的代码部分(参见代码清单 11.8)。

代码清单 11.9。训练 DQN 的核心函数
trainOnReplayBatch(batchSize, gamma, optimizer) {
  const batch = this.replayMemory.sample(batchSize);              ***1***
  const lossFunction = () => tf.tidy(() => {                      ***2***
    const stateTensor = getStateTensor(
        batch.map(example => example[0]),
                  this.game.height,
                  this.game.width);
    const actionTensor = tf.tensor1d(
        batch.map(example => example[1]), 'int32');
    const qs = this.onlineNetwork                                 ***3***
        .apply(stateTensor, {training: true})
        .mul(tf.oneHot(actionTensor, NUM_ACTIONS)).sum(-1);
    const rewardTensor = tf.tensor1d(batch.map(example => example[2]));
    const nextStateTensor = getStateTensor(
        batch.map(example => example[4]),
                  this.game.height, this.game.width);
    const nextMaxQTensor =
        this.targetNetwork.predict(nextStateTensor).max(-1);
    const doneMask = tf.scalar(1).sub(
        tf.tensor1d(batch.map(example => example[3])).asType('float32'));
    const targetQs =                                              ***4***
        rewardTensor.add(nextMaxQTensor.mul(doneMask).mul(gamma));
    return tf.losses.meanSquaredError(targetQs, qs);              ***5***
  });
  const grads = tf.variableGrads(                                 ***6***
      lossFunction, this.onlineNetwork.getWeights());
  optimizer.applyGradients(grads.grads);                          ***7***
  tf.dispose(grads);
}
  • 1 从重播缓冲区中获取一组随机示例
  • 2 lossFunction 返回标量,将用于反向传播。
  • 3 预测的 Q 值
  • 4 通过应用贝尔曼方程计算的目标 Q 值
  • 5 使用均方误差(MSE)作为预测和目标 Q 值之间差距的度量
  • 6 计算损失函数相对于在线 DQN 权重的梯度
  • 7 通过优化器使用梯度更新权重

至此,深度 Q 学习算法的内部细节就介绍完了。在 Node.js 环境中,可以使用以下命令开始基于这个算法的训练:

yarn train --logDir /tmp/snake_logs

如果您有支持 CUDA 的 GPU,请将--gpu标志添加到命令中,以加快训练速度。此--logDir标志让该命令在训练过程中将以下指标记录到 TensorBoard 日志目录中:1)最近 100 个游戏周期内累计奖励的运行平均值(cumulativeReward100);2)最近 100 个周期内食用水果数量的运行平均值(eaten100);3)探索参数的值(epsilon);4)每秒钟处理的步数(framesPerSecond)的训练速度。这些日志可以通过使用以下命令启动 TensorBoard 并导航到 TensorBoard 前端的 HTTP URL(默认为:http://localhost:6006)进行查看:

pip install tensorboard tensorboard --logdir /tmp/snake_logs

图 11.17 展示了一组训练过程中典型的对数曲线。在强化学习中,cumulativeReward100 和 eaten100 曲线都经常展现出波动。经过几个小时的训练,模型可以达到 cumulativeReward100 的最佳成绩为 70-80,eaten100 的最佳成绩约为 12。

图 11.17:tfjs-node 中蛇的强化学习训练过程的示例日志。面板显示:1)cumulativeReward100,最近 100 场游戏的累积奖励的移动平均;2)eaten100,最近 100 场游戏中水果被吃的移动平均;3)epsilon,epsilon 的值,您可以从中看到 epsilon-greedy 策略的时间进程;以及 4)framesPerSecond,训练速度的度量。

训练脚本还会在每次达到新的最佳 cumulativeReward100 值时,将模型保存到相对路径./models/dqn。在调用 yarn watch 命令时,从 web 前端加载保存的模型。前端会在游戏的每一步显示 DQN 预测的 Q 值(参见图 11.18)。在训练期间使用的 epsilon-greedy 策略在训练后的游戏中被“始终贪婪”的策略所取代。蛇的动作总是选择对应于最高 Q 值的动作(例如,在图 11.18 中,直行的 Q 值为 33.9)。这可以直观地了解训练后的 DQN 如何玩游戏。

图 11.18:经过训练的 DQN 估计的 Q 值以数字形式显示,并以不同的绿色叠加在游戏的前端。

从蛇的行为中有几个有趣的观察。首先,在前端演示中,蛇实际吃到的水果数量(约为 18)平均要大于训练日志中的 eaten100 曲线(约为 12)。这是因为 epsilon-greedy 策略的移除,这消除了游戏过程中的随机动作。请记住,epsilon 在 DQN 训练的后期维持为一个小但非零的值(参见图 11.17 的第三个面板)。由此引起的随机动作偶尔会导致蛇的提前死亡,这就是探索性行为的代价。其次,蛇在靠近水果之前会经过棋盘的边缘和角落,即使水果位于棋盘的中心附近。这种策略对于帮助蛇在长度适中(例如,10-18)时减少碰到自己的可能性是有效的。这并不是坏事,但也不是完美的,因为蛇尚未形成更聪明的策略。例如,蛇在长度超过 20 时经常陷入一个循环。这就是蛇的强化学习算法能够带给我们的。为了进一步改进蛇的智能体,我们需要调整 epsilon-greedy 算法,以鼓励蛇在长度较长时探索更好的移动方式。[9] 在当前的算法中,一旦蛇的长度需要在其自身周围熟练操纵时,探索的程度太低。

例如,查看github.com/carsonprindle/OpenAIExam2018

这就是我们对 DQN 技术的介绍结束了。我们的算法是基于 2015 年的论文“通过深度强化学习实现人类水平的控制”,[10],在该论文中,DeepMind 的研究人员首次证明,结合深度神经网络和强化学习的力量使得机器能够解决许多类似 Atari 2600 的视频游戏。我们展示的 snake-dqn 解决方案是 DeepMind 算法的简化版本。例如,我们的 DQN 仅查看当前步骤的观察,而 DeepMind 的算法将当前观察与前几个步骤的观察结合起来作为 DQN 的输入。但我们的示例捕捉到了这一划时代技术的本质——即使用深度卷积网络作为强大的函数逼近器来估计动作的状态相关值,并使用 MDP 和贝尔曼方程进行训练。强化学习研究人员的后续成就,如征服围棋和国际象棋等游戏,都基于类似的深度神经网络和传统非深度学习强化学习方法的结合。

¹⁰

Volodymyr Mnih 等人,《深度强化学习实现人类水平的控制》,自然, vol. 518, 2015, pp. 529–533,www.nature.com/articles/nature14236/

进一步阅读材料

  • Richard S. Sutton 和 Andrew G. Barto,《强化学习导论》,A Bradford 书籍,2018。
  • David Silver 在伦敦大学学院的强化学习讲座笔记:www0.cs.ucl.ac.uk/staff/d.silver/web/Teaching.html
  • Alexander Zai 和 Brandon Brown,《深度强化学习实战》,Manning 出版社,即将出版,www.manning.com/books/deep-reinforcement-learning-in-action
  • Maxim Laplan,《深度强化学习实战:应用现代强化学习方法,包括深度 Q 网络,值迭代,策略梯度,TRPO,AlphaGo Zero 等》,Packt 出版社,2018。

练习

  1. 在小车摆杆示例中,我们使用了一个策略网络,其中包含一个带有 128 个单元的隐藏密集层,因为这是默认设置。这个超参数如何影响基于策略梯度的训练?尝试将其更改为小值,如 4 或 8,并将结果的学习曲线(每游戏平均步数与迭代曲线)与默认隐藏层大小的曲线进行比较。这对模型容量和其估计最佳动作的有效性之间的关系告诉了你什么?
  2. 我们提到使用机器学习解决类似倒立摆的问题的一个优点是人力经济性。具体来说,如果环境意外改变,我们不需要弄清楚它是如何真正改变的并重新确定物理方程,而是可以让代理人自行重新学习问题。通过以下步骤向自己证明这一点。首先,确保倒立摆示例是从源代码而不是托管的网页启动的。使用常规方法训练一个有效的倒立摆策略网络。其次,编辑 cart-pole/cart_pole.js 中的 this.gravity 的值,并将其更改为一个新值(例如,如果您要假装我们将倒立摆的配置移到一个比地球更高重力的外行星上,可以将其改为 12)。再次启动页面,加载您在第一步训练的策略网络,并对其进行测试。你能确认它因为重力的改变而表现明显更差吗?最后,再多训练几次策略网络。你能看到策略又逐渐适应新环境而在游戏中表现越来越好吗?
  3. (有关 MDP 和贝尔曼方程的练习)我们在 第 11.3.2 节 和 图 11.10 中提供的 MDP 示例在一定程度上是简单的,因为状态转移和相关奖励没有随机性。但是,许多现实世界的问题更适合描述为随机 MDP。在随机 MDP 中,代理人在采取行动后将进入的状态和获得的奖励遵循概率分布。例如,如 图 11.19 所示,如果代理人在状态S[1]采取行动A[1],它将以 0.5 的概率进入状态S[2],以 0.5 的概率进入状态S[3]。与这两个状态转换关联的奖励是不同的。在这种随机 MDP 中,代理人必须计算预期未来奖励,以考虑随机性。预期未来奖励是所有可能奖励的加权平均值,权重为概率。你能应用这种概率方法并在图中估计s[1]的a[1]和a[2]的 Q 值吗?根据答案,在状态s[1]时,a[1]和a[2]哪个是更好的行动?
    图 11.19. 练习 3 第一部分的 MDP 图表

    现在让我们看一个稍微复杂一点的随机 MDP,其中涉及多个步骤(参见 图 11.20)。在这种稍微复杂的情况下,您需要应用递归的贝尔曼方程,以考虑第一步之后的可能的最佳未来奖励,这些奖励本身也是随机的。请注意,有时在第一步之后,该情节结束,而有时它将持续进行另一步。你能决定在s[1]时哪个行动更好吗?对于这个问题,您可以使用奖励折扣因子 0.9。
    图 11.20。练习 3 第二部分中 MDP 的图表

  4. 在贪吃蛇-dqn 示例中,我们使用ε-贪心策略来平衡探索和利用的需求。默认设置将ε从初始值 0.5 减小到最终值 0.01,并将其保持在那里。尝试将最终ε值更改为较大的值(例如 0.1)或较小的值(例如 0),并观察蛇代理学习效果的影响。您能解释ε扮演的角色造成的差异吗?

摘要

  • 作为一种机器学习类型,强化学习是关于学习如何做出最优决策。在强化学习问题中,代理学习在环境中选择行动以最大化称为累积奖励的指标。
  • 与监督学习不同,RL 中没有标记的训练数据集。相反,代理必须通过尝试随机动作来学习在不同情况下哪些动作是好的。
  • 我们探讨了两种常用的强化学习算法类型:基于策略的方法(以倒立摆为例)和基于 Q 值的方法(以贪吃蛇为例)。
  • 策略是一种算法,代理根据当前状态观察选择动作。策略可以封装在一个神经网络中,该网络将状态观察作为输入并产生动作选择作为输出。这样的神经网络称为策略网络。在倒立摆问题中,我们使用策略梯度和 REINFORCEMENT 方法来更新和训练策略网络。
  • 与基于策略的方法不同,Q 学习使用一种称为Q 网络的模型来估算在给定观察状态下行动的值。在贪吃蛇-dqn 示例中,我们演示了深度卷积网络如何作为 Q 网络以及如何通过使用 MDP 假设、贝尔曼方程和一种称为回放 记忆的结构来训练它。

第四部分:总结和结束语。

本书的最后一部分包括两个章节。第十二章解决了 TensorFlow.js 用户在部署模型到生产环境时可能遇到的问题。本章讨论了帮助开发人员更有信心地确保模型正确性的最佳实践,使模型体积更小且运行更高效的技术,以及 TensorFlow.js 模型支持的部署环境的范围。第十三章是对整本书的总结,回顾了关键概念、工作流程和技术。

JavaScript 深度学习(四)(5)https://developer.aliyun.com/article/1516985

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
1天前
|
机器学习/深度学习 JavaScript 前端开发
【JS】深度学习JavaScript
【JS】深度学习JavaScript
6 2
|
30天前
|
机器学习/深度学习 自然语言处理 JavaScript
JavaScript 深度学习(四)(1)
JavaScript 深度学习(四)
13 2
|
30天前
|
机器学习/深度学习 前端开发 JavaScript
JavaScript 深度学习(四)(5)
JavaScript 深度学习(四)
15 1
|
30天前
|
机器学习/深度学习 算法 JavaScript
JavaScript 深度学习(四)(3)
JavaScript 深度学习(四)
28 1
|
30天前
|
机器学习/深度学习 编解码 JavaScript
JavaScript 深度学习(四)(2)
JavaScript 深度学习(四)
24 1
|
30天前
|
机器学习/深度学习 JavaScript 前端开发
JavaScript 深度学习(五)(5)
JavaScript 深度学习(五)
11 0
|
30天前
|
机器学习/深度学习 JavaScript 前端开发
JavaScript 深度学习(五)(4)
JavaScript 深度学习(五)
26 0
|
30天前
|
存储 机器学习/深度学习 JavaScript
JavaScript 深度学习(五)(3)
JavaScript 深度学习(五)
25 0
|
30天前
|
机器学习/深度学习 并行计算 JavaScript
JavaScript 深度学习(五)(2)
JavaScript 深度学习(五)
32 0
|
30天前
|
机器学习/深度学习 人工智能 JavaScript
JavaScript 深度学习(五)(1)
JavaScript 深度学习(五)
28 0