Python 深度强化学习教程(一)

原文:Deep Reinforcement Learning with Python

协议:CC BY-NC-SA 4.0

一、强化学习简介

强化学习是发展最快的学科之一,正在帮助人工智能成为现实。将深度学习与强化学习相结合已经导致了许多重大进步,这些进步越来越多地让机器接近人类的行为方式。在本书中,我们将从基础开始,以掌握该领域的一些最新发展结束。使用 PyTorch 和 TensorFlow 将会很好地混合理论(用最少的数学)和代码实现。

在这一章中,我们将设置背景,并为你在本书的其余部分做好一切准备。

强化学习

所有有智慧的生物都是从一些知识开始的。然而,随着他们与世界的互动和经验的积累,他们学会了适应环境,变得更擅长做事。引用 1994 年《华尔街日报》、 1 、专栏中的一句话,智力可以定义如下:

一种非常普遍的心智能力,包括推理、计划、解决问题、抽象思考、理解复杂想法、快速学习和从经验中学习的能力。这不仅仅是书本知识、狭隘的学术技能或应试技巧。相反,它反映了理解我们周围环境的更广泛和更深入的能力——对事物的“理解”、“理解”或“弄清楚”该做什么。

机器背景下的智能叫做人工智能*。牛津语言对人工智能(AI)的定义如下:

能够执行通常需要人类智能的任务的计算机系统的理论和发展,例如视觉感知、语音识别、决策和语言之间的翻译。

这就是我们将在本书中研究的内容:通过与环境互动并不断从成功、失败和回报中学习,帮助机器(代理)获得执行任务能力的算法的理论和设计。最初,人工智能围绕着设计解决方案,作为一系列可以用逻辑和数学符号表达的正式规则。这些规则由一系列编入知识库的信息组成。这些人工智能系统的设计还包括一个推理引擎,使用户能够查询知识库,并结合规则/知识的各个部分进行推理。这些系统也被称为专家系统决策支持系统等。然而,很快人们意识到这些系统太脆弱了。随着问题越来越复杂,编纂知识或建立有效的推理系统变得越来越困难。

强化学习的现代概念是两种不同的思路通过各自的发展结合起来的。首先是最优控制的概念。在许多解决最优控制问题的方法中,理查德·贝尔曼在 1950 年提出了动态规划的原则,我们将在本书中广泛使用。然而,动态编程不涉及学习。这是所有关于规划通过空间的各种选择使用贝尔曼递归方程。在第二章和第三章中,我们会有很多关于这些方程的内容。

第二条线索是通过尝试和错误来学习,它起源于动物训练心理学。爱德华·桑戴克是第一个明确表达试错概念的人。用他的话说:

在对同一情况做出的几种反应中,那些伴随着或紧随其后的对动物的满足的反应,在其他条件相同的情况下,将与该情况更紧密地联系在一起,因此,当它重现时,它们将更有可能重现;那些伴随着或紧随其后的动物的不适,在其他条件相同的情况下,它们与这种情况的联系被削弱,这样,当它复发时,它们就不太可能发生。满足感或不适感越大,这种纽带的加强或削弱就越大。

我们将在关于政策梯度的第七章中看到增加好结果发生率和减少坏结果发生率的概念。

在 20 世纪 80 年代,这两个领域合并起来,形成了现代强化学习领域。在过去的十年里,随着强大的深度学习方法的出现,强化学习与深度学习相结合,正在产生非常强大的算法,这些算法可以在未来的时代实现人工智能。今天的强化系统与世界互动,以获取经验,并通过概括世界的经验,学习根据与世界互动的结果优化自己的行动。专家知识没有明确的编码。

机器学习分支

机器学习涉及从呈现给系统的数据中学习,以便系统可以执行指定的任务。系统没有被明确告知如何完成任务。更确切地说,它与数据一起呈现,并且系统学习基于定义的目标执行一些任务。我们不会多说,因为我们假设你熟悉机器学习的概念。

机器学习方法传统上分为三大类,如图 1-1 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-1

机器学习的分支

机器学习的三个分支在学习系统可用的“反馈”的意义上有所不同。下面几节将对它们进行讨论。

监督学习

