TensorFlow 强化学习:1~5

2023-04-27 15:11:26 浏览数 (3)

一、深度学习–架构和框架

人工神经网络是一种计算系统,为我们提供了解决诸如图像识别到语音翻译等具有挑战性的机器学习任务的重要工具。 最近的突破,例如 Google DeepMind 的 AlphaGo 击败了最好的围棋玩家,或者卡内基梅隆大学的 Libratus 击败了世界上最好的职业扑克玩家,都证明了算法的进步。 这些算法像人类一样学习狭窄的智能,并达到超人水平的表现。 用通俗易懂的话说,人工神经网络是我们可以在计算机上编程的人脑的松散表示。 确切地说,这是受我们对人脑功能知识的启发而产生的一种方法。 神经网络的一个关键概念是创建输入数据的表示空间,然后在该空间中解决问题。 也就是说,从数据的当前状态开始扭曲数据,以便可以以不同的状态表示数据,从而可以解决有关的问题陈述(例如分类或回归)。 深度学习意味着多个隐藏的表示,即具有许多层的神经网络,可以创建更有效的数据表示。 每一层都会细化从上一层收到的信息。

另一方面,强化学习是机器学习的另一个方面,它是一种学习遵循一系列动作的任何类型的活动的技术。 强化学习智能体从环境中收集信息并创建状态表示; 然后执行一个导致新状态和报酬的动作(即来自环境的可量化反馈,告诉我们该动作是好是坏)。 这种现象一直持续到智能体能够将表现提高到超过特定阈值(即最大化奖励的期望值)为止。 在每个步骤中,可以随机选择这些动作,将其固定或使用神经网络进行监督。 使用深度神经网络对动作预测进行监督将打开一个新领域,称为深度强化学习。 这构成了 AlphaGo,Libratus 和人工智能领域中许多其他突破性研究的基础。

我们将在本章介绍以下主题:

  • 深度学习
  • 强化学习
  • TensorFlow 和 OpenAI Gym 简介
  • 强化学习中有影响力的研究人员和项目

深度学习

深度学习是指训练大型神经网络。 首先,让我们讨论神经网络的一些基本用例,以及即使这些神经网络已经存在数十年,为什么深度学习仍会引起如此激烈的争论。

以下是神经网络中监督学习的示例:

输入(x)

输出(y)

应用领域

建议的神经网络方法

房屋特征

房子的价格

房地产

在输出层中带有线性整流单元的标准神经网络

广告和点击广告的用户信息

是(1)或否(0)

在线广告

具有二分类的标准神经网络

图像对象

从 100 个不同的对象进行分类,即(1,2,…,100)

照片标注

卷积神经网络(由于图像,即空间数据)

语音

文字笔录

语音识别

循环神经网络(因为两个输入输出都是序列数据)

英文

中文

机器翻译

循环神经网络(因为输入是序列数据)

图像,雷达信息

其他汽车的位置

自动驾驶

定制的混合/复杂神经网络

在本章的后续部分中,我们将详细介绍前面提到的神经网络,但是首先我们必须了解,根据问题陈述的目的使用了不同类型的神经网络。

监督学习是机器学习中的一种方法,其中,使用输入特征对及其对应的输出/目标值(也称为标签)来训练智能体。

传统的机器学习算法对于结构化数据非常有效,其中大多数输入特征都定义得很好。 对于非结构化数据(例如音频,图像和文本)而言,情况并非如此,其中数据分别是信号,像素和字母。 对于计算机而言,比结构化数据更难理解非结构化数据。 神经网络基于这种非结构化数据进行预测的能力是其普及并产生经济价值的关键原因。

首先,它是当前的规模,即数据,计算能力和新算法的规模,这正在推动深度学习的发展。 互联网已经过去了四十年,导致大量的数字足迹不断积累和增长。 在此期间,研究和技术发展帮助扩大了计算系统的存储和处理能力。 当前,由于这些繁重的计算系统和海量数据,我们能够验证过去三十年来在人工智能领域的发现。

现在,我们需要什么来实现深度学习?

首先,我们需要大量数据。

其次,我们需要训练一个相当大的神经网络。

那么,为什么不对少量数据训练大型神经网络呢?

回想一下您的数据结构课程,其中结构的用途是充分处理特定类型的值。 例如,您将不会在具有张量数据类型的变量中存储标量值。 类似地,鉴于大量数据,这些大型神经网络会创建不同的表示并开发理解模式,如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XmxWJnvn-1681786426144)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/ee3226e9-923f-4bd2-97b7-0b4035267f13.png)]

请参考以下数据的图形表示与不同机器学习算法的表现的比较:

  1. 我们看到传统的机器学习算法的表现在一定时间后收敛,因为它们无法吸收超过阈值的数据量的不同表示形式。
  2. 检查图的左下角,靠近原点。 这是算法的相对顺序没有很好定义的区域。 由于数据量小,内部表示形式并没有那么明显。 结果,所有算法的表现指标都一致。 在此级别上,表现与更好的特征工程成正比。 但是这些手工设计的特征随着数据大小的增加而失败。 这就是深度神经网络的来历,因为它们能够从大量数据中捕获更好的表示。

因此,我们可以得出结论,不应该将深度学习架构应用于任何遇到的数据。 所获得数据的数量和种类表明要应用哪种算法。 有时,小数据通过传统的机器学习算法比深度神经网络更有效。

深度学习问题陈述和算法可以根据其研究和应用领域进一步细分为四个不同的部分:

  • 通用深度学习:密集连接的层或全连接网络
  • 序列模型:循环神经网络,长短期记忆网络,门控循环单元等
  • 空间数据模型(例如,图像):卷积神经网络,生成对抗网络
  • 其他:无监督学习,强化学习,稀疏编码等

当前,该行业主要由前三个部分驱动,但是人工智能的未来取决于第四部分的发展。 沿着机器学习的发展历程,我们可以看到直到现在,这些学习模型都将实数作为输出,例如电影评论(情感评分)和图像分类(类对象)。 但是现在,以及其他类型的输出也正在生成,例如,图像字幕(输入:图像,输出:文本),机器翻译(输入:文本,输出:文本)和语音识别(输入:音频, 输出:文本)。

人类水平的表现是必要的,并且通常应用于深度学习。 在一段时间内收敛到最高点之后,人员水平的准确率变得恒定。 这一点称为最佳错误率(也称为贝叶斯误差率,即对于任何随机结果分类器而言,最低的错误率)。

其背后的原因是由于数据中的噪声,许多问题在表现上都有理论上的限制。 因此,人员级别的准确率是通过进行误差分析来改进模型的好方法。 这是通过合并人为误差,训练集误差和验证集误差来估计偏差方差影响(即欠拟合和过拟合条件)来完成的。

数据规模,算法类型和表现指标是一组方法,可以帮助我们针对不同的机器学习算法确定改进水平。 因此,决定是否投资深度学习或采用传统机器学习方法的关键决策。

具有一些输入特征(下图中的三个)的基本感知器如下所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rqDNEBUa-1681786426145)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/4d39b442-6c4c-4960-ab6a-944651a3f70b.png)]

上图设置了神经网络的基本方法,即如果我们在第一层中有输入,而在第二层中有输出,则它看起来像。 让我们尝试解释一下。 这里:

  • X1X2X3是输入特征变量,即此处的输入维为 3(考虑到没有偏差变量)。
  • W1W2W3是与特征变量关联的相应权重。 当我们谈论神经网络的训练时,我们的意思是说权重的训练。 因此,这些构成了我们小型神经网络的参数。
  • 输出层中的函数是应用于从前一层接收的信息的聚合上的激活函数。 此函数创建一个与实际输出相对应的表示状态。 从输入层到输出层的一系列过程导致预测输出,称为正向传播。
  • 通过多次迭代,可以最小化激活函数的输出和实际输出之间的误差值。
  • 只有在我们将权重的值(从输出层到输入层)更改为可以使误差函数最小的方向时,才会使误差最小。 这个过程称为反向传播,因为我们正朝相反的方向发展。

现在,牢记这些基础知识,让我们进一步使用逻辑回归作为神经网络对神经网络进行神秘化,并尝试创建具有一个隐藏层的神经网络。

深度学习的激活函数

激活函数是人工神经网络的组成部分。 他们决定特定神经元是否被激活,即神经元接收到的信息是否相关。 激活函数对接收信号(数据)执行非线性变换。

我们将在以下各节中讨论一些流行的激活函数。

Sigmoid 函数

Sigmoid 是一种平滑且连续可微的函数。 这导致非线性输出。 Sigmoid 函数在这里表示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oBrFUlss-1681786426145)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/9241aa68-606b-4192-b3cc-ccb29f4ef110.png)]

请查看下图的 Sigmoid 函数观察结果。 该函数的范围是 0 到 1。观察函数的曲线,我们看到x值介于 -3 和 3 之间时,梯度非常高,但超出此范围则变得平坦。 因此,可以说这些点附近的x的微小变化将使 Sigmoid 函数的值产生较大的变化。 因此,函数目标是将 Sigmoid 函数的值推向极限。

因此,它被用于分类问题:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xlsy3foy-1681786426146)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/ba07be70-2b01-4112-be20-9689a19ac370.png)]

查看下面的 Sigmoid 函数的梯度,我们观察到取决于x的平滑曲线。 由于梯度曲线是连续的,因此很容易反向传播误差并更新参数Wb

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W7mGRyD7-1681786426146)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/d8011c52-5574-4521-a42d-e18893417c8d.png)]

Sigmoid 得到广泛使用,但其缺点是该函数趋于 3 和 -3 平坦。 因此,无论何时函数落在该区域中,梯度都趋于接近零,并且我们的神经网络学习停止。

由于 Sigmoid 函数输出的值从 0 到 1,即全为正,因此它在原点周围是非对称的,并且所有输出信号都是正的,即具有相同的符号。 为了解决这个问题,已将 Sigmoid 函数缩放为 tanh 函数,我们将在下面进行研究。 而且,由于梯度导致的值很小,因此很容易遇到梯度消失的问题(我们将在本章稍后讨论)。

tanh 函数

Tanh 是围绕原点对称的连续函数。 它的范围是 -1 至 1。tanh 函数表示如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xahGR7ew-1681786426146)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/8aee5967-4c25-4643-b7c8-42fa21c6fa21.png)]

因此,输出信号将同时为正和负,从而增加了原点周围信号的隔离。 如前所述,它是连续的,也是非线性的,并且在所有点上都是可微的。 在下图中,我们可以在 tanh 函数的图中观察这些属性。 尽管是对称的,但它变得平坦,超过 -2 和 2:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yv979Ydi-1681786426146)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/93d7dc16-99f6-48f9-b12f-39bb21a534d6.png)]

现在看下面的 tanh 函数的梯度曲线,我们发现它比 Sigmoid 函数陡峭。 tanh 函数还具有梯度消失问题:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eNmeD9by-1681786426147)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/1472e114-eba7-4719-9668-48f28c2716b0.png)]

softmax 函数

softmax 函数主要用于处理分类问题,并且最好在输出层中使用,以输出输出类别的概率。 如前所述,在解决二进制逻辑回归时,我们看到 Sigmoid 函数只能处理两个类。 为了处理多类,我们需要一个可以为所有类生成值的函数,这些值遵循概率规则。 这个目标是通过 softmax 函数实现的,该函数将每个类别的输出缩小在 0 和 1 之间,然后将它们除以所有类别的输出总和:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MT3mJznU-1681786426147)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/38a1827e-ea6a-4f2d-8a42-0474cdd3ab8f.png)]

例如,x ∈ {1, 2, 3, 4},其中x涉及四个类别。

然后,softmax 函数将得出结果(四舍五入到小数点后三位):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NHTMtjfS-1681786426147)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/ddd06e28-c0ca-46f2-922e-775c5abf106c.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F4Y5rD7x-1681786426147)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/d04e299e-1adc-4696-aa5a-8486bc517d7b.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NPwqqwwO-1681786426148)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/f9007c9f-f65e-4d0f-9639-f971a340e327.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pbnJ1g4S-1681786426148)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/34d148f9-2312-43f7-9510-e342db0e621b.png)]

因此,我们看到了所有类别的概率。 由于每个分类器的输出都需要所有分类的概率值,因此 softmax 函数成为分类器外层激活函数的最佳候选者。

整流线性单元函数

整流线性单元(更称为 ReLU)是使用最广泛的激活函数:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aAw7xhNX-1681786426148)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/06cd6c36-3f74-4314-b132-e936dc4ddd03.png)]

ReLU 函数具有非线性的优势。 因此,反向传播很容易,因此可以堆叠由 ReLU 函数激活的多个隐藏层,其中对于x <= 0f(x) = 0,对于x > 0f(x) = x

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PLhqqxGo-1681786426148)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/a3b9045b-2a4e-4e1f-9414-8b330464d72a.png)]

ReLU 函数相对于其他激活函数的主要优势在于,它不会同时激活所有神经元。 这可以从前面的 ReLU 函数图中观察到,在图中我们可以看到,如果输入为负,则输出零,而神经元不会激活。 这导致网络稀疏,并且计算快速简便。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i512YKuU-1681786426149)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/b6cdb514-2155-4640-a257-7b5a91ad8668.png)]

ReLU 的导数图,对于x <= 0f'(x) = 1,对于x > 0f'(x) = 0

查看前面的 ReLU 的前面的梯度图,我们可以看到该图的负侧显示了一个恒定的零。 因此,落入该区域的激活将具有零梯度,因此权重将不会更新。 这将导致节点/神经元不活动,因为它们将不会学习。 为了解决这个问题,我们提供了泄漏的 ReLU,将其修改为:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tZzQEKag-1681786426149)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/484eab03-e2e4-4978-aeb4-eb940f10ba9b.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H2kLpzaQ-1681786426149)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/c3952215-4eed-4655-855c-80bd5a590460.png)]

这防止了梯度在负侧变为零,并且由于a的值较低,权重训练持续但缓慢地进行。

如何选择正确的激活函数

激活函数取决于问题陈述的目的和所关注的属性。 一些推断如下:

  • Sigmoid 函数在浅层网络和二分类器的情况下效果很好。 较深的网络可能会导致梯度消失。
  • ReLU 函数是使用最广泛的函数,请尝试使用 Leaky ReLU 以避免神经元死亡的情况。 因此,从 ReLU 开始,如果 ReLU 无法提供良好的结果,则移至另一个激活函数。
  • 在外层使用 softmax 进行多类分类。
  • 避免在外层使用 ReLU。

作为神经网络的逻辑回归

逻辑回归是一种分类器算法。 在这里,我们尝试预测输出类别的概率。 具有最高概率的类别将成为预测输出。 使用交叉熵计算实际输出和预测输出之间的误差,并通过反向传播将其最小化。 检查下图,了解二进制逻辑回归和多类逻辑回归。 区别基于问题陈述。 如果输出类的唯一数量为两个,则称为二分类;如果输出类的唯一数量为两个以上,则称为多类分类。 如果没有隐藏层,我们将 Sigmoid 函数用于二分类,并获得用于二进制逻辑回归的架构。 类似地,如果没有隐藏层,并且我们使用 softmax 函数进行多类分类,则可以得到多类逻辑回归的架构。

现在出现一个问题,为什么不使用 Sigmoid 函数进行多类逻辑回归?

对于任何神经网络的所有预测输出层而言,答案都是正确的,即预测输出应遵循概率分布。 正常来说,输出具有N个类别。 对于具有例如d维的输入数据,这将导致N个概率。 因此,此一个输入数据的N概率之和应为 1,并且这些概率中的每一个应在 0 到 1 之间(含 0 和 1)。

一方面,在大多数情况下,N个不同类别的 Sigmoid 函数之和可能不为 1。 因此,在二进制的情况下,使用 Sigmoid 函数来获得一个类别的概率,即p(y = 1 | x),而对于另一个类别,则是概率,即p(y = 0 | x) = 1 - p(y = 1 | x)。 另一方面,softmax 函数的输出是满足概率分布属性的值。 在图中,σ指的是 Sigmoid 函数:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nNSEEFie-1681786426149)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/b2853099-2f91-4eee-a8aa-958bd42e566f.png)]

还会出现一个后续问题:如果在二进制逻辑回归中使用 softmax 怎么办?

如前所述,只要您的预测输出遵循概率分布规则,一切都会很好。 稍后,我们将讨论交叉熵以及概率分布作为解决任何机器学习问题(尤其是处理分类任务)的基础的重要性。

如果分布中所有值的概率在 0 到 1 之间(含 0 和 1)且所有这些概率的总和必须为 1,则该概率分布有效。

逻辑回归可以在很小的神经网络中查看。 让我们尝试逐步实现二进制逻辑回归,如下所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E403roY6-1681786426149)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/6ed20793-896a-4ad7-a4ce-0c8bb73a334c.png)]

符号

假设数据的格式为(x^(i), y^(i)),其中:

  • x^(i) ∈ Ry^(i) ∈ {0, 1}(类数为 2,因为它是二分类)
  • x^(i)n维,即x^(i) = {x[1]^(i), x[2]^(i), ..., x[n]^(i)}(请参见上图)
  • 训练示例的数量是m。 因此,训练集如下所示:
    • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rymB6qIY-1681786426150)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/21aef233-5c4f-4455-90f3-619163f54e10.png)]
    • m为训练数据集的大小。
    • 并且,由于x = {x[1], x[2], ..., x[m]},其中每个x^(i) = {x[1]^(i), x[2]^(i), ..., x[m]^(i)}
    • 因此,X是大小为n * m的矩阵,即特征数*训练示例数
    • Y = [y^(1), y^(2), ..., y^(m)]m个输出的向量,其中每个y^(i) ∈ {0, 1}
    • 参数:权重W ∈ R和偏差b ∈ RW = {W[1], W[2], ..., W[n]},其中W[i]b是标量值。

目的

任何监督分类学习算法的目的都是以较高的概率预测正确的类别。 因此,对于每个给定的x^(i),我们必须计算预测的输出,即概率y_hat^(i) = P(y^(i) = 1 | x^(i))。 因此,y_hat^(i) ∈ [0, 1]

参考上图中的二进制逻辑回归:

  • 预测的输出,即y_hat^(i) = σ(Wx^(i) b)。 在此,Sigmoid 函数将Wx^(i) b的值缩小为 0 到 1。
  • 这意味着,当Wx^(i) b -> ∞时,Sigmoid 函数,即σ(Wx^(i) b) -> 1
  • Wx^(i) b -> -∞时,它的 Sigmoid 函数即σ(Wx^(i) b) -> 0

一旦我们计算出y_hat,即预测的输出,就完成了前向传播任务。 现在,我们将使用成本函数计算误差值,并尝试通过梯度下降来更改参数Wb的值,从而反向传播以最小化误差值。

成本函数

代价函数是一个度量,它确定关于实际训练输出和预测输出执行的机器学习算法的好坏。 如果您还记得线性回归,其中误差平方和用作损失函数,即L(y^(i), y_hat^(i)) = 1/2 (y^(i) - y_hat^(i))²。 这在凸曲线上效果更好,但是在分类的情况下,曲线是非凸曲线; 结果,梯度下降效果不好,也不会趋于全局最优。 因此,我们使用交叉熵损失作为成本函数,它更适合分类任务。

交叉熵作为损失函数(对于第i个输入数据),即:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1qWM9Y4r-1681786426150)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/9c987b8f-71cb-4bd1-98be-7b8f3e3eb331.png)]

其中C表示不同的输出类别。因此,成本函数=平均交叉熵损失(对于整个数据集),即:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-57nldX6P-1681786426150)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/9e200f44-5fdf-4d20-8d0f-b9d08be64ffa.png)]

在二元逻辑回归中,输出类别只有两个,即 0 和 1,因为类别值的总和始终为 1。因此(对于第i个输入数据),如果一个类别为y^(i),则其他输出类别将是1 - y^(i)。 类似地,由于类别y^(i)的概率为y_hat^(i)(预测),因此另一类别(即1 - y^(i))的概率将为1 - y_hat^(i)

因此,损失函数修改为:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UILT1lcz-1681786426150)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/f38e0490-568b-4304-bd59-cfed5d179d13.png)]

其中:

  • 如果y^(i) = 1,即L(y^(i), y_hat^(i)) = -log y_hat^(i)。 因此,为了使L最小化,y_hat^(i)应该较大,即接近 1。
  • 如果y^(i) = 0,即L(y^(i), y_hat^(i)) = -log(1 - y_hat^(i))。 因此,为了使L最小化,y_hat^(i)应该较小,即接近于 0。

损失函数适用于单个示例,而成本函数适用于整个训练批量。 因此,这种情况下的成本函数为:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UgbWHtiI-1681786426150)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/e07efd45-fd71-41ac-9589-0b916252baf6.png)]

梯度下降算法

梯度下降算法是一种使用一阶导数查找函数最小值的优化算法,也就是说,我们仅将函数的参数微分为一阶。 在此,梯度下降算法的目的是使关于Wb的成本函数J(W, b)最小化。

此方法包括以下步骤,可进行多次迭代以最小化J(W, b)

  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0hoVZ6o5-1681786426151)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/322d38ac-27f4-4518-8a17-f58b391472d5.png)]
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ovHzLZgR-1681786426151)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/fbdcd2fc-ce0e-4eb7-a681-cc83616c6af7.png)]

上式中使用的α是指学习率。 学习率是学习智能体适应新知识的速度。 因此,α,即学习率是需要作为标量值或时间函数分配的超参数。 这样,在每次迭代中,Wb的值都按照上式进行更新,直到成本函数的值达到可接受的最小值为止。

梯度下降算法意味着沿斜率向下移动。 曲线的斜率由关于参数的成本函数J表示。 斜率(即斜率)给出了正斜率(如果为正)和负斜率的方向。 因此,我们必须使用负号与斜率相乘,因为我们必须与增加斜率的方向相反并朝减小斜率的方向相反。

使用最佳学习率α,可以控制下降,并且我们不会过度降低局部最小值。 如果学习率α非常小,那么收敛将花费更多时间,而如果学习率α非常高,则由于迭代次数众多,它可能会过冲并错过最小值和偏差:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bcnmWpgF-1681786426151)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/6fb841aa-19f8-4076-8f53-668fdf30a60a.png)]

计算图

基本的神经网络由前向传播和后向传播组成。 结果,它由一系列步骤组成,这些步骤包括不同节点,权重和偏差的值,以及所有权重和偏差的成本函数导数。 为了跟踪这些过程,图片中出现了计算图。 无论神经网络的深度如何,计算图还可以跟踪链式规则的微分。

使用梯度下降法解决逻辑回归的步骤

将我们刚刚介绍的所有构建模块放在一起,让我们尝试解决具有两个输入特征的二进制逻辑回归。

计算的基本步骤是:

  1. 计算Z^(i) = WX^(i) b, ∀i
  2. 计算y_hat^(i) = σ(Z^(i)), ∀i,预测输出
  3. 计算成本函数: [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-119YeKjS-1681786426151)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/5f23715e-3539-463e-abfc-3c9c24407512.png)]

假设我们有两个输入特征,即二维和m样本数据集。 因此,将是以下情况:

  1. X = {x[1], x[2]}
  2. 权重W = {w[1], w[2]}和偏差b
  3. 因此,z^(i) = w[1]x[1]^(i) w[2]x[2]^(i) by_hat^(i) = σ(z^(i))
  4. 计算J(W, b)(所有示例的平均损失)
  5. 计算关于w[1]w[2]b的导数,分别为∂J/∂w[1]∂J/∂w[2]∂J/∂b
  6. 按照前面的梯度下降部分所述修改w[1]w[2]b

先前m个样本数据集的伪代码为:

  1. 初始化学习率α和周期数e的值
  2. 循环遍历多个周期e(每次完整的数据集将分批通过)
  3. J(成本函数)和b(偏差)初始化为 0,对于W1W2,您可以使用随机正态或 xavier 初始化(在下一节中说明)

在此,ay_hatdw1∂J/∂w[1]dw2∂J/∂w[2]db∂J/∂b。 每个迭代都包含一个循环遍历m个示例的循环。

这里给出了相同的伪代码:

代码语言:javascript复制
w1 = xavier initialization, w2 = xavier initialization, e = 100, α = 0.0001
for j → 1 to e :
     J = 0, dw1 = 0, dw2 = 0, db = 0
     for i → 1 to m :
         z = w1x1[i]   w2x2[i]   b
         a = σ(z)
         J = J - [ y[i] log a   (1-y) log (1-a) ]
         dw1 = dw1   (a-y[i]) * x1[i] 
         dw2 = dw2   (a-y[i]) * x2[i]
         db = db   (a-y[i])
     J = J / m
     dw1 = dw1 / m
     dw2 = dw2 / m
     db = db / m
     w1 = w1 - α * dw1
     w2 = w2 - α * dw2 

什么是 Xavier 初始化?

Xavier 初始化是神经网络中权重的初始化,是遵循高斯分布的随机变量,其中方差var(W)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nP4wsJYQ-1681786426151)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/f0d757fd-0f14-45c1-bbe8-64d82825de3a.png)]

其中,n_in是当前层中的单元数,即传入的信号单元,n_out是下一层中的单元数,即传出的结果信号单元。 简而言之,n_in * n_outW的形状。

为什么我们使用 xavier 初始化?

以下因素要求应用 xavier 初始化:

  • 如果网络中的权重开始很小,则大多数信号将收缩并在随后的层中激活函数处于休眠状态
  • 如果权重开始很大,则大多数信号将大量增长并通过后面各层的激活函数

因此,xavier 初始化有助于产生最佳权重,以使信号在最佳范围内,从而使信号变得既不会太小也不会太大的机会最小。

前述公式的推导超出了本书的范围。 请随时在此处进行搜索,并进行推导以获得更好的理解。

神经网络模型

神经网络模型类似于前面的逻辑回归模型。 唯一的区别是在输入层和输出层之间添加了隐藏层。 让我们考虑使用单个隐藏层神经网络进行分类,以了解该过程,如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VKJmpwUh-1681786426152)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/c223d6fe-857a-48ff-86a3-a9448e0bb397.png)]

在这里,第 0 层是输入层,第 1 层是隐藏层,第 2 层是输出层。 这也称为两层神经网络,这是由于以下事实:当我们计算神经网络中的层数时,我们并不将输入层视为第一层。 因此,将输入层视为第 0 层,然后连续的层将获得第 1 层,第 2 层的表示法,依此类推。

现在,想到一个基本问题:为什么输入和输出层之间的层称为隐藏层?

这是因为在训练集中不存在隐藏层中节点的值。 如我们所见,在每个节点上都会进行两次计算。 这些是:

  • 汇总来自先前各层的输入信号
  • 对聚合信号进行激活以创建更深层的内部表示,这些表示又是相应隐藏节点的值

参考上图,我们有三个输入特征x[1], x[2], x[3]。 显示值为 1 的节点被视为偏置单元。 除输出外,每一层通常都有一个偏置单元。 偏置单元可以看作是一个拦截项,在左右激活函数中起着重要作用。 请记住,隐藏层和其中的节点的数量是我们一开始定义的超参数。 在这里,我们将隐藏层数定义为 1,将隐藏节点的数数定义为 3,a[1], a[2], a[3]。 因此,可以说我们有三个输入单元,三个隐藏单元和三个输出单元(h[1], h[2], h[3],因为我们要预测的类别是三类)。 这将为我们提供与层关联的权重和偏差的形状。 例如,第 0 层具有 3 个单元,第 1 层具有 3 个单元。与第i层相关联的权重矩阵和偏差向量的形状如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-awVfG9fa-1681786426152)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/491169f7-46c4-469b-93cd-1cb8f4f01162.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Xkzhywoo-1681786426152)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/d407cb5e-ec47-4ab2-8f75-d0849c2468a7.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yl4o6tPx-1681786426152)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/6998e345-37c1-41b9-8253-7255e7962b32.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6qWmPyZC-1681786426152)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/baf09fc9-c37d-4f13-b269-46ee4fdb98fa.png)]

因此,形状如下:

  • w[0, 1]将是3x3b[0, 1]将是3
  • w[1, 2]将是3x1b[1, 2]将是1

现在,让我们了解以下符号:

  • w[a(i)]^d(i 1):此处,是指将第i层中的节点a连接到第i 1层中的节点d的权重值
  • b[i, i 1]^d:此处,它是指将第i层中的偏置单元节点连接到第i 1层中的节点d的偏置值。

因此,可以通过以下方式计算隐藏层中的节点:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KHzVGZ3X-1681786426152)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/82ab1371-fa81-4031-836d-e9c9ddcc6d15.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-48x7KXeA-1681786426153)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/0f4d3035-6a32-41ca-beda-fa89b8162a36.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B0KW5GGO-1681786426153)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/de1a8626-a125-4147-947e-5a9d005835c8.png)]

其中,f函数是指激活函数。 记住逻辑回归,其中我们分别使用 Sigmoid 和 softmax a 激活函数进行二进制和多类逻辑回归。

类似地,我们可以这样计算输出单元:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QJa8PkUT-1681786426153)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/89fd6d5b-2b5c-4f51-be1a-3fd44e83eddd.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H2s1ef1w-1681786426153)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/acf8e772-2262-4de6-a26c-63f142b893bc.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PWboF0uT-1681786426153)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/fc85f160-4f98-4e08-beee-ee3edf5a852b.png)]

这使我们结束了正向传播过程。 我们的下一个任务是通过反向传播训练神经网络(即训练权重和偏差参数)。

令实际输出类别为y[1], y[2], y[3]

回顾线性回归中的成本函数部分,我们使用交叉熵来表示成本函数。 由于成本函数定义为

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eCQAH5I8-1681786426154)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/fc10aaaf-bef5-473c-abaf-981aff989850.png)]

其中,C = 3y[c] = Y[c], y_hat[c] = h[c]m为示例数

由于这是一个分类问题,因此对于每个示例,输出将只有一个输出类别为 1,其余的将为零。 例如,对于i,它将是:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DPBrs33J-1681786426154)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/80e793d1-3d94-404f-9f37-49f8fef4fe63.png)]

因此,成本函数为:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UwmRbAV7-1681786426154)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/b3337baf-1416-4884-a76f-9c8cbf5c4ca0.png)]

现在,我们的目标是针对Wb将成本函数J最小化。 为了训练我们给定的神经网络,首先随机初始化Wb。 然后,我们将尝试通过梯度下降来优化J,在此我们将以以下方式以学习率α相应地更新Wb

  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UohLaBYI-1681786426154)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/3135cb59-4deb-442d-9eb4-aee81be280ad.png)]
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GvjKnpxm-1681786426154)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/7c7a767b-ffe7-461d-a077-0137dbc1af53.png)]

设置好此结构后,我们必须重复执行这些优化步骤(更新Wb),以进行多次迭代以训练我们的神经网络。

这将我们带到了神经网络基础的结尾,它构成了任何浅或深的神经网络的基本构建块。 我们的下一个前沿将是了解一些著名的深度神经网络架构,例如循环神经网络RNN)和卷积神经网络CNN)。 除此之外,我们还将介绍基准的深度神经网络架构,例如 AlexNet,VGG-net 和 Inception。

循环神经网络

循环神经网络,缩写为 RNN,用于序列数据的情况下,无论是作为输入,输出还是两者。 RNN 之所以如此有效,是因为它们的架构可以汇总来自过去数据集的学习,并将其与新数据一起使用以增强学习。 这样,它可以捕获事件的顺序,这在前馈神经网络或统计时间序列分析的早期方法中是不可能的。

考虑时间序列数据,例如股票市场,音频或视频数据集,其中事件的顺序非常重要。 因此,在这种情况下,除了从整个数据中进行集体学习之外,从时间上遇到的数据中学习的顺序也很重要。 这将有助于捕捉潜在趋势。

执行基于序列的学习的能力是使 RNN 高效的原因。 让我们退后一步,尝试了解问题所在。 考虑以下数据图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WTgvlyyO-1681786426155)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/fcde1c89-3462-4bca-b371-d51700ddbccd.png)]

假设您有一系列事件,类似于图中的事件,并且您希望在每个时间点都按照事件序列进行决策。 现在,如果您的序列相当稳定,则可以在任何时间步长使用具有相似权重的分类器,但这就是小故障。 如果您以不同的时间步长数据分别运行同一分类器,则对于不同的时间步长,它不会训练为相似的权重。 如果在包含所有时间步数据的整个数据集上运行单个分类器,则权重将相同,但是基于序列的学习会受到阻碍。 对于我们的解决方案,我们希望在不同的时间步长上共享权重,并利用到最后一个时间步长所学到的知识,如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v4jlOcEF-1681786426155)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/69f98fb4-8dca-49a3-aa61-a3136042c993.png)]

根据问题,我们已经了解到我们的神经网络应该能够考虑过去的经验。 可以在前面的示意图中看到这一概念,在第一部分中,它表明在每个时间步长上,网络训练权重都应考虑从过去学习的数据,而第二部分给出解决方案。 我们使用前一个时间步的分类器输出的状态表示作为输入,并使用新的时间步数据来学习当前状态表示。 状态表示可以定义为递归直到最后一个步骤为止所发生事件的集体学习(或总结)。 状态不是分类器的预测输出。 相反,当它受到 softmax 激活函数时,它将产生预测的输出。

为了回想起更远的地方,将需要更深的神经网络。 取而代之的是,我们将使用一个汇总过去的模型,并将该信息以及新信息提供给分类器。

因此,在循环神经网络中的任何时间步长t处,都会发生以下计算:

  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tr28dCNd-1681786426155)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/574a41ce-1848-4111-ba71-d166d62d5cff.png)]。
  • W[h]b[h]是随时间共享的权重和偏差。
  • tanh是激活函数f
  • [h[t-1]; x[t]]指这两个信息的连接。 假设您的输入x[t]的形状为nxd,即n样本/行和d尺寸/列,h[t-1]n*l。 然后,您的连接将得到形状为n*(d l)的矩阵。

由于任何隐藏状态h[i]的形状为n*l。 因此,W[h]的形状为(d l)*lb[h]的形状为l

以来,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LDfXfOwL-1681786426155)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/78c36997-ef55-439b-bfe0-6273c2895926.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PsouQ2vQ-1681786426155)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/e17a10dc-5665-4089-b3e2-d2ffd8a90d58.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TmAO5MCq-1681786426156)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/bd2c9bcd-56e4-437c-a288-7ced3b058ec7.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RTdYorWf-1681786426156)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/4ce9988c-11a9-4daa-885e-d1a5db1ee0fd.png)]

在给定的时间步长t中,这些操作构成 RNN 信元单元。 让我们在时间步t可视化 RNN 单元,如下所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wmlj3Q3o-1681786426156)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/93dd25ec-59c7-4c71-82b3-ce6a8f7fe21d.png)]

一旦完成计算直到最后一个时间步,我们的前向传播任务就完成了。 下一个任务是通过反向传播来训练我们的循环神经网络,以使总损失最小化。 一个这样的序列的总损失是所有时间步长上损失的总和,也就是说,给定X值序列及其对应的Y值输出序列,损失如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0kGdetkO-1681786426156)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/db611e16-6507-40be-a626-26d9096b2a64.png)]

因此,包含m个示例的整个数据集的成本函数为(其中k表示第k个示例):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lKJS2YJ7-1681786426156)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/bfc6e3b7-5070-40ed-8b99-c4e9eaa28e96.png)]

由于 RNN 合并了序列数据,因此反向传播会随时间扩展到反向传播。 在此,时间是一系列相互连接的有序时间步长,从而可以通过不同的时间步长进行反向传播。

长短期记忆网络

RNN 实际上无法处理长期依赖项。 随着输出序列中的输出数据点与输入序列中的输入数据点之间的距离增加,RNN 无法在两者之间连接信息。 这通常发生在基于文本的任务中,例如机器翻译,音频到文本,以及序列长度较长的更多任务。

长短期记忆网络也称为 LSTM(由 Hochreiter 和 Schmidhuber 引入),能够处理这些长期依赖性。 看一下此处给出的图像:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2j7qRwcV-1681786426156)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/3822e6c5-1056-44e1-ad43-c0b7c68a00e0.png)]

LSTM 的关键特征是单元状态C[t]。 这有助于信息保持不变。 我们将从遗忘门层f[t]开始,该层将最后一个隐藏状态,h[t-1]x[t]的连接作为输入,并训练一个神经网络,该神经网络对于其中的每个数字得出 0 到 1 之间的数字。 最后一个单元格状态C[t-1],其中 1 表示保留该值,0 表示忘记该值。 因此,该层将识别过去要忘记的信息,并保留哪些信息。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OluHOgHt-1681786426157)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/8e72b527-34c7-411d-ab7a-e134400a410a.png)]

接下来,我们进入输入门层i[t]和 tanh 层C_tilde[t],它们的任务是确定要添加到过去接收到的信息中的新信息以更新信息,即单元状态。 tanh 层创建新值的向量,而输入门层则标识用于信息更新的那些值中的哪一个。 将此新信息与使用“遗忘门”层f[t]保留的信息结合起来以更新我们的信息,即单元状态C[t]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MhI0CvuE-1681786426157)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/e014559b-eeb5-4a39-bdcc-87d91ea0c4dc.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wKoUKEAU-1681786426157)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/6d389234-7948-426d-b353-12be63a4b478.png)] = [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xg85vH7K-1681786426157)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/780ac43a-e452-447c-a6b1-9afc609fb5b1.png)]

因此,新的单元状态C[t]为:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w02zkCGh-1681786426158)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/ed6a7bac-a7a6-4b3c-94c3-dbe47b666824.png)]

最后,在输出门控层o[t]训练一个神经网络,返回单元状态C[t]的哪些值作为隐藏状态h[t]输出:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EuG48Txk-1681786426158)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/d9b18419-c661-4500-82ec-baadcd374dbe.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t9BBV1Zv-1681786426159)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/f684fde6-9611-4959-983f-73c663249b40.png)]

因此,LSTM 单元合并了最后一个单元状态C[t-1],最后一个隐藏状态h[t-1]和当前时间步输入x[t],并输出了更新后的单元状态C[t]和当前隐藏状态h[t]

LSTM 是一项突破,因为人们可以通过将 RNN 合并为单元单元来对 RNN 的显着结果进行基准测试。 这是朝着解决与长期依赖有关的问题迈出的重要一步。

卷积神经网络

卷积神经网络或卷积神经网络是在计算机视觉中提供成功结果的深度神经网络。 它们的灵感来自动物视皮层中神经元的组织和信号处理,也就是说,单个皮层神经元会对其相关小区域(视野)中的刺激做出反应,称为感受域 ,并且不同神经元的这些感受域完全重叠,覆盖了整个视野。

当输入空间中的输入包含相同类型的信息时,我们将共享权重并针对这些输入共同训练这些权重。 对于空间数据(例如图像),这种权重共享会导致 CNN。 同样,对于序列数据(例如文本),我们目睹了 RNN 中的这种权重共享。

CNN 在计算机视觉和自然语言处理领域具有广泛的应用。 就行业而言,Facebook 在其自动图像标记算法中使用了它,在图像搜索中使用了 Google,在产品推荐系统中使用了亚马逊,对家用提要进行个性化的 Pinterest,以及用于图像搜索和推荐的 Instagram。

就像神经网络中的神经元(或节点)从最后一层接收输入信号的加权聚合一样,最后一层接受激活函数导致输出。 然后我们反向传播以最小化损失函数。 这是应用于任何类型的神经网络的基本操作,因此适用于 CNN。

与输入为向量形式的神经网络不同,CNN 的输入图像是多通道的,即 RGB(三个通道:红色,绿色和蓝色)。 假设有一个像素大小为a×b的图像,则实际张量表示将为a×b×3形状。

假设您的图片与此处显示的图片相似:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mvv0NyFP-1681786426159)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/f6a21dcf-aeb2-40bf-86fd-e7201d42287e.png)]

它可以表示为具有宽度,高度的平板,并且由于具有 RGB 通道,因此其深度为 3。 现在,拍摄此图像的小片段,例如2×2,并在其上运行一个微型神经网络,其输出深度为k。 这将导致形状为1×1×k的图形表示。 现在,在不改变权重的情况下在整个图像上水平和垂直滑动此神经网络,将生成另一幅宽度,高度和深度为k的图像(也就是说,现在有k个通道)。

这种整合任务统称为卷积。 通常,ReLU 在以下神经网络中用作激活函数:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SJirlsLT-1681786426159)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/858a5158-bd38-4ab1-86da-57bdd947e8ed.png)]

在这里,我们将 3 个特征映射(即 RGB 通道)映射到k个特征映射

斑块在图像上的滑动运动称为步幅,而每次移动的像素数(水平或垂直)都称为步幅。 如果补丁没有超出图像空间,则将其视为有效填充。 另一方面,如果色块超出图像空间以映射色块大小,则在该空间之外的色块像素将填充零。 这称为相同填充

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RhLvF0hs-1681786426159)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/ca81209f-a26b-4c0e-ab94-77dceaa9bcab.png)]

CNN 架构由一系列这些卷积层组成。 如果这些卷积层中的跨步值大于 1,则会导致空间缩小。 因此,步幅,斑块大小和激活函数成为超参数。 除卷积层外,有时还会添加一个重要的层,称为池化层。 这将附近的所有卷积合并起来。 池化的一种形式称为最大池化

在最大池中,特征映射会查看补丁中的所有值,并返回其中的最大值。 因此,池大小(即池补丁/窗口大小)和池跨度是超参数。 下图描述了最大池化的概念:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZOR3NKaw-1681786426159)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/c026d9c2-b027-4ff9-8b53-1b4cb26dfe9b.png)]

最大池化通常会产生更准确的结果。 类似地,我们具有平均池化,其中取最大值而不是取池窗口中值的平均值,以提供特征映射的低分辨率视图。

通过合并和全连接层来操纵卷积层的超参数和排序,已经创建了许多不同的 CNN 变体,这些变体正在研究和工业领域中使用。 其中一些著名的是 LeNet-5,Alexnet,VGG-Net 和 Inception 模型。

LeNet-5 卷积神经网络

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Br7uH1Wm-1681786426160)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/8bad0a66-b329-41a9-966b-e3f7fac7dee8.png)]

LeNet-5 的架构,来自 LeCunn 等人的《用于文档识别的基于梯度的学习》

LeNet-5 是一个七级卷积神经网络,由 Yann LeCunn,Yoshua Bengio,Leon Bottou 和 Patrick Haffner 于 1998 年组成的团队发布,对数字进行了分类,银行将其用于识别支票上的手写数字。 这些层按以下顺序排序:

  • 输入图片 -> 卷积层 1(ReLU) -> 池化 1 -> 卷积层 2(ReLU) -> 池化 2 -> 全连接(ReLU)1 -> 全连接 2 -> 输出
  • LeNet-5 取得了令人瞩目的结果,但是处理高分辨率图像的能力需要更多的卷积层,例如在 AlexNet,VGG-Net 和 Inception 模型中。

AlexNet 模型

AlexNet 是 LeNet 的一种改进,由 SuperVision 小组设计,该小组由 Alex Krizhevsky,Geoffrey Hinton 和 Ilya Sutskever 组成。 在 2012 年 ImageNet 大规模视觉识别挑战赛中,AlexNet 的前 5 位错误率达到 15.3%,比第二名高出 10 个百分点,创造了历史记录。

该架构使用五个卷积层,三个最大池化层和最后三个全连接层,如下图所示。 该模型总共训练了 6000 万个参数,训练了 120 万张图像,在两个 NVIDIA GTX 580 3GB GPU 上花费了大约五到六天的时间。 下图显示了 AlexNet 模型:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sozLAmc6-1681786426160)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/d75582a1-ff11-41b9-8162-3c52f80b141f.png)]

Hinton 等人《用于 ImageNet 分类的深度卷积神经网络的 AlexNet 架构》。

卷积层 1 -> 最大池化层 1 -> 归一化层 1 -> 卷积层 2 -> 最大池化层 2 -> 归一化层 2 -> 卷积层 3 -> 卷积层 4 -> 卷积层 5 -> 最大池化层 3 -> 全连接 6 -> 全连接 7 -> 全连接 8 -> 输出

VGG-Net 模型

VGG-Net 由牛津大学视觉几何组VGG)的 Karen Simonyan 和 Andrew Zisserman 引入。 他们使用大小为3 x 3的小型卷积过滤器训练深度为 16 和 19 的网络。他们的团队分别在 ImageNet Challenge 2014 的本地化和分类任务中获得了第一和第二名。

通过向模型添加更多非线性来设计更深层的神经网络的想法导致合并了较小的过滤器,以确保网络没有太多参数。 在训练时,很难收敛模型,因此首先使用预先训练的简单神经网络模型来初始化较深架构的权重。 但是,现在我们可以直接使用 xavier 初始化方法,而无需训练神经网络来初始化权重。 由于模型的深度,训练起来很慢。

Inception 模型

Inception 由 Google 团队于 2014 年创建。其主要思想是创建更广泛的网络,同时限制参数数量并避免过拟合。 下图显示了完整的 Inception 模块:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X6PZa4Cx-1681786426160)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/8237a854-ea25-4837-a615-d71b2af70c96.png)]

Szegedy 等人的《从卷积开始深入研究》

它为单个输入应用多个卷积层,并输出每个卷积的堆叠输出。 使用的卷积大小主要为1x13x35x5。 这种架构允许您从相同大小的输入中提取多级特征。 早期版本也称为 GoogLeNet,该版本在 2014 年赢得了 ImageNet 挑战。

深度学习的局限性

深度神经网络是权重和偏置的黑盒,它们经过大量数据训练,可以通过内部表示找到隐藏的模式。 对于人类来说这将是不可能的,即使有可能,那么可伸缩性也是一个问题。 每个神经可能都有不同的权重。 因此,它们将具有不同的梯度。

训练在反向传播期间进行。 因此,训练的方向始终是从较后的层(输出/右侧)到较早的层(输入/左侧)。 与早期的层相比,这导致后面的层学习得很好。 网络越深入,情况恶化的程度就越大。 这引起了与深度学习相关的两个可能的问题,它们是:

  • 梯度消失问题
  • 梯度爆炸问题

梯度消失问题

当早期层中存在的神经元无法学习时,梯度梯度消失是与人工神经网络训练相关的问题之一,因为训练权重的梯度缩小到零。 这是由于神经网络的深度较大,加上带有导数的激活函数导致值较低。

请尝试以下步骤:

  1. 创建一个隐藏层神经网络
  2. 一层一层地添加更多隐藏层

我们观察了所有节点的梯度,发现当我们从后面的层移动到早期的层时,梯度值变得相对较小。 随着层数的增加,这种情况变得更糟。 这表明与后一层神经元相比,前一层神经元学习缓慢。 此条件称为梯度消失问题

梯度爆炸问题

梯度爆炸问题是与训练人工神经网络有关的另一个问题,当在早期层中存在的神经元的学习发生分歧时,因为梯度变得太大而导致权重发生严重变化,从而避免了收敛。 如果权重分配不正确,通常会发生这种情况。

在遵循针对梯度消失问题提到的步骤时,我们观察到梯度在早期层中爆炸,也就是说,它们变大了。 早期层发散的现象称为梯度爆炸问题

克服深度学习的局限性

这两个可能的问题可以通过以下方法克服:

  • 减少使用 Sigmoid 和 tanh 激活函数
  • 使用基于动量的随机梯度下降
  • 权重和偏差的正确初始化,例如 xavier 初始化
  • 正则化(将正则化损失与数据损失一起添加并最小化)

有关更多详细信息,以及消失和梯度爆炸的数学表示,您可以阅读本文:《智能信号:不稳定的深度学习,为什么以及如何解决它们》。

强化学习

强化学习是人工智能的一个分支,它与以状态空间和动作空间的形式感知环境信息并作用于环境的主体进行处理,从而产生新的状态并获得作为该动作的反馈的奖励 。 该收到的奖励被分配给新状态。 就像当我们必须最小化成本函数以训练我们的神经网络时一样,在这里强化学习智能体必须最大化整体奖励以找到解决特定任务的最佳策略。

这与有监督和无监督学习有何不同?

在监督学习中,训练数据集具有输入特征X及其对应的输出标签Y。 在此训练数据集上训练一个模型,将具有输入特征X'的测试用例作为输入给出,并且模型预测Y'

在无监督学习中,为训练目的给出了训练集的输入特征X。 没有关联的Y值。 目标是创建一个模型,该模型可通过了解底层模式来学习将数据隔离到不同的群集中,从而对数据进行分类以找到某种用途。 然后将该模型进一步用于输入特征X',以预测它们与聚类之一的相似性。

强化学习与有监督的和无监督的都不同。 强化学习可以指导智能体如何在现实世界中行动。 接口比训练向量更广泛,例如在有监督或无监督学习中。 这是整个环境,可以是真实的世界也可以是模拟的世界。 智能体以不同的方式进行训练,即目标是达到目标状态,这与监督学习的情况不同,后者的目的是使可能性最大化或成本最小化。

强化学习智能体会自动从环境中获得反馈,即从环境中获得奖励,这与监督学习中的标注需要耗时的人力不同。 强化学习的更大优势之一是,以目标的形式表述任何任务的目标有助于解决各种各样的问题。 例如,视频游戏智能体的目标是通过获得最高分数来赢得比赛。 这也有助于发现实现目标的新方法。 例如,当 AlphaGo 成为围棋世界冠军时,它发现了新颖独特的取胜方法。

强化学习智能体就像人一样。 人类进化非常缓慢。 智能体可以增强力量,但它可以很快完成任务。 就感知环境而言,人类和人工智能智能体都无法立即感知整个世界。 感知环境创建了一种状态,在该状态中,智能体执行操作并落入新状态,即新感知的环境不同于早期环境。 这将创建一个既可以有限也可以无限的状态空间。

对此技术感兴趣的最大部门是国防。 强化学习智能体人可以代替不仅步行,还能战斗并做出重要决定的士兵吗?

基本术语和约定

以下是与强化学习相关的基本术语:

  • 智能体:我们是通过编程创建的,因此它能够感知环境,执行动作,接收反馈并尝试获得最大回报。
  • 环境:智能体程序所在的世界。 它可以是真实的或模拟的。
  • 状态:智能体感知的环境感知或配置。 状态空间可以是有限的或无限的。
  • 奖励:座席采取任何措施后收到的反馈。 智能体的目标是最大化整体奖励,即立即和将来的奖励。 奖励是预先定义的。 因此,必须正确创建它们才能有效实现目标。
  • 操作:智能体在给定环境中能够执行的任何操作。 动作空间可以是有限的或无限的。
  • SAR 三元组:(状态,动作,奖励)称为 SAR 三元组,表示为(s, a, r)
  • 剧集:代表整个任务的一次完整运行。

让我们推断下图所示的约定:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FqHB5tOA-1681786426160)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/06a931de-864d-4ff2-baf5-7d2a08ea201d.png)]

每个任务都是一系列的 SAR 三元组。 我们从状态S(t)开始,执行动作A(t),从而收到奖励R(t 1),然后进入新状态S(t 1)。 当前状态和动作对为下一步提供了奖励。 由于S(t)A(t)产生了S(t 1),因此我们有了(当前状态,作用, 新状态),即[S(t), S(t), S(t 1)](s, a, s')

最优标准

最优性标准是根据数据创建的模型的拟合优度的度量。 例如,在监督分类学习算法中,我们将最大似然性作为最优性标准。 因此,根据问题陈述和客观最优标准不同。 在强化学习中,我们的主要目标是最大化未来的回报。 因此,我们有两个不同的最优性标准,分别是:

  • 值函数:根据未来可能的回报来量化状态
  • 策略:指导智能体在给定状态下应采取的措施

我们将在接下来的主题中详细讨论这两个方面。

最优值函数

智能体应该能够考虑立即和未来的回报。 因此,还会为每个遇到的状态分配一个值,该值也反映了此将来的信息。 这称为值函数。 延迟奖励的概念来了,目前,现在采取的行动将导致未来的潜在奖励。

V(s),即状态的值定义为,从该状态到后续状态直到智能体到达目标状态之前,所有将来在该状态下将要收到的奖励的期望值。 基本上,值函数告诉我们处于这种状态有多好。 值越高,状态越好。

分配给每个(s, a, s')三元组的奖励是固定的。 状态值不是这种情况。 它会随着剧集中的每个动作以及不同剧集而变化。

我们想到一个解决方案,而不是值函数,为什么我们不存储每个可能状态的知识呢?

答案很简单:这既耗时又昂贵,而且成本成倍增长。 因此,最好存储当前状态的知识,即V(s)

代码语言:javascript复制
V(s) = E[all future rewards discounted | S(t)=s]

关于值函数的更多详细信息将在第 3 章,“马尔可夫决策过程和部分可观察的 MDP”中进行介绍。

最优策略模型

策略被定义为指导智能体在不同状态下进行操作选择的模型。 策略被表示为ππ本质上是在特定状态下某种动作的概率:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EYhlyNc0-1681786426160)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/be42dad4-02e2-4f9e-8af8-bb8b677521d4.png)]

因此,策略图将提供给定特定状态的不同操作的概率集。 该策略与值函数一起创建了一个解决方案,可根据策略和状态的计算值来帮助智能体导航。

强化学习的 Q 学习方法

Q 学习是尝试学习在特定状态下向智能体提供的特定操作的值Q(s, a)。 考虑一个表,其中行数代表状态数,而列数代表动作数。 这称为 Q 表。 因此,我们必须学习值,以找出对于给定状态下的智能体而言,哪种行动最合适。

Q 学习涉及的步骤:

  1. 用统一的值(例如全零)初始化Q(s, a)的表。
  2. 观察当前状态s
  3. 通过 ε 贪婪或任何其他动作选择策略选择一个动作a,然后采取该动作
  4. 结果,收到了a的奖励r,并且感知到了新状态s'
  5. 使用以下 Bellman 公式更新表中(s, a)对的Q值: [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yThXgI9O-1681786426161)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/038ba71d-be57-48da-8e37-93730b0e0d76.png)] 其中γ是折扣因子。
  6. 然后,将当前状态的值设置为新状态,并重复该过程以完成一个剧集,即达到终端状态
  7. 运行多个剧集来训练智能体

为简化起见,我们可以说,给定状态s和动作a的 Q 值由当前奖励r以及新状态在其所有操作中的折扣(γ)最大Q最大值。 与当前奖励相比,折扣因子延迟了来自未来的奖励。 例如,今天的 100 奖励在将来值将超过 100。 同样,将来的 100 奖励的当前值必须不到 100。 因此,我们将折扣未来的奖励。 连续重复此更新过程会导致 Q 表值收敛到给定状态下给定操作的预期未来奖励的准确率量。

当状态空间和动作空间的数量增加时,很难维护 Q 表。 在现实世界中,状态空间无限大。 因此,需要另一种无需 Q 表即可生成Q(s, a)方法。 一种解决方案是用函数替换 Q 表。 该函数将状态作为向量形式的输入,并输出给定​​状态下所有动作的 Q 值向量。 该函数逼近器可以由神经网络表示,以预测 Q 值。 因此,当状态和动作空间变大时,我们可以添加更多的层并适合于深度神经网络,以更好地预测 Q 值,这对于 Q 表来说似乎是不可能的。 这样就产生了 Q 网络,如果使用了更深层的神经网络(例如卷积神经网络),那么它会导致深度 Q 网络DQN)。

有关 Q 学习和深度 Q 网络的更多详细信息,将在第 5 章, “Q 学习和深度 Q 网络”中进行介绍。

异步优势演员评论家

A3C 算法由 Google DeepMind 和 MILA 的联合团队于 2016 年 6 月发布。 它更简单并且具有更轻的框架,该框架使用异步梯度下降来优化深度神经网络。 它速度更快,并且能够在多核 CPU 而非 GPU 上显示出良好的结果。 A3C 的一大优势是它可以在连续动作空间和离散动作空间上工作。 结果,它为许多具有复杂状态和动作空间的新挑战性难题打开了门户。