在监督学习中,向系统呈现已标记的数据,目标是概括知识,以便可以标记新的未标记数据。考虑将猫和狗的图像连同哪个图像显示猫或狗的标签一起呈现给系统。输入数据表示为一组数据 D = ( x 1y 1 ), x 2y 2 ),…(xny x n 是各个图像的像素值, y 1y 2 ,…, y n 是各个图像的标签,比如说,猫的图像的值为 0,狗的图像的值为 1。 系统/模型接受该输入并学习从图像 x i 到标签 y i 的映射。一旦训练完毕,系统将呈现一幅新图像 x ,以根据图像是猫还是狗来预测标签 y = 0 或 1。

这是一个分类问题,系统学习将输入分类到正确的类别。另一个问题类型是回归问题,我们希望根据房子的特征来预测房子的价格。训练数据再次表示为 D = ( x 1y 1 ),( x 2y 2 ),…(xny n 输入是 x 1x 2 ,…, x n 其中每个输入 x i 是一个具有某些属性的向量,例如,一所房子的房间数量、其面积和前草坪的大小等。系统给出的标签为 y i ,房子的市场价值。系统使用来自许多房屋的输入数据来学习输入特征 x i 到房屋价值 y i 的映射。然后给训练好的模型呈现一个向量 x ,这个向量由新房子的特征组成,模型预测这个新房子的市场价值 y

无监督学习

无监督学习没有标签。它只有输入 D = x 1x 2 ,…, x n ,没有标签。系统使用这些数据来学习数据的隐藏结构,从而可以将数据聚类/分类到一些大的类别中。后学习,当系统呈现新的数据点 x ' 时,它可以将新的数据点匹配到已学习的聚类之一。与监督学习不同,每个类别没有明确定义的含义。一旦数据被聚类到一个类别中,基于一个聚类中最常见的属性,我们可以赋予它一些意义。无监督学习的另一个用途是利用基础输入数据来学习数据分布,以便随后可以查询系统来产生新的合成数据点。

很多时候,无监督学习被用于特征提取,然后被馈送到监督学习系统。通过聚类数据,我们可以首先识别隐藏的结构,并将数据重新映射到一个更低的维度形式。有了这个更低维度的数据,监督学习就能够学习得更快。这里我们使用无监督学习作为特征提取器。

还有另一种方法来利用无监督学习方法。考虑这种情况,当你有少量的标记数据和大量的未标记数据。标记数据和未标记数据首先被聚集在一起。接下来,在每个这样的聚类中,基于该聚类中已标记数据的强度,给未标记数据分配标签。我们基本上是利用有标签的数据给无标签的数据分配标签。完全标记的数据接下来被馈送到监督学习算法中,以训练分类系统。这种将监督和非监督学习结合在一起的方法被称为半监督学习

强化学习

我们先来看一个例子。我们正试图设计一种可以自己驾驶的自动驾驶汽车。我们有一辆汽车,我们将称之为代理,即一个正在学习自己驾驶的系统或算法。它正在学习一种行为来驾驶。当它的当前坐标、速度和运动方向组合成一个数字向量时,就被称为它的当前状态。代理使用其当前状态来决定是应用刹车还是踩油门。它还利用这些信息来转动方向盘,以改变汽车的运动方向。“刹车/加速”和“驾驶汽车”的组合决定被称为动作。特定当前状态到特定动作的映射被称为策略。代理人的行为在好的时候会产生一个快乐的结果,而在坏的时候,会导致一个不快乐的结果。代理使用结果的反馈来评估其动作的有效性。作为反馈的结果被称为奖励,代理人在特定的状态下以特定的方式行动得到奖励。基于当前状态及其动作,汽车达到一组新的坐标、速度和方向。这是新状态,代理根据其在上一步中的行为发现自己所处的状态。谁提供这个结果并决定新的状态?是车的周围环境,是车/代理无法控制的东西。代理不控制的其他一切都被称为环境。在整本书中,我们会对这些术语进行更多的讨论。

在这种设置中,系统以状态向量、采取的行动和获得的奖励的形式提供的数据是连续的和相关的。基于代理人采取的行动,从环境中获得的下一个状态和奖励可能会发生剧烈变化。在前面的自动驾驶汽车的例子中,想象有一个行人正在汽车前面过马路。在这种情况下,加速和刹车会有非常不同的结果。使汽车加速可能会导致行人受伤以及汽车和乘员受损。制动汽车可以避免任何损坏,并且在道路畅通后汽车可以继续前进。

在强化学习中,主体没有系统的先验知识。它收集反馈,并使用反馈来计划/学习行动,以最大限度地实现特定目标。由于它最初没有足够的关于环境的信息,它必须探索以收集洞察力。一旦它收集了“足够”的知识,它需要利用这些知识来开始调整它的行为,以最大化它所追求的目标。难的是没有办法知道什么时候探索是“足够的”。如果代理即使在获得了完美的知识后还继续探索,那么它试图收集没有剩余的新信息就是在浪费资源。另一方面,如果代理过早地认为它已经收集了足够的知识,它可能会基于不完整的信息进行优化,并且可能表现不佳。这种何时探索、何时利用的困境是强化学习算法反复出现的核心主题。当我们在本书中讨论不同的行为优化算法时,我们会看到这个问题一次又一次地发生。

核心要素

强化学习系统可以分为四个关键部分:政策、奖励、价值函数和环境模型。

策略是形成代理智能的东西。代理开始与环境交互以感知环境的当前状态,例如,机器人从系统获得视觉和其他感官输入,也称为环境的当前状态或机器人感知的当前观察数据。像智能实体一样,机器人使用当前信息和可能的过去历史来决定下一步做什么,即执行什么动作。该策略将状态映射到代理采取的操作。策略可以是确定性的。换句话说,对于给定的环境状态,代理采取固定的动作。有时政策可以是随机的。换句话说,对于给定的状态,代理可以采取多种可能的动作。

奖励是指代理人试图达到的目标/目的。假设一个机器人试图从 A 点到 b 点,它感应到当前位置并采取行动。如果这个行动使它更接近目标 B,我们期望回报是正的。如果它把机器人从 B 点带走,这是一个不利的结果,我们会期望回报是负的。换句话说,奖励是一个数字值,表示代理人基于其试图实现的目的/目标而采取的行动的好坏。奖励是代理评估行为是好是坏的主要方式,并使用此信息来调整其行为,即优化其正在学习的策略。

奖励是环境的内在属性。所获得的回报是代理人当前所处状态以及在该状态下所采取的行动的函数。代理遵循的奖励和政策定义了价值函数

  • 状态中的值是基于代理所处的状态及其遵循的政策,代理期望获得的总累积奖励。

  • 奖励是基于状态和在该状态下采取的行动的来自环境的即时反馈。与价值不同,奖励不会因代理人的行为而改变。在特定的状态下采取特定的行动总是会产生相同的回报。

价值函数就像长期回报一样,不仅受环境的影响,还受代理人遵循的政策的影响。价值因为回报而存在。代理在遵循策略时累积奖励,并使用这些累积的奖励来评估状态中的值。然后,它改变其政策,以增加国家的价值。

我们可以将这个想法与我们之前谈到的勘探开发困境联系起来。在某些情况下,最佳行动可能会带来直接的负面回报。然而,这样的动作可能仍然是最佳的,因为它可以将代理置于一个新的状态,从该状态它可以更快地到达它的目标。一个例子是穿过一个驼峰或绕道走一条更短的路到达目标。

除非代理进行了足够的探索,否则它可能无法发现这些较短的路径,并可能最终满足于一个次优路径。然而,在发现了更短的路径之后,它无法知道它是否仍然需要更多的探索来找到另一条更快的路径,或者它是否更好地利用其先前的知识来奔向目标。

这本书的前五章着重于使用前面描述的价值函数来寻找最优行为/政策的算法。

最后一个部分是环境模型。在一些寻找最佳行为的方法中,代理使用与环境的交互来形成环境的内部模型。这种内部模型有助于代理人进行规划,即考虑一个或多个动作链来评估最佳动作序列。这种方法叫做基于模型的学习。同时,还有其他完全基于试错的方法。这种方法不形成任何环境模型。因此,这些被称为无模型方法。大多数代理使用基于模型和无模型方法的组合来寻找最优策略。

具有强化学习的深度学习

近年来,涉及基于神经网络的模型的机器学习的子分支出现了爆炸式增长。随着功能强大的计算机、大量数据和新算法的出现,现在可以训练模型根据图像、文本和语音等原始输入进行归纳,类似于人类的操作方式。在深度学习的子分支下,对特定领域手工制作的特征来训练模型的需求正在被强大的基于神经网络的模型所取代。

2014 年,DeepMind 成功地将深度学习技术与强化学习相结合,从一个没有对原始输入进行任何特定领域处理的环境中收集的原始数据中进行学习。它的第一个成功是将强化学习下的传统 Q 学习算法转换为深度 Q 学习方法,该方法被命名为深度 Q 网络(DQN)。Q-learning 涉及一个智能体,它遵循某种策略,以当前状态、它采取的行动、它得到的回报以及它发现自己所处的下一个状态的元组的形式来收集它的行动经验。然后,代理在迭代循环中使用这些经验和贝尔曼方程来寻找最优策略,使得每个状态的价值函数(如前所述)增加。

早期将深度学习与强化学习相结合的尝试并不成功,因为这种结合方法的性能不稳定。DeepMind 做了一些有趣而聪明的改变来克服不稳定问题。它首先将传统的强化学习和深度学习相结合的方法应用于开发 Atari 游戏的游戏代理*。*代理将获得游戏的快照,并且事先不知道游戏的规则。代理将使用这些原始视觉数据来学习玩游戏,例如 Atari 视频游戏。在许多情况下,它达到了人类水平的性能。该公司随后扩展了这一方法,以开发能够在围棋等游戏中击败冠军人类选手的代理人。将深度学习与强化学习结合使用,已经使机器人变得更加智能,而不需要手工制作特定领域的知识。这是一个令人兴奋的快速发展的领域。我们将在第五章中访问这个。我们从第六章开始学习的大多数算法都将涉及深度学习和强化学习的结合。

示例和案例研究

为了激励你,我们将看看强化学习的各种用途,以及它如何帮助解决一些现实世界的问题。

自动驾驶汽车

首先,我们看看自动驾驶汽车(AVs)领域。AVs 有像激光雷达、雷达、摄像机等传感器。感知附近的环境。这些传感器然后被用于执行对象检测、车道检测等。原始感觉数据和对象检测被组合以获得用于规划到目的地的路径的统一场景表示。接下来,计划的路径用于将输入馈送到控制,以使系统/代理遵循该路径。运动规划是规划轨迹的部分。

像逆向强化学习这样的概念可以用来优化成本函数,以得出平滑的轨迹,在逆向强化学习中,我们观察专家,并根据专家的交互来学习隐含的目标/回报。超车、变道和自动泊车等行为也利用强化学习的各个部分来将智能构建到行为中。另一种选择是手工制定各种各样的规则,这永远不可能是详尽的或灵活的。

机器人

使用计算机视觉和自然语言处理或使用深度学习技术的语音识别,为自主机器人增加了类似人类的感知能力。此外,结合深度学习和强化学习的方法已经导致教会机器人学习像人类一样走路、拿起和操纵物体,或者通过相机观察人类行为并学习像人类一样表演。

推荐系统

今天,我们随处可见推荐系统。视频共享/托管应用 YouTube、抖音和脸书根据我们的观看历史向我们推荐我们想要观看的视频。当我们访问任何电子商务网站时,根据我们当前查看的产品和我们过去的购买模式,或者根据其他用户的行为方式,我们会看到其他类似的产品推荐。

所有这些推荐引擎越来越多地由基于强化学习的系统驱动。这些系统不断地从用户对引擎给出的建议的反应中学习。根据建议采取行动的用户在给定的上下文中将这些行动强化为好的行动。

金融和贸易

由于其顺序动作优化的焦点,其中过去的状态和动作影响未来的结果,强化学习在时间序列分析中发现了重要的用途,特别是在金融和股票交易领域。许多自动化交易策略使用强化学习方法,根据过去行动的反馈不断改进和微调交易算法。银行和金融机构使用聊天机器人与用户互动,以提供有效、低成本的用户支持和参与。这些机器人再次使用强化学习来微调其行为。投资组合风险优化和信用评分系统也受益于基于 RL 的方法。

卫生保健

强化学习在医疗保健中有着重要的用途,无论是生成预测信号和实现早期医疗干预,还是机器人辅助手术或管理医疗和患者数据。它还用于改进成像数据的解释,成像数据本质上是动态的。基于 RL 的系统提供从其经验中学习到的建议,并不断发展。

博弈

最后,我们不能过分强调基于 RL 的代理能够在许多棋盘游戏中击败人类玩家。虽然设计能玩游戏的代理看起来有些浪费,但这是有原因的。游戏提供了一个更简单的理想化世界,使设计、训练和比较方法变得更容易。在这种理想化的环境/设置下学习的方法可以随后被增强,以使代理在真实世界的情况下表现良好。游戏提供了一个控制良好的环境来更深入地研究这个领域。

正如我们之前所说,深度强化学习是一个令人着迷且快速发展的领域,我们希望为您提供一个坚实的基础,以便在您掌握该领域的旅程中入门。

库和环境设置

本书中的所有代码示例都是用 Python 和 PyTorch、TensorFlow、OpenAI Gym 库编写的。虽然有许多方法可以设置环境,但我们将使用conda环境。以下是获得完整环境的步骤:

  1. 访问 https://docs.conda.io/en/latest/miniconda.html 并为您的平台选择 Miniconda install 来安装 Miniconda。请选择最新的 Python3.x 版本。如果您已经安装了 Anaconda 或 Miniconda,可以跳过这一步。

  2. 我们将创建一个新的环境来运行本书附带的代码。打开命令终端,键入以下内容:

    conda create -n apress python=3.8
    
    

    其中apress是环境的名称,我们使用的是 Python 3.8.x。对所有提示回答“是”。

  3. 使用以下命令切换到您创建的新环境:

    conda activate apress
    
    

    确保我们在接下来的步骤中要求您运行的所有命令都在您激活新的conda环境的同一终端中执行。

  4. 安装 TensorFlow 2.x,可以参考 https://www.tensorflow.org/install 了解更多细节,也可以在conda shell 中运行以下命令:

    pip install tensorflow
    
    

    按照提示,适当回答(大部分是)。

  5. 我们现在将安装 PyTorch。访问 https://pytorch.org/get-started/locally/ 并选择您拥有的环境设置。你不需要一台支持 GPU 的机器。本书中的大多数例子都可以在 CPU 上运行良好,除了第六章中的一个例子,在那里我们训练一个代理玩 Atari 游戏。即使有了 GPU,训练一个雅达利游戏代理也可能需要很长时间。

    在写这本书的时候,我们在前一页选择了以下组合:

    PyTorch build: Stable (1.7.0)

    Your OS: Windows or Mac

    Package: Conda

    Language: Python

    CUDA: None

    有了这些选择,生成的命令如下:

    conda install pytorch torchvision torchaudio cpuonly -c pytorch
    
    

    将生成的命令从网页复制并粘贴到您的conda终端中,在这里您将apress作为当前活动的conda环境(步骤 3)。

    确保使用链接 https://pytorch.org/get-started/locally/ 为本地机器生成命令,并做出适当的选择。

  6. 接下来,我们将安装一个 Jupyter 笔记本。在之前的终端中,在终端中运行以下命令:

    conda install -c conda-forge jupyter notebook
    
    

    请注意,我们使用的是经典的 Jupyter 笔记本。然而,如果你喜欢你可以安装一个 JupyterLab 界面,你可以在 https://jupyter.org/install 找到更多的细节。

  7. 我们现在将安装 OpenAI Gym 库,其中包含各种强化学习环境,我们将使用这些环境来培训代理。请在命令行中键入以下内容:

    pip install gym
    
    

    详情可以参考 https://gym.openai.com/docs/#installation

  8. 下一行是matplotlib,,它有绘制图形的例程。请在前一个终端中运行以下命令。请注意,所有命令都必须在命令 shell 中运行,其中apress是当前活动的conda环境。

    conda install -c conda-forge matplotlib
    
    

    你可以在 https://matplotlib.org/ 了解更多matplotlib

  9. 让我们也安装另一个名为seaborn.的绘图库,它建立在matplotlib之上,帮助安装格式良好的图形例程。在终端中运行以下命令:

    conda install -c anaconda seaborn
    
    

你可以在 https://seaborn.pydata.org/index.html 了解更多关于 Seaborn 的信息。

  1. 我们将安装一个小图书馆,为培训提供进度条。在终端运行以下命令:

  2. 让我们为 OpenAI Gym 安装一些额外的依赖项。我们将安装 Atari 相关的依赖项,以便 Atari 游戏可以通过 OpenAI Gym 接口进行训练。我们还将安装 Box2D,以允许我们将在第八章中使用的连续控制依赖。最后,我们将安装pygame,它允许我们玩 Atari 游戏,并使用键盘与之交互。请在终端中使用以下命令:

conda install -c conda-forge tqdm

pip install gym[atari]

在 Mac 上,您可能需要运行pip install 'gym[atari]'。请注意单引号。

接下来,在终端运行以下两个命令:

conda install -c conda-forge pybox2d

pip install pygame

如果您使用的是 Windows,您可能需要重新安装 Atari 模拟器。从pip install gym[atari]直接安装似乎不能与 Windows 一起工作,并给出一个dll not found错误。但是,请不要跳过跑pip install gym[atari]。运行 Atari 模拟器需要安装一些其他的依赖项。运行以下两个命令来解决这个问题,并在您执行了pip install gym[atari]之后仅运行*😗

*1. 最后,我们安装stable-baselines3,它已经交付了许多流行的 RL 算法的实现。要安装它,请在终端中运行以下命令:

pip unistall atari-py

pip install -f https://github.com/Kojoley/atari-py/releases atari_py

pip install stable-baselines3

你可以在 https://github.com/DLR-RM/stable-baselines3 了解更多关于这个图书馆的信息。

  1. 现在您下载并解压缩本书附带的代码文件。打开终端,在解压后文件夹中导航。使用以下命令切换到先前安装的conda环境:
conda activate apress

接下来,在终端使用以下命令启动 Jupyter 笔记本:

jypyter notebook

此时,您将看到您的默认浏览器打开,并且您已经准备好导航到相应的章节(图 1-2 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-2

您可以打开笔记本来运行和研究代码。

图 1-3 显示了打开的第二章中的一个笔记本。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-3

打开示例笔记本

安装本地环境的替代方法

解压代码文件夹后,您会看到一个名为environments的文件夹。它包含两个 YML 环境文件。一个是 Windows 版,名为environment_win.yml,另一个是 macOS 版,名为environment_mac.yml。您可以使用这些文件在本地机器上复制环境。导航到该文件夹并运行以下程序。

在 Windows 上:

conda env create -f environment_win.yml

在 macOS 上:

conda env create -f environment_mac.yml

使用这种方法将处理前面的步骤 2 到 12。

摘要

在这一章中,我们首先介绍了强化学习领域,以及它是如何从一个严格的基于规则的决策系统演变为一个灵活的最佳行为学习系统的历史,它可以根据以前的经验自行学习。

我们讨论了机器学习的三个子分支,即监督学习、非监督学习和强化学习。我们比较了这三种方法,以阐述每种方法适用的问题背景。我们还讨论了组成强化设置的子组件和术语。它们是主体、行为、状态、行动、政策、奖励和环境。我们用一辆汽车和一个机器人的例子来说明这些子组件是如何相互作用的,以及这些术语的含义。

我们讨论了奖励和价值函数的概念。我们讨论了奖励是短期反馈,价值函数是代理行为的长期反馈。最后,我们介绍了基于模型和无模型的学习方法。接下来,我们谈到了深度学习在强化学习领域的影响,以及 DQN 是如何开启深度学习与强化学习相结合的趋势的。我们还讨论了组合方法如何产生可扩展的学习,包括来自图像、文本和语音等非结构化输入的学习。

我们接着讨论了强化学习的各种用例,引用了自动驾驶汽车、智能机器人、推荐系统、金融和贸易、医疗保健以及视频/棋盘游戏等领域的例子。最后,我们介绍了设置 Python 环境并能够运行本书附带的代码示例所需的步骤。

https://en.wikipedia.org/wiki/Mainstream_Science_on_Intelligence

**

二、马尔可夫决策过程

正如在第一章中所讨论的,强化学习包括顺序决策。在这一章中,我们将在模拟连续决策行为的概率分支下形式化使用随机过程的概念。虽然我们在强化学习中研究的大多数问题都被建模为马尔可夫决策过程 (MDP),但我们首先介绍马尔可夫链(MC),然后介绍马尔可夫奖励过程(MRP)。我们最后深入讨论了 MDP,同时涵盖了 MDP 背后的模型设置和假设。

然后我们讨论了相关的概念,如状态的价值函数和状态-动作对的动作价值函数。最后,我们在本章结尾详细讨论了各种形式的贝尔曼方程,如贝尔曼期望方程和贝尔曼最优性方程,并简要介绍了各种类型的学习算法。

虽然本章的重点将是强化学习的理论基础,我们将有例子和练习来帮助巩固概念。没有比自己编码更好的学习方法了,这是本书反复出现的主题。

强化学习的定义

在前一章中,我们讨论了一个主体通过基于其当前状态采取行动、获得数字奖励以及发现自己处于新状态而与环境互动的循环。

图 2-1 说明了这个概念。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-1

代理环境交互的循环

代理在时间 t 处于状态 S t 。从代理在这种状态下可以采取的一组动作中,它采取了一个特定的动作 A t 。此时,系统过渡到下一个时间段(t+1)。环境用数字奖励 R t+1 来响应代理,并且将代理置于 S t+1 的新状态。“状态到行动到奖励和下一个状态”的循环一直持续到时间代理达到某个最终目标状态,如游戏结束、任务完成或特定数量的时间步结束。

代理行为环境反应

St->At->Rt+1,S t+1

当前状态采取的行动奖励和新状态

S 0 ,A 0 ,R 1 ,S 1 ,A 1 ,R 2 ,S 2 ,A 2 ,R 3 ,S 3 ,A 3 ,R 4 ,S

有一个状态、行动、回报和状态(S,A,R,S)的循环。

代理根据它发现自己所处的状态采取行动;即,代理通过采取动作来“行动”。环境对代理的行为作出“反应”,给代理一些数字奖励,并把代理转换到一个新的状态。代理人的目标是采取一系列行动,使其从环境中获得的总回报最大化。

强化学习的目的是让代理学习它可能发现自己所处的每个状态的最佳可能动作,牢记累积报酬最大化的目标。

作为一个例子,考虑国际象棋比赛。棋子在棋盘上的位置可以形成当前状态**(S**t)。代理人(玩家)通过移动棋子采取动作**(A**t)。代理人获得奖励**(R**t+1),假设 0 代表安全移动,1 代表导致将死的移动。游戏也移动到新的状态,(St+1)

有时,在文献中,状态也被称为观察值,以区分在某些情况下代理可能只能看到实际状态的部分细节。这种部分可观测的状态被称为观测。代理必须使用全部或部分状态信息来做出关于它应该采取的动作的决定。在现实生活的实现中,理解代理将观察到什么以及它将有多详细是一个重要的方面。学习算法的选择和理论保证会受到部分可观测性水平的显著影响。我们将首先关注状态和观察值相同的情况;换句话说,代理知道当前状态的每一个可能的细节。但是从第五章开始,我们将开始关注状态不是完全已知,或者即使完全已知也需要使用某种近似来总结的情况。

现在让我们通过几个例子来深入理解状态/观察到行动再到下一个状态的循环。在整本书中,我们将使用 OpenAI 的 Python 库 Gym,它实现了一些常见的简单环境。我们来看第一个环境:MountainCar。启动 Jupyter 笔记本并导航至listing_2_1.ipynb

MountainCar环境中,有一个汽车试图攀爬的小山,最终目标是到达小山右上角的旗帜。车子动力不够,需要向左摆动,然后向右加速才能到达目标。这种来回摆动需要发生多次,这样汽车才能获得足够的动力,到达右谷顶部的旗帜。见图 2-2 。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-2

登山车-v0 环境。这个环境有一个二维状态和一组三个离散的动作

清单 2-1 显示了测试MountainCar环境的代码。

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

# reset environment and get initial observation/state
# Observation/state is a tuple of (position, velocity)
obs = env.reset()
print("initial observation:", obs)

# possible 3 actions
# {0: "accelerate to left", "1": "do nothing", "2": "accelerate to right"}
print("possible actions:", env.action_space.n)

# reinforcement learning is all
# about learing to take good actions
# from a given state/observation
# right now taking a random action
def policy(observation):
    return env.action_space.sample()

# take 5 random actions/steps
for _ in range(5):

    # to render environment for visual inspection
    # when you train, you can skip rendering to speed up
    env.render()

    # based on curret policy, use the current observation
    # to find the best action to take.
    action = policy(obs)
    print("\ntaking action:", action)

    # pass the action to env which will return back
    # with new state/"observation" and "reward"
    # there is a "done" flag which is true when game ends
    # "info" provides some diagnostic information
    obs, reward, done, info = env.step(action)
    print("got reward: {0}. New state/observation is: {1}".format(reward, obs))

# close the enviroment
env.close()

Listing 2-1MountainCar Environment

让我们一行一行地浏览代码。我们先用import gym导入 OpenAI 健身房库。OpenAI Gym 为强化学习实现了多种环境。在我们阅读本书的章节时,我们将会用到其中的一些环境。

继续,我们用env = gym.make('MountainCar-v0').实例化MountainCar环境,然后通过obs = env.reset()初始化环境,返回初始观察值。在MountainCar,的情况下,观察值是两个值的元组:(x-position, x-velocity)。代理通过观察找到最佳行动:action = policy(obs)

在清单 2-1 中,代理采取随机行动。然而,随着我们在本书中的进展,我们将学习不同的算法,这些算法将被用来寻找报酬最大化的政策。对于MountainCar环境,有三种可能的动作:向左加速,什么都不做,向右加速。代理将动作传递给环境。在这一点上,系统在概念上采用从时间 t 移动到时间 t+1 的时间步长。

环境执行这个动作并返回一个由四个值组成的元组:时间(t+1)的新观察值、奖励(r t+1 )、完成标志和一些调试信息。这些值用obsrewarddoneinfo = env.step(action)存储在本地。

接下来,代理使用新的观察再次采取下一步骤,取回新的四个值的元组,即下一状态、奖励、完成标志和调试信息。这种“状态到行动再到奖励到新状态”的循环一直持续到游戏结束或者在代码中终止。在这个设置中,代理在每个时间步长获得-1 的奖励,当游戏结束时奖励为 0。因此,代理的目标是在尽可能短的步数内到达标志。

让我们看看另一个环境。用env = gym.make('CartPole-v1')替换env = gym.make('MountainCar-v0')行,再次运行清单 2-1 中的代码。见图 2-3 。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-3

CartPole-v1 环境。这样做的目的是尽可能长时间保持立杆的平衡

CartPole环境有一个由四个值组成的观察空间。购物车在 x 轴上的位置和沿 x 轴的速度是前两个值。杆的倾斜角是观察元组中的第三个值,必须在-24 o 到 24 o 之间。观测元组中的第四个值是极点的角速度。

可能的动作是 0 和 1,分别向左或向右推购物车。代理人在每个时间步长不断获得 1 的奖励,从而激励代理人在尽可能长的时间内平衡极点并获得尽可能多的点。如果杆子在任一侧倾斜超过 12 度 o ,或者推车在任一侧移动超过 2.4 度,即推车到达任一端,或者已经走了 200 步,游戏结束。

您可能已经注意到,这两种情况下的代码设置是相同的。我们只是更改了一行代码来实例化不同的环境。遍历环境并接收反馈的其余代码保持不变。OpenAI Gym 库提供的抽象使我们更容易在许多环境中测试特定的算法。此外,我们还可以根据手头的问题创建自己的定制环境。

本节涵盖了强化学习的简要定义和我们将使用的 OpenAI 健身房环境的基础知识。在接下来的章节中,我们将详细介绍强化学习(RL)设置的不同组件。

代理和环境

代理和环境的设置非常灵活。代理是一个从系统外部获取状态/观察细节的封闭系统。它使用一个给定的策略或学习一个策略来最大化提供给它的一些目标。它还根据当前的观察/状态和代理采取的行动从环境中获得奖励。代理无法控制这些奖励将会是什么。代理也不控制从一种状态到另一种状态的转换。这种转变取决于环境,主体只能通过决定在特定状态下采取何种行动来间接影响结果。

另一方面,环境是代理之外的一切。换句话说,它可能是整个世界的其余部分。然而,环境的定义通常非常狭窄:包括可能影响奖励的信息。环境接收代理想要采取的动作。它以奖励的形式向代理提供反馈,并根据代理采取的行动转换到新的状态。接下来,环境向代理提供部分修改后的状态信息,该信息成为代理的新观察/状态。

代理和环境之间的边界是抽象的。它是根据手头的任务和我们在特定情况下试图实现的目标来定义的。让我们看一些例子。

考虑一下自动驾驶汽车的情况。代理状态可以是来自多个相机、光探测和测距(LiDAR)的视觉图像、其他传感器读数以及地理坐标。虽然“环境”是代理之外的一切,即整个世界,但是代理的相关状态/观察仅是与代理采取行动相关的世界的那些部分。两个街区外的行人的位置和动作可能与自动驾驶汽车做出决策无关,因此不需要成为代理的观察/状态的一部分。自动驾驶汽车的行动空间可以根据油门踏板值、刹车和转向控制来定义。车辆采取的行动导致汽车转换到新的观察状态。如此循环下去。期望代理(即,自动驾驶汽车)基于特定目标(例如,从 A 点到 b 点)采取最大化回报的行动

让我们考虑另一个机器人试图解魔方的例子。这种情况下的观察/状态是魔方六个面的配置,而动作是可以在魔方上执行的操作。对于每个时间步长,奖励可以是-1,而在成功求解结束时,即在终止时,奖励可以是 0。这样的奖励设置将激励代理找到最少数量的操作来解决难题。

在我们的设置中,我们将使用 OpenAI 健身房环境,观察将始终是一个各种值的元组,其确切组成取决于特定的健身房环境。行动将取决于具体的环境。奖励将由环境提供,具体的实数数值取决于具体的健身房环境。在这种情况下,代理将是我们编写的软件程序。代理(软件程序)的主要工作将是接收来自 OpenAI 健身房环境的观察,采取行动,并等待环境在奖励和下一状态方面提供反馈。

虽然我们谈论的是离散的步骤,即处于状态(S t )的代理采取行动(A t )以及在下一个时间步骤中接收奖励(R t+1 )和下一个状态(St+1),但实际问题的本质往往是连续的。在这种情况下,我们可以在概念上将时间划分为小的离散时间步长,从而将问题建模回离散时间步长环境,并使用前面所示的熟悉设置进行求解。

在最一般的层面上,当代理发现自己处于状态 S 时,它可以采取许多可能的动作中的一个,这些动作在动作空间上具有某种概率分布。这种政策被称为随机政策。此外,在某些情况下,每当代理发现自己处于给定状态时,代理可能只对该状态采取一个特定动作。这种策略被称为确定性策略。策略的定义如下:

)

(2.1)

即当代理处于状态 s 时采取动作 a 的概率

类似地,在最一般的水平上,所接收的奖励和代理的下一个状态将是奖励和下一个状态的可能值的概率分布。这被称为跃迁动力学

)

(2.2)

其中 S t 和 S t-1 属于所有可能的状态。A t 属于所有可能的行动,奖励 r 是一个数值。前面的等式定义了下一个状态是 s '的概率,当上一个状态是 s,代理采取行动 a 时,奖励是 r。

奖励

在强化学习中,奖励是环境给代理人的一个信号,让代理人知道行动的好坏。代理使用这个反馈来微调它的知识,并学习采取好的行动来最大化回报。这引发了一些重要的问题,比如你最大化了什么,对最后一次行动的直接回报还是对整个生命历程的回报?当代理对环境不够了解时会发生什么?它应该在多大程度上通过在开始之前采取一些随机步骤来探索环境?这种困境被称为勘探与开发的困境。在我们讨论各种算法时,我们会不断回到这一点。代理人最大化累计总报酬的目标被称为报酬假设

重申一下,奖励是一个信号,或者是一个单一的数值,由环境发回给代理,告知代理行动的质量。请注意,观察可以是多维的,比如对MountainCar是二维的,对CartPole是四维的。类似地,动作可以是多维的,例如,自动驾驶汽车场景的加速度值和转向角。然而,在每种情况下,奖励总是一个标量实数。只有一个值似乎有局限性,但事实并非如此。最后,为了达到目标,代理人接受训练,而奖励则记录了这一过程。

让我们来看一个迷宫的例子,在这个例子中,代理试图找到它的出路。我们可以把奖励表述为代理在每个时间点获得-1 的奖励,在一集结束时获得 0 的奖励。这样的奖励设置激励代理人以最少的可能步数走出迷宫,并最小化负一的总和(-1)。另一个奖励设置可以是代理在所有时间步骤中获得奖励 0,在每集结束时代理走出迷宫时获得奖励 1。你认为在后期设定中,代理的行为会发生什么变化?代理人有理由走出迷宫去收集+1 的奖励,但它并不着急。不管是 5 步后出来还是 500 步后出来都会得到相同的+1。我们如何改变这种情况,促使代理不仅仅关注奖励的收取,而是在尽可能短的时间内这样做?

这个问题自然引出了贴现的概念。什么更有用?五个时间步后奖励“x”还是 500 个时间步后奖励“x”?当然,越早越好,因此 5 步后+1 的奖励比 500 步后+1 的奖励更有价值。我们通过贴现未来到现在的回报来诱导这种行为。来自下一个时间步的奖励“R”通过折扣因子“γ”(γ)折扣到当前时间。折扣系数是一个介于 0 和 1 之间的值。在迷宫示例中,在五个步骤中完成迷宫的奖励意味着奖励γ 5 。(+1)对奖励γ 500 。(+1)为 500 步完成。时间“t”处的“返回”定义如下:

g= r+r+2+2【r】++

贴现因子类似于我们在金融界看到的。这和现在的钱比将来的钱更有价值是一个概念。

贴现因子还有一个重要的数学目的,即确保总收益 G t 对于连续任务是有界的。考虑一个持续任务的场景,其中每个状态给出一些积极的奖励。由于这是一项没有逻辑终点的持续任务,总回报将不断增加,并爆炸到无穷大。然而,有了折扣因素,总的累积奖励将得到上限。因此,折扣总是在连续任务中引入,在间断任务中是可选的。

有趣的是要注意贴现因子对代理人试图最大化累积报酬的影响。考虑折扣系数为 0。如果你在( 2.3 中使用这个折扣值,你会看到累积奖励正好等于下一个即时奖励的奖励。这反过来会导致代理人变得短视和贪婪,只考虑下一个时间步骤的奖励。接下来,考虑贴现因子接近 1 的另一个极端。在这种情况下,代理人将变得越来越有远见,因为使用贴现因子 1,我们可以看到在( 2.3 )中定义的累积报酬将给予所有未来报酬同等的重要性。从当前时间步到结束的整个行动序列变得重要,而不仅仅是紧接的下一个时间步奖励。

前面的讨论应该强调根据代理需要优化的行为来设计适当的奖励的重要性。奖励是代理用来决定好的或坏的状态和/或行动的信号。例如,在一场国际象棋比赛中,如果你将奖励设计为捕获的对手棋子的数量,那么代理人可能会学习进行危险的移动,以最大化立即行动的奖励,而不是牺牲自己的一个棋子来获得优势地位和未来移动的可能胜利。奖励设计领域是一个开放的研究领域。话虽如此,书中的例子还是会使用相当简单直观的奖励定义。

奖励设计不是一件容易的事情,尤其是在连续控制和机器人领域。考虑一个人形机器人的任务,比如说,训练的目标是让代理尽可能长时间地学习奔跑。代理将如何知道手臂和腿需要协调移动的方式来学习跑步的任务?代理人的重心离地面的距离、做出动作所花费的能量、躯干与地面的角度等具体措施,结合试错法,使代理人学会一个好的策略。如果没有一个好的奖励信号来塑造我们希望代理人学习的行为,代理人将需要很长时间来学习。更糟糕的是,代理有时可能会学习到违反直觉的行为。一个很好的例子是 OpenAI 的代理玩视频游戏 CoastRunners,其目标是快速完成划船比赛,并领先于其他玩家。游戏提供了击中目标的分数,没有明确的完成游戏的分数。这位代理人学会了一种滑稽和破坏性的行为,即重复击中一组目标,但在比赛中没有进步,但得分比人类选手高 20%。你可以在 OpenAI 博客 1 中了解更多信息,并观看一段视频。奖励需要精心设计,以确保自主 RL 代理不会学习到潜在的危险行为。

在其他情况下,根本不清楚如何对奖励函数进行建模。假设一个机器人试图学习类似人类的行为,或者说,从水壶向玻璃杯倒水,而不会因为过度的抓握力而溢出水或打碎玻璃杯。在这种情况下,被称为逆强化学习的强化学习的扩展被用于基于观看人类专家执行任务的观察来学习隐式奖励函数。我们将在第十章中简要谈论它。然而,对奖励形成和发现的详细研究可以单独成书。

因此,简而言之,就像数据质量对监督学习很重要一样,一个合适的奖励函数对于让算法训练代理人达到预期行为也很重要。

马尔可夫过程

强化学习领域是基于马尔可夫过程的形式主义。在我们深入学习(行为优化)算法之前,我们需要很好地掌握这个基本的理论结构。在这一节中,我们将讨论马尔可夫链,然后是马尔可夫回报过程,最后是马尔可夫决策过程。

马尔可夫链

先说什么是马尔可夫性质。考虑图 2-4 中的图表。我们正试图模拟一个城市每天的降雨状况。它有两种状态;也就是说,在任何一天,要么下雨,要么不下雨。从一个状态到另一个状态的箭头表示基于当前状态第二天处于两个状态之一的概率。比如今天下雨,第二天下雨的几率是 0.3,第二天不下雨的几率是 0.7。同样,如果今天没有下雨,明天继续干燥的可能性是 0.8,而明天下雨的可能性是 0.2。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-4

简单的双态马尔可夫链。一种状态是“下雨”,第二种状态是“不下雨”

在这个模型中,我们假设某一天的降雨取决于前一天的状态;即,如果今天也下雨,明天下雨的可能性是 0.3,如果今天不下雨,明天下雨的可能性是 0.2。昨天下雨还是之前下雨对明天下雨的概率没有影响。这是一个重要的概念。被称为独立;即知道现在(t 时刻的现在状态)使得未来(t+1 时刻的未来状态)独立于过去(0,1,…,t-1 时刻的所有过去状态)。数学上,我们可以这样表达:

)

(2.4)

它仅仅意味着处于时间 t+1 的状态的概率仅取决于时间 t 的状态。时间(t+1)的状态不依赖于时间(t)之前的状态,即不依赖于状态 S 0 至 S t-1

如果环境向代理提供足够详细的观察,那么代理可以从它的当前状态知道什么是已知的,并且不需要记住它在过去到达现在必须经历的状态/事件链。这个马尔可夫独立性是一个重要的假设,需要它来证明强化学习算法的理论合理性。在实践中,很多时候我们对于非马尔可夫系统仍然得到相当好的结果。然而,重要的是要记住,在没有马尔可夫属性的情况下,结果不能被评估为理论上的最坏情况界限。

回到图 2-3 中的马尔可夫链图,我们可以将转移概率定义为在前一时间步中从状态 S t 移动到状态 S t+1 的概率。如果一个系统有 m 个状态,转移概率将是一个 m 行 m 列的方阵。图 2-3 的转移概率矩阵如下所示:

)