我们将在此处重点讨论,但是我们将在第 6 章,“异步方法”中进行更深入的探讨。 让我们从名称开始,即异步优势演员评论家A3C)算法,然后将其解压缩以获得该算法的基本概述:

  • 异步:在 DQN 中,您还记得我们将神经网络与我们的智能体一起使用来预测动作。 这意味着只有一个智能体,并且它正在与一个环境交互。 A3C 所做的是创建智能体环境的多个副本,以使智能体更有效地学习。 A3C 具有一个全球网络和多个工作器智能体,其中每个智能体都有其自己的一组网络参数,并且每个参数都与其环境副本同时进行交互,而无需与另一个智能体的环境进行交互。 它比单个智能体更好的原因是每个智能体的经验独立于其他智能体的经验。 因此,所有工作器智能体的总体经验导致了各种各样的训练。
  • 演员评论家:演员评论家结合了值迭代和策略迭代的优势。 因此,网络将为给定状态 s 估计值函数V(s)和策略π(s)。 函数逼近器神经网络的顶部将有两个单独的全连接层,分别输出状态的值和状态策略。 智能体使用值,该值充当批评者来更新策略,即,智能角色。
  • 优势:策略梯度使用折扣收益告诉智能体该行动是好是坏。 用优势代替它不仅可以量化操作的好坏状态,而且可以更好地鼓励和劝阻操作(我们将在第 4 章,“策略梯度”中进行讨论)。

TensorFlow 和 OpenAI Gym 简介

TensorFlow 是 Google 的 Google Brain 团队创建的数学库。 由于其数据流编程,它已被用作研究和开发部门的深度学习库。 自 2015 年成立以来,TensorFlow 已发展成为一个非常庞大的社区。

OpenAI Gym 是一个由 OpenAI 团队创建的强化学习操场,旨在提供一个简单的接口,因为创建环境本身是强化学习中的繁琐任务。 它提供了一个很好的环境列表来测试您的强化学习算法,以便您可以对它们进行基准测试。

TensorFlow 中的基本计算

TensorFlow 的基础是我们在本章前面讨论过的计算图张量。 张量是 n 维向量。 因此,标量和矩阵变量也是张量。 在这里,我们将尝试从 TensorFlow 开始的一些基本计算。 请尝试在 python IDE(例如 Jupyter 笔记本)中实现本节。

有关 TensorFlow 的安装和依赖项,请参考以下链接。

通过以下命令导入tensorflow

代码语言:javascript复制
import tensorflow as tf

tf.zeros()tf.ones()是实例化基本张量的一些函数。 tf.zeros()采用张量形状(即元组),并返回该形状的张量,所有值均为零。 同样,tf.ones()采用张量形状,但返回仅包含一个形状的该张量。 在 python shell 中尝试以下命令来创建张量:

代码语言:javascript复制
>>> tf.zeros(3)

<tf.Tensor 'zeros:0' shape=(3,) dtype=float32>

>>>tf.ones(3)

<tf.Tensor 'ones:0' shape=(3,) dtype=float32>

如您所见,TensorFlow 返回对张量的引用,而不是张量的值。 为了获得该值,我们可以通过运行会话来使用eval()run()(张量对象的函数):

代码语言:javascript复制
>>> a = tf.zeros(3)
>>> with tf.Session() as sess:
        sess.run(a)
        a.eval()

array([0., 0.,0.], dtype=float32)

array([0., 0.,0.], dtype=float32)

接下来是tf.fill()tf.constant()方法,以创建具有一定形状和值的张量:

代码语言:javascript复制
>>> a = tf.fill((2,2),value=4.)
>>> b = tf.constant(4.,shape=(2,2))
>>> with tf.Session() as sess:
        sess.run(a)
        sess.run(b)

array([[ 4., 4.],
[ 4., 4.]], dtype=float32)

array([[ 4., 4.],
[ 4., 4.]], dtype=float32)

接下来,我们有一些函数可以随机初始化张量。 其中,最常用的是:

  • tf.random_normal:从指定平均值和标准差的正态分布中采样随机值
  • tf.random_uniform():从指定范围的均匀分布中采样随机值
代码语言:javascript复制
>>> a = tf.random_normal((2,2),mean=0,stddev=1)
>>> b = tf.random_uniform((2,2),minval=-3,maxval=3)
>>> with tf.Session() as sess:
        sess.run(a)
        sess.run(b)

array([[-0.31790468, 1.30740941],
[-0.52323157, -0.2980336 ]], dtype=float32)

array([[ 1.38419437, -2.91128755],
[-0.80171156, -0.84285879]], dtype=float32)

TensorFlow 中的变量是张量的持有者,并由函数tf.Variable()定义:

代码语言:javascript复制
>>> a = tf.Variable(tf.ones((2,2)))
>>> a

<tf.Variable 'Variable:0' shape=(2, 2) dtype=float32_ref>

如果使用变量,则评估失败,因为必须在会话中使用tf.global_variables_initializer明确初始化它们:

代码语言:javascript复制
>>> a = tf.Variable(tf.ones((2,2)))
>>> with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())
        a.eval()

array([[ 1., 1.],
[ 1., 1.]], dtype=float32)

队列中的下一个,我们有矩阵。 身份矩阵是对角线为 1 且其他位置为零的正方形矩阵。 这可以通过function tf.eye()完成:

代码语言:javascript复制
>>> id = tf.eye(4) #size of the square matrix = 4
>>> with tf.Session() as sess:
         sess.run(id)

array([[ 1., 0., 0., 0.],
[ 0., 1., 0., 0.],
[ 0., 0., 1., 0.],
[ 0., 0., 0., 1.]], dtype=float32)

同样,也有对角矩阵,其对角线的值和其他地方的零,如下所示:

代码语言:javascript复制
>>> a = tf.range(1,5,1)
>>> md = tf.diag(a)
>>> mdn = tf.diag([1,2,5,3,2])
>>> with tf.Session() as sess:
        sess.run(md)
        sess.run(mdn)

array([[1, 0, 0, 0],
[0, 2, 0, 0],
[0, 0, 3, 0],
[0, 0, 0, 4]], dtype=int32)

array([[1, 0, 0, 0, 0],
[0, 2, 0, 0, 0],
[0, 0, 5, 0, 0],
[0, 0, 0, 3, 0],
[0, 0, 0, 0, 2]], dtype=int32)

我们使用tf.matrix_transpose()函数对给定矩阵进行转置,如下所示:

代码语言:javascript复制
>>> a = tf.ones((2,3))
>>> b = tf.transpose(a)
>>> with tf.Session() as sess:
        sess.run(a)
        sess.run(b)

array([[ 1., 1., 1.],
[ 1., 1., 1.]], dtype=float32)

array([[ 1., 1.],
[ 1., 1.],
[ 1., 1.]], dtype=float32)

下一个矩阵运算是矩阵乘法函数,如下所示。 这是通过函数tf.matmul()完成的:

代码语言:javascript复制
>>> a = tf.ones((3,2))
>>> b = tf.ones((2,4))
>>> c = tf.matmul(a,b)
>>> with tf.Session() as sess:
        sess.run(a)
        sess.run(b)
        sess.run(c)

array([[ 1., 1.],
[ 1., 1.],
[ 1., 1.]], dtype=float32)

array([[ 1., 1., 1., 1.],
[ 1., 1., 1., 1.]], dtype=float32)

array([[ 2., 2., 2., 2.],
[ 2., 2., 2., 2.],
[ 2., 2., 2., 2.]], dtype=float32)

使用tf.reshape()函数将张量从一个重塑为另一个,如下所示:

代码语言:javascript复制
>>> a = tf.ones((2,4)) #initial shape is (2,4)
>>> b = tf.reshape(a,(8,)) # reshaping it to a vector of size 8. Thus shape is (8,)
>>> c = tf.reshape(a,(2,2,2)) #reshaping tensor a to shape (2,2,2)
>>> d = tf.reshape(b,(2,2,2)) #reshaping tensor b to shape (2,2,2) 
#####Thus, tensor 'c' and 'd' will be similar
>>> with tf.Session() as sess:
        sess.run(a)
        sess.run(b)
        sess.run(c)
        sess.run(d)

array([[ 1., 1., 1., 1.],
[ 1., 1., 1., 1.]], dtype=float32)

array([ 1., 1., 1., 1., 1., 1., 1., 1.], dtype=float32)

array([[[ 1., 1.],
[ 1., 1.]],
[[ 1., 1.],
[ 1., 1.]]], dtype=float32)
&gt;
array([[[ 1., 1.],
[ 1., 1.]],
[[ 1., 1.],
[ 1., 1.]]], dtype=float32)

TensorFlow 中的计算流程表示为一个计算图,作为tf.Graph的实例。 该图包含张量和操作对象,并跟踪涉及的一系列操作和张量。 图的默认实例可以通过tf.get_default_graph()获取:

代码语言:javascript复制
>>> tf.get_default_graph()

<tensorflow.python.framework.ops.Graph object at 0x7fa3e139b550>

在接下来的章节中,我们将在 TensorFlow 中探索复杂的操作,神经网络的创建以及更多内容。

OpenAI Gym 简介

由 OpenAI 团队创建的 OpenAI Gym 是一个在不同环境中运动的场所,您可以在其中开发和比较强化学习算法。 它与 TensorFlow 和 Theano 等深度学习库兼容。

OpenAI Gym 由两部分组成:

  • Gym 开源代码库:它包含许多环境,可以解决不同的测试问题,您可以在其中测试您的强化学习算法。 这足以满足状态和操作空间的信息。
  • OpenAI Gym 服务:这使您可以将座席的表现与其他经过训练的座席进行比较。

有关安装和依赖项,请参考以下链接。

在介绍了基础知识之后,现在我们将从下面的第 2 章“使用 OpenAI Gym 训练强化学习智能体”开始使用 OpenAI Gym 实现强化学习。

强化学习的先驱与突破

在继续进行所有编码之前,让我们阐明一些在深度强化学习领域中的先驱,行业领导者和研究突破。

David Silver

David Silver 博士的 H 指数为 30,是 Google DeepMind 强化学习研究团队的负责人,也是 AlphaGo 的首席研究员。 David 共同创立了 Elixir Studios,然后在艾伯塔大学获得了强化学习博士学位,在那里他共同介绍了第一个大师级9x9围棋程序中使用的算法。 此后,他成为伦敦大学学院的讲师。 在 2013 年加入全职之前,他曾为 DeepMind 担任顾问。David 领导了 AlphaGo 项目,该项目成为在围棋游戏中击败顶级职业玩家的第一个程序。

Pieter Abbeel

Pieter Abbeel 是加州大学伯克利分校的教授,也是 OpenAI 的研究科学家。 Pieter 在 Ng 的带领下完成了计算机科学博士学位。 他目前的研究重点是机器人技术和机器学习,尤其是深度强化学习,深度模仿学习,深度无监督学习,元学习,学习到学习和 AI 安全。 Pieter 还获得了 NIPS 2016 最佳论文奖。

Google DeepMind

Google DeepMind 是一家英国人工智能公司,成立于 2010 年 9 月,并于 2014 年被 Google 收购。它们是深度强化学习和神经图灵机领域的行业领导者。 当 AlphaGo 计划击败了 9 段围棋选手 Lee Sedol 时,他们在 2016 年发布了新闻。 Google DeepMind 已将重点转移到两个大领域:能源和医疗保健。

以下是其一些项目:

  • 2016 年 7 月,Google DeepMind 与 Moorfields 眼科医院宣布合作,使用眼部扫描技术研究导致失明的疾病的早期征兆
  • 2016 年 8 月,Google DeepMind 宣布与伦敦大学学院医院合作研究和开发一种算法,以自动微分头部和颈部的健康组织和癌性组织
  • Google DeepMind AI 将 Google 的数据中心散热费用降低了 40%

AlphaGo 程序

正如 Google DeepMind 先前所述,AlphaGo 是一种计算机程序,它首先击败了 Lee Sedol,然后击败了当时在围棋中排名世界第一的 Ke Jie。 在 2017 年的改进版本中,AlphaGo Zero 发布,击败了 AlphaGo 100 场比赛至 0 局。

Libratus

Libratus 是由卡内基梅隆大学的 Tuomas Sandholm 教授带领的团队设计的人工智能计算机程序,用于玩扑克。 Libratus 和它的前身克劳迪科有着相同的含义,平衡。

2017 年 1 月,它在一场为期 20 天的马拉松比赛中击败了世界上最好的四名职业扑克选手,创造了历史。

尽管 Libratus 专注于玩扑克,但其设计师提到了它能够学习任何信息不完整且对手参与欺骗的游戏的能力。 结果,他们提出该系统可以应用于网络安全,商务谈判或医疗计划领域中的问题。

总结

在本章中,我们涵盖了构建模块,例如包括逻辑回归的浅层和深度神经网络,单隐藏层神经网络,RNN,LSTM,CNN 及其其他变体。 针对这些主题,我们还介绍了多个激活函数,正向和反向传播的工作方式以及与深度神经网络训练相关的问题,例如消失和梯度爆炸。

然后,我们涵盖了强化学习中非常基本的术语,我们将在接下来的章节中对其进行详细探讨。 这些是最优标准,即值函数和策略。 我们还了解了一些强化学习算法,例如 Q 学习和 A3C 算法。 然后,我们在 TensorFlow 框架中介绍了一些基本计算,这是 OpenAI Gym 的简介,还讨论了强化学习领域的一些有影响力的先驱者和研究突破。

在下一章中,我们将对几个 OpenAI Gym 框架环境实现基本的强化学习算法,并更好地理解 OpenAI Gym。

二、使用 OpenAI Gym 训练强化学习智能体

OpenAI Gym 提供了许多虚拟环境来训练您的强化学习智能体。 在强化学习中,最困难的任务是创造环境。 OpenAI Gym 通过提供许多玩具游戏环境为用户提供了一个平台,以训练和确定他们的强化学习智能体,从而为救援提供了帮助。

换句话说,它为强化学习智能体提供了一个学习和基准化他们的表现的场所,其中智能体必须学会从开始状态导航到目标状态,而不会遭受任何不幸。

因此,在本章中,我们将学习从 OpenAI Gym 了解和使用环境,并尝试实现基本的 Q 学习和 Q 网络,供我们的智能体学习。

OpenAI Gym 提供不同类型的环境。 它们如下:

  • 经典控制
  • 算法化
  • Atari
  • 棋盘游戏
  • Box2D
  • 参数调整
  • MuJoCo
  • 玩具文本
  • Safety
  • Minecraft
  • PyGame 学习环境
  • 足球
  • Doom

有关这些广泛的环境类别及其环境游乐场的详细信息,请访问这个页面。

我们将在本章介绍以下主题:

  • OpenAI Gym 环境
  • 使用 OpenAI Gym 环境对智能体进行编程
  • 将 Q 网络用于实际应用

OpenAI Gym

为了下载并安装 OpenAI Gym,您可以使用以下任一选项:

代码语言:javascript复制
$ git clone https://github.com/openai/gym 
$ cd gym 
$ sudo pip install -e . # minimal install

这将进行最小安装。 您以后可以运行以下命令进行完整安装:

代码语言:javascript复制
$ sudo pip install -e .[all]

您还可以按以下说明将 Gym 作为不同 Python 版本的包来获取:

对于 Python 2.7,可以使用以下选项:

代码语言:javascript复制
$ sudo pip install gym              # minimal install
$ sudo pip install gym[all]         # full install 
$ sudo pip install gym[atari]       # for Atari specific environment installation

对于 Python 3.5,可以使用以下选项:

代码语言:javascript复制
$ sudo pip3 install gym              # minimal install
$ sudo pip3 install gym[all]         # full install
$ sudo pip install gym[atari]        # for Atari specific environment installation

了解 OpenAI Gym 环境

为了了解导入 Gym 包,加载环境以及与 OpenAI Gym 相关的其他重要功能的基础,这里是冰冻湖环境的示例。

通过以下方式加载冰冻湖环境:

代码语言:javascript复制
import Gym 
env = Gym.make('FrozenLake-v0')   #make function of Gym loads the specified environment

接下来,我们来重置环境。 在执行强化学习任务时,智能体会经历多个剧集的学习。 结果,在每个剧集开始时,都需要重置环境,使其恢复到初始状态,并且智能体从开始状态开始。 以下代码显示了重置环境的过程:

代码语言:javascript复制
import Gym 
env = Gym.make('FrozenLake-v0') 
s = env.reset()  # resets the environment and returns the start state as a value
print(s)

-----------
0                #initial state is 0

在执行每个操作之后,可能需要显示智能体在环境中的状态。 通过以下方式可视化该状态:

代码语言:javascript复制
env.render()

------------
SFFF
FHFH
FFFH
HFFG

前面的输出显示这是一个4 x 4网格的环境,即以前面的方式排列的 16 个状态,其中SHFG表示状态的不同形式,其中:

  • S:开始块
  • F:冻结块
  • H:块有孔
  • G:目标块

在较新版本的“体育馆”中,不能直接修改环境特征。 这可以通过以下方式解开环境参数来完成:

代码语言:javascript复制
env = env.unwrapped

每个环境都由状态空间和供智能体执行的操作空间定义。 为了构建增强学习智能体,了解状态空间和动作空间的类型(离散或连续)和大小非常重要:

代码语言:javascript复制
print(env.action_space)
print(env.action_space.n)

----------------
Discrete(4)
4 

Discrete(4)输出表示冰冻湖环境的动作空间是一组离散值,并且具有可由智能体执行的四个不同的动作。

代码语言:javascript复制
print(env.observation_space)
print(env.observation_space.n)

----------------
Discrete(16)
16

Discrete(16)输出表示冻结湖环境的观察(状态)空间是一组离散值,并具有 16 种要由智能体探索的不同状态。

使用 OpenAI Gym 环境对智能体编程

本节考虑的环境是 FrozenLake-v0。 有关环境的实际文档可以在这个页面中找到。

此环境由代表一个湖泊的4 x 4网格组成。 因此,我们有 16 个网格块,其中每个块可以是开始块(S),冻结块(F),目标块(G)或孔块(H)。 因此,智能体程序的目标是学会从头到尾进行导航而不会陷入困境:

代码语言:javascript复制
import Gym
env = Gym.make('FrozenLake-v0')    #loads the environment FrozenLake-v0
env.render()                       # will output the environment and position of the agent

-------------------
SFFF
FHFH
FFFH
HFFG

在任何给定状态下,智能体都有四个要执行的动作,分别是上,下,左和右。 除了导致目标状态的那一步外,每一步的奖励都是 0,那么奖励将是 1。我们从S状态开始,我们的目标是达到G状态而不以最优化的路径通过F状态降落到H状态。

Q 学习

现在,让我们尝试使用 Q 学习对增强学习智能体进行编程。 Q 学习由一个 Q 表组成,该表包含每个状态动作对的 Q 值。 表中的行数等于环境中的状态数,而列数等于操作数。 由于状态数为16,而动作数为4,因此此环境的 Q 表由16行和4列组成。 它的代码在这里给出:

代码语言:javascript复制
print("Number of actions : ",env.action_space.n)
print("Number of states : ",env.observation_space.n)

----------------------
Number of actions : 4 
Number of states : 16

Q 学习涉及的步骤如下:

  1. 用零初始化 Q 表(最终,更新将随着学习过程中所采取的每个操作收到的奖励而发生)。
  2. 更新一个状态-动作对的 Q 值,即Q(s, a)的方法是:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NAilf72I-1681786426161)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/f282edec-7cde-460d-bc23-87e02635cdf9.png)]

在此公式中:

  1. 通过按照步骤 2 中提到的公式更新 Q 值,该表收敛以获取给定状态下某个动作的准确值。

ε 贪婪方法

ε 贪婪是探索-利用困境的一种广泛使用的解决方案。 探索就是通过实验和研究来搜索和探索新的选择以产生新的值,而探索则是通过重复这些选择并提高其值来完善现有的选择。

ε 贪婪方法非常易于理解且易于实现:

代码语言:javascript复制
epsilon() = 0.05 or 0.1 #any small value between 0 to 1
#epsilon() is the probability of exploration

p = random number between 0 and 1

if p  epsilon() :
    pull a random action
else:
    pull current best action

最终,经过几次迭代,我们发现了每个状态中所有状态中的最佳操作,因为它可以选择探索新的随机操作以及利用现有操作并对其进行优化。

让我们尝试实现一种基本的 Q 学习算法,以使智能体学习如何在从头到尾的 16 个网格的冰冻湖面中导航,而不会陷入困境:

代码语言:javascript复制
# importing dependency libraries
from __future__ import print_function
import Gym
import numpy as np
import time

#Load the environment

env = Gym.make('FrozenLake-v0')

s = env.reset()
print("initial state : ",s)
print()

env.render()
print()

print(env.action_space) #number of actions
print(env.observation_space) #number of states
print()

print("Number of actions : ",env.action_space.n)
print("Number of states : ",env.observation_space.n)
print()

#Epsilon-Greedy approach for Exploration and Exploitation of the state-action spaces
def epsilon_greedy(Q,s,na):
    epsilon = 0.3
    p = np.random.uniform(low=0,high=1)
    #print(p)
    if p > epsilon:
        return np.argmax(Q[s,:])#say here,initial policy = for each state consider the action having highest Q-value
    else:
        return env.action_space.sample()

# Q-Learning Implementation

#Initializing Q-table with zeros
Q = np.zeros([env.observation_space.n,env.action_space.n])

#set hyperparameters
lr = 0.5 #learning rate
y = 0.9 #discount factor lambda
eps = 100000 #total episodes being 100000

for i in range(eps):
    s = env.reset()
    t = False
    while(True):
        a = epsilon_greedy(Q,s,env.action_space.n)
        s_,r,t,_ = env.step(a)
        if (r==0): 
            if t==True:
                r = -5 #to give negative rewards when holes turn up
                Q[s_] = np.ones(env.action_space.n)*r #in terminal state Q value equals the reward
            else:
                r = -1 #to give negative rewards to avoid long routes
        if (r==1):
                r = 100
                Q[s_] = np.ones(env.action_space.n)*r #in terminal state Q value equals the reward
        Q[s,a] = Q[s,a]   lr * (r   y*np.max(Q[s_,a]) - Q[s,a])
        s = s_ 
        if (t == True) :
            break

print("Q-table")
print(Q)
print()

print("Output after learning")
print()
#learning ends with the end of the above loop of several episodes above
#let's check how much our agent has learned
s = env.reset()
env.render()
while(True):
    a = np.argmax(Q[s])
    s_,r,t,_ = env.step(a)
    print("===============")
    env.render()
    s = s_
    if(t==True) :
        break
----------------------------------------------------------------------------------------------
<<OUTPUT>>

initial state : 0

SFFF
FHFH
FFFH
HFFG

Discrete(4)
Discrete(16)

Number of actions : 4
Number of states : 16

Q-table
[[  -9.85448046   -7.4657981    -9.59584501  -10.        ]
 [  -9.53200011   -9.54250775   -9.10115662  -10.        ]
 [  -9.65308982   -9.51359977   -7.52052469  -10.        ]
 [  -9.69762313   -9.5540111    -9.56571455  -10.        ]
 [  -9.82319854   -4.83823005   -9.56441915   -9.74234959]
 [  -5.           -5.           -5.           -5.        ]
 [  -9.6554905    -9.44717167   -7.35077759   -9.77885057]
 [  -5.           -5.           -5.           -5.        ]
 [  -9.66012445   -4.28223592   -9.48312882   -9.76812285]
 [  -9.59664264    9.60799515   -4.48137699   -9.61956668]
 [  -9.71057124   -5.6863911    -2.04563412   -9.75341962]
 [  -5.           -5.           -5.           -5.        ]
 [  -5.           -5.           -5.           -5.        ]
 [  -9.54737964   22.84803205   18.17841481   -9.45516929]
 [  -9.69494035   34.16859049   72.04055782   40.62254838]
 [ 100.          100.          100.          100.        ]]

Output after learning

SFFF
FHFH
FFFH
HFFG
===============
  (Down)
SFFF
FHFH
FFFH
HFFG
===============
  (Down)
SFFF
FHFH
FFFH
HFFG
===============
  (Right)
SFFF
FHFH
FFFH
HFFG
===============
  (Right)
SFFF
FHFH
FFFH
HFFG
===============
  (Right)
SFFF
FHFH
FFFH
HFFG
===============
 (Right)
SFFF
FHFH
FFFH
HFFG
===============
  (Right)
SFFF
FHFH
FFFH
HFFG
===============
  (Right)
SFFF
FHFH
FFFH
HFFG
===============
  (Right)
SFFF
FHFH
FFFH
HFFG
===============
  (Right)
SFFF
FHFH
FFFH
HFFG
===============
  (Right)
SFFF
FHFH
FFFH
HFFG
===============
  (Right)
SFFF
FHFH
FFFH
HFFG

将 Q 网络用于实际应用

维护少数状态的表是可能的,但在现实世界中,状态会变得无限。 因此,需要一种解决方案,其合并状态信息并输出动作的 Q 值而不使用 Q 表。 这是神经网络充当函数逼近器的地方,该函数逼近器针对不同状态信息的数据及其所有动作的相应 Q 值进行训练,从而使它们能够预测任何新状态信息输入的 Q 值。 用于预测 Q 值而不是使用 Q 表的神经网络称为 Q 网络。

在这里,对于FrozenLake-v0环境,让我们使用一个将状态信息作为输入的单个神经网络,其中状态信息表示为一个1 x 状态形状的单热编码向量(此处为1 x 16)并输出形状为1 x 动作数的向量(此处为1 x 4)。 输出是所有操作的 Q 值:

代码语言:javascript复制
# considering there are 16 states numbered from state 0 to state 15, then state number 4 will be # represented in one hot encoded vector as
input_state = [0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0]

通过添加更多隐藏层和不同激活函数的选择,Q 网络绝对比 Q 表具有许多优势。 与 Q 表不同,在 Q 网络中,通过最小化反向传播的损失来更新 Q 值。 损失函数由下式给出:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gUlWSj3H-1681786426161)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/2aa82c3f-b95f-402e-998d-529bfbadff7d.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wMD7p3ov-1681786426161)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/d8af6b5f-f92c-4f40-84de-37dedd116228.png)]

让我们尝试用 Python 来实现这一点,并学习如何实现基本的 Q 网络算法,以使智能体从头到尾在整个 16 个网格的冰冻湖面中导航,而不会陷入困境:

代码语言:javascript复制
# importing dependency libraries
from __future__ import print_function
import Gym
import numpy as np
import tensorflow as tf
import random

# Load the Environment
env = Gym.make('FrozenLake-v0')

# Q - Network Implementation

## Creating Neural Network

tf.reset_default_graph()
# tensors for inputs, weights, biases, Qtarget
inputs = tf.placeholder(shape=[None,env.observation_space.n],dtype=tf.float32)
W = tf.get_variable(name="W",dtype=tf.float32,shape=[env.observation_space.n,env.action_space.n],initializer=tf.contrib.layers.xavier_initializer())
b = tf.Variable(tf.zeros(shape=[env.action_space.n]),dtype=tf.float32)

qpred = tf.add(tf.matmul(inputs,W),b)
apred = tf.argmax(qpred,1)

qtar = tf.placeholder(shape=[1,env.action_space.n],dtype=tf.float32)
loss = tf.reduce_sum(tf.square(qtar-qpred))

train = tf.train.AdamOptimizer(learning_rate=0.001)
minimizer = train.minimize(loss)

## Training the neural network

init = tf.global_variables_initializer() #initializing tensor variables
#initializing parameters
y = 0.5 #discount factor
e = 0.3 #epsilon value for epsilon-greedy task
episodes = 10000 #total number of episodes