每一行中的值之和将为 1。行值表示从一个给定状态到系统中所有状态的概率。例如,第 1 行表示从 s 1 到 s 1 的概率是 0.3,从 s 1 到 s 2 的概率是 0.7。

前面的马尔可夫链将有一个稳定状态,其中在给定的一天有一个确定的概率处于两个状态之一。假设处于状态 s 1 和 s 2 的概率由一个向量 S =【S1S2T给出。从图 2-4 中我们可以看出

s1= 0.3 x s1+0.2 x s2(a)

s2= 0.7 x s1+0.8 x s2(b)

我们也知道

s 1 + s 2 = 1 ©

因为系统在任何时间点都必须处于两种状态之一。

(a)、(b)和©中的方程构成了一个一致的、可解的方程组。

从(a)中,0.7 x s 1 = 0.2 x s 2 。或者,s 1 = (0.2/0.7) s 2

将之前©中的 s 1 的值代入,我们得到:

0.2/0.7 秒 2 +秒 2 = 1

由此得出 s 2 = 0.7/0.9 = 0.78,进而得出 s 1 = 0.2/0.9 = 0.22。

在向量代数符号中,我们可以将稳定状态下的关系指定如下:

S T = S T 。P ……©

该关系可用于迭代求解稳态概率。清单 2-2 给出了一个代码片段。

# import numpy library to do vector algebra
import numpy as np

# define transition matrix
P = np.array([[0.3, 0.7], [0.2, 0.8]])
print("Transition Matrix:\n", P)

# define any starting solution to state probabilities
# Here we assume equal probabilities for all the states
S = np.array([0.5, 0.5])

# run through 10 iterations to calculate steady state
# transition probabilities
for i in range(10):
    S = np.dot(S, P)
    print("\nIter {0}. Probability vector S = {1}".format(i, S))

print("\nFinal Vector S={0}".format(S))

Listing 2-2Markov Chain Example and Its Solution by Iterative Method

当我们运行清单 _2_2.ipynb 中的程序时,产生的输出如下所示,它与我们一起求解等式(a)、(b)和©得到的值相匹配:

Final Vector S=[0.22222222 0.77777778]

图 2-3 中的公式没有开始和结束状态。这是一个继续任务的例子。还有另一类公式有一个或多个最终状态。让我们看看图 2-4 。这就是所谓的阶段性任务,其中代理从某个状态开始,经过许多转换,最终到达结束状态。可能有一个或多个具有不同结果的结束状态。在图 2-5 中,结束状态是成功完成考试并获得证书。一盘棋可能有三种结局:赢、输或平。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-5

具有一个通过方形框描述的结束状态的情节马尔可夫链的例子

像连续公式一样,有一个转移概率矩阵的概念。对于图 2-4 ,它看起来像这样:

s 1 =“学习”;s 2 =“走神”;s 3 =“参加考试”;s 4 = “证书”

)

在前面显示的情节任务的情况下,我们可以查看多次运行,每次运行称为一个情节。让开始状态总是 s 1 。多集的例子可能是这样的。在前面的例子中,我们只有一个结束状态,因此剧集将总是在 s 4 结束,如下所示:

s 1 ,s 2 ,s 2 ,s 1 ,s 3 ,s 4

s 1 ,s 2 ,s 2 ,s 1 ,s 3 ,s 1 ,s 2 ,s 3, s 4

s 1 ,s 2 ,s 2 ,s 2 ,s 2 ,s 2 ,s 2 ,s 2 ,s 2 ,s 2 ,s 2 ,s 2 ,s

在阶段性任务中,我们没有稳定状态的概念。最终,系统将转换到最终状态之一,而不管转换的顺序如何。

马尔可夫奖励过程

转到马尔可夫奖励过程,我们现在引入奖励的概念。查看图 2-6 和图 2-7 中修改后的状态图。它们与上一节中的问题相同(分别为图 2-4 和图 2-5 ),只是在每个过渡阶段增加了奖励。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-7

类似于图 2-5 的阶段性马尔可夫奖励过程,每次转换都有额外的奖励 R

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-6

持续马尔可夫奖励过程。这类似于图 2-4 中的马尔可夫链,只是为每个转移箭头增加了奖励 R

在前面的两个 MRP 设置中,我们可以计算类似于前面 MC 中的转移概率。此外,我们可以计算一个状态 v(s)的值,这是代理人在 t 时刻处于状态 S=s 时得到的累积报酬,它遵循系统的动力学。

)

(2.5)

其中 G t 在 2.3 中定义。

)

符号E[Gt|St=S读作期望返回 G t 当 t 时刻的起始状态为St=期望值一词是指进行大量模拟时 G t 的平均值。期望算子(E[])用于推导公式和证明理论结果。然而,在实践中,它被许多样本模拟的平均值所代替,也被称为蒙特卡罗模拟

我们还注意到使用了 γ 作为折扣因子。如前所述, γ 抓住了今天的回报比明天的回报更好的概念。在数学上避免连续任务的无限回报也很重要。除了提到这个事实之外,我们将不在这里深入研究数学细节。

γ = 1 意味着代理人是有远见的,它对未来的回报和对眼前的回报一样关心。值 γ = 0 意味着代理人目光短浅,只关心下一时间步的即时回报。你可以通过在方程Gt=Rt+1+γR+2+γ2R中放入 γ 的不同值来检验这个概念

综上所述,到目前为止我们已经介绍了转移概率 P 的概念,返回Gt,以及状态值v(S)=E**Gt|St=S

马尔可夫决策过程

马尔可夫决策过程通过引入“行动”的额外概念来扩展奖励过程在 MRP 中,代理人无法控制结果。一切都受环境支配。然而,在 MDP 体制下,代理可以基于当前状态/观察来选择动作。代理人可以学习采取使累积报酬最大化的行动,即总回报 G t

让我们看看图 2-7 中的阶段性 MRP 的扩展。查看图 2-8 ,可以看到在状态学习中,代理可以采取两种行动中的一种,要么继续学习,要么参加考试。代理以 0.5 的相等概率选择这两个动作。该动作影响下一个状态的概率和奖励值。如果代理人决定学习更多,则有 0.7%的可能性代理人会分心并忙于社交媒体,有 0.3%的可能性代理人会继续专注于学习。但是,“考”的决定导致两种结果。要么代理以 0.4 的概率失败回去学习奖励-2,要么代理以 0.6 的概率成功完成考试,“领证”,奖励 10。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-8

图 2-7 中给出的作为马尔可夫回报过程的扩展的情景马尔可夫决策过程。黑色实心圆圈是代理可以做出的决定

转换函数现在是从当前状态和动作到下一个状态和奖励的映射。它定义了每当代理人在状态 s 采取行动 a 时,获得奖励 r 并转移到下一个状态**s’**的概率。

)

(2.6)

我们可以用( 2.6 )推导出很多有用的关系。转移概率可以从先前的转移函数中导出。转移概率定义了当代理在状态 s 中采取动作 a 时,它发现自己处于状态**s’**的概率。其定义如下:

)

(2.7)

( 2.7 )已经通过平均代理人在从 (s,a) 过渡到**s’**时可能获得的所有奖励而获得。

让我们看看 MDP 的另一个例子,这是一项持续的任务。想象一下机场里的电动货车将乘客从一个航站楼运送到另一个航站楼。货车有两种状态,“高电荷”和“低电荷”在每个州,面包车可以“保持闲置”,“通过连接到充电站充电”,或“运送乘客”如果面包车运送乘客,它得到奖励“b”,但在“低电量”状态下,运送乘客有可能会完全耗尽电池,面包车需要救援,导致奖励-10。图 2-9 显示了 MDP。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-9

电动货车:具有两种状态的连续马尔可夫决策过程。每个状态有三个动作:空闲、摆渡或充电

在“低”状态下,货车可以从一组可能的动作{再充电、空闲、摆渡}中采取一个动作。在“High”状态下,面包车可以从{idle,ferry}采取一个动作。所有其他跃迁为零。有了前面给出的 p(s ‘,r | s,a ),就可以计算 p(s’ | s,a)。在这种情况下,它与 p(s ',r | s,a)相同。这是因为从(s,a)到(s’)的每一次转变只有一个固定的回报。换句话说,奖励没有不确定性或概率分布。每当处于状态 s 的代理采取行动 a 并发现自己处于下一个状态**s’**时,奖励是相同的固定值。前面的设置是实际问题中最常见的设置之一。然而,从理论上讲,在大多数情况下,奖励可能是一种概率分布,如“摆渡”行动奖励与货车运送的乘客数量相关联。

我们将在本章的以下部分继续使用这个例子。

政策和价值函数

如前所述,MDP 有状态,代理可以采取措施将代理从当前状态转换到下一个状态。此外,代理人以“奖励”的形式从环境中获得反馈 MDP 的动力学定义为p(St=S'Rt=R|St—1=SA 我们也看到了“累计回报” G t ,是从时间 t 开始收到的所有奖励的总和,代理人对过渡动态没有控制。这超出了代理的控制范围。然而,代理可以控制决策,即在哪个状态下采取什么动作。

这正是代理试图基于系统的转换动态来学习的内容。代理这样做的目的是最大化每个状态 S tG t ,这可以在多次运行中平均预期。状态到动作的映射被称为策略。其正式定义如下:

)

(2.8)

策略被定义为当代理在时间 t 处于状态 s 时,在时间 t 采取行动 a 的概率。代理试图学习从状态到行动的映射函数,以最大化总回报。

策略有两种类型。参见图 2-10 。第一类策略是随机策略,其中π( a | s )是概率函数。对于一个给定的状态,代理可以采取多种行动,采取每种行动的概率由 π ( a | s 定义。第二种类型是确定性策略,其中对于给定的状态只有一个唯一的动作。换句话说,概率函数π(At=A|St=S)就变成了一个简单的映射函数,对于某个动作At=【1】

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-10

政策类型。(a)随机政策,其中代理可以根据概率分布采取多种行动中的一种。(b)确定性策略,其中代理学习只采取一个最优的行动

代理人在状态 S t 时在时间 t 所能获得的累积奖励Gt(即回报)依赖于状态 S t ,策略代理人如下。它被称为状态值函数*。 G t 的值取决于代理在时间 t 之后将看到的状态的轨迹,这又取决于代理将遵循的策略。因此,值函数总是在代理遵循的策略的上下文中定义。它也被称为代理人的行为。其正式定义如下:*

)

(2.9)

让我们试着稍微分解一下。vπ(s)指定代理在遵循策略π时状态 s 的“状态值”。 E π 【】表示方括号内的值是多个样本的平均值。虽然它在数学上被称为策略π下方括号内表达式的期望值,但实际上我们通常使用模拟来计算这些值。我们通过多次迭代进行计算,然后取平均值。根据统计学的一个基本定律,在一般情况下,平均值收敛于期望值。这个概念在计算中被广泛使用,并被命名为蒙特卡罗模拟。到了最后一部分,方括号内的表达式是gtst=s,即代理人在 t 时刻处于状态 s 的行为下,在 t 时刻可以得到的多次运行的平均收益 G t

至此,我们来介绍一下术语备份图。它示出了从代理处于状态 S t 的时间 t 到代理在时间 t+1 可以发现自己处于其后继状态的路径。这取决于代理人在 t 时刻采取的动作,即π(At=A|St=S)。再进一步,还要看环境/车型过渡函数 Pr {St+1=SRt+1=R|St=S 它将代理带到状态 S t+1 ,基于状态 S t 和它采取的动作 A t 奖励 R t+1 。 形象地说,从当前状态到可能的后续状态的一步转换称为备份图,看起来如图 2-11 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-11

备份图表从状态开始,采取行动。空心圆圈表示状态,黑圆圈表示动作

我们将广泛使用备用图,尤其是在下一节讨论贝尔曼方程时。备份图有助于对等式进行概念化和推理,以及为各种学习算法提供证明。它们也有助于推理培训代理所需的数据收集。

另一个与价值函数相关的概念是行动价值函数的概念。价值函数是代理人根据策略 π 采取行动时获得的预期累积报酬。然而,假设代理可以在这个第一时间步 t 自由地采取任何行动,条件是它必须在所有随后的时间步 t+1 上遵循策略 π 。代理人在 t 时刻得到的期望收益现在称为行动价值函数qπ(sa )。正式定义如下:

)

(2.10)

v(状态值)和 q 值(状态动作值)之间有一个简单而微妙的关系,这将在下一节中详细探讨。

这基本上完成了 MDP 各种组件的定义。在下一节中,我们将探讨 t 时刻状态/状态动作的 v 和 q 值与 t+1 时刻后续值之间的递归关系。几乎所有的学习算法都利用了这种递归关系。

贝尔曼方程

我们再来看看( 2.9 ),它定义了价值函数。再来看( 2.3 )中定义的Gt的定义;两者均转载于此:

)

(2.11)

)

(2.12)

换句话说,一个状态的值是在该状态下的代理人 s 遵循一个策略 π 时累积回报的期望值/平均值。主体发现自己所处的状态和它从环境中获得的回报取决于它遵循的策略,即它在给定状态下采取的行动。有一个递归关系,其中用于 G t 的表达式可以用Gt+1来表示。

)

(2.13)

让我们专注于方括号内的表达式。

)

接下来,我们将变量从 t 更改为 t ,其中t=t+1。前面的表达式可以重写如下:

)

(2.14)

比较( 2.14 )与( 2.11 )中给出的Gt的表达式,我们看到

)

(2.15)

接下来,我们将( 2.15 )代入( 2.13 ),得到这个:

)

(2.16)

我们现在可以将表达式中的 G t 的递归定义替换为( 2.12 )中的vπ(s)以得到:

)

(2.17)

期望 E π 是在状态St=S以及环境将代理转换到由转换函数p(Sr 定义的所有新状态下,代理可以采取的所有可能动作 a 期望的扩展形式导致vπ(s)的修正表达式如下:

)

(2.18)

解释这个等式的方式是, s 的状态值是后续状态**s’**的所有回报和状态值的平均值。平均是基于在状态 s 中采取动作 a 的策略 π ( a | s ),接着是代理转移的环境转移概率p(sr | sa )( 2.18 )中的等式显示了将当前状态的状态值与后续状态的状态值相链接的递归性质。