with tf.Session() as sess:
    sess.run(init)
    for i in range(episodes):
        s = env.reset() #resetting the environment at the start of each episode
        r_total = 0 #to calculate the sum of rewards in the current episode
        while(True):
            #running the Q-network created above
            a_pred,q_pred = sess.run([apred,qpred],feed_dict={inputs:np.identity(env.observation_space.n)[s:s 1]})
            #a_pred is the action prediction by the neural network
 #q_pred contains q_values of the actions at current state 's'
            if np.random.uniform(low=0,high=1) < e: #performing epsilon-greedy here
                a_pred[0] = env.action_space.sample()
                #exploring different action by randomly assigning them as the next action
            s_,r,t,_ = env.step(a_pred[0]) #action taken and new state 's_' is encountered with a feedback reward 'r'
            if r==0: 
                if t==True:
                    r=-5 #if hole make the reward more negative
                else:
                    r=-1 #if block is fine/frozen then give slight negative reward to optimize the path
            if r==1:
                r=5 #good positive goat state reward

            q_pred_new = sess.run(qpred,feed_dict={inputs:np.identity(env.observation_space.n)[s_:s_ 1]})
            #q_pred_new contains q_values of the actions at the new state 

            #update the Q-target value for action taken
            targetQ = q_pred
            max_qpredn = np.max(q_pred_new)
            targetQ[0,a_pred[0]] = r   y*max_qpredn
            #this gives our targetQ

 #train the neural network to minimize the loss
            _ = sess.run(minimizer,feed_dict={inputs:np.identity(env.observation_space.n)[s:s 1],qtar:targetQ})

            s=s_
            if t==True:
                break

    #learning ends with the end of the above loop of several episodes above
 #let's check how much our agent has learned
    print("Output after learning")
    print()
    s = env.reset()
    env.render()
    while(True):
        a = sess.run(apred,feed_dict={inputs:np.identity(env.observation_space.n)[s:s 1]})
        s_,r,t,_ = env.step(a[0])
        print("===============")
        env.render()
        s = s_
        if t==True:
            break
-----------------------------------------------------------------------------------------------
<<OUTPUT>>

Output after learning

SFFF
FHFH
FFFH
HFFG
===============
 (Down)
SFFF
FHFH
FFFH
HFFG
===============
 (Left)
SFFF
FHFH
FFFH
HFFG
===============
 (Up)
SFFF
FHFH
FFFH
HFFG
===============
 (Down)
SFFF
FHFH
FFFH
HFFG
===============
 (Right)
SFFF
FHFH
FFFH
HFFG
===============
 (Right)
SFFF
FHFH
FFFH
HFFG
===============
 (Up)
SFFF
FHFH
FFFH
HFFG

Q 学习和 Q 网络都需要付出一定的稳定性。 在某些情况下,如果给定的 Q 值超参数集未收敛,但具有相同的超参数,则有时会出现收敛。 这是由于这些学习方法的不稳定。 为了解决这个问题,如果状态空间较小,则应定义更好的初始策略(此处为给定状态的最大 Q 值)。 此外,超参数,尤其是学习率,折扣因子和ε值,起着重要的作用。 因此,必须正确初始化这些值。

由于增加了状态空间,因此与 Q 学习相比,Q 网络具有更大的灵活性。 Q 网络中的深度神经网络可能会导致更好的学习和表现。 就使用深度状态 Q 网络玩 Atari 而言,有许多调整,我们将在接下来的章节中进行讨论。

总结

在本章中,我们学习了 OpenAI Gym,包括安装各种重要功能以加载,渲染和理解环境状态操作空间。 我们了解了 ε 贪婪方法作为探索与利用难题的解决方案,并尝试实现基本的 Q 学习和 Q 网络算法,以训练增强学习智能体从 OpenAI Gym 导航环境。

在下一章中,我们将介绍强化学习中最基本的概念,包括马尔可夫决策过程MDP),贝尔曼方程和 Markov Chain Monte Carlo。

三、马尔可夫决策过程

马尔可夫决策过程(通常称为 MDP)是一种强化学习的方法,可用于在网格世界环境中进行决策。 Gridworld 环境由网格形式的状态组成,例如 OpenAI Gym 的 FrozenLake-v0 环境中的状态,我们在上一章中试图进行研究和解决。

MDP 试图通过将网格划分为状态,动作,模型/转换模型和奖励来捕获网格形式的世界。 MDP 的解决方案称为策略,目标是为该 MDP 任务找到最佳策略。

因此,任何遵循 Markov 属性的,由一组状态,动作和奖励组成的强化学习任务都将被视为 MDP。

在本章中,我们将深入研究 MDP,状态,动作,奖励,策略,以及如何使用贝尔曼方程求解它们。 此外,我们将介绍部分可观察的 MDP 的基础知识及其解决的复杂性。 我们还将介绍探索利用难题和著名的 E3(开发,探索或利用)算法。 然后我们将进入引人入胜的部分,在该部分中,我们将为智能体编程,以使用 MDP 原理学习和玩乒乓球。

我们将在本章介绍以下主题:

  • 马尔可夫决策过程
  • 部分可观察的马尔可夫决策过程
  • 使用 MDP 训练 FrozenLake-v0 环境

马尔可夫决策过程

如前所述,MDP 是网格世界环境中的强化学习方法,其中包含状态,动作和奖励集,遵循马尔可夫属性以获得最佳策略。 MDP 被定义为以下各项的集合:

  • 状态S
  • 动作A(s), A
  • 转移模型T(s, a, s') ~ P(s' | s, a)
  • 奖励R(s), R(s, a), R(s, a, s')
  • 策略π(s) -> a[π*]是最佳策略

对于 MDP,环境是完全可观察的,也就是说,智能体在任何时间点所做的任何观察都足以做出最佳决策。 在部分可观察的环境中,智能体程序需要一个内存来存储过去的观察结果,以便做出最佳决策。

让我们尝试将其分解为不同的乐高积木,以了解整个过程的含义。

马尔可夫属性

简而言之,根据马尔可夫属性,为了了解不久的将来的信息(例如,在时间t 1),当前信息在时间t很重要。

给定序列[x[1], ..., x[t]],马尔可夫一阶表示P(x[t] | x[t-1], ..., x[1]) = P(x[t] | x[t-1]),即x[t]仅取决于x[t-1]。 因此,x[t 1]将仅取决于x[t]。 马尔可夫的二阶表示P(x[t] | x[t-1], ..., x[1]) = P(x[t] | x[t-1], x[t-2]),即x[t]仅取决于x[t-1]x[t-2]

在我们的上下文中,从现在开始,我们将遵循马尔科夫属性的一阶。 因此,如果新状态的概率x[t 1]仅取决于当前状态x[t],则我们可以将任何过程转换为马尔科夫属性,以便当前状态捕获并记住过去的属性和知识 。 因此,根据马尔可夫属性,世界(即环境)被认为是固定的,也就是说,世界中的规则是固定的。

S状态集

S状态集是构成环境的一组不同状态,表示为s。 状态是从环境获得的数据的特征表示。 因此,来自智能体传感器的任何输入都可以在状态形成中发挥重要作用。 状态空间可以是离散的也可以是连续的。 从开始状态开始,必须以最优化的路径达到目标状态,而不会在不良状态下结束(例如下图所示的红色状态)。

考虑以下网格世界,它具有 12 个离散状态,其中绿色网格是目标状态,红色是要避免的状态,黑色是一面碰到墙壁就会反弹的墙壁:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rcSdRlgq-1681786426162)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/8a30501f-c8d5-4a0c-a3a0-043d4829fb03.png)]

状态可以表示为 1、2,…,12 或由坐标(1, 1)(1, 2),…(3, 4)表示。

动作

动作是智能体可以在特定状态下执行或执行的操作。 换句话说,动作是允许智能体在给定环境中执行的一组操作。 像状态一样,动作也可以是离散的或连续的。

考虑以下具有 12 个离散状态和 4 个离散动作(UPDOWNRIGHTLEFT)的 gridworld 示例:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PmGOrnim-1681786426166)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/0afe050d-5e8b-4dbf-82c0-be526f469140.png)]

前面的示例将动作空间显示为离散的设置空间,即a ∈ A,其中A = {UP, DOWN, LEFT, RIGHT}。 也可以将其视为状态的函数,即a = A(s),其中根据状态函数来确定可能采取的行动。

转移模型

转移模型T(s, a, s')是三个变量的函数,它们是当前状态(s),动作(a) 以及新状态(s'),并定义了在环境中玩游戏的规则。 它给出了P(s' | s, a)的概率,也就是说,假设智能体采取了行动a,在给定状态s下降落到新的s'状态的概率。

转移模型在随机世界中起着至关重要的作用,与确定性世界的情况不同,在确定性世界中,除了确定的着陆状态之外,任何着陆状态的概率都为零。

让我们考虑以下环境(世界),并考虑确定和随机的不同情况:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TERyGvr1-1681786426166)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/86a85406-eeaf-450f-b93b-b11d8d1f3283.png)]

由于动作a ∈ A,其中A = {UP, DOWN, LEFT, RIGHT}

这两种情况的行为取决于某些因素:

  • 确定的环境:在确定的环境中,如果您采取某种行动,例如说UP,则您肯定会以概率 1 执行该行动。
  • 随机环境:在随机环境中,如果您执行相同的操作(例如说UP),则有一定的概率说 0.8 可以实际执行给定的操作,而有 0.1 的概率可以执行垂直于给定UP动作的动作(LEFTRIGHT)。 在此,对于s状态和UP动作转换模型,T(s', UP, s) = P(s' | s, UP) = 0.8

由于T(s, a, s') ~ P(s' | s, a),因此新状态的概率仅取决于当前状态和操作,而与过去状态无关。 因此,转移模型遵循一阶马尔可夫性质。

我们也可以说我们的宇宙也是一个随机环境,因为宇宙是由处于不同状态的原子组成的,这些原子由位置和速度定义。 每个原子执行的动作都会改变其状态,并导致宇宙发生变化。

奖励

状态的奖励量化了进入状态的有用性。 有三种不同的形式来表示奖励,即R(s)R(s, a)R(s, a, s'),但它们都是等效的。

对于特定环境,领域知识在不同状态的奖励分配中起着重要作用,因为奖励中的细微变化对于找到 MDP 问题的最佳解决方案确实很重要。

当采取某种行动时,我们会奖励智能体以两种方式。 他们是:

  • 信用分配问题:我们回顾过去,检查哪些活动导致了当前的奖励,即哪个活动获得了荣誉
  • 延迟奖励:相比之下,在当前状态下,我们检查要采取的行动会导致我们获得潜在的奖励

延迟的报酬形成了远见规划的想法。 因此,该概念被用于计算不同状态的预期奖励。 我们将在后面的部分中对此进行讨论。

策略

到现在为止,我们已经解决了造成 MDP 问题的块,即状态,动作,转移模型和奖励,现在是解决方案。 该策略是解决 MDP 问题的方法。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n3bR1yY2-1681786426166)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/ec5c8b7c-4b92-426c-97e0-673286364a05.png)]

策略是将状态作为输入并输出要采取的措施的函数。 因此,该策略是智能体必须遵守的命令。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jHAI6W3t-1681786426167)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/8b92a7d2-aa9e-465a-88d3-7880d412516e.png)]被称为最佳策略,它使预期收益最大化。 在采取的所有策略中,最优策略是一种优化的策略,用于最大化一生中已收到或预期收到的奖励金额。 对于 MDP,生命周期没有尽头,您必须确定结束时间。

因此,该策略不过是一个指南,它告诉您针对给定状态应采取的行动。 它不是计划,而是通过返回针对每个状态要采取的行动来揭示环境的基础计划。

奖励序列-假设

奖励序列在找到 MDP 问题的最佳策略中起着重要作用,但是有一些假设揭示了一系列奖励如何实现延迟奖励的概念。

无限的视野

第一个假设是无限的视野,即从起始状态到达目标状态的无限时间步长。 因此,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IIxffbkH-1681786426167)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/b8a7da56-da61-4a36-a9ff-098fc89c0451.png)]

策略函数未考虑剩余时间。 如果是有限期的话,那么策略应该是

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-euqWrhNb-1681786426167)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/4eecac99-2630-4813-9c1f-df9712f7780b.png)]

其中t是完成任务所需的时间。

因此,如果没有无限远景的假设,那么策略的概念就不会是固定不变的,即π(s) -> a,而是π(s, t) -> a

序列的效用

序列的效用是指当智能体经历状态序列时所获得的总报酬。 它表示为U(s[0], s[1], s[2], ...),其中s[0], s[1], s[2], ...表示状态序列。

第二个假设是,如果有两个效用U(s[0], s[1], s[2], ...)U(s[0], s'[1], s'[2], ...),则两个序列的开始状态都相同,并且

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U8sj6tBw-1681786426167)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/bb59d758-3f56-4513-bc9d-9d1d8292d013.png)]

然后,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QOObQMCg-1681786426167)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/0abf959d-3da4-4b0a-9ad2-af3631dd0381.png)]

这意味着,如果序列U(s[0], s[1], s[2], ...)的效用大于另一个函数U(s[0], s'[1], s'[2], ...),只要两个序列的起始状态都相同,那么没有该起始状态的序列将具有相同的不等式,即U(s[1], s[2], ...)将大于U(s'[1], s'[2], ...)。 这种假设称为偏好的平稳性。

因此,以下等式满足了偏好的平稳性,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tRz6UEK3-1681786426168)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/9d4cca6c-6221-43c3-98d4-b310017f62cf.png)]

上式中的求和足以满足我们的假设,但它有两个缺点,如下所示:

  • 无限的时间将使求和无限
  • 求和的大小写或顺序无异,即U(a, b, c)U(a, c, b)的效用值相同,即R(a) R(b) R(c)

因此,我们采用折扣系数γ来实现未来奖励的概念,即延迟奖励。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bt3CQkDj-1681786426168)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/82d34b1d-de39-4e24-99d9-7e440d9b03b4.png)]

其中0 <= γ < 1

让我们考虑所有R(s[t]),即在给定特定环境中来自不同状态的奖励,R_max是最大值,然后

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V8dP6FbP-1681786426168)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/108100d2-dded-4635-afa3-4246591385cb.png)]

怎么样? 让我们算出这个上限,

由于R(s[t]) <= R_max

因此,γ^t R(s[t]) <= γ^t R_max

因此,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bU52i6Ss-1681786426168)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/64493a99-de50-43fc-adb9-7369d0d0ae8e.png)]

让,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-99i7bnUP-1681786426168)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/ac4374e7-cbfa-4d55-a3f8-d55cac56d8e2.png)]

然后,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nMymD5ju-1681786426168)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/fe553776-03a4-41f5-8eda-01ccfa694b7a.png)]

从而,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Um5jntbp-1681786426169)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/8f07a082-d0fe-467d-9593-d0ecc0a1e2b6.png)]

因此,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8SR2T6fF-1681786426169)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/8f5b159e-922b-4faf-bdac-df4b213f271e.png)]

贝尔曼方程

由于最佳π*策略是使预期收益最大化的策略,因此,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3XHQcKli-1681786426169)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/2ccc6145-78d9-4ba3-a12e-19b454d98f69.png)],

其中E[...]表示从状态序列获得的报酬的期望值,智能体观察其是否遵循π策略。 因此,argmax[π]输出具有最高预期奖励的π策略。

同样,我们也可以计算状态为的策略效用,也就是说,如果我们处于s状态,则给定π策略,则, s状态的π策略,即U[π](s)将是从该状态开始的预期收益:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-crpMX9DQ-1681786426169)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/c99b8ca1-0cfb-419a-bc57-c9de97773b37.png)]

由于延迟奖励的概念,状态的立即奖励R(s)与状态的效用U(s)(即状态的最佳策略的效用U[π*](s))不同。 从现在开始,状态的效用U(s)将指该状态的最佳策略的效用(即U[π*](s))。

此外,最佳策略也可以被视为最大化预期效用的策略。 因此,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Uxe4Zm1W-1681786426169)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/c1a7f9d8-d174-4168-b470-01cde9c8ec20.png)]

其中,T(s, a, s')是转移概率,即P(s' | s, a);在对s状态采取a动作后,U(s')是新着陆状态的效用。

Σ[s'] T(s, a, s')U(s')指的是针对特定行动采取的所有可能的新状态结果的总和,然后无论哪个行动给出了Σ[s'] T(s, a, s')U(s')的最大值,该最大值被认为是最优策略的一部分,因此, 状态由以下 贝尔曼方程给出,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pGRfCUMa-1681786426170)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/0ef93e7c-5cc5-45ab-8baf-db2a07bd9e29.png)]

其中,R(s)是即时奖励,max[a] Σ[s'] T(s, a, s')U(s')是来自未来的奖励,也就是说,如果采取行动a,智能体可以从给定的s状态到达的s'状态的贴现效用。

解决贝尔曼方程来找到策略

假设在给定的环境中我们有n个状态,如果我们看到贝尔曼方程,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8AFPP0nb-1681786426170)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/5051739f-0788-416d-9182-38ae2169ffca.png)]

我们发现给出了n个状态; 因此,我们将拥有n方程,而n未知,但是max[a]函数使其变为非线性。 因此,我们不能将它们作为线性方程式求解。

因此,为了解决:

  • 从任意效用开始
  • 根据邻域更新效用,直到收敛为止,也就是说,根据给定状态的着陆状态的效用,使用贝尔曼方程更新状态的效用

多次迭代以得出状态的真实值。 迭代以收敛到状态的真实值的过程称为值迭代

对于游戏结束的终端状态,那些终端状态的效用等于智能体在进入终端状态时所获得的立即奖励。

让我们尝试通过示例来理解这一点。

使用贝尔曼方程的值迭代的示例

请考虑以下环境和给定的信息:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w4j3k01O-1681786426170)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/9115f073-9e8f-497e-9d96-c52a668dbef4.png)]

给定信息:

  • ACX是某些状态的名称。
  • 绿色状态是目标状态G,奖励为 1。
  • 红色状态为错误状态B,奖励为 -1,请尝试阻止您的智能体进入此状态
  • 因此,绿色和红色状态是终端状态,进入其中一个状态,游戏结束。 如果座席遇到绿色状态(即进球状态),则座席获胜;如果他们进入红色状态,则座席将输掉比赛。
  • γ = 1/2R(s) = -0.04(即,对除GB状态之外的所有状态的奖励是 -0.04),U[0](s) = 0(即,第一个时间步长的效用为 0,除GB状态外)。
  • 如果沿期望的方向前进,则过渡概率T(s, a, s')等于 0.8; 否则,如果垂直于所需方向,则各为 0.1。 例如,如果操作是UP,则概率为 0.8,智能体会UP,但概率为 0.1 时,它会RIGHT,而 0.1 则为LEFT

问题:

  1. 找到U[1](X),时间步骤 1 的X状态的效用,也就是说,智能体将进行一次迭代
  2. 同样,找到U[2](X)

解:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2mmXlosc-1681786426170)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/8ec27bf9-be88-4e5e-83e6-eb42a58b57b8.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HUjX4Znv-1681786426170)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/827fc525-4ce3-4ba0-8b43-6f8abfbc5592.png)]

R(X) = -0.04

| 动作 | s' | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UwQfNXvK-1681786426171)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/6fd891e0-ce75-462e-b4eb-a59aed75ce93.png)] | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j0MICw4G-1681786426171)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/22ac4a64-43bd-401c-92b7-a91376a60576.png)] | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Te2lVxFp-1681786426171)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/8795d96c-8164-4074-a370-586d0a1ff9a0.png)] | | — | — | — | | RIGHT | G | 0.8 | 1 | 0.8 x 1 = 0.8 | | RIGHT | C | 0.1 | 0 | 0.1 x 0 = 0 | | RIGHT | X | 0.1 | 0 | 0.1 x 0 = 0 |

因此,对于动作a = RIGHT

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pi6CffEe-1681786426171)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/42781834-798e-4673-a3ac-1b5601553691.png)]

| 动作 | s' | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x1gvYZCH-1681786426171)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/001a79e8-232c-416c-b1e7-c8d89cb12a90.png)] | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KxDakFcJ-1681786426171)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/9a391f2f-337e-46c8-b314-7be8f2e1effa.png)] | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hnsLa5sC-1681786426172)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/8eb44a6d-f977-4086-b239-2e592f166cbf.png)] | | — | — | — | | DOWN | C | 0.8 | 0 | 0.8 x 0 = 0 | | DOWN | G | 0.1 | 1 | 0.1 x 1 = 0.1 | | DOWN | A | 0.1 | 0 | 0.1 x 0 = 0 |

因此,对于动作a = DOWN

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HJAbxN9H-1681786426172)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/77918404-8e8e-4efa-9b97-1001d8bc8f4c.png)]

| 动作 | s' | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0xVOZbCh-1681786426172)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/acc2cb11-44c3-4325-9daa-68fe740d67cd.png)] | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZscAfMV5-1681786426172)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/b16f854f-055d-4ad4-898f-d6a84b08a775.png)] | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8wU8NOI8-1681786426172)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/55c7b3a8-f1c9-4e3f-ba20-69dcb80d4244.png)] | | — | — | — | | UP | X | 0.8 | 0 | 0.8 x 0 = 0 | | UP | G | 0.1 | 1 | 0.1 x 1 = 0.1 | | UP | A | 0.1 | 0 | 0.1 x 0 = 0 |

因此,对于动作a = UP

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8hWIycxd-1681786426173)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/98b807be-a36f-472a-97d1-0dad9bf4f92e.png)]

| 动作 | s' | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IklLg7LL-1681786426173)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/782bf5ec-3014-40c2-803d-dd2163340c53.png)] | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iNqwGzuG-1681786426173)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/90de6f01-fb3e-4558-b557-520defc86bab.png)] | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0uMSOmh9-1681786426173)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/7817ee4f-64fd-44ba-b2c4-7ad635cea69e.png)] | | — | — | — | | LEFT | A | 0.8 | 0 | 0.8 x 0 = 0 | | LEFT | X | 0.1 | 0 | 0.1 x 0 = 0 | | LEFT | C | 0.1 | 0 | 0.1 x 0 = 0 |

因此,对于动作a = LEFT

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V36XnjCh-1681786426173)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/9813b6c4-66cf-4dac-8764-0583ad1fff5f.png)]

因此,在所有动作中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xakRO7Sd-1681786426173)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/393b0f69-fd8a-4298-8b2a-cb478a41e930.png)]

因此,U[1](X) = -0.04 0.5 * 0.8 = 0.36,其中R(X) = -0.04γ = 1/2 = 0.5

类似地,计算U[1](A)U[1](C),我们得到R(X) = -0.04γ = 1/2 = 0.5

由于U[1](X) = 0.36, U[1](A) = -0.04, U[1](C) = -0.04, U[1](G) = 1, U[1](B) = -1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fsvtx4rm-1681786426174)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/bc2a8cef-a5a1-439c-a602-9d309920ca8d.png)]

R(X) = -0.04

动作

s'

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0UfhET7A-1681786426174)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/7eab76d9-3bb5-4e5c-baf6-76ac3ab4e7d7.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M0hsYfJK-1681786426174)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/22ac4a64-43bd-401c-92b7-a91376a60576.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SqryHDUl-1681786426174)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/8795d96c-8164-4074-a370-586d0a1ff9a0.png)]

RIGHT

G

0.8

1

0.8 x 1 = 0.8

RIGHT

C

0.1

-0.04

0.1 x -0.04 = -0.004

RIGHT

X

0.1

0.36

0.1 x 0.36 = 0.036

因此,对于动作a = RIGHT

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QuVaDXg9-1681786426174)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/8eabbfa6-ba45-4387-82fd-804d44792a6d.png)]

动作

s'

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jRuhdZNe-1681786426175)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/59516b7d-b5b9-4309-abff-a42760a70dbd.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gOiGQzLs-1681786426175)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/9a391f2f-337e-46c8-b314-7be8f2e1effa.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m7CoMEw7-1681786426175)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/8eb44a6d-f977-4086-b239-2e592f166cbf.png)]

DOWN

C

0.8

-0.04

0.8 x -0.04 = -0.032

DOWN

G

0.1

1

0.1 x 1 = 0.1

DOWN

A

0.1

-0.04

0.1 x -0.04 = -0.004

因此,对于动作a = DOWN

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TU6oxgJi-1681786426175)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/bf1e83c6-52e3-46d9-91fb-6be453900357.png)]

动作

s'

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b2B2Jf3p-1681786426175)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/aeb2ed22-fa4e-4c2d-9a9d-247d56150c02.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GSjidHxg-1681786426175)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/b16f854f-055d-4ad4-898f-d6a84b08a775.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ePqQcN9s-1681786426176)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/55c7b3a8-f1c9-4e3f-ba20-69dcb80d4244.png)]

UP

X

0.8

0.36

0.8 x 0.36 = 0.288

UP

G

0.1

1

0.1 x 1 = 0.1

UP

一种

0.1

-0.04

0.1 x -0.04 = -0.004

因此,对于动作a = UP

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5h8Gkwel-1681786426176)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/a1393b1e-af2c-4d45-a9d3-9f939826c082.png)]

动作

s'

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2y2Y78Ck-1681786426176)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/cafa86c3-0a4d-46b0-ade6-eb54a20d65f5.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KceOUz2q-1681786426176)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/90de6f01-fb3e-4558-b557-520defc86bab.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d4OQL30C-1681786426176)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/7817ee4f-64fd-44ba-b2c4-7ad635cea69e.png)]

LEFT

一种

0.8

-0.04

0.8 x -0.04 = -0.032

LEFT

X

0.1

0.36

0.1 x 0.36 = 0.036

LEFT

C

0.1

-0.04

0.1 x -0.04 = -0.004

因此,对于动作a = LEFT

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zRAh10ho-1681786426177)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/6b4a7765-353e-41bd-9d1d-b0d911f5fcb8.png)]

因此,在所有动作中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6WpkVNL2-1681786426177)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/7b5c01bd-c97a-4b98-a1ca-2b518675d6bd.png)]

因此,U[2](X) = -0.04 0.5 * 0.832 = 0.376,其中R(X) = -0.04γ = 1/2 = 0.5

因此,上述问题的答案是:

  1. U[1](X) = 0.36
  2. U[2](X) = 0.376

策略迭代

通过迭代策略并更新策略本身(而不是值)直到策略收敛到最优值以获得最优效用的过程称为策略迭代。 策略迭代的过程如下:

  • 从随机策略π[0]开始
  • 对于迭代步骤t中给定的π[t]策略,请使用以下公式计算U[t] = U[t]^π: [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4oDqmOZh-1681786426177)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/41e3cc56-1bb4-4874-b6db-8246f4054d3e.png)]
  • 根据以下公式改进π[t 1]: [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GLxxnb9l-1681786426177)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/1c9d9376-7286-498e-b097-c6aa03197556.png)]

部分可观察的马尔可夫决策过程

在 MDP 中,可观察的数量是动作,设置A,状态,设置S,转移模型,T和奖励,设置R。 在部分可观察的 MDP(也称为 POMDP)的情况下,情况并非如此。 在 POMDP 中,内部存在一个 MDP,智能体无法直接观察到它,而是根据所做的任何观察来做出决定。

在 POMDP 中,有一个观测集Z,它包含不同的可观测状态和观测函数O,它采用s状态和z观察值作为输入,输出在s状态下看到z观察值的可能性。

POMDP 基本上是 MDP 的概括:

  • MDP{S, A, T, R}
  • POMDP{S, A, Z, T, R, O}
  • 其中,SATR相同。 因此,要使 POMDP 成为真正的 MDP,请满足以下条件:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xcuPPBIn-1681786426177)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/305943b7-4263-4be1-aa15-dc23670ece53.png)],即完全遵守所有状态

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zX4C2QpW-1681786426177)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/b1be7b8a-48e3-400e-96ed-548c592e268e.png)]

POMDP 难以解决,无法实现最佳解决方案。

状态估计

如果我们扩展状态空间,这将有助于我们将 POMDP 转换为 MDP,其中Z包含完全可观察的状态空间。 这给出了信念状态b(s)的概念,这是决策者将在 POMDP 上下文中使用的状态。 置信状态,即b(s)给出了智能体处于s状态的可能性。 因此,置信状态b,是代表所有状态上概率分布的向量。 因此,一旦采取行动,信念状态就会更新。

假设存在一个信念状态b,该智能体采取行动a并收到了一些观察结果z。 这形成了一个新的信念状态。 因此,我们正在将 POMDP 转换为信念 MDP,其中它将由信念状态组成为 MDP 状态。

根据前述条件,给出的信息是置信状态b,动作a和观察值z。 因此,

b'(s')为在baz之后给出的处于s状态的概率 ,即P(s' | b, a, z)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jONUGXv3-1681786426178)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/35b5669e-32a7-4c9f-ad01-833abdb38255.png)]

其中p(z | b, a, s') = O(s', z)p(s' | s, b, a) = T(s, a, s')。 从而,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XW5Qo9yE-1681786426178)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/628f5062-7f82-4c25-b541-b1e76ae81d96.png)]

POMDP 中的值迭代