动作值函数也存在类似的关系。让我们从( 2.10 )开始,走一遍 q 值之间递归关系的推导。

)

)

通过对所有可能性求和来扩展期望值,我们得到这个:

)

(2.19)

我们现在来看看vπ(s)和qπ(sa )的关系。它们通过代理所遵循的策略π( s | a )相关联。q 值是元组(s,a)的值,状态值是状态(s)的值。该策略通过概率分布将状态与一组可能的动作联系起来。结合关系中的这些结果,我们得到这样的结果:

)

(2.20)

前面的关系也可以从方程( 2.18 )和( 2.19 )合起来推导出来,其中我们根据方程( 2.19 )用qπ(sa )替换( 2.18 中的部分右边表达式。

就像等式( 2.18 )给出了一个关于下一个状态的状态值的递归关系vπ(s)vπ(s),我们也可以表示为 q π ain(2.19)就对子而言qπ(sa)。 这是通过将( 2.1 )中的vπ(s)替换为( 2.20 )中的表达式来实现的。这种操作为我们提供了 q 值之间的递归关系。

)

(2.21)

将当前状态值或 q 值与连续值联系起来的方程( 2.18 )和( 2.21 )可以通过备份图来表示。我们现在扩展图 2-12 中的后图,以涵盖前两种情况。该图遵循标准惯例:状态 s 显示为空心圆圈,表示 a 的动作节点显示为实心黑色圆圈。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-12

状态值和动作值的备份图。空心圆表示状态,实心圆表示动作

方程( 2.18 为vπT7(s)的贝尔曼方程,方程( 2.21 为qπ(sa )的贝尔曼方程。这些方程在类似强化学习的顺序决策设置中形式化了递归关系。强化学习中的所有算法都基于这两个方程的变体,目的是在不同的假设和近似下最大化价值函数。当我们讨论这些算法时,我们将继续强调方程中在某些假设下被近似的部分,以及各种方法的优缺点。

作为强化学习的实践者,你的大部分专业知识将首先围绕将现实生活中的问题公式化为 RL 设置,其次基于约束和假设选择正确的算法集。因此,本书的重点将放在使一种技术发挥最佳作用所需的条件和假设上,选择使用一种特定的技术是有意义的,以及对于一个给定的问题可用的竞争选择的利弊。我们将给出数学方程来形式化这种关系,但核心焦点将是帮助你对正在发生的事情以及给定的方法/算法何时有意义有一个直观的感觉。

最优性贝尔曼方程

解决强化学习问题意味着找到(学习)最大化状态值函数的策略。假设你有一套政策。目标是选择最大化状态值 v π (s)的策略。状态的最佳值函数被定义为 v ӿ (s)。最佳状态值的关系可以表述如下:

)

(2.22)

前面的等式表明,最佳状态值是在所有可能的策略π上可以获得的最大状态值。假设这个最优策略用策略的上标(*)表示,即π。如果一个代理遵循最优策略,那么处于(s)状态的代理将采取行动 a,这使得在最优策略下获得的 q(s,a)最大化。换句话说,等式( 2.20 )从期望值修改为最大值,如下所示:

)

(2.23)

此外,类似于方程( 2.18 )和( 2.22 ),最佳状态和动作值函数的递归形式给出如下:

)

(2.24)

)

(2.25)

这些最佳等式可以用图 2-13 所示的备份图来表示,突出显示当前值和后续值之间的递归关系。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-13

最佳状态值和动作值的备份图。策略π被“最大”操作所取代

如果你有所有的 v* 值,那么就很容易找到最优策略。我们使用如图 2-12 所示的一步备份图来寻找产生最优值 v* 的 a* 。可以看作是一步到位的搜索。如果给我们一个最优 q*(s,a),找到最优策略就更容易了。在状态 s 下,我们只要选择动作 a ,它的 q 值最高。这从等式( 2.23 )可以明显看出。让我们将这些概念应用于图 2-9 中介绍的电动厢式车的问题,该问题在图 2-14 中重现。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-14

电动货车:具有两种状态的连续马尔可夫决策过程。每个状态有三个动作:空闲、摆渡或充电。图 2-9 的再现

让我们画出描述各种值的表格,如表 2-1 所示。在这个表中我们列出了 ( sasr )的所有可能的组合以及概率p*(sr | sa )。*

表 2-1

图 2-14 中 MDP 的系统动力学

|

国家

|

行动(a)

|

新状态

|

奖励®

|

P(s ',r | s,a)

|
| — | — | — | — | — |
| 低电量 | 闲置的 | 低电量 | a | One |
| 低电量 | 费用 | 高电量电池 | Zero | One |
| 低电量 | 渡船 | 低电量 | b | 一个 |
| 低电量 | 渡船 | 高电量电池 | -10 | 1- a |
| 高电量电池 | 闲置的 | 高电量电池 | a | One |
| 高电量电池 | 渡船 | 高电量电池 | b | B |
| 高电量电池 | 渡船 | 低电量 | b | 1-b |

让我们使用表 2-1 使用等式( 2.24 )计算最佳状态值。

)

)

对于给定的 a、b、α、β、γ值,将有一组唯一的值v】∫()和v()满足前面的等式。然而,方程的显式求解只适用于简单的玩具问题。现实生活中更大的问题需要使用其他可扩展的方法。这正是我们在本书的其余部分将要研究的内容:求解贝尔曼方程的各种方法和途径,以得到一个最优策略,在给定的环境中,代理人应该遵循这个策略。

使用思维导图的解决方法的类型

在强化学习设置和贝尔曼方程的背景下,是时候看看强化学习世界中算法的前景了。来自 OpenAI 的图 2-15 显示了 RL 空间中各种类型的学习算法的高级景观。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-15

强化学习算法的思维导图。这是一个仅显示宽泛分类的高级图(参见 spinningup)。openai。com/en/latest/spinning up/rl _ intro 2。html

如在贝尔曼方程中所见,系统跃迁动力学p(sr | sa )形成中心部分。转变动力学描述了环境的模型。然而,跃迁动力学并不总是已知的。因此,学习算法的第一个广泛分类可以基于模型的知识(或缺乏模型的知识)来完成,即基于模型和无模型算法的分类。

基于模型的算法可以进一步分为两类:一类是给我们模型,例如围棋或象棋,第二类是代理需要探索和学习模型。“学习模型”下的一些流行方法是世界模型、想象力增强代理(I2A)、基于模型的 RL 和无模型微调(MBMF)以及基于模型的价值探索(MBVE)。

回到无模型设置,我们注意到贝尔曼方程为我们提供了一种找到状态/动作值并使用它们找到最优策略的方法。这些算法大多采用迭代改进的方法。最终,我们希望代理有一个最优的策略,算法使用贝尔曼方程来评估策略的好坏,并引导向正确的方向改进。然而,还有另一种方法。为什么不直接改善政策,而不是通过价值观间接影响政策?这种直接策略改进的方法被称为策略优化

回到无模型世界的范畴,Q 学习形成了无模型贝尔曼驱动的状态/动作值优化的主要部分。这种方法下的流行变体是深度 Q 网络(DQN)以及 DQN 的各种次要变体,如分类 51 原子 DQN (C51)、分位数回归 DQN (QR-DQN)和后见之明经验重放(HER)。

直接遵循策略优化路径的算法是策略梯度、行动者批评及其变体(A2C/A3C)、邻近策略优化(PPO)和信赖域策略优化(TRPO)。

最后,有一组算法介于 Q 学习和策略优化之间。这一类别中最受欢迎的是深度确定性政策梯度(DDPG)、孪生延迟 DDPG (TD3)和软行动者-批评家(SAC)。

这些分类只是为了帮助你理解不同的方法和流行的算法。然而,清单和分类并不详尽。强化学习领域正在迅速发展,新的方法也在不断增加。请仅将之前的思维导图用作高级指导。

摘要

本章介绍了什么是强化学习、设置和各种定义。然后讨论了贝尔曼方程和最优化方程,以及状态/动作值函数和备份图。本章最后对算法的前景进行了展望。

接下来的章节将从下一章的动态编程开始,更深入地研究这些算法。

https://openai.com/blog/faulty-reward-functions/

三、基于模型的算法

在第二章中,我们讨论了构成代理的设置部分和构成环境的部分。代理获得状态St=S,并学习将状态映射到动作的策略π( s | a )。当处于状态St=S时,代理使用该策略采取动作At=A。系统转换到下一个时刻 t + 1。环境通过将代理置于新的状态S*t*+1*=S'并以奖励的形式向代理提供反馈 R t +来响应动作(At)代理人无法控制新状态S*t+1和奖励Rt+1会是什么样。这个过渡从(St=SAt=A)→(Rt+1=R这被称为跃迁动力学*。对于给定的一对( sa ),可能有一对或多对( rs )。在一个确定性的世界里,我们会有一对( rs )的固定组合( sa )。然而,在随机环境中,即具有不确定结果的环境中,对于给定的( sa ),我们可能有许多对( rs' )。*

在这一章中,我们将重点介绍跃迁动力学 Pr {St+1=SRt+1=R|St=S,代理将使用这些知识来“规划”一个策略,使状态值 v π (s)的累积回报最大化。所有这些算法都将基于动态规划,它允许我们将问题分解成更小的子问题,并使用第二章中解释的贝尔曼方程的递归关系。在此过程中,您将了解如何在一般意义上改进政策的其他概念。

然而,在我们深入研究算法之前,我们将绕一小段路来研究本章将要用到的 RL 环境。一旦我们解决了这个问题,我们将把重点放在基于模型的算法上。

开放 AI 健身房

编码练习基于上一章简单介绍的 OpenAI Gym 环境。Gym 是 OpenAI 开发的一个库,用于比较强化学习(RL)算法。它提供了一套标准化的环境,可用于开发和比较各种 RL 算法。所有这些环境都有一个共享的接口,允许我们编写通用算法。

Gym 的安装很简单,在第一章的设置部分已经解释过了。在第二章中,我们介绍了学习强化学习时使用的两种流行环境:MountainCar-v0CartPole-v1。在这一章中,我们将使用一个更简单的环境来讨论动态编程。它是一个 4x4 的网格,如图 3-1 所示。左上和右下位置是终端状态,如图中阴影单元所示。在给定的单元中,代理可以向四个方向移动:UPRIGHTDOWNLEFT。除非有一堵墙,否则这些动作确定性地在动作的方向上移动代理。在碰壁的情况下,代理停留在当前位置。代理在每个时间步长获得-1 的奖励,直到它到达终止状态。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-1

网格世界环境。它是一个 4×4 的网格,终端状态位于左上角和右下角。网格中的数字代表状态 S

健身房图书馆不提供这种环境。我们将在 OpenAI 健身房创建一个自定义环境。虽然如果我们想要向外界发布环境,需要遵循一个文档化的( https://github.com/openai/gym/blob/master/docs/creating-environments.md )文件结构,但是我们将遵循一个更简单的单文件结构来定义网格世界环境,因为它仅供我们个人使用。一个环境必须实现以下功能:step(action)reset()render()

我们将采用扩展 Gym 中提供的模板环境之一的方法:DiscreteEnv。它已经实现了stepreset功能。我们只需要提供nA(每个状态中的动作数)nS(状态总数),以及一个字典P,其中P[s][a]给出了一个带有值(probability, next_state, reward, done)的元组列表。换句话说,它提供了过渡动力。换句话说,对于给定的状态 s 和动作 a,它给出了由可能的下一个状态 s ‘、奖励 r 和概率p(sr | sa )组成的元组列表。元组中的第四个值是布尔标志done,指示下一个状态 s’是终止状态还是非终止状态。

在基于模型的学习的当前设置下,过渡动态P是已知的,这是本章的重点。然而,P不应该直接用于无模型算法,即在没有模型知识的情况下学习的算法(过渡动态)。我们将在后续章节中研究无模型算法。清单 3-1 显示了脚本文件gridworld.py

import numpy as np
import sys
from gym.envs.toy_text import discrete
from contextlib import closing
from io import StringIO

# define the actions
UP = 0
RIGHT = 1
DOWN = 2
LEFT = 3

class GridworldEnv(discrete.DiscreteEnv):
    """
    A 4x4 Grid World environment from Sutton's Reinforcement
    Learning book chapter 4\. Termial states are top left and
    the bottom right corner.

    Actions are (UP=0, RIGHT=1, DOWN=2, LEFT=3).
    Actions going off the edge leave agent in current state.
    Reward of -1 at each step until agent reachs a terminal state.
    """

    metadata = {'render.modes': ['human', 'ansi']}

    def __init__(self):
        self.shape = (4, 4)
        self.nS = np.prod(self.shape)
        self.nA = 4

        P = {}
        for s in range(self.nS):
            position = np.unravel_index(s, self.shape)
            P[s] = {a: [] for a in range(self.nA)}
            P[s][UP] = self._transition_prob(position, [-1, 0])
            P[s][RIGHT] = self._transition_prob(position, [0, 1])
            P[s][DOWN] = self._transition_prob(position, [1, 0])
            P[s][LEFT] = self._transition_prob(position, [0, -1])

        # Initial state distribution is uniform

        isd = np.ones(self.nS) / self.nS

        # We expose the model of the environment for dynamic programming
        # This should not be used in any model-free learning algorithm
        self.P = P

        super(GridworldEnv, self).__init__(self.nS, self.nA, P, isd)

    def _limit_coordinates(self, coord):
        """
        Prevent the agent from falling out of the grid world
        :param coord:
        :return:
        """
        coord[0] = min(coord[0], self.shape[0] - 1)
        coord[0] = max(coord[0], 0)
        coord[1] = min(coord[1], self.shape[1] - 1)
        coord[1] = max(coord[1], 0)
        return coord

    def _transition_prob(self, current, delta):
        """
        Model Transitions. Prob is always 1.0.
        :param current: Current position on the grid as (row, col)
        :param delta: Change in position for transition
        :return: [(1.0, new_state, reward, done)]
        """

        # if stuck in terminal state
        current_state = np.ravel_multi_index(tuple(current), self.shape)
        if current_state == 0 or current_state == self.nS - 1:
            return [(1.0, current_state, 0, True)]

        new_position = np.array(current) + np.array(delta)
        new_position = self._limit_coordinates(new_position).astype(int)
        new_state = np.ravel_multi_index(tuple(new_position), self.shape)

        is_done = new_state == 0 or new_state == self.nS - 1

        return [(1.0, new_state, -1, is_done)]

    def render(self, mode="human"):
        outfile = StringIO() if mode == 'ansi' else sys.stdout

        for s in range(self.nS):
            position = np.unravel_index(s, self.shape)
            if self.s == s:
                output = " x "
            # Print terminal state
            elif s == 0 or s == self.nS - 1:
                output = " T "
            else:
                output = " o "

            if position[1] == 0:
                output = output.lstrip()
            if position[1] == self.shape[1] - 1:
                output = output.rstrip()
                output += '\n'

            outfile.write(output)
        outfile.write('\n')

        # No need to return anything for human
        if mode != 'human':
            with closing(outfile):
                return outfile.getvalue()

Listing 3-1Grid World Environment

GridworldEnv是通过扩展健身房库中提供的模板环境discrete.DiscreteEnv创建的。在__init__(self)中,我们根据图 3-1 中描述的动态定义nA, nS和过渡函数P。清单 3-1 完整地描述了将在本章剩余部分使用的定制健身房环境。

现在让我们把重点放在无模型算法上,这也是我们打算在本章“动态编程”一节中学习的内容。

动态规划

动态规划是 20 世纪 50 年代由理查德·贝尔曼提出的一种优化技术。它是指将一个复杂的问题分解成更简单的子问题,寻找子问题的最优解,然后将子问题最优解组合起来,得到原问题的最优解。我们来看看贝尔曼方程( 2。18 )表示一个状态的值vπ(s)就政策而言( a | s ),系统动力学p(sr | sa

??

我们是用其他状态值vπ(s)来表示值vπ(s),这些都是未知的。如果我们能够以某种方式获得当前状态的所有后续状态值,我们将能够计算出vπ(s)。这显示了方程的递归性质。

我们还注意到,当 s’是某个状态 s 的后继状态时,将会多次需要特定的值vπ(s)。由于这一性质,我们可以缓存(即存储)值vπ(s)并多次使用它来避免

动态规划是一种广泛用于各类问题的优化技术,它允许将复杂的问题分解成较小的问题。一些常见的应用是调度算法、最短路径之类的图形算法、维特比算法之类的图形模型以及生物信息学中的网格模型。因为这本书是关于强化学习的,我们将限制动态规划在求解贝尔曼期望和贝尔曼最优方程中的应用,包括价值函数和行动-价值函数。这些方程在( 2 中给出。18 ),( 2。21 ),( 2。24 ),以及( 2。25 ),并将其复制以备参考。

下面是价值函数的贝尔曼期望方程:

)

(3.1)

下面是行为-价值函数的贝尔曼期望方程:

)

(3.2)

下面是价值函数的贝尔曼最优性方程:

)

(3.3)

下面是作用值函数的贝尔曼最优方程:

)

(3.4)

这四个方程中的每一个都根据满足动态编程的递归性质的后继状态或状态动作的值来表示状态或状态动作对的 v 或 q 值。在下面的章节中,我们将首先使用期望方程来评估一项政策,这被称为评估预测。然后,我们将利用最优性方程来寻找最大化状态值和状态动作值的最优策略。接下来是一个关于通用设置的部分,这是一个广泛用于政策改进的通用框架。我们将通过谈论大规模问题设置中的实际挑战和在这种情况下优化动态规划的各种方法来结束这一章。

这一章将主要集中在这样一类问题上,在这类问题中,我们有一个有限的状态集,代理可以发现自己所处的状态,以及在每个状态中的有限的动作集。具有连续状态和连续动作的问题在技术上可以通过首先离散化状态和动作使用动态编程来解决。你可以在第四章的末尾看到这种方法的例子。它也将构成第五章的主要部分。

政策评估/预测

我们现在将利用方程( 3.1 )利用其迭代性质和动态编程的概念来导出状态值。等式( 3.1 )表示根据其后继状态的状态*?? sT7 的状态值。状态的值还取决于代理遵循的策略,该策略被定义为策略π(a|s)。由于值对策略的这种依赖性,所有状态值都标有π,以表示( 3.1 )中的状态值是通过遵循特定策略π(a|s获得的值。请注意,改变策略π会产生一组不同的值vπ(s)和qπ(sa )。*

( 3.1 中的关系可以用备份图图形化表示,如图 3-2 。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-2

状态值函数的贝尔曼期望方程的备份图。空心圆圈表示状态,黑圆圈表示动作

代理开始于状态 s 。它根据其当前策略π(a|s)将 a 作为动作。环境根据系统动力学p(sr | sa )将智能体转换到一个新的状态 s 连同奖励 r 。如你所见,方程( 3.1 )是一个方程组,每个状态一个方程。如果有∣ S ∣国家,我们就会有∣ S ∣这样的方程。等式的数量等于∣ S ∣,并且与未知 v(s)的数量相同,每个 vs=s一个。因此,( 3.1 )代表具有|S|个未知数的∣ S ∣方程组。我们可以用任何线性规划技术来解这个方程组。然而,它将涉及矩阵的求逆,因此对于现实生活中的大多数 RL 问题来说不是很实用。

相反,我们将求助于迭代解的使用。这是通过在第一次迭代 k = 0 时从一些随机状态值 v 0 ( s )开始,并在( 3.1 )的右侧使用它们来获得下一次迭代步骤的状态值来实现的。

)

(3.6)

注意下标从π到( k )和( k + 1)的变化。还要注意等号(=)到赋值(←)的变化。我们现在根据先前迭代 k 中的状态值来表示迭代( k + 1)中的状态值,并且在每次迭代中将有∣ S ∣(状态总数)这样的更新。可以看出,随着 k 增大并趋于无穷大(∞), V k 会收敛到 V π 。先前为给定策略寻找所有状态值的方法被称为策略评估。我们从在 k = 0 时任意选择的值 V 0 开始,并使用等式( 3.6 )迭代状态值,直到状态值 V k 停止变化。策略评估的另一个名称是预测,即预测给定策略的状态值。

通常,在每次迭代中,我们创建一个现有状态值 v 的新副本,并根据前一个数组中所有状态的值更新新数组中的所有值。我们为状态值维护两组数组, V kV k + 1 。这被称为同步更新,即基于来自先前迭代的状态值更新所有状态值。但是,还有一种替代方法。人们可以只维护一个状态值数组,并在适当的位置进行更新*,其中每个新值立即覆盖旧值。如果每个状态都更新了足够的次数,就地更新有助于加快收敛。这种就地更新被称为异步更新。在本章的后面,我们有一节专门介绍各种类型的就地更新。*

图 3-3 给出了迭代策略评估的伪代码。

ITERATIVE POLICY EVALUATION

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-3

策略评估算法

现在让我们将前面的算法应用到图 3-1 中给出的网格世界。我们将假设一个随机策略π( a | s ),其中四个动作(UPRIGHTDOWNLEFT)的每一个都有 0.25 的相等概率。清单 3-2 显示了应用于网格世界的策略评估代码。这是来自listing3_2.ipynb的文件。

Note

本书中的代码清单将只显示讨论上下文中的相关代码。请查看 Python 脚本文件和/或 Python 笔记本以了解完整的实现。

def policy_eval(policy, env, discount_factor=1.0, theta=0.00001):
    """
    Evaluate a policy given an environment and
    a full description of the environment's dynamics.

    Args:
        policy: [S, A] shaped matrix representing the policy. Random in our case
        env: OpenAI env. env.P -> transition dynamics of the environment.
            env.P[s][a] [(prob, next_state, reward, done)].
            env.nS is number of states in the environment.
            env.nA is number of actions in the environment.
        theta: Stop evaluation once value function change is
            less than theta for all states.
        discount_factor: Gamma discount factor.

    Returns:
        Vector of length env.nS representing the value function.
    """
    # Start with a (all 0) value function
    V = np.zeros(env.nS)
    V_new = np.copy(V)
    while True:
        delta = 0
        # For each state, perform a "backup"
        for s in range(env.nS):
            v = 0
            # Look at the possible next actions
            for a, pi_a in enumerate(policy[s]):
                # For each action, look at the possible next states...
                for prob, next_state, reward, done in env.P[s][a]:
                    # Calculate the expected value as per backup diagram

                    v += pi_a * prob * \
                        (reward + discount_factor * V[next_state])
            # How much our value function changed (across any states)
            V_new[s] = v
            delta = max(delta, np.abs(V_new[s] - V[s]))
        V = np.copy(V_new)
        # Stop if change is below a threshold
        if delta < theta:
            break
    return np.array(V)

Listing 3-2Policy Evaluation/Policy Planning: listing3_2.ipynb

当我们用遵循随机策略的代理运行网格世界的代码时,我们看到图 3-4 中给出的每个网格单元的状态值v【π(s)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-4

策略评估vπ(s)对于图 3-1 中的网格世界,代理遵循随机策略。四个动作UPDOWNLEFTRIGHT中的每一个都有 0.25 的相等概率

我们可以看到价值观已经趋同。我们来看看最后一列的第三行,状态值为vπ(s)= 14。在这种状态下,动作UP将代理带到状态值为-20 的单元,动作LEFT将代理带到状态值为-18 的单元,动作DOWN将代理带到值为 0 的终止状态,动作RIGHT碰壁,使代理处于相同的状态。让我们应用方程式( 3.1 )。我们将展开等式的右侧( 3.1 ),按顺序应用动作— TOPRIGHTDOWNLEFT:

-14 = 0.25*(-1+(-20)) + 0.25*(-1+(-14)) + 0.25*(-1+0) + 0.25*(-1+(-18))

-14 = -14

两边的值是匹配的,这证实了收敛。因此,图 3-4 所示的值是代理遵循随机策略时的状态值。请注意,我们已经考虑了γ = 1.0 的贴现因子。

了解了策略评估之后,在下一节中,我们将讨论如何针对给定的环境改进策略。

政策改进和迭代

上一节展示了如何迭代获取给定策略的状态值 v π ( s )。我们可以利用这些信息来改进政策。在我们的网格世界中,我们可以从任何状态采取四种行动。现在,我们不再遵循随机的策略π( a | s ),而是着眼于分别采取所有四个行动,然后在这一步之后遵循策略π的价值。这样就会给我们四个值 q ( sa )动作值,分别是在网格世界中采取四种可能动作中的每一种的动作值。

)

注意 q ( sa )没有π的下标。我们正在评估 q ( sa )在状态 S = s 时所有可能的动作。如果任一个 q ( sa )大于当前状态值vπ(S),则意味着当前策略π( a | s )没有采取最优行动,我们可以在当前状态下对策略S=采取 q 值最大化动作 A = a 并将其定义为状态 S = s 中的策略,将给出比当前策略π( a | s )更高的状态值。换句话说,我们定义如下:

)

(3.7)

由于一个叫做策略改进定理的一般结果(我们在这里不详细讨论),在新策略π ' 下所有状态的值将等于或大于策略π下的状态值。换句话说,在特定状态下选择一个最大化动作 S = s 虽然提高了那个状态的状态值,但不能降低其他状态的值。它既可以保持它们不变,也可以改善那些依赖于 S = s 的其他状态。数学上,我们可以这样表达:

)

最大化的先前最大化步骤(贪婪步骤)可以基于它们当前的 q 值应用于所有状态。这种跨所有状态的最大化动作的扩展被称为贪婪策略,递归状态值关系由贝尔曼最优方程( 3.4 给出)。

我们现在有了一个改进政策的框架。对于给定的 MDP,我们首先迭代地进行策略评估,以获得状态值 v ( s ),然后我们根据( 3.7 )应用最大化 q 值的动作的贪婪选择。这导致状态值与贝尔曼方程不同步,因为最大化步骤被应用于每个单独的状态,而没有流过所有的后续状态。因此,我们再次在新策略π ' 下进行策略迭代,以找到改进策略下的状态/动作值。一旦获得状态值,可以再次应用最大化动作来进一步将策略改进为π”。如此循环下去,直到观察不到进一步的改善。这个动作顺序可以描述如下:

)

从政策改进定理我们知道,贪婪改进和政策评估的每一次刷卡,)都给了我们一个优于上一个带)的政策。对于一个具有有限数量的离散状态和每个状态中有限数量的动作的 MDP,每次滑动都会导致一个改进,一旦我们停止观察状态值的任何进一步改进,就会找到一个最优策略。这在有限数量的改善周期内必然会发生。

这种寻找最优策略的方法被称为策略迭代。图 3-5 显示了策略迭代的伪代码。

POLICY ITERATION

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-5

有限 MDP 的策略迭代算法

让我们将前面的算法应用到图 3-1 中的网格世界。清单 3-3 显示了应用于网格世界的策略迭代的代码。完整代码见listing3_3.ipynb。功能policy_evaluation保持与清单 3-2 相同。有一个新函数policy_improvement,它应用贪婪最大化来返回一个对现有策略进行改进的策略。policy_iteration是一个循环运行policy_evaluationpolicy_improvement的函数,直到状态值停止增加并收敛到一个固定点。

# Policy Improvement

def policy_improvement(policy, V, env, discount_factor=1.0):
    """
    Improve a policy given an environment and a full description
    of the environment's dynamics and the state-values V.

    Args:
        policy: [S, A] shaped matrix representing the policy.
        V: current state-value for the given policy
        env: OpenAI env. env.P -> transition dynamics of the environment.
            env.P[s][a] [(prob, next_state, reward, done)].
            env.nS is number of states in the environment.
            env.nA is number of actions in the environment.
        discount_factor: Gamma discount factor.

    Returns:
        policy: [S, A] shaped matrix representing improved policy.
        policy_changed: boolean which has value of `True` if there
                        was a change in policy
    """

    def argmax_a(arr):
        """
        Return idxs of all max values in an array.
        """
        max_idx = []
        max_val = float('-inf')
        for idx, elem in enumerate(arr):
            if elem == max_val:
                max_idx.append(idx)
            elif elem > max_val:
                max_idx = [idx]
                max_val = elem
        return max_idx

    policy_changed = False

    Q = np.zeros([env.nS, env.nA])
    new_policy = np.zeros([env.nS, env.nA])

    # For each state, perform a "greedy improvement"
    for s in range(env.nS):
        old_action = np.array(policy[s])
        for a in range(env.nA):
            for prob, next_state, reward, done in env.P[s][a]:
                # Calculate the expected value as per backup diagram
                Q[s,a] += prob * (reward + discount_factor * V[next_state])

        # get maximizing actions and set new policy for state s
        best_actions = argmax_a(Q[s])
        new_policy[s, best_actions] = 1.0 / len(best_actions)

    if not np.allclose(new_policy[s], policy[s]):
        policy_changed = True

    return new_policy, policy_changed

# Policy Iteration
def policy_iteration(env, discount_factor=1.0, theta=0.00001):

    # initialize a random policy
    policy = np.ones([env.nS, env.nA]) / env.nA
    while True:
        V = policy_evaluation(policy, env, discount_factor, theta)
        policy, changed = policy_improvement(policy, V, env, discount_factor)
        if not changed: #terminate iteration once no improvement is observed
            V_optimal = policy_evaluation(policy, env, discount_factor, theta)
            print("Optimal Policy\n", policy)
            return np.array(V_optimal)

Listing 3-3Policy Iteration: listing3_3.ipynb

图 3-6 显示了在网格世界中运行policy_iteration后每个网格单元的状态值。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-6

图 3-1 中网格世界的策略迭代v(s)。代理遵循通过应用清单 3-3 中的 policy_iteration 找到的最优策略

我们看到,最佳状态值是达到最接近的终端状态所需的步数的负数。由于在代理到达终端状态之前,每个时间步长的回报都是-1,所以最优策略会以最少的可能步长将代理带到终端状态。对于某些状态,一个以上的动作可能导致相同数量的步骤到达最终状态。例如,查看状态值= -3 的右上角状态,需要三个步骤才能到达左上角的终止状态或右下角的终止状态。换句话说,状态值是状态和最近的终端状态之间的曼哈顿距离的负值。

我们还可以提取最优策略,如图 3-7 所示。图的左边显示了从清单 3-3 的代码中提取的策略,图的右边显示了以图形方式叠加在网格上的相同策略。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-7

从图 3-1 到图 3-6 所示网格世界的策略迭代v(s)。左图:网格中每个单元都有行动概率的最优策略。右图:叠加了最优策略的网格

策略评估也被称为预测,因为我们试图找到与代理正在遵循的当前策略一致的状态值。同样,利用策略迭代寻找最优策略也被称为ccontrol:控制 agent,寻找最优策略。

价值迭代

让我们看看策略迭代,并尝试评估我们需要多少遍才能找到最优策略。策略迭代有两个循环步骤。第一个是策略评估,它针对当前策略运行,并需要多次通过状态空间,以便状态值收敛并变得与当前策略一致。循环的第二部分是策略改进,这需要在状态空间上一遍,以找到每个状态的最佳动作,即,相对于当前状态动作值的贪婪改进。由此可见,很大一部分时间花在了政策评估和让价值观趋同上。

另一种方法是截断策略评估中的循环。当我们将策略评估中的循环截短为只有一个循环时,我们有一种称为值迭代的方法。类似于方程( 3.6 )的方法,我们采用贝尔曼最优方程( 3.3 )作为状态值,并通过迭代将其转化为分配。修正后的方程式如下:

)

(3.8)

随着我们的迭代,状态值将不断提高,并将收敛到 v*,这是最优值。

)

一旦值收敛到最佳状态值,我们可以使用一步备份图来找到最佳策略。

)

(3.9)

之前迭代每一步取最大值的过程称为值迭代。图 3-8 显示了伪代码。

VALUE ITERATION

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-8

有限 MDP 的值迭代算法

让我们将前值迭代算法应用于图 3-1 中给出的网格世界。清单 3-4 包含应用于网格世界的值迭代代码。您可以查看文件listing3_4.ipynb了解详细的实现。函数value_iteration是图 3-8 中伪代码的直接实现。

# Value Iteration
def value_iteration(env, discount_factor=1.0, theta=0.00001):
    """
    Varry out Value iteration given an environment and a full description
    of the environment's dynamics

.

    Args:
        env: OpenAI env. env.P -> transition dynamics of the environment.
            env.P[s][a] [(prob, next_state, reward, done)].
            env.nS is number of states in the environment.
            env.nA is number of actions in the environment.
        discount_factor: Gamma discount factor.
        theta: tolernace level to stop the iterations

    Returns:
        policy: [S, A] shaped matrix representing optimal policy.
        value : [S] length vector representing optimal value
    """

    def argmax_a(arr):
        """
        Return idx of max element in an array.
        """
        max_idx = []
        max_val = float('-inf')
        for idx, elem in enumerate(arr):
            if elem == max_val:
                max_idx.append(idx)
            elif elem > max_val:
                max_idx = [idx]
                max_val = elem
        return max_idx

    optimal_policy = np.zeros([env.nS, env.nA])
    V = np.zeros(env.nS)
    V_new = np.copy(V)

    while True:
        delta = 0
        # For each state, perform a "greedy backup"
        for s in range(env.nS):
            q = np.zeros(env.nA)
            # Look at the possible next actions
            for a in range(env.nA):
                # For each action, look at the possible next states
                # to calculate q[s,a]
                for prob, next_state, reward, done in env.P[s][a]:

                    # Calculate the value for each action as per backup diagram
                    if not done:
                        q[a] += prob * (reward + discount_factor * V[next_state])
                    else:
                        q[a] += prob * reward

            # find the maximum value over all possible actions
            # and store updated state value
            V_new[s] = q.max()
            # How much our value function changed (across any states)
            delta = max(delta, np.abs(V_new[s] - V[s]))

        V = np.copy(V_new)

        # Stop if change is below a threshold
        if delta < theta

:
            break

    # V(s) has optimal values. Use these values and one step backup
    # to calculate optimal policy
    for s in range(env.nS):
        q = np.zeros(env.nA)
        # Look at the possible next actions
        for a in range(env.nA):
            # For each action, look at the possible next states
            # and calculate q[s,a]
            for prob, next_state, reward, done in env.P[s][a]:

                # Calculate the value for each action as per backup diagram
                if not done:
                    q[a] += prob * (reward + discount_factor * V[next_state])
                else:
                    q[a] += prob * reward

        # find the optimal actions
        # We are returning stochastic policy which will assign equal
        # probability to all those actions which are equal to maximum value
        best_actions = argmax_a(q)
        optimal_policy[s, best_actions] = 1.0 / len(best_actions)

    return optimal_policy, V

Listing 3-4Value Iteration: listing3_4.ipynb

对网格世界运行价值算法的输出将产生价值和策略完全相似的最优状态值v∫(s)和最优策略),如图 3-7 和 3-8 所示。

在继续之前,我们先总结一下。我们到目前为止所看到的被归类为同步动态编程算法,如表 3-1 中所总结的。

表 3-1

同步动态规划算法

|

算法

|

贝尔曼方程

|

问题类型

|
| — | — | — |
| 迭代策略评估 | 期望方程 | 预报 |
| 策略迭代 | 期望方程和贪婪改进 | 控制 |
| 价值迭代 | 最优性方程 | 控制 |

广义策略迭代

前面描述的策略迭代有两个步骤:策略评估,它使状态值与代理正在遵循的策略保持同步,需要多次遍历所有状态以使值收敛到 v π ,以及贪婪动作选择以改进策略。正如所解释的,改进的第二步导致值的当前状态与新策略不同步。因此,我们需要执行另一轮策略评估,以使状态值重新与新策略同步。当状态值没有进一步变化时,评估和改进的循环停止。当代理达到最佳策略且状态值也是最佳的并且与最佳策略同步时,就会发生这种情况。收敛到最优策略(不动点)v π 可以直观的描绘出来,如图 3-9 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-9

两步之间的迭代。第一步是评估,使状态值与遵循的策略同步。第二步是政策改进,对行动进行贪婪的最大化

我们已经看到在策略评估步骤中迭代的循环数有两种极端情况。此外,每次迭代,无论是策略评估还是策略改进,都覆盖了模型中的所有状态。然而,即使在一次迭代中,我们也只能通过贪婪选择访问部分状态集来评估和/或改进状态操作。策略改进定理保证了即使所有状态的部分覆盖也会导致改进,除非代理已经在遵循最优策略。换句话说,状态值同步不需要完成。可能会中途终止,导致图 3-9 中的箭头未触及v=vπ的底线而停止。同样,策略改进步骤可能不会对所有状态进行改进,这再次导致图 3-9 中的箭头在π = 贪婪 ( v )的上线处停止。

总之,只要每个状态在评估和改进中被访问足够的次数,策略评估和策略改进的这两个步骤以及它们在评估步骤中扫描多少个状态或在多少次迭代之后停止的所有变化都会导致收敛。这被称为广义策略迭代 (GPI)。我们将要研究的大多数算法都可以归类为某种形式的 GPI。当我们通过各种算法时,请记住图 3-10 中给出的图片。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-10

两步之间的迭代。第一步是评估,使状态值与遵循的策略同步。第二步是政策改进,对行动进行贪婪的最大化

异步备份

基于动态编程的算法存在可扩展性问题。动态规划方法比直接求解方法(如线性规划,涉及求解矩阵方程)好得多,并且可扩展。然而,动态编程仍然不能很好地适应现实生活中的问题。考虑策略迭代下的单次扫描。它需要访问每个州,在每个州下,你需要考虑所有可能的行动。此外,每个动作涉及一个计算,该计算理论上可能再次涉及所有状态,这取决于状态转移函数p(sr | sa )。换句话说,每次迭代的复杂度为O(|A|∫|s|2)。我们从策略迭代开始,在策略迭代下,我们执行多次迭代,作为状态值收敛的评估步骤的一部分。我们研究的第二种控制方法是值迭代。通过利用贝尔曼最优方程,我们将评估迭代减少到只有一步。所有这些都是同步动态规划算法,在这些算法下,使用贝尔曼备份方程( 3.1 )到( 3.4 )来更新所有状态。

然而,不需要在每次迭代中更新每个状态。我们可以以任何顺序更新和/或优化,仅覆盖系统中全部状态的一部分。只要每个状态被足够频繁地访问,所有这些扫描状态的方法都会产生最优的状态值和最优的策略。清扫有多种方法。