POMDP 中的值迭代基本上是从信念 MDP 获得的无限状态空间上的值迭代。

t = 0时,V[0](b) = 0

t > 0时,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BkAXzs9b-1681786426178)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/d9d073a1-0957-434c-962a-fc558a1bec1c.png)]

其中b'b'(s') = p(s' | b, a, z),即(b, a, z)R(b, a)的状态估计是对信念状态的预期奖励,如下所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HlV5P6rM-1681786426178)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/76b35625-61ca-47bf-b1f7-a0b13e381607.png)]

哪里,

p(s)s状态的概率

R(s, a)为该状态下的奖励

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-099uyK6v-1681786426178)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/c113e9fc-da9e-4c29-a01a-ae92779162cf.png)] =对信念状态的预期奖励

使用 MDP 训练 FrozenLake-v0 环境

这是关于 OpenAI Gym 中名为 FrozenLake-v0 的网格世界环境,在第 2 章“使用 OpenAI Gym 训练强化学习智能体”中讨论。 我们实现了 Q 学习和 Q 网络(我们将在以后的章节中进行讨论)以了解 OpenAI Gym 的环境。

现在,让我们尝试使用以下代码实现值迭代,以获取 FrozenLake-v0 环境中每个状态的效用值:

代码语言:javascript复制
# importing dependency libraries
from __future__ import print_function
import gym
import numpy as np
import time

#Load the environment
env = gym.make('FrozenLake-v0')

s = env.reset()
print(s)
print()

env.render()
print()

print(env.action_space) #number of actions
print(env.observation_space) #number of states
print()

print("Number of actions : ",env.action_space.n)
print("Number of states : ",env.observation_space.n)
print()

# Value Iteration Implementation

#Initializing Utilities of all states with zeros
U = np.zeros([env.observation_space.n])

#since terminal states have utility values equal to their reward
U[15] = 1 #goal state
U[[5,7,11,12]] = -1 #hole states
termS = [5,7,11,12,15] #terminal states
#set hyperparameters
y = 0.8 #discount factor lambda

eps = 1e-3 #threshold if the learning difference i.e. prev_u - U goes below this value break the learning

i=0
while(True):
    i =1
    prev_u = np.copy(U)
    for s in range(env.observation_space.n):
        q_sa = [sum([p*(r   y*prev_u[s_]) for p, s_, r, _ in env.env.P[s][a]]) for a in range(env.action_space.n)]
        if s not in termS: 
            U[s] = max(q_sa)
    if (np.sum(np.fabs(prev_u - U)) <= eps):
        print ('Value-iteration converged at iteration# %d.' %(i 1))
        break

print("After learning completion printing the utilities for each states below from state ids 0-15")
print()
print(U[:4])
print(U[4:8])
print(U[8:12])
print(U[12:16])
------------------------------------------------------------------------------------------------
<<OUTPUT>>
[2018-04-16 20:59:03,661] Making new env: FrozenLake-v0
0

SFFF
FHFH
FFFH
HFFG

Discrete(4)
Discrete(16)

Number of actions : 4
Number of states : 16

Value-iteration converged at iteration# 25.
After learning completion printing the utilities for each states below from state ids 0-15

[ 0.023482 0.00999637 0.00437564 0.0023448 ]
[ 0.0415207 -1. -0.19524141 -1. ]
[ 0.09109598 0.20932556 0.26362693 -1. ]
[-1. 0.43048408 0.97468581 1. ]

分析输出,

让状态表示如下:

0 1 2 3

4 5 6 7

8 9 10 11

12 13 14 15

我们智能体的起始状态为 0。让我们从s = 0开始,

U[s = 0] = 0.023482,现在动作可以是UPDOWNLEFTRIGHT

s = 0的情况下,如果:

  • 采取UP动作,s_new = o,因此,u[s_new] = 0.023482
  • 采取DOWN动作,s_new = 4,因此,u[s_new] = 0.0415207
  • 采取LEFT动作,s_new = o,因此,u[s_new] = 0.023482
  • 采取RIGHT动作,s_new = 1,因此,u[s_new] = 0.00999637

最大值为u[s_new = 4] = 0.0415207,因此,采取的动作为DOWNs_new = 4

现在在s = 4的情况下:

  • 采取UP动作,s_new = o,因此,u[s_new] = 0.023482
  • 采取DOWN动作,s_new = 8,因此,u[s_new] = 0.09109598
  • 采取LEFT动作,s_new = 4,因此,u[s_new] = 0.0415207
  • 采取RIGHT动作,s_new = 5,因此,u[s_new] = -1.0

最大值为u[s_new = 8] = 0.09109598,则采取的动作将为DOWNs_new = 8

现在,如果s = 8,则:

  • 采取UP动作,s_new = 4,因此,u[s_new] = 0.0415207
  • 采取DOWN动作,s_new = 12,因此,u[s_new] = -1.0
  • 采取LEFT动作,s_new = 8,因此,u[s_new] = 0.09109598
  • 采取RIGHT动作,s_new = 9,因此,u[s_new] = 0.20932556

最大值为u[s_new = 9] = 0.20932556,因此,采取的动作为RIGHTs_new = 9

现在,如果s = 9,则:

  • 采取UP动作,s_new = 5,因此,u[s_new] = -1.0
  • 采取DOWN动作,s_new = 13,因此,u[s_new] = 0.43048408
  • 采取LEFT动作,s_new = 8,因此,u[s_new] = 0.09109598
  • 采取RIGHT动作,s_new = 10,因此,u[s_new] = 0.26362693

最大值为u[s_new = 13] = 0.43048408,因此,采取的动作为DOWNs_new = 13

现在,如果s = 13,则:

  • 采取UP动作,s_new = 9,因此,u[s_new] = 0.20932556
  • 采取DOWN动作,则s_new = 13,因此,u[s_new] = 0.43048408
  • 采取LEFT动作,s_new = 12,因此,u[s_new] = -1.0
  • 采取RIGHT动作,s_new = 14,因此,u[s_new] = 0.97468581

最大值为u[s_new = 14] = 0.97468581,因此,采取的动作为RIGHTs_new = 14

现在,如果s = 14,则:

  • 采取UP动作,s_new = 10,因此,u[s_new] = 0.26362693
  • 采取DOWN动作,s_new = 14,因此,u[s_new] = 0.97468581
  • 采取LEFT动作,s_new = 13,因此,u[s_new] = 0.43048408
  • 采取RIGHT动作,s_new = 15(目标状态),因此,u[s_new] = 1.0

最大值为u [s_new = 15] = 1.0,因此,采取的动作为RIGHTs_new = 15

因此,我们的策略包含DOWNDOWNRIGHTDOWNRIGHTRIGHT通过避开空穴状态(5、7、11、12)从s = 0(开始状态)到达s = 15(目标状态)。

总结

在本章中,我们介绍了网格世界类型的环境的详细信息,并了解了马尔可夫决策过程的基础,即状态,动作,奖励,转移模型和策略。 此外,我们利用这些信息通过值迭代和策略迭代方法来计算效用和最优策略。

除此之外,我们对部分可观察的马尔可夫决策过程是什么样子以及解决它们的挑战有了基本的了解。 最后,我们从 OpenAI Gym 获取了我们最喜欢的 gridworld 环境,即 FrozenLake-v0,并实现了一种值迭代方法,以使我们的智能体学会在该环境中导航。

在下一章中,我们将从策略梯度开始,然后从 FrozenLake 过渡到其他一些引人入胜的复杂环境。

四、策略梯度

到目前为止,我们已经看到了如何使用基于值的方法从值函数中派生隐式策略。 在这里,智能体将尝试直接学习策略。 做法是相似的,任何有经验的智能体都会在目睹之后更改策略。

值迭代,策略迭代和 Q 学习属于通过动态编程解决的基于值的方法,而策略优化方法则涉及策略梯度和该知识与策略迭代的结合,从而产生了行动者批判算法。

根据动态编程方法,存在一组自洽方程,可以满足QV值。 策略优化是不同的,策略学习直接进行,而不是从值函数中得出:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Otgfva7K-1681786426179)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/783c3d44-900b-4334-a5e6-b4a2a297a1a8.png)]

因此,基于值的方法学习了值函数,我们得出了一个隐式策略,但是使用基于策略的方法,就不会学习任何值函数,而直接学习了该策略。 由于我们同时学习了值函数和策略,而行为者批评方法更为先进,而网络学习值函数则充当了作为参与者的策略网络的批评者。 在本章中,我们将深入研究基于策略的方法。

我们将在本章介绍以下主题:

  • 策略优化方法
  • 为什么要采用策略优化方法?
  • 策略目标函数
  • 时差规则
  • 策略梯度
  • 使用策略梯度的智能体学习 Pong

策略优化方法

策略优化方法的目标是找到随机策略π[θ],它是针对给定状态的操作分布,该状态使期望的奖励总和最大化。 它旨在直接找到该策略。 基本概述是创建一个神经网络(即策略网络),该网络处理一些状态信息并输出智能体可能采取的行动的分布。

策略优化的两个主要组成部分是:

  • 神经网络的权重参数由θ向量定义,这也是我们控制策略的参数。 因此,我们的目标是训练权重参数以获得最佳策略。 由于我们将保单视为给定保单的预期奖励总和。 在此,对于θ的不同参数值,策略将有所不同,因此,最佳策略将是具有最大总体奖励的策略。 因此,具有最大预期奖励的θ参数将是最佳策略。 以下是预期奖励金额的公式:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g1eHWVnm-1681786426179)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/b0592927-b05c-471e-a9f1-18b6c1bedaa4.png)]

那就是最大化期望的奖励总和。

这里, H地平线的时间步长,因此,如果开始时间步长t = 0,则总时间步长为H 1

  • 随机策略类消除了策略优化问题,为我们提供了一组可以选择最佳策略的策略。 在网格世界环境中采用确定性策略的情况下,由于动作变化而导致的变化并不平滑,但是如果每个状态都有动作分布,我们可以稍微改变分布,而这只会稍微改变预期的总和。 奖励。 这是使用随机策略的优势,其中π[θ](a | s)给出给定状态s的动作a的概率。 因此,π[θ]给出了给定状态下动作的概率分布。

因此,由于随机策略,我们有一个平滑的优化问题,可以应用梯度下降来获得良好的局部最优,从而导致最优策略。

为什么要采用策略优化方法?

在本节中,我们将介绍策略优化方法相对于基于值的方法的优缺点。 优点如下:

  • 它们提供了更好的融合。
  • 在高维/连续状态动作空间的情况下,它们非常有效。 如果动作空间很大,则基于值的方法中的max函数将在计算上昂贵。 因此,基于策略的方法通过更改参数来直接更改策略,而不是在每个步骤中求解max函数。
  • 学习随机策略的能力。

与基于策略的方法相关的缺点如下:

  • 收敛到局部而不是全局最优
  • 策略评估效率低下且差异很大

我们将在本章后面讨论解决这些缺点的方法。 现在,让我们集中讨论对随机策略的需求。

为什么采用随机策略?

让我们看两个例子,这些例子将解释基于值方法将随机策略与接近确定性策略相结合的重要性。

示例 1-石头,剪刀,布

石头,剪刀,布是两人游戏,包含以下规则:

  • 石头打剪刀
  • 剪刀打布
  • 布打石头

因此,不可能有确定的策略来取胜。 说,如果有确定性的策略,那摇滚总是赢家,但是如果对手有剪刀,情况就是这样。 但是,当对手拿到纸时,石头就被击败了。 因此,在这种环境下无法确定解决方案。 解决此问题的唯一方法是使用本质上是随机的统一随机策略。

示例 2-名为网格世界的状态

考虑以下别名为 grid-world 的状态:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YWO1FN5T-1681786426179)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/c3a614ea-6981-49ca-b3a3-65614cdb7db6.png)]

在上图中,我们看到有八个状态,其中两个奖励为 -10 的状态是要避免的不良状态,并且有一个奖励为 10 的状态是良好状态,还有目标状态。 但是,我们主要担心的是灰色阴影状态,在该状态下,智能体无法区分这两个状态,因为它们具有相同的特征。

假设任何状态的特征都为φ(s, a),动作为北,东,西和南。

考虑到这些函数,让我们比较基于值的方法和基于策略的方法:

  • 基于值的强化学习将使用近似值函数Q(s, a) = f(φ(s, a), w)
  • 基于策略的强化学习将使用参数化策略π[θ](s, a) = g(φ(s, a), θ)

在此,w是值函数的参数,θ是策略的参数。 众所周知,灰色方块具有相同的特征,即在北部和南部都有墙。 因此,由于相同的特征,这导致状态混淆。 结果,基于值的强化学习方法学习了如下确定性策略:

  • 要么在两个灰色阴影状态下向西移动
  • 或在两个灰色阴影状态下向东移动

无论哪种方式,都极有可能被卡住或花费很长时间达到目​​标状态。

另一方面,在基于策略的方法的情况下,最优随机策略将随机选择处于灰色状态的东西方行为,因为这两种行为具有以下相同的概率:

  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6m2dS8UR-1681786426179)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/6a5d3643-6236-4ad0-beb6-3a15a52416aa.png)](南北墙,行动=东)= 0.5
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kLi2227t-1681786426179)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/ba7a9786-5034-4fb6-9c46-7f35d5ce2caf.png)](南北墙,行动=西)= 0.5

结果,它将以更少的步骤达到目标状态,因为西方和东方都将具有相同的发生概率。 因此,与基于值的强化学习方法相比,不同的灰色阴影状态可能会随机发生不同的动作,从而导致目标状态更快。 因此,基于策略的强化学习可以学习最佳的随机策略。

因此,从前面的示例中,我们已经知道,每当发生状态别名时,随机策略都可以执行得更好。 因此,只要存在状态混叠的情况(即环境的表示),就会部分观察状态(例如,您处于部分可观察的马尔可夫决策过程中)或函数逼近器使用状态的特征,这会限制环境的整体视角,使用基于随机策略的基于策略的方法要比基于值的方法要好。

策略目标函数

现在让我们讨论如何优化策略。 在策略方法中,我们的主要目标是具有参数向量θ的给定策略J(θ)找到参数向量的最佳值。 为了测量哪个最好,我们针对参数向量θ的不同值测量J(θ)策略π[θ](s, a)的质量。

在讨论优化方法之前,让我们首先弄清楚度量策略π[θ](s, a)的质量的不同方法:

  • 如果是情景环境,则J(θ)可以是开始状态V[π[θ]](s[1])的值函数,也就是说,如果它从任何状态s[1]开始,则其值函数将是该状态开始时的预期奖励总和 。 因此,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7o3ALSfT-1681786426180)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/01578517-3d8f-4227-a234-94a8e6fc9985.png)]

  • 如果是连续环境,则J(θ)可以是状态的平均值函数。 因此,如果环境持续不断,那么策略质量的衡量标准可以是处于任何状态s的概率之和,该概率是d[π[θ]](s)乘以该状态的值,也就是说,从该状态开始的预期奖励。 因此,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N7EmMtL6-1681786426180)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/2cfba9ad-f36c-4239-80f2-8c4acd2b5738.png)]

  • 对于连续环境,J(θ)可以是每个时间步长的平均奖励,它是处于任何状态s的概率的总和,即d[π[θ]](s)乘以对该状态的不同动作的期望奖励E[R[s]^a] = Σ[a] π[θ](s, a) R[s]^a的总和。 因此:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pIrLHelO-1681786426180)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/d42506b9-9710-4ec3-93a3-50c62b3b2991.png)]

在此,R[s]^a是在状态s采取行动a的奖励。

到目前为止,我们知道基于策略的强化学习是一个优化问题,目标是找到使J(θ)最大化的θ。 同样,在这里,我们将使用基于梯度的方法进行优化。 使用基于梯度的优化的原因是要获得更高的效率,因为与无梯度方法相比,梯度指向了更高效率的方向。

让衡量策略质量的J(θ)成为我们的策略目标函数。 因此,策略梯度算法通过相对于参数θ提升策略的梯度,在J(θ)中寻找局部最大值。 这是因为我们的目标是使J(θ)相对于θ最大化,因此我们进行了梯度上升,其中 为Δθ的参数的增量如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HCLTj0Wk-1681786426180)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/9ff648e7-9f6f-4a44-a52f-7d749cb6e1cf.png)]

在这里,ᐁ[θ] J(θ)是策略梯度,α是学习率,也称为步长参数,步长参数决定该参数应在每个步长上偏移多少梯度。 策略梯度还可以用以下形式阐述:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NZ5cGVxz-1681786426180)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/0d918775-9864-4b04-9b05-f59c0941e222.png)]

策略梯度定理

假设给定策略π[θ](s, a)每当不为零时都是可微的,则给定策略相对于θ的梯度为ᐁ[θ] π[θ](s, a)。 因此,我们可以以似然比的形式进一步利用此梯度量,如下所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3JjGe95E-1681786426181)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/0af8a486-8579-4e97-8e22-ec4b276714f5.png)]

此处,ᐁ[θ] log π[θ](s, a)是得分函数,供将来参考。

现在,让我们考虑一个简单的一步式 MDP,即马尔可夫决策过程,其中:

  • 起始状态为s,发生的可能性为d[π[θ]](s)
  • 终止仅发生在获得奖励r = R[s]^a的一步之后

考虑到它是一个持续的环境,因此:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pJfnlhM1-1681786426181)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/a8296a39-e2c7-4a1e-a8c4-02e15df4fd4e.png)]

因此,策略梯度ᐁ[θ] J(θ)将如下所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LH7qW1ZS-1681786426181)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/ba08ca33-c4b3-4d33-85f9-cd7ee7413342.png)]

在这里,我们证明了以下几点:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kCg12F4f-1681786426181)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/0c0c9dc6-9641-4931-855c-216477a3a009.png)]

结果是:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rvIeE0d4-1681786426181)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/b6666854-9607-4fcd-9536-6ad2779a7ffd.png)]

因此,将该方法推广到多步马尔可夫决策过程将导致状态奖励 Q 值函数Q[π](s, a)代替瞬时奖励r。 这称为策略梯度定理。 该定理对前面讨论的其他类型的策略目标函数有效。 因此,对于任何这样的策略目标函数和任何可微分的π[θ](s, a),策略梯度如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lfeepEaR-1681786426181)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/22aa628a-e90d-49c5-ae2d-c8f5e168393c.png)]

时差规则

首先,时间差TD)是两个时间步之间的值估计值之差。 这与基于结果的蒙特卡洛方法不同,后者基于前瞻性的观点直到剧集结束才完成,以更新学习参数。 在时间差异学习的情况下,仅需完成一个步骤,而下一步的状态值估计将用于更新当前状态的值估计。 因此,学习参数会不断更新。 进行时差学习的不同规则是TD(1)TD(0)TD(λ)规则。 所有方法中的基本概念是,下一步的值估计用于更新当前状态的值估计。

TD(1)规则

TD(1)纳入了资格跟踪的概念。 让我们看一下该方法的伪代码,然后将详细讨论它:

代码语言:javascript复制
Episode T
    For all s, At the start of the episode : e(s) = 0 and 
        After  : (at step t)

        For all s,

每个Episode T都以以下初始化开始:

  • 对于所有状态s,合格分数e(s) = 0
  • 对于所有状态s,给定Episode T中的状态值V[T](s)等于V[T-1](s)

在剧集的每个时间步,即当前步t,我们更新要离开的状态s[t-1]的资格,然后为所有状态更新以下内容:

  • 针对当前离开状态s[t-1](即r[t] γ V[T-1](s[t]) V[T-1](s[t-1]))和要更改其值的状态的合格分数e(s)使用时间差误差的状态值函数
  • 通过给定折扣系数进行折扣的资格分数

由于这些更新针对所有状态独立发生,因此可以针对所有状态并行执行这些操作。

在完成剧集的最后一步之后扩展每个状态的计算值估计值时,我们发现基于值的更新与基于结果的更新相同,例如在蒙特卡洛方法中,我们会进行全面的展望,直到剧集的结尾。 因此,我们需要一种更好的方法来更新我们的值函数估计值,而无需多做一步。 通过合并TD(0)规则,我们可以解决此问题。

TD(0)规则

如果无限重复重复有限数据,则TD(0)规则会找到值估计值,通常就是如果我们获取此有限数据并不断重复运行以下估计值更新规则。 然后,实际上我们正在平均每个转换。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hH8fZm5n-1681786426182)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/262fb91d-f5d6-4d42-97fc-710d525ac99f.png)]

因此,值函数由以下给出:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jYN0QkYN-1681786426182)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/49b93224-b17b-4deb-b5cf-bf76e9eb19db.png)]

在基于结果的模型中,这是不正确的,在该模型中,我们不使用状态估计值V[T](s[t]),而是使用整个奖励序列,直到事件结束为止。 因此,在蒙特卡洛方法基于结果的模型的情况下,值函数由以下公式给出:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-76DGMexK-1681786426182)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/24c5dac2-f8c8-45a1-b87c-bf33254da2aa.png)]

此外,在基于结果的方法中,我们只看到一个序列,重复该过程不会更改值函数的值。 但是,在TD(0)的情况下,每一步都会根据中间状态计算和完善值函数估计。

TD(0)TD(1)之间的区别是,在TD(1)情况下使用资格跟踪更新状态值函数,而在TD(0)情况下则不使用。

TD(0)规则的伪代码如下:

代码语言:javascript复制
Episode T
    For all s, At the start of the episode : 
        After  : (at step t)
        For s = ,

因此,这里我们仅使用时间差误差r[t] γ V[T-1](s[t]) V[T-1](s[t-1])更新当前离开状态s[t-1]的值函数。

TD(λ)规则

TD(1)TD(0)规则产生一个通用规则,即TD(λ),因此对于λ ∈ [0, 1]并应满足以下条件:

  • 如果λ = 0,则TD(λ)趋于TD(0)
  • 如果λ = 1,则TD(λ)趋于TD(1)

TD(0)TD(1)都基于时间连续预测之间的差异进行更新。

因此,TD(λ)的伪代码如下:

代码语言:javascript复制
Episode T
    For all s, At the start of the episode : e(s) = 0 and 
        After  : (at step t)

        For all s,

这满足前面两个条件,并且可以合并λ ∈ [0, 1]的任何值。

策略梯度

根据策略梯度定理,对于先前指定的策略目标函数和任何可微分策略π[θ](s, a),策略梯度如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2udgPV7a-1681786426182)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/406f3ccb-146a-485c-b05a-89a8f4d935cd.png)]

下一节显示了使用基于蒙特卡洛策略梯度的方法更新参数的步骤。

蒙特卡洛策略梯度

蒙特卡洛策略梯度方法中,我们使用随机梯度上升方法更新参数,并按照策略梯度定理进行更新,并使用v[t]作为Q[π[θ]](s[t], a[t])的无偏样本。 在此,v[t]是从该时间步开始的累计奖励。

蒙特卡洛策略梯度法如下:

代码语言:javascript复制
Initialize  arbitrarily
for each episode as per the current policy  do
    for step t=1 to T-1 do

    end for
end for

Output: final 

演员评论家算法

先前使用蒙特卡洛策略梯度方法进行的策略优化导致较大的差异。 为了解决此问题,我们使用评论家来估计状态作用值函数,如下所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aJnaRJlD-1681786426182)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/bdee56e7-7167-4d77-a478-710505461008.png)]

这产生了著名的演员评论家算法。 顾名思义,演员评论家算法出于以下目的维护两个网络:

  • 一个网络充当评论者,它更新状态动作函数逼近器的权重w参数向量
  • 其他网络充当演员,它根据批评者给出的方向更新策略参数向量θ

下图代表了演员评论家算法:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RfM0Sbaz-1681786426183)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/ca78b5e5-1459-4f34-ad61-de744f663054.png)]

因此,在执行者批评算法的情况下,实际策略梯度公式中的真实状态作用值函数Q[π[θ]](s, a)被替换为近似状态作用值函数Q[w](s, a)。 因此:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZKSr7eSP-1681786426183)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/d8b7251d-91aa-4183-add1-5a5a0f9f8d0c.png)]

并且:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BhQGEs9T-1681786426183)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/d9896c48-a57d-45e1-974e-3d9bed7c4af3.png)]

因此,为了估计状态作用值函数,评论者网络使用TD(0)(先前讨论)来更新权重参数w,并更新策略参数向量θ演员网络使用策略梯度。 演员评论家算法的一种简单方法如下所示:

代码语言:javascript复制
Initialize s, 
for each episode
    Sample  as per the current policy     
    for each step do 
        Take action  and observe reward  and next state 
        Sample action  as per the current policy 

    end for
end for

Output : final 

因此,我们可以看到参与者评论家既具有基于值的优化又具有基于策略的优化。 因此,在采用蒙特卡洛策略梯度法的情况下,策略改进会贪婪地进行。 但是在演员批评家中,演员通过按照批评家的方向采取措施来更新策略参数,以便制定更好的策略。

使用基线减少方差

除了我们最初使用行为准则的方法来减少方差之外,我们还可以通过从策略梯度中减去基线函数 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nhwP7Nfr-1681786426183)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/57466ce5-670a-4989-9de6-c43208771e1b.png)]来减少方差。 这将减少方差而不影响期望值,如下所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hHOjQnrq-1681786426183)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/3564b3a4-9d0f-4cc2-87a6-a3ae59c46f7e.png)]

有许多选择基线函数的选项,但是状态值函数被认为是很好的基线函数。 因此:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PxlFBuMb-1681786426184)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/b0a2160a-823c-437d-a1a1-64413a9be3cc.png)]

因此,我们可以通过减去基线函数来重写策略梯度公式,如下所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j1xo2vZ5-1681786426184)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/e4d88794-1d88-4b34-89cb-b726ea224273.png)]

在这里,Q[π[θ]](s, a) - A[π[θ]](s)被称为优势函数A[π[θ]](s, a)。 因此,策略梯度公式变为:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YXwLlwB1-1681786426184)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/5b410fc8-1d5b-4a36-9250-730d1a651b86.png)]

因此,通过使用基线函数,期望值受到方差降低的控制,而方向没有任何变化。

原始策略梯度

在常规策略梯度方法中,目标是使用策略梯度估计和更好的基线估计来更新策略。

以下是实现原始策略梯度以找到最佳策略的伪代码:

代码语言:javascript复制
Initialize: Policy parameter , and baseline b
for iteration = 1,2,......N   do
        Collect a set of trajectories using the current policy
        At each time step t in each trajectory, compute the following:
            returns ,and
            advantage estimate 
        Refit the baseline function 

end for

使用策略梯度的学习 Pong 的智能体

在本节中,我们将创建一个策略网络,该策略网络将使用来自 pong 环境的原始像素(来自 OpenAI Gym 的 pong-v0)作为输入。 策略网络是一个单独的隐藏层神经网络,它全连接到输入层上 pong 的原始像素,还全连接到包含单个节点的输出层,该单个节点返回了桨上升的可能性。 我要感谢 Andrej Karpathy 提出了一种使智能体使用策略梯度进行学习的解决方案。 我们将尝试实现类似的方法。

灰度大小为80 * 80的像素图像(我们将不使用 RGB,即80 * 80 * 3)。 因此,我们有一个80 * 80的二进制网格,它告诉我们桨和球的位置,并将其作为输入输入到神经网络。 因此,神经网络将包含以下内容:

  • 输入层(X:将80 * 80压缩为6400 * 1,即 6400 个节点
  • 隐藏层:200 个节点
  • 输出层:1 个节点

因此,总参数如下:

  • 连接输入层和隐藏层的权重和偏置6400 * 200(权重) 200(偏置)参数
  • 连接隐藏层和输出层的权重和偏置200 * 1(权重) 1(偏置)参数

因此,总参数将约为 130 万。

这是巨大的,因此训练需要几天的时间才能见证您的经纪人流畅地打乒乓球。

不能从静态帧播放 Pong,因此,需要捕获某种运动信息,可以通过连接两个这样的帧或新帧与前一帧之间的差异来完成。 因为我们没有使用卷积神经网络,所以除了 6400 个像素值在 0 和 1 之间翻转之外,没有可用的空间信息。 而不是桨和球的位置。

训练 130 万个参数的有效计算方法是使用策略梯度。 您可以将其与监督学习相关联,其中针对每个状态都提到了一个动作标签。 因此,数据和训练如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nAujjIlI-1681786426184)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/297c6703-97ca-4ba2-8e19-70dabec7b4a1.png)]