第一种是原地动态编程。到目前为止,我们一直维护着状态的两个副本。第一个副本保存现有的状态值,第二个副本保存正在更新的新状态值。一个就地策略只使用状态值数组的一个副本。同一数组用于读取旧的状态值和更新新的状态值。作为一个例子,让我们看看来自( 3.8 )的值迭代方程。请注意原始值迭代等式左右两侧状态值的子指标与就地版本相比的细微差异。原版本使用数组 V k 更新一个新数组Vk+1,而原地编辑更新的是同一个数组。

以下是原文:

)

这是就地更新:箭头两边都是相同的 v(s)数组。

)

实验表明,即使在迭代中途,当值向上移动时,就地编辑也能提供更快的收敛。

第二个想法围绕着状态更新的顺序。在同步编程中,我们在一次迭代中更新所有的状态。然而,如果我们使用优先扫描,值可能收敛得更快。优先扫描需要了解状态的前任。假设我们刚刚更新了状态 S = s ,并且值改变了δ。状态 S = s 的所有前趋状态被添加到优先级为δ的优先级队列中。如果一个前趋状态已经在优先级队列中,其优先级大于δ,则它保持不变。在下一次迭代中,一个具有最高优先级的新状态被从队列中取出并更新,将我们带回循环的开始。优先级扫描的策略需要反向动态的知识,即给定状态的前身。

第三个思路是实时动态编程。使用这种方法,我们只更新代理当前看到的状态的值,即与代理相关的状态,并使用其当前的探索路径来区分更新的优先级。这种方法避免了对不在代理当前路径范围内的状态进行无用的更新,因此这些状态大多是不相关的。

动态编程,无论是同步还是异步,都使用全宽度备份,如图 3-2 所示。对于一个给定的状态,我们需要知道每个可能的动作和每个后继状态 S = s 。我们还需要了解环境动态 p ( s r | sa )。然而,使用异步方法并不能完全解决可伸缩性问题。它只是稍微扩展了可伸缩性。换句话说,即使是异步更新,动态编程也只适用于中型问题。

从下一章开始,我们将考虑使用基于样本的方法来解决强化学习问题的更具可扩展性的方法。在基于样本的方法中,我们不了解环境动态,也不进行全宽度扫描。

摘要

在这一章中,我们介绍了动态规划的概念以及它是如何应用于强化学习领域的。我们着眼于预测的策略评估,然后是控制的策略迭代。然后我们看了价值迭代。这些讨论导致了广义的政策迭代。本章最后快速回顾了异步变体,以获得更有效的状态更新方法。

四、无模型方法

在前一章中,我们看了动态编程,其中我们知道模型动态p(sr | sa ),并且这些知识被用于“计划”最优动作。这也被称为计划问题。在这一章中,我们将转移我们的焦点,看看学习问题,即模型动态未知的设置。我们将通过抽样来学习价值和行动-价值函数,即通过遵循现实世界中的一些策略或通过模拟中的策略运行代理来收集经验。在另一类问题中,我们发现无模型方法更适用。在一些问题中,采样比计算转移动态更容易,例如,考虑解决寻找玩像 21 点这样的游戏的最佳策略的问题。有许多组合来达到一个分数,这取决于到目前为止看到的卡和仍然在甲板上的卡。几乎不可能计算从一个状态到另一个状态的精确转移概率,但是很容易从环境中采样状态。总之,当我们不知道模型动态或我们知道模型时,我们使用无模型方法,但是采样比计算转变动态更实际。

在这一章中,我们将研究两大类无模型学习:蒙特卡罗(MC)方法和时间差分(TD)方法。我们将首先理解策略评估在无模型设置中如何工作,然后扩展我们的理解来看控制,即找到最优策略。我们也将触及自举的重要概念和探索与开发的困境,以及政策外与政策内的学习。最初,重点将是分别研究 MC 和 TD 方法,之后我们将研究其他概念,如 n 步回报、重要性抽样和合格跟踪,以将 MC 和 TD 方法结合成一个通用的、更通用的方法,称为 TD(λ)。

蒙特卡罗估计/预测

当我们不知道模型动力学时,我们做什么?回想一下你对某个问题一无所知的情况。在那种情况下你做了什么?你尝试,采取一些步骤,并找出情况如何反应。例如,假设你想找出一个骰子或硬币是否有偏差。你多次投掷硬币或骰子,观察结果,并以此来形成你的观点。换句话说,你取样。统计学中的大数定律告诉我们,样本的平均值可以很好地替代平均值。此外,随着样本数量的增加,这些平均值会变得更好。如果你回顾上一章的贝尔曼方程,你会注意到在那些方程中我们有期望算子 E[];例如一个状态的值为v(S)=E**Gt|St=S。此外,为了计算 v ( s ),我们使用了需要转换动态p(s'r | sa )的动态编程。在缺乏模型动力学知识的情况下,我们该怎么办?我们只是从模型中取样,观察从状态 S = s 开始直到剧集结束的返回。然后,我们对所有剧集运行的回报进行平均,并使用该平均值作为代理遵循的策略π的估计值vπ(s)。简而言之,这就是蒙特卡罗方法的方法:用样本回报的平均值代替预期回报。

有几点需要注意。MC 方法不需要模型知识。唯一需要的是,我们应该能够从中取样。我们需要知道从一个状态开始直到终止的回报,因此我们只能在每一次运行最终终止的间断 MDP 上使用 MC 方法。它不能在非终止环境中工作。第二点是,对于一个大的 MDP,我们可以把重点放在只对 MDP 的相关部分进行采样,而避免探索 MDP 的不相关部分。这种方法使得 MC 方法对于非常大的问题具有高度的可扩展性。一种叫做蒙特卡罗树搜索 (MCTS)的 MC 方法的变体被 OpenAI 用于训练一个围棋游戏代理。

第三点是关于马尔可夫假设;即过去完全编码在当前状态中。换句话说,了解现在使未来独立于过去。我们在第 [2 章讨论了这个属性。马尔可夫独立性的这一特性形成了贝尔曼方程的递归性质的基础,其中一个状态仅仅依赖于后继状态值。然而,在 MC 方法下,我们观察从状态 S 开始直到终止的全部回报。我们不依赖于后续状态的值来计算当前状态值。这里没有马尔可夫性质的假设。MC 方法中缺少马尔可夫假设,这使得它们对于被称为 POMDPs(对于“部分可观测 MDP”)的 MDPs 类更加可行。在 POMDP 环境中,我们只获得部分状态信息,这被称为观察

接下来,让我们看看一种估计给定策略的状态值的正式方法。我们让代理开始新的一集,并观察从时间代理的返回在该集中第一次处于状态 S = s ,直到该集结束。进行许多集运行,并取跨集返回的平均值作为对vπ(S=S)的估计。这就是所谓的首诊 MC 法。请注意,根据动态,代理可能会在终止前的同一情节中的某个后续步骤中访问同一状态 S = s 。然而,在首次访问 MC 方法中,我们仅从一集的首次访问到该集的结束获取总回报。还有另一个变体,我们取每次访问该州的平均回报,直到该集结束。这就是所谓的每次拜访司仪法

图 4-1 显示了 MC 方法的备份图。在 DP 中,我们进行了一次完整的滑动,以涵盖从一个状态到一个新状态的所有可能的动作,以及从状态-动作对( S = sA = a )到一个新状态的所有可能的转换。我们只从状态 S = sA = a 再到下一个状态S=S'再深入一层。与此相比,在 MC 方法中,备份覆盖了从当前状态 S = s 到终止状态的完整样本轨迹。它没有涵盖所有的分支可能性;相反,它只覆盖了从起始状态 S = s 到终止状态的一条采样路径。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-1

与基于贝尔曼方程的 DP 备份相比,MC 方法的备份图

现在让我们看看初诊版本的伪代码,如图 4-2 所示。我们输入代理当前遵循的策略 π 。我们初始化两个数组:一个保存当前对 V ( s )的估计,第二个保存对状态 S = s 的访问次数 N ( s )。执行多集,每集更新 V ( s )和 N ( s )的国家特工访问,在“首次访问版本”中只更新首次访问,在“每次访问版本”中更新每次访问伪代码仅涵盖了“首次访问变体”“每次访问”变体很容易实现,只需删除条件“除非 St出现在 S 0 ,S 1 ,… ”根据大数定律,蒙特卡洛模拟所基于的统计定律,当对每个状态的访问次数趋于无穷大时, V ( s )将收敛为真Vπ(s)。

First Visit MC Prediction

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-2

初访 MC 预测估算vπ(s)。伪代码使用在线版本在收到样本时更新值

我们存储累计总数和第一次访问一个州的次数。平均值的计算方法是将总数除以计数。每次访问一个状态,进行如下更新:

)

稍作改动,我们可以从另一个角度来看这个更新,而不需要累计总数 S ( s )。在这个替代公式中,我们将有一个数组 V ( s ),它直接为每次访问更新,而不需要将总数除以计数。这个更新规则的推导如下:

)

)

)

)

(4.1)

差值[GV(s)n]可视为最新采样值 G 与当前估计值V(s)n之间的误差。然后,通过将 1/N误差加到当前估计值,该差值/误差被用于更新当前估计值。随着访问次数的增加,新样本对修正 V ( s )估计值的影响越来越小。这是因为随着 N 变得非常大,乘法因子 1/ N 减小到零。

有时候,我们可以不用一个递减因子 1/ N ,而是在差值前面用一个常数α作为倍增因子[GV(s)N]。

)

(4.2)

常数乘数法更适合于非平稳的问题,或者当我们希望给所有误差一个常数权重时。当旧的估计值 V n 可能不太准确时,可能会发生这种情况。

现在让我们看看 MC 值预测的实现。清单 4-1 显示了代码,完整的代码在文件listing4_1.ipynb中。

def mc_policy_eval(policy, env, discount_factor=1.0, episode_count=100):

    # Start with (all 0) state value array and a visit count of zero
    V = np.zeros(env.nS)
    N = np.zeros(env.nS)
    i = 0

    # run multiple episodes
    while i < episode_count:

        #collect samples for one episode
        episode_states = []
        episode_returns = []
        state = env.reset()
        episode_states.append(state)
        while True:
            action = np.random.choice(env.nA, p=policy[state])
            (state, reward, done, _) = env.step(action)
            episode_returns.append(reward)
            if not done:
                episode_states.append(state)
            else:
                break

        #update state values
        G = 0
        count = len(episode_states)
        for t in range(count-1, -1, -1):
            s, r = episode_states[t], episode_returns[t]
            G = discount_factor * G + r
            if s not in episode_states[:t]:
                N[s] += 1
                V[s] = V[s] + 1/N[s] * (G-V[s])

        i = i+1

    return np.array(V)

Listing 4-1MC Value Prediction Algorithm for Estimation

清单 4-1 中的代码是图 4-2 中伪代码的直接实现。代码实现在线版本的更新,如等式( 4.1 所解释的,即 N[s]+= 1;V[s]=V[s]+1/N[s]*(G-V[s])。该代码还实现了“第一次访问”版本,但可以通过一个非常小的调整转换为“每次访问”。我们只需要去掉“如果”检查,即“如果不在集状态[:t]”来执行每一步的更新。

为了确保收敛到真实的状态值,我们需要确保每个状态被访问足够的次数,在极限中是无限的。如 Python 笔记本末尾的结果所示,状态值在 100 集内没有很好地收敛。然而,对于 10,000 集,这些值已经很好地收敛,并且与列表 3-2 中给出的 DP 方法产生的值相匹配。

mc 预测方法的偏差和方差

现在让我们来看看“首次就诊”与“每次就诊”的利弊两者是否都收敛到真正的底层 V ( s )?他们收敛的时候波动大吗?一个人是否更快地收敛到真正的价值?在我们回答这个问题之前,让我们先回顾一下在所有统计模型估计中看到的偏差-方差权衡的基本概念,例如,在监督学习中。

偏差是指模型收敛到我们试图估计的真实潜在价值的性质,在我们的例子中是vπ(s)。一些估计值是有偏差的,这意味着由于其固有的缺乏灵活性,即对于给定的真实模型来说过于简单或受限制,它们不能收敛到真实值。同时,在其他一些情况下,随着样本数量的增加,模型的偏差会下降到零。

方差指的是对所使用的特定样本数据敏感的模型估计。这意味着估计值可能波动很大,因此可能需要大量的数据集或试验来使估计平均值收敛到一个稳定的值。

这些模型非常灵活,具有较低的偏差,因为它们能够使模型适应数据集的任何配置。同时,由于灵活性,他们可以过度适应数据,使得估计随着训练数据的变化而变化很大。另一方面,模型越简单,偏差越大。由于固有的简单性和局限性,这样的模型可能无法代表真正的底层模型。但是它们也将具有低方差,因为它们不会过拟合。这就是所谓的偏差-方差权衡,可以用图 4-3 来表示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-3

偏差方差权衡。模型复杂度在 x 轴上向右增加。当模型受到限制时,偏差从高开始,随着模型变得灵活,偏差下降。方差显示了与模型复杂性相反的趋势

将“首次就诊”与“每次就诊”进行比较,首次就诊是无偏的,但具有较高的方差。每次访问都有偏差,随着试验次数的增加,偏差下降到零。此外,每次访问都具有低方差,并且通常比第一次访问更快地收敛到真实值估计。

蒙特卡罗控制

现在让我们来谈谈无模型设置中的控制。我们需要在不知道模型动态的情况下找到这个设置中的最优策略。作为复习,让我们看看在第三章中介绍的通用策略迭代(GPI)。在 GPI 中,我们在两个步骤之间迭代。第一步是找到给定策略的状态值,第二步是使用贪婪优化来改进策略。对于 MC 下的控制,我们将遵循相同的 GPI 方法。不过,我们会做一些调整,以考虑到我们处于无模型世界中,无法访问/了解转换动态的事实。

在第三章中,我们看了状态值, v(s) 。然而,在没有过渡动态的情况下,仅有状态值是不够的。对于贪婪改进步骤,我们需要访问动作值, q ( sa )。我们需要知道所有可能动作的 q 值,即在状态 S = s 下所有可能动作 a 的所有q(S=Sa )。只有有了这些信息,我们才能应用贪婪最大化来选择最佳行动,即 arg maxaq(s, a )

当与 DP 相比时,我们还有另一个复杂因素*。代理在生成样本的时间遵循一个策略。然而,这样的策略可能导致许多状态-动作对从不被访问,如果该策略是确定性的,则更是如此。如果代理不访问状态-动作对,它不知道给定状态*、的所有 q ( sa ),因此它不能找到产生动作的最大 q 值。解决该问题的一种方法是通过探索开始来确保足够的探索,从而确保代理从随机的状态-动作对开始一集,并且在许多集的过程中覆盖每个状态-动作对足够的时间 s,事实上,无限长的时间。**

4-4 显示了 GPI 图随着v-值到q-值的变化。现在的评估步骤是 MC p 预测步骤,该步骤在前面的部分中介绍。一旦q-值稳定下来,就可以应用贪婪最大化来获得新的策略。政策与 ?? 改进定理确保新政策会更好,或者至少和旧政策一样好。GPI 之前的方法将成为一个反复出现的主题。根据设置,评估步骤将会改变,而改进步骤将始终保持贪婪最大化。****

*外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-4

两步之间的迭代。第一步是评估,以使状态-动作值与所遵循的策略同步。第二步是政策改进,对行动进行贪婪的最大化

“探索开始”的假设不太实际,效率也不高。在许多场景中,这是不实际的,因为代理不能选择开始条件,例如,在训练自动驾驶汽车时。这是没有效率的,因为它可能是不可行的,并且对于代理来说,无限(有限)次访问每个状态-动作对也是浪费。我们仍然需要继续探索,让代理访问当前策略所访问的州中的所有动作。这是通过使用ε-贪婪策略实现的。**

在ε-贪婪策略中,代理以最大 q 值概率 1-ε采取行动,并以概率ε/|A|随机采取任何行动。换句话说,ε的剩余概率在所有动作中平均分配,以确保智能体继续探索非最大化动作。换句话说,智能体以概率 1-ε利用的知识,它以概率ε探索

)

(4.3)

作为随机探索的一部分,从 greedy max 和ε/|A|中以概率 1-ε挑选具有最大 q 值的动作。所有其他动作以概率ε/|A|被拾取。当代理在多个情节中学习时,ε的可以慢慢减小到零,以达到最优贪婪策略。

让我们对迭代的估计/预测步骤再做一次改进。在上一节中,我们看到,即使对于一个简单的 4×4 网格 MDP,我们也需要 10,000 集的数量级才能使值收敛。参见图 3-9 ,该图显示了 GPI 的收敛:MC 预测步骤将v-值或 q- 值与当前策略同步。然而,在前一章中,我们也看到了代理人没有必要一路走到价值观的趋同。类似地,我们可以运行 MC 预测,然后逐集进行政策改进。这种方法将消除在估计/预测步骤中大量迭代的需要,从而使该方法可扩展用于大的 MDP。与图 3-9 中的收敛图相比,这种方法将产生如图 4-5 所示的收敛。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-5

两步之间的迭代。第一步是针对单个步骤的 MC 预测/评估,以在当前策略的方向上移动 q 值。第二步是政策改进,对行动进行ε-贪婪最大化

结合所有先前的调整,我们有了一个基于蒙特卡罗的最优策略学习控制的实用算法。这在《极限与无限探索》(GLIE)中被称为贪婪。我们将在 q 值预测步骤中使用每次访问版本。但是它需要一个小的调整,就像图 4-6 中 MC 预测中的那个一样,使它成为第一次访问的变体。

GLIE For Policy Optimization

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-6

用于策略优化的每次访问(GLIE) MC 控制

现在让我们看看listing4_2.ipynb中给出的实际实现。清单 4-2 转载了代码的相关部分。实现遵循伪代码。我们有一些像argmax_a()这样的额外函数,帮助我们找到给定状态下max Q(s,a)的动作。另一个函数get_action(state)返回当前策略的ε-贪婪操作。我们鼓励您进行调整,并将其转换为“首次访问”版本。您可能还想比较“首次就诊”与“每次就诊”MC 对照的结果。

def GLIE(env, discount_factor=1.0, episode_count=100):
    """
    Find optimal policy given an environment.
    Returns:
        Vector of length env.nS representing the value function.
        policy: [S, A] shaped matrix representing the policy. Random in our case

    """
    # Start with (all 0) state value array and state-action matrix.
    # also initialize visit count to zero for the state-action visit count.
    V = np.zeros(env.nS)
    N = np.zeros((env.nS, env.nA))
    Q = np.zeros((env.nS, env.nA))
    #random policy
    policy = [np.random.randint(env.nA) for _ in range(env.nS)]
    k = 1
    eps = 1

    def argmax_a(arr):
        """
        Return idx of max element in an array.
        Break ties uniformly.
        """
        max_idx = []
        max_val = float('-inf')
        for idx, elem in enumerate(arr):
            if elem == max_val:
                max_idx.append(idx)
            elif elem > max_val:
                max_idx = [idx]
                max_val = elem
        return np.random.choice(max_idx)

    def get_action(state):
        if np.random.random() < eps:
            return np.random.choice(env.nA)
        else:
            return argmax_a(Q[state])

    # run multiple episodes
    while k <= episode_count:

        #collect samples for one episode
        episode_states = []
        episode_actions = []
        episode_returns = []
        state = env.reset()
        episode_states.append(state)
        while True:
            action = get_action(state)
            episode_actions.append(action)
            (state, reward, done, _) = env.step(action)
            episode_returns.append(reward)
            if not done:
                episode_states.append(state)
            else:
                break

        #update state-action values
        G = 0
        count = len(episode_states)
        for t in range(count-1, -1, -1):
            s, a, r = episode_states[t], episode_actions[t], episode_returns[t]
            G = discount_factor * G + r
            N[s, a] += 1
            Q[s, a] = Q[s, a] + 1/N[s, a] * (G-Q[s, a])

        #Update policy and optimal value
        k = k+1
        eps = 1/k
        #uncomment this to have higher exploration initially
        #and then let epsilon decay after 5000 episodes
        #if k <=100:
        #    eps = 0.02

        for s in range(env.nS):
            best_action = argmax_a(Q[s])
            policy[s] = best_action
            V[s] = Q[s,best_action]

    return np.array(V), np.array(policy)

Listing 4-2GLIE MC Control Algorithm

到目前为止,本章我们已经研究了预测和控制的策略算法。换句话说,相同的策略用于生成样本以及策略改进。策略改进是针对相同ε-贪婪策略的 q 值进行的。我们需要探索为所有的状态-行为对找到 Q ( sa ),这是通过使用ε-贪婪策略来实现的。然而,随着我们的进展,带有限制的ε-贪婪策略是次优的。随着我们对环境的了解越来越多,我们需要开发更多。我们还看到,虽然理论上策略会收敛到最优策略,但有时需要仔细控制ε值。MC 方法的另一个大缺点是,我们需要在更新 q 值和执行策略优化之前完成剧集。因此,MC 方法只能应用于情节性的和情节结束的环境。从下一节开始,我们将开始研究一种不同的算法,称为时间差分方法,它结合了 DP(单时间步长更新)和 MC(即不需要了解系统动态)的优点。然而,在我们深入研究 TD 方法之前,让我们先来看一小段介绍,然后比较策略上学习和策略外学习。

不符合政策的 MC 控制

在 GLIE 中,我们看到,为了进行足够的探索,我们需要使用ε-贪婪策略,以便在有限的范围内足够频繁地访问所有状态动作。在循环结束时学习的策略被用于为循环的下一次迭代生成情节。我们正在使用与被最大化的策略相同的策略进行探索。这种方法被称为 on-policy ,从正在优化的同一策略中生成样本。

还有另一种方法,其中使用具有较高ε的更具探索性的策略来生成样本,而被优化的策略可能具有较低的ε,甚至可能是完全确定性的策略。这种使用与优化策略不同的策略进行学习的方法被称为偏离策略学习。用于生成样本的策略被称为行为策略,被学习(最大化)的策略被称为目标策略。让我们看看图 4-7 中的非策略 MC 控制算法的伪代码。

Off Policy MC Control Optimization

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-7

用于策略优化的非策略 MC 控制

时间差异学习方法

参考图 4-1 研究 DP 和 MC 方法的备份图。在 DP 中,我们使用来自后继状态的值来估计当前状态值,仅备份一步的值。我们还根据所遵循的政策,从( sa )对到所有可能的奖励和后继状态,对行动概率进行了预期。

)

一个状态的值vπ(s)是根据后继状态的当前估计值vπ(s)来估计的。这被称为自举。该估计基于另一组估计。这两个总和就是图 4-1 中 DP 备份图中表示为分支节点的总和。与 DP 相比,MC 基于从一个状态开始,并基于代理遵循的当前策略对结果进行采样。估计值是多次运行的平均值*。换句话说,模型转移概率的总和被平均值代替,因此 MC 的备份图是从一个状态到最终状态的单一长路径。MC 方法允许我们建立一个可扩展的学习方法,同时不需要知道确切的模型动态。然而,它产生了两个问题:MC 方法仅适用于情节环境,并且更新仅在情节结束时发生。DP 的优势在于使用对后续状态的估计来更新当前状态值,而无需等待情节结束。*

*时间差异学习是一种结合了动态规划和蒙特卡罗的优点的方法,使用了动态规划的自举和蒙特卡罗的基于样本的方法。TD 的更新方程式如下:

)

(4.4)

状态 S = s ,即 G t 的总回报的当前估计,现在是通过从样本运行中所示的后继状态的当前估计(S)中引导给出的。换句话说,方程( 4.2 )中的 G t 换成R+γV(s),一个估计。与此相比,在 MC 方法中, G t 是样本运行的贴现总回报。

所描述的 TD 方法被称为 TD(0),并且它是一步估计。当我们在本章末尾讨论 TD(λ)时,称之为 TD(0)的原因将变得更加清楚。为了清楚起见,让我们看看图 4-8 中给出的使用 TD(0)进行值估计的伪代码。

TD(0) For Estimation

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-8

TD(0)策略估计

已经看到了所有三种方法,DP、MC 和 TD,让我们把所有三种方法的备份图放在一起,如图 4-9 所示。TD(0)类似于 DP,因为它使用自举,自举是对下一个状态值的估计,以估计当前状态值。TD(0)类似于 MC,因为它对情节进行采样,并使用观察到的回报和下一个状态来更新估计。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-9

比较 DP、MC 和 TD(0)的备用图

现在我们来介绍另一个你很可能在强化学习文献中反复看到的量。在等式( 4.4 )中,量[R+γV(S)]是从状态 S = s 的总回报的修正估计,并且它基于从后继状态的一步备份。此外,在等式( 4.4 )中,赋值右侧的 V ( s )是当前估计值。更新是将估计值从 V ( s )移动到V(s)+α(),其中差 δ t 由以下表达式给出:

)

(4.5)

差值δt称为TD误差。它代表基于奖励Rt+1加上贴现的下一时间步状态值V(St+1)的估计值 V ( s )和当前估计值V(S的误差(差)该状态的值被移动该误差 δ t 的一个比例(学习率、步进率 α )。

TD 比 DP 有优势,因为它不需要模型知识(转移函数)。TD 方法优于 MC,因为 TD 方法可以在每一步更新状态值;也就是说,它们可以用于在线设置,通过在线设置,我们可以随着情况的发展而学习,而不必等到一集结束。

时间差异控制

本节将开始带你进入 RL 世界中使用的真实算法的领域。在这一章的剩余部分,我们将看看在 TD 学习中使用的各种方法。我们将从一个简单的一步到位的策略学习方法开始,称为 SARSA 。接下来是一种叫做 Q-learning 的强大的策略外技术。在本章中,我们将研究 Q 学习的一些基础方面,在下一章中,我们将把深度学习与 Q 学习相结合,给我们提供一种称为深度 Q 网络(DQN)的强大方法。使用 DQN,你将能够在雅达利模拟器上训练游戏代理。在这一章中,我们还将讨论 Q-learning 的一个变种,叫做预期 SARSA ,另一种非策略学习算法。然后,我们将讨论 Q 学习中最大化偏差的问题,带我们到双 Q 学习。当与深度学习结合来表示状态空间时,Q 学习的所有变体都变得非常强大,这将构成下一章的主要内容。在本章的末尾,我们将介绍其他概念,如经验回放,它使非学习算法在学习最优策略所需的样本数量方面更有效。然后我们将讨论一个强大的、有点复杂的方法,叫做 TD( λ ),它试图在一个连续体上结合 MC 和 TD 方法。最后,我们将看看一个具有连续状态空间的环境,以及如何将状态值二进制化并应用前面提到的 TD 方法。这个练习将展示我们将在下一章讨论的方法的必要性,包括状态表示的函数近似和深度学习。在关于深度学习和 DQN 的第 5 和 6 章之后,我们将展示另一种称为策略优化的方法,该方法围绕直接学习策略,而不需要找到最佳状态/动作值。

到目前为止,我们一直使用 4×4 网格世界。现在,我们将在本章的剩余部分看看更多的环境。我们将以封装的方式编写代理,这样相同的代理/算法无需任何更改就可以应用于各种环境。

我们将使用的第一个环境是网格世界的变体;这是健身房图书馆的一部分,叫做悬崖漫步环境。在这个环境中,我们有一个 4×12 的网格世界,左下角的单元格是起始状态 S ,右下角的状态是目标状态 G 。底排的其余部分形成悬崖;踩上去会获得-100 的奖励,代理人再次回到开始状态。每走一步获得-1 的奖励,直到代理达到目标状态。类似于 4×4 网格世界,代理可以向任何方向(上、右、下、左)走一步。当代理达到目标状态时,情节终止。图 4-10 描述了该设置。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-10

4×10 悬崖世界。踩悬崖有-100 的奖励,其他转场有-1 的奖励。目标是从 S 开始,到达 G 并获得尽可能多的奖励

我们要看的下一个环境是“出租车问题”有四个标记为 R、G、Y 和 b 的位置。当一集开始时,一名乘客随机站在这四个位置中的一个。随机选择四个方块中的一个作为目的地。出租车有 25 个可能的位置。乘客有五个位置:起始位置或内部出租车。有四个目的地。所有这些组合将状态空间的可能值作为 500 种不同的组合。状态值被表示为一个元组(taxi_row, taxi_col, passenger_location, destination)。有六种行动可以果断地采取。出租车可以移动北、南、西、东,然后有客两个动作。奖励为每个时间步长-1,成功取货/卸货+20,从错误地点取货/卸货-10。当乘客被成功接送时,这一集就结束了。图 4-11 所示为环境示意图。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-11

5×5 出租车问题。所有转换都有-1 的奖励。当乘客在目的地下车时,一集结束

我们现在来看看被称为CartPole的第三种环境。我们在第二章中讨论了这个环境。在这种状态下,空间是一个连续的空间,包括四个观察:[Cart position, Car velocity, Pole angle, pole angular velocity]。代理有两个动作:向左推车向右推车,即两个离散动作。每个时间步长的回报都是+1,代理希望通过在最长的时间间隔内保持极点平衡来最大化回报。一旦杆角度在任一方向上超过 12 度,或者推车位置偏离中心超过 2.4 度,即< -2.4 度或> 2.4 度,或者如果代理能够平衡杆 200 个时间步,该情节终止。图 4-12 显示了实际环境的快照。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-12

推车杆子问题。目标是在最大数量的时间步长内垂直平衡极点,每个时间步长奖励+1

解释了各种环境之后,现在让我们开始用 TD 算法解决这些问题,首先从 SARSA 开始,这是一种基于策略的方法。

保单 SARSA

像 MC 控制方法一样,我们将再次利用 GPI。我们将使用 TD 驱动的方法进行政策价值评估/预测步骤,并将继续使用贪婪最大化方法进行政策改进。就像 MC 一样,我们需要探索足够多的地方,并访问所有的州无数次,以找到一个最佳策略。类似于 MC 方法,我们可以使用ε-贪婪策略,并慢慢地将ε值降低到零,即,对于将探索降低到零的限制。

TD 设置是无模型的;也就是说,我们事先没有关于转变的全面知识。同时,为了能够通过选择正确的行动来最大化回报,我们需要知道状态行动值 Q ( SA )。我们可以将等式( 4.4 )中的 TD 估计重新表述为( 4.6 )中的 TD 估计,本质上是用 Q ( sa )替换 V ( s )。这两种设置都是马尔可夫过程,方程( 4.4 )关注状态到状态的转换,现在关注的焦点是状态-动作到状态-动作。

)

(4.6)

与等式( 4.5 )类似,TD 误差现在以 q 值的形式给出。

)

(4.7)

为了根据等式( 4.6 )进行更新,我们需要所有五个值StA tRt+1St+1A 这就是这种方法被称为 SARSA(状态、行动、奖励、状态、行动)的原因。我们将遵循ε-greedy 策略生成样本,使用( 4.6 )更新 q 值,然后基于更新的 q 值创建新的ε-greedy。政策改进定理保证新政策将优于旧政策,除非旧政策已经是最优的。当然,为了保证成立,我们需要将极限中的探索概率ε降低到零。

还请注意,对于所有的阶段性策略,终端状态有 Q ( SA )等于零;即,一旦处于终止状态,此人不能转移到任何地方,并将继续获得零奖励。这是情节结束的另一种说法,并且对于所有终端状态, Q ( SA )为零。因此,当St+1为终态时,方程( 4.6 )会有Q(St+1At+1)= 0,更新方程会是这样的:【1

现在让我们看看 SARSA 算法的伪代码;参见图 4-13 。

SARSA On-Policy TD Control

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-13

SARSA,保单 TD 控制