但是,实际上我们没有标签。 因此,我们将实现强化学习,在其中我们将尝试许多任务并记下观察结果。 然后,更频繁地执行表现更好的任务。

在输入 Python 代码之前,让我们放下步骤。 它们如下:

  1. 初始化随机策略。
  2. 对于给定的当前策略,我们将通过以下方式收集不同的样本轨迹(展示):
    1. 运行游戏的一个剧集并捕获该剧集的轨迹。
    2. 同样,收集一批轨迹,如下所示:

轨迹

游戏结果

轨迹好/坏

上,下,上,上,下,下,下,上

下,上,上,下,上,上

上,上,下,下,下,下,上

下,上,上,下,上,上

因此,我们能够创建示例数据,在这些示例中,我们认为赢得的案例是该操作的正确标签。 因此,我们将增加这些动作的对数概率,即log p(y[i], x[i]),并且每个动作失败的情况将被视为错误标签。 因此,在这些情况下,我们将减少这些操作的对数概率。

因此,在收集了一批轨迹之后,我们将最大化优势与对数概率的乘积,即Σ[i] A[i] x log p(y[i], x[i])

在此,A[i]是与状态操作对关联的优势。 优势是一个标量,它量化了操作最终的效果。 如果我们想在将来鼓励给定的行动,则A[i]会很高,而如果我们想阻止该行动,则A[i]会很低。 正优势使该状态的将来更有可能发生该行为,而负优势使该状态的将来不太可能发生该行为。

首先,我们将导入所需的重要依赖项,如下所示:

代码语言:javascript复制
#import dependencies
import numpy as np #for matrix math
import cPickle as pickle #to save/load model
import gym
  • 超参数初始化:由于我们正在使用RMSProp优化器进行梯度下降,因此超参数(例如隐藏层节点的数量,批量大小,学习率,折扣系数gamma,衰减率)。 我们将使用以下代码进行初始化:
代码语言:javascript复制
#hyperparameters
H = 200 #number of nodes in the hidden layer
batch_size = 10 
learning_rate = 1e-4 
gamma = 0.99 #discount factor
decay_rate = 0.99 #for RMSProp Optimizer for Gradient Descent
resume = False #to resume from previous checkpoint or not
  • 策略神经网络模型初始化:策略神经网络的权重参数的初始化。 在这里,我们使用一个隐藏层神经网络。 我们将使用以下代码进行初始化:
代码语言:javascript复制
#initialize : init model
D = 80*80 #input dimension
if resume:
    model = pickle.load(open('model.v','rb'))
else:
    model = {}
    #xavier initialisation of weights
    model['W1'] = np.random.randn(H,D)*np.sqrt(2.0/D)
    model['W2'] = np.random.randn(H)*np.sqrt(2.0/H)
    grad_buffer = {k: np.zeros_like(v) for k,v in model.iteritems()} #to store our gradients which can be summed up over a batch
    rmsprop_cache = {k: np.zeros_like(v) for k,v in model.iteritems()} #to store the value of rms prop formula
  • 激活函数sigmoid(x)relu(x)分别指执行 Sigmoid 和 ReLU 激活计算的函数。 我们将使用以下代码定义函数:
代码语言:javascript复制
#activation function
def sigmoid(x):
    return 1.0/(1.0 np.exp(-x)) #adding non linearing   squashing

def relu(x):
    x[x<0] = 0
    return x
  • 预处理函数preprocess(image)函数将图像像素作为参数,并通过裁剪,下采样,使其成为灰度,擦除背景并将图像展平为一维向量来对其进行预处理。 我们将使用以下代码定义函数:
代码语言:javascript复制
#preprocessing function
def preprocess(image): #where image is the single frame of the game as the input
    """ take 210x160x3 frame and returns 6400 (80x80) 1D float vector """
    #the following values have been precomputed through trail and error by OpenAI team members
    image = image[35:195] #cropping the image frame to an extent where it contains on the paddles and ball and area between them
    immage = image[::2,::2,0] #downsample by the factor of 2 and take only the R of the RGB channel.Therefore, now 2D frame
    image[image==144] = 0 #erase background type 1
    image[image==109] = 0 #erase background type 2
    image[image!=0] = 1 #everything else(other than paddles and ball) set to 1
    return image.astype('float').ravel() #flattening to 1D
  • 折扣奖励discount_rewards(r)函数将与不同时间步长对应的奖励列表r作为参数,并返回与不同时间步长对应的折扣奖励列表,如以下代码所示 :
代码语言:javascript复制
def discount_rewards(r):
    """ take 1D float array of rewards and compute discounted reward """
    discount_r = np.zeros_like(r)
    running_add = 0 #addition of rewards
    for t in reversed(xrange(0,r.size)):
        if r[t] != 0: #episode ends
            running_add = 0
        running_add = gamma*running_add r[t]
        discount_r[t] = running_add
    return discount_r
  • 正向传播policy_forward(x)函数接收预处理的图像向量x,返回动作概率为UP,并且向量包含隐藏状态节点的值,例如以下代码:
代码语言:javascript复制
def policy_forward(x):
    h = np.dot(model['W1'],x) 
    h = relu(h) 
    logit = np.dot(model['W2'],h)
    p = sigmoid(logit)
    return p,h #probability of action 2(that is UP) and hidden layer state that is hidden state
  • 反向传播policy_backward(arr_hidden_state, gradient_logp, observation_values)函数采用隐藏状态值,误差gradient_logp和观测值来针对不同权重参数计算导数,如以下代码所示:
代码语言:javascript复制
def policy_backward(arr_hidden_state,gradient_logp,observation_values):
    """ backward pass """
    #arr_hidden_state is array of intermediate hidden states shape [200x1]
    #gradient_logp is the loss value [1x1]
    dW2 = np.dot(arr_hidden_state.T,gradient_logp).ravel() 
    # [200x1].[1x1] => [200x1] =>flatten=>[1x200]
    dh = np.outer(gradient_logp,model['W2']) # [1x1]outer[1x200] => [1x200]
    dh = relu(dh) #[1x200]
    dW1 = np.dot(dh.T,observation_values) #[200x1].[1x6400] => [200x6400]
    return {'W1':dW1,'W2':dW2}

创建环境并结合先前的函数以使智能体逐步学习多个剧集的最终任务如下:

代码语言:javascript复制
#implementation details
env = gym.make('Pong-v0')
observation = env.reset()
prev_x = None 
#prev frame value in order to compute the difference between current and previous frame
#as discussed frames are static and the difference is used to capture the motion
#Intially None because there's no previous frame if the current frame is the 1st frame of the game
episode_hidden_layer_values, episode_observations, episode_gradient_log_ps, episode_rewards = [], [], [], []
running_reward = None
reward_sum = 0
episode_number = 0

#begin training
while True:
    env.render()
    #get the input and preprocess it
    cur_x = preprocess(observation)
    #get the frame difference which would be the input to the network
    if prev_x is None:
        prev_x = np.zeros(D)
    x = cur_x - prev_x
    prev_x = cur_x

    #forward propagation of the policy network
    #sample an action from the returned probability
    aprob, h = policy_forward(x)
    #stochastic part
    if np.random.uniform() < aprob:
        action = 2
    else:
        action = 3

    episode_observations.append(x) #record observation
    episode_hidden_layer_values.append(h) #record hidden state
    if action == 2:
        y = 1
    else:
        y = 0

    episode_gradient_log_ps.append(y-aprob) #record the gradient

    #new step in the environment
    observation,reward,done,info = env.step(action)
    reward_sum =reward #for advantage purpose
    episode_rewards.append(reward) #record the reward

    if done: #if the episode is over
        episode_number =1

        #stack inputs,hidden_states,actions,gradients_logp,rewards for the episode
        arr_hidden_state = np.vstack(episode_hidden_layer_values)
        gradient_logp = np.vstack(episode_gradient_log_ps)
        observation_values = np.vstack(episode_observations)
        reward_values = np.vstack(episode_rewards)

        #reset the memory arrays
        episode_hidden_layer_values, episode_observations, episode_gradient_log_ps, episode_rewards = [], [], [], []

        #discounted reward computation
        discounted_episoderewards = discount_rewards(reward_values)
        #normalize discounted_episoderewards
        discounted_episoderewards = (discounted_episoderewards - np.mean(discounted_episoderewards))/np.std(discounted_episoderewards) #advantage

        #modulate the gradient with the advantage
        gradient_logp *= discounted_episoderewards

        grad = policy_backward(arr_hidden_state,gradient_logp,observation_values)

        #summing the gradients over the batch size
        for layer in model:
            grad_buffer[layer] =grad[layer]

        #perform RMSProp to update weights after every 10 episodes
        if episode_number % batch_size == 0:
            epsilon = 1e-5
            for weight in model.keys():
                g = grad_buffer[weight] #gradient
                rmsprop_cache[weight] = decay_rate*rmsprop_cache[weight] (1-decay_rate)`g`2
                model[weight] =learning_rate*g/(np.sqrt(rmsprop_cache[weight])   epsilon)
                grad_buffer[weight] = np.zeros_like(model[weight])

        if running_reward is None:
            running_reward = reward_sum
        else:
            running_reward = running_reward*learning_rate reward_sum*(1-learning_rate)

        print('Episode Reward : {}, Running Mean Award : {}'.format(reward_sum,running_reward))
        if episode_number % 100 == 0:
            pickle.dump(model,open('model.v','wb'))

        reward_sum = 0
        prev_x = None
        observation = env.reset() #resetting the environment since episode has ended

    if reward != 0: #if reward is either  1 or -1 that is an episode has ended
        print("Episode {} ended with reward {}".format(episode_number,reward))

智能体玩的游戏的屏幕截图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-588l76EZ-1681786426184)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/a93603fa-c7c9-450d-9df9-5549bb6fb056.png)]

如果您在笔记本电脑上运行先前的代码,则融合可能需要几天的时间。 尝试使用 GPU 驱动的云实例在大约 5-6 个小时内获得更好的结果。

总结

在本章中,我们介绍了强化学习中最著名的算法,策略梯度和参与者批评算法。 在制定策略梯度以强化学习中更好的基准测试方面,正在进行大量研究。 策略梯度的进一步研究包括信任区域策略优化TRPO),自然策略梯度深度依赖策略梯度DDPG),这些内容不在本书的讨论范围之内。

在下一章中,我们将研究 Q 学习的基础知识,应用深度神经网络以及更多技术。

五、Q 学习和深度 Q 网络

在第 3 章,“马尔可夫决策过程”中,我们讨论了遵循马尔可夫性质的环境转移模型以及延迟的奖励和值函数的概念(或工具)。 好,在本章中,我们将研究马尔可夫决策过程,了解 Q 学习,以及一种称为深度 Q 网络的改进方法,用于在不同环境中进行泛化。

我们将在本章介绍以下主题:

  • 人工智能的有监督和无监督学习
  • 基于模型的学习和无模型学习
  • Q 学习
  • 深度 Q 网络
  • 蒙特卡罗树搜索算法
  • SARSA 算法

为什么是强化学习?

2014 年,Google 以高达 5 亿美元的价格收购了伦敦一家名为 DeepMind 的创业公司。 在新闻中,我们了解到他们创建了一个 AI 智能体来击败任何 Atari 游戏,但是 Google 付出如此高的价格收购它的主要原因是因为这一突破向通用人工智能靠近了一步。 通用人工智能被称为 AI 智能体。 它能够完成各种任务并像人类一样泛化。 当它超过这一点时,该点称为人工超级智能。 目前,AI 社区所做的工作就是我们所说的人工智能,即人工智能,其中 AI 智能体能够执行多个任务,但不能概括各种任务。

DeepMind 在研究期刊 Nature 上发表了他们的论文《通过深度强化学习进行人类水平控制》,这表明,他们的深度强化学习算法可成功应用于 50 种不同的 Atari 游戏,并在其中 30 种游戏中达到高于人类水平的表现。 他们的 AI 智能体称为深度 Q 学习器。 在详细深入学习强化学习之前,让我们回顾一下强化学习的基础知识。

有监督和无监督的学习是 AI 应用社区众所周知的。 监督学习处理包含输入特征和目标标签(连续或离散)的标记数据集,并创建将这些输入特征映射到目标标签的模型。 另一方面,无监督学习处理仅包含输入特征但不包含目标标签的未标记数据集,其目的是发现基础模式,以将数据分类到不同集群中,并分别定义其效用。 不同集群中特定类型的数据。

因此,使用有监督和无监督的学习,我们可以创建数据分类器/回归器或数据生成器,通过一次学习就可以通过一批数据进行学习。 为了随着时间的推移增强学习,该批量需要合并越来越多的数据,从而导致有监督和无监督的学习变得缓慢且难以推广。 让我们考虑一种情况,您希望 AI 智能体为您玩特定的视频/虚拟游戏,但要注意的是,随着时间的流逝,该算法应该变得智能。

那么,如何解决这个问题呢?

假设我们拍摄了特定视频游戏中所有最佳玩家的视频,并以图像帧和目标标签的形式输入数据,作为可能采取的不同动作的集合。 由于我们具有输入特征和目标标签,因此形成了监督学习分类问题。 假设数据量巨大,并且我们可以使用具有最新 GPU 的高端机器,那么为该任务创建一个深度神经网络完全有意义。

但是这里有什么收获呢?

为了创建可解决此分类问题的深度神经网络,以使最终的 AI 智能体可以击败该游戏中任何级别的任何对手,我们的输入数据需要数千小时的视频数据分配到不同级别的游戏中, 不同的玩家,采用不同的方法赢得比赛,因此我们的神经网络可以以最佳方式概括映射。 获得更多数据的原因是为了避免欠拟合。 此外,过拟合中的大量数据也可能是一个问题,但是正则化是根据给定数据将模型推广到最佳状态的可能解决方案。 因此,我们看到,即使在获得了数千小时的专家播放器的视频数据(即非常高的数据量)之后,这种有监督的学习方法似乎也不是一个很好的解决方案。 这是因为,与其他应用的 AI 问题不同,此处的数据集是动态的而不是静态的。

此处的训练数据是连续的,新的框架在游戏世界中不断出现。 现在,问问自己我们人类如何学习这项任务,答案很简单,即我们通过与环境交互而不是看着其他人与环境交互来学习最好。 因此,AI 智能体可以尝试通过与环境交互来更好地学习,并通过一系列的反复试验来逐步发展其学习成果。

在现实世界和游戏世界中,环境通常是随机,其中可能发生许多事件。 由于所有事件都与某种发生概率相关联,因此可以对它们进行统计分析,但不能精确确定。 假设在给定的e环境中,我们只有三个动作来执行abc,但是每个动作都有一些与之相关的某种不确定性,即它们的出现机会是随机的,并且它们中的任何一个都可以发生,但是每个结果都不确定。 对于监督分类问题,我们认为环境是确定性的,其中确定了与特定动作相关的结果,并且结果是精确的预测,即特定类别(目标标签)。 在继续讨论该主题之前,让我们看一下两种环境之间的区别:

  • 确定性环境:由于没有不确定性,智能体的动作可以唯一地确定结果的环境。 例如,在国际象棋中,您将一块从一个正方形移到另一个正方形。 因此,确定结果,即结果平方。 没有随机性。
  • 随机环境:每个动作都与某种程度的随机性相关联的环境,因此,无论我们采取何种行动,都无法确定结果。 例如,将飞镖投掷到旋转的棋盘上或掷骰子。 在这两种情况下,结果均不确定。

因此,对于基于随机环境的问题,似乎最好的学习方法是尝试各种可能性。 因此,与其通过监督分类将其解决为模式识别问题,不如通过试错法更好,在这种方法中,结果标签将替换为量化特定操作对完成给定问题的最终目的的有用性的奖励。 声明。

这产生了环境-智能体交互方法,我们在第 1 章,“深度强化–架构和框架”中讨论了该方法,在该系统中,我们设计了一种使智能体与环境交互的系统。 首先,通过传感器感知状态,通过效应器对环境执行一些操作,然后接收反馈,即对所采取操作的奖励,如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-twPFi6LG-1681786426185)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/c7f38da6-965e-4b9f-9149-bc088223f8bd.png)]

根据传感器在特定时间步长感测环境时接收到的信号,此处的状态基本上是智能体对环境的看法。

基于模型的学习和无模型学习

在第 3 章,“马尔可夫决策过程”中,我们使用状态,动作,奖励,转移模型和折扣因子来解决我们的马尔可夫决策过程,即 MDP 问题。 因此,如果 MDP 问题的所有这些元素均可用,我们可以轻松地使用规划算法为目标提出解决方案。 这种类型的学习称为基于模型的学习,其中 AI 智能体将与环境交互,并基于其交互,将尝试近似环境的模型,即状态转换模型。 给定模型,现在智能体可以尝试通过值迭代或策略迭代找到最佳策略。

但是,对于我们的 AI 智能体来说,学习环境的显式模型不是必需的。 它可以直接从与环境的交互中得出最佳策略,而无需构建模型。 这种学习称为无模型学习。 无模型学习涉及在没有具体环境模型的情况下预测某个策略的值函数。

可以使用两种方法完成无模型学习:

  • 蒙特卡洛学习
  • 时差学习

我们将在以下主题中讨论它们两者。

蒙特卡洛学习

蒙特卡洛(Monte Carlo)是用于模型免费学习的最简单方法,在该方法中,智能体会观察剧集中前进的所有步骤(即前瞻)的回报。 因此,在时间t时的总估计报酬为R[t]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZyJPuRjF-1681786426185)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/142010fa-0c37-4b54-b47d-e27ca48bd3d6.png)]

这里,γ是折扣因子,T是剧集结束的时间步长。 我们可以使用以下代码初始化蒙特卡洛学习技术:

代码语言:javascript复制
Initialize:

     i.e. the policy to be evaluated
    V i.e. an arbitrary state-value function
    Returns(s) = empty list 
    #here Returns(s) refer to returns for a particular state i.e. the series of rewards the agent receives from that state onward

Repeat forever:

    Generate an episode using the current 
    For each state 's' appearing in the episode perform the following:
        R = returns following the first occurrence of 's'
        Append R to Returns(s)
        V(s) = Average(Returns(s))
    Update policy as per V

时差学习

与在蒙特卡洛学习中我们要全面展望未来不同,在时间差异学习中,我们只有一个展望,也就是说,我们只观察到剧集的下一步:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J57hgx4d-1681786426185)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/d82f0db8-7eb5-4425-b229-a08877c1ba7d.png)]

时间差异学习是一种用于学习值和策略迭代方法中的值函数以及 Q 学习中的 Q 函数的方法。

如果我们希望我们的 AI 智能体始终选择最大化折扣未来奖励的行动,那么我们需要某种时间差异学习。 为此,我们需要定义一个函数 Q,该函数表示当我们在状态s上执行动作a时最大的未来折扣。 因此,Q 函数表示给定状态下的动作质量。 使用它,我们可以通过仅了解当前状态和操作来估计最终得分,而在此之后无需进行任何操作。 因此,目标将是对具有最高 Q 值的状态采取该措施。 因此,我们必须通过称为 Q 学习的过程来学习此 Q 函数。

策略内和策略外学习

顾名思义,脱离策略的学习是独立于智能体行为的最优策略学习。 因此,您不需要一开始就使用特定的策略,并且即使通过随机动作开始,智能体也可以学习最佳策略,最终收敛到最佳策略。 Q 学习是非策略学习的一个例子。

另一方面,基于策略的学习通过执行当前策略并通过探索方法对其进行更新来学习最佳策略。 因此,基于策略的学习取决于开始时的策略。 SARSA 算法是基于策略学习的示例。

Q 学习

在强化学习中,我们希望 Q 函数Q(s, a)预测状态s的最佳动作,以便最大化未来的回报。 使用 Q 学习估计 Q 函数,该过程涉及使用贝尔曼方程通过一系列迭代更新 Q 函数的过程,如下所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y3Lg1VYW-1681786426185)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/8276b9a8-c086-4554-9c03-a77af4be5eb4.png)]

这里:

Q(s, a)为当前状态s和动作a对的Q

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ItZ5ii8W-1681786426185)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/ea5b424c-de3c-4fbc-a152-adbc346fd533.png)] =学习收敛速度

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WIBikiLs-1681786426186)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/48218f21-9c36-4878-ab3e-bb4cbf9ee090.png)] =未来奖励的折扣系数

Q(s', a')为在状态s下采取动作a之后,所得状态s'的状态动作对的Q

R表示即时奖励

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RTS7wies-1681786426186)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/84d0d09c-e36e-4493-bf36-f0ec2f63fccb.png)] =未来奖励

在状态空间和动作空间是离散的更简单的情况下,使用 Q 表实现 Q 学习,其中表代表状态,列代表动作。

Q 学习涉及的步骤如下:

  1. 随机初始化 Q 表
  2. 对于每个剧集,请执行以下步骤:
    1. 对于给定状态s,从 Q 表中选择动作a
    2. 执行动作a
    3. 奖励R和状态s'
    4. 通过以下方法更新当前状态操作对的 Q 值,即Q(s, a)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bvO3c4O9-1681786426186)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/8276b9a8-c086-4554-9c03-a77af4be5eb4.png)]

但是,这里并没有探索新路径,并且在大多数情况下,智能体正在利用已知路径。 因此,实现了一定程度的随机性,以使 AI 智能体有时通过采取随机动作而不是当前的最佳动作来随机探索新路径。 探索背后的原因是,它增加了获得比当前更好的路径(即新的最佳策略)的可能性:

代码语言:javascript复制
Create Q-table where rows represent different states and columns represent different actions
Initialize Q(s,a) arbitrarily
For each episode:
    Start with the starting state i.e. Initialize s to start
    Repeat for each step in the episode:
        Choose action a for s using the policy derived from Q
        [e.g. -greedy, either for the given 's' which 'a' has the max Q-value or choose a random action]
        Take the chosen action a, observe reward R and new state s'
        Update 

    until s is the terminal state
end

探索利用困境

下表总结了探索与利用之间的困境:

探索

利用

除了当前的最佳动作之外,随机选择其他动作,并希望获得更好的回报。

选择当前的最佳操作而不尝试其他操作。

因此,难题是 AI 是仅根据当前最佳策略基于动作信任已获悉的 Q 值,还是应该随机尝试其他动作以希望获得更好的回报,从而改善 Q 值,因此, 得出更好的最佳策略。

OpenAI Gym 山地车问题的 Q 学习

山地车是强化学习领域的标准测试问题。 它由动力不足的汽车组成,必须将陡峭的山坡驱动到标志点,如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9U6NBl9d-1681786426186)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/b2dea4b9-c53a-4380-a729-7330fa346818.png)]

这里的要点是重力要比汽车的引擎强,因此即使在全油门的情况下,汽车也无法在陡峭的斜坡上加速。 因此,汽车必须以相反的方向反向行驶来利用势能,然后再利用势能到达右上角的标志点。

在这里,状态空间是连续的,由两点定义:位置和速度。 对于给定的状态(即位置和速度),智能体可以采取三个离散的动作,即向前移动(向图中右上方),向相反方向(向图中左上方)或不使用引擎 ,即汽车处于空档。 智能体会收到负面奖励,直到达到目标状态。

Q 学习可以轻松地应用于具有离散状态空间和动作的环境,但是这个问题成为了强化学习算法的测试平台,因为它具有连续状态空间,并且需要离散化连续状态空间或函数逼近才能将其映射到离散类。

下面列出了山地车问题的技术细节,供您参考:

状态空间是二维且连续的。 它由位置和速度组成,具有以下值:

  • 位置(-1.2, 0.6)
  • 速度(-0.07, 0.07)

动作空间是离散的和一维的,具有三个选项:

  • (左,中,右)

每个时间步数都奖励 -1。

起始状态:

  • 位置:-0.5
  • 速度:0.0

终端状态条件:

  • 剧集在大于等于 0.6 的位置结束

正如我们现在看到的 Q 学习的参数一样,我们现在将研究解决山地车问题的 Q 学习的实现。

首先,我们将使用以下代码导入依赖项并检查山地车环境:

代码语言:javascript复制
#importing the dependencies

import gym
import numpy as np

#exploring Mountain Car environment

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

print("Action Set size :",env.action_space)
print("Observation set shape :",env.observation_space) 
print("Highest state feature value :",env.observation_space.high) 
print("Lowest state feature value:",env.observation_space.low) 
print(env.observation_space.shape) 

先前的打印语句输出以下内容:

代码语言:javascript复制
Making new env: MountainCar-v0
('Action Set size :', Discrete(3))
('Observation set shape :', Box(2,))
('Highest state feature value :', array([ 0.6 , 0.07]))
('Lowest state feature value:', array([-1.2 , -0.07]))
(2,)

因此,我们看到动作空间是一个离散集合,显示了三个可能的动作,状态空间是一个二维连续空间,其中一个维度满足位置,而另一个则满足汽车的速度。 接下来,我们将使用以下代码分配超参数,例如状态数,剧集数,学习率(初始和最小值),折扣因子伽玛,剧集中的最大步数以及 ε 贪婪的ε

代码语言:javascript复制
n_states = 40  # number of states
episodes = 10 # number of episodes

initial_lr = 1.0 # initial learning rate
min_lr = 0.005 # minimum learning rate
gamma = 0.99 # discount factor
max_steps = 300
epsilon = 0.05

env = env.unwrapped
env.seed(0)         #setting environment seed to reproduce same result
np.random.seed(0)   #setting numpy random number generation seed to reproduce same random numbers

我们的下一个任务是创建一个函数来对连续状态空间进行离散化。 离散化是将连续状态空间观察转换为离散状态空间集:

代码语言:javascript复制
def discretization(env, obs):

    env_low = env.observation_space.low
    env_high = env.observation_space.high

    env_den = (env_high - env_low) / n_states
    pos_den = env_den[0]
    vel_den = env_den[1]

    pos_high = env_high[0]
    pos_low = env_low[0]
    vel_high = env_high[1]
    vel_low = env_low[1]

    pos_scaled = int((obs[0] - pos_low)/pos_den)  #converts to an integer value
    vel_scaled = int((obs[1] - vel_low)/vel_den)  #converts to an integer value

    return pos_scaled,vel_scaled

现在,我们将通过初始化 Q 表并相应地更新 Q 值来开始实现 Q 学习算法。 在这里,我们将奖励值更新为当前位置与最低点(即起点)之间的绝对差值,以便它通过远离中心即最低点来最大化奖励。 这样做是为了实现更好的收敛:

代码语言:javascript复制
#Q table
#rows are states but here state is 2-D pos,vel
#columns are actions
#therefore, Q- table would be 3-D

q_table = np.zeros((n_states,n_states,env.action_space.n))
total_steps = 0
for episode in range(episodes):
      obs = env.reset()
      total_reward = 0
      # decreasing learning rate alpha over time
      alpha = max(min_lr,initial_lr*(gamma**(episode//100)))
      steps = 0
      while True:
          env.render()
          pos,vel = discretization(env,obs)

          #action for the current state using epsilon greedy
          if np.random.uniform(low=0,high=1) < epsilon:
                a = np.random.choice(env.action_space.n)
          else:
                a = np.argmax(q_table[pos][vel])
          obs,reward,terminate,_ = env.step(a) 
          total_reward  = abs(obs[0] 0.5)

          #q-table update
          pos_,vel_ = discretization(env,obs)
          q_table[pos][vel][a] = (1-alpha)*q_table[pos][vel][a]   alpha*(reward gamma*np.max(q_table[pos_][vel_]))
          steps =1
          if terminate:
                break
      print("Episode {} completed with total reward {} in {} steps".format(episode 1,total_reward,steps)) 

while True: #to hold the render at the last step when Car passes the flag
      env.render() 

根据学习情况,前面的 Q 学习器将以以下方式打印输出:

代码语言:javascript复制
Episode 1 completed with total reward 8433.30289388 in 26839 steps
Episode 2 completed with total reward 3072.93369963 in 8811 steps
Episode 3 completed with total reward 1230.81734028 in 4395 steps
Episode 4 completed with total reward 2182.31111239 in 6629 steps
Episode 5 completed with total reward 2459.88770998 in 6834 steps
Episode 6 completed with total reward 720.943914405 in 2828 steps
Episode 7 completed with total reward 389.433014729 in 1591 steps
Episode 8 completed with total reward 424.846699654 in 2362 steps
Episode 9 completed with total reward 449.500988781 in 1413 steps
Episode 10 completed with total reward 222.356805259 in 843 steps

这还将渲染一个环境,显示汽车在行驶并采取最佳路径并达到目标状态,如以下屏幕截图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LzLxULdK-1681786426186)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/69dbc107-d503-4cbe-b66c-c073c7a4a74d.png)]

山地赛车游戏的最终状态视图

因此,我们看到无模型学习可以从其与环境的交互中得出最佳策略,而无需创建环境模型。 因此,我们已经知道,Q 学习是一种无模型的时间差异学习,它通过估计 Q 函数来找到最佳状态动作选择策略。

深度 Q 网络

如果我们回想起第 2 章和“使用 OpenAI Gym 训练强化学习智能体”,我们曾尝试在其中实现基本的 Q 网络,之后我们针对一个实际问题研究了 Q 学习。 由于连续的状态和动作空间,使用 Q 表不是可行的解决方案。 而且,Q 表是特定于环境的,而不是通用的。 因此,我们需要一个模型,该模型可以将作为输入提供的状态信息映射到可能的一组动作的 Q 值。 在这里,神经网络开始发挥函数逼近器的作用,函数逼近器可以接受向量形式的状态信息输入,并学习将其映射为所有可能动作的 Q 值。

让我们讨论游戏环境中的 Q 学习问题以及深度 Q 网络的发展。 考虑将 Q 学习应用于游戏环境,该状态将由玩家,障碍物,对手等的位置来定义,但这将是特定于游戏的,即使在我们创建一个以某种方式表示该游戏所有可能状态的 Q 表,也不能在其他游戏环境中推广。

好吧,游戏环境有一个共同点,那就是全部由像素组成。 如果可以将像素输入可以映射到动作的模型中,则可以在所有游戏中将其推广。 DeepMind 的卷积神经网络实现具有游戏图像帧,其中输入和输出是该环境中每个可能动作的 Q 值。 卷积神经网络由三个卷积层和两个全连接层组成。 卷积神经网络CNN)的一个元素是池化层,在此已避免。 使用池化层的主要原因是在图像中进行对象检测的情况下,其中图像中对象的位置并不重要,而在此处,在游戏框架中对象的位置非常重要时,则不然。

因此,在游戏环境中,深度 Q 网络DQN)由连续的游戏帧组成,作为捕获动作的输入,并为游戏中所有可能的动作输出 Q 值。 游戏。 由于将深度神经网络用作 Q 函数的函数逼近器,因此此过程称为深度 Q 学习。

与 Q 网络相比,深度 Q 网络具有更强的泛化能力。 为了将 Q 网络转换为深度 Q 网络,我们需要进行以下改进:

  • 使用卷积神经网络代替单层神经网络
  • 使用经验回放
  • 分离目标网络来计算目标 Q 值

在以下主题中,我们将详细讨论每个参数:

使用卷积神经网络代替单层神经网络

我们的游戏环境是视频,而卷积神经网络在计算机视觉方面已经显示了最新的成果。 而且,游戏框架中物体检测的水平应该接近人类水平的能力,并且卷积神经网络从图像中学习表示,类似于人类原始视觉皮层的行为。

DeepMind 在其 DQN 网络中使用了三个卷积层和两个全连接层,从而在 Atari 游戏中实现了超人水平的表现,如以下流程图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4KJBBJg5-1681786426187)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/373d89a3-057c-444c-91b2-bbe4eeb5e1b4.png)]

使用经验回放

添加到深度 Q 网络的另一个重要功能是经验回放。 该功能背后的想法是,智能体可以存储其过去的经验,并分批使用它们来训练深度神经网络。 通过存储经验,智能体可以随机抽取批量,并帮助网络从各种数据中学习,而不仅仅是对即时经验的决策正式化。 这些经历中的每一个都以包括状态,动作,奖励下一个状态的四维向量的形式存储。

为了避免存储问题,经验回放的缓冲区是固定的,并且随着新体验的存储,旧体验将被删除。 为了训练神经网络,从缓冲区中提取均匀批量的随机经验。

分离目标网络来计算目标 Q 值

生成目标 Q 值的单独网络是一项重要功能,它使深层 Q 网络具有独特性。 由此单独的目标网络生成的 Q 值用于计算智能体在训练过程中采取的每项操作之后的损失。 使用两个网络而不是一个网络的原因在于,由于每个步骤的权重变化,主要的 Q 网络值在每个步骤中都在不断变化,这使得从该网络生成的 Q 值不稳定。

为了获得稳定的 Q 值,使用了另一个神经网络,其权重与主要 Q 网络相比变化缓慢。 这样,训练过程更加稳定。 DeepMind 的文章也发表在这个页面中。 他们发现这种方法能够稳定训练过程。

改编自 Minh 等人(2015),以下是 DQN 的伪代码:

代码语言:javascript复制
Input: the image(game frame) pixels 

Initialize replay memory D for experience replay
Initialize action-value function`Q`i.e. primary neural network with random weight   
Initialize target action-value function QT i.e. target neural network with weights 

for each episode do
    Initialize sequence  and preprocessed sequence 
    for t = 0 to max_step in an episode do
        Choose  using -greedy such that 

        Perform action 
        Observe reward  and image 
        Set  and preprocess 
        Store transition  in D

        // experience replay
        Sample random batch of transitions  from D
        Set 
        Compute the cost function = 
        Perform gradient descent on the cost function w.r.t. the primary network parameter θ

        // periodic update of target network
        After every C steps reset , i.e., set 

    until end of episode 
end

深度 Q 网络以及其他方面的进步

随着更多的研究和更多的时间,深度 Q 网络已经进行了许多改进,从而获得了更好的架构,从而提供了更高的表现和稳定性。 在本节中,我们将仅讨论两种著名的架构,即双重 DQN决斗 DQN

双 DQN

使用**双重 DQN(DDQN)**的原因是常规 DQN 高估了在给定状态下可能采取的措施的 Q 值。 在常规 DQN 中,所有动作之间的高估都不相等。 因此,问题仍然存在:否则,所有行动之间的均等估计就不会成为问题。 结果,某些次优的行为获得了更高的值,因此学习最佳策略的时间增加了。 这导致我们对常规 DQN 架构进行了少量修改,并导致了所谓的 DDQN,即双深度 Q 网络。

在 DDQN 中,我们不是在训练过程中计算目标 Q 值时取最大 Q 值,而是使用主要网络选择操作,并使用目标网络为该操作生成目标 Q 值。 这使动作脱钩。 从目标 Q 网络中进行选择,生成目标 Q 值,从而减少高估,并有助于更快地进行训练。 DDQN 中的目标 Q 值通过以下公式更新:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kOXR3cqG-1681786426187)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/da95ebb9-7b3c-454e-950c-125072e0b43f.png)]

这里:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-er2kcGtE-1681786426187)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/9ce748d1-69db-4379-9a41-8f3ee9dc46c2.png)]代表主网络的权重,并且

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6oA0yQyD-1681786426187)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/e6c5b010-6999-4954-bfb0-64848de92322.png)]代表目标网络的权重。

决斗 DQN

决斗 DQN 的情况下,Q 值已被修改为状态的值函数和操作的优势函数的总和。 值函数V(s)量化处于状态s的有用性或优势,优势函数A(a)量化行为的优势代替其他可能的操作。 因此,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3xGd7ozv-1681786426187)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/f31160d6-e246-41db-84d5-91fc9bd2495c.png)]

决斗 DQN 具有独立的网络来计算值和优势函数,然后将它们组合回以获取 Q 函数的值。 将值和优势的计算脱钩的原因在于,对于给定状态下的每个动作,智能体不必照顾不必要的值函数。 因此,将这些计算去耦会导致鲁棒的状态动作 Q 值。

用于 OpenAI Gym 山地车问题的深度 Q 网络

在针对山地车问题实现 Q 学习时,我们已经讨论了环境。 让我们直接深入实现一个深度 Q 网络来解决山地车问题。 首先,我们将使用以下代码导入所需的库:

代码语言:javascript复制
#importing the dependencies

import numpy as np
import tensorflow as tf
import gym

让我们讨论一下 DQN 类,其中包含深度 Q 网络的架构:

  • __init__selflearning_rategamman_featuresn_actionsepsilonparameter_changing_pointermemory_size):默认构造器,用于分配超参数,例如:
    • learning_rate
    • gamma,即折扣因子
    • n_feature:状态中的特征数,即状态中的尺寸数
    • epsilon:ε 贪婪条件的阈值,以利用或探索动作
  • build_networks():使用 Tensorflow 创建主要和目标网络
  • target_params_replaced(self):用主要网络参数替换目标网络参数
  • store_experience(self,obs,a,r,obs_):存储经验,即(状态,动作,奖励,新状态)的元组
  • fit(self):训练我们的深度 Q 网络
  • epsilon_greedy(self,obs):对于给定的观察状态,要采取的操作,即根据现有策略利用操作或随机探索新操作

可以使用以下代码定义具有main函数的 DQN 类的架构:

代码语言:javascript复制
class DQN:
    def __init__(self,learning_rate,gamma,n_features,
                 n_actions,epsilon,parameter_changing_pointer,memory_size):
    ....
    def build_networks(self):
    ....
    def target_params_replaced(self):
    ....
    def store_experience(self,obs,a,r,obs_):
    ....
    def fit(self):
    ....
    def epsilon_greedy(self,obs): 
    ....

if __name__ == "__main__":
    ....

__init__:在以下代码片段中解释了默认构造器以及注释:

代码语言:javascript复制
def __init__(self,learning_rate,gamma,n_features,n_actions,epsilon,parameter_changing_pointer,memory_size):

        self.learning_rate = learning_rate
        self.gamma = gamma
        self.n_features = n_features
        self.n_actions = n_actions
        self.epsilon = epsilon
        self.batch_size = 100
        self.experience_counter = 0
        self.experience_limit = memory_size
        self.replace_target_pointer = parameter_changing_pointer
        self.learning_counter = 0
        self.memory = np.zeros([self.experience_limit,self.n_features*2 2]) #for experience replay

        self.build_networks()
        #to fetch parameters under the collection : 'primary_network_parameters'
        p_params = tf.get_collection('primary_network_parameters')

        #to fetch parameters under the collection : 'target_network_parameters'
        t_params = tf.get_collection('target_network_parameters')

        #replacing tensor replace the target network parameters with primary network parameters
        self.replacing_target_parameters = [tf.assign(t,p) for t,p in zip(t_params,p_params)]

        self.sess = tf.Session()
        self.sess.run(tf.global_variables_initializer())

现在让我们初始化build_networks(self)。 它是构建主要网络和目标网络的函数:

  • variable_scopeprimary_network下创建主要网络参数,并在primary_network_parameters集合中创建
  • variable_scopetarget_network下创建目标网络参数,并在target_network_parameters集合中创建目标网络参数
  • 这两个参数具有相同的结构,即:
    • w1:与输入层关联的权重矩阵
    • b1:与输入层关联的偏置向量
    • ReLU:信号从输入到隐藏层的激活函数
    • w2:与隐藏层关联的权重矩阵
    • b2:与隐藏层关联的偏置向量
  • 计算主网络输出的 Q 值与目标网络输出的 Q 值之间的损失
  • 使用 adam 优化器将损失降到最低

我们将使用以下代码定义build_networks(self)函数:

代码语言:javascript复制
def build_networks(self):
        #primary network
        hidden_units = 10
        self.s = tf.placeholder(tf.float32,[None,self.n_features])
        self.qtarget = tf.placeholder(tf.float32,[None,self.n_actions])

        with tf.variable_scope('primary_network'):
                c = ['primary_network_parameters', tf.GraphKeys.GLOBAL_VARIABLES]
                # first layer
                with tf.variable_scope('layer1'):
                    w1 = tf.get_variable('w1',[self.n_features,hidden_units],initializer=tf.contrib.layers.xavier_initializer(),dtype=tf.float32,collections=c)
                    b1 = tf.get_variable('b1',[1,hidden_units],initializer=tf.contrib.layers.xavier_initializer(),dtype=tf.float32,collections=c)
                    l1 = tf.nn.relu(tf.matmul(self.s, w1)   b1)

                # second layer
                with tf.variable_scope('layer2'):
                    w2 = tf.get_variable('w2',[hidden_units,self.n_actions],initializer=tf.contrib.layers.xavier_initializer(),dtype=tf.float32,collections=c)
                    b2 = tf.get_variable('b2',[1,self.n_actions],initializer=tf.contrib.layers.xavier_initializer(),dtype=tf.float32,collections=c)
                    self.qeval = tf.matmul(l1, w2)   b2

        with tf.variable_scope('loss'):
                self.loss = tf.reduce_mean(tf.squared_difference(self.qtarget,self.qeval))

        with tf.variable_scope('optimizer'):
                self.train = tf.train.AdamOptimizer(self.learning_rate).minimize(self.loss)

        #target network
        self.st = tf.placeholder(tf.float32,[None,self.n_features])

        with tf.variable_scope('target_network'):
                c = ['target_network_parameters', tf.GraphKeys.GLOBAL_VARIABLES]
                # first layer
                with tf.variable_scope('layer1'):
                        w1 = tf.get_variable('w1', [self.n_features,hidden_units],initializer=tf.contrib.layers.xavier_initializer(),dtype=tf.float32,collections=c)
                        b1 = tf.get_variable('b1', [1,hidden_units],initializer=tf.contrib.layers.xavier_initializer(),dtype=tf.float32,collections=c)
                        l1 = tf.nn.relu(tf.matmul(self.st, w1)   b1)

                # second layer
                with tf.variable_scope('layer2'):
                        w2 = tf.get_variable('w2',[hidden_units,self.n_actions],initializer=tf.contrib.layers.xavier_initializer(),dtype=tf.float32,collections=c)
                        b2 = tf.get_variable('b2',[1,self.n_actions],initializer=tf.contrib.layers.xavier_initializer(),dtype=tf.float32,collections=c)
                        self.qt = tf.matmul(l1, w2)   b2

现在,我们将使用以下代码定义target_params_replaced(self)。 运行将主网络参数分配给目标网络参数的张量操作的函数:

代码语言:javascript复制
def target_params_replaced(self):
        self.sess.run(self.replacing_target_parameters)

现在,我们将定义store_experience(self,obs,a,r,obs_),该函数用于将每种体验(即(状态,动作,奖励,新状态)的元组)存储在其体验缓冲区中,通过该数组可以训练主要目标,例如以下代码:

代码语言:javascript复制
def store_experience(self,obs,a,r,obs_):
        index = self.experience_counter % self.experience_limit
        self.memory[index,:] = np.hstack((obs,[a,r],obs_))
        self.experience_counter =1

在这里,我们将定义fit(self),该函数是通过从经验缓冲区中选择一批来训练网络,计算q_target的张量值,然后最小化qeval之间的损失(即输出)的函数。 来自主网络的数据)和q_target(即使用目标网络计算的 Q 值)。 我们将使用以下代码定义函数:

代码语言:javascript复制
def fit(self):
        # sample batch memory from all memory
        if self.experience_counter < self.experience_limit:
               indices = np.random.choice(self.experience_counter, size=self.batch_size)
        else:
               indices = np.random.choice(self.experience_limit, size=self.batch_size)

        batch = self.memory[indices,:]
        qt,qeval = self.sess.run([self.qt,self.qeval],feed_dict={self.st:batch[:,-self.n_features:],self.s:batch[:,:self.n_features]})

        qtarget = qeval.copy() 
        batch_indices = np.arange(self.batch_size, dtype=np.int32)
        actions = self.memory[indices,self.n_features].astype(int)
        rewards = self.memory[indices,self.n_features 1]
        qtarget[batch_indices,actions] = rewards   self.gamma * np.max(qt,axis=1)

        self.sess.run(self.train,feed_dict = {self.s:batch[:,:self.n_features],self.qtarget:qtarget})

        #increasing epsilon 
        if self.epsilon < 0.9:
                self.epsilon  = 0.0002

        #replacing target network parameters with primary network parameters 
        if self.learning_counter % self.replace_target_pointer == 0:
                self.target_params_replaced()
                print("target parameters changed")

        self.learning_counter  = 1

我们已经讨论了探索与利用难题。 ε 贪婪方法是用于选择阈值ε并产生随机数的方法之一。 如果小于ε,我们将遵循相同的策略;如果大于ε,我们将随机探索行动,反之亦然。 在epsilon_greedy(self,obs)中,我们以动态方式实现了 ε 贪婪方法,其中在fit(self)函数中,我们在每个学习步骤中都增加了ε的值:

代码语言:javascript复制
def epsilon_greedy(self,obs):
        #epsilon greedy implementation to choose action
        if np.random.uniform(low=0,high=1) < self.epsilon:
            return np.argmax(self.sess.run(self.qeval,feed_dict={self.s:obs[np.newaxis,:]}))
        else:
            return np.random.choice(self.n_actions)

以下是main函数,该函数创建上一个 DQN 类的对象,使用 Gym 来获取MountainCar-v0环境,并训练智能体程序来解决问题。 像在 Q 学习中一样,在这里我们还更新了奖励值,将其作为当前位置与最低点位置(即起点)之间的绝对差值,从而使其偏离中心而最大化了奖励:

代码语言:javascript复制
if __name__ == "__main__":
      env = gym.make('MountainCar-v0')
      env = env.unwrapped
      dqn = DQN(learning_rate=0.001,gamma=0.9,n_features=env.observation_space.shape[0],n_actions=env.action_space.n,epsilon=0.0,parameter_changing_pointer=500,memory_size=5000)
      episodes = 10
      total_steps = 0

      for episode in range(episodes):
            steps = 0 
            obs = env.reset()
            episode_reward = 0
            while True:
                env.render()
                action = dqn.epsilon_greedy(obs)
                obs_,reward,terminate,_ = env.step(action)
                reward = abs(obs_[0] 0.5)
                dqn.store_experience(obs,action,reward,obs_)
                if total_steps > 1000:
                      dqn.fit()
                episode_reward =reward
                if terminate:
                      break
                obs = obs_
                total_steps =1
                steps =1
            print("Episode {} with Reward : {} at epsilon {} in steps {}".format(episode 1,episode_reward,dqn.epsilon,steps))

      while True: #to hold the render at the last step when Car passes the flag
            env.render() 

前面的程序将打印以下内容:

代码语言:javascript复制
target parameters changed
target parameters changed
target parameters changed
target parameters changed
target parameters changed
target parameters changed
Episode 1 with Reward : 1399.25710453 at epsilon 0.5948 in steps 3974
target parameters changed
Episode 2 with Reward : 168.166352703 at epsilon 0.6762 in steps 406
target parameters changed
Episode 3 with Reward : 67.6246277944 at epsilon 0.7568 in steps 402
Episode 4 with Reward : 53.1292577484 at epsilon 0.7942 in steps 186
target parameters changed
Episode 5 with Reward : 38.90009005 at epsilon 0.818 in steps 118
Episode 6 with Reward : 60.9286778233 at epsilon 0.8738 in steps 278
target parameters changed
Episode 7 with Reward : 72.433268035 at epsilon 0.9002 in steps 257
Episode 8 with Reward : 80.7812592557 at epsilon 0.9002 in steps 251
target parameters changed
Episode 9 with Reward : 92.123978864 at epsilon 0.9002 in steps 234
Episode 10 with Reward : 38.7923903502 at epsilon 0.9002 in steps 126

在这里,它收敛很快,但是还取决于对动作的探索和利用以及参数和超参数的初始化。 这还将渲染一个环境,显示汽车在行驶并采取最佳路径并达到目标状态,如以下屏幕截图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H509Acmx-1681786426188)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/c662d7ff-84d0-4610-b63d-daec721d3143.png)]

接下来,在以下主题中,我们尝试实现一个深度 Q 网络来解决 OpenAI Gym 中的 Cartpole 问题。

用于 OpenAI Gym Cartpole 问题的深度 Q 网络

Cartpole 是 MDP 环境中最简单的问题之一,如以下屏幕快照所示。 它由一个在水平轴上移动的推车组成,该推车的中心处固定有一根可旋转的杆。 目的是采取行动,使电杆保持接近垂直且不会向下旋转。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fYCYul6i-1681786426188)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/59c7c998-1669-4892-a27c-9b375e62a033.png)]

车杆环境中的状态是一个 4 维连续空间,其中每个维如下:

  • x:表示推车位置(最小值为 -2.4,最大值为 2.4)
  • x_dot:表示推车速度(最小值为-∞
  • theta:显示以弧度为单位的角度(最小值为 -0.73,最大值为 0.73)
  • theta_dot:显示角速度(最小值为-∞,最大值为

在给定状态下的每一步,都有两种可能的动作,即推车可以向左或向右移动,并且每一步收到的奖励为 1。这里,只要杆子靠近垂直,推车在边界内。 如果发生以下情况,则剧集被视为结束:

  • 极点下降超过某个角度,即超过 ±0.20944 弧度
  • 推车超出框架左侧或右侧太远,即超出 ±2.4

因此,该问题的目的是将杆保持在接近垂直的位置,而推车不会越过边界越长。

为了为 Cartpole 问题实现深层 Q 网络,我们将导入先前创建的 DQN 类。 请按照以下代码在 Cartpole 环境中实现深度 Q 网络。 如果连续 100 次试验的平均奖励大于或等于 195,则认为该问题已解决:

代码语言:javascript复制
#Importing dependencies
import gym
import numpy as np

#Importing the DQN class created preceding
from Deep_Q_Network_Mountain_Car import DQN

现在,我们将使用以下代码探索 Cartpole 环境:

代码语言:javascript复制
env = gym.make('CartPole-v0')
env = env.unwrapped

print(env.action_space)
print(env.observation_space)
print(env.observation_space.high)
print(env.observation_space.low)
print("Position extreme threshold value:",env.x_threshold)
print("Angle extreme threshold value:",env.theta_threshold_radians)

先前的打印语句输出以下内容:

代码语言:javascript复制
Making new env: CartPole-v0
Discrete(2)
Box(4,)
[ 4.80000000e 00 3.40282347e 38 4.18879020e-01 3.40282347e 38]
[ -4.80000000e 00 -3.40282347e 38 -4.18879020e-01 -3.40282347e 38]
Position extreme threshold value: 2.4
Angle extreme threshold value: 0.20943951023931953

在此,观察空间的高/低值遵循以下顺序(位置,速度,角度,角速度)

下面的代码是我们创建 DQN 上一类的对象,使用 Gym 来获取 Cartpole-v0 环境以及训练智能体程序以解决问题的主要部分。 在这里,我们将奖励值更新为位置与极端位置之差和角度与极端极角之差的总和,因为远离极限位置,角度将变得更小,并且更接近推车的中央,因此奖励应该更高。 这样做是为了实现更好的主网络融合。 我们将使用此奖励进行学习,但是为了计算总体成功程度,我们将使用原始奖励:

代码语言:javascript复制
dqn = DQN(learning_rate=0.01,gamma=0.9,n_features=env.observation_space.shape[0],n_actions=env.action_space.n,epsilon=0.0,parameter_changing_pointer=100,memory_size=2000)

episodes = 150
total_steps = 0
rew_ep = []

for episode in range(episodes):
    steps = 0
    obs = env.reset()
    episode_reward = 0
    while True:
        env.render()
        action = dqn.epsilon_greedy(obs)
        obs_,reward,terminate,_ = env.step(action)

        #smaller the theta angle and closer to center then better should be the reward
        x, vel, angle, ang_vel = obs_
        r1 = (env.x_threshold - abs(x))/env.x_threshold - 0.8
        r2 = (env.theta_threshold_radians - abs(angle))/env.theta_threshold_radians - 0.5
        reward = r1   r2

        dqn.store_experience(obs,action,reward,obs_)
        if total_steps > 1000:
            dqn.fit()
        episode_reward =reward
        if terminate:
            break
        obs = obs_
        total_steps =1
        steps =1
    print("Episode {} with Reward : {} at epsilon {} in steps {}".format(episode 1,episode_reward,dqn.epsilon,steps))
    rew_ep.append(episode_reward)

print("Mean over last 100 episodes are: ",np.mean(rew_ep[50:]))
while True: #to hold the render at the last step when Car passes the flag
    env.render()

前面的程序将打印输出,如下所示:

代码语言:javascript复制
.................
.................
.................
Episode 145 with Reward : 512.0 at epsilon 0.9002 in steps 511
target parameters changed
target parameters changed
target parameters changed
target parameters changed
target parameters changed
target parameters changed
Episode 146 with Reward : 567.0 at epsilon 0.9002 in steps 566
target parameters changed
target parameters changed
target parameters changed
target parameters changed
target parameters changed
target parameters changed
target parameters changed
target parameters changed
target parameters changed
target parameters changed
target parameters changed
target parameters changed
target parameters changed
Episode 147 with Reward : 1310.0 at epsilon 0.9002 in steps 1309
Episode 148 with Reward : 22.0 at epsilon 0.9002 in steps 21
target parameters changed
target parameters changed
target parameters changed
target parameters changed
target parameters changed
target parameters changed
target parameters changed
target parameters changed
target parameters changed
target parameters changed
target parameters changed
target parameters changed
Episode 149 with Reward : 1171.0 at epsilon 0.9002 in steps 1170
target parameters changed
target parameters changed
target parameters changed
target parameters changed
target parameters changed
target parameters changed
target parameters changed
target parameters changed
target parameters changed
target parameters changed
Episode 150 with Reward : 1053.0 at epsilon 0.9002 in steps 1052
Mean over last 100 episodes are: 248.72999999999999

由于输出日志太长,因此在这里,我们的输出适合最近六个剧集以及最近 100 个剧集的每集平均奖励。

用于 OpenAI Gym Atari Breakout 的深度 Q 网络

Breakout 环境是 Atari 的 Nolan Bushnell,Steve Bristow 和 Steve Wozniak 团队开发的。与我们在山地车 Cartpole 中看到的状态相比,Atari Breakout 环境的状态要大得多。 或“冰湖”。 状态空间与我们在 Atari Pong 中看到的范围相似。 因此,学习收敛需要很长时间。 以下屏幕快照说明了 Atari Breakout 环境的初始图像帧:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W7SGjHb6-1681786426188)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/0ddf6045-7c46-45a8-b644-11756f45a966.png)]

Breakout-v0 环境的屏幕截图

观察空间是连续的,包含图像帧的像素值,并且动作空间是离散的,包括四个不同的动作。 每个图像帧的大小为210 * 160 * 3(高度为 210 像素,宽度为 160 像素,具有 3 个颜色通道,即 RGB)。 因此,我们可以拍摄灰度图像帧,因为不会丢失任何信息,并且尺寸将变为210 * 160。 仅拍摄状态的图像帧将不起作用,因为它无法捕获任何运动信息。 因此,我们将为每个状态堆叠四个连续的图像帧。 因此,状态大小将是4 * 210 * 160 = 134,440。 对于 Atari Breakout,在一定程度上降低采样率不会造成任何信息丢失。 此外,我们还可以裁剪图像框架以避免图像的不必要部分,并保留可能包含足够信息来玩游戏的重要部分。

首先,使用以下代码检查环境:

代码语言:javascript复制
import gym

env = gym.make('Breakout-v0')
s = env.reset()
print("State space shape : ",env.observation_space)
print("Action space shape : ",env.action_space)
print("Type of actions : ",env.unwrapped.get_action_meanings())

这将输出以下语句:

代码语言:javascript复制
State space shape : Box(210, 160, 3)
Action space shape : Discrete(4)
Type of actions : ['NOOP', 'FIRE', 'RIGHT', 'LEFT']

因此,我们得到了状态空间和动作空间的形状,以及球拍可以采取的四种不同类型的动作,即无动作(无操作的简称),开火(上方目标砖的球),向右走, 或向左移动以阻止球下降。

我们还检查示例裁剪并查看差异,如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oNkRu5fL-1681786426188)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/74724bff-3706-4ea9-990d-707020570fbf.png)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DiKRrrTS-1681786426188)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/1bbe7bb3-0e3c-48ae-97e8-cb276076ab45.png)]