现在让我们浏览一下来自listing4_3.ipynb的代码,它实现了 SARSA。代码中有一个名为SARSAAgent的类,它实现了 SARSA 学习代理。它有两个关键函数:update(),取值为stateactionrewardnext_statenext_action以及done标志。它使用 TD 更新公式( 4.6 和( 4.8 )更新 q 值。另一个函数是get_action(state),以概率ε返回一个随机动作,以概率 1-ε返回argmax Q(S,A)。在类之外有一个通用函数train_agent(),它在给定的环境中训练代理。有两个辅助函数:plot_rewards()随着训练的进行绘制每集的奖励,以及print_policy()打印学习到的最佳策略。该清单仅显示了SARSAAgent.的代码,其余代码请参考listing4_3.ipynb(清单 4-3 )。

# SARSA Learning agent class
from collections import defaultdict

class SARSAAgent:
    def __init__(self, alpha, epsilon, gamma, get_possible_actions):
        self.get_possible_actions = get_possible_actions
        self.alpha = alpha
        self.epsilon = epsilon
        self.gamma = gamma
        self._Q = defaultdict(lambda: defaultdict(lambda: 0))

    def get_Q(self, state, action):
        return self._Q[state][action]

    def set_Q(self, state, action, value):
        self._Q[state][action] = value

    # carryout SARSA updated based on the sample (S, A, R, S', A')
    def update(self, state, action, reward, next_state, next_action, done):
        if not done:
            td_error = reward + self.gamma * self.get_Q(next_state, next_action) /
- self.get_Q(state,action)
        else:
            td_error = reward - self.get_Q(state,action)

        new_value = self.get_Q(state,action) + self.alpha * td_error
        self.set_Q(state, action, new_value)

    # get argmax for q(s,a)
    def max_action(self, state):
        actions = self.get_possible_actions(state)
        best_action = []
        best_q_value = float("-inf")

        for action in actions:
            q_s_a = self.get_Q(state, action)
            if q_s_a > best_q_value:
                best_action = [action]
                best_q_value = q_s_a
            elif  q_s_a == best_q_value:
                best_action.append(action)
        return np.random.choice(np.array(best_action))

    # choose action as per ε-greedy policy
    def get_action(self, state):
        actions = self.get_possible_actions(state)

        if len(actions) == 0:
            return None

        if np.random.random() < self.epsilon:
            a = np.random.choice(actions)
            return a
        else:
            a = self.max_action(state)
            return a

Listing 4-3SARA On-Policy TD Control

图 4-14 显示了随着学习的进展和学习到的最佳策略,每集奖励的图表。我们可以看到回报很快接近最优值。我们学到的策略是通过首先一路向上,然后右转走向目标来避免悬崖。这是令人惊讶的,因为我们本来期望代理学习策略以跳过悬崖并到达目标,与代理学习的策略相比,这将是最短的四步路径。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-14

在 SARSA 和代理学习的策略下学习期间的报酬图

但是,随着我们的策略继续使用ε-greedy 进行探索,总有很小的几率,在某个状态下,当智能体在悬崖细胞旁边时,它随机采取一个动作,掉下悬崖。它表明了即使对环境已经有了足够的了解,也要继续探索的问题,也就是说,当同样的ε-贪婪策略被用于采样和改进时。我们将看到如何在 Q-learning 中避免这个问题,在 Q-learning 中,使用探索性行为策略来执行非策略学习以生成训练样本,并且将确定性策略学习为最佳目标策略。

接下来,我们将研究我们的第一个非策略 TD 算法,称为 Q-learning

Q-Learning:一种非策略 TD 控制

在 SARSA 中,我们使用值为 SARSA 的样本,这些样本由以下策略生成。动作来自状态S*的一个*是使用ε-greedy 策略产生的,该策略随后在 GPI 的“改进”步骤中得到改进。然而,与其从策略中生成 A ,不如我们查看所有的Q(SA )并选择动作 A ,这使得跨动作 A 的 Q(S ‘,A ‘)的值最大化我们可以继续生成样本( SARS )(注意编号 A 为该元组中的第五个值),使用类似ε-greedy 的探索性策略。但是我们通过选择 A arg maxAQ(SA )来改进策略。这种方法上的小变化创造了一种学习最优策略的新方法,称为 Q-learning 。这不再是一种策略上的学习,而是一种策略外的控制方法,其中样本( SARS )是由探索性策略生成的,同时我们最大化Q(S, *A *

我们使用带有ε-贪婪策略的探索来生成样本( SARS )。同时,我们通过寻找状态 S 中的 Q 最大化动作arg maxAQ(SA )来利用已有的知识。我们将在第九章讲述更多关于勘探和开采之间的权衡。

q 值的更新规则现在定义如下:

)

(4.10)

将前面的等式与等式( 4.8 )进行比较,你会注意到这两种方法之间的微妙差异,以及这是如何使 Q-learning 成为一种非策略方法的。Q-learning 的非策略行为很方便,它使样本很有效。我们将在稍后讨论体验回放回放缓冲时触及这一点。图 4-15 给出了 Q 学习的伪代码。

Q-learning Off-Policy TD Control

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-15

q-学习,非策略 TD 控制

listing4_4.ipynb中的代码实现了 Q-learning。像 SARSA 一样,它有一个 Q-learning 代理,类似于清单 4-3 中的 SARSA 代理,除了更新规则的变化,它遵循( 4.10 )用于 Q-learning。学习功能与 SARSA 相比也有微小的变化。在 SARSA 中我们不再需要第五个值,即来自*next_action A 。我们现在选择最佳行动A同时处于状态S来改进类似于贝尔曼最优方程的策略。来自 SARSA 的其余实现被带到 Q-learning 中。清单 4-4 展示了QLearningAgent的实现。请仔细注意与 SARSA 代理相比,更新规则中的更改。*

# Q- Learning agent class
from collections import defaultdict

class QLearningAgent:
    def __init__(self, alpha, epsilon, gamma, get_possible_actions):
        self.get_possible_actions = get_possible_actions
        self.alpha = alpha
        self.epsilon = epsilon
        self.gamma = gamma
        self._Q = defaultdict(lambda: defaultdict(lambda: 0))

    def get_Q(self, state, action):
        return self._Q[state][action]

    def set_Q(self, state, action, value):
        self._Q[state][action] = value

    # Q learning update step
    def update(self, state, action, reward, next_state, done):
        if not done:
            best_next_action = self.max_action(next_state)
            td_error = reward + self.gamma * self.get_Q(next_state, best_next_action) - self.get_Q(state,action)
        else:
            td_error = reward - self.get_Q(state,action)

        new_value = self.get_Q(state,action) + self.alpha * td_error
        self.set_Q(state, action, new_value)

    # get best A for Q(S,A) which maximizes the Q(S,a) for actions in state S
    def max_action(self, state):
        actions = self.get_possible_actions(state)
        best_action = []
        best_q_value = float("-inf")

        for action in actions:
            q_s_a = self.get_Q(state, action)
            if q_s_a > best_q_value:
                best_action = [action]
                best_q_value = q_s_a
            elif  q_s_a == best_q_value:
                best_action.append(action)
        return np.random.choice(np.array(best_action))

    # choose action as per ε-greedy policy for exploration
    def get_action(self, state):
        actions = self.get_possible_actions(state)

        if len(actions) == 0:
            return None

        if np.random.random() < self.epsilon:
            a = np.random.choice(actions)
            return a
        else:
            a = self.max_action(state)
            return a

Listing 4-4Q Learning Off-Policy TD Control

让我们看看 Q-learning 在悬崖世界环境中的应用。每集的奖励随着训练而提高,并达到最佳值-13,而在 SARSA 下为-17。如图 4-16 所示,Q-learning 下更好的政策是显而易见的。在 Q-learning 下,代理学习通过悬崖上方第二行的单元格导航到目标。在 Q-learning 下,agent 在学习一个确定性的策略,我们的环境也是确定性的。换句话说,如果代理人采取向右移动的行动,它肯定会向右移动,并且没有机会在任何其他方向上采取任何随机步骤。因此,代理人学习通过越过悬崖向目标前进的最优策略。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-16

Q 学习和 agent 学习策略下学习过程中的报酬图

文件listing4_4.ipynb还显示了 Q-agent 应用于出租车世界环境时的学习曲线。我们将在下一章的状态值近似的深度学习方法中重新讨论 Q-learning。在这种情况下,Q-learning 被称为 DQN。DQN 及其变体将被用于在一些雅达利游戏上训练游戏代理。

为了结束对 Q-learning 的讨论,让我们看看 Q-learning 可能引入的一个特殊问题,即最大化偏差。

最大化偏差和双重学习

如果你回头看看方程( 4.10 ,你会注意到我们正在对A进行最大化,以获得最大值Q(SA)。类似地,在 SARSA 中,我们发现了一个新的ε-贪婪策略,它也是在 Q 上最大化,以获得具有最高 Q 值的动作。此外,这些 q 值是真实状态-动作值的估计值。总之,我们使用 q 估计的最大值作为最大值的“估计”。这种将“最大估计值”作为“最大估计值”的方法引入了+ve 偏差。

为了看到这一点,考虑一个场景,其中在某些转换中奖励取三个值:5、0、+5,每个值的概率为 1/3。预期回报是零,但当我们看到+5 的时候,我们把它作为最大化的一部分,然后它永远不会下降。因此,+5 成为真实回报的估计值,否则预期值为 0。这是由于最大化步骤而引入的正偏差。

消除+ve 偏差的方法之一是使用一组两个 q 值。一个q-值用于寻找使 q 值最大化的动作,然后另一组 q 值用于寻找该最大动作的 q 值。在数学上,它可以表示如下:

替换*??【最大】【a】【q12】(*

*我们使用 Q 2 来寻找最大化动作 A ,然后使用 Q 1 来寻找最大 Q 值。可以看出,这种方法消除了+ve 或最大化偏差。当我们谈到 DQN 时,我们将再次讨论这个概念。

预期的 SARSA 控制

让我们看看另一种方法,它是 Q-learning 和 SARSA 的混合体;叫做预期萨萨。它与 Q-learning 类似,只是( 4.10 )中的“max”被替换为一个期望,如下所示:

)

(4.11)

由于随机选择了 A t + 1 ,预期的 SARSA 与在 SARSA 中看到的方差相比具有较低的方差。在预期 SARSA 中,我们对所有可能的行为都采用预期,而不是抽样。

在悬崖世界问题中,我们有确定性动作,因此我们可以设置学习速率 α = 1,而不会对学习质量产生任何重大影响。我们给出了算法的伪代码。除了采用期望而不是最大化的更新逻辑之外,它反映了 Q-learning。我们可以将 expected SARSA 作为 on-policy 运行,这是我们在 cliff 和 taxi 环境中测试它时要做的事情。它也可以脱离策略运行,其中行为策略更具探索性,而目标策略 π 遵循确定性贪婪策略。参见图 4-17 。

Expected SARSA TD Control

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-17

预期 SARA TD 控制

文件listing4_5.ipynb显示了预期的 SARSA 代理的代码。它类似于 Q-agent,只是用期望代替了最大化。这种变化增加了算法的计算复杂度,但比 SARSA 和 Q-learning 收敛得更快。清单 4-5 重现了预期的 SARSA 代理类的代码。

# Expected SARSA Learning agent class

class ExpectedSARSAAgent:
    def __init__(self, alpha, epsilon, gamma, get_possible_actions):
        self.get_possible_actions = get_possible_actions
        self.alpha = alpha
        self.epsilon = epsilon
        self.gamma = gamma
        self._Q = defaultdict(lambda: defaultdict(lambda: 0))

    def get_Q(self, state, action):
        return self._Q[state][action]

    def set_Q(self, state, action, value):
        self._Q[state][action] = value

    # Expected SARSA Update
    def update(self, state, action, reward, next_state, done):
        if not done:
            best_next_action = self.max_action(next_state)
            actions = self.get_possible_actions(next_state)
            next_q = 0
            for next_action in actions:
                if next_action == best_next_action:
                    next_q += (1-self.epsilon+self.epsilon/len(actions))* self.get_Q(next_state, next_action)
                else:
                    next_q += (self.epsilon/len(actions))* self.get_Q(next_state, next_action)

            td_error = reward + self.gamma * next_q - self.get_Q(state,action)
        else:
            td_error = reward - self.get_Q(state,action)

        new_value = self.get_Q(state,action) + self.alpha * td_error
        self.set_Q(state, action, new_value)

    # get best A for Q(S,A) which maximizes the Q(S,a) for actions in state S
    def max_action(self, state):
        actions = self.get_possible_actions(state)
        best_action = []
        best_q_value = float("-inf")

        for action in actions:
            q_s_a = self.get_Q(state, action)
            if q_s_a > best_q_value:
                best_action = [action]
                best_q_value = q_s_a
            elif  q_s_a == best_q_value:
                best_action.append(action)
        return np.random.choice(np.array(best_action))

    # choose action as per ε-greedy policy for exploration
    def get_action(self, state):
        actions = self.get_possible_actions(state)

        if len(actions) == 0:
            return None

        if np.random.random() < self.epsilon:
            a = np.random.choice(actions)
            return a
        else:
            a = self.max_action(state)
            return a

Listing 4-5Expected SARSA TD Control

图 4-18 显示了为悬崖世界训练预期的 SARSA 代理的结果。与 SARSA 和 Q-learning 相比,它能最快地收敛到最优值。你可以实验,看看变化的学习率 α 在极限内对收敛没有大的影响。有趣的是,预期 SARA 学习的策略介于 Q-learning 和 SARSA 之间。在这种策略下的代理人通过迷宫的中间一排到达目标。在我们的例子中,我们使用预期的 SARSA 作为 on-policy,即使用相同的ε-greedy 策略进行探索和改进。但可能由于这种期望,它从常规的 SARSA 中学习改进,并发现通过中间一排足够安全。在 Python 笔记本中,你还可以看到对出租车世界运行这个算法的结果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-18

带有悬崖世界中预期 SARSA 和代理学习的策略的报酬图

重放缓冲和偏离策略学习

离策学习涉及两个独立的策略:行为策略 b ( a | s )探索并生成范例;以及 π ( a | s ),代理正在努力学习的目标策略为最优策略。因此,我们可以反复使用行为策略产生的样本来训练代理。该方法使得过程样本高效,因为由代理观察到的单个转换可以被多次使用。

这叫做体验回放。作为学习过程的一部分,代理从环境中收集经验,并多次重复这些经验。在体验回放中,我们将样本(s, a, r, s', done)存储在缓冲区中。样本是使用探索性行为策略生成的,而我们使用 q 值来改进确定性目标策略。因此,我们总是可以使用行为策略中的旧样本,并一次又一次地应用它们。我们将缓冲区大小固定为某个预先确定的大小,并在收集新样本时不断删除旧样本。该过程通过多次重用样本来提高学习样本的效率。该方法的其余部分与非策略代理相同。

让我们将这种方法应用于 Q-learning agent。这次我们将跳过给出伪代码,因为除了在每次转换中多次使用重放缓冲区中的样本之外,几乎没有任何变化。我们在缓冲器中存储一个新的过渡,然后从缓冲器中采样batch_size个样本。这些样本用于以通常的方式训练 Q-agent。然后,代理在环境中采取另一个步骤,循环再次开始。Listing4_6.ipynb给出了重放缓冲区的实现以及如何在学习算法中使用它。参见清单 4-6 。

class ReplayBuffer:
    def __init__(self, size):
        self.size = size #max number of items in buffer
        self.buffer =[] #array to hold buffer

    def __len__(self):
        return len(self.buffer)

    def add(self, state, action, reward, next_state, done):
        item = (state, action, reward, next_state, done)
        self.buffer = self.buffer[-self.size:] + [item]

    def sample(self, batch_size):
        idxs = np.random.choice(len(self.buffer), batch_size)
        samples = [self.buffer[i] for i in idxs]
        states, actions, rewards, next_states, done_flags = list(zip(*samples))
        return states, actions, rewards, next_states, done_flags

# training algorithm with reply buffer
def train_agent(env, agent, episode_cnt=10000, tmax=10000,
anneal_eps=True, replay_buffer = None, batch_size=16):

    episode_rewards = []
    for i in range(episode_cnt):
        G = 0
        state = env.reset()
        for t in range(tmax):
            action = agent.get_action(state)
            next_state, reward, done, _ = env.step(action)
            if replay_buffer:
                replay_buffer.add(state, action, reward, next_state, done)
                states, actions, rewards, next_states, done_flags = replay_buffer(batch_size)
                for i in range(batch_size):
                    agent.update(states[i], actions[i], rewards[i], next_states[i], done_flags[i])
            else:
                agent.update(state, action, reward, next_state, done)

            G += reward
            if done:
                episode_rewards.append(G)
                # to reduce the exploration probability epsilon over the
                # training period.
                if anneal_eps:
                    agent.epsilon = agent.epsilon * 0.99
                break
            state = next_state
    return np.array(episode_rewards)

Listing 4-6Q-Learning with Replay Buffer

具有重放缓冲器的 Q-agent 应该通过从缓冲器中重复采样来改善初始收敛。当我们看 DQN 时,抽样效率将变得更加明显。从长远来看,在有或没有重放缓冲区的情况下,学习到的最佳值之间不会有任何显著差异。它的另一个优点是打破了样本之间的相关性。当我们用 Q-learning(即 DQN)来看深度学习时,这一点也会变得很明显。

连续状态空间的 q 学习

到目前为止,我们看到的所有例子都有离散的状态空间。迄今为止所研究的所有方法都可以归类为列表法。状态动作空间被表示为一个矩阵,其中状态沿一维,动作沿横轴。

我们将很快过渡到连续状态空间,并大量使用深度学习来通过神经网络表示状态。然而,我们仍然可以用一些简单的方法解决许多连续状态问题。在为下一章做准备时,让我们看看将连续值转换为离散值的最简单的方法。我们将采用的方法是以一定的精度对连续浮点数进行舍入,例如,将-1 到 1 之间的连续状态空间值转换为-1,-0.9,-0.8,… 0,0.1,0.2,… 1.0。

listing4_7.ipynb展示了这种方法的实际应用。我们将继续使用来自listing4_6的 Q 学习代理、经验回复和学习算法。然而,这一次我们将在一个连续的环境中应用这些知识,这个环境就是在本章开始时详细描述的CartPole。我们需要的关键变化是从环境中接收状态值,离散化这些值,然后将其作为观察结果传递给代理。代理只能看到离散值,并使用这些离散值通过 QAgent 学习最佳策略。我们在清单 4-7 中重现了用于将连续状态值转换成离散值的方法。见图 4-19 。

# We will use ObservationWrapper class from gym to wrap our environment.
# We need to implement observation() method which will receive the
# original state values from underlying environment
# In observation() we will discretize the state values
# which then will be passed to outside world by env
# the agent will use these discrete state values
# to learn an effective policy using q-learning
from gym.core import ObservationWrapper

class Discretizer(ObservationWrapper):
    def observation(self, state):
        discrete_x_pos = round(state[0], 1)
        discrete_x_vel = round(state[1], 1)
        discrete_pole_angle = round(state[2], 1)
        discrete_pole_ang_vel = round(state[3], 1)

        return (discrete_x_pos, discrete_x_vel,
                discrete_pole_angle, discrete_pole_ang_vel)

Listing 4-7Q-Learning (Off-Policy) on Continuous State Environment

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-19

CartPole 连续状态空间环境下 Q 学习的报酬图

与 200 的最大奖励相比,状态离散化的 Q-agent 能够获得大约 50 的奖励。在后续章节中,我们将研究其他更强大的方法来获得更多奖励。

n 步返回

在本节中,我们将统一 MC 和 TD 方法。MC 方法从一个状态采样返回,直到情节结束,并且它们不引导。因此,MC 方法不能用于连续的任务。另一方面,TD 使用一步回报来估计剩余奖励的价值。TD 方法在一个步骤之后立即对轨迹和自举进行短暂观察。

这两种方法都是两个极端,在很多情况下,中庸之道会产生更好的结果。 n 步的思路是用后面 n 步的奖励,然后从 n+1 步 bootstrap 来估算剩余奖励的价值。图 4-20 显示了 n 的不同值的备份图。一个极端是一步法,这是我们刚刚在 SARSA、Q-learning 和其他相关方法的上下文中看到的 TD(0)方法。另一个极端是∞-步 TD,它只不过是一种 MC 方法。广义的观点是,TD 和 MC 方法是同一连续体的两个极端。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-20

TD(0)和 MC 处于两个极端的 n 步方法的备份图,n=1 和 n=∞(本集结束)

n-step 方法可用于 on-policy 设置。我们可以有 n 步 SARSA 和 n 步预期 SARSA,这些都是我们目前所学内容的自然延伸。然而,在非策略学习中使用 n 步方法需要我们再考虑一个概念,即在行为策略和目标策略下观察特定 n 步状态转换的相对差异。为了使用来自行为策略 b ( a | s )的数据来优化目标策略 π ( a | s ),我们需要将在行为策略下观察到的 n 步回报乘以一个比率,称为重要性抽样比率

考虑一个起始状态 S t 以及动作的轨迹和状态序列直到剧集结束, A tSt+1At+1,…。, S T 。在策略 π 下观察序列的概率由下面给出:

)

重要抽样比是目标策略下轨迹的概率 π 与行为策略下轨迹的概率 b 之比。

)

(4.12)

重要性采样比率确保基于在目标策略下观察轨迹的相对机会与在行为策略下观察相同轨迹的机会,向上或向下调整在行为策略下观察到的轨迹的返回。

没有免费的东西,重要性抽样就是这种情况。重要抽样比率会导致很大的差异。另外,这些计算效率不高。有许多先进的技术,如折扣感知重要性采样每决策重要性采样,它们以各种不同的方式查看重要性采样和奖励,以减少方差,并使这些算法有效。

在本书中,我们不会深入讨论实现这些算法的细节。我们的重点是在概念层面上介绍这些,并让您了解先进的技术。

资格跟踪和 TD(λ)

资格追踪以算法有效的方式统一了 MC 和 TD 方法。当 TD 方法与合格跟踪结合时,产生 TD(λ),其中 λ = 0,使其相当于我们到目前为止所研究的一步 TD。这就是一步 TD 也被称为 TD(0)的原因。 λ = 1 的值使其类似于常规的∞-步长 TD,或者换句话说,类似于 MC 方法。资格追踪使得在非周期性任务中应用 MC 方法成为可能。我们将只涵盖资格追踪和 TD( λ )的高级概念。

在上一节中,我们讨论了 n 步回报,其中 n=1 表示常规 TD 方法,n=∞表示 MC 方法。我们也提到了两个极端都不好的事实。一种算法在 n 的某个中间值时表现最佳,n-step 提供了如何统一 TD 和 MC 的观点。资格所做的是提供一种有效的方法来组合它们,而不需要跟踪每一步的 n 步转换。到目前为止,我们已经研究了一种基于未来的下一个 n 转换来更新状态值的方法。这叫做前视。然而,你也可以向后看,即,在每个时间步 t ,并且看到在时间步 t 的奖励在过去对前面的 n 状态的影响。这就是所谓的后视,构成了 TD 的核心( λ )。该方法允许在 TD 学习中集成 n 步返回的有效实现。

回头看图 4-20 。如果不是选择不同的 n 值,而是将所有的 n 步收益与某个权重相结合,会怎么样?这就是所谓的λ-返回,等式如下:

)

(4.13)

这里,Gt:t+n是在第 n 步结束时使用剩余步骤的引导值的 n 步返回。其定义如下:

)

(4.14)

如果我们把 λ = 0 放入( 4.13 ,我们得到如下:

)

前面的表达式类似于( 4.7 )中 TD(0)状态动作更新的目标值。

另一方面,将 λ = 1 放入( 4.13 )使)模仿 MC 并返回 G * t * 如下:

)

TD(λ)算法使用先前的λ-return 和被称为合格跟踪的跟踪向量,以使用“后向”视图获得有效的在线 TD 方法。资格跟踪记录了一个州在过去多长时间内被观察到,以及该州的估计值将在多大程度上受到当前观察到的回报的影响。

我们将在这里停止对λ回报、合格跟踪和 TD(λ)的基本介绍。关于数学推导以及基于它们的各种算法的详细回顾,请参考《强化学习:巴尔托和萨顿的介绍》一书,第二版。

DP、MC 和 TD 之间的关系

在本章的开始,我们讨论了 DP、MC 和 TD 方法的比较。我们先介绍 MC 方法,然后介绍 TD 方法,然后我们使用 n 步和 TD(λ)将 MC 和 TD 结合起来,作为基于样本的无模型学习的两个极端。

作为本章的总结,表 4-1 总结了 DP 和 TD 方法的比较。

表 4-1

贝尔曼方程背景下 DP 和 TD 方法的比较

|   |

完整备份(DP)

|

示例备份(TD)

|
| — | — | — |
| 贝尔曼期望方程为vπ(s | 迭代策略评估 | TD 预测 |
| 贝尔曼期望方程为 q π ( sa | q-策略迭代 | 撒尔沙 |
| 贝尔曼最优方程为qπ(sa ) | q 值迭代 | q 学习 |

摘要

在这一章中,我们看了强化学习的无模型方法。我们从使用蒙特卡罗方法估计状态值开始。我们研究了“首次就诊”和“每次就诊”的方法。然后,我们从总体上,特别是在 MC 方法的背景下,研究了偏差和方差的权衡。在 MC 估算的基础上,我们将 MC 控制方法与第三章中介绍的政策改进 GPI 框架联系起来。我们看到了如何通过将基于 DP 的方法的估计步骤换成基于 MC 的方法来应用 GPI。我们详细研究了需要平衡的勘探开发困境,尤其是在转换概率未知的无模型世界中。然后,我们简要地讨论了 MC 方法上下文中的非策略方法。

TD 是我们研究的关于无模型学习的下一个方法。我们从建立 TD 学习的基础开始,从基于 TD 的价值评估开始。接下来是对 SARSA 的深入研究,这是一种政策性 TD 控制方法。然后我们研究了 Q-learning,一种强大的非策略 TD 学习方法,以及它的一些变体,如 expected SARSA。

在 TD 学习的背景下,我们还引入了状态近似的概念,将连续状态空间转换为近似的离散状态值。状态近似的概念将构成下一章的主要内容,并将允许我们将深度学习与强化学习结合起来。

在结束这一章之前,我们最后看了 n 步回报、合格轨迹和 TD(λ)作为将 TD 和 MC 结合到一个单一框架中的方法。******

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值