裁剪前(左)和裁剪后(右)

游戏以下列方式进行:

  • 底部的球拍将球击发,击中砖块以摧毁屏幕的顶层
  • 击中砖块后,球反弹回来
  • 球拍应向左或向右移动以击中球并阻止其掉落
  • 如果球落到下方,也就是说,从球拍下方的屏幕上移开,则游戏结束且玩家输
  • 如果球从球拍弹起,它将再次上升,从墙壁上弹起并击中更多砖块

因此,目标是通过摧毁所有积木而不让球进入球拍下来赢得比赛。

让我们开始实现一个深层的 Q 网络,以使我们的智能体学习 Atari Breakout 的游戏。 首先,我们将使用以下代码导入必要的库:

代码语言:javascript复制
#Importing the dependencies

import numpy as np
import tensorflow as tf
import gym
from scipy.misc import imresize

具有以下main函数的class DQN的架构可以使用以下代码定义:

代码语言:javascript复制
class DQN:

    def __init__(self,learning_rate,gamma,n_features,n_actions,epsilon,parameter_changing_pointer,memory_size,epsilon_incrementer):
    ....
    def add_layer(self,inputs,w_shape=None,b_shape=None,layer=None,ctivation_fn=None,c=None,isconv=False:
    ....
    def weight_variable(self,w_shape,layer,c):
    ....
    def bias_variable(self,b_shape,layer,c):
    ....
    def conv(self,inputs,w):
    ....
    def build_networks(self):
    ....
    def target_params_replaced(self):
    ....
    def store_experience(self,obs,a,r,obs_):
    ....
    def fit(self):
    ....
    def epsilon_greedy(self,obs): 
    ....

if __name__ == "__main__":
    ....

让我们讨论 DQN 类及其参数,它包含一个深度 Q 网络的架构:

  • __init__(self,learning_rate,gamma,n_features,n_actions,epsilon,parameter_changing_pointer,memory_size):分配超参数的默认构造器,例如:
    • learning_rate
    • gamma:即折扣系数
    • n_feature:状态下的特征数量,即状态下的尺寸数
    • epsilon:利用或探索行为的ε贪婪条件的阈值
    • parameter_changing_pointer:一个整数值(例如n),指定在每n次迭代之后,将主网络的参数复制到目标网络
    • memory_size:体验回复的最大长度
  • add_layer(self,inputs,w_shape=None,b_shape=None,layer=None,activation_fn=None,c=None,isconv=False):在神经网络中创建层的函数
  • weight_variable(self,w_shape,layer,c):创建权重参数的函数
  • bias_variable(self,b_shape,layer,c):创建偏置参数的函数
  • conv(self,inputs,w):对图像帧执行卷积运算的函数
  • build_networks():此函数用于使用 TensorFlow 创建主要网络和目标网络
  • target_params_replaced(self):用于将目标网络参数替换为主网络参数
  • store_experience(self,obs,a,r,obs_):帮助存储经验,即(状态,动作,奖励,新状态)的元组
  • fit(self):用于训练我们的深度 Q 网络
  • epsilon_greedy(self,obs):它可以帮助我们针对给定的观察状态选择正确的操作,即按照现有策略利用操作或随机探索新操作

现在,使用以下代码定义__init__ default构造器:

代码语言:javascript复制
def __init__(self,learning_rate,gamma,n_features,n_actions,epsilon,parameter_changing_pointer,
             memory_size,epsilon_incrementer):

        tf.reset_default_graph()
        self.learning_rate = learning_rate
        self.gamma = gamma
        self.n_features = n_features
        self.n_actions = n_actions
        self.epsilon = epsilon
        self.batch_size = 32
        self.experience_counter = 0
        self.epsilon_incrementer = epsilon_incrementer
        self.experience_limit = memory_size
        self.replace_target_pointer = parameter_changing_pointer
        self.learning_counter = 0
        self.memory = [] #np.zeros([self.experience_limit,4]) #for experience replay

        self.build_networks()
        p_params = tf.get_collection('primary_network_parameters')
        t_params = tf.get_collection('target_network_parameters')
        self.replacing_target_parameters = [tf.assign(t,p) for t,p in zip(t_params,p_params)]

        self.sess = tf.Session()
        self.sess.run(tf.global_variables_initializer())

以下代码定义了add_layer函数,该函数通过提供isconv的布尔参数来帮助根据卷积的要求创建不同的层或全连接层,如果isconvtrue,则表示是卷积层:

代码语言:javascript复制
def add_layer(self,inputs,w_shape=None,b_shape=None,layer=None,activation_fn=None,c=None,isconv=False):
        w = self.weight_variable(w_shape,layer,c)
        b = self.bias_variable(b_shape,layer,c)
        eps = tf.constant(value=0.000001, shape=b.shape)
        if isconv:
            if activation_fn is None:
                return self.conv(inputs,w) b eps
            else:
                h_conv = activation_fn(self.conv(inputs,w) b eps) 
                return h_conv
        if activation_fn is None:
            return tf.matmul(inputs,w) b eps
        outputs = activation_fn(tf.matmul(inputs,w) b eps)
        return outputs

接下来,我们具有weight_variablebias_variable函数。 以下代码用于定义权重参数:

代码语言:javascript复制
def weight_variable(self,w_shape,layer,c):
        return tf.get_variable('w' layer,w_shape,initializer=tf.contrib.layers.xavier_initializer(),
                                     dtype=tf.float32,collections=c)

定义偏置参数的代码:

代码语言:javascript复制
def bias_variable(self,b_shape,layer,c):
        return tf.get_variable('b' layer,b_shape,initializer=tf.contrib.layers.xavier_initializer(),
                                     dtype=tf.float32,collections=c)

现在让我们定义conv(self,inputs,w),该函数调用 TensorFlow 的tf.nn.conv2d函数并采用:

  • 输入为二维向量
  • 权重:形状[patch_size,patch_size,input_vector_depth,output_vector_depth]的权重
  • 跨步:以[1,x_movement,y_movement,1]形式出现的列表,其中:
    • x_movement:定义水平移动补丁的步数
    • y_movement:定义在垂直方向上移动的色块的步数
  • 填充SAMEVALID(我们在第 1 章,“深度学习–架构和框架”中讨论了此和有效的填充)

我们将使用以下代码定义函数:

代码语言:javascript复制
def conv(self,inputs,w):
        #strides [1,x_movement,y_movement,1]
        #stride[0] = stride[3] = 1
        return tf.nn.conv2d(inputs,w,strides=[1,1,1,1],padding='SAME') 

现在,让我们定义build_networks(self)。 它是在以下情况下建立主要和目标网络的函数:

  • variable_scope下创建主要网络参数,即:primary_network和集合primary_network_parameters
  • variable_scope下创建目标网络参数,即:target_network 和集合target_network_parameters
  • 两者具有相同的结构,即:
    • 卷积层 1
    • 卷积层 2
    • 全连接层 1
    • 全连接层 2
    • 使用的激活函数:ReLU

该函数还有助于:

  • 计算主网络输出的 Q 值与目标网络输出的 Q 值之间的损失

我们可以使用亚当优化器将这种损失降到最低。

现在,我们已经了解了该函数,让我们对其进行定义:

代码语言:javascript复制
def build_networks(self):
        #primary network
        shape = [None]   self.n_features
        self.s = tf.placeholder(tf.float32,shape)
        self.qtarget = tf.placeholder(tf.float32,[None,self.n_actions])

        with tf.variable_scope('primary_network'):
            c = ['primary_network_parameters', tf.GraphKeys.GLOBAL_VARIABLES]
            #first convolutional layer
            with tf.variable_scope('convlayer1'):
                l1 = self.add_layer(self.s,w_shape=[5,5,4,32],b_shape=[32],layer='convL1',activation_fn=tf.nn.relu,c=c,isconv=True)

            #first convolutional layer
            with tf.variable_scope('convlayer2'):
                l2 = self.add_layer(l1,w_shape=[5,5,32,64],b_shape=[64],layer='convL2',activation_fn=tf.nn.relu,c=c,isconv=True)

            #first fully-connected layer
            l2 = tf.reshape(l2,[-1,80*80*64])
            with tf.variable_scope('FClayer1'):
                l3 = self.add_layer(l2,w_shape=[80*80*64,128],b_shape=[128],layer='fclayer1',activation_fn=tf.nn.relu,c=c)

            #second fully-connected layer
            with tf.variable_scope('FClayer2'):
                self.qeval = self.add_layer(l3,w_shape=[128,self.n_actions],b_shape=[self.n_actions],layer='fclayer2',c=c)

        with tf.variable_scope('loss'):
                self.loss = tf.reduce_mean(tf.squared_difference(self.qtarget,self.qeval))

        with tf.variable_scope('optimizer'):
                self.train = tf.train.AdamOptimizer(self.learning_rate).minimize(self.loss)

        #target network
        self.st = tf.placeholder(tf.float32,shape)

        with tf.variable_scope('target_network'):
            c = ['target_network_parameters', tf.GraphKeys.GLOBAL_VARIABLES]
            #first convolutional layer
            with tf.variable_scope('convlayer1'):
                l1 = self.add_layer(self.st,w_shape=[5,5,4,32],b_shape=[32],layer='convL1',activation_fn=tf.nn.relu,c=c,isconv=True)

            #first convolutional layer
            with tf.variable_scope('convlayer2'):
                l2 = self.add_layer(l1,w_shape=[5,5,32,64],b_shape=[64],layer='convL2',activation_fn=tf.nn.relu,c=c,isconv=True)

            #first fully-connected layer
            l2 = tf.reshape(l2,[-1,80*80*64])
            with tf.variable_scope('FClayer1'):
                l3 = self.add_layer(l2,w_shape=[80*80*64,128],b_shape=[128],layer='fclayer1',activation_fn=tf.nn.relu,c=c)

            #second fully-connected layer
            with tf.variable_scope('FClayer2'):
                self.qt = self.add_layer(l3,w_shape=[128,self.n_actions],b_shape=[self.n_actions],layer='fclayer2',c=c)

现在,让我们定义target_params_replaced(self),它是运行将主网络参数分配给目标网络参数的张量操作的函数:

代码语言:javascript复制
def target_params_replaced(self):
     self.sess.run(self.replacing_target_parameters)

现在,我们将定义store_experience(self,obs,a,r,obs_)函数,以将每种体验(即(状态,动作,奖励,新状态)的元组)存储在其体验缓冲区中,主要目标将在该缓冲区上进行训练:

代码语言:javascript复制
def store_experience(self,obs,a,r,obs_):
    if len(obs.shape)<3 or len(obs_.shape)<3: 
          print("Wrong shape entered : ",obs.shape,obs_.shape,len(self.memory))
    else:
          index = self.experience_counter % self.experience_limit
          if self.experience_counter < self.experience_limit:
                self.memory.append([obs,a,r,obs_])
          else:
                self.memory[index] = [obs,a,r,obs_]
    self.experience_counter =1

现在,我们将定义fit(self)函数,以通过从经验缓冲区中选择一个批量来训练网络,计算q_target的张量值,然后最小化qeval之间的损失(即主数据库的输出) 网络)和q_target(即使用目标网络计算的 Q 值):

代码语言:javascript复制
def fit(self):
        # sample batch memory from all memory
        indices = np.random.choice(len(self.memory), size=self.batch_size)
        batch = [self.memory[i] for i in indices]
        obs_nlist = np.array([i[3] for i in batch])
        obs_list = np.array([i[0] for i in batch])
        qt,qeval = self.sess.run([self.qt,self.qeval],feed_dict={self.st:obs_nlist,self.s:obs_list})

        qtarget = qeval.copy() 
        batch_indices = np.arange(self.batch_size, dtype=np.int32)
        actions = np.array([int(i[1]) for i in batch])
        rewards = np.array([int(i[2]) for i in batch])
        qtarget[batch_indices,actions] = rewards   self.gamma * np.max(qt,axis=1)

        _ = self.sess.run(self.train,feed_dict = {self.s:obs_list,self.qtarget:qtarget})
  print(self.learning_counter 1," learning done")
        #increasing epsilon 
        if self.epsilon < 0.9:
                self.epsilon  = self.epsilon_incrementer

        #replacing target network parameters with primary network parameters 
        if self.learning_counter % self.replace_target_pointer == 0:
            self.target_params_replaced()
            print("target parameters changed")

        self.learning_counter  = 1

现在,我们将定义epsilon_greedy(self,obs),该函数类似于我们在 DQN 中为山地车和 Cartpole 实现的函数:

代码语言:javascript复制
def epsilon_greedy(self,obs):
  new_shape = [1] list(obs.shape)
  obs = obs.reshape(new_shape)
        #epsilon greedy implementation to choose action
        if np.random.uniform(low=0,high=1) < self.epsilon:
            return np.argmax(self.sess.run(self.qeval,feed_dict={self.s:obs})) #[np.newaxis,:]
        else:
            return np.random.choice(self.n_actions)

在类之外,我们有一个函数preprocessing_image,该函数用于预处理以下参数:

  • 裁剪图像
  • 将其转换为灰度
  • 对图像进行下采样
  • 归一化图像

我们将使用以下代码定义函数:

代码语言:javascript复制
def preprocessing_image(s):
    s = s[31:195]  #cropping
    s = s.mean(axis=2)  #converting to greyscale
    s = imresize(s,size=(80,80),interp='nearest')  #downsampling
    s = s/255.0  #normalizing
    return s 

以下代码定义了main函数,该函数创建 DQN 的上一类的对象,使用gym来获取Breakout-v0环境,并训练智能体程序解决问题:

代码语言:javascript复制
if __name__ == "__main__":
    env = gym.make('Breakout-v0')
    env = env.unwrapped
    epsilon_rate_change = 0.9/500000.0
    dqn = DQN(learning_rate=0.0001,
              gamma=0.9,
              n_features=[80,80,4],
              n_actions=env.action_space.n,
              epsilon=0.0,
              parameter_changing_pointer=100,
              memory_size=50000,
              epsilon_incrementer=epsilon_rate_change)

    episodes = 100000
    total_steps = 0

    for episode in range(episodes):
        steps = 0

        obs = preprocessing_image(env.reset())
        s_rec = np.stack([obs]*4,axis=0)
        s = np.stack([obs]*4,axis=0)
        s = s.transpose([1,2,0])
        episode_reward = 0
        while True:
            env.render()
            action = dqn.epsilon_greedy(s)
            obs_,reward,terminate,_ = env.step(action)
            obs_ = preprocessing_image(obs_)

            a = s_rec[1:]
            a = a.tolist()
            a.append(obs_)
            s_rec = np.array(a)

            s_ = s_rec.transpose([1,2,0])
            dqn.store_experience(s,action,reward,s_)
            if total_steps > 1999 and total_stepsP0==0:
                dqn.fit()
            episode_reward =reward
            if terminate:
                break
            s = s_
            total_steps =1
            steps =1
        print("Episode {} with Reward : {} at epsilon {} in steps {}".format(episode 1,episode_reward,dqn.epsilon,steps))

    while True: #to hold the render at the last step when Car passes the flag
        env.render()

由于权重参数很多,因此在普通计算机上进行收敛需要花费大量时间,而运行 GPU 的计算机运行成本很高。 但是,要见证在普通计算机上融合的可能性,请运行代码 5 到 6 个小时,以查看智能体情况如何好转。 我建议,如果价格合理,请在带有 GPU 的计算机上运行它。 无论如何,前面的main函数的示例输出将如下所示:

代码语言:javascript复制
....
....
....
Episode 992 with Reward : 0.0 at epsilon 0.0008766 in steps 174
Episode 993 with Reward : 2.0 at epsilon 0.0008766 in steps 319
(488, ' learning done')
Episode 994 with Reward : 0.0 at epsilon 0.0008784 in steps 169
Episode 995 with Reward : 1.0 at epsilon 0.0008784 in steps 228
Episode 996 with Reward : 1.0 at epsilon 0.0008784 in steps 239
(489, ' learning done')
Episode 997 with Reward : 4.0 at epsilon 0.0008802 in steps 401
(490, ' learning done')
Episode 998 with Reward : 0.0 at epsilon 0.000882 in steps 171
Episode 999 with Reward : 4.0 at epsilon 0.000882 in steps 360
(491, ' learning done')
Episode 1000 with Reward : 0.0 at epsilon 0.0008838 in steps 171
Episode 1001 with Reward : 1.0 at epsilon 0.0008838 in steps 238
(492, ' learning done')
Episode 1002 with Reward : 1.0 at epsilon 0.0008856 in steps 249
Episode 1003 with Reward : 1.0 at epsilon 0.0008856 in steps 232
....
....
....

尝试使用不同的参数以更好地收敛。

蒙特卡罗树搜索算法

蒙特卡洛树搜索MCTS)是一种规划算法,是在出现人工窄智能问题时做出最佳决策的一种方法。 MCTS 致力于解决问题的预先规划方法。

在诸如 minimax游戏树之类的早期算法未能显示出具有复杂问题的结果之后,MCTS 算法变得越来越重要。 那么,是什么使 MCTS 与过去的决策算法(例如 minimax)不同并且更好呢?

让我们首先讨论什么是 minimax。

Minimax 和游戏树

Minimax 是 IBM Deep Blue 在 1996 年 2 月 10 日的国际象棋比赛中击败世界冠军 Gary Kasparov 的算法。 那时的胜利是一个非常重要的里程碑。 minimax 和游戏树都是有向图,其中每个节点代表游戏状态,即游戏位置,如下面的井字游戏图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7QtKC8Bc-1681786426189)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/1d4b1704-d9ef-4d48-aaff-79d8161cc9e5.png)]

井字游戏树。 顶部节点代表游戏的开始位置。 沿着树走下去导致游戏的结果位置

因此,通过搜索游戏树,由于节点和树中存在的节点和路径的组合,AI 智能体可以选择最佳的移动方式。 这对于游戏复杂度处于可接受水平的问题很有用,因为随着复杂度的增加,游戏树的大小也会增加。 例如,国际象棋的游戏树具有比宇宙中原子更多的节点。 因此,在这种情况下,仅可能进行搜索。 因此,随着复杂度的增加,极大极小的可用性和游戏树减少。

另一个很好的例子是围棋的开放式中文游戏,其棋类复杂度为10^360,而棋类的复杂度为10^123。 由于具有如此高的复杂性,minimax 无法绘制评估函数,甚至无法创建如此大的游戏树。 因此,在《深蓝》取得成功的大约 20 年后,没有算法能够掌握围棋游戏。 原因很简单,当时的最新技术(例如 minimax)无法解决诸如围棋游戏之类具有很高复杂性的问题。 而且,解决围棋需要一种更人性化的学习方式,即基于交互。

因此,Google DeepMind 的 AlphaGo 被认为是最先进的 AI 智能体,它能够在 2016 年使用深度强化学习成功击败 Lee Sedol,该学习用于神经网络,强化学习和蒙特卡洛树搜索。 这是第一次以人类可以实现的方式完成 AI 任务,即通过不断的交互,并通过不断的反复试验过程来获取知识。

蒙特卡罗树搜索

那么,蒙特卡罗树搜索与 minimax 的方法有何不同?它如何在高度复杂的围棋游戏中进行提前计划,围棋有大量潜在的反制动作? MCTS 建立了一个看起来像游戏树的统计树,但是游戏树或 minimax 具有游戏位置,即有向图的节点中的游戏状态,在 MCTS 中,有向图的节点是该游戏的状态数量,告诉我们成功模拟的次数(即在游戏结束时导致获胜的动作)相对于游戏状态经过的模拟总数的数量。 因此,仿真次数越多,越多的节点有机会成为仿真的一部分,从而导致收敛。 因此,每个节点的值取决于仿真次数。

收敛后,此统计树将指导 AI 智能体在每个级别上寻找最佳可能节点,并继续进行直至达到目标。 最初,统计树用于扩展,以便通过多次仿真为可能的游戏状态添加更多节点。 收集足够数量的节点后,同时开始选择更好的节点,如果节点成功实现了问题目标,则每次仿真它们的值都会增加,从而提高了实用性。

在选择策略中,MCTS 还通过在现有的,有希望的节点与可能更有希望的未探索节点之间保持平衡来纳入探索与利用的权衡。 因此,仿真次数越多,统计树越大,则节点值收敛得越准确,最优决策就越好。

MCTS 是独立于域的,不需要复杂的手写试探法。 因此,它是解决各种开放式 AI 问题的强大算法。

SARSA 算法

状态-动作-奖励-状态-动作SARSA)算法是一种基于策略的学习问题。 就像 Q 学习一样,SARSA 也是一个时差学习问题,也就是说,它会预测剧集的下一步以估计未来的回报。 SARSA 和 Q 学习之间的主要区别在于,具有最大 Q 值的动作不用于更新当前状态动作对的 Q 值。 取而代之的是,选择作为当前策略结果的操作的 Q 值,或者由于采用诸如 ε 贪婪之类的探索步骤来更新当前状态操作对的 Q 值。 SARSA 之所以得名,是因为使用五元组Q(s, a, r, s', a')完成了 Q 值更新,其中:

  • sa:当前状态和操作
  • r:采取行动后观察到的奖励a
  • s':在采取操作a后到达下一个状态
  • a':要在状态s'下执行的动作

SARSA 算法涉及的步骤如下:

  1. 随机初始化 Q 表
  2. 对于每个剧集:
    1. 对于给定状态s,请从 Q 表中选择操作a
    2. 执行动作a
    3. 奖励R和新状态s'
    4. 对于新状态s',请从 Q 表中选择操作a'
    5. 通过以下操作更新当前状态操作的 Q 值,即Q(s, a)对:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iRXBsgUZ-1681786426189)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/rl-tf/img/356ee392-4f51-400f-a5af-90f2b50ee30c.png)]

SARSA 算法的伪代码如下:

代码语言:javascript复制
Create Q-table where rows represent different states and columns represent different actions

Initialize Q(s,a) arbitrarily, e.g. 0 for all states
set action value for terminal states as 0

For each episode:
    Start with the starting state that is Initialize s to start
    Choose action a for s using the policy derived from Q [e.g. -greedy, either for the given 's' which 'a' has the max Q-value or choose a random action]
    Repeat for each step in the episode:
        Take the chosen action a, observe reward R and new state s'
        Choose action a' for s' using the policy derived from Q 
        [e.g. -greedy]
        Update 

    until s is the terminal state
end

用于 OpenAI Gym 山地车问题的 SARSA 算法

让我们尝试实现先前在山地车问题中解释过的 SARSA 算法。 该程序的初始部分与先前的 Q 学习器具有相似之处。

首先,我们将使用以下代码导入依赖项并检查山地车环境:

代码语言:javascript复制
#importing the dependencies

import gym
import numpy as np

#exploring Mountain Car environment

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

print("Action Set size :",env.action_space)
print("Observation set shape :",env.observation_space) 
print("Highest state feature value :",env.observation_space.high) 
print("Lowest state feature value:",env.observation_space.low) 
print(env.observation_space.shape) 

前面的打印语句输出以下内容:

代码语言:javascript复制
Making new env: MountainCar-v0
('Action Set size :', Discrete(3))
('Observation set shape :', Box(2,))
('Highest state feature value :', array([ 0.6 , 0.07]))
('Lowest state feature value:', array([-1.2 , -0.07]))
(2,)

接下来,我们将使用以下代码分配超参数,例如状态数,剧集数,学习率(初始和最小值),折扣因子伽玛,剧集中的最大步长以及 ε 贪婪的ε

代码语言:javascript复制
n_states = 40  # number of states
episodes = 10 # number of episodes

initial_lr = 1.0 # initial learning rate
min_lr = 0.005 # minimum learning rate
gamma = 0.99 # discount factor
max_steps = 300
epsilon = 0.05

env = env.unwrapped
env.seed(0)         #setting environment seed to reproduce same result
np.random.seed(0)   #setting numpy random number generation seed to reproduce same random numbers

我们的下一个任务是创建一个函数,对连续状态空间进行离散化。 离散化是将连续状态空间观察转换为离散状态空间集。 我们将使用以下代码执行离散化:

代码语言:javascript复制
def discretization(env, obs):

    env_low = env.observation_space.low
    env_high = env.observation_space.high

    env_den = (env_high - env_low) / n_states
    pos_den = env_den[0]
    vel_den = env_den[1]

    pos_high = env_high[0]
    pos_low = env_low[0]
    vel_high = env_high[1]
    vel_low = env_low[1]

    pos_scaled = int((obs[0] - pos_low)/pos_den)  #converts to an integer value
    vel_scaled = int((obs[1] - vel_low)/vel_den)  #converts to an integer value

    return pos_scaled,vel_scaled

到目前为止,每个任务都与我们在 Q 学习算法中所做的相似。 现在,SARSA 的实现从初始化 Q 表并相应地更新 Q 值开始,如以下代码所示。 同样,在这里,我们将奖励值更新为当前位置与最低点(即起点)之间的绝对差值,以便它通过远离中心即最低点来最大化奖励:

代码语言:javascript复制
#Q table
#rows are states but here state is 2-D pos,vel
#columns are actions
#therefore, Q- table would be 3-D

q_table = np.zeros((n_states,n_states,env.action_space.n))
total_steps = 0
for episode in range(episodes):
   obs = env.reset()
   total_reward = 0
   # decreasing learning rate alpha over time
   alpha = max(min_lr,initial_lr*(gamma**(episode//100)))
   steps = 0

   #action for the initial state using epsilon greedy
   if np.random.uniform(low=0,high=1) < epsilon:
        a = np.random.choice(env.action_space.n)
   else:
        pos,vel = discretization(env,obs)
        a = np.argmax(q_table[pos][vel])

   while True:
      env.render()
      pos,vel = discretization(env,obs)

      obs,reward,terminate,_ = env.step(a) 
      total_reward  = abs(obs[0] 0.5) 
      pos_,vel_ = discretization(env,obs)

      #action for the next state using epsilon greedy
      if np.random.uniform(low=0,high=1) < epsilon:
          a_ = np.random.choice(env.action_space.n)
      else:
          a_ = np.argmax(q_table[pos_][vel_])

      #q-table update
      q_table[pos][vel][a] = (1-alpha)*q_table[pos][vel][a]   alpha*(reward gamma*q_table[pos_][vel_][a_])
      steps =1
      if terminate:
          break
      a = a_
   print("Episode {} completed with total reward {} in {} steps".format(episode 1,total_reward,steps)) 
while True: #to hold the render at the last step when Car passes the flag
   env.render()  

前面的程序将以以下方式打印:

代码语言:javascript复制
Episode 1 completed with total reward 11167.6296185 in 36605 steps
Episode 2 completed with total reward 830.204697241 in 2213 steps
Episode 3 completed with total reward 448.46977318 in 1899 steps
Episode 4 completed with total reward 930.154976751 in 3540 steps
Episode 5 completed with total reward 6864.96292351 in 20871 steps
Episode 6 completed with total reward 677.449030827 in 3995 steps
Episode 7 completed with total reward 2994.99152751 in 7401 steps
Episode 8 completed with total reward 724.212076546 in 3267 steps
Episode 9 completed with total reward 192.502071909 in 928 steps
Episode 10 completed with total reward 213.212231118 in 786 steps

因此,我们已经能够成功实现山地车问题的 SARSA 算法。

总结

我们知道强化学习可以优化环境中智能体的回报,马尔可夫决策过程MDP)是一种环境表示和数学框架,用于使用状态对决策进行建模 ,动作和奖励。 在本章中,我们了解到 Q 学习是一种无需任何转移模型即可为任何 MDP 找到最佳动作选择策略的方法。 另一方面,如果给出了转换模型,则值迭代会为任何 MDP 找到最佳的动作选择策略。

我们还学习了另一个重要的话题,称为深度 Q 网络,这是一种经过改进的 Q 学习方法,它采用深度神经网络作为函数逼近器来在不同环境中进行泛化,这与特定于环境的 Q 表不同。 此外,我们还学会了在 OpenAI Gym 环境中实现 Q 学习,深度 Q 网络和 SARSA 算法。 先前显示的大多数实现可能在具有更好的超参数值和更多训练集的情况下效果更好。

在下一章中,我们将详细介绍著名的异步优势参与者批评算法。

1 人点赞