深度强化学习探索指南-全-
深度强化学习探索指南(全)
原文:Grokking Deep Reinforcement Learning
译者:飞龙
前置材料
前言
那么,关于强化学习,这里有一些事情。由于多种原因,它既难以学习,也难以教授。首先,它是一个非常技术性的主题。它背后有大量的数学和理论。在不陷入其中时传达正确数量的背景知识本身就是一项挑战。
其次,强化学习鼓励了一种概念错误。强化学习既是思考决策问题的一种方式,也是解决这些问题的工具集。我所说的“一种方式”是指强化学习提供了一个决策框架:它讨论了状态和强化信号等细节。我所说的“一套工具”是指当我们讨论强化学习时,我们会发现自己使用诸如马尔可夫决策过程和贝尔曼更新等术语。将思维方式与我们对这种思维方式所使用的数学工具混淆是极其容易的。
最后,强化学习可以用多种方式实现。因为强化学习是一种思维方式,我们可以通过尝试以非常抽象的方式实现框架来讨论它,或者将其基于代码,或者,就事论事,基于神经元。一个人决定使用的底层使得这两个困难变得更加具有挑战性——这把我们带到了深度强化学习。
专注于深度强化学习很好地将这些问题一次性放大。关于强化学习(RL)和深度神经网络都有背景知识。两者都值得单独研究,并且以完全不同的方式发展。在开发工具的背景下解释这两者的方法并不容易。此外,不要忘记,理解强化学习不仅需要理解工具及其在深度网络中的实现,还需要理解关于强化学习的思维方式;否则,你无法将所学内容推广到直接研究的例子之外。再次强调,教授强化学习是困难的,而且有那么多错误的方法可以用来教授深度强化学习——这把我们带到了米格尔·莫雷莱斯和这本书。
这本书组织得非常好。它用技术但清晰的语言解释了机器学习是什么,深度学习是什么,以及强化学习是什么。它使读者能够理解该领域的更大背景,以及你可以使用深度强化学习的技巧做什么,同时也理解了机器学习(ML)、强化学习(RL)和深度强化学习所呈现的思维方式。它清晰简洁。因此,它既是一本学习指南,也是一本参考书,至少对我来说,它还是一些灵感的来源。
我对这一切并不感到惊讶。我已经认识米格尔好几年了。他从学习机器学习课程到教授它们。他作为佐治亚理工学院在线硕士学位课程《强化学习与决策制定》的主要助教,比我能够数得上的学期还要多。在那段时间里,他影响了成千上万的学生。我看着他作为一个从业者、一个研究人员和一个教育者的成长。他帮助使佐治亚理工学院的 RL 课程比最初更好,而且在我写这篇文章的时候,还在继续使强化学习的理解对学生来说更加深入。他是一个天生的教师。
这篇文章反映了他的才华。我很高兴能够与他合作,并且我很高兴他决定写这本书。享受吧。我想你会学到很多东西。我自己也学到了一些东西。
查尔斯·伊斯贝尔,小
教授和约翰·P·伊莱,小
计算机学院
乔治亚理工学院
前言
强化学习是一个充满活力、有可能对人类历史产生深远影响的领域。几种技术已经影响了我们世界的历史,并改变了人类历史的进程,从火、轮子、电到互联网。每一次技术发现都以累积的方式推动下一次发现。没有电,个人电脑就不会存在;没有它,互联网就不会存在;没有它,搜索引擎就不会存在。
对我来说,强化学习和人工智能最令人兴奋的方面,与其说是仅仅在我们身边有其他智能实体,这确实很令人兴奋,不如说是那之后的事情。我相信强化学习作为一个用于自主优化特定任务的稳健框架,有可能改变世界。除了任务自动化之外,智能机器的创造可能会推动我们对人类智能的理解达到前所未有的地方。可以说,如果你能够确定地找到每个问题的最优决策,你很可能就理解了找到这些最优决策的算法。我有一种感觉,通过创造智能实体,人类可以成为更加智能的存在。
但我们离这个目标还远着呢,要实现这些宏伟的梦想,我们需要更多的智慧。强化学习不仅处于起步阶段,而且已经处于这种状态有一段时间了,所以前方还有许多工作要做。我写这本书的原因是让更多的人理解深度强化学习,以及一般的强化学习,并帮助你做出贡献。
尽管强化学习框架直观易懂,但大多数资源对于新手来说都难以理解。我的目标不是写一本只提供代码示例的书,更不是创建一本教授强化学习理论的资源。相反,我的目标是创建一个能够弥合理论与实践之间差距的资源。正如你很快就会看到的,我不回避方程式;如果你想要深入理解一个研究领域,它们是必不可少的。即使你的目标是实际应用,构建高质量的强化学习解决方案,你仍然需要这个理论基础。然而,我也不会仅仅依赖于方程式,因为并不是所有对强化学习感兴趣的人都喜欢数学。有些人更习惯于代码和具体的例子,所以这本书提供了这个精彩领域的实际应用方面。
在这个为期三年的项目中,我大部分的努力都投入到了弥合这个差距上;我不回避直观地解释理论,也不只是简单地列出代码示例。我两者都做,而且非常注重细节。对于那些理解教科书和讲座有困难的人来说,更容易掌握顶尖研究者使用的词汇:为什么用这些特定的词汇,而不是其他词汇。而对于那些知道这些词汇并喜欢阅读方程式,但难以看到这些方程式在代码中的表现形式以及它们如何连接的人来说,更容易理解强化学习的实际应用方面。
最后,我希望你喜欢这份工作,更重要的是,它确实实现了你的目标。我希望你能深入理解深度强化学习,并能够回馈并贡献于这个我逐渐热爱的出色社区。正如我之前提到的,如果不是因为一系列相对较新的技术创新,你不会读到这本书。但书后的世界取决于你,所以勇敢地去影响世界吧。
致谢
我想感谢乔治亚理工学院的那些人,他们承担了风险,为世界上任何人都提供了第一个在线计算机科学硕士学位,让他们能够获得高质量的毕业教育。如果不是那些使这一切成为可能的人,我可能就不会写这本书。
我想感谢查尔斯·伊斯贝尔教授和迈克尔·利特曼教授,他们共同开设了一门优秀的强化学习课程。我对伊斯贝尔院长特别感激,他给了我很多成长和学习强化学习的机会。此外,我教授强化学习的方式——通过将问题分为三种类型的反馈——是从利特曼教授那里学到的。我非常感激能够从他们那里接受指导。
我要感谢乔治亚理工学院 CS 7642 课程的充满活力的教学团队,他们一起努力探讨如何帮助学生学得更多并享受与我们在一起的时光。特别感谢 Tim Bail、Pushkar Kolhe、Chris Serrano、Farrukh Rahman、Vahe Hagopian、Quinn Lee、Taka Hasegawa、Tianhang Zhu 和 Don Jacob。你们都是如此优秀的队友。我还要感谢那些之前对该课程做出重大贡献的人。我从我们的互动中学到了很多:Alec Feuerstein、Valkyrie Felso、Adrien Ecoffet、Kaushik Subramanian 和 Ashley Edwards。我还要感谢我们的学生,是他们的提问帮助我识别了那些试图学习强化学习的人的知识盲点。这本书是为你们写的。特别感谢那位推荐我给 Manning 写这本书的匿名学生;我仍然不知道你是谁,但你很清楚你是谁。谢谢。
我要感谢 Lockheed Martin 团队在我写这本书期间的所有反馈和互动。特别感谢 Chris Aasted、Julia Kwok、Taylor Lopez 和 John Haddon。John 是第一个审阅我早期草稿的人,他的反馈帮助我将写作提升到下一个层次。
我要感谢 Manning 团队为使这本书成为现实提供的框架。感谢 Brian Sawyer 主动联系并打开大门;感谢 Bert Bates 在早期设定方向并帮助我专注于教学;感谢 Candace West 帮助我从零开始有所成就;感谢 Susanna Kline 在我忙碌时帮助我加快进度;感谢 Jennifer Stout 在我冲过终点线时为我加油;感谢 Rebecca Rinehart 处理紧急事务;感谢 Al Krinker 为我提供可操作的反馈并帮助我区分信号和噪音;感谢 Matko Hrvatin 跟进 MEAP 发布并给我施加额外的写作压力;感谢 Candace Gillhoolley 将这本书推广出去;感谢 Stjepan Jurekovic´帮助我推广;感谢 Ivan Martinovic 为改进文本提供必要的反馈;感谢 Lori Weidert 两次将书籍调整为生产就绪;感谢 Jennifer Houle 在设计变更上保持温和;感谢 Katie Petito 耐心地处理细节;感谢 Katie Tennant 进行细致和最终的润色;以及任何我遗漏的人,或者那些在幕后使这本书成为现实的人。我知道还有更多:感谢你们的辛勤工作。
向所有审稿人——Al Rahimi、Alain Couniot、Alberto Ciarlanti、David Finton、Doniyor Ulmasov、Edisson Reinozo、Ezra Joel Schroeder、Hank Meisse、Hao Liu、Ike Okonkwo、Jie Mei、Julien Pohie、Kim Falk Jørgensen、Marc-Philippe Huget、Michael Haller、Michel Klomp、Nacho Ormeño、Rob Pacheco、Sebastian Maier、Sebastian Zaba、Swaminathan Subramanian、Tyler Kowallis、Ursin Stauss 和 Xiaohu Zhu——表示感谢,你们的建议帮助使这本书变得更好。
我要感谢 Udacity 的团队,他们让我有机会与他们学生分享我对这个领域的热情,并为他们深度强化学习纳米学位录制演员-评论家讲座。特别感谢 Alexis Cook、Mat Leonard 和 Luis Serrano。
我要感谢强化学习社区帮助我澄清文本并提高我的理解。特别感谢 David Silver、Sergey Levine、Hado van Hasselt、Pascal Poupart、John Schulman、Pieter Abbeel、Chelsea Finn、Vlad Mnih,感谢他们的讲座;Rich Sutton 将这个领域的黄金副本集中在一个地方(他的教科书);以及 James MacGlashan 和 Joshua Achiam,感谢他们的代码库、在线资源和在我不知道去哪里寻找问题的答案时的指导。我要感谢 David Ha,他让我了解到下一步该去哪里。
特别感谢 Silvia Mora,她帮助使本书中的所有图表都显得可接受,并在我几乎承担的每一个副项目中给予我帮助。
最后,我要感谢我的家人,他们是我整个项目的基础。我知道写一本书是一个挑战,然后我学会了。但我的妻子和孩子始终在那里,在周末每隔两小时左右等待我的 15 分钟休息时间。感谢 Solo,在本书中途照亮了我的生活。感谢 Rosie,分享你的爱和美丽,感谢 Danelle,我亲爱的妻子,因为你的一切和你所做的一切。你是这个有趣的游戏(生活)中的完美队友。我很高兴我找到了你。
关于这本书
掌握深度强化学习架起了深度强化学习理论与实践之间的桥梁。本书的目标读者是熟悉机器学习技术、希望学习强化学习的人。本书从深度强化学习的基础开始。然后深入探讨了深度强化学习的算法和技术。最后,它提供了一项具有潜在影响力的高级技术的概述。
谁应该阅读这本书
对于一个研究领域感到舒适、熟悉 Python 代码、偶尔有一些数学知识、大量的直观解释和有趣且具体的例子来推动学习的人来说,这本书会很有趣。然而,任何只熟悉 Python 的人,只要对学习有足够的兴趣,都能从中获得很多。尽管假设了基本的深度学习知识,但本书提供了关于神经网络、反向传播和相关技术的简要复习。总之,这本书是自包含的,任何想要尝试与 AI 代理互动并掌握深度强化学习的人都可以使用这本书来实现目标。
本书是如何组织的:一个路线图
本书分为两部分,共 13 章。
在第一部分,第一章介绍了深度强化学习领域,并为未来的旅程设定了期望。第二章介绍了一个框架,用于设计 RL 代理能够理解的问题。第三章包含了当代理知道世界动态时解决 RL 问题的算法细节。第四章包含了当代理不知道世界动态时解决简单 RL 问题的算法细节。第五章介绍了解决预测问题的方法,这是高级 RL 方法的基础。
在第二部分,第六章介绍了解决控制问题的方法,这些方法是纯粹通过试错学习来优化策略的。第七章教授了更高级的 RL 方法,包括使用规划来提高样本效率的方法。第八章通过实现一个简单的使用神经网络进行函数近似的 RL 算法,介绍了在 RL 中使用函数近似的方法。第九章深入探讨了使用函数近似解决强化学习问题的更高级技术。第十章教授了迄今为止介绍的方法的一些最佳改进技术。第十一章介绍了一种使用深度学习模型与 RL 结合的不同技术,该技术在多个深度 RL 基准测试中证明了达到最先进性能的能力。第十二章深入探讨了深度 RL 的更高级方法、最先进的算法以及常用于解决现实世界问题的技术。第十三章概述了 RL 的高级研究领域,这些领域表明了向通用人工智能进步的最佳路径。
关于代码
本书包含了许多源代码示例,既有标题为“我说 Python”的框中的示例,也有文本中的示例。源代码以固定宽度字体格式化,如这样,以将其与普通文本区分开来,并具有语法高亮,以便更容易阅读。
在许多情况下,原始源代码已经被重新格式化;我们添加了换行符,重命名了变量,并重新调整了缩进以适应书中的可用页面空间。在极少数情况下,即使这样也不够,代码中包含 Python 中的行续符(\),以指示语句将在下一行继续。
此外,源代码中的注释通常已从框中移除,并在文本中描述了代码。代码注释指出了重要概念。
本书中的示例代码可以从 Manning 网站www.manning.com/books/grokking-deep-reinforcement-learning和 GitHubgithub.com/mimoralea/gdrl下载。
liveBook 讨论论坛
购买 掌握深度强化学习 包括免费访问由 Manning Publications 运营的私人网络论坛,您可以在论坛上对书籍发表评论、提出技术问题,并从作者和其他用户那里获得帮助。要访问论坛,请访问 livebook.manning.com/#!/book/grokking-deep-reinforcement-learning/discussion。您还可以在 livebook.manning.com/#!/discussion 了解更多关于 Manning 的论坛和行为准则。
Manning 对我们读者的承诺是提供一个场所,让读者之间以及读者与作者之间可以进行有意义的对话。这不是对作者参与特定数量活动的承诺,作者对论坛的贡献仍然是自愿的(且未付费)。我们建议您尝试向他提出一些挑战性的问题,以免他的兴趣转移!只要书籍在印刷中,论坛和先前讨论的存档将从出版社的网站提供访问。
关于作者
米格尔·莫雷莱斯 在科罗拉多州丹佛的洛克希德·马丁公司,导弹和火控,自主系统部门从事强化学习工作。他是乔治亚理工学院强化学习和决策课程的部分时间制教学助理。米格尔曾作为机器学习项目审稿人、自动驾驶汽车纳米学位导师和深度强化学习纳米学位内容开发者为 Udacity 工作。他从乔治亚理工学院计算机科学硕士毕业,专攻交互智能。
1 深度强化学习简介
在本章中
-
你将学习深度强化学习是什么,以及它与其他机器学习方法有何不同。
-
你将了解深度强化学习的最新进展以及它能为各种问题带来什么。
-
你将了解这本书的内容以及如何从中获得最大收益。
我想象着一个时代,我们将对机器人产生如同狗对人类那样的影响,我为机器人的进步感到自豪。
——克劳德·香农 信息时代之父,人工智能领域的贡献者
人类天生追求幸福感。从挑选我们的食物到提升我们的职业生涯,我们选择的每一个行动都源于我们体验生活中有益时刻的驱动力。这些时刻是自私的愉悦还是更慷慨的目标,它们是否带给我们即时的满足或长期的成功,它们仍然是我们对它们重要性和价值的感知。在某种程度上,这些时刻是我们存在的理由。
我们实现这些珍贵时刻的能力似乎与智力相关;“智力”被定义为获取和应用知识和技能的能力。社会认为智力高的人能够为了长期目标而放弃即时满足,为了可能更好但不确定的未来而放弃一个确定但可能更好的未来。那些需要较长时间才能实现且具有未知长期价值的目标通常是最难实现的,而那些能够承受沿途挑战的人是例外,是领导者,是社会中的知识分子。
在这本书中,你将了解一种被称为深度强化学习的方法,它涉及创建能够实现需要智能的目标的计算机程序。在本章中,我介绍了深度强化学习,并给出了一些建议,帮助你从这本书中获得最大收益。
什么是深度强化学习?
深度强化学习(DRL)是一种机器学习方法,涉及创建能够解决需要智能问题的计算机程序。DRL 程序的独特属性是通过利用强大的非线性函数逼近,从同时具有序列性、评估性和样本性的反馈中通过试错进行学习。
我将逐步为你解释这个定义。但是,不要过于纠结于细节,因为这需要我整本书的时间来帮助你理解深度强化学习。以下是对你在本书中将要学习内容的介绍。因此,它将在接下来的章节中重复并详细解释。
如果我实现了这本书的目标,在你完成阅读后,你应该能够精确地理解这个定义。你应该能够解释我为什么使用这些词,以及为什么没有使用更多或更少的词。但是,对于这一章,只需放松并深入阅读即可。
深度强化学习是一种人工智能的机器学习方法
人工智能(AI)是计算机科学的一个分支,涉及创建能够展示智能的计算机程序。传统上,任何显示认知能力(如感知、搜索、规划和学习)的软件都被认为是 AI 的一部分。由 AI 软件产生的功能示例包括
-
搜索引擎返回的页面
-
GPS 应用生成的路线
-
智能助手软件的语音识别和合成语音
-
电子商务网站推荐的产品
-
无人机中的跟随功能

人工智能的子领域
所有显示智能的计算机程序都被认为是 AI,但并非所有 AI 的例子都能学习。机器学习(ML)是 AI 的一个领域,涉及创建能够通过学习数据来解决需要智能的问题的计算机程序。ML 有三个主要分支:监督学习、无监督学习和强化学习。

机器学习的主要分支
监督学习(SL)是从标记数据中学习的任务。在 SL 中,人类决定收集哪些数据以及如何标记它们。SL 的目标是泛化。SL 的一个经典例子是手写数字识别应用:人类收集带有手写数字的图像,标记这些图像,并训练一个模型来正确识别和分类图像中的数字。期望训练好的模型能够泛化并在新的图像中正确分类手写数字。
无监督学习(UL)是从未标记数据中学习的任务。尽管数据不再需要标记,但计算机用于收集数据的方法仍然需要由人类设计。UL 的目标是压缩。UL 的一个经典例子是客户细分应用;人类收集客户数据并训练一个模型将客户分组到聚类中。这些聚类压缩了信息,揭示了客户之间的潜在关系。
强化学习(RL)是通过试错来学习的任务。在这种类型的任务中,没有人类标记数据,也没有人类收集或明确设计数据集。RL 的目标是行动。RL 的一个经典例子是玩 Pong 的代理;代理反复与 Pong 模拟器交互,通过采取行动并观察其效果来学习。期望训练好的代理能够以成功玩 Pong 的方式行动。
一种强大的近期 ML 方法,称为深度学习(DL),涉及使用多层非线性函数逼近,通常是神经网络。DL 不是一个独立的 ML 分支,因此它不同于之前描述的任务。DL 是一系列技术和方法,用于使用神经网络来解决 ML 任务,无论是 SL、UL 还是 RL。DRL 仅仅是使用 DL 来解决 RL 任务。

深度学习是一个强大的工具箱
重要的是,强化学习(RL)是一种解决问题的方法。人工智能领域定义了这个问题:创造智能机器。解决这个问题的方法之一是强化学习(RL)。在整个书中,你会找到 RL 与其他机器学习(ML)方法的比较,但只有在这一章中,你会找到人工智能的一般定义和历史概述。需要注意的是,强化学习(RL)领域包括深度强化学习(DRL)领域,所以尽管我在必要时会做出区分,但当我提到 RL 时,请记住 DRL 也包括在内。
深度强化学习关注的是创建计算机程序
深度强化学习(DRL)的核心是关于在不确定性下的复杂序列决策问题。但是,这是一个在许多领域都感兴趣的话题;例如,控制理论(CT)研究控制复杂已知动态系统的方法。在 CT 中,我们试图控制的系统的动力学通常事先是已知的。运筹学(OR)是另一个例子,它也研究不确定性下的决策,但这个领域的问题通常比 DRL 中常见的动作空间要大得多。心理学研究人类行为,这在某种程度上与“不确定性下的复杂序列决策”问题相同。

相似领域之间的协同作用
重要的是,你已经进入了一个受到许多其他领域影响的领域。尽管这是好事,但也带来了术语、符号等方面的不一致。我的观点是计算机科学方法来解决这个问题,因此这本书是关于构建解决不确定性下复杂决策问题的计算机程序,因此你可以在整本书中找到代码示例。
在 DRL 中,这些计算机程序被称为代理。代理只是一个决策者,没有其他角色。这意味着如果你在训练一个机器人来捡起物体,机器人手臂不是代理的一部分。只有做出决策的代码才被称为代理。
深度强化学习代理可以解决需要智能的问题
在代理的另一边是环境。环境是代理之外的一切;代理无法完全控制的一切。再次想象你正在训练一个机器人来捡起物体。要捡起的物体、放置物体的托盘、风以及决策者之外的一切都是环境的一部分。这意味着机器人手臂也是环境的一部分,因为它不是代理的一部分。即使代理可以决定移动手臂,实际的手臂运动是嘈杂的,因此手臂也是环境的一部分。
这种代理和环境之间的严格界限一开始可能感觉不太直观,但决策者,即代理,只能有一个单一的角色:做出决策。决策之后的所有事物都被打包进环境中。

代理和环境之间的界限
第二章深入探讨了 DRL 的所有组成部分。以下是对第二章内容的预览。
环境由与问题相关的一系列变量表示。例如,在机器人臂的例子中,手臂的位置和速度将是构成环境的变量的一部分。这些变量及其所有可能的值被称为状态空间。状态是状态空间的一个实例化,是一组变量所取的值。
有趣的是,通常情况下,智能体无法访问环境的实际完整状态。智能体可以观察到的状态部分被称为观察。观察依赖于状态,但却是智能体所能看到的。例如,在机器人臂的例子中,智能体可能只能访问摄像头图像。虽然每个物体的确切位置都存在,但智能体无法访问这个特定的状态。相反,智能体感知到的观察是从状态中派生出来的。你经常会在文献中看到状态被交替使用,包括在这本书中。我提前为这些不一致之处道歉。只需了解这些差异,并注意术语;这才是最重要的。

状态与观察
在每个状态下,环境会提供一组智能体可以选择的动作。智能体通过这些动作影响环境。环境可能会根据智能体的动作改变状态。负责这种映射的函数称为转换函数。环境还可能提供奖励信号作为响应。负责这种映射的函数称为奖励函数。转换和奖励函数的集合被称为环境的模型。

强化学习周期
环境通常有一个定义良好的任务。这个任务的目标是通过奖励函数来定义的。奖励函数的信号可以是同时序列的、评估的和采样的。为了实现目标,智能体需要表现出智能,或者至少是与智能相关联的认知能力,如长期思考、信息收集和泛化。
智能体有三个步骤的过程:智能体与环境交互,智能体评估其行为,智能体改进其响应。智能体可以被设计成学习从观察到的动作映射,称为策略。智能体可以被设计成学习环境的模型映射,称为模型。智能体可以被设计成学习估计奖励到映射,称为价值函数。
深度强化学习智能体通过试错学习改进其行为
代理与环境之间的交互会持续几个周期。每个周期被称为一个时间步长。在每个时间步长,代理观察环境,采取行动,并接收新的观察和奖励。状态、行动、奖励和新的状态集被称为一个经验。每个经验都有学习和改进性能的机会。

经验元组
代理试图解决的问题可能有一个自然的结束,也可能没有。有自然结束的任务,如游戏,被称为事件任务。相反,没有自然结束的任务,如学习前进运动,被称为持续任务。从事件任务开始到结束的时间步序列被称为一个事件。代理可能需要几个时间步和事件来学习解决一个任务。代理通过试错来学习:他们尝试一些东西,观察,学习,再尝试其他东西,如此循环。
你将在第四章中开始学习更多关于这个循环的内容,该章包含一个每个事件只有一个步骤的环境。从第五章开始,你将学习如何处理每个事件需要多个交互周期才能完成的环境。
深度强化学习代理从序列反馈中学习
代理采取的行动可能具有延迟后果。奖励可能稀疏,并且只在几个时间步之后才显现。因此,代理必须能够从序列反馈中学习。序列反馈引发了被称为时间信用分配问题的问题。时间信用分配问题是指确定哪个状态和/或行动对奖励负责的挑战。当问题有时间成分,并且行动有延迟后果时,为奖励分配信用是一个挑战。

时间信用分配问题的难度
在第三章中,我们将研究孤立情况下序列反馈的来龙去脉。也就是说,你的程序会从同时序列的、监督的(与评估的相对)、以及穷尽的(与抽样的相对)反馈中学习。
深度强化学习代理从评估反馈中学习
代理收到的奖励可能较弱,从某种意义上说,它可能不提供监督。奖励可能表明良好,但不正确,这意味着它可能不包含关于其他潜在奖励的信息。因此,代理必须能够从评估反馈中学习。评估反馈引发了探索的需求。代理必须能够在收集信息和利用当前信息之间取得平衡。这也被称为探索与利用的权衡。

探索与利用权衡的难度
在第四章中,我们将单独研究评估反馈的细节。也就是说,你的程序将从同时具有一次性(与序列性相对)、评估性和详尽性(与样本性相对)的反馈中学习。
深度强化学习代理从样本反馈中学习
代理收到的奖励只是一个样本,代理无法访问奖励函数。此外,状态和动作空间通常很大,甚至是无限的,因此从稀疏和微弱的反馈中学习变得更具挑战性。因此,代理必须能够从样本反馈中学习,并且必须能够泛化。

从样本反馈中学习的难度
被设计用来近似策略的代理被称为基于策略的;被设计用来近似价值函数的代理被称为基于价值的;被设计用来近似模型的代理被称为基于模型的;而那些被设计用来近似策略和价值函数的代理被称为演员-评论家。代理可以被设计来近似一个或多个这些组件。
深度强化学习代理使用强大的非线性函数近似
代理可以使用各种机器学习方法和技巧来近似函数,从决策树到支持向量机再到神经网络。然而,在这本书中,我们只使用神经网络;这也就是 DRL 中“深度”部分的含义。神经网络并不一定是每个问题的最佳解决方案;神经网络需要大量数据且难以解释,你必须牢记这些事实。然而,神经网络是可用的最强大的函数近似之一,它们的性能通常也是最好的。

一个简单的前馈神经网络
人工神经网络(ANN)是多层的非线性函数近似器,其灵感来源于动物大脑中的生物神经网络。ANN 不是一个算法,而是一个由多个数学变换层组成的结构,这些变换应用于输入值。
从第三章到第七章,我们只处理代理从详尽(与样本相对)反馈中学习的问题。从第八章开始,我们研究完整的 DRL 问题;也就是说,使用深度神经网络,使代理能够从样本反馈中学习。记住,DRL 代理从同时具有序列性、评估性和样本性的反馈中学习。
深度强化学习的过去、现在和未来
历史并非获得技能的必要条件,但它可以帮助你理解一个主题的背景,这反过来又可以帮助你获得动力,从而获得技能。AI 和 DRL 的历史应该帮助你设定对这项强大技术未来的期望。有时,我感觉围绕 AI 的炒作实际上是有益的;人们对此感兴趣。但是,当需要付出努力的时候,炒作就不再有帮助,这成为一个问题。虽然我很想对 AI 感到兴奋,但我也需要设定现实的期望。
人工智能和深度强化学习的近期历史
深度强化学习的开端可以追溯到很多年前,因为自古以来,人类就对除了我们自己之外的智能生物的可能性感到着迷。但一个良好的开端可以追溯到 20 世纪 30 年代、40 年代和 50 年代,当时艾伦·图灵的工作为现代计算机科学和 AI 的发展奠定了关键的理论基础,这些基础后来被科学家们利用。
其中最著名的是图灵测试,它提出了一种衡量机器智能的标准:如果一名人类审问者在聊天问答环节中无法区分一台机器和另一名人类,那么这台计算机就被认为是有智能的。尽管图灵测试非常基础,但它让几代人开始思考通过设定一个研究人员可以追求的目标来创造智能机器的可能性。
人工智能作为一门学术学科的正式开端可以归功于约翰·麦卡锡(John McCarthy),这位有影响力的 AI 研究人员对该领域做出了多项显著贡献。例如,麦卡锡在 1955 年提出了“人工智能”这一术语,1956 年领导了第一次 AI 会议,1958 年发明了 Lisp 编程语言,1959 年共同创立了麻省理工学院 AI 实验室,并在几十年的时间里为 AI 作为一门学科的发展做出了重要贡献。
人工智能寒冬
早期人工智能的所有工作和进步都引起了极大的兴奋,但也伴随着重大的挫折。杰出的 AI 研究人员曾建议我们将在几年内创造出类似人类的机器智能,但这一目标从未实现。当一位知名研究者 James Lighthill 编制了一份批评 AI 学术研究现状的报告时,情况变得更糟。所有这些发展共同导致了长达一段时间的资金减少和对 AI 研究的兴趣降低,这一时期被称为第一次人工智能寒冬。
该领域在多年间持续这种模式:研究人员取得进展,人们过度乐观,然后高估——导致政府和行业合作伙伴减少资金投入。

年度 AI 资金模式
人工智能的当前状态
我们可能正处于人工智能历史中的另一个高度乐观的时期,因此我们必须谨慎。从业者明白人工智能是一个强大的工具,但有些人将人工智能视为一个神奇的黑色盒子,它可以接受任何问题并给出最佳解决方案。这离事实相去甚远。其他人甚至担心人工智能会获得意识,好像这很重要,正如著名计算机科学家埃德加·W·迪杰斯特拉所说:“计算机能否思考的问题并不比潜水艇能否游泳的问题更有趣。”
然而,如果我们抛开好莱坞植入的人工智能愿景,我们可以让自己对这一领域的近期进展感到兴奋。如今,世界上最有影响力的公司对人工智能研究投入了最多的资金。像谷歌、Facebook、微软、亚马逊和苹果这样的公司投资于人工智能研究,并因人工智能系统而变得高度盈利。他们的重大和持续投资为当前的人工智能研究速度创造了完美的环境。当代研究人员拥有最好的计算能力和大量数据用于他们的研究,顶尖的研究团队在同一地点、同一时间共同研究相同的问题。当前的 AI 研究变得更加稳定和高效。我们已经见证了一个接一个的 AI 成功,而且似乎不会很快停止。
深度强化学习的进展
人工神经网络在强化学习问题上的应用始于 20 世纪 90 年代。其中之一是 Gerald Tesauro 等人创建的经典的国际象棋程序TD-Gammon。TD-Gammon 通过学习通过强化学习评估棋盘位置来学习玩国际象棋。尽管实施的技术并不精确地被认为是深度强化学习,但TD-Gammon 是第一个广泛报道的,使用人工神经网络解决复杂强化学习问题的成功案例。

TD-Gammon 架构
在 2004 年,Andrew Ng 等人开发了一架能够通过观察数小时人类专家飞行的飞行特技的自主直升机。他们使用了一种称为逆强化学习的技术,其中智能体从专家演示中学习。同年,Nate Kohl 和 Peter Stone 使用一类称为策略梯度方法的深度强化学习方法开发了一个用于 RoboCup 锦标赛的足球机器人。他们使用强化学习来教智能体前进运动。经过仅三小时的训练,该机器人达到了任何具有相同硬件的机器人中最快的向前移动速度。
在 2000 年代还有其他一些成功,但强化学习领域真正开始增长是在大约 2010 年深度学习领域起飞之后。2013 年和 2015 年,Mnih 等人发表了几篇论文,介绍了 DQN 算法。DQN 通过原始像素学会了玩 Atari 游戏。使用卷积神经网络(CNN)和一组超参数,DQN 在 49 场比赛中有 22 场的表现优于专业人类玩家。

Atari DQN 网络架构
这一成就引发了强化学习社区的革命:2014 年,Silver 等人发布了确定性策略梯度(DPG)算法,一年后 Lillicrap 等人通过深度确定性策略梯度(DDPG)对其进行了改进。2016 年,Schulman 等人发布了信任域策略优化(TRPO)和广义优势估计(GAE)方法,Sergey Levine 等人发表了引导策略搜索(GPS),Silver 等人展示了 AlphaGo。次年,Silver 等人展示了 AlphaZero。在那几年里,还发布了许多其他算法:双深度 Q 网络(DDQN)、优先经验回放(PER)、近端策略优化(PPO)、带经验回放的演员-评论家(ACER)、异步优势演员-评论家(A3C)、优势演员-评论家(A2C)、使用克罗内克分解信任域的演员-评论家(ACKTR)、Rainbow、Unicorn(顺便说一句,这些都是真实名称),等等。2019 年,Oriol Vinyals 等人展示了 AlphaStar 智能体在星际争霸 II 游戏中击败了职业玩家。几个月后,Jakub Pachocki 等人看到他们的 Dota-2 玩机器人团队,名为 Five,成为第一个在电子竞技游戏中击败世界冠军的 AI。
多亏了强化学习的进步,我们在二十年的时间里从解决具有 10²⁰完美信息状态的宾果游戏,到解决具有 10¹⁷⁰完美信息状态的围棋游戏,或者更好的是,解决具有 10²⁷⁰不完美信息状态的星际争霸 II 游戏。这是一个难以想象的更好的进入该领域的时间。你能想象下一个二十年会给我们带来什么吗?你将成为其中的一员吗?强化学习是一个蓬勃发展的领域,我预计其进步速度将继续。

围棋游戏:巨大的分支因子
前方的机遇
我相信,无论恐惧主义者说什么,AI 都是一个具有无限积极变革潜力的领域。回到 1750 年代,由于工业革命的开始,出现了混乱。强大的机器正在取代重复性的体力劳动,无情地取代人类。每个人都担心:这些机器能比人类工作更快、更有效、更便宜?这些机器将夺走我们所有的饭碗!我们以后靠什么生活?但事实是,许多这些工作不仅不令人满意,而且危险。
工业革命一百年后,这些变化的长期影响开始造福社区。那些通常只拥有几件衬衫和一条裤子的普通人,只需花费一小部分成本就能获得更多。的确,变革是困难的,但长期效果惠及了整个世界。
数字革命始于 20 世纪 70 年代,随着个人电脑的引入。随后,互联网改变了我们的做事方式。由于互联网,我们得到了大数据和云计算。机器学习利用这片肥沃的土壤茁壮成长,成为今天的模样。在接下来的几十年里,AI 对社会的影响和变化可能一开始难以接受,但长期效果将远远优于途中的任何挫折。我预计在几十年后,人类甚至不需要为食物、衣物或住所而工作,因为这些都是由 AI 自动生产的。我们将因富足而繁荣。

劳动力革命
随着我们继续提升机器的智能水平,某些 AI 研究人员认为我们可能会发现一个比我们自身更聪明的 AI。在这个时候,我们将解锁一个被称为奇点的现象;一个比人类更聪明的 AI 能够以更快的速度提升 AI,因为自我改进的循环不再有瓶颈,即人类。但我们必须谨慎,因为这更多的是一个理想而非实际需要担忧的方面。

奇点可能就在几十年之后
虽然我们必须始终意识到 AI 的影响并努力追求 AI 安全,但奇点目前并不是一个问题。另一方面,正如你在本书中将会看到的,当前深度强化学习存在许多问题。这些问题使我们能够更好地利用时间。
深度强化学习的适用性
你可以将任何机器学习问题表述为一个深度强化学习问题,但这并不总是一个好的主意,有多个原因。你应该了解使用深度强化学习的一般优缺点,并且能够识别哪些问题和设置适合深度强化学习,哪些则不太适合。
有哪些优缺点?
除去技术比较之外,我希望你能思考一下使用深度强化学习(DRL)在你下一个项目中的固有优势和劣势。你会发现,每个被强调的点都可能是一个优点或缺点,这取决于你试图解决的问题类型。例如,这个领域是关于让机器接管控制。这是好是坏?你能否接受让计算机为你做决策?选择游戏作为 DRL 研究环境的原因之一是:让代理在现实世界中直接训练可能会代价高昂且危险。你能想象一个自动驾驶汽车代理通过碰撞来学习不发生碰撞吗?在 DRL 中,代理将不得不犯错误。你能承担这种代价吗?你愿意承担对人类造成实际伤害的负面后果的风险吗?在你开始下一个 DRL 项目之前,考虑这些问题。

深度强化学习代理将会探索!你能承担错误吗?
你还需要考虑你的代理将如何探索其环境。例如,大多数基于价值的方 法通过随机选择动作来探索。但其他方法可以拥有更具有战略性的探索策略。现在,每种方法都有其优缺点,这是你必须熟悉的权衡。
最后,每次从头开始训练可能会令人畏惧、耗时且资源密集。然而,有几个领域研究如何利用先前获得的知识。首先,有迁移学习,这是关于将任务中获得的知 识转移到新的任务上。例如,如果你想教机器人使用锤子和螺丝刀,你可以重用“拿起锤子”任务中学习到的低级动作,并将这些知识应用到开始学习“拿起螺丝刀”的任务中。这对你来说应该是有直觉意义的,因为人类在学习新任务时不必每次都重新学习低级动作。人类似乎在学习过程中形成了动作的层次结构。分层强化学习领域试图在 DRL 代理中复制这一点。
深度强化学习的优势
DRL 是关于掌握特定任务。与旨在实现泛化的 SL 不同,RL 擅长具体、明确指定的任务。例如,每个 Atari 游戏都有一个特定的任务。DRL 代理不擅长在不同任务之间泛化行为;并不是因为训练了一个代理来玩 Pong,这个代理也能玩 Breakout。如果你天真地试图同时教你的代理 Pong 和 Breakout,你很可能会得到一个在两者上都不擅长的代理。另一方面,SL 在同时分类多个对象方面相当出色。重点是 DRL 的优势在于定义明确的单一任务。
在 DRL 中,我们使用泛化技术直接从原始感官输入中学习简单技能。泛化技术的性能、新的技巧和技巧、以及训练更深网络等,是我们近年来看到的一些主要改进。幸运的是,大多数 DL 的进步直接为 DRL 开辟了新的研究途径。
深度强化学习的弱点
当然,DRL 并不完美。你将发现的最显著问题是,在大多数问题中,代理需要数百万个样本才能学习到良好的策略。另一方面,人类可以从几次交互中学习。样本效率可能是 DRL 需要改进的顶级领域之一。我们将在几个章节中涉及这个话题,因为它是一个关键问题。

深度强化学习代理需要大量的交互样本!
DRL 的另一个问题是奖励函数和了解奖励的意义。如果人类专家将为代理定义其试图最大化的奖励,这意味着我们某种程度上“监督”了这个代理吗?这是好事吗?奖励应该尽可能密集,使学习更快,还是尽可能稀疏,使解决方案更令人兴奋和独特?
我们人类似乎并没有明确地定义奖励。通常,同一个人可以通过改变他们的观点,将一个事件视为积极或消极。此外,为像走路这样的任务设计奖励函数并不简单。我们应该针对的是前进的运动,还是不是跌倒?什么是人类行走的“完美”奖励函数?!
关于奖励信号的研究正在进行中,其中一项我特别感兴趣的是称为内在动机。内在动机允许代理仅仅出于好奇探索新的动作。使用内在动机的代理在稀疏奖励的环境中表现出改进的学习性能,这意味着我们可以保持令人兴奋和独特的解决方案。关键是如果你试图解决一个尚未建模或没有明确奖励函数的任务,你会面临挑战。
明确的双向期望
让我们接下来谈谈另一个重要的观点。接下来可以期待什么?坦白说,对我来说,这非常重要。首先,我想让你知道你可以从这本书中期待什么,这样就不会有后续的惊喜。我不想让人们认为,通过这本书,他们能够想出一个能让他们致富的交易代理。抱歉,如果这很简单,我就不会写这本书。我也期待那些想要学习的人投入必要的工作。事实上,学习将来自于我努力使概念易于理解,以及你努力去理解它们。我确实付出了努力。但是,如果你决定跳过一个你认为不必要的框,我们都会失去。
从这本书中可以期待什么?
我写这本书的目标是带你,一个机器学习爱好者,从没有任何深度强化学习经验到能够开发最先进的深度强化学习算法。为此,本书分为大约两部分。在第三章到第七章,你将学习能够从顺序和评估反馈中学习的代理,首先在孤立状态下,然后在相互作用中。在第八章到第十二章,你将深入研究核心的深度强化学习算法、方法和技巧。第一章和第二章是适用于深度强化学习的一般概念介绍,第十三章有总结性评论。
我对第一部分(第三章到第七章)的目标是让你理解“表格”强化学习。也就是说,可以彻底抽样的强化学习问题,其中不需要神经网络或任何类型的函数逼近。第三章是关于强化学习的顺序方面和时间信用分配问题。然后,我们将在第四章中单独研究从评估反馈中学习以及探索与利用之间的权衡。最后,你将学习可以同时处理这两个挑战的方法。在第五章,你研究学习估计固定行为结果的代理。第六章处理学习改进行为,第七章展示了使强化学习更有效率和高效的技巧。
我对第二部分(第八章到第十二章)的目标是让你掌握核心深度强化学习算法的细节。我们将深入探讨细节;你可以确信这一点。你将了解许多不同类型的代理,从基于价值和策略的到演员-评论家方法。在第八章到第十章,我们将深入研究基于价值的深度强化学习。在第十一章,你将学习基于策略的深度强化学习和演员-评论家,第十二章是关于确定性策略梯度(DPG)方法、软演员-评论家(SAC)和近端策略优化(PPO)方法。
这些章节中的示例在相同类型的代理之间重复出现,以便更方便地比较和对比代理。你仍然探索根本不同类型的问题,从小型连续到基于图像的状态空间,以及从离散到连续的动作空间。但,本书的重点不是关于建模问题,这是它自己的技能;相反,重点是解决已经建模的环境。

深度强化学习中不同算法方法的比较
如何充分利用这本书
为了深入理解强化学习,你需要带来一些东西。你需要具备一些机器学习和深度学习的基本知识。你需要熟悉 Python 代码和简单的数学。最重要的是,你必须愿意投入努力。
我假设读者对机器学习(ML)有扎实的理论基础。你应该知道机器学习是什么,而不仅仅是本章所涵盖的内容;你应该知道如何训练简单的监督学习(SL)模型,比如 Iris 或 Titanic 数据集;你应该熟悉深度学习(DL)的概念,如张量和矩阵;你应该至少训练过一个深度学习模型,比如在 MNIST 数据集上训练一个卷积神经网络(CNN)。
本书专注于强化学习(DRL)主题,其中并没有孤立的学习深度学习(DL)。外面有许多有用的资源你可以利用。但再次强调,你需要有基本的理解;如果你之前训练过卷积神经网络(CNN),那么你没问题。否则,我强烈建议你在开始阅读本书的第二部分之前,先跟随几个深度学习(DL)教程。
我还假设读者对 Python 代码比较熟悉。Python 是一种相对清晰的编程语言,易于理解,不熟悉它的人往往只需阅读就能有所收获。现在,我的观点是,你应该感到舒适,愿意并期待阅读代码。如果你不阅读代码,你将错过很多东西。
同样,这本书中有很多数学公式,这也是一件好事。数学是完美的语言,没有任何东西可以取代它。然而,我要求人们能够适应数学,愿意阅读,仅此而已。我展示的公式都有大量的注释,以便那些“不热衷于数学”的人也能利用这些资源。
最后,我假设你愿意付出努力。我的意思是,你真的想学习深度强化学习(DRL)。如果你决定跳过数学框、Python 代码片段、某个部分、一页、一章,或者任何其他内容,你将错过很多相关信息。为了最大限度地利用这本书,我建议你从头到尾阅读整本书。由于格式不同,图表和侧边栏是本书主要叙述的一部分。
此外,请确保运行本书的源代码(下一节将提供更多关于如何操作的细节),并尝试扩展你发现最有趣的代码。
深度强化学习开发环境
除了这本书,你还提供了一个完全测试的环境和代码来重现我的结果。我创建了一个 Docker 镜像和几个 Jupyter 笔记本,这样你就不必费心安装包和配置软件,或者复制粘贴代码。唯一的前提是 Docker。请按照github.com/mimoralea/gdrl上的说明运行代码。这相当直接。
代码是用 Python 编写的,我大量使用了 NumPy 和 PyTorch。我选择 PyTorch 而不是 Keras 或 TensorFlow,因为我发现 PyTorch 是一个“Pythonic”库。如果你使用过 NumPy,使用 PyTorch 会感觉自然,而像 TensorFlow 这样的库则感觉像是一个全新的编程范式。现在,我的意图并不是开始一场“PyTorch 与 TensorFlow”的辩论。但根据我使用这两个库的经验,PyTorch 是一个更适合研究和教学的库。
深度强化学习(DRL)涉及算法、方法、技术、技巧等等,因此我们重写 NumPy 或 PyTorch 库是没有意义的。但在这本书中,我们从头开始编写 DRL 算法;我不是教你如何使用深度强化学习库,如 Keras-RL、Baselines 或 RLlib。我想让你学习深度强化学习,因此我们编写了 DRL 代码。在我教授强化学习的这些年里,我发现编写强化学习代码的人更有可能理解强化学习。现在,这本书也不是关于 PyTorch 的;没有单独的 PyTorch 回顾或类似的内容,只是随着我们的进展,我会解释我使用的 PyTorch 代码。如果你对深度学习概念有些熟悉,你将能够跟随这本书中使用的 PyTorch 代码。不用担心,在你到达这本书之前,你不需要单独的 PyTorch 资源。随着我们的进展,我会详细解释一切。
至于我们用于训练代理的环境,我们使用流行的 OpenAI Gym 软件包以及我为这本书开发的一些其他库。但我们也不会深入探讨 Gym 的细节。只需知道 Gym 是一个提供强化学习代理训练环境的库。除此之外,记住我们的重点是强化学习算法,解决方案,而不是环境或建模问题,这些不用说也是至关重要的。
由于你应该熟悉深度学习(DL),我假设你知道什么是图形处理单元(GPU)。深度强化学习(DRL)架构不需要像深度学习模型那样常见的计算水平。因此,虽然使用 GPU 是个好主意,但并非必需。相反,与深度学习模型不同,一些深度强化学习代理(agent)大量使用中央处理单元(CPU)和线程数。如果你打算投资购买机器,确保考虑到 CPU 的功率(好吧,技术上讲,是核心数,而不是速度)。正如你稍后将会看到的,某些算法会大规模并行化处理,在这种情况下,成为瓶颈的是 CPU,而不是 GPU。然而,代码在容器中运行良好,无论你的 CPU 或 GPU 如何。但是,如果你的硬件非常有限,我建议检查一下云平台。我见过一些服务,例如 Google Colab,提供免费的深度学习硬件。
摘要
深度强化学习具有挑战性,因为智能体必须从同时具有顺序性、评价性和样本性的反馈中学习。从顺序性反馈中学习迫使智能体学习如何平衡短期和长期目标。从评价性反馈中学习使智能体学会平衡信息的收集和利用。从样本性反馈中学习迫使智能体从旧经验推广到新经验。
人工智能,强化学习所属的计算机科学主要领域,是一门致力于创建显示类似人类智能的计算机程序的学科。这个目标与其他许多学科,如控制理论和运筹学,是共享的。机器学习是人工智能中最受欢迎和最成功的方法之一。强化学习是机器学习的三个分支之一,与监督学习和无监督学习并列。深度学习作为一种机器学习方法,与任何特定分支无关,但其力量有助于推动整个机器学习社区的发展。
深度强化学习是使用多层强大的函数逼近器,即神经网络(深度学习),在不确定性下解决复杂顺序决策问题的方法。深度强化学习在许多控制问题中表现良好,但无论如何,我们必须牢记,对于关键决策,释放人类控制不应被轻视。深度强化学习的核心需求包括具有更好样本复杂度的算法、性能更好的探索策略和安全的算法。
尽管如此,深度强化学习的未来光明,随着技术的成熟,也许会面临一些危险,但更重要的是,这个领域具有潜力,你应该感到兴奋并感到有必要发挥你的最佳水平,踏上这段旅程。成为这样一场巨大变革的一部分的机会,每隔几代人才能出现一次。你应该为生活在这样的时代而感到高兴。现在,让我们成为其中的一员。
到现在为止,你已经
-
理解了深度强化学习是什么以及它与其他机器学习方法相比如何
-
意识到深度强化学习领域的最新进展,并直观地理解它具有应用于广泛问题的潜力
-
对这本书的内容有所了解,并知道如何从中获得最大收益
| 在自己的工作上努力并分享你的发现 |
|---|
| | 每章结束时,我会提供几个想法,告诉你如何将所学内容提升到下一个层次。如果你愿意,可以将你的结果与世界分享,并确保查看其他人所做的事情。这是一个双赢的局面,希望你能充分利用它。
-
#gdrl_ch01_tf01: 监督学习、无监督学习和强化学习是机器学习的三大重要分支。虽然了解它们之间的区别至关重要,但同样重要的是了解它们之间的相似之处。写一篇分析这些不同方法如何比较以及如何将它们结合起来解决人工智能问题的文章。所有分支都追求同一个目标:创造通用人工智能,对我们所有人来说,更好地理解如何使用可用的工具至关重要。
-
#gdrl_ch01_tf02: 如果你没有机器学习或计算机科学背景,但对这本书提供的内容感兴趣,这并不会让我感到惊讶。一个重要的贡献是发布来自研究决策的其他领域的资源。你有运筹学背景吗?心理学、哲学或神经科学背景?控制理论?经济学?你为什么不创建一个资源列表、博客文章、YouTube 视频书籍或其他任何形式的列表,并与我们这些也在研究决策的人分享呢?
-
#gdrl_ch01_tf03: 本章的部分文本通过图形、表格和其他形式可以更好地解释。例如,我谈到了不同类型的强化学习代理(基于价值的、基于策略的、演员-评论家、基于模型的、无梯度)。你为什么不抓取密集的文本,提炼知识,并将你的总结与世界分享呢?
-
#gdrl_ch01_tf04: 在每一章中,我都在使用最后一个标签作为通用的标签。请随意使用这个标签来讨论与本章相关的任何其他内容。没有比为自己创造作业更令人兴奋的了。确保分享你打算调查的内容和你的结果。
用你的发现写一条推文,@mimoralea(我会转发),并使用此列表中的特定标签来帮助感兴趣的人找到你的结果。没有正确或错误的结果;你分享你的发现并检查他人的发现。利用这个机会社交、贡献并让自己脱颖而出!我们正在等待你!以下是一条推文示例:“嘿,@mimoralea。我创建了一篇博客文章,列出了学习深度强化学习的资源。查看链接: #gdrl_ch01_tf01”。我会确保转发并帮助他人找到你的作品。|
2 强化学习的数学基础
在本章中
-
你将了解强化学习的核心组件。
-
你将学会使用称为马尔可夫决策过程的数学框架,将顺序决策问题表示为强化学习环境。
-
你将从头开始构建环境,强化学习代理将在后续章节中学习解决这些环境。
人类的历史一直是在与恶劣环境的斗争中。我们最终达到了一个可以开始主宰我们环境的阶段。 ... 一旦我们理解了这个事实,我们的数学兴趣必然会在许多领域从描述性分析转向控制理论。
— 理查德·贝尔曼 美国应用数学家,IEEE 荣誉奖章获得者
你拿起这本书,尽管时间有限,还是决定再读一章。教练在今晚的比赛中将最佳球员放在板凳上,无视媒体的批评。一位家长投入大量时间和耐心,教孩子良好的行为举止。这些都是不确定情况下复杂顺序决策的例子。
我想提醒大家这个短语中的一些关键词:不确定情况下的复杂顺序决策。第一个词,复杂,指的是代理可能在具有广阔动作空间的环境中学习。在教练的例子中,即使你发现你的最佳球员需要时不时地休息,也许在与特定对手的比赛中休息他们比与其他对手更好。由于我们是从样本反馈中学习的,因此学会准确泛化是具有挑战性的。
我使用的第二个词是 顺序,这个词指的是在许多问题中,存在延迟的后果。在教练的例子中,再次,假设教练在赛季中途的一场比赛中让最佳球员休息,看似无关紧要。但是,如果休息球员的行动降低了他们的士气和表现,这种影响仅在决赛中显现出来?换句话说,如果实际后果是延迟的?事实上,由于我们是从顺序反馈中学习的,因此为过去的决策分配信用是具有挑战性的。
最后,词 不确定性 指的是我们不知道世界的实际运作方式,以了解我们的行动如何影响它;一切都被留给了我们的解释。假设教练确实让最佳球员休息,但他们下一场比赛受伤了。是休息的决定导致球员受伤,因为球员变得不健康吗?如果伤病在整个赛季中成为团队的动力,并且球队最终赢得了决赛?再次,休息是正确的决定吗?这种不确定性引发了探索的需求。在探索和利用之间找到适当的平衡是具有挑战性的,因为我们是从评估反馈中学习的。
在本章中,你将学习如何使用一种称为马尔可夫决策过程(MDPs)的数学框架来表示这类问题。MDPs 的一般框架使我们能够以 RL 代理可以与之交互并仅通过经验学习解决的方式,在不确定性下对几乎任何复杂的顺序决策问题进行建模。
在第三章中,我们将深入探讨从顺序反馈中学习的挑战,然后在第四章中探讨从评估反馈中学习的挑战,接着在第五章到第七章中探讨同时从顺序和评估反馈中学习的挑战,最后在第八章到第十二章中,我们将把复杂性加入其中。
强化学习组件
强化学习中的两个核心组件是代理和环境。代理是决策者,是问题的解决方案。环境是问题的表示。强化学习与其他机器学习方法的根本区别之一是代理和环境之间的交互;代理试图通过行动影响环境,而环境则对代理的行动做出反应。

强化学习-交互循环
| 米格尔的类比故事:中国农民的寓言 | |
|---|---|
| 有一个很好的寓言,说明了同时具有顺序性、评价性和样本性的反馈是多么难以解读。这个寓言是这样的:一位中国农民得到了一匹马,不久后马跑了。邻居说:“哎呀,真伤心。这是个坏消息。”农民回答说:“好消息,坏消息,谁能说呢?”马又回来了,还带来了一匹马。邻居说:“真幸运。这是个好消息。”农民回答说:“好消息,坏消息,谁能说呢?”农民把第二匹马给了他的儿子,儿子骑马,然后摔了下来,严重摔断了腿。邻居说:“为你的儿子感到抱歉。这绝对是坏消息。”农民回答说:“好消息,坏消息,谁能说呢?”大约一周后,皇帝的人来了,把所有健康的年轻人带走参加战争。农民的儿子被免除了。所以,是好消息还是坏消息?谁能说呢?有趣的故事,对吧?在生活中,确定事件和我们的行为的长期后果是具有挑战性的。我们常常发现不幸导致了我们后来的好运,或者好运导致了我们后来的不幸。尽管这个故事可以解释为“美在心中”,但在强化学习中,我们假设我们采取的行动和世界发生的事情之间存在相关性。只是这些关系如此复杂,以至于人类很难确定地连接这些点。但也许这正是计算机可以帮助我们弄清楚的事情。兴奋,对吧?记住,当反馈同时具有评价性、顺序性和样本性时,学习是一个难题。而深度强化学习是解决这类问题的计算方法。欢迎来到深度强化学习的世界! |
问题、代理和环境的示例
以下是对 RL 问题、代理、环境、可能动作和观察的简略示例:
-
问题:你正在训练你的狗坐下。代理:做出决策的大脑部分。环境:你的狗、奖励、狗的爪子、吵闹的邻居等等。动作:和你的狗说话。等待狗的反应。移动你的手。展示奖励。给予奖励。抚摸。观察:你的狗正在注意你。你的狗感到疲倦。你的狗正在离开。你的狗按照命令坐下。
-
问题:你的狗想要你手中的奖励。代理:做出决策的狗脑部分。环境:你、奖励、狗的爪子、吵闹的邻居等等。动作:盯着主人看。吠叫。跳向主人。试图偷奖励。跑。坐下。观察:主人一直在大声对狗说话。主人正在展示奖励。主人正在隐藏奖励。主人给了狗奖励。
-
问题:一个投资股市的交易代理。代理:内存和 CPU 中执行的 DRL 代码。环境:你的互联网连接,代码运行的机器,股价,地缘政治不确定性,其他投资者,日交易者等等。动作:卖出y公司的n股。买入y公司的n股。持有。观察:市场正在上涨。市场正在下跌。两个强国之间存在经济紧张。大陆有战争的危险。全球大流行正在整个世界造成破坏。
-
问题 你正在开车。代理:你大脑中做出决策的部分。环境:你的汽车的品牌和型号,其他汽车,其他司机,天气,道路,轮胎等等。动作:通过x转向,通过y加速。通过z刹车。打开前灯。除雾窗户。播放音乐。观察:你正在接近目的地。Main Street 上交通堵塞。旁边的车正在鲁莽驾驶。开始下雨了。前面有警察在开车。
正如你所见,问题可以采取多种形式:从需要长期思考和广泛一般知识的高级决策问题,例如投资股市,到低级控制问题,其中地缘政治紧张似乎没有直接作用,例如开车。
此外,你也可以从多个代理的角度来表示一个问题。在狗训练的例子中,实际上有两个代理,每个代理都感兴趣于不同的目标,并试图解决不同的问题。
让我们独立地深入探讨每个组件。
代理:决策者
正如我在第一章中提到的,这本书的整个内容都是关于代理的,除了这一章是关于环境的。从第三章开始,你将深入挖掘代理的内部运作、它们的组件、过程以及创建有效且高效的代理的技术。
目前,你需要了解关于代理的唯一重要的事情是,他们是强化学习大图景中的决策者。他们有自己内部的组件和过程,这就是使每个代理都独特并且擅长解决特定问题的原因。
如果我们放大来看,我们会发现大多数代理都有一个三步过程:所有代理都有一个交互组件,用于收集学习数据;所有代理都会评估他们的当前行为;所有代理都会在他们的内部组件中改进某些东西,这使他们能够改进(或者至少尝试改进)他们的整体表现。

每个强化学习代理都会经历的三步内部过程
我们将在下一章继续讨论代理的内部运作。现在,让我们讨论一种表示环境的方法,它们看起来如何,以及我们应该如何建模它们,这是本章的目标。
环境:其他所有事物
大多数现实世界的决策问题都可以表达为强化学习环境。在强化学习中表示决策过程的一种常见方式是使用一种称为马尔可夫决策过程(MDPs)的数学框架来建模问题。在强化学习中,我们假设所有环境都在底层运行着 MDP。无论是 Atari 游戏、股市、自动驾驶汽车、你的另一半,还是其他任何问题,每个问题都在底层运行着 MDP(至少在强化学习世界中是这样,无论是对是错)。
环境由与问题相关的变量集表示。这个变量集可以取的所有可能值的组合被称为状态空间。状态是在任何给定时间变量所取的特定值集。
代理可能无法访问实际环境的真实状态;然而,无论如何,代理都可以从环境中观察到某些东西。代理在任何给定时间感知到的变量集被称为观测。
这些变量可以取的所有可能值的组合是观测空间。要知道在强化学习社区中,状态和观测是可互换使用的术语。这是因为通常代理被允许看到环境的内部状态,但这并不总是如此。在这本书中,我也将状态和观测互换使用。但你需要知道,尽管强化学习社区经常互换使用这些术语,状态和观测之间可能存在差异。
在每个状态,环境都会提供一组代理可以选择的动作。通常,动作集与动作空间相同。
代理试图通过这些动作影响环境。环境可能会根据代理的动作改变状态。负责这种转换的函数被称为转换函数。
在转换之后,环境会发出一个新的观测。环境还可能提供一个奖励信号作为响应。负责这种映射的函数被称为奖励函数。转换和奖励函数的集合被称为环境的模型。
| 具体示例:老丨虎丨机漫步环境 | |
|---|---|
让我们通过第一个强化学习环境来具体化这些概念。我为这本书创建了一个非常简单的环境;我称之为“老丨虎丨机漫步”(BW)。BW 是一个简单的网格世界(GW)环境。GWs 是研究强化学习算法的常见环境类型,它们是任何大小的网格。GWs 可以有任何你想象得到的模型(转换和奖励函数),并且可以提供任何类型的动作。但是,它们通常都为智能体提供移动动作:左、下、右、上(或西、南、东、北,这更精确,因为智能体没有方向,通常也没有对整个网格的可见性,但基本方向也可能更令人困惑)。当然,每个动作都对应其逻辑转换:左移动到左边,右移动到右边。此外,它们通常都有一个完全可观察的离散状态和观察空间(即状态等于观察),用整数表示智能体的单元格 ID 位置。一个“漫步”是网格世界环境的一个特殊情况,只有一个行。实际上,我所说的“漫步”更常见地被称为“走廊”。但在本书中,我使用“漫步”一词来指代所有只有一个行的网格世界环境。“老丨虎丨机漫步”(BW)是一个有三个状态的环境,但只有一个非终止状态。只有一个非终止状态的环境被称为“老丨虎丨机”环境。“老丨虎丨机”在这里是类比于老丨虎丨机,也称为“单臂老丨虎丨机”;它们有一个臂,如果你喜欢赌博,可以让你口袋空空,就像一个强盗一样。BW 环境只有两个动作可用:一个右。奖励信号是在落在最右侧单元格时为+1,否则为 0。智能体从中间单元格开始。![]() |
BW 环境的图形表示如下。

老丨虎丨机漫步图
我希望这能引发一些问题,但你会发现答案贯穿整章。例如,为什么终止状态有转换到自身的动作:这似乎是浪费的,不是吗?还有其他问题吗?比如,如果环境是随机的呢?一个“随机”的环境究竟是什么?!继续阅读。
我们也可以用表格形式表示这个环境。
| 状态 | 动作 | 下一个状态 | 转换概率 | 奖励信号 |
|---|---|---|---|---|
| 0 (洞) | 0 (左) | 0 (洞) | 1.0 | 0 |
| 0 (洞) | 1 (右) | 0 (洞) | 1.0 | 0 |
| 1 (开始) | 0 (左) | 0 (洞) | 1.0 | 0 |
| 1 (开始) | 1 (右) | 2 (目标) | 1.0 | +1 |
| 2 (目标) | 0 (左) | 2 (目标) | 1.0 | 0 |
| 2 (目标) | 1 (右) | 2 (目标) | 1.0 | 0 |
有趣,对吧?让我们看看另一个简单的例子。
| 一个具体的例子老丨虎丨机滑动漫步环境 | |
|---|---|
好的,那么我们如何使这个环境变得随机呢?假设步行的表面是滑的,每个动作有 20%的概率将代理送回。我把这个环境称为老丨虎丨机滑动步走(BSW)。BSW 仍然是一个单行网格世界,一条步行道,只有左和右的动作可用。再次强调,三个状态和两个动作。奖励与之前相同,*(来自自身),否则为零。然而,转移函数不同:80%的时间代理移动到目标单元格,20%的时间移动到相反方向。这个环境的描述如下。 老丨虎丨机滑动步走(BSW)环境与 BW 环境相同!有趣的是……我们如何知道动作效果是随机的?我们如何表示这个问题的“滑”的部分?表格表示可以帮助我们做到这一点。 |
bSW 环境的图形表示如下。

老丨虎丨机滑动步走图
看看现在的转移函数有何不同?BSW 环境有一个随机的转移函数。现在让我们也以表格形式表示这个环境。
| 状态 | 动作 | 下一个状态 | 转移概率 | 奖励信号 |
|---|---|---|---|---|
| 0 (洞) | 0 (左) | 0 (洞) | 1.0 | 0 |
| 0 (洞) | 1 (右) | 0 (洞) | 1.0 | 0 |
| 1 (开始) | 0 (左) | 0 (洞) | 0.8 | 0 |
| 1 (开始) | 0 (左) | 2 (目标) | 0.2 | +1 |
| 1 (开始) | 1 (右) | 2 (目标) | 0.8 | +1 |
| 1 (开始) | 1 (右) | 0 (洞) | 0.2 | 0 |
| 2 (目标) | 0 (左) | 2 (目标) | 1.0 | 0 |
| 2 (目标) | 1 (右) | 2 (目标) | 1.0 | 0 |
此外,我们不必局限于思考具有离散状态和动作空间的环境,甚至包括走廊(我们将在下一章深入讨论)或老丨虎丨机(BSW 环境),或者网格世界。将环境表示为马尔可夫决策过程(MDP)是一种出人意料强大且直接的方法,用于在不确定性下建模复杂的顺序决策问题。
这里有一些由底层 MDP 驱动的环境的更多示例。
| 描述 | 观察空间 | 样本观察 | 动作空间 | 样本动作 | 奖励函数 |
|---|---|---|---|---|---|
| 更热,更冷:使用提示猜测随机选择的数字。 | 整数范围 0–3.0 表示尚未提交猜测,1 表示猜测低于目标,2 表示猜测等于目标,3 表示猜测高于目标。 | 2 | 浮点数从–2000.0–2000.0。代理猜测的浮点数。 | –909.37 | 奖励是代理猜测距离目标的平方百分比。 |
| 倒立摆:在车上保持杆的平衡。 | 一个包含四个元素的向量,范围从 [–4.8, –Inf, –4.2, –Inf] 到 [4.8, Inf, 4.2, Inf]。第一个元素是车的位置,第二个是车的速度,第三个是杆的弧度角,第四个是杆尖的速度。 | [–0.16, –1.61, 0.17, 2.44] | 整数范围 0–1.0 表示向左推车,1 表示向右推车。 | 0 | 每走一步(包括终止步)的奖励是 1。 |
| 月球着陆器:将着陆器导航到着陆平台。 | 一个包含八个元素的向量,范围从 [–Inf, –Inf, –Inf, –Inf, –Inf, –Inf, 0, 0] 到 [Inf, Inf, Inf, Inf, Inf, Inf, 1, 1]。第一个元素是 x 位置,第二个是 y 位置,第三个是 x 速度,第四个是 y 速度,第五个是车辆的角度,第六个是角速度,最后两个值是布尔值,表示腿与地面的接触。 | [0.36 , 0.23, –0.63, –0.10, –0.97, –1.73, 1.0, 0.0] | 整数范围 0–3。No-op(什么都不做),点火左引擎,点火主引擎,点火右引擎。 | 2 | 着陆的奖励是 200。从顶部移动到着陆平台、碰撞或静止、每个腿接触地面以及点火引擎都会有奖励。 |
| Pong:将球击过对手,并避免让球通过你。 | 一个形状为 210, 160, 3 的张量。值从 0–255。表示游戏屏幕图像。 | [[[246, 217, 64], [ 55, 184, 230], [ 46, 231, 179], ..., [ 28, 104, 249], [ 25, 5, 22], [173, 186, 1]], ...]] | 整数范围 0–5。动作 0 是 No-op,1 是开火,2 是向上,3 是向右,4 是向左,5 是向下。注意有些动作根本不影响游戏。实际上,桨只能上下移动,或者不移动。 | 3 | 当球越过对手时,奖励是 1,当你的代理的桨错过球时,奖励是 –1。 |
| 人形机器人:让机器人尽可能快地跑,并且不摔倒。 | 一个包含 44 个元素(或更多,取决于实现)的向量。值从 –Inf 到 Inf。表示机器人关节的位置和速度。 | [0.6, 0.08, 0.9, 0. 0, 0.0, 0.0, 0.0, 0.0, 0.045, 0.0, 0.47, ... , 0.32, 0.0, –0.22, ... , 0.] | 一个包含 17 个元素的向量。值从 –Inf 到 Inf。表示施加到机器人关节的力。 | [–0.9, –0.06, 0.6, 0.6, 0.6, –0.06, –0.4, –0.9, 0.5, –0.2, 0.7, –0.9, 0.4, –0.8, –0.1, 0.8, –0.03] | 奖励基于前进运动,并带有轻微的惩罚以鼓励自然步态。 |
注意我没有将转换函数添加到这个表中。这是因为,虽然你可以查看实现某些环境动态的代码,但其他实现并不容易访问。例如,倒立摆环境的转换函数是一个小的 Python 文件,定义了车的质量和杆,并实现了基本的物理方程,而像 Pong 这样的 Atari 游戏的动态则隐藏在一个 Atari 模拟器和相应的游戏特定 ROM 文件中。
注意,我们在这里试图表示的是环境以某种方式“反应”智能体的行动,甚至可能忽略智能体的行动。但最终,有一个内部过程是不确定的(除了本章和下一章)。为了在马尔可夫决策过程(MDP)中表示与环境交互的能力,我们需要状态、观察、行动、转移和奖励函数。

环境处理过程是智能体行动的结果
智能体-环境交互周期
环境通常有一个定义良好的任务。这个任务的目标是通过奖励信号来定义的。奖励信号可以是密集的、稀疏的或介于两者之间。当你设计环境时,奖励信号是训练你的智能体的方式。越密集,智能体将获得更多的监督,学习速度越快,但你会向智能体注入更多的偏差,智能体出现意外行为的可能性就越小。越稀疏,监督越少,因此,出现新的、新兴行为的机会就越高,但智能体学习的时间就会更长。
智能体与环境之间的交互会持续几个周期。每个周期被称为时间步。时间步是时间的单位,可以是毫秒、秒、1.2563 秒、分钟、一天或任何其他时间段。
在每个时间步,智能体观察环境,采取行动,并接收新的观察和奖励。请注意,尽管奖励可以是负值,但在强化学习领域,它们仍然被称为奖励。观察(或状态)、行动、奖励和新的观察(或新状态)的集合被称为经验元组。
智能体试图解决的问题可能有或没有自然的结束。有自然结束的任务,如游戏,被称为周期性任务。没有自然结束的任务,如学习前进运动,被称为持续任务。从周期性任务的开始到结束的时间步序列称为周期。智能体可能需要几个时间步和周期来学习解决任务。单个周期中收集到的奖励总和称为回报。智能体通常被设计成最大化回报。在持续任务中经常添加时间步限制,使它们成为周期性任务,智能体可以最大化回报。
每个经验元组都有学习和改进性能的机会。智能体可能有一到多个组件来辅助学习。智能体可能被设计成学习从观察到行动的映射,称为策略。智能体可能被设计成学习从观察到新的观察和/或奖励的映射,称为模型。智能体可能被设计成学习从观察(和可能的行动)到奖励到估计(回报的一部分)的映射,称为价值函数。
在本章的剩余部分,我们将暂时放下智能体和交互,深入探讨环境和内部 MDP。在第三章中,我们将重新引入智能体,但会有用于学习的神经网络。
MDPs:环境的引擎
让我们在了解构成它们的组件时,为几个环境构建马尔可夫决策过程(MDPs)。我们将创建代表 MDPs 的 Python 字典,这些字典来自对问题的描述。在下一章中,我们将研究在 MDPs 上规划算法。这些方法可以设计出 MDPs 的解决方案,并使我们能够找到本章中所有问题的最优解。
能够自己构建环境是一项重要的技能。然而,你经常会发现别人已经为某些环境创建了 MDP。此外,环境的动态通常隐藏在模拟引擎背后,过于复杂,无法详细检查;某些动态甚至无法访问,隐藏在现实世界背后。实际上,强化学习智能体不需要知道问题的精确 MDP 来学习鲁棒的行为,但了解你,因为智能体通常设计时假设即使无法访问,MDP 也在幕后运行。
| 一个具体示例:冰冻湖环境 | |
|---|---|
这是另一个更具挑战性的问题,我们将在本章中为它构建一个 MDP。这个环境被称为冰冻湖(FL)。FL 是一个简单的离散状态和动作空间。然而,这次有四种动作可供选择:向左移动、向下移动、向右移动或向上移动。在 FL 环境中的任务与 BW 和 BWS 环境中的任务相似:从起始位置移动到目标位置,同时避免掉入坑中。挑战与bSW相似,因为 FL 环境的表面很滑,毕竟它是一个冰冻湖。但环境本身更大。让我们看看 FL 的描述图。 冰冻湖(FL)环境 FL 是一个 4×4 的网格(它有 16 个单元格,id 为 0-15)。每个新剧集开始时,智能体都会出现在起始单元格中。到达目标单元格会得到+1 的奖励;其他任何情况都是 0。由于表面很滑,智能体的移动只有三分之一的预期效果。其他三分之二均匀地分布在正交方向上。例如,如果智能体选择向下移动,有 33.3%的概率它会向下移动,33.3%的概率它会向左移动,33.3%的概率它会向右移动。湖周围有一道栅栏,所以如果智能体试图离开网格世界,它会弹回到它试图移动的单元格。湖中有四个洞。如果智能体掉入其中一个洞,游戏就结束了。你准备好开始构建这些动态的表示了吗?我们需要一个 Python 字典来表示这里描述的 MDP。让我们开始构建 MDP。 |
状态:环境的特定配置
状态是问题的唯一且自包含的配置。所有可能状态的集合,即状态空间,定义为集合s。状态空间可以是有限的或无限的。但请注意,状态空间与组成单个状态的一组变量不同。这个其他集合必须始终是有限的,并且从状态到状态大小恒定。最终,状态空间是一组集合。内部集合必须大小相等且有限,因为它包含表示状态的变量的数量,但外部集合可以根据内部集合的元素类型是无限的。

状态空间:一组集合
对于 BW、BSW 和 FL 环境,状态由一个包含代理在任意给定时间所在细胞 ID 的单个变量组成。代理的位置细胞 ID 是一个离散变量。但状态变量可以是任何类型,变量集可以大于一个。我们可以有一个欧几里得距离,这将是一个连续变量,并且状态空间是无限的;例如,2.124、2.12456、5.1、5.1239458 等等。我们也可以有多个变量定义状态,例如,x 轴和 y 轴上离目标多少个细胞。这将代表单个状态的两个变量。这两个变量都是离散的,因此状态空间是有限的。然而,我们也可以有混合类型的变量;例如,一个可以是离散的,另一个可以是连续的,另一个可以是布尔值。
对于 BW、BSW 和 FL 环境的状态表示,状态空间的大小分别为 3、3 和 16。给定我们有 3、3 或 16 个细胞,代理在任意给定时间可以处于任何状态,因此状态空间中有 3、3 和 16 种可能的状态。我们可以从零开始设置每个细胞的 ID,从左到右,从上到下。
在 FL 中,我们从零到 15 设置 ID,从左到右,从上到下。你可以以任何其他方式设置 ID:随机顺序,按邻近性分组细胞,或者任何其他方式。这取决于你;只要你在整个训练过程中保持一致,它就会工作。然而,这种表示是足够的,并且效果很好,所以我们将使用它。

在 FL 中,状态包含一个变量,表示在任意给定时间步中代理所在的细胞 ID。
在 MDPs 的情况下,状态是完全可观察的:我们可以在每个时间步看到环境的内部状态,也就是说,观察和状态是相同的。部分可观察马尔可夫决策过程(POMDPs)是一个更通用的框架,用于建模环境,其中观察结果,仍然依赖于环境的内部状态,是代理唯一能看到的东西,而不是状态。请注意,对于 BW、BSW 和 FL 环境,我们正在创建一个 MDP,因此代理将能够观察到环境的内部状态。
状态必须包含所有必要的变量,使它们独立于所有其他状态。在 FL 环境中,你只需要知道智能体的当前状态,就可以告诉它的下一个可能状态。也就是说,你不需要智能体访问过的状态历史。你知道从状态 2,智能体只能转移到状态 1、3、6 或 2,而且这无论智能体的先前状态是 1、3、6 还是 2 都是正确的。
给定当前状态和动作,下一个状态的概率与交互历史无关。MDP 的这种无记忆特性被称为马尔可夫性质:在两次不同场合,给定相同的动作a,从状态s转移到另一个状态s的概率是相同的,无论之前遇到的所有状态或动作。
| 显示数学马尔可夫性质 | |
|---|---|
![]() |
但你为什么关心这个?嗯,在我们已经探索的环境中,这并不明显,也不那么重要。但是,因为大多数 RL(和 DRL)智能体都是设计来利用马尔可夫假设的,你必须确保你向智能体提供必要的变量,使其尽可能紧密地保持(完全保持马尔可夫假设是不切实际的,也许是不可能的)。
例如,如果你正在设计一个学习如何着陆宇宙飞船的智能体,智能体必须接收所有表示速度的变量以及其位置。仅位置信息不足以安全着陆宇宙飞船,而且因为你必须假设智能体是无记忆的,所以你需要向智能体提供比仅其远离着陆点的 x、y、z 坐标更多的信息。
但是,你可能知道加速度是速度相对于位置导数的关系:导数。你可能也知道你可以继续对加速度求导。为了使 MDP 完全马尔可夫化,你需要求多深?这更多的是一种艺术而不是科学:你添加的变量越多,训练智能体所需的时间就越长,但变量越少,提供给智能体的信息不足的可能性就越高,学习任何有用的东西就越困难。对于宇宙飞船的例子,通常位置和速度是足够的,而对于网格世界环境,智能体的状态 id 位置就足够了。
MDP 中所有状态的集合用si表示,来自一个概率分布。这个分布可以是任何东西,但必须在整个训练过程中保持固定:也就是说,概率必须从训练的第一集到最后一集,以及对于智能体评估都是相同的。
有一个独特的状态称为吸收状态或终端状态,所有非终端状态的集合用s表示。现在,虽然通常的做法是创建一个单一的终端状态(一个汇状态),所有终端转换都指向它,但这并不总是这样实现的。你更常看到的是多个终端状态,这是可以的。在底层,如果你让所有终端状态都按预期行为,这实际上并不重要。
如预期的那样?是的。终端状态是一种特殊状态:它必须具有所有可用的动作,以概率 1 转换到自身,并且这些转换必须不提供任何奖励。请注意,我指的是从终端状态到终端状态的转换,而不是终端状态本身。
通常情况下,一个局部的结束会提供非零的奖励。例如,在一场棋局中,你可能赢、输或平局。一个逻辑上的奖励信号可以是+1、-1 和 0,分别对应赢、输和平局。但是,这是一个兼容性约定,允许所有算法收敛到相同的解决方案,使得所有动作在终端状态转换到自身时,概率为 1 且奖励为 0。否则,你可能会遇到无限和的问题,以及可能完全无法工作的算法。还记得 BW 和 BSW 环境中的这些终端状态吗?
例如,在 FL 环境中,只有一个起始状态(即状态 0)和五个终端状态(或者五个转换到单个终端状态的状态,根据你的喜好)。为了清晰起见,我在插图和代码中使用多个终端状态(5、7、11、12 和 15)的约定;再次强调,每个终端状态都是一个单独的终端状态。

冰冻湖环境中的状态
动作:影响环境的一种机制
MDPs 提供了一组依赖于状态的行动 A。也就是说,可能存在在某个状态下不允许的动作——实际上,A 是一个以状态为参数的函数;即,A(s)。这个函数返回状态 s 的可用动作集合。如果需要,你可以定义这个集合在状态空间中是常数;即,在每一个状态下都有所有动作可用。你也可以将状态-动作对的所有转换设置为 0,如果你想在一个给定的状态下拒绝一个动作。你也可以将状态 s 和动作 a 的所有转换设置为相同的 s,以表示动作 a 是一个不干预或无操作动作。
就像状态一样,动作空间可能是有限的或无限的,单个动作的变量集合可能包含多个元素,并且必须是有限的。然而,与状态变量的数量不同,组成动作的变量数量可能不是恒定的。一个状态中可用的动作可能根据该状态而变化。为了简单起见,大多数环境都是设计为所有状态下动作数量相同。
环境提前知道所有可用的动作集合。智能体可以选择确定性或随机地选择动作。这与说环境对智能体的动作做出确定性或随机反应是不同的。这两个陈述都是真实的,但我在这里指的是智能体可以从查找表或每个状态的概率分布中选择动作。
在 BW、BSW 和 FL 环境中,动作是单例,表示智能体将尝试移动的方向。在 FL 中,所有状态下都有四个可用的动作:上、下、右或左。每个动作有一个变量,动作空间的大小是 4。

冰冻湖环境有四种简单的移动动作
转移函数:智能体动作的后果
环境对动作的反应方式被称为 状态转移概率,或者更简单地说,转移函数,用 t(s, a, s') 表示。转移函数 t 将一个转移元组 s,a,s' 映射到一个概率;也就是说,你传递一个状态 s,一个动作 a 和一个下一个状态 s',它将返回采取动作 a 时从状态 s 转移到状态 s' 的对应概率。你也可以将其表示为 t(s, a),并返回一个字典,其中键是下一个状态,值是概率。
注意,t 也描述了一个概率分布 p( · | s, a),它决定了系统在交互周期中从状态 s 选择动作 a 后将如何演变。当对下一个状态 s' 进行积分时,作为任何概率分布,这些概率的总和必须等于 1。
| 展示数学公式 | 转移函数 | |
|---|---|---|
![]() |
BW 环境是确定性的;也就是说,给定当前状态 s 和动作 a,下一个状态 s' 的概率总是 1。总是只有一个可能的后继状态 s'. BSW 和 FL 环境是随机的;也就是说,给定当前状态 s 和动作 a,下一个状态 s' 的概率小于 1。存在多个可能的后继状态 s'。
许多强化学习(RL)和深度强化学习(DRL)算法的一个关键假设是,这个分布是平稳的。也就是说,虽然可能会有高度随机的转换,但在训练或评估过程中概率分布可能不会改变。正如马尔可夫假设一样,平稳性假设通常会被放宽到一定程度。然而,对于大多数智能体来说,与环境互动的环境至少看起来是平稳的是很重要的。
在 FL 环境中,我们知道有 33.3%的几率我们会转换到目标单元格(状态),有 66.6%的几率我们会转换到正交方向。还有可能反弹回我们来的状态,如果它靠近墙壁的话。
为了简单和清晰,我在以下图像中只添加了 FL 环境中状态 0、2、5、7、11、12、13 和 15 的所有动作的转移函数。这个状态子集允许展示所有可能的转移,而不会过于杂乱。

冻结湖环境的转移函数
可能仍然有些困惑,但这样看:为了保持一致性,非终端状态中的每个动作都有三个独立的转移(角落状态中的某些动作可能只需要两个,但再次强调,让我保持一致性):一个转移到目标单元格,两个转移到正交方向的单元格。
奖励信号:胡萝卜和大棒
奖励函数 r 将一个状态转移元组 s, a, s' 映射到一个标量。奖励函数为状态转移提供了一种关于好坏的数值信号。当信号为正时,我们可以将奖励视为收入或奖励。大多数问题至少有一个正信号——比如赢得棋局或达到期望的目的地。但是,奖励也可以是负的,我们可以将其视为成本、惩罚或处罚。在机器人领域,添加时间步长成本是一种常见的做法,因为我们通常希望在有限的时间步内达到目标。有一点需要明确的是,无论是正还是负,奖励函数输出的标量始终被称为 奖励。强化学习的人们都是快乐的人。
还需要强调的是,虽然奖励函数可以表示为 r(s,a,s'),这是明确的,我们也可以使用 r(s,a),甚至 r(s),这取决于我们的需求。有时根据状态奖励智能体是我们需要的;有时使用动作和状态更有意义。然而,表示奖励函数最明确的方式是使用状态、动作和下一个状态的三元组。有了这个,我们可以在 r(s,a,s') 中对下一个状态进行边缘化,以获得 r(s,a),并在 r(s,a) 中对动作进行边缘化以获得 r(s)。但是,一旦我们处于 r(s),我们就无法恢复 r(s,a) 或 r(s,a,s'),一旦我们处于 r(s,a),我们就无法恢复 r(s,a,s')。
| 展示数学公式奖励函数 | |
|---|---|
奖励函数 |
在 FL 环境中,当落在状态 15 时,奖励函数为+1,否则为 0。同样,为了使以下图像更清晰,我只添加了给予非零奖励的转移信号,即落在最终状态(状态 15)。
只有三种方式可以落在 15 上。(1)从状态 14 选择 向下 动作,会无意中将智能体转移到那里,每个动作的概率为 33.3%。你注意到动作和转移之间的区别了吗?看到随机性如何使事情复杂化,很有趣,对吧?

具有非零奖励转移状态的重奖信号
将转移和奖励函数扩展到表格形式也是有用的。以下是我推荐的大多数问题的格式。请注意,我只为表格添加了一部分转移(行)来展示练习。另外请注意,我在这里很明确,这些转移中的几个可以分组和重构(例如,角落单元格)。
| 状态 | 动作 | 下一个状态 | 转移概率 | 奖励信号 |
|---|---|---|---|---|
| 0 | 左 | 0 | 0.33 | 0 |
| 0 | 左 | 0 | 0.33 | 0 |
| 0 | 左 | 4 | 0.33 | 0 |
| 0 | 下 | 0 | 0.33 | 0 |
| 0 | 下 | 4 | 0.33 | 0 |
| 0 | 下 | 1 | 0.33 | 0 |
| 0 | 右 | 4 | 0.33 | 0 |
| 0 | 右 | 1 | 0.33 | 0 |
| 0 | 右 | 0 | 0.33 | 0 |
| 0 | 上 | 1 | 0.33 | 0 |
| 0 | 上 | 0 | 0.33 | 0 |
| 0 | 上 | 0 | 0.33 | 0 |
| 1 | 左 | 1 | 0.33 | 0 |
| 1 | 左 | 0 | 0.33 | 0 |
| 1 | 左 | 5 | 0.33 | 0 |
| 1 | 下 | 0 | 0.33 | 0 |
| 1 | 下 | 5 | 0.33 | 0 |
| 1 | 下 | 2 | 0.33 | 0 |
| 1 | 右 | 5 | 0.33 | 0 |
| 1 | 右 | 2 | 0.33 | 0 |
| 1 | 右 | 1 | 0.33 | 0 |
| 2 | 左 | 1 | 0.33 | 0 |
| 2 | 左 | 2 | 0.33 | 0 |
| 2 | 左 | 6 | 0.33 | 0 |
| 2 | 下 | 1 | 0.33 | 0 |
| ... | ... | ... | ... | ... |
| 14 | 下 | 14 | 0.33 | 0 |
| 14 | 下 | 15 | 0.33 | 1 |
| 14 | 右 | 14 | 0.33 | 0 |
| 14 | 右 | 15 | 0.33 | 1 |
| 14 | 右 | 10 | 0.33 | 0 |
| 14 | 上 | 15 | 0.33 | 1 |
| 14 | 上 | 10 | 0.33 | 0 |
| ... | ... | ... | ... | ... |
| 15 | 左 | 15 | 1.0 | 0 |
| 15 | 下 | 15 | 1.0 | 0 |
| 15 | 右 | 15 | 1.0 | 0 |
| 15 | 上 | 15 | 1.0 | 0 |
地平线:时间改变最优解
我们也可以在马尔可夫决策过程(MDP)中表示时间。一个时间步,也称为纪元、周期、迭代或甚至交互,是一个全局时钟,同步所有各方并离散化时间。拥有一个时钟会产生几种可能的任务类型。一个周期性任务是一个时间步数有限的任务,要么因为时钟停止,要么因为智能体达到终端状态。还有持续任务,这些任务会一直进行下去;没有终端状态,因此有无限多个时间步。在这种任务中,智能体必须手动停止。
周期性和持续任务也可以从智能体的角度定义。我们称之为规划地平线。一方面,有限地平线是一个智能体知道任务将在有限个时间步内终止的规划地平线:例如,如果我们强迫智能体在 15 步内完成冻结的湖环境。这种规划地平线的一个特殊情况称为贪婪地平线,其规划地平线为 1。BW 和 BSW 都有一个贪婪的规划地平线:一个交互后立即结束纪元。实际上,所有赌博机环境都有贪婪地平线。
另一方面,无限视野是指智能体没有预定的时间步限制,因此智能体计划无限多个时间步。这样的任务可能仍然是周期性的,因此可以终止,但从智能体的角度来看,其规划视野是无限的。我们将这种类型的无限规划视野任务称为不确定视野任务。智能体计划无限,但环境可以随时停止交互。
对于智能体有很高的可能性陷入循环而无法终止的任务,通常的做法是添加基于时间步的人工终止状态:使用转换函数的硬时间步限制。这些情况需要对时间步限制终止状态进行特殊处理。第 8、9、10 章的环境,即小车和杆环境,就有这种人工终止步,你将在那些章节中学习如何处理这些特殊情况。
BW、BSW 和 FL 环境是不确定规划视野;智能体计划无限多个步骤,但交互可以随时由环境停止。我们不会在 FL 环境中添加时间步限制,因为智能体自然终止的可能性很高;环境高度随机。这种任务在强化学习(RL)中最为常见。
我们将一个周期性任务从开始到结束的连续时间步序列称为周期、试验、阶段或阶段。在不确定规划视野中,一个周期是一个包含从初始状态到终止状态之间所有交互的集合。
折现:未来是不确定的,因此价值较低
由于无限视野任务中可能存在无限的时间步序列,我们需要一种方法来随时间折现奖励的价值;也就是说,我们需要一种方法告诉智能体,尽早获得+1 比晚些时候获得更好。我们通常使用一个小于 1 的正实数来指数折现未来奖励的价值。我们接收奖励的时间越远,它在当前的价值就越低。
这个数字被称为折现因子,或伽玛。折现因子调整随时间变化的奖励的重要性。我们接收奖励的时间越晚,它们对当前计算就越没有吸引力。折现因子通常被广泛使用的一个重要原因是减少回报估计的方差。鉴于未来是不确定的,而且我们看得越远,积累的随机性就越多,我们的价值估计的方差就越大,折现因子有助于减少未来奖励对我们价值函数估计的影响,这对于大多数智能体的学习是稳定的。

折现因子和时间对奖励价值的影响
有趣的是,伽马是 MDP 定义的一部分:问题,而不是智能体。然而,你通常找不到关于给定环境伽马适当值的指导。再次强调,这是因为伽马也被用作减少方差的超参数,因此留给智能体调整。
你也可以使用伽马(gamma)作为给智能体(agent)传达“紧迫感”的一种方式。为了更好地理解这一点,想象一下我告诉你,一旦你读完这本书,我会给你 1000 美元,但我每天会按 0.5 的比例(伽马)减少这个奖励。这意味着我每天都会将我支付的价值减半。你可能会在今天读完这本书。如果我说伽马是 1,那么你何时完成它都无关紧要,你仍然会得到全额。
对于 BW 和 BSW 环境,伽马值为 1 是合适的;然而,对于 FL 环境,我们将使用 0.99 的伽马值,这是一个常用的值。
| 展示数学 | 折现因子(伽马) | |
|---|---|---|
![]() |
MDP 的扩展
如我们之前讨论的,MDP 框架有许多扩展。它们允许我们针对不同类型的强化学习(RL)问题。以下列表并不全面,但它应该能给你一个关于该领域规模的概念。要知道,MDPs 的缩写通常用来指代所有类型的 MDP。我们目前只看到了冰山一角:
-
):当智能体无法完全观察环境状态时
-
因子马尔可夫决策过程(FMDP):允许更紧凑地表示转移和奖励函数,以便我们可以表示大型 MDP
-
连续 [时间|动作|状态] 马尔可夫决策过程:当时间、动作、状态或它们的任何组合是连续的
-
关联马尔可夫决策过程(RMDP):允许结合概率和关系知识
-
半马尔可夫决策过程(SMDP):允许包含需要多个时间步才能完成的抽象动作
-
多智能体马尔可夫决策过程(MMDP):允许在相同环境中包含多个智能体
-
):允许多个智能体协作并最大化共同奖励
| 我会说 Python 智能体 | 走廊智能体(BW)马尔可夫决策过程(MDP) | |
|---|---|---|
P = {
0: { ①
0: [(1.0, 0, 0.0, True)], ②
1: [(1.0, 0, 0.0, True)] ③
},
1: {
0: [(1.0, 0, 0.0, True)],
1: [(1.0, 2, 1.0, True)]
},
2: {
0: [(1.0, 2, 0.0, True)], ④
1: [(1.0, 2, 0.0, True)] ④
}
}
# import gym, gym_walk ⑤
# P = gym.make('BanditWalk-v0').env.P
① 外部字典的键是状态。② 内部字典的键是动作。③ 内部字典的值是一个列表,包含该状态-动作对的所有可能转移。④ 转移元组有四个值:该转移的概率、下一个状态、奖励以及一个标志,表示下一个状态是否是终端状态。⑤ 你也可以用这种方式加载 MDP。 |
| 我会说 Python 智能体 | 走廊滑行智能体(BSW)马尔可夫决策过程(MDP) | |
|---|---|---|
P = {
0: { ①
0: [(1.0, 0, 0.0, True)],
1: [(1.0, 0, 0.0, True)]
},
1: {
0: [(0.8, 0, 0.0, True), (0.2, 2, 1.0, True)], ②
1: [(0.8, 2, 1.0, True), (0.2, 0, 0.0, True)] ③
},
2: {
0: [(1.0, 2, 0.0, True)],
1: [(1.0, 2, 0.0, True)]
}
}
# import gym, gym_walk
# P = gym.make('BanditSlipperyWalk-v0').env.P ④
①看看终止状态。状态 0 和 2 是终止状态。②这是构建随机转换的方法。这是状态 1,动作 0。③这是在状态 1 采取动作 1 后的转换。④这是如何在笔记本中加载 Bandit Slippery Walk 的方法;确保查看它们! |
| 我会说 Python 冻结湖(FL)MDP | |
|---|---|
P = {
0: {
0: [(0.6666666666666666, 0, 0.0, False), ①
(0.3333333333333333, 4, 0.0, False) ②
], ③
<...>
3: [(0.3333333333333333, 1, 0.0, False),
(0.3333333333333333, 0, 0.0, False), ④
(0.3333333333333333, 0, 0.0, False) ④
]
},
<...> ⑤
14: {
<...> ⑥
1: [(0.3333333333333333, 13, 0.0, False),
(0.3333333333333333, 14, 0.0, False),
(0.3333333333333333, 15, 1.0, True) ⑦
],
2: [(0.3333333333333333, 14, 0.0, False),
(0.3333333333333333, 15, 1.0, True), ⑦
(0.3333333333333333, 10, 0.0, False)
],
3: [(0.3333333333333333, 15, 1.0, True), ⑦
(0.3333333333333333, 10, 0.0, False),
(0.3333333333333333, 13, 0.0, False)
]
},
15: { ⑧
0: [(1.0, 15, 0, True)],
1: [(1.0, 15, 0, True)],
2: [(1.0, 15, 0, True)],
3: [(1.0, 15, 0, True)]
}
}
# import gym ⑨
# P = gym.make('FrozenLake-v0').env.P
①在状态 0 选择动作 0 时落在状态 0 的概率②在状态 0 选择动作 0 时落在状态 4 的概率③你可以将概率分组,例如在这一行中。④或者明确表示,例如在这两行中。两种方式都可以。⑤为了清晰起见,从这个例子中去掉了许多内容。⑥前往笔记本查看完整的 FL MDP。⑧状态 14 是唯一提供非零奖励的状态。四个动作中有三个只有一个转换会导致状态 15。落在状态 15 提供+1 奖励。⑨状态 15 是一个终止状态。⑩同样,你可以这样加载 MDP。 |
整合一切
不幸的是,当你进入现实世界时,你会发现有许多不同的方式来定义 MDPs。此外,某些来源描述 POMDPs 并将它们称为 MDPs,但没有完全披露。所有这些都给新来者带来了困惑,因此我有一些要点要为你澄清。首先,你之前看到的 Python 代码不是一个完整的 MDP,而只是转换函数和奖励信号。从这些中,我们可以轻松推断状态和动作空间。这些代码片段来自几个包含我为这本书开发的几个环境的包,FL 环境是第一章节中提到的 OpenAI Gym 包的一部分。MDP 中缺失的许多额外组件,如来自初始状态集合 sH 的初始状态分布 sθ,在上面的字典中没有显示,并且 OpenAI Gym 框架不提供这些。就像我之前说的那样,折现因子通常被认为是超参数,不管好坏。而且,通常假设视野是无限的。
但不用担心这个问题。首先,为了计算本章中提出的 MDPs 的最优策略(我们将在下一章中这样做),我们只需要之前显示的包含转换函数和奖励信号的字典;从这些中,我们可以推断状态和动作空间,我会提供给你折现因子。我们将假设视野是无限的,不需要初始状态分布。此外,本章最重要的部分是让你了解 MDPs 和 POMDPs 的组件。记住,你不需要比本章中做的更多 MDP 构建。尽管如此,让我定义 MDPs 和 POMDPs,以便我们保持一致。
| 展示数学 MDPs 与 POMDPs 的比较 | |
|---|---|
![]() |
摘要
好的。我知道这一章有很多新术语,但这正是其目的。这一章的最佳总结在前一页,更具体地说,是关于马尔可夫决策过程(MDP)的定义。再次看看最后两个方程式,并尝试记住每个字母的含义。一旦你做到了这一点,你就可以确信你已经从这一章中获得了继续前进所需的内容。
在最高层次上,强化学习问题涉及智能体与其存在的环境之间的交互。在这个设置下可以模拟各种问题。马尔可夫决策过程是表示不确定条件下复杂决策问题的数学框架。
马尔可夫决策过程(MDPs)由一组系统状态、每个状态的一组初始状态分布组成。状态描述了环境的配置。动作允许智能体与环境交互。转移函数说明了环境如何演变以及如何对智能体的动作做出反应。奖励信号编码了智能体要实现的目标。视野和折现因子为交互添加了时间概念。
状态空间,所有可能状态的集合,可以是无限的或有限的。然而,组成单个状态的变量数量必须是有限的。状态可以是完全可观察的,但在更一般的马尔可夫决策过程(MDP)的更一般情况下,即部分可观察马尔可夫决策过程(POMDP),状态是部分可观察的。这意味着智能体不能观察到系统的完整状态,而只能观察到一种噪声状态,称为观察。
动作空间是一组可以随状态变化的动作。然而,惯例是使用相同的集合来表示所有状态。动作可以与多个变量组合,就像状态一样。动作变量可以是离散的或连续的。
转移函数将一个状态(下一个状态)与一个状态-动作对联系起来,并定义了在给定状态-动作对的情况下达到该未来状态的概率。奖励信号在其更一般的形式下,将一个转移元组 s, a, s' 映射到标量,并指示转移的好坏。转移函数和奖励信号定义了环境模型,并且假设是平稳的,这意味着概率在整个过程中保持不变。
到现在为止,
-
理解强化学习问题的组成部分以及它们如何相互作用
-
认识到马尔可夫决策过程,并了解它们由什么组成以及它们是如何工作的
-
可以将顺序决策问题表示为马尔可夫决策过程(MDPs)
| 可分享的亮点:自己动手并分享你的发现 |
|---|
| | 这里有一些如何将你所学的内容提升到下一个层次的想法。如果你愿意,与世界分享你的结果,并确保查看其他人所做的事情。这是一个双赢的局面,希望你能充分利用它。
-
#gdrl_ch02_tf01: 创建环境是一项至关重要的技能,值得有一本书来专门介绍。你为什么不自己创建一个网格世界环境呢?在这里,看看本章中步行环境的代码(
github.com/mimoralea/gym-walk)以及一些其他的网格世界环境(github.com/mimoralea/gym-aima,github.com/mimoralea/gym-bandits,github.com/openai/gym/tree/master/gym/envs/toy_text)。现在,创建一个包含新网格世界环境的 Python 包!不要限制自己只做简单的移动动作;你可以创建一个‘传送’动作,或者任何其他动作。也许还可以在环境中添加除了你的智能体之外的其他生物。也许可以添加一些小怪物,你的智能体需要避开。在这里发挥创意。你可以做很多事情。 -
#gdrl_ch02_tf02: 另一个尝试的事情是为你选择的仿真引擎创建一个所谓的“Gym 环境”。首先,调查一下“Gym 环境”究竟是什么。接下来,探索以下 Python 包(
github.com/openai/mujoco-py,github.com/openai/atari-py,github.com/google-research/football, 以及github.com/openai/gym/blob/master/docs/environments.md中的许多包)。然后,尝试理解其他人如何将仿真引擎暴露为 Gym 环境。最后,为你选择的仿真引擎创建一个 Gym 环境。这是一个具有挑战性的任务! -
#gdrl_ch02_tf03: 在每一章中,我都在使用最后的标签作为通用的标签。请随意使用这个标签来讨论与本章相关的任何其他你工作过的事情。没有什么比为自己创造作业更令人兴奋的了。确保分享你设定去调查的内容以及你的结果。
用你的发现写一条推文,@mimoralea(我会转发),并使用这个列表中的特定标签来帮助感兴趣的人找到你的结果。没有正确或错误的结果;你分享你的发现并检查他人的发现。利用这个机会社交,做出贡献,让自己脱颖而出!我们正在等待你!以下是一条推文示例:“嘿,@mimoralea。我创建了一个包含资源列表的博客文章,用于研究深度强化学习。查看它在这里<链接>。#gdrl_ch01_tf01”我会确保转发并帮助他人找到你的工作。|
3 平衡短期和长期目标
在本章
-
你将了解从序列性反馈中学习的挑战以及如何正确平衡短期和长期目标。
-
你将开发出能够找到用 MDP 建模的序列决策问题中最佳行为策略的算法。
-
你将找到你在上一章中为构建的 MDP 的所有环境的最佳策略。
在准备战斗时,我总是发现计划是没有用的,但规划是必不可少的。
—— 德怀特·D·艾森豪威尔,美国陆军五星上将和第 34 任美国总统
在上一章中,你为 BW、BSW 和 FL 环境构建了 MDP。MDP 是推动强化学习环境的引擎。它们定义了问题:它们描述了智能体如何通过状态和动作空间与环境交互,智能体的目标通过奖励函数,环境如何通过转移函数对智能体的动作做出反应,以及时间如何通过折现因子影响行为。
在本章中,你将学习解决马尔可夫决策过程(MDP)的算法。我们首先讨论智能体的目标以及为什么简单的计划不足以解决 MDP 问题。然后,我们讨论在称为动态策略迭代(PI)的技术下解决 MDP 的两个基本算法。
你很快就会注意到,这些方法在某种程度上“作弊”:它们需要完全访问 MDP,并且依赖于了解环境的动态,而这并不是我们总能获得的。然而,你将学习的这些基础知识对于了解更高级的算法仍然是有用的。最终,价值迭代(VI)和策略迭代(PI)是几乎所有其他强化学习(和深度强化学习)算法的基础。
你还会注意到,当一个智能体完全访问 MDP 时,没有不确定性,因为你可以查看动态和奖励并直接计算期望。直接计算期望意味着不需要探索;也就是说,不需要平衡探索和利用。不需要交互,因此不需要试错学习。所有这一切都是因为我们在本章用于学习的反馈不是评估性的,而是监督性的。
记住,在深度强化学习(DRL)中,智能体从同时具有序列性(与一次性相反)、评估性(与监督性相反)和采样性(与穷举性相反)的反馈中学习。在本章中,我所做的是消除从评估性和采样性反馈中带来的复杂性,并单独研究序列性反馈。在本章中,我们从序列性、监督性和穷举性的反馈中学习。
决策智能体的目标
起初,似乎智能体的目标是找到一系列动作,以最大化回报:在一段时间或智能体的整个生命周期内,根据任务的不同,是奖励的总和(折现或未折现,取决于伽马值)。
让我介绍一个新的环境来更具体地解释这些概念。
| 一个具体的例子:滑稽的走步五(SWF)环境 | |
|---|---|
滑稽的走步五(SWF)是一个单行网格世界环境(一次走步),它是随机的,类似于冰冻湖,并且它只有五个非终止状态(如果计算两个终止状态,总共是七个)。 滑稽的走步五环境智能体从s, H开始,H是洞,g是目标,提供+1 奖励。 |
|
| 展示数学:回报 G | |
![]() |
你可以将回报视为向后看——“从过去的时间步中得到了多少”;但另一种看待它的方式是“未来的奖励”——基本上是向前看。例如,想象在 SWF 环境中有一个这样的场景:3/0, 4/0, 5/0, 4/0, 5/0, 6/1。这个轨迹/场景的回报是多少?
好吧,如果我们使用折现,数学将这样计算。

滑稽的走步五环境中的折现回报
如果我们不使用折现,那么,对于这个轨迹以及所有以最右侧单元格、状态 6 结束的轨迹,回报将是 1,而对于所有以最左侧单元格、状态 0 结束的轨迹,回报将是 0。
在 SWF 环境中,很明显向右走是最好的选择。因此,似乎所有智能体必须找到的只是一些被称为计划的东西——即从目标状态开始的一系列动作。但这并不总是有效。

SWF 环境中的稳固计划
在 FL 环境中,计划看起来如下。

FL 环境中的稳固计划
但这还不够!计划的问题在于它们没有考虑到环境中的随机性,而 SWF 和 FL 都是随机的;采取的动作不一定总是按照我们的意图行事。如果由于环境的随机性,我们的智能体落在我们的计划未覆盖的单元格上,会发生什么?

我们计划中可能存在的漏洞
在 FL 环境中也发生同样的事情。

在随机环境中,计划是不够的
智能体需要提出的是所谓的策略。策略是通用的计划;策略覆盖所有可能的状态。我们需要为每个可能的状态进行规划。策略可以是随机的或确定的:策略可以为给定状态(或观察)返回动作概率分布或单个动作。目前,我们正在使用确定性的策略,这是一个将动作映射到状态的查找表。

SWF 环境中的最优策略
在 SWF 环境中,最优策略总是向右走,对于每个状态。很好,但仍然有许多未解决的问题。例如,我应该从这个策略中期望多少奖励?因为,尽管我们知道如何采取最优行动,但环境可能会将我们的智能体送回洞中,即使我们总是选择朝向目标前进。这就是为什么回报还不够。智能体真正寻求的是最大化期望回报;这意味着考虑环境随机性的回报。
此外,我们需要一种方法来自动找到最优策略,因为在 FL 示例中,例如,最优策略看起来并不明显!
在智能体内部保留了一些组件,可以帮助它找到最优行为:有策略,对于给定的环境可能有多个策略,实际上,在特定环境中,可能有多个最优策略。此外,还有价值函数帮助我们跟踪回报估计。对于给定的 MDP,有一个单一的最优价值函数,但通常可能有多个价值函数。
让我们看看强化学习智能体内部的所有组件,这些组件允许它们学习和找到最优策略,并用示例使所有这些更加具体。
策略:每个状态的动作规定
考虑到 Frozen Lake 环境中的随机性(以及大多数强化学习问题),智能体需要找到一个策略,记为π。策略是为给定非终端状态规定采取行动的函数。(记住,策略可以是随机的:直接针对一个动作或动作的概率分布。我们将在后面的章节中扩展随机策略。)
这里是一个示例策略。

一个随机生成的策略
当查看一个策略时,一个立即出现的问题是这样的:这个策略有多好?如果我们找到一种方法给策略赋予数字,我们也可以问,这个策略比另一个策略好多少?

我们如何比较策略?
状态价值函数:从这里可以期待什么?
要帮助我们比较策略,我们可以为给定策略中的状态赋予数字。也就是说,如果我们给定一个策略和 MDP,我们应该能够从每个单一状态开始计算期望回报(我们主要关心起始状态)。我们如何计算处于某个状态的价值?例如,如果我们的智能体处于状态 14(目标左侧),这比处于状态 13(14 左侧)好多少?以及它究竟好多少?更重要的是,在哪种策略下我们会得到更好的结果,是“积极进取”策略还是“谨慎”策略?
让我们用“积极进取”策略快速尝试一下。在“积极进取”策略下,处于状态 14 的价值是多少?

在运行“积极进取”策略时,处于状态 14 的价值是多少?
好吧,按照“立即行动”策略计算状态 14 的值并不那么简单,因为依赖于其他状态(在这个例子中是状态 10 和 14)的值,而这些值我们也没有。这就像“先有鸡还是先有蛋”的问题。让我们继续前进。
我们定义了π。记住,我们处于随机环境中,因此我们必须考虑环境对我们策略的所有可能反应!这正是期望值给我们的。
我们现在定义在遵循策略π时状态 s 的值:状态 s 在策略π下的值是如果智能体从状态 s 开始遵循策略π,则回报的期望值。为每个状态计算这个值,你得到状态值函数,或 V 函数或值函数。它表示遵循策略π从状态 s 开始时的期望回报。
| 展示数学公式 | |
|---|---|
![]() |
这些方程非常迷人。由于递归依赖关系,有点混乱,但仍然很有趣。注意状态的价值如何递归地依赖于可能许多其他状态的值,而这些值也可能依赖于其他状态,包括原始状态!
状态和后续状态之间的递归关系将在下一节中再次出现,当我们查看可以迭代求解这些方程并获取 FL 环境(或任何其他环境)中任何策略的状态值函数的算法时。
现在,让我们继续探索 RL 智能体中常见的其他组件。我们将在本章后面学习如何计算这些值。请注意,状态值函数通常被称为值函数,甚至称为 V 函数,或者更简单地说,v**π(s)。这可能有点令人困惑,但你会习惯的。
动作值函数:如果我这样做,我应该期望从这里得到什么?
另一个我们经常需要问的至关重要的问题不仅仅是关于状态的值,而是在状态 s 中采取动作 a 的值。区分这类问题的答案将帮助我们决定采取哪种动作。
例如,注意“立即行动”策略在状态 14 时是向右走的,但“谨慎”策略是向下走的。但哪个动作更好?更具体地说,在每种策略下哪个动作更好?也就是说,向下走而不是向右走的值是多少,以及向右走而不是向下走的值是多少,然后遵循“立即行动”策略?
通过比较同一策略下的不同动作,我们可以选择更好的动作,从而改进我们的策略。动作值函数,也称为 Q 函数或 Q^π**(s,a)*,精确地捕捉了这一点:智能体在状态 s 中采取动作 a 后遵循策略π的期望回报。
事实上,当我们关心改进策略时,这通常被称为控制问题,我们需要动作值函数。想想看:如果你没有 MDP,你怎么能仅仅通过知道所有状态的价值来决定采取什么动作?V 函数没有捕捉到环境的动态。另一方面,Q 函数在一定程度上捕捉了环境的动态,并允许你在不需要 MDP 的情况下改进策略。我们将在后面的章节中进一步阐述这一事实。
动作优势函数:如果我这么做会好多少?
另一种价值函数是从前两种函数推导出来的。这种动作优势函数,也称为优势函数、a 函数或a^π**(s, a),是动作a在状态s的动作值函数与在策略π下状态s的状态值函数之间的差值。
优势函数描述了采取动作a而不是遵循策略π的好处:选择动作a相对于默认动作的优势。
看看 SWF 环境中(愚蠢的)策略的不同价值函数。记住,这些值取决于策略。换句话说,Qπ(s, a)假设你会遵循策略π(在以下示例中始终向左)并在状态s采取动作a之后。

状态值函数、动作值函数和动作优势函数
最优性
策略、状态值函数、动作值函数和动作优势函数是我们用来描述、评估和改进行为的组件。当这些组件处于最佳状态时,我们称之为最优性。
最优策略是一种策略,对于每个状态,它可以获得大于或等于任何其他策略的预期回报。最优状态值函数是所有策略对所有状态的最大值的状态值函数。同样,最优动作值函数是所有策略对所有状态-动作对的最大值的动作值函数。最优动作优势函数遵循类似的模式,但请注意,最优优势函数对于所有状态-动作对都等于或小于零,因为没有任何动作可以从最优状态值函数中获得任何优势。
此外,请注意,尽管对于给定的 MDP 可能有多个最优策略,但只有一个最优状态值函数、最优动作值函数和最优动作优势函数。
你可能也注意到,如果你有了最优的 V 函数,你可以使用 MDP 进行一步搜索以找到最优的 Q 函数,然后使用这个函数构建最优策略。另一方面,如果你有了最优的 Q 函数,你根本不需要 MDP。你可以通过仅对动作取最大值来使用最优 Q 函数找到最优的 V 函数。你也可以通过对动作取 argmax 来使用最优 Q 函数获得最优策略。
| 展示数学 The Bellman 最优方程 | |
|---|---|
![]() |
规划最优动作序列
我们有状态值函数来跟踪状态的价值,动作值函数来跟踪状态-动作对的价值,动作优势函数来显示采取特定动作的“优势”。我们有所有这些方程来评估当前策略,即从策略到值函数,并计算和找到最优值函数,因此找到最优策略。
现在我们已经讨论了强化学习问题的公式化,并且我们已经定义了我们追求的目标,我们可以开始探索寻找这个目标的方法。迭代计算前一小节中提出的方程是解决强化学习问题并获得已知环境动态、MDPs 时的最优策略的最常见方法之一。让我们看看这些方法。
策略评估:评估策略
我们在上一节中讨论了比较策略。我们确定,如果对于所有状态,策略π的期望回报优于或等于策略πq',则策略π优于或等于策略πq'。然而,在我们能够使用这个定义之前,我们必须设计一个评估任意策略的算法。这种算法被称为迭代策略评估或简称为策略评估。
策略评估算法包括通过遍历状态空间并迭代改进估计来计算给定策略的 V 函数。我们将接受策略并输出值函数的算法称为解决预测问题的算法,即计算预定策略的价值。
| 展示数学 The policy-evaluation equation | |
|---|---|
![]() |
使用这个方程,我们可以迭代地近似任意策略的真实 V 函数。迭代策略评估算法在给定足够迭代次数的情况下,保证收敛到策略的价值函数,更具体地说,当我们接近无穷大时。然而,在实践中,我们使用一个小的阈值来检查我们近似的价值函数的变化。一旦价值函数的变化小于这个阈值,我们就停止。
让我们看看这个算法在 SWF 环境中,对于始终向左的策略是如何工作的。

政策评估的初始计算
然后,你计算所有状态 0-6 的值,完成后,进入下一个迭代。请注意,为了计算v[2]π(s),你必须使用前一个迭代中获得的估计值,v[1]π(s)。这种从估计值计算估计值的技术被称为bootstrapping,并且在强化学习(包括深度强化学习)中得到了广泛的应用。
此外,重要的是要注意,这里的K是估计之间的迭代,但它们并不是与环境交互。这些不是智能体在外部选择动作并观察环境的回合。这些也不是时间步。相反,这些是迭代策略评估算法的迭代。进行更多的这些估计。下表显示了您应该得到的结果。
| k | V^π(0) | V^π(1) | V^π(2) | V^π(3) | V^π(4) | V^π(5) | V^π(6) |
|---|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 1 | 0 | 0 | 0 | 0 | 0 | 0.1667 | 0 |
| 2 | 0 | 0 | 0 | 0 | 0.0278 | 0.2222 | 0 |
| 3 | 0 | 0 | 0 | 0.0046 | 0.0463 | 0.2546 | 0 |
| 4 | 0 | 0 | 0.0008 | 0.0093 | 0.0602 | 0.2747 | 0 |
| 5 | 0 | 0.0001 | 0.0018 | 0.0135 | 0.0705 | 0.2883 | 0 |
| 6 | 0 | 0.0003 | 0.0029 | 0.0171 | 0.0783 | 0.2980 | 0 |
| 7 | 0 | 0.0006 | 0.0040 | 0.0202 | 0.0843 | 0.3052 | 0 |
| 8 | 0 | 0.0009 | 0.0050 | 0.0228 | 0.0891 | 0.3106 | 0 |
| 9 | 0 | 0.0011 | 0.0059 | 0.0249 | 0.0929 | 0.3147 | 0 |
| 10 | 0 | 0.0014 | 0.0067 | 0.0267 | 0.0959 | 0.318 | 0 |
| ... | ... | ... | ... | ... | ... | ... | ... |
| 104 | 0 | 0.0027 | 0.011 | 0.0357 | 0.1099 | 0.3324 | 0 |
那么,结果的状态值函数告诉我们哪些事情?
首先,我们可以说,在这个环境中开始一个回合并遵循始终向左的策略时,我们期望获得 0.0357 的回报。相当低。
我们还可以说,即使我们发现自己处于状态 1(最左边的非终端状态),我们仍然有不到 1%的机会最终进入目标单元格(状态 6)。更准确地说,当我们处于状态 1 时,我们有 0.27%的机会进入目标状态。而且我们一直选择向左!非常有趣。
有趣的是,由于这个环境的随机性,我们有 3.57%的机会到达目标单元格(记住这个环境有 50%的动作成功率,33.33%无效果,16.66%反向)。再次强调,这是在始终向左的策略下。尽管如此,左边的动作可能会让我们向右移动,然后再次向右,或者向左,然后向右,向右,向右,等等。
考虑轨迹概率的组合方式。同时,注意迭代过程以及值是如何从奖励(从状态 5 到状态 6 的转换)一步一步向后传播的。这种值的反向传播是强化学习算法的常见特征,并且在多个地方再次出现。
| 我会说 Python 政策评估算法 | |
|---|---|
def policy_evaluation(pi, P, gamma=1.0, theta=1e-10): ①
prev_V = np.zeros(len(P)) ②
while True: ③
V = np.zeros(len(P)) ④
for s in range(len(P)): ⑤
for prob, next_state, reward, done in P[s][pi(s)]: ⑥⑦
V[s] += prob * (reward + gamma * \ ⑧
prev_V[next_state] * (not done)) ⑨
if np.max(np.abs(prev_V - V)) < theta:
break ⑩
prev_V = V.copy() ⑪
return V
① 这是对策略评估算法的完整实现。我们需要的只是我们想要评估的策略以及策略运行的 MDP。折现因子 gamma 默认为 1,theta 是一个小数,我们用它来检查收敛性。② 在这里,我们将状态值函数的第一迭代估计初始化为零。③ 我们首先通过“永远”循环……④ 我们也将当前迭代的估计初始化为零。⑤ 然后遍历所有状态来估计状态值函数。⑥ 看这里我们如何使用策略π来获取可能的转换。⑦ 每个转换元组都有一个概率、下一个状态、奖励和一个表示“下一个状态”是否为终端状态的 done 标志。⑧ 我们通过计算该转换的加权值来计算该状态的价值。⑨ 注意我们如何使用‘done’标志来确保当落在终端状态时下一个状态的价值为零。我们不希望有无限的和。⑩ 在每次迭代(状态扫描)结束时,我们确保状态值函数正在变化;否则,我们称之为收敛。⑪ 最后,“复制”以准备下一次迭代或返回最新的状态值函数。
现在我们将在之前为 FL 环境生成的随机策略中运行策略评估。

回忆一下随机生成的策略
以下显示了策略评估在仅经过八次迭代后对随机生成策略的状态值函数的准确估计所取得的进展。

FL 环境随机生成策略的策略评估

随机生成策略的状态值函数
这个最终的状态值函数是此策略的状态值函数。请注意,尽管这仍然是一个估计值,因为我们处于离散的状态和动作空间,我们可以假设当使用 gamma 为 0.99 时,这实际上是实际的价值函数。
如果你想知道之前展示的两个策略的状态值函数,这里有一些结果。

策略演化的结果
在 FL 环境中,似乎成为一个“积极进取”的策略并不划算!有趣的结果,对吧?但是,一个问题出现了:这个环境中有没有更好的策略?
政策改进:利用评级获得更好的结果
现在动机很明确了。你有一种评估任何策略的方法。这已经给了你一些自由:你可以评估许多策略,并按起始状态的状态值函数对它们进行排名。毕竟,这个数字告诉你,如果你运行许多剧集,所讨论的策略将获得的预期累积奖励。酷,对吧?
不!毫无意义。你为什么会随机生成一堆策略并评估它们?首先,这完全是浪费计算资源,更重要的是,这并不能保证你找到更好的策略。必须有一种更好的方法。
解决这个问题的关键是动作值函数,即 Q 函数。使用 V 函数和 MDP,你可以得到 Q 函数的估计。Q 函数将为你展示所有状态下所有动作的值,而这些值反过来又可能暗示如何改进策略。看看 Careful 策略的 Q 函数以及我们可以如何改进这个策略:

Q 函数如何帮助我们改进策略?
注意,如果我们对策略的 Q 函数采取贪婪行动,我们就会得到一个新的策略:Careful+。这个策略是否更好?嗯,策略评估可以告诉我们!让我们来看看!

Careful 策略的状态值函数
新策略比原始策略更好。这太棒了!我们使用了原始策略的状态值函数和 MDP 来计算其动作值函数。然后,根据动作值函数采取贪婪行动给了我们一个改进的策略。这就是策略改进算法所做的事情:它使用状态值函数和 MDP 计算动作值函数,并返回一个关于原始策略动作值函数的贪婪策略。让它沉淀下来,这很重要。
| 展示数学公式策略改进方程 | |
|---|---|
![]() |
这就是策略改进算法在 Python 中的样子。
| 我会说 Python 策略改进算法 | |
|---|---|
def policy_improvement(V, P, gamma=1.0): ①
Q = np.zeros((len(P), len(P[0])), dtype=np.float64) ②
for s in range(len(P)): ③
for a in range(len(P[s])):
for prob, next_state, reward, done in P[s][a]: ④
Q[s][a] += prob * (reward + gamma * \ ⑤
V[next_state] * (not done))
new_pi = lambda s: {s:a for s, a in enumerate( ⑥
np.argmax(Q, axis=1))}[s]
return new_pi ⑥
① 非常简单的算法。它需要你想要改进的策略的状态值函数 V,MDP,P(以及可选的 gamma)。② 然后,将 Q 函数初始化为零(技术上,你可以随机初始化这些值,但让我们保持简单)。③ 然后,遍历状态、动作和转换。④ 标记表示下一个状态是否为终端状态。⑤ 我们使用这些值来计算 Q 函数。⑥ 最后,通过取原始策略 Q 函数的最大值,我们获得一个新的贪婪策略。这样,你就可能得到了一个改进的策略。|
自然接下来要问的问题是这些:有没有比这个更好的策略?我们能做得比再次更好吗?也许可以!但,只有一种方法可以找到答案。让我们试试看!

我们能否改进 Careful+策略?
我对 Careful+策略进行了策略评估,然后进行了策略改进。Careful 和 Careful+的 Q 函数不同,但基于 Q 函数的贪婪策略是相同的。换句话说,这次没有改进。
没有任何改进发生,因为 Careful+策略是 FL 环境(gamma 为 0.99)的一个最优策略。我们只需要对 Careful 策略进行一次改进,因为该策略一开始就很好。
现在,即使我们从一个旨在表现不佳的对抗性策略开始,交替进行策略评估和改进,最终也会得到一个最优策略。需要证明吗?让我们试试!让我们为 FL 环境想出一个对抗性策略,看看会发生什么。

FL 环境的对抗性策略
政策迭代:改进改进行为
使用这个对抗性策略的计划是在策略评估和策略改进之间交替,直到策略改进阶段输出的策略不再产生不同的策略。事实上,如果我们不是从一个对抗性策略开始,而是从一个随机生成的策略开始,这就是一个称为策略迭代的算法所做的事情。
| 我会说 Python 政策迭代算法 | |
|---|---|
def policy_iteration(P, gamma=1.0, theta=1e-10): ①
random_actions = np.random.choice(
tuple(P[0].keys()), len(P))
pi = lambda s: {s:a for s, a in enumerate( ②
random_actions)}[s] ②
while True:
old_pi = {s:pi(s) for s in range(len(P))} ③
V = policy_evaluation(pi, P, gamma, theta) ④
pi = policy_improvement(V, P, gamma) ⑤
if old_pi == {s:pi(s) for s in range(len(P))}: ⑥
break ⑦
return V, pi ⑧
① 策略迭代很简单,只需要 MDP(包括 gamma)。② 第一步是创建一个随机生成的策略。这里任何东西都可以。我创建了一个随机动作列表,并将其映射到状态。③ 在我们修改策略之前,我保留了一个策略的副本。④ 获取策略的状态值函数。⑤ 获取一个改进的策略。⑥ 然后,检查新策略是否有所不同。⑦ 如果它不同,我们再次做所有这些。⑧ 如果它没有变化,我们就跳出循环,返回一个最优策略和最优状态值函数。 |
太好了!但是,让我们先尝试从对抗性策略开始,看看会发生什么。

改进对抗性策略 1/2

改进对抗性策略 2/2
如前所述,交替进行策略评估和策略改进,无论你从什么策略开始,都会得到一个最优策略和状态值函数。现在,我想就这句话提出几点。
注意我如何使用“一个最优策略”,同时也使用“一个最优状态值函数”。这不是巧合或用词不当;事实上,这是我想要再次强调的一个属性。一个 MDP 可以有多于一个的最优策略,但它只能有一个最优状态值函数。这并不难理解。
状态值函数是一组数字。数字可以有无限小的精度,因为它们是数字。将只有一个最优状态值函数(所有状态中数值最高的集合)。然而,状态值函数可能包含对于给定状态具有同等价值的动作;这包括最优状态值函数。在这种情况下,可能会有多个最优策略,每个最优策略选择不同的、但具有同等价值的动作。看看:FL 环境是这种情况的一个很好的例子。

FL 环境具有多个最优策略
顺便说一下,这里没有显示,但终端状态中的所有动作都具有相同的值,即零,因此这与我在状态 6 中强调的问题类似。
作为最后的注意事项,我想强调政策迭代保证收敛到精确的最优策略:数学证明显示它不会陷入局部最优。然而,作为一个实际考虑,有一件事需要注意。如果动作值函数存在平局(例如,状态 6 中的右/左),我们必须确保不要随机打破平局。否则,策略改进可能会不断返回不同的策略,即使没有任何真正的改进。解决了这个问题之后,让我们来看看另一个寻找最优状态值函数和最优策略的基本算法。
值迭代:早期改进行为
你可能已经注意到了策略评估的工作方式:值在每个迭代中一致地传播,但速度很慢。看看。

SWF 环境中的始终左策略的政策评估
图像显示了政策评估的单次状态空间扫描,随后是对 Q 函数的估计。我们通过在每个迭代中使用 V 函数和 MDP 的截断估计来完成这项工作。通过这样做,我们可以更容易地看到,即使在第一次迭代之后,对早期 Q 函数估计的贪婪策略也是一种改进。看看第一次迭代中状态 5 的 Q 值;将动作改为指向 GOAL 状态显然已经更好。
换句话说,即使我们进行值迭代(VI)。
VI 可以被看作是“贪婪地贪婪化策略”,因为我们尽可能贪婪地计算贪婪策略。VI 不会等到我们有策略的准确估计后再进行改进,而是 VI 在单次状态扫描后截断策略评估阶段。看看我所说的“贪婪地贪婪化策略”是什么意思。

贪婪地改进 SFW 环境的始终左策略
如果我们从一个随机生成的策略开始,而不是 SWF 环境中的这个对抗性的始终左策略,VI 仍然会收敛到最优状态值函数。VI 是一个简单的算法,可以用一个方程表示。
| 显示数学公式值迭代方程 | |
|---|---|
![]() |
注意,在实践中,在 VI 中,我们根本不需要处理策略。VI 没有单独的评价阶段,该阶段运行到收敛。虽然 VI 的目标与 PI 的目标相同——为给定的 MDP 找到最优策略——VI 恰好通过值函数来完成这项工作;因此得名值迭代。
再次强调,我们只需要跟踪一个 V 函数和一个 Q 函数(具体取决于实现方式)。记住,为了在 Q 函数上获得贪婪策略,我们取该 Q 函数动作的最大值(argmax)。而不是通过取 argmax 来改善策略以获得更好的策略,然后评估这个改进的策略以再次获得价值函数,我们直接计算用于下一次状态遍历的动作的最大(max,而不是 argmax)值。
只有在 VI 算法的末尾,当 Q 函数收敛到最优值后,我们才通过在 Q 函数的动作上取 argmax 来提取最优策略,就像之前一样。你将在下一页的代码片段中更清楚地看到这一点。
需要强调的一个重要事项是,虽然 VI 和 PI 是两种不同的算法,但从更广泛的角度来看,它们是广义策略迭代(GPI)的两个实例。GPI 是强化学习中的一个通用概念,其中策略通过其价值函数估计来改进,而价值函数估计则朝着当前策略的实际价值函数改进。你是否等待完美的估计只是一个细节。
| 我会说 Python 价值迭代算法 | |
|---|---|
def value_iteration(P, gamma=1.0, theta=1e-10): ①
V = np.zeros(len(P), dtype=np.float64) ②
while True: ③
Q = np.zeros((len(P), len(P[0])), dtype=np.float64) ④
for s in range(len(P)):
for a in range(len(P[s])):
for prob, next_state, reward, done in P[s][a]: ⑤
Q[s][a] += prob * (reward + gamma * \ ⑥
V[next_state] * (not done)) ⑦
if np.max(np.abs(V - np.max(Q, axis=1))) < theta: ⑧
break
V = np.max(Q, axis=1) ⑨
pi = lambda s: {s:a for s, a in enumerate(
np.argmax(Q, axis=1))}[s]
return V, pi ⑩
① 与策略迭代一样,价值迭代是获得最优策略的方法。为此,我们需要一个 MDP(包括 gamma)。Theta 是收敛标准。1e-10 足够准确。② 首先是要初始化一个状态价值函数。要知道,带有随机数的 V 函数应该可以正常工作。③ 我们进入这个循环,并将 Q 函数初始化为零。④ 注意这里必须为零。否则,估计将是不正确的。⑤ 然后,对于每个状态中每个动作的每个转换,我们 ...⑥ ... 计算动作价值函数 ...⑦ ... 注意,使用 V,这是旧的“截断”估计。⑧ 在每次遍历状态空间后,我们确保状态价值函数持续变化。否则,我们找到了最优的 V 函数,应该跳出循环。⑨ 多亏了这一行,我们不需要单独的策略改进阶段。它不是直接替换,而是一种改进和评估的组合。⑩ 只有在最后,我们提取最优策略并将其与最优状态价值函数一起返回。 |
摘要
强化学习代理的目标是最大化期望回报,即多个回合的总奖励。为此,代理必须使用策略,这可以被视为通用计划。策略为状态指定动作。它们可以是确定性的,意味着它们返回单个动作,或者随机性的,意味着它们返回概率分布。为了获得策略,代理通常跟踪几个汇总值。主要的是状态价值、动作价值和动作优势函数。
状态价值函数总结了从状态中期望获得的回报。它们表明代理从状态到一期的结束预期将获得多少奖励。动作价值函数总结了从状态-动作对的期望回报。这种类型的价值函数告诉代理在给定状态下选择特定动作后预期的后续奖励。动作价值函数允许代理比较不同的动作,从而解决控制问题。动作优势函数显示了代理如果选择特定的状态-动作对,可以比默认情况做得更好。所有这些价值函数都映射到特定的策略,可能是最优策略。它们取决于遵循策略直到一期的结束。
策略评估是从策略和 MDP 中估计价值函数的方法。策略改进是从价值函数和 MDP 中提取贪婪策略的方法。策略迭代包括在策略评估和策略改进之间交替,以从 MDP 中获得最优策略。策略评估阶段可能需要运行多次迭代,才能准确估计给定策略的价值函数。在策略迭代中,我们等待策略评估找到这个准确的估计。另一种称为价值迭代的方法,截断策略评估阶段并提前退出,进入策略改进阶段。
这些方法的更一般观点是广义策略迭代,它描述了两个过程之间的相互作用以优化策略:一个过程将价值函数估计值逐渐接近当前策略的真实价值函数,另一个过程使用其价值函数估计值来改进当前策略,随着这个循环的持续,策略会逐渐变得越来越好。
到现在为止,
-
了解强化学习代理的目标以及它在任何给定时间可能持有的不同统计数据
-
理解从策略中估计价值函数的方法,以及从价值函数中改进策略的方法
-
你可以在由 MDP 建模的顺序决策问题中找到最优策略
| 在自己的工作中努力并分享你的发现 |
|---|
| | 在每一章的结尾,我会给出几个想法,告诉你如何将所学内容提升到下一个层次。如果你愿意,可以与全世界分享你的成果,并确保查看其他人所做的事情。这是一个双赢的局面,希望你能充分利用它。
-
#gdrl_ch01_tf01: 监督学习、无监督学习和强化学习是机器学习的三个基本分支。虽然了解它们之间的区别至关重要,但同样重要的是了解它们之间的相似之处。写一篇帖子分析这些不同的方法如何比较,以及它们如何可以一起用来解决人工智能问题。所有分支都追求同一个目标:创造通用人工智能,对我们所有人来说,更好地理解如何使用可用的工具是至关重要的。
-
#gdrl_ch01_tf02: 如果你没有机器学习或计算机科学背景,但对这本书提供的内容感兴趣,这并不会让我感到惊讶。一个重要的贡献是发布来自其他研究决策领域的资源。你有没有运筹学背景?心理学、哲学或神经科学背景?控制理论?经济学?你为什么不创建一个资源列表、博客文章、YouTube 视频书籍或其他任何形式的列表,并与我们这些也在研究决策的其他人分享呢?
-
#gdrl_ch01_tf03: 本章的部分文本包含一些信息,这些信息通过图形、表格和其他形式可能解释得更好。例如,我谈到了不同类型的强化学习代理(基于价值的、基于策略的、演员-评论家、基于模型的、无梯度)。你为什么不抓取那些密集的文字,提炼知识,并将你的总结与世界分享呢?
-
#gdrl_ch01_tf04: 在每一章中,我都在使用最后一个标签作为通用的标签。请随意使用这个标签来讨论与本章相关的任何其他你工作过的事情。没有什么比为自己创造作业更令人兴奋的了。确保分享你设定去调查的内容以及你的结果。
用你的发现写一条推文,@mimoralea(我会转发),并使用这个列表中的特定标签来帮助感兴趣的人找到你的结果。没有正确或错误的结果;你分享你的发现并检查他人的发现。利用这个机会社交,做出贡献,让自己脱颖而出!我们正在等待你!以下是一条推文示例:“嘿,@mimoralea。我创建了一篇博客文章,列出了学习深度强化学习的资源。查看它在这里<链接>。#gdrl_ch01_tf01”我会确保转发并帮助他人找到你的工作。|
4 平衡信息的收集和使用
在本章中
-
你将了解从评估反馈中学习的挑战以及如何正确平衡信息的收集和利用。
-
你将开发探索策略,在具有未知转移函数和奖励信号的问题中积累低水平的遗憾。
-
你将使用试错学习代理编写代码,这些代理通过在多选项、单选择环境(称为多臂老丨虎丨机)中的自身经验来学习优化其行为。
不确定性和期望是生活的乐趣。安全感是一种乏味的东西。
—— 威廉·康格里夫,英国复辟时期的剧作家、诗人,以及英国辉格党政治人物
无论一个决策看起来多么小或不重要,你做出的每一个决策都是在信息收集和信息利用之间的权衡。例如,当你去你最喜欢的餐厅时,你应该再次点你最喜欢的菜,还是应该要求尝试你一直想尝试的菜?如果一个硅谷初创公司给你提供一份工作,你应该做出职业变动,还是应该留在你当前的角色中?
这些问题说明了探索-利用困境,并且是强化学习问题的核心。这归结为决定何时获取知识以及何时利用已学到的知识。知道我们已有的好处是否足够好是一个挑战。我们何时满足?何时追求更多?你的想法是什么:手头的一只鸟是否比树上的两只鸟更有价值?
主要问题是生活中的奖励时刻是相对的;你必须比较事件才能清楚地了解它们的价值。例如,我敢打赌,当你被提供第一份工作时,你可能会感到惊讶。你甚至可能认为那是你一生中最美好的事情。但是,然后生活继续,你经历的事情看起来甚至更有回报——也许,当你得到晋升、加薪或结婚时,谁知道呢!
核心问题是:即使你根据“多么令人惊叹”的感觉对迄今为止经历的时刻进行排名,你也不知道你一生中可能经历的最令人惊叹的时刻是什么——生活是不确定的;你没有生活的转移函数和奖励信号,所以你必须继续探索。在本章中,你将了解你的代理在与不确定环境互动时探索的重要性,这些问题中 MDP 不可用于规划。
在上一章中,你学习了从顺序反馈中学习的挑战以及如何正确平衡短期和长期目标。在本章中,我们检查从评估反馈中学习的挑战,我们这样做是在非顺序环境中,而是单次环境:多臂老丨虎丨机(MABs))。
MABs 隔离并暴露了从评估性反馈中学习的挑战。我们将深入研究在特定类型环境中平衡探索和利用的许多不同技术:具有多个选项的单状态环境,但只有一个选择。智能体将在不确定性下操作,也就是说,它们将无法访问 MDP。然而,它们将与没有序列组件的一次性环境进行交互。
记住,在深度强化学习(DRL)中,智能体从同时是评估性反馈的反馈中学习。让我们开始吧。
解释评估性反馈的挑战
在上一章中,当我们解决了 FL 环境时,我们事先就知道环境会对我们的任何行动如何反应。知道环境的精确转移函数和奖励信号使我们能够使用规划算法(如 PI 和 VI)来计算最优策略,而无需与环境进行任何交互。
但是,事先知道一个马尔可夫决策过程(MDP)可能会过分简化问题,可能是不切实际的。我们并不能总是精确地知道环境会对我们的行动如何反应——世界并不是这样运作的。我们可以选择学习这些事情,正如你将在后面的章节中学到的,但关键是我们需要让我们的智能体自己通过与环境的交互和体验来学习,这样它们才能仅从自己的经验中学习到最优的行为。这就是所谓的试错学习。
在强化学习(RL)中,当智能体通过与环境交互来学习行为时,环境会不断地问智能体同样的问题:你现在想做什么?这个问题对一个决策智能体提出了一个基本挑战。它现在应该采取什么行动?智能体应该利用其当前的知识并选择当前估计最高的行动?还是应该探索它还没有尝试足够的行动?但是,还有许多其他问题:你什么时候知道你的估计足够好了?你怎么知道你已经尝试了一个明显不好的行动足够多了?等等。

你将学习到处理探索-利用权衡的更有效的方法
这是关键直觉:探索构建了允许有效利用的知识,而最大化的利用是任何决策者的最终目标。
老丨虎丨机:单状态决策问题
多臂老丨虎丨机(MAB)是强化学习问题的一个特殊情况,其中状态空间的大小和时间步长等于一。MAB 有多个动作,一个状态和一个贪婪的时间步长;你也可以将其视为一个“多选项,单选择”的环境。这个名字来源于可以选择多个臂(老丨虎丨机)的投币机(更现实的是,可以选择多个投币机)。

多臂老丨虎丨机问题
MAB 研究得出的方法在商业应用中有很多。广告公司需要找到一种正确的方式,在预测你可能点击的广告和展示有潜力成为更适合你的新广告之间取得平衡。为慈善机构或政治运动筹集资金的网站需要在展示导致最多贡献的布局和尚未充分利用但仍有更好结果潜力的新设计之间取得平衡。同样,电子商务网站需要在推荐畅销产品以及推荐有潜力的新产品之间取得平衡。在临床试验中,需要尽快了解药物对患者的效果。许多其他问题都受益于探索-利用权衡的研究:石油钻探、游戏和搜索引擎,仅举几例。我们研究 MAB 的原因并不是将其直接应用于现实世界,而是如何将一种适合平衡探索和利用的方法集成到 RL 代理中。
| 展示数学多臂老丨虎丨机 | |
|---|---|
![]() |
悔过:探索的成本
MAB 的目标与 RL 的目标非常相似。在 RL 中,代理需要最大化期望的累积折现奖励(最大化期望回报)。这意味着在环境具有多个状态且代理在每个集中与它进行多次交互的情况下,尽快(如果折现——后期奖励折现更多)通过整个集获得尽可能多的奖励(最大化)。这在环境具有多个状态且代理在每个集中与它进行多次交互的情况下是有意义的。但在 MAB 中,尽管有多个集,我们每个集只有一个选择动作的机会。
因此,我们可以从 RL 目标中排除不适用于 MAB 案例的词语:我们移除了“累积”,因为在每个集中只有一个时间步,以及“折现”,因为没有下一个状态需要考虑。这意味着,在 MAB 中,目标是让代理最大化期望奖励。请注意,“期望”这个词仍然存在,因为环境中存在随机性。实际上,这正是 MAB 代理需要学习的东西:奖励信号的潜在概率分布。
然而,如果我们把目标定为“最大化期望奖励”,那么比较代理就不再简单。例如,假设一个代理通过在除了最后一集之外的所有集中选择随机动作来学习最大化期望奖励,而一个样本效率更高的代理则使用一种巧妙策略快速确定最佳动作。如果我们只比较这些代理在最后一集的表现,这在强化学习中并不少见,这两个代理将会有同样好的表现,这显然不是我们想要的。
捕获更完整目标的一个稳健方法是,让智能体在最大化每集期望奖励的同时,仍然最小化所有集奖励的总期望损失。为了计算这个值,称为总遗憾,我们将每集真实期望奖励与所选动作的真实期望奖励之间的差异相加。显然,总遗憾越低,越好。注意我在这里使用“真实”这个词;为了计算遗憾,你必须能够访问 MDP。这并不意味着你的智能体需要 MDP,只是你需要它来比较智能体的探索策略效率。
| 展示数学公式总遗憾方程 | |
|---|---|
![]() |
解决 MAB 环境的策略
解决多臂老丨虎丨机(MAB)问题主要有三种方法。最流行且最直接的方法是在我们的动作选择过程中注入随机性来进行探索;也就是说,我们的智能体大部分时间会利用,有时会使用随机性进行探索。这一系列方法被称为随机探索策略。这个家族的一个基本例子是,大部分时间选择贪婪动作的策略,并且有一个 epsilon 阈值,随机均匀选择。现在,从这个策略中产生了多个问题;例如,我们应该在整个集数中保持这个 epsilon 值不变吗?我们应该在早期最大化探索吗?我们应该定期增加 epsilon 值以确保智能体始终进行探索?
另一种处理探索-利用困境的方法是保持乐观。是的,你妈妈是对的。乐观探索策略这一系列策略是一种更系统的方法,它量化决策问题中的不确定性,并增加对具有最高不确定性的状态的偏好。底线是,保持乐观会自然地将你引向不确定状态,因为你将假设你尚未经历的状态是最好的。这个假设将帮助你进行探索,随着你探索并直面现实,你的估计将随着它们接近真实值而越来越低。
处理探索-利用困境的第三种方法是信息状态空间探索策略这一系列策略。这些策略将智能体的信息状态建模为环境的一部分。将不确定性作为状态空间的一部分意味着,当未探索或已探索时,环境状态将被以不同的方式看待。将不确定性作为环境的一部分是一种合理的方法,但也会显著增加状态空间的大小及其复杂性。
在这一章中,我们将探讨前两种方法的一些实例。我们将在具有不同属性、优点和缺点的一小批不同的 MAB 环境中这样做,这将使我们能够深入比较策略。
重要的是要注意,在多臂老丨虎丨机(MAB)环境中对 Q 函数的估计相当直接,并且所有策略都会具有共同点。因为 MAB 是单步环境,为了估计 Q 函数,我们需要计算每个动作的平均奖励。换句话说,动作a的估计等于选择动作a时获得的总奖励除以动作a被选择的次数。
必须强调的是,我们在这个章节中评估的策略在估计 Q 函数方面没有差异;唯一的区别在于每个策略如何使用 Q 函数估计来选择动作。
| 一个具体例子滑动老丨虎丨机步(SBW)环境又回来了! | |
|---|---|
我们将要考虑的第一个 MAB 环境是我们之前玩过的:老丨虎丨机滑动步(BSW)。 老丨虎丨机滑动步环境记住,BSW 是一个单行的网格世界,因此是一个步。但这个步的特殊之处在于,智能体从中间开始,任何动作都会立即将智能体送入终端状态。因为它是一次性步,所以它是一个老丨虎丨机环境。BSW 是一个双臂老丨虎丨机,它对智能体来说可能看起来像是一个双臂伯努利老丨虎丨机。伯努利老丨虎丨机以概率p支付+1 的奖励,以概率q = 1 – p支付 0 的奖励。换句话说,奖励信号是一个伯努利分布。在 BSW 中,两个终端状态支付 0 或+1。如果你做数学计算,你会注意到选择动作 0 时获得+1 奖励的概率是 0.2,选择动作 1 时是 0.8。但你的智能体不知道这一点,我们也不会分享这个信息。我们试图问的问题是:你的智能体需要多快才能找出最佳动作?在学会最大化期望奖励的过程中,智能体会积累多少总遗憾?让我们来看看! 老丨虎丨机滑动步图 |
贪婪:总是利用
我想要你考虑的第一个策略实际上不是一个策略,而是一个基线。我已经提到我们需要在我们的算法中有些探索;否则,我们可能会收敛到一个次优动作。但是,为了比较,让我们考虑一个完全没有探索的算法。
这个基线被称为贪婪策略,或纯利用策略。贪婪的动作选择方法包括始终选择具有最高估计值的动作。虽然我们选择的第一动作可能是最佳整体动作,但随着可用的动作数量增加,这种幸运巧合的可能性会降低。

BSW 中的纯利用
如你所料,贪婪策略会立即陷入第一个动作。如果 Q 表初始化为零,并且环境中没有负面奖励,贪婪策略将始终陷入第一个动作。
| 我会说 Python 纯利用策略 | |
|---|---|
def pure_exploitation(env, n_episodes=5000):
Q = np.zeros((env.action_space.n)) ①
N = np.zeros((env.action_space.n)) ②
Qe = np.empty((n_episodes, env.action_space.n))
returns = np.empty(n_episodes) ③
actions = np.empty(n_episodes, dtype=np.int)
name = 'Pure exploitation'
for e in tqdm(range(n_episodes), ④
desc='Episodes for: ' + name, leave=False):
action = np.argmax(Q) ⑤
_, reward, _, _ = env.step(action) ⑥
N[action] += 1 ⑦
Q[action] = Q[action] + (reward - Q[action])/N[action]
Qe[e] = Q
returns[e] = reward ⑧
actions[e] = action
return name, returns, Qe, actions
① 几乎所有策略都有相同的记账代码来估计 Q 值。② 我们将 Q 函数和计数数组初始化为零。③ 这些其他变量用于计算统计数据,不是必要的。④ 我们进入主循环并与环境交互。⑤ 足够简单,我们选择最大化我们估计的 Q 值的动作。⑥ 然后,将其传递给环境并接收新的奖励。⑦ 最后,我们更新计数和 Q 表。⑧ 然后,我们更新统计数据并开始新的回合。|
我想让你注意到贪婪策略和时间之间的关系。如果你的代理只剩下一回合,最好的做法是贪婪行动。如果你知道自己只剩一天可活,你会做你最享受的事情。在某种程度上,这就是贪婪策略所做的:它尽其所能,假设剩下的时间有限。
当你剩下的时间有限时,这样做是合理的;然而,如果你不这样做,那么你看起来是短视的,因为你不能为了获得信息而牺牲即时的满足感或奖励,这些信息将允许你获得更好的长期结果。
随机:始终探索
让我们也考虑光谱的另一端:一种具有探索但没有利用的策略。这是另一个基本的基线,我们可以称之为随机策略或纯探索策略。这仅仅是一种没有任何利用的行动选择方法。代理的唯一目标是获取信息。
你认识那些在开始一个新项目时,花很多时间“研究”而不跳入水中的吗?我也是!他们可以花几周时间阅读论文。记住,虽然探索是必要的,但它必须很好地平衡,以获得最大收益。
随机策略显然也不是一个好的策略,也会给你带来次优的结果。与一直利用类似,你也不想一直探索。我们需要能够同时进行探索和利用的算法:获取并使用信息。
我在代码片段中留下了一条注释,并想重新阐述并扩展它。我提出的纯探索策略是探索的一种方式,即随机探索。但你可以考虑许多其他方式。也许基于计数,即你尝试一个动作与尝试其他动作的次数相比,或者也许基于获得的奖励的方差。
让我们稍微思考一下:虽然只有一种利用的方法,但有多种探索的方法。利用只是做你认为最好的事情;这很简单。你认为 A 是最好的,你就做 A。另一方面,探索要复杂得多。显然,你需要收集信息,但如何收集信息是另一个问题。你可以尝试收集信息来支持你的当前信念。你可以收集信息来试图证明自己是错的。你可以基于自信或基于不确定性进行探索。这个列表可以继续下去。
核心是直观的:利用是你的目标,探索为你提供实现目标的信息。你必须收集信息以达到你的目标,这是显而易见的。但是,除了这一点之外,还有几种收集信息的方法,这就是挑战所在。
epsilon-greedy:几乎总是贪婪,有时是随机的
现在让我们结合两种基线策略,即纯利用和纯探索,这样智能体就可以利用,同时也能收集信息以做出明智的决策。混合策略包括大多数时候贪婪行动,偶尔随机探索。
这种策略,被称为 epsilon-greedy 策略,效果出奇地好。如果您几乎每次都选择您认为最好的动作,您将获得稳定的结果,因为您仍在选择被认为最好的动作,但同时也选择了您尚未充分尝试的动作。这样,您的动作值函数就有机会收敛到其真实值;这反过来又可以帮助您在长期内获得更多奖励。

BSW 中的 epsilon-greedy
| 我会说 PythonEpsilon-greedy 策略 | |
|---|---|
def epsilon_greedy(env, epsilon=0.01, n_episodes=5000):
<...> ①
name = 'E-greedy {}'.format(epsilon)
for e in tqdm(range(n_episodes),
desc='Episodes for: ' + name,
leave=False):
if np.random.random() > epsilon: ②③
action = np.argmax(Q) ③
else:
action = np.random.randint(len(Q)) ④⑤
<...> ⑥
return name, returns, Qe, actions
① 与之前相同:删除了样板文本② epsilon-greedy 策略因其简单性而令人惊讶地有效。它包括偶尔随机选择一个动作。首先,需要抽取一个随机数并与超参数“epsilon”进行比较。③ 如果抽取的数字大于 epsilon,我们选择贪婪动作,即具有最高估计值的动作。④ 否则,我们通过随机选择一个动作来进行探索。⑤ 请注意,这可能会非常有效地产生贪婪动作,因为我们是从所有可用的动作中随机选择动作,包括贪婪动作。您并不是真的以 epsilon 概率进行探索,但这个概率略低于动作的数量。⑥ 删除了估计和统计代码 |
epsilon-greedy 策略是一种随机探索策略,因为我们使用随机性来选择动作。首先,我们使用随机性来决定是利用还是探索,但我们还使用随机性来选择一个探索性动作。还有其他随机探索策略,例如 softmax(将在本章后面讨论),它们没有那个第一个随机决策点。
我想让您注意,如果ε值为 0.5,并且您有两个动作,您不能说您的智能体将会有 50%的时间进行探索,如果“探索”意味着选择非贪婪动作。请注意,ε-greedy 中的“探索步骤”包括贪婪动作。实际上,您的智能体将探索的次数略少于ε值,这取决于动作的数量。
衰减ε-greedy:首先最大化探索,然后最大化利用
直观上,当智能体对环境的体验还不足时,我们希望它进行最多的探索;而后期,当它获得更好的价值函数估计时,我们希望智能体进行更多的利用。机制很简单:从大于或等于 1 的高ε值开始,并在每一步衰减其值。这种策略被称为衰减ε-greedy 策略,其形式取决于您如何改变ε的值。这里我向您展示了两种方法。
| 我会说 Python 线性衰减的ε-greedy 策略 | |
|---|---|
def lin_dec_epsilon_greedy(env,
init_epsilon=1.0,
min_epsilon=0.01,
decay_ratio=0.05,
n_episodes=5000):
<...> ①
name = 'Lin e-greedy {} {} {}'.format(
init_epsilon, min_epsilon, decay_ratio)
for e in tqdm(range(n_episodes),
desc='Episodes for: ' + name,
leave=False):
decay_episodes = n_episodes * decay_ratio ②
epsilon = 1 - e / decay_episodes
epsilon *= init_epsilon - min_epsilon ③
epsilon += min_epsilon
epsilon = np.clip(epsilon, min_epsilon, init_epsilon)
if np.random.random() > epsilon:
action = np.argmax(Q) ④
else:
action = np.random.randint(len(Q))
<...> ⑤
return name, returns, Qe, actions
① 再次,样板代码已删除!② 线性衰减的ε-greedy 策略是通过使ε值随着步数的增加而线性衰减。我们首先计算我们希望将ε衰减到最小值的回合数。③ 然后,计算当前回合的ε值。④ 之后,一切与ε-greedy 策略相同。⑤ 这里也移除了统计数据。|
| 我会说 Python 指数衰减的ε-greedy 策略 | |
|---|---|
def exp_dec_epsilon_greedy(env,
init_epsilon=1.0,
min_epsilon=0.01,
decay_ratio=0.1,
n_episodes=5000):
<...> ①
decay_episodes = int(n_episodes * decay_ratio)
rem_episodes = n_episodes - decay_episodes
epsilons = 0.01
epsilons /= np.logspace(-2, 0, decay_episodes) ②
epsilons *= init_epsilon - min_epsilon
epsilons += min_epsilon
epsilons = np.pad(epsilons, (0, rem_episodes), 'edge')
name = 'Exp e-greedy {} {} {}'.format( init_epsilon, min_epsilon, decay_ratio)
for e in tqdm(range(n_episodes),
desc='Episodes for: ' + name,
leave=False):
if np.random.random() > epsilons[e]: ③
action = np.argmax(Q)
else:
action = np.random.randint(len(Q))
<...> ④
return name, returns, Qe, actions
① 供您参考,代码不完整② 在这里,我们计算指数衰减的ε值。现在,请注意,你可以一次性计算出所有这些值,然后在循环过程中只查询预计算的值数组。③ 其他一切与之前相同。④ 当然,统计数据也被移除了。|
您可以处理ε衰减的许多其他方法:从简单的 1/回合到阻尼正弦波。甚至有相同线性技术和指数技术的不同实现。底线是,智能体应该早期以更高的概率进行探索,并在后期以更高的概率进行利用。早期,价值估计出错的可能性很高。然而,随着时间的推移和知识的积累,您的价值估计接近实际值的可能性增加,这时您应该减少探索的频率,以便利用获得的知识。
乐观初始化:一开始就相信这是一个美好的世界
另一种处理探索-利用困境的有趣方法是,将您尚未充分探索的动作视为最佳可能动作——就像您真的在天堂一样。这类策略被称为面对不确定性的乐观主义。乐观初始化策略是这个类别的一个实例。

BSW 中的乐观初始化
乐观初始化策略的机制很简单:我们将 Q 函数初始化为高值,并使用这些估计贪婪地行动。需要澄清两点:首先,“高值”在强化学习(RL)中是我们无法获取的,我们将在本章后面解决这个问题;但就目前而言,假设我们提前知道了这个数字。其次,除了 Q 值之外,我们还需要将计数初始化为一个大于一的值。如果我们不这样做,Q 函数会变化得太快,策略的效果将会减弱。
| 我会说 Python 乐观初始化策略 | |
|---|---|
def optimistic_initialization(env,
optimistic_estimate=1.0,
initial_count=100,
n_episodes=5000):
Q = np.full((env.action_space.n),
optimistic_estimate, ①
dtype=np.float64)
N = np.full((env.action_space.n),
initial_count, ②
dtype=np.float64)
<...> ③
name = 'Optimistic {} {}'.format(optimistic_estimate,
initial_count)
for e in tqdm(range(n_episodes),
desc='Episodes for: ' + name,
leave=False):
action = np.argmax(Q) ④
<...> ⑤
return name, returns, Qe, actions
① 在这个策略中,我们首先将 Q 值初始化为一个乐观的值。② 我们还初始化了将作为不确定性度量:数值越高,越确定。③ 这里移除了一些代码④ 之后,我们总是选择估计值最高的动作,类似于“纯利用”策略。⑤ 移除了更多代码 |
很有趣,对吧?妈妈是对的!因为智能体最初期望获得的奖励比实际能获得的更多,它会四处探索,直到找到奖励来源。随着它获得经验,“天真”的智能体逐渐消失,也就是说,Q 值会越来越低,直到收敛到它们的实际值。
再次强调,通过将 Q 函数初始化为高值,我们鼓励探索未探索的动作。随着智能体与环境的交互,我们的估计值将开始收敛到更低的、但更准确的估计值,使智能体能够找到并收敛到实际最高收益的动作。
重要的是,如果你打算贪婪地行动,至少要保持乐观。
| 一个具体例子两臂伯努利老丨虎丨机环境 | |
|---|---|
让我们比较我们在一系列双臂伯努利赌博机环境中提出的策略的具体实例。双臂伯努利赌博机环境有一个非终止状态和两个动作。动作 0 有α的概率支付+1 奖励,而 1-α的概率将支付 0 奖励。动作 1 有β的概率支付+1 奖励,而 1-β的概率将支付 0 奖励。这在一定程度上类似于 BSW。BSW 有互补的概率:动作 0 以α的概率支付+1,动作 1 以 1-α的概率支付+1。在这种赌博机环境中,这些概率是独立的;它们甚至可以相等。看看我对双臂伯努利赌博机 MDP 的描述! 双臂伯努利赌博机环境重要的是要注意,有几种不同的方式来表示这个环境。实际上,这并不是我在代码中这样写的,因为这里有很多冗余和不必要的信息。例如,考虑两个终止状态。两个动作可以转换到同一个终止状态。但是,你知道,画出来会让图变得太复杂了。这里的重要教训是你可以自由地以自己的方式构建和表示环境;没有唯一的正确答案。当然有多个错误的方式,但也有多个正确的方式。确保要探索!是的,我提到了那里。 |
|
| 计分板简单探索策略在双臂伯努利赌博机环境中 | |
我运行了所有至今为止提出的策略的两个超参数实例:ε-贪婪、两种衰减和乐观方法,以及纯利用和探索基线,在五个带有概率α和β的双臂伯努利赌博机环境中,这些概率随机初始化,以及五个种子。结果是在 25 次运行中的平均值! 在这个实验中表现最好的策略是具有 1.0 初始 Q 值和 10 个初始计数的乐观方法。所有策略都表现得很不错,这些策略并没有经过高度调整,所以这只是为了乐趣,没有其他原因。前往第四章的笔记本,玩一玩,享受乐趣。 |
|
| 细节简单策略在双臂伯努利赌博机环境中 |
| | 让我们讨论这个实验中的几个细节。首先,我运行了五个不同的种子(12、34、56、78、90)来生成五个不同的双臂伯努利赌博机环境。记住,所有伯努利赌博机以一定的概率为每个臂支付+1 奖励。结果环境和它们的支付概率如下:带有种子 12 的双臂赌博机:
- 奖励概率:[0.41630234, 0.5545003 ]
带有种子 34 的双臂赌博机:
- 奖励概率:[0.88039337, 0.56881791]
带有种子 56 的双臂赌博机:
- 奖励概率:[0.44859284, 0.9499771 ]
带有种子 78 的双臂赌博机:
- 奖励概率:[0.53235706, 0.84511988]
带有种子 90 的双臂投币机:
- 奖励概率:[0.56461729, 0.91744039]
所有种子下的平均最优值是 0.83。所有策略都在上述每个环境中针对五个不同的种子(12, 34, 56, 78, 90)运行,以平滑和消除结果中的随机性。例如,我首先使用种子 12 创建了一个伯努利投币机,然后使用种子 12、34 等,以获取在种子 12 创建的环境中每个策略的性能。然后,我使用种子 34 创建另一个伯努利投币机,并使用 12、34 等来评估在种子 34 创建的环境中每个策略的性能。我为所有策略在所有五个环境中都这样做。总体而言,结果是五个环境和五个种子上的平均值,因此每个策略有 25 次运行。我独立地调整了每个策略,但也进行了手动调整。我使用了大约 10 种超参数组合,并从那些组合中选择了前两个。|
战略性探索
好的,想象一下你被分配了一个任务,那就是编写一个强化学习代理来学习驾驶汽车。你决定实现一个ε-贪婪探索策略。你将你的代理闪入汽车的电脑中,启动汽车,按下那个漂亮的明绿色按钮,然后你的汽车开始探索。它将掷硬币并决定随机采取一个动作,比如开到马路对面。你喜欢吗?对,我也不喜欢。我希望这个例子能帮助说明不同探索策略的必要性。
让我明确一点,这个例子当然是夸张的。你不会直接将未经训练的代理放入现实世界去学习。在现实中,如果你试图在真正的汽车、无人机或一般现实世界中使用 RL,你首先会在模拟中预训练你的代理,或者使用更高效的采样方法。
但是,我的观点仍然成立。如果你这么想,虽然人类在探索,但我们并不是随机探索。也许婴儿是这样的。但不是成年人。也许不精确是我们随机性的来源,但我们不会仅仅因为(除非你去拉斯维加斯)就随机嫁给某人。相反,我认为成年人有更战略性的探索方式。我们知道我们在牺牲短期满足以换取长期满足。我们知道我们想要获取信息。我们通过尝试那些我们没有充分尝试但有可能改善我们生活的事情来探索。也许,我们的探索策略是估计及其不确定性的组合。例如,我们可能更喜欢那些我们可能喜欢但尚未尝试的菜肴,而不是那些我们每周都喜欢的菜肴。也许我们根据我们的“好奇心”或预测误差来探索。例如,我们可能更倾向于尝试在一家我们认为味道可能不错的餐厅的新菜肴,但结果却是你吃过的最好的食物。那种“预测误差”和那种“惊喜”有时可能成为我们探索的指标。
在本章的剩余部分,我们将探讨一些稍微更高级的探索策略。其中一些仍然是随机探索策略,但它们将这种随机性应用于动作的当前估计值。其他探索策略则考虑了估计的置信度和不确定性水平。
话虽如此,我想重申,epsilon-greedy 策略(及其衰减版本)仍然是今天使用最广泛的探索策略,这可能是因为它表现良好,也可能是因为它的简单性。也许是因为大多数今天的强化学习环境都生活在计算机中,虚拟世界中的安全问题很少。认真思考这个问题很重要。平衡探索与利用之间的权衡,收集和利用信息是人类智能、人工智能和强化学习的核心。我确信这一领域的进步将对人工智能、强化学习以及所有对这一基本权衡感兴趣的领域产生重大影响。
Softmax:按估计值成比例随机选择动作
如果随机探索策略考虑了 Q 值估计,那么它们更有意义。通过这样做,如果有一个动作的估计值非常低,我们尝试它的可能性就会降低。有一种称为softmax 策略的策略就是这样做的:它从动作值函数的概率分布中抽取动作,使得选择动作的概率与其当前的动作值估计成正比。这种策略也是随机探索策略家族的一部分,因为它在探索阶段注入了随机性。epsilon-greedy 策略从给定状态可用的完整动作集中均匀随机抽取,而 softmax 策略则基于高值动作的偏好进行抽取。
通过使用 softmax 策略,我们实际上是将动作值估计作为偏好的指标。值的高低并不重要;如果你给所有值加上一个常数,概率分布将保持不变。你将偏好放在 Q 函数上,并从这个偏好中根据概率分布抽取一个动作。Q 值估计之间的差异将产生一种倾向,即更频繁地选择估计值最高的动作,而较少选择估计值最低的动作。
我们还可以添加一个超参数来控制算法对 Q 值估计差异的敏感性。这个超参数被称为温度(参考统计力学),其工作方式是这样的:当它接近无穷大时,对 Q 值的偏好是相等的。基本上,我们均匀地采样一个动作。但是,当温度值接近零时,具有最高估计值的动作将以 100%的概率被采样。此外,我们可以以线性、指数或其他方式衰减这个超参数。但在实践中,出于数值稳定性的原因,我们不能使用无穷大或零作为温度;相反,我们使用一个非常高或非常低的正实数,并归一化这些值。
| Show Me The MathSoftmax 探索策略 | |
|---|---|
![]() |
|
| 我会说 PythonSoftmax 策略 | |
def softmax(env,
init_temp=1000.0,
min_temp=0.01,
decay_ratio=0.04,
n_episodes=5000):
<...> ①
name = 'SoftMax {} {} {}'.format(init_temp,
min_temp,
decay_ratio)
for e in tqdm(range(n_episodes),
desc='Episodes for: ' + name,
leave=False):
decay_episodes = n_episodes * decay_ratio
temp = 1 - e / decay_episodes
temp *= init_temp - min_temp ②
temp += min_temp
temp = np.clip(temp, min_temp, init_temp) ③
scaled_Q = Q / temp
norm_Q = scaled_Q - np.max(scaled_Q) ④⑤
exp_Q = np.exp(norm_Q)
probs = exp_Q / np.sum(exp_Q)
assert np.isclose(probs.sum(), 1.0)
action = np.random.choice(np.arange(len(probs)), ⑥
size=1,
p=probs)[0]
_, reward, _, _ = env.step(action)
<...> ⑦
return name, returns, Qe, actions
① 为了简化,这里删除了代码。② 首先,我们以与线性衰减 epsilon 相同的方式计算线性衰减的温度。③ 我确保 min_temp 不是 0,以避免除以零。详细信息请查看笔记本。④ 接下来,我们通过将 softmax 函数应用于 Q 值来计算概率。⑤ 归一化以保持数值稳定性。⑥ 最后,我们确保得到了良好的概率,并根据这些概率选择动作。⑦ 同样,这里也删除了代码。 |
UCB:这不仅仅是乐观,而是现实的乐观
在上一节中,我介绍了乐观初始化策略。这是一种巧妙(也许可以说是哲学性的)处理探索与利用权衡的方法,并且是面对不确定性时乐观策略家族中最简单的方法。但是,我们之前考虑的特定算法有两个显著的不便之处。首先,我们并不总是知道智能体可以从环境中获得的最大奖励。如果你将乐观策略的初始 Q 值估计设置得远高于其实际最大值,不幸的是,算法将表现不佳,因为智能体需要很多个回合(取决于“计数”超参数)才能将估计值接近实际值。但更糟糕的是,如果你将初始 Q 值设置得低于环境的最大值,算法将不再乐观,并且将不再起作用。
这种策略的第二个问题是我们所提出的,即“counts”变量是一个超参数,需要调整,但在现实中,我们试图用这个变量表示的是估计的不确定性,这不应该是一个超参数。一个更好的策略,而不是一开始就相信一切都会顺利,随意设置确定性度量值,遵循乐观初始化的相同原则,同时使用统计技术来计算值估计的不确定性,并将其作为探索的额外奖励。这正是上置信界(UCB)策略所做的事情。
在 UCB 中,我们仍然保持乐观,但这是一种更现实的乐观;而不是盲目地希望最好的结果,我们关注价值估计的不确定性。Q 值估计的不确定性越大,探索它的必要性就越大。请注意,这不再是相信值将是“最大可能的”,尽管它可能是!我们在这里关心的新指标是不确定性;我们希望给不确定性带来好处。
| 展示我数学上置信界(UCB)方程 | |
|---|---|
![]() |
要实现这种策略,我们选择具有最高 Q 值估计和动作不确定性奖励 U 的动作。也就是说,我们将添加一个奖励,即上置信界uU奖励值,因为我们对 Q 值估计更有信心;它们不是那么需要探索。
| 我会说 Python 上置信界(UCB)策略 | |
|---|---|
def upper_confidence_bound(env,
c=2,
n_episodes=5000):
<...> ①
name = 'UCB {}'.format(c)
for e in tqdm(range(n_episodes),
desc='Episodes for: ' + name,
leave=False):
if e < len(Q): ②
action = e
else:
U = np.sqrt(c * np.log(e)/N) ③
action = np.argmax(Q + U) ④
<...> ⑤
return name, returns, Qe, actions
① 为了简洁起见,删除了代码。② 我们首先选择所有动作一次,以避免除以零。③ 然后,继续计算置信界限。④ 最后,我们选择具有最高价值并带有不确定性奖励的动作:动作的价值越不确定,奖励就越高。⑤ 为了简洁起见,删除了统计代码。
在实际层面上,如果你将u作为剧集和计数的函数绘制出来,你会注意到它与指数衰减函数非常相似,但有几点不同。与显示平滑衰减的指数函数不同,它一开始有急剧的衰减和长长的尾巴。这使得在剧集数量低的时候,动作之间的较小差异会有更高的奖励,但随着剧集的增加和计数的增加,不确定性奖励的差异会变得较小。换句话说,在 100 次尝试与 200 次尝试中,0 次尝试应该比 100 次尝试得到更高的奖励。最后,c超参数控制奖励的规模:c值越高,奖励越高,c值越低,奖励越低。
汤普森抽样:平衡奖励和风险
UCB 算法是一种频率派处理探索与利用权衡的方法,因为它对 Q 函数背后的分布做出了最小的假设。但其他技术,如贝叶斯策略,可以使用先验来做出合理的假设并利用这些知识。汤普森采样策略是一种基于样本的概率匹配策略,它允许我们使用贝叶斯技术来平衡探索与利用的权衡。
实现这种策略的一种简单方法是跟踪每个 Q 值作为高斯分布(即正态分布)。实际上,你可以使用任何其他类型的概率分布作为先验;例如,beta 分布是一个常见的选择。在我们的情况下,高斯均值是 Q 值估计,高斯标准差衡量估计的不确定性,这些都会在每个剧集中进行更新。

比较表示为高斯分布的两个动作值函数
如其名称所示,在汤普森采样中,我们从这些正态分布中进行采样,并选择返回最高样本值的动作。然后,为了更新高斯分布的标准差,我们使用一个类似于 UCB 策略的公式,在早期,当不确定性较高时,标准差更为重要;因此,高斯分布较宽。但随着剧集的进行,均值逐渐向更好的估计值移动,标准差降低,高斯分布缩小,因此其样本更有可能接近估计的均值。
| 我会说 Python 汤普森采样策略 | |
|---|---|
def thompson_sampling(env,
alpha=1,
beta=0,
n_episodes=5000):
<...> ①
name = 'Thompson Sampling {} {}'.format(alpha, beta)
for e in tqdm(range(n_episodes),
desc='Episodes for: ' + name,
leave=False):
samples = np.random.normal(
loc=Q, scale=alpha/(np.sqrt(N) + beta)) ②
action = np.argmax(samples) ③
<...> ④
return name, returns, Qe, actions
① 初始化代码已移除② 在我们的实现中,我们将从高斯分布中采样数字。注意‘scale’(即高斯宽度,标准差)随着我们尝试每个动作的次数而缩小。同时,注意‘alpha’控制高斯初始宽度,而‘beta’控制它们缩小的速率。③ 然后,我们选择样本值最高的动作。④ 统计代码已移除 |
在这个特定的实现中,我使用了两个超参数:alpha,用于控制高斯的比例,即初始标准差的大小,以及 beta,用于调整衰减速率,使得标准差缩小得更慢。在实践中,这些超参数对于本章的示例需要很少的调整,因为,正如你可能已经知道的,标准差仅为五,例如,几乎是一个看起来平坦的高斯分布,代表着超过十单位的范围。鉴于我们的问题具有介于 0 和 1 之间的奖励(和 Q 值),以及大约介于-3 和 3 之间(下一个示例),我们不需要标准差太大的高斯分布。
最后,我想再次强调,使用高斯分布可能是 Thompson 抽样的最不常见的方法。Beta 分布似乎是这里的首选。我之所以喜欢这些问题的 Beta 分布,是因为它们围绕均值的对称性,以及它们的简单性使它们适合教学目的。然而,我鼓励你更深入地研究这个主题,并分享你发现的内容。
摘要
从评估反馈中学习是一个基本挑战,这使得强化学习与众不同。当从评估反馈中学习时,即+1、+1.345、+1.5、-100、-4,你的智能体不知道底层的 MDP,因此无法确定它能获得的最大奖励是多少。你的智能体“想”: “好吧,我得到了+1,但我不确定,也许这块石头下面有一个+100?”这种环境中的不确定性迫使你设计出探索的智能体。
但正如你所学的,你不能轻视探索。从根本上讲,探索浪费了本可以用来最大化奖励的周期,用于利用,然而,你的智能体在收集信息之前无法最大化奖励,或者至少假装它能,这就是探索的作用。突然之间,你的智能体必须学会平衡探索和利用;它必须学会妥协,在两个至关重要的但相互竞争的方面之间找到平衡。我们都在生活中面临过这种基本的权衡,所以这些问题应该对你来说很直观:“手中有鸟胜于林中两鸟”,然而“人的抱负应超越其能力”。选择你的毒药,享受这个过程,只是不要陷入其中任何一个。平衡它们! |
了解这个基本权衡后,我们介绍了几种不同的技术来创建智能体或策略,以平衡探索和利用。epsilon-greedy 策略通过大部分时间利用和一小部分时间探索来实现。这一探索步骤是通过随机采样一个动作来完成的。衰减 epsilon-greedy 策略捕捉到智能体最初需要更多探索的事实,因为它们需要收集信息以开始做出正确的决策,但它们应该迅速开始利用,以确保它们不会积累遗憾,遗憾是衡量我们行动距离最优程度的一个指标。衰减 epsilon-greedy 策略随着剧集的增加而衰减 epsilon,希望随着我们的智能体收集信息而增加。
但然后我们学习了其他策略,这些策略试图确保“希望”更有可能实现。这些策略考虑了估计及其不确定性、潜力和可能性,并据此进行选择:乐观初始化、UCB、Thompson 抽样,尽管 softmax 实际上并不使用不确定性度量,但它通过在估计比例中随机选择来探索。
到现在为止,
-
理解从评估反馈中学习的挑战在于智能体无法看到控制其环境的底层 MDP
-
学到探索与利用之间的权衡源于这个问题
-
了解许多常用的策略来处理这个问题
| 在自己的工作中努力并分享你的发现 |
|---|
| | 这里有一些想法,可以帮助你将所学知识提升到新的水平。如果你愿意,可以将你的结果分享给全世界,并确保查看其他人所做的事情。这是一个双赢的局面,希望你能充分利用它。
-
#gdrl_ch04_tf01: 解决多臂老丨虎丨机环境的技巧还有很多。尝试探索其他资源,并告诉我们哪些技巧是重要的。研究基于贝叶斯的方法进行动作选择,以及基于信息增益的动作选择策略。什么是信息增益?为什么在强化学习(RL)的背景下这很重要?你能开发出其他有趣的动作选择策略,包括使用信息衰减探索率的衰减策略吗?例如,想象一个根据状态访问次数衰减 epsilon 的智能体——也许基于另一个指标。
-
#gdrl_ch04_tf02: 你能想到一些其他有趣的带臂老丨虎丨机环境来研究吗?克隆我的带臂老丨虎丨机仓库(
github.com/mimoralea/gym-bandits-它也被分叉了,)并向其中添加一些其他带臂老丨虎丨机环境。 -
#gdrl_ch04_tf03: 在 Bandit 环境之后,但在强化学习算法之前,还有一种被称为上下文 Bandit 问题的环境。这些是什么类型的问题?你能帮助我们理解这些是什么吗?但是,不要只是写一篇关于它们的博客文章。还要创建一个带有上下文 Bandit 的 Gym 环境。这是否可能?将这些环境创建在一个 Python 包中,并创建另一个 Python 包,其中包含可以解决上下文 Bandit 环境的算法。
-
#gdrl_ch04_tf04: 在每一章中,我都在使用最后的标签作为一个总标签。自由使用这个标签来讨论任何与你在这章中工作的相关内容。没有比你自己创建的作业更有趣的了。确保分享你设定要调查的内容和你的结果。
用你的发现写一条推文,@提及我 @mimoralea(我会转发),并使用这个列表中的特定标签来帮助感兴趣的人找到你的结果。没有对错之分;你分享你的发现,并检查他人的发现。利用这个机会社交,做出贡献,让自己更受关注!我们正在等待你的加入!以下是一条推文示例:“嘿,@mimoralea。我创建了一篇博客文章,列出了学习深度强化学习的资源列表。查看它在这里<链接>。#gdrl_ch01_tf01”我会确保转发并帮助他人找到你的作品。|
5 评估智能体的行为
在本章中
-
你将学习在从同时具有顺序性和评价性的反馈中学习时如何估计策略。
-
当状态转移和奖励函数未知时,你将开发算法来评估强化学习环境中的策略。
-
你将为在显示完整强化学习问题的环境中估计策略的价值编写代码。
我认为人类大部分的痛苦都是由于他们对事物价值的错误估计而带来的。
—— 本杰明·富兰克林 美国开国元勋,作家,政治家,发明家和公民活动家
你知道平衡短期和长期目标是多么具有挑战性。你可能每天都会多次体验到这一点:今晚你应该看电影还是继续阅读这本书?一部电影能给你带来即时的满足感;你看完电影,就从贫穷到富有,从孤独到爱情,从超重到健康,等等,大约两个小时,而且还能吃爆米花。另一方面,阅读这本书今晚可能不会给你带来太多,但也许,只是也许,会在长期提供更高的满足感。
这正是我们之前讨论的另一个问题的完美过渡。在长期中,你将获得多少额外的满足感,确切地说,你可能会问。我们能知道吗?有没有一种方法可以找出答案?嗯,这就是生活的美妙之处:我不知道,你也不知道,除非我们尝试,除非我们利用。
然而,在前一章中,我们独立于强化学习的顺序方面研究了这一挑战。基本上,你假设你的行为没有长期影响,你唯一关心的是找到当前情况下最佳的选择。例如,你的关注点可能是选择一部好电影,或者一本好书,但不会考虑电影或书籍将如何影响你余下的生活。在这里,你的行为没有“累积效应。”
现在,在本章中,我们来看看那些从同时具有顺序性和评价性的反馈中学习的智能体;智能体需要同时平衡继续阅读?提示!
你很聪明... . 在本章中,我们将研究能够学习到强化学习同等重要方面的智能体。在机器学习中,有句俗语说,“模型的好坏取决于数据。”在强化学习中,我说,“策略的好坏取决于估计的准确性,”或者更详细地说,“策略的改进程度取决于其估计的准确性和精确度。”
再次强调,在深度强化学习(DRL)中,智能体从同时具有顺序性(与一次性相反)、评价性(与监督学习相反)和抽样(与穷举相反)的反馈中学习。在本章中,我们关注的是那些从同时具有顺序性和评价性的反馈中学习的智能体。我们暂时将“抽样”部分放在一边,但在第八章中,我们将打开这些大门,那里将充满乐趣。我保证。
学习估计策略的价值
如我之前所述,本章是关于学习估计现有策略的价值。当我最初接触到这个预测问题时,我没有理解其动机。对我来说,如果你想估计策略的价值,最直接的方法是重复运行策略并平均你所得到的结果。
而且,这确实是一个有效的途径,也许是最自然的。然而,我当时并没有意识到,还有许多其他方法可以用来估计值函数。这些方法各有优缺点。许多方法可以被视为完全相反的替代方案,但同时也存在一个中间地带,它创造了一个完整的算法谱系。
在本章中,我们将探讨这些方法的多样性,深入分析它们的优缺点,并展示它们是如何相互关联的。
| ŘŁ | 带有强化学习口音的奖励、回报与值函数 |
|---|---|
| 奖励:指的是智能体获得的一步奖励信号:智能体观察到一个状态,选择一个动作,并接收到奖励信号。奖励信号是强化学习的核心,但并非智能体试图最大化的目标!再次强调,智能体并不是试图最大化奖励!意识到,虽然你的智能体在最大化一步奖励,但从长远来看,它获得的奖励可能少于它本可以获得的。回报:指的是总折扣奖励。回报是从任何状态计算得出的,通常持续到剧集结束。也就是说,当达到终端状态时,计算停止。回报通常被称为总奖励、累积奖励、奖励总和,并且通常是折扣的:总折扣奖励、累积折扣奖励、折扣奖励总和。但基本上是相同的:回报告诉你智能体在一个剧集中所获得的奖励有多少。正如你所看到的,回报是更好的性能指标,因为它们包含一个长期序列,一个单剧集的奖励历史。但回报也不是智能体试图最大化的目标!试图获得最高回报的智能体可能会找到一条噪声路径;有时,这条路径会提供高回报,但也许大多数时候回报较低。值函数:指的是回报的期望。当然,我们希望获得高回报,但高期望(平均)。如果智能体处于噪声环境中,或者如果智能体使用随机策略,那都是可以的。毕竟,智能体试图最大化的是期望的总折扣奖励:值函数。 | |
| 米格尔的类比奖励、回报、值函数与生活 | |
| 你如何面对生活?你是选择对自己最有利的行动,还是你是那些优先考虑他人而先考虑自己的人?两种方式都没有什么可耻的!对我来说,自私是一种极好的奖励信号。它带你走向成功。它推动你前进。在生活的早期,追求即时奖励可以是一个相当稳固的策略。许多人因为别人“太自私”而评判他们,但对我来说,这是开始行动的方式。去做你想做的事,去追求你的梦想,去做让你满足的事,去追求奖励!你看起来自私且贪婪。但你不应该在意。随着你继续前进,你会意识到追求奖励并不是最好的策略,即使是对你自己的好处。你开始看到更大的图景。如果你吃太多糖果,你的肚子会疼;如果你把所有的钱都花在网购上,你可能会破产。最终,你开始关注回报。你开始理解你的自私和贪婪动机背后还有更多。你放弃了贪婪的一面,因为它会从长远上伤害你,现在你意识到了这一点。但你还保持着自私,你仍然只从奖励的角度思考,但现在是从“总”奖励,即回报的角度。对此也不必感到羞耻!在某个时刻,你会意识到世界在没有你的情况下运转,世界比你最初想象的要复杂得多,世界有难以理解的潜在动态。你现在知道,“善有善报,恶有恶报”,不管以何种方式,不管何时,它都会发生。你再次退后一步;现在,你不再追求奖励或回报,而是追求价值函数。你变得聪明了!你意识到,你帮助他人学习得越多,你学到的就越多。不知道为什么,但这是真的。你越爱你的人生伴侣,他们就越爱你,不可思议!你越不花钱(储蓄),你就能花得越多。多么奇怪!注意,你仍然自私!但你现在意识到了世界的复杂潜在动态,并理解到,对自己最好的方式是帮助他人——这是一个完美的双赢局面。我希望奖励、回报和价值函数之间的区别能深深地印在你的心中,所以希望这能让你思考一会儿。跟随奖励!然后,是回报!然后,是价值函数。 | |
| 一个具体的例子随机游走环境 | |
在本章中,我们将主要使用一个称为随机游走(RW)的环境。这是一个单行网格世界环境,具有五个非终端状态。但它很特别,所以我想要从两个角度来解释它。一方面,你可以将 RW 视为一个环境,其中无论采取什么行动,向右走的概率都是 50%。毕竟,它是一个随机游走,疯狂!! 随机游走环境 MDP 但对我来说,这并不是对 RW 令人满意的解释,也许是因为我喜欢代理控制某物的想法。在没有任何可能控制的环境中进行 RL(一个用于学习最优控制的框架)研究有什么意义呢!因此,你可以将 RW 视为一个具有确定性转移函数的环境(意味着如果代理选择左转,代理就会左转,如果选择右转,就会右转——正如预期的那样)。但假设代理想要评估一个随机策略,该策略以均匀随机的方式选择动作。一半的时间选择左转,另一半时间选择右转。无论如何,概念是相同的:我们有一个五个非终端状态的游走,其中代理以均匀随机的方式左右移动。目标是估计在这些情况下代理可以获得的总折现奖励的期望值。 |
首次访问蒙特卡洛:每轮后改进估计
好吧!目标是估计策略的价值,即学习从策略中期望获得多少总奖励。更确切地说,目标是估计策略π的状态值函数 vπ(s)。最直接的方法就是我之前提到的方法:就是运行几个带有此策略的回合,收集数百个轨迹,然后计算每个状态的平均值,就像我们在老丨虎丨机环境中做的那样。这种估计值函数的方法称为蒙特卡洛预测(MC)。
MC 易于实现。代理将首先使用策略π与环境交互,直到代理达到终端状态 s[T]。状态 s[t],动作 A[t],奖励 r[t+1]和下一个状态 s[t+1]的集合称为一个经验元组。一系列经验称为一个轨迹。你需要做的第一件事是让你的代理生成一个轨迹。
一旦你有了轨迹,你将为遇到的每个状态 s[t] 计算回报 g[t:T]。例如,对于状态 s[t],你从时间步 t 开始向前,累加并折现沿途收到的奖励:r[t+1], R[t+2], R[t+3], ... , R[T],直到轨迹在时间步 t 结束。然后,你重复这个过程为状态 s[t+1],累加从时间步 t+1 到达 t 的折现奖励;然后为 s[t+2] 以及所有其他状态,除了 s[T],根据定义其值为 0。g[t:T] 将最终使用时间步 t+1 到轨迹结束的奖励,使用指数衰减折现因子:γ⁰, γ¹, γ²*, ... , γ^(T-1)。这意味着将相应的折现因子 γ 乘以奖励 r,然后累加沿途的乘积。
在生成轨迹并计算所有状态 s[t] 的回报后,你只需通过平均从每个状态 s 获得的回报,就可以在每次剧集 e 和最终时间步 t 结束时估计状态值函数 vπ(s)。换句话说,我们正在用平均值估计一个期望值。就这么简单。

蒙特卡洛预测
| 展示数学:蒙特卡洛学习 | |
|---|---|
![]() |
每次访问蒙特卡洛:处理状态访问的不同方式
你可能注意到在实践中,实现平均回报算法有两种不同的方式。这是因为单个轨迹可能包含对同一状态的多次访问。在这种情况下,我们应该独立计算每次访问的回报,然后将所有这些目标包括在平均值中,还是我们只使用每个状态的首次访问?
这两种方法都是有效的,并且它们具有相似的理论特性。更“标准”的版本是首次访问蒙特卡洛(FVMC),其收敛特性很容易证明,因为每个轨迹都是 vπ(s) 的独立同分布(IID)样本,因此当我们收集无限样本时,估计将收敛到它们的真实值。每次访问蒙特卡洛(EVMC)略有不同,因为当状态在相同轨迹中被多次访问时,回报不再是独立同分布的。但幸运的是,EVMC 也已被证明在无限样本的情况下会收敛。
| 简化:首次访问与每次访问蒙特卡洛 | |
|---|---|
| MC 预测估计 v**π 为 π 的回报的平均值。FVMC 每个状态在每个剧集只使用一个回报:首次访问后的回报。EVMC 平均所有访问到状态的回报,即使在同一剧集内也是如此。 | |
| 0001 | 一点历史:首次访问蒙特卡洛预测 |
| 你可能之前听说过“蒙特卡洛模拟”或“运行”这个术语。一般来说,蒙特卡洛方法自 20 世纪 40 年代以来一直存在,是一类广泛使用的算法,它使用随机抽样进行估计。它们历史悠久且应用广泛。然而,直到 1996 年,Satinder Singh 和 Richard Sutton 在论文“具有替换资格痕迹的强化学习”中首次确定了首次和每次访问的 MC 方法。Satinder Singh 和 Richard Sutton 都从马萨诸塞大学阿默斯特分校获得了计算机科学博士学位,由 Andy Barto 教授指导,由于他们的许多基础性贡献,成为了强化学习领域的杰出人物,现在他们是 Google DeepMind 的杰出研究科学家。Rich 在 1984 年获得了博士学位,是阿尔伯塔大学的教授,而 Satinder 在 1994 年获得了博士学位,是密歇根大学的教授。 | |
| 我会说 Python 指数衰减计划 | |
def decay_schedule(init_value, min_value, ①
decay_ratio, max_steps,
log_start=-2, log_base=10):
decay_steps = int(max_steps * decay_ratio) ②
rem_steps = max_steps - decay_steps
values = np.logspace(log_start, 0, decay_steps,
base=log_base, endpoint=True)[::-1]
values = (values - values.min()) / \ ③
(values.max() - values.min())
values = (init_value - min_value) * values + min_value
values = np.pad(values, (0, rem_steps), 'edge')
return values
① 此函数允许您计算整个训练过程中 alpha 的所有值。② 首先,使用 decay_ratio 变量计算衰减值的步数。③ 然后,将实际值计算为倒数对数曲线。注意我们随后将其归一化到 0 和 1 之间,最后将点转换到 init_value 和 min_value 之间。 |
| 我会说 Python 生成完整轨迹 | |
|---|---|
def generate_trajectory(pi, env, max_steps=20):
done, trajectory = False, []
while not done: ①
state = env.reset()
for t in count():
action = pi(state)
next_state, reward, done, _ = env.step(action)
experience = (state, action, reward,
next_state, done)
trajectory.append(experience)
if done: break
if t >= max_steps - 1: ②
trajectory = [] break
state = next_state
return np.array(trajectory, np.object)
① 这是一个简单的函数。它正在运行策略并提取用于离线处理的经验元组(轨迹)。② 这允许你传递最大步数,以便你可以根据需要截断长轨迹。 |
| 我会说 Python 蒙特卡洛预测 1/2 | |
|---|---|
def mc_prediction(pi, ①
env,
gamma=1.0,
init_alpha=0.5,
min_alpha=0.01,
alpha_decay_ratio=0.3, ②
n_episodes=500,
max_steps=100,
first_visit=True):
nS = env.observation_space.n
discounts = np.logspace( ③
0, max_steps, num=max_steps,
base=gamma, endpoint=False)
alphas = decay_schedule( ④
init_alpha, min_alpha,
alpha_decay_ratio, n_episodes)
V = np.zeros(nS) ⑤
V_track = np.zeros((n_episodes, nS))
for e in tqdm(range(n_episodes), leave=False): ⑥
trajectory = generate_trajectory( ⑦
pi, env, max_steps)
visited = np.zeros(nS, dtype=np.bool) ⑧
for t, (state, _, reward, _, _) in enumerate( ⑨
trajectory):
① mc_prediction 函数适用于首次访问和每次访问的 MC。您在这里看到的超参数是标准的。记住,折现因子 gamma 取决于环境。② 对于学习率 alpha,我使用从 init_alpha 的 0.5 衰减到 min_alpha 的 0.01 的衰减值,在前 500 个总最大剧集的 30%内衰减(alpha_decay_ratio 为 0.3)。我们已经在之前的功能中讨论了 max_steps,所以我正在传递这个参数。并且 first_visit 在 FVMC 和 EVMC 之间切换。③ 这很酷。我一次计算所有可能折扣。这个 logspace 函数对于 gamma 为 0.99 和最大步数为 100 返回一个 100 个数字的向量:[1, 0.99, 0.9801, ..., 0.3697]。④ 这里我计算所有的 alpha!⑤ 这里我们初始化在主循环内部使用的变量:当前状态价值函数的估计 V,以及每个剧集的 V 副本,用于离线分析。⑥ 我们为每个剧集循环。注意,我们在这里使用‘tqdm’。这个包会打印进度条,对我来说很有用。你可能不需要它(除非你也像我一样不耐烦)。⑦ 生成完整轨迹。⑧ 初始化访问检查布尔向量。⑨ 这最后一行在下一页重复出现是为了方便您阅读。 |
| 我会说 Python 蒙特卡洛预测 2/2 | |
|---|---|
for t, (state, _, reward, _, _) in enumerate( ⑩⑪
trajectory):
if visited[state] and first_visit: ⑫
continue ⑬
visited[state] = True
n_steps = len(trajectory[t:]) ⑭⑮
G = np.sum(discounts[:n_steps] * trajectory[t:, 2]) ⑯
V[state] = V[state] + alphas[e] * (G - V[state]) ⑰
V_track[e] = V ⑱
return V.copy(), V_track ⑲
⑩ 这第一行在上一页重复出现是为了方便您阅读。⑪ 我们现在遍历轨迹中的所有经验。⑫ 检查状态是否已经在该轨迹上访问过,并执行 FVMC。⑬ 如果是,我们处理下一个状态。⑭ 如果这是第一次访问或者我们在执行 EVMC,我们处理当前状态。⑮ 首先,计算从 t 到 T 的步数。⑯ 然后,计算回报。⑰ 最后,估计价值函数。⑱ 跟踪这一幕的 V。⑲ 完成后返回 V 和跟踪信息。 |
| ŘŁ | 带有强化学习口音的增量与顺序与试错 |
|---|---|
| 增量方法:指的是估计的迭代改进。动态规划是一种增量方法:这些算法迭代地计算答案。它们不与环境“交互”,而是通过连续迭代、增量地达到答案。多臂老丨虎丨机也是增量方法:它们通过连续的剧集或试验达到良好的近似。强化学习也是增量方法。根据具体算法,估计是在每个剧集或每个时间步的基础上增量改进的。顺序方法:指的是在具有多个非终止(且可到达)状态的环境中学习。动态规划是一种顺序方法。多臂老丨虎丨机不是顺序方法,它们是一状态一步马尔可夫决策过程。代理的行为没有长期后果。强化学习肯定是顺序的。试错方法:指的是通过与环境的交互进行学习。动态规划不是试错学习。多臂老丨虎丨机是试错学习。强化学习也是试错学习。 |
时间差分学习:每一步改进估计
蒙特卡洛方法的一个主要缺点是代理必须等待到剧集结束时才能获得实际回报 g[t:T],然后才能更新状态值函数估计 v**T。一方面,蒙特卡洛方法具有相当稳健的收敛性质,因为它将值函数估计 v**T 更新到实际回报 g[t]:[T],这是一个对真实状态值函数 v**π 的无偏估计。
然而,尽管实际回报的估计相当准确,但它们也不是非常精确。实际回报也是对真实状态值函数 v**π 的高方差估计。这很容易理解:实际回报在相同的轨迹中累积了许多随机事件;所有动作、所有下一个状态、所有奖励都是随机事件。实际回报 G[t:T] 收集并复合了所有这些随机性,跨越多个时间步,从 t 到 T。再次强调,实际回报 G[t:T] 是无偏的,但方差很高。
此外,由于实际回报 g[t]:[T] 的高方差,蒙特卡洛方法可能样本效率低下。所有这些随机性都变成了噪声,只能通过数据、大量数据、大量轨迹和实际回报的估计来缓解,即使只是部分估计?想想看!
是的!你可以使用单步奖励r[t+1],一旦你观察到下一个状态s[t+1],你可以使用状态值函数估计v(S[t+1])作为下一个步骤g[t+1:][T]的回报估计。这是时序差分(TD)方法所利用的关系。这些方法与 MC 不同,可以通过使用单步实际回报(即立即奖励r[t+1]),然后从下一个状态开始估计回报,即下一个状态的状态值函数估计v(S[t+1]):即r[t+1] + γ**v(S[t+1]),这被称为TD 目标。 |
| 简化时序差分学习和自举 | |
|---|---|
| TD 方法使用对v**π的估计来估计v**π。它通过从猜测中猜测来自举;它使用估计的回报而不是实际回报。更具体地说,它使用r[t+1] + γv**t来计算和估计v**t+1。因为它也使用了一步的实际回报r[t+1],所以一切正常。这个奖励信号r[t+1]逐渐将“现实”注入到估计中。 | |
| 展示数学时序差分学习方程 | |
![]() |
|
| 我会说 Python 时序差分学习算法 | |
def td(pi, ①
env,
gamma=1.0,
init_alpha=0.5, ②
min_alpha=0.01, ③
alpha_decay_ratio=0.3,
n_episodes=500):
nS = env.observation_space.n
V = np.zeros(nS) ④
V_track = np.zeros((n_episodes, nS))
alphas = decay_schedule( ⑤
init_alpha, min_alpha,
alpha_decay_ratio, n_episodes)
for e in tqdm(range(n_episodes), leave=False): ⑥
state, done = env.reset(), False
while not done: ⑦
action = pi(state) ⑧
next_state, reward, done, _ = env.step(action) ⑨
td_target = reward + gamma * V[next_state] * \ ⑩
(not done)
td_error = td_target - V[state] ⑪
V[state] = V[state] + alphas[e] * td_error ⑫
state = next_state ⑬
V_track[e] = V
return V, V_track ⑭
① td 是一种预测方法。它接受一个策略 pi,一个与环境交互的环境 env,以及折扣因子 gamma。②学习方法有一个可配置的超参数 alpha,它是学习率。③处理学习率的一种方法是对其进行指数衰减。初始值是 init_alpha,min_alpha,最小值,alpha_decay_ratio 是 alpha 从 init_alpha 衰减到 min_alpha 所需剧集的分数。④我们初始化所需的变量。⑤然后我们计算所有剧集的学习率计划...⑥...然后循环 n_episodes。⑦我们获取初始状态然后进入交互循环。⑧首先是要采样策略 pi 以在状态中采取行动。⑨然后我们使用行动与环境交互...我们执行策略的一步。⑩我们可以立即计算一个目标来更新状态值函数的估计...⑪...然后,有了目标,一个误差。⑫最后更新V(s)⑬不要忘记更新下一次迭代的变量。这种错误可能很难找到!⑭然后返回 V 函数和跟踪变量。 |
| ŘŁ | 带有强化学习口音的真实与实际与估计 |
|---|---|
| 真实值函数:指的是精确且完美准确的价值函数,就像由先知给出的一样。真实值函数是代理通过样本估计的价值函数。如果我们有真实值函数,我们就可以很容易地估计回报。实际回报:指的是经验回报,与估计回报相对。代理只能体验实际回报,但它们可以使用估计的价值函数来估计回报。实际回报指的是完整的经验回报。估计价值函数或估计回报:指的是对真实值函数或实际回报的粗略计算。“估计”意味着一个近似值,一个猜测。真实值函数让你估计回报,而估计价值函数会给这些估计增加偏差。 |
现在,为了清楚起见,TD 目标是对真实状态值函数 vπ(s) 的有偏估计,因为我们使用状态值函数的估计来计算状态值函数的估计。是的,我知道这听起来很奇怪。这种用估计来更新估计的方法被称为自助法,这与我们在第三章中学习的动态规划方法非常相似。但是,DP 方法是在一步期望上自助,而TD方法是在一步期望的样本上自助。这个词“样本”有很大的区别。
在好的方面,新的估计回报,即TD目标,是我们用于蒙特卡洛更新的t:T。这是因为TD目标只依赖于单个动作、单个转换和单个奖励,因此积累的随机性要少得多。因此,TD方法通常比 MC 方法学习得更快。
| 0001 | 一点历史时序差分学习 |
|---|---|
| 在 1988 年,理查德·萨顿发表了一篇题为“通过时序差分方法学习预测”的论文,其中他介绍了TD学习方法。我们在这章中使用的 RW 环境也首次在这篇论文中提出。这篇论文的关键贡献是意识到,虽然 MC 方法使用预测和实际回报之间的差异来计算误差,但TD能够使用时序连续预测之间的差异,因此得名时序差分学习。TD 学习是 SARSA、Q 学习、双 Q 学习、深度 Q 网络(DQN)、双深度 Q 网络(DDQN)等方法的前身。我们将在本书中学习这些方法。 |

TD 预测
| 它在于细节 FVMC, EVMC, 和 TD 在 RW 环境中 | |
|---|---|
我在这三个策略评估算法上运行了 RW 环境。所有方法都评估了全左策略。现在,请记住,环境的动态使得任何动作,无论是左还是右,都有相同的过渡概率(50% 左和 50% 右)。在这种情况下,被评估的策略是不相关的。我在所有算法中使用了相同的学习率 alpha 的调度:alpha 从 0.5 开始,在 500 个总回合中的 250 个回合内指数衰减到 0.01。这是总回合数的 50%。这个超参数至关重要。通常,alpha 是一个小于 1 的正常数。在非平稳环境中,保持 alpha 的常数有助于学习。 然而,我选择衰减 alpha 以展示收敛。我衰减 alpha 的方式有助于算法接近收敛,但由于我没有将 alpha 减少到零,它们并没有完全收敛。除此之外,这些结果应该能帮助你了解这些方法之间的差异。 |
|
| Tally it UpMC 和 TD 都几乎收敛到真实的状态值函数 | |
![]() |
|
| Tally it UpMC 的估计值有噪声;TD 的估计值偏离目标 | |
![]() |
|
| Tally it UpMC 旨在解决高方差问题;TD 旨在解决偏差问题 | |
![]() |
从多步学习估计
在本章中,我们探讨了通过交互估计给定策略的价值函数的两个核心算法。在 MC 方法中,我们在估计值函数之前,对环境进行采样直到回合结束。这些方法将实际回报,即折现的总奖励,分配到所有状态上。例如,如果折现因子小于 1,并且回报只有 0 或 1,就像 RW 环境中那样,MC 目标将始终为每个单个状态是 0 或 1。相同的信号被推回到轨迹的起点。这显然不适用于具有不同折现因子或奖励函数的环境。

中间是什么?
另一方面,在 TD 学习中,智能体与环境只交互一次,然后估计到达目标状态的预期回报,然后估计目标,然后是值函数。TD 方法是自举的:它们从一个猜测形成另一个猜测。这意味着,与 MC 方法等待直到一个回合的结束以获取实际回报不同,TD 方法使用单步奖励,然后估计预期的回报到目标,即下一个状态的价值函数。
但是,中间有什么东西吗?我的意思是,TD 在一步之后进行自举是不错的,但是两步之后呢?三步?四步?我们应该等待多少步之后才去估计期望回报并基于值函数进行自举?
实际上,在蒙特卡洛(MC)和 TD 之间存在着一系列算法。在本节中,我们将探讨中间部分。你会发现我们可以调整我们的目标依赖于多少自举,从而平衡偏差和方差。
| 米格尔的类比:MC 和 TD 具有不同的个性 | |
|---|---|
| 我喜欢将蒙特卡洛风格的算法视为 A 型人格的代理人,而 TD 风格的算法视为 B 型人格的代理人。如果你查阅一下,你就会明白我的意思。A 型人格的人是结果驱动的,时间观念强,做事有条理,而 B 型人格的人则随和、深思熟虑,有点嬉皮士的风格。MC 使用实际回报,而 TD 使用预测回报,这应该让你怀疑每种目标类型都有其个性。思考一下;我相信你一定能注意到几个有趣的模式来帮助你记忆。 |
N 步 TD 学习:在几步之后改进估计
动机应该是清晰的;我们有两种极端,蒙特卡洛方法和时间差分方法。根据具体情况,一种方法可能比另一种方法表现更好。MC 是一个无限步方法,因为它一直进行到情节的结束。
我知道,“无限”可能听起来有些令人困惑,但回想一下第二章,我们定义一个终端状态为一个所有动作和所有从这些动作产生的转换都回到该状态的循环状态,没有奖励。这样,你可以想象一个代理人在这个循环中永远“卡住”,因此进行无限多的步骤,而没有积累奖励或更新状态值函数。
另一方面,TD 是一个单步方法,因为它在与环境交互一步之后进行自举和更新状态值函数。你可以将这些两种方法推广为一个 n 步方法。与其像 TD 一样只进行一步,或者像 MC 一样进行整个情节,为什么不使用 n 步来计算值函数并抽象出 n 呢?这种方法被称为 n 步 TD,它进行 n 步的自举。有趣的是,中间的 n 值通常比两种极端都表现更好。你看,你不应该成为一个极端主义者!
| 展示数学:N 步时间差分方程 | |
|---|---|
![]() |
|
| 我会说 Python:N 步 TD | |
def ntd(pi, ①
env,
gamma=1.0,
init_alpha=0.5, ②
min_alpha=0.01,
alpha_decay_ratio=0.5,
n_step=3,
n_episodes=500):
nS = env.observation_space.n ③
V = np.zeros(nS)
V_track = np.zeros((n_episodes, nS))
alphas = decay_schedule( ④
init_alpha, min_alpha,
alpha_decay_ratio, n_episodes)
discounts = np.logspace( ⑤
0, n_step+1, num=n_step+1, base=gamma, endpoint=False)
for e in tqdm(range(n_episodes), leave=False): ⑥
state, done, path = env.reset(), False, [] ⑦
while not done or path is not None: ⑧
path = path[1:] ⑨
while not done and len(path) < n_step: ⑩
while not done and len(path) < n_step: ⑪
action = pi(state)
next_state, reward, done, _ = env.step(action) ⑫
experience = (state, reward, next_state, done)
path.append(experience)
state = next_state
if done:
break
n = len(path) ⑬
est_state = path[0][0] ⑭
rewards = np.array(path)[:,1] ⑮
partial_return = discounts[:n] * rewards ⑯
bs_val = discounts[-1] * V[next_state] * (not done) ⑰
ntd_target = np.sum(np.append(partial_return, ⑱
bs_val))
ntd_error = ntd_target - V[est_state] ⑲
V[est_state] = V[est_state] + alphas[e] * ntd_error ⑳
if len(path) == 1 and path[0][3]: ㉑
path = None
V_track[e] = V
return V, V_track ㉒
① 这里是我的 n-步 TD 算法的实现。你可以用很多种方式来实现它;这是其中一种供你参考。② 我们在这里使用与之前相同的超参数。注意 n_step 的默认值是 3。这意味着三步然后进行自举,或者如果遇到终端状态,则更少,在这种情况下我们不会再次进行自举(再次强调,终端状态的价值定义为零。)③ 这里是我们通常会遇到的情况。④ 提前计算所有 alpha 值。⑤ 现在,这里是一个 MC 和 TD 的混合体。注意我们计算了折现因子,但与我的 MC 实现不同,我们不是走到 max_steps,而是走到 n_step + 1,包括 n 步和自举估计。⑥ 我们进入情节循环。⑦ 这个路径变量将保存最近的 n_step 经验。这是一个部分轨迹。⑧ 我们会继续进行,直到完成并设置路径为 none。你很快就会看到。⑨ 这里,我们在“弹出”路径的第一个元素。⑩ 这一行会在下一页重复。⑪ 同样。只是为了让你跟随缩进。⑫ 这是交互块。我们基本上是在收集经验,直到完成或路径长度等于 n_step。⑬ n 这里可以是 ‘n_step’,但如果路径中有一个终端状态,它也可以是一个更小的数字。⑭ 这里我们正在提取我们正在估计的状态,这并不是状态。⑮ rewards 是从 est_state 到 n 遇到的所有奖励的向量。⑯ partial_return 是从 est_state 到 n 的 折现 奖励的向量。⑰ bs_val 是自举值。注意在这种情况下,下一个状态是正确的。⑱ ntd_target 是部分回报和自举值的总和。⑲ 这是错误,就像我们一直在计算的那样。⑳ 这是状态值函数的更新。㉑ 这里我们设置路径为 None 以跳出情节循环,如果路径只有一个经验且该经验的完成标志为 True(路径中只有一个终端状态。)㉒ 我们像往常一样返回 V 和 V_track。|
前瞻性 TD(λ):改进所有访问状态的估计
但是,一个问题出现了:那么一个好的 n 值是什么?你什么时候应该使用一步、两步、三步,或者任何其他步骤?我已经给出了实际的建议,即通常高于一的所有 n 值都更好,但我们也不应该走到实际回报。自举有帮助,但其偏差是一个挑战。
使用所有 n-步目标的加权组合作为一个单一目标如何?我的意思是,我们的智能体可以出去计算与一、二、三、……无限步目标相对应的 n-步目标,然后使用指数衰减因子将这些目标混合在一起。这绝对有必要!
这就是一种称为向前视角 TD(λ)的方法。向前视角的TD(λ)是一种将多个n步合并为单个更新的预测方法。在这个特定版本中,智能体必须等待一个场景的结束,才能更新状态值函数的估计。然而,另一种称为向后视角 TD(λ)的方法,可以将相应的更新拆分为部分更新,并将这些部分更新应用于每个步骤的状态值函数估计,就像沿着轨迹留下TD更新的痕迹。很酷,对吧?让我们更深入地了解一下。

泛化回溯
| 显示数学公式:向前视角的TD(λ) | |
|---|---|
![]() |
TD(λ):在每个步骤后改进所有访问状态估计
蒙特卡洛(MC)方法受到“时间步诅咒”的影响,因为它们只能在达到终端状态后才能对状态值函数估计应用更新。使用n步回溯,你仍然受到“时间步诅咒”的影响,因为你仍然必须等待与环境的n次交互过去后,才能对状态值函数估计进行更新。你基本上是在一个n步延迟中追赶。例如,在五步回溯方法中,你必须等待看到五个(或当达到终端状态时更少)状态和五个奖励,然后才能进行任何计算,有点像蒙特卡洛方法。
在向前视角的TD(λ)中,我们回到了蒙特卡洛的时间步;向前视角的TD(λ)也必须等待一个场景的结束,才能将相应的更新应用于状态值函数估计。但至少我们得到了一些东西:如果我们愿意接受偏差,我们可以得到更低方差的目标。
除了泛化和统一蒙特卡洛(MC)和TD方法外,向后视角的TD(λ),或简称为TD(λ),还可以调整偏差/方差权衡,除了在每个时间步应用更新的能力外,就像TD一样。
提供这种优势的机制被称为资格跟踪。资格跟踪是一个记忆向量,它跟踪最近访问过的状态。基本思想是跟踪每个步骤上可以更新的状态。我们不仅跟踪状态是否有资格更新,而且还跟踪其程度,以确保相应的更新正确应用于有资格的状态。

在八步场景中,四个状态环境的资格跟踪
例如,所有资格迹都初始化为零,当你遇到一个状态时,你将其迹增加一个一。每次时间步,你计算所有状态的值函数的更新,并将其乘以资格迹向量。这样,只有合格的状态会得到更新。更新后,资格迹向量通过λ(权重混合因子)和γ(折扣因子)衰减,这样未来的强化事件对早期状态的影响就小了。通过这样做,最近的状态在最近转换中遇到的奖励比在剧集早期访问的状态获得更多的信用,前提是λ没有设置为 1;否则,这类似于 MC 更新,它给剧集期间访问的所有状态分配相同的信用(假设没有折扣)。 |
| 展示数学 Backward-view TD(λ) — 带资格迹的TD(λ),即“the” TD(λ) | |
|---|---|
![]() |
最后我想重申的是,当λ=0 时,TD(λ)与我们之前学过的TD方法等效。因此,TD通常被称为TD(0);另一方面,当λ=1 时,TD(λ)相当于 MC,某种程度上。实际上,它等于 MC 假设离线更新,假设更新在剧集结束时累积并应用。对于在线更新,估计的状态值函数可能每步都改变,因此自举估计会变化,进而改变估计的进展。尽管如此,TD(1)通常假设等于 MC。此外,一种最近的方法,称为true online TD(λ),是TD(λ)的不同实现,它实现了TD(0)与TD和TD(1)与 MC 的完美等价。 |
| 我会说 Python TD(λ)算法,也称为向后视角TD(λ)算法 | |
|---|---|
def td_lambda(pi, ①
env,
gamma=1.0,
init_alpha=0.5,
min_alpha=0.01,
alpha_decay_ratio=0.3,
lambda_=0.3,
n_episodes=500):
nS = env.observation_space.n
V = np.zeros(nS) ②
V_track = np.zeros((n_episodes, nS))
E = np.zeros(nS) ③
alphas = decay_schedule( ④
init_alpha, min_alpha,
alpha_decay_ratio, n_episodes)
for e in tqdm(range(n_episodes), leave=False): ⑤
E.fill(0) ⑥
state, done = env.reset(), False ⑦
while not done: ⑧
action = pi(state)
next_state, reward, done, _ = env.step(action) ⑨
td_target = reward + gamma * V[next_state] * \
(not done)
td_error = td_target - V[state] ⑩
E[state] = E[state] + 1 ⑪
V = V + alphas[e] * td_error * E ⑫
E = gamma * lambda_ * E ⑬
state = next_state
V_track[e] = V ⑭
return V, V_track
① td_lambda 方法具有与其他所有方法非常相似的签名。唯一的新的超参数是 lambda_(下划线是因为 lambda 在 Python 中是一个受限的关键字)。② 设置常规的嫌疑对象。③ 添加一个新家伙:资格迹向量。④ 计算所有剧集的 alpha。⑤ 这里我们进入剧集循环。⑥ 每个新剧集将 E 设置为零。⑦ 设置初始变量。⑧ 进入时间步循环。⑨ 我们首先与环境交互一步并获取经验元组。⑩ 然后,我们使用该经验来计算通常的TD误差。⑪ 我们将状态资格增加 1。⑫ 并将错误更新应用于 E 指示的所有合格状态。⑬ 我们衰减 E...⑭ ...然后继续我们的生活,就像平常一样。 |
| 累计起来运行估计n-步TD和TD(λ)在 RW 环境中的产生 | |
|---|---|
![]() |
|
| 一个具体的例子评估 Russell 和 Norvig 的网格世界环境的最佳策略 | |
让我们在一个略微不同的环境中运行所有算法。这个环境你可能过去多次遇到过。它来自拉塞尔和诺维格的《人工智能》一书! 拉塞尔和诺维格的 Gridworld 这个环境,我将称之为拉塞尔和诺维格的网格世界(RNG),是一个 3 x 4 的网格世界,其中智能体从左下角开始,必须到达右上角。目标南方有一个洞,类似于冰冻湖环境,起点附近有一堵墙。转移函数有 20%的噪声;也就是说,80%的动作成功,20%的动作以随机均匀的方式在正交方向上失败。奖励函数是一个-0.04 的生存惩罚,在目标上获得+1 分,在洞上获得-1 分。现在,我们正在评估一个策略。我恰好将最优策略包含在第三章的笔记本中:在那个章节我没有空间来讨论它。实际上,确保你检查了书中提供的所有笔记本!![]() |
|
| 总结 FVMC,TD,n-步 TD 和 TD(λ)在 RNG 环境中的表现 | |
![]() |
|
| 总结 RNG 展示了偏差和方差效应在估计上的影响 | |
![]() |
|
| 总结 RNG 的初始状态下的 FVMC 和TD目标 | |
![]() |
摘要
从顺序反馈中学习是具有挑战性的;你在第三章学到了很多关于它的知识。你创建了平衡即时和长期目标的智能体。价值迭代(VI)和政策迭代(PI)等方法是 RL 的核心。从评估反馈中学习也非常困难。第四章全部关于一种特定的环境,其中智能体必须学会平衡信息的收集和利用。epsilon-greedy,softmax,乐观初始化等策略也是 RL 的核心。
我希望你再停下来思考一下这两个权衡问题,作为独立的问题。我见过有 500 页甚至更长的教科书专门讨论这些权衡。虽然我们应该为每个权衡只用了 30 页而感到高兴,但你也应该有所疑问。如果你想开发新的 DRL 算法,推动技术前沿,我建议你独立研究这两个权衡。寻找关于“规划算法”和“赌博机算法”的书籍,并投入时间和精力去理解这些领域。当你回到 RL 并看到所有联系时,你会感到有很大的进步。现在,如果你的目标仅仅是理解 DRL,实现一些方法,在自己的项目中使用它们,这里的内容就足够了。
在本章中,你学习了能够处理同时具有顺序性和评估性的反馈的智能体。正如之前提到的,这可不是一件小事!同时平衡短期和长期目标以及收集和利用信息,即使是大多数人类都有困难!当然,在本章中,我们限制了自己只关注预测问题,即估计智能体行为的值。为此,我们介绍了蒙特卡洛预测和时序差分学习等方法。这两种方法是光谱两端的极端,可以用 n-步 TD 智能体进行泛化。只需改变步长,你就可以得到介于两者之间的任何智能体。然后我们学习了 TD(λ)以及单个智能体如何以非常创新的方式结合这两个极端以及两者之间的所有内容。
下一章,我们将探讨控制问题,这实际上就是改善智能体的行为。就像我们将策略迭代算法分为策略评估和策略改进一样,将强化学习问题分为预测问题和控制问题,使我们能够深入了解细节并获得更好的方法。
到现在为止,
-
不断变化的环境
-
学到了如何将这两个挑战结合起来,并产生强化学习领域。
-
了解了许多计算目标值以估计状态值函数的方法。
| 可分享的成就:独自工作并分享你的发现 |
|---|
| | 这里有一些想法,如何将你所学的内容提升到下一个层次。如果你愿意,与世界分享你的结果,并确保查看其他人所做的事情。这是一个双赢的局面,希望你能充分利用它。
-
#gdrl_ch05_tf01:本章中没有任何一种方法处理围绕许多 Gym 环境的时间步限制。不明白我在说什么?不用担心,我在第八章中会详细解释。然而,目前你可以查看这个文件:
github.com/openai/gym/blob/master/gym/envs/__init__.py。看看有多少环境,包括冻结湖,都有一个可变的最大回合步数。这是对环境施加的时间步限制。思考一下:这个时间步限制如何影响本章中提出的算法?去书中的笔记本,修改算法以正确处理时间步限制,并使值函数估计更加准确。值函数会改变吗?为什么,为什么不呢?请注意,如果你不明白我在说什么,你应该继续前进,并在你理解之后再回来。 -
#gdrl_ch05_tf02: 比较和绘制蒙特卡洛和时差目标是有用的。帮助你理解差异的一件事是对这两种类型的目标进行更全面的分析,并包括 n 步和 TD-lambda 目标。首先,收集不同时间步长值的 n 步目标,并对 TD-lambda 目标中的不同 lambda 值做同样的事情。这些与 MC 和 TD 相比如何?此外,找到其他比较这些预测方法的方法。但是,用图表和视觉来进行比较!
-
#gdrl_ch05_tf03: 在每一章中,我都在使用最后的标签作为通用的标签。请随意使用这个标签来讨论与本章相关的工作。没有什么比为自己创造作业更令人兴奋的了。确保分享你打算调查的内容以及你的结果。
用你的发现写一条推文,@我 @mimoralea(我会转发),并使用这个列表中的特定标签来帮助感兴趣的人找到你的结果。没有正确或错误的结果;你分享你的发现并检查他人的发现。利用这个机会社交,做出贡献,让自己脱颖而出!我们正在等待你!以下是一条推文示例:“嘿,@mimoralea。我创建了一个博客文章,列出了学习深度强化学习的资源列表。查看它在这里<链接>。#gdrl_ch01_tf01”我会确保转发并帮助他人找到你的工作。|
6 改善智能体的行为
在本章
-
你将了解在同时从顺序和评估反馈中学习时如何改进策略。
-
当状态转移和奖励函数未知时,你将开发在强化学习环境中寻找最优策略的算法。
-
你将编写代码,为那些仅通过经验和决策就能从随机行为转变为最优行为的智能体,并在各种环境中训练这些智能体。
当显然无法达到目标时,不要调整目标,调整行动步骤。
—— 孔子 中国春秋时期的教育家、编辑、政治家、哲学家
在本章之前,你已独立地学习了来自三种不同类型反馈的强化学习智能体的学习:顺序的、评估的和样本的。在第二章,你学习了如何使用被称为马尔可夫决策过程(Markov decision processes)的数学框架来表示顺序决策问题。在第三章,你学习了如何使用从 MDP 中提取策略的算法来解决这些问题。在第四章,你学习了在 MDP 表示不可用的情况下解决简单控制问题,这些问题是多选项、单选决策问题,称为多臂老丨虎丨机。最后,在第五章,我们将这两种类型的控制问题结合起来,即我们处理了顺序且不确定的控制问题,但我们只学会了估计价值函数。我们解决了所谓的预测问题,即学习评估策略,学习预测回报。
在本章中,我们将介绍解决控制问题的智能体,我们通过简单地改变两个东西来实现这一点。首先,我们不是估计状态价值函数 V(s),而是估计动作价值函数 Q(s, a)。这样做的主要原因是因为 Q 函数,与 V 函数不同,它允许我们在不使用 MDP 的情况下看到动作的价值。其次,在我们获得这些 Q 值估计之后,我们使用它们来改进策略。这与我们在策略迭代算法中所做的是类似的:我们评估,我们改进,然后评估改进后的策略,然后在这个改进的策略上进一步改进,依此类推。正如我在第三章中提到的,这种模式被称为广义策略迭代(GPI),它可以帮助我们创建一个架构,几乎任何强化学习算法都可以适应,包括最先进的深度强化学习智能体。
本章的概述如下:首先,我将扩展广义策略迭代架构,然后您将了解许多不同类型的代理,它们解决了控制问题。您将了解蒙特卡洛预测和时序差分学习代理的控制版本。您还将了解稍微不同类型的代理,它们将学习与行为解耦。在实践意义上,这意味着在本章中,您将开发通过试错学习来解决问题的代理。这些代理通过与环境交互来学习最优策略。
强化学习代理的解剖结构
在本节中,我想向您提供一个心理模型,大多数,如果不是所有,强化学习代理都符合这个模型。首先,每个强化学习代理都会收集经验样本,无论是通过与环境的交互还是查询环境的学习模型。然而,数据是在代理学习的过程中产生的。其次,每个强化学习代理都会学习估计某些东西,可能是一个环境模型,或者可能是一个策略、一个价值函数,或者仅仅是回报。第三,每个强化学习代理都会尝试改进一个策略;毕竟,这就是强化学习(RL)的全部意义。
| 奖励、回报和价值函数 | |
|---|---|
现在是刷新记忆的好时机。你需要记住奖励、回报和价值函数之间的区别,这样你才能理解本章内容,并开发出通过试错学习来学习最优策略的代理。让我再重复一遍。奖励是一个表示状态转换好坏的数值信号。你的代理观察到状态 S[t],采取行动 A[t];然后环境发生变化,给出奖励 R[t+1],并发出新的状态 S[t+1]。奖励是表示在每个时间步长发生的转换好坏的单个数值信号! 回报是整个过程中收到的所有奖励的总和。你的代理收到奖励 R[t+1],然后 R[t+2],等等,直到它获得在终端状态 S[T] 之前最后的奖励 RT。回报是整个过程中所有奖励的总和。回报通常定义为折扣总和,而不是简单的总和。折扣总和优先考虑在过程中早期找到的奖励(当然,这取决于折扣因子。)从技术上讲,折扣总和是对回报的更一般定义,因为折扣因子为 1 时,它就是一个简单的总和! 价值函数是期望回报。期望是通过将所有可能值相加并乘以其发生的概率来计算的。将期望视为无限多个样本的平均值;回报的期望就像是从无限多个回报中进行采样并取平均值。当你从选择行动之后开始计算回报时,期望是该状态-行动对的行动价值函数,Q(s, a)。如果你忽略采取的行动并从状态 s 开始计数,那么它就变成了状态价值函数 V(s)。![]() |
大多数代理收集经验样本
强化学习(RL)的一个独特特征是代理通过试错来学习。代理与环境交互,在这个过程中收集数据。这里不寻常的方面是,收集数据与从数据中学习是两个不同的挑战。正如你很快就会看到的,从数据中学习也与从数据中改进不同。在 RL 中,有收集、学习和改进。例如,一个在收集数据方面相当出色的代理可能并不擅长从数据中学习;或者相反,一个不擅长收集数据的代理可能在从数据中学习方面很出色,等等。我们都有那种在学校笔记记不好的朋友,但他们考试却做得很好,而其他人把所有东西都记下来,但成绩却不那么好。
在第三章中,当我们学习动态规划方法时,我提到价值迭代和政策迭代不应该被称为强化学习,而应该称为规划方法,原因在于它们与环境的交互,因为环境模型,即马尔可夫决策过程(MDP),是事先提供的。
| ŘŁ | 规划问题与学习问题 |
|---|---|
| 规划问题:指的是有环境模型可用的问题,因此不需要学习。这类问题可以使用价值迭代和政策迭代等规划方法来解决。这类问题的目标是找到最优策略,而不是学习。假设我给你一张地图,让你从点 A 找到到点 B 的最佳路线;这里不需要学习,只需要规划。学习问题:指的是需要从样本中进行学习的问题,通常是因为没有环境模型可用,或者可能是因为无法创建一个模型。学习问题的主要挑战是我们使用样本进行估计,而样本可能具有高方差,这意味着它们的质量较差,难以从中学习。样本也可能存在偏差,要么是因为来自与估计不同的分布,要么是因为使用估计来估计,这可能导致我们的估计完全错误。假设这次我没有给你该地区的地图。你将如何找到“最佳路线”?很可能是通过试错学习。 |
要被视为标准的强化学习方法,算法与环境的交互,即我们试图解决的问题的方面,应该是存在的。大多数强化学习代理通过自己收集经验样本,与例如被给予数据集的监督学习方法不同,强化学习代理有选择其数据集的额外挑战。大多数强化学习代理收集经验样本,因为强化学习通常涉及解决交互式学习问题。
| ŘŁ | 带有强化学习口音的非交互式与交互式学习问题 |
|---|---|
| 非交互式学习问题:指的是一种学习问题,其中不需要或不可能与环境交互。在这些类型的问题中,学习过程中没有与环境的交互,但可以从之前生成的数据中进行学习。目标是根据样本找到某些东西,通常是策略,但不一定是。例如,在逆强化学习中,目标是根据专家行为样本恢复奖励函数。在学徒学习中,目标是根据恢复的奖励函数到策略。在行为克隆中,这是一种模仿学习的形式,目标是直接使用监督学习从专家行为样本到策略。交互式学习问题:指的是一种学习问题,其中学习和交互是交织在一起的。这些问题的有趣之处在于,学习者也控制着数据收集过程。从样本中进行最优学习是一个挑战,找到用于最优学习的样本是另一个挑战。 |
大多数智能体都会估计某些东西
在收集数据之后,智能体可以使用这些数据做很多事情。例如,某些智能体学会预测预期的回报或价值函数。在上一章中,你学习了多种实现这一目标的方法,从使用蒙特卡洛到TD目标,从每次访问到首次访问 MC 目标,从n-步到λ-回报目标。有许多不同的方法可以用来计算目标,这些方法可以用于估计价值函数。
但价值函数并不是智能体可以用经验样本学习的唯一东西。智能体也可能被设计成学习环境模型。正如你将在下一章中看到的,基于模型的强化学习智能体使用收集的数据来学习状态转移和奖励函数。通过学习环境模型,智能体可以预测下一个状态和奖励。进一步来说,有了这些,智能体可以像 DP 方法一样规划一系列动作,或者使用与这些学习模型交互生成的合成数据来学习其他东西。关键是智能体可能被设计成学习环境模型。
此外,智能体可以被设计成直接使用估计的回报来改进策略。在后面的章节中,我们将看到策略梯度方法包括近似函数,这些函数接受状态作为输入并输出动作的概率分布。为了改进这些策略函数,我们可以使用实际回报,在最简单的情况下,也可以使用估计的价值函数。最后,智能体可以被设计成同时估计多个东西,这是典型的情况。重要的是大多数智能体都会估计某些东西。
| 蒙特卡洛与时间差分目标 | |
|---|---|
值得重复的其他重要概念是价值函数的不同估计方法。一般来说,所有学习价值函数的方法都是逐步将误差的一部分移动到目标。大多数学习方法遵循的一般方程是 估计 = 估计 + 步长 * 误差。误差简单地是采样目标与当前估计之间的差异:(目标 – 估计)。计算这些目标的主要且相反的方法是蒙特卡洛和时序差分学习! 蒙特卡洛目标由实际回报组成:实际上,没有其他东西。蒙特卡洛估计包括使用经验(观察)平均回报代替期望(如果你能平均无限样本)回报来调整价值函数的估计! 时序差分目标由一个估计回报组成。还记得“自举”吗?它基本上意味着使用后续状态的估计期望回报来估计当前状态的期望回报。TD 就是这样做的:从一个猜测中学习一个猜测。TD 目标是通过使用单个奖励和下一个状态的估计期望回报(使用运行价值函数估计)来形成的。 |
大多数代理改进策略
最后,大多数代理改进策略。这一最终步骤高度依赖于正在训练的代理的类型以及代理估计的内容。例如,如果代理正在估计价值函数,一个常见的事情是改进隐式编码在价值函数中的目标策略,即正在学习的策略。改进目标策略的好处是,行为策略(即数据生成策略)将相应地改进,从而提高代理随后收集的数据质量。如果目标和行为策略相同,那么底层价值函数的改进将明确提高随后生成数据的质量。
现在,如果策略是通过价值函数而不是显式表示,例如在策略梯度法和演员-评论家方法中,代理可以使用实际回报来改进这些策略。代理还可以使用价值函数来估计回报以改进策略。最后,在基于模型强化学习中,有多种改进策略的选项。可以使用学习到的环境模型来规划一系列动作。在这种情况下,在规划阶段隐式地改进了策略。可以使用模型来学习价值函数,这隐式地编码了一个策略。也可以使用模型直接改进策略。底线是,所有代理都试图改进策略。
| ŘŁ | 带有强化学习口音的贪婪策略 vs. epsilon-贪婪策略 vs. 最优策略 |
|---|---|
| 贪婪策略:指的是一种策略,它总是选择每个状态下被认为能带来最高期望回报的动作。重要的是要知道,“贪婪策略”在价值函数上是贪婪的。“被认为”的部分来自价值函数。这里的洞察是,当有人说“贪婪策略”时,你必须问,相对于什么贪婪?相对于随机价值函数的贪婪策略是一个相当糟糕的策略。ε-贪婪策略:指的是一种策略,它通常选择被认为能从每个状态下带来最高期望回报的动作。与之前相同;ε-贪婪策略在特定价值函数上是ε-贪婪的。始终确保你理解正在引用哪个价值函数。最优策略:指的是一种策略,它总是选择从每个状态下实际上能带来最高期望回报的动作。虽然贪婪策略可能或可能不是最优策略,但最优策略无疑必须是一种贪婪策略。你问,“相对于什么贪婪?”做得好!最优策略是相对于唯一价值函数的贪婪策略,即最优价值函数。 |
广义策略迭代
另一个更常用来理解强化学习算法架构的简单模式被称为广义策略迭代(GPI)。GPI 是一个基本理念,即策略评估和策略改进的持续交互推动策略向最优性发展。
如你很可能记得,在策略迭代算法中,我们有两个过程:策略评估和策略改进。策略评估阶段接受任何策略,并对其进行评估;它估计策略的价值函数。在策略改进中,这些估计,即价值函数,被用来获得更好的策略。一旦策略评估和改进稳定下来,也就是说,一旦它们的交互不再产生任何变化,那么策略和价值函数就是最优的。
现在,如果你还记得,在学习策略迭代之后,我们学习了另一种算法,称为值迭代。这个算法与策略迭代类似;它包含策略评估和策略改进两个阶段。然而,主要区别在于策略评估阶段只包含一个迭代。换句话说,策略评估并没有产生实际的价值函数。在值迭代的策略评估阶段,价值函数的估计值会逐渐接近实际价值函数,但并不完全到达那里。尽管如此,即使有这个策略评估阶段,值迭代的广义策略迭代模式仍然会产生最优价值函数和策略。
这里的关键洞察是,策略评估通常包括收集和估计价值函数,类似于你在上一章中学到的算法。正如你所知,评估策略有多种方式,估计策略价值函数的方法众多,有各种选择方法来满足广义策略迭代模式中的策略评估要求。
此外,策略改进包括将策略改变为使其对价值函数更贪婪。在策略迭代算法的策略改进方法中,我们将策略完全变为对评估策略的价值函数贪婪。但是,我们之所以能够完全贪婪化策略,仅仅是因为我们有了环境的 MDP。然而,我们在上一章中学到的策略评估方法不需要环境的 MDP,这付出了一定的代价。我们不能再完全贪婪化策略;我们需要让我们的智能体进行探索。从现在开始,我们不再完全贪婪化策略,而是使策略更贪婪,留出探索的空间。这种部分策略改进在第四章中使用不同的探索策略时被采用。
这就是全部内容。大多数强化学习算法遵循这种 GPI 模式:它们有独特的策略评估和改进阶段,我们只需挑选和选择方法即可。
| Miguel 的类比:广义策略迭代以及为什么你应该听取批评 | |
|---|---|
| 广义策略迭代(GPI)类似于批评家和表演者永恒的舞蹈。策略评估提供了政策改进所需的重要反馈,以使政策变得更好。同样,批评家提供了表演者可以用来做得更好的必要反馈。正如本杰明·富兰克林所说,“批评家是我们的朋友,他们指出我们的错误。” 他是个聪明人;他允许 GPI 帮助他改进。你让批评家告诉你他们的想法,你利用这些反馈来变得更好。很简单!一些最好的公司也遵循这个流程。你认为“数据驱动决策”这句话是什么意思?它意味着他们确保使用一个出色的策略评估过程,以便他们的政策改进过程产生可靠的结果;这与 GPI 的模式相同!诺曼·文森特·皮尔说,“我们大多数人的麻烦是,我们宁愿被赞誉所毁,也不愿被批评所救。” 去吧,让批评家帮助你。但要注意!他们确实可以帮助你,并不意味着批评家总是正确的,或者你应该盲目地接受他们的建议,尤其是如果你第一次听到这样的反馈。批评家通常是有偏见的,策略评估也是如此!作为一位伟大的表演者,你的任务是仔细倾听这些反馈,聪明地收集尽可能好的反馈,并在确定的情况下采取行动。但最终,世界属于那些做工作的人。西奥多·罗斯福说得最好:“重要的不是批评家;不是指出强者跌倒或做事者本可以做得更好的那个人;荣誉属于真正进入竞技场的人,他的脸被尘土、汗水和鲜血弄脏;他英勇奋斗;他犯错误,一次又一次地失败,因为不努力就没有错误和不足;但他确实努力去做事;他了解巨大的热情、伟大的奉献;他为了一个崇高的目标而倾其所有;他最好地知道,最终,他要么取得了高成就的胜利,要么在最坏的情况下,如果他失败了,至少是勇敢地失败了,这样他的位置永远不会与那些既不知道胜利也不知道失败冷漠而胆怯的灵魂为伍。”在后面的章节中,我们将研究演员-批评家方法,你会看到这个类比如何扩展,信不信由你!演员和批评家互相帮助。敬请期待更多内容。令人敬畏的是,最优决策模式在各个领域都是有效的。你在学习深度强化学习(DRL)中学到的知识可以帮助你成为一个更好的决策者,而你从自己的生活中学到的知识可以帮助你创造更好的智能体。酷,不是吗? |
学习改进行为策略
在上一章中,你学习了如何解决预测问题:如何让智能体最准确地估计给定策略的价值函数。然而,虽然这对我们的智能体来说是一个有用的能力,但它并不能直接使它们在任何任务上表现得更好。在本节中,你将学习如何解决控制问题:如何让智能体优化策略。这种新的能力使智能体能够通过试错学习来学习最佳行为,从任意策略开始,最终达到最佳策略。在本章之后,你可以开发出能够解决任何由 MDP 表示的任务的智能体。任务必须是离散状态和动作空间的 MDP,除此之外,它就是即插即用的。
为了展示几个智能体,我们将利用你所学到的 GPI 模式。也就是说,我们将从你在上一章中学到的算法中选择策略评估阶段的算法,以及从你在前一章中学到的算法中选择策略改进阶段的策略。希望这能激发你的想象力。只需挑选策略评估和改进的算法,一切就会顺利,这是因为这两个过程的相互作用。
| ŘŁ | 带有强化学习口音的预测问题、控制问题、策略评估与改进问题 |
|---|---|
| 预测问题:指的是评估策略的问题,即在给定策略的情况下估计价值函数的问题。估计价值函数实际上就是学习预测回报。状态价值函数估计从状态中期望的回报,动作价值函数估计从状态-动作对中期望的回报。控制问题:指的是寻找最优策略的问题。控制问题通常通过遵循广义策略迭代(GPI)的模式来解决,其中策略评估和策略改进的竞争过程逐渐将策略推向最优。强化学习方法通常将动作价值预测方法与策略改进和动作选择策略配对。策略评估:指的是解决预测问题的算法。请注意,有一个名为策略评估的动态规划方法,但这个术语也用来指代所有解决预测问题的算法。策略改进:指的是通过使新策略相对于原始策略的价值函数更贪婪来改进原始策略的算法。请注意,策略改进本身并不能解决控制问题。通常必须将策略评估与策略改进配对来解决控制问题。策略改进仅指根据评估结果改进策略的计算。 | |
| 一个具体的例子:滑稽的走步七环境 | |
对于本章,我们使用一个名为 slippery walk seven (SWS) 的环境。这个环境是一个行走环境,一个单行网格世界环境,有七个非终止状态。这个环境的特别之处在于它是一个滑溜的行走环境;动作效果是随机的。如果智能体选择向左走,它有可能这么做,但也有可能向右走,或者保持在原地。让我给你展示这个环境的 MDP。记住,对于智能体来说,这些是未知的。我之所以提供这些信息,只是为了教学目的。此外,请记住,对于智能体来说,在事先没有任何状态之间的关系。智能体不知道状态 3 位于整个行走路径的中间,或者它位于状态 2 和 4 之间;它甚至不知道什么是“行走”!智能体不知道动作 0 表示向左走,或者动作 1 表示向右走。老实说,我鼓励你亲自去笔记本中玩这个环境,以获得更深入的理解。事实上,智能体只能看到状态 ID,比如 0,1,2 等等,并选择动作 0 或 1。 滑溜行走七环境 MDPSWS 环境与我们之前章节中学到的随机行走(RW)环境类似,但具有控制能力。记住,随机行走是一个环境,在执行左行动作时向左走的概率等于向右走的概率。而在执行右行动作时向右走的概率等于向左走的概率,因此没有控制。这个环境是有噪声的,但智能体选择的动作会影响其性能。此外,这个环境有七个非终止状态,而 RW 环境有五个。 |
蒙特卡洛控制:在每个回合后改进策略
让我们尝试创建一个控制方法,使用蒙特卡洛预测来满足我们的策略评估需求。让我们最初假设我们使用的是与策略迭代算法相同的策略改进步骤。也就是说,策略改进步骤获取与策略评估的价值函数相关的贪婪策略。这会形成一个帮助我们仅通过交互找到最优策略的算法吗?实际上,不会。在我们使这种方法生效之前,我们需要做出两个改变。
首先,我们需要确保我们的智能体估计的是动作值函数 Q(s, a),而不是我们在上一章中估计的 V(s, a)。V 函数的问题在于,没有 MDP,我们无法知道从某个状态采取的最佳动作是什么。换句话说,策略改进步骤将不会起作用。

我们需要估计动作值函数
第二,我们需要确保我们的智能体进行探索。问题是,我们不再使用 MDP 来满足我们的策略评估需求。当我们从样本中进行估计时,我们得到了我们访问的所有状态-动作对的值,但如果我们没有访问到最佳状态的一部分怎么办?

我们需要探索
因此,让我们在策略评估阶段使用首次访问蒙特卡洛预测,并在策略改进阶段使用衰减 epsilon-greedy 动作选择策略。就这样——你有一个完整的、无模型的 RL 算法,其中我们使用蒙特卡洛预测评估策略,并使用衰减 epsilon-greedy 动作选择策略来改进它们。
与价值迭代一样,它有一个截断的政策评估步骤,我们可以截断蒙特卡洛预测方法。与我们在上一章中用蒙特卡洛预测估计单个策略的价值函数相比,我们截断预测步骤,在单个完整回合和轨迹样本估计之后,立即改进策略。我们交替进行单个 MC 预测步骤和单个衰减 epsilon-greedy 动作选择改进步骤。
让我们看看我们的第一个 RL 方法 MC 控制。你会看到三个函数:
-
decay_schedule:根据函数参数计算衰减值。
-
generate_trajectory:在环境中执行策略的全局回合。
-
mc_control:MC 控制方法的完整实现。
| 我说 Python 指数衰减计划 | |
|---|---|
def decay_schedule( ①
init_value, min_value,
decay_ratio, max_steps, ②
log_start=-2, log_base=10):
decay_steps = int(max_steps * decay_ratio) ③
rem_steps = max_steps - decay_steps ④
values = np.logspace( ⑤
log_start, 0, decay_steps,
base=log_base, endpoint=True)[::-1] ⑤
values = (values - values.min()) / \ ⑥
(values.max() - values.min())
values = (init_value - min_value) * values + min_value ⑦
values = np.pad(values, (0, rem_steps), 'edge') ⑧
return values
① 我们将用于 alpha 和 epsilon 的衰减计划与上一章中用于 alpha 的相同。这次让我们更详细地探讨一下。② 我个人喜欢这个函数的地方在于,你可以给它一个初始值、一个最小值,以及从初始值衰减到最小值的 max_steps 百分比。③ 这个 decay_steps 是值衰减终止的索引,min_value 继续直到 max_steps。④ 因此,rem_steps 是这个差值。⑤ 我正在使用以 log_start 为起始点,默认设置为-2,并以 0 为结束点的 logspace 来计算值。我请求的这个空间中的值的数量是 decay_steps,基数是 log_base,默认设置为 10。注意,我反转了这些值!⑥ 因为值可能不会正好结束在 0,考虑到它是对数,我将它们改为介于 0 和 1 之间,以便曲线看起来平滑且美观。⑦ 然后,我们可以进行线性变换,得到 init_value 和 min_value 之间的点。⑧ 这个 pad 函数只是重复最右侧的值 rem_step 次数。|
| 我说 Python 生成探索性策略轨迹 | |
|---|---|
def generate_trajectory( ①
select_action, Q, epsilon,
env, max_steps=200):
done, trajectory = False, [] ②
while not done: ③
state = env.reset() ④
for t in count(): ⑤
action = select_action(state, Q, epsilon) ⑥
next_state, reward, done, _ = env.step(action) ⑦
experience = (state,
action,
reward,
next_state,
done)
trajectory.append(experience) ⑧
if done: ⑨
break
if t >= max_steps - 1: ⑩
trajectory = []
break
state = next_state ⑪
return np.array(trajectory, np.object) ⑫
① 生成轨迹函数的这个版本略有不同。我们现在需要接受一个动作选择策略,而不是贪婪策略。② 我们首先初始化 done 标志和一个名为 trajectory 的经验列表。③ 然后开始循环,直到 done 标志设置为 true。④ 将环境重置以进行新循环的交互。⑤ 然后开始计数步骤 t。⑥ 然后,使用传递的 ‘select_action’ 函数选择一个动作。⑦ 使用该动作步进环境,并获得完整经验元组。⑧ 将经验追加到轨迹列表中。⑨ 如果我们达到终端状态并且 ‘done’ 标志被提升,则中断并返回。⑩ 如果当前轨迹中的步骤数 ‘t’ 达到最大允许值,则清除轨迹,中断,并尝试获取另一个轨迹。⑪ 记得更新状态。⑫ 最后,我们返回轨迹的 NumPy 版本,以便于数据操作。|
| 我会说 PythonMonte Carlo 控制 1/2 | |
|---|---|
def mc_control(env, ①
gamma=1.0,
init_alpha=0.5,
min_alpha=0.01,
alpha_decay_ratio=0.5,
init_epsilon=1.0, ②
min_epsilon=0.1,
epsilon_decay_ratio=0.9,
n_episodes=3000,
max_steps=200,
first_visit=True):
nS, nA = env.observation_space.n, env.action_space.n
discounts = np.logspace( ③
0, max_steps,
num=max_steps, base=gamma,
endpoint=False)
alphas = decay_schedule( ④
init_alpha, min_alpha,
alpha_decay_ratio,
n_episodes)
epsilons = decay_schedule( ⑤
init_epsilon, min_epsilon,
epsilon_decay_ratio,
n_episodes)
pi_track = [] ⑥
Q = np.zeros((nS, nA), dtype=np.float64) ⑥
Q_track = np.zeros((n_episodes, nS, nA), dtype=np.float64)
select_action = lambda state, Q, epsilon: \ ⑦
np.argmax(Q[state]) \
if np.random.random() > epsilon \
else np.random.randint(len(Q[state]))
for e in tqdm(range(n_episodes), leave=False): ⑧
① mc_control 与 mc_prediction 类似。两个主要区别是,我们现在现在估计动作值函数 Q,并且我们需要进行探索。② 注意在函数定义中,我们使用 epsilon 的值来配置随机探索的衰减计划。③ 我们提前计算折扣因子的值。注意我们使用 max_steps,因为那是轨迹的最大长度。④ 我们还提前使用传递的值计算 alphas。⑤ 最后,我们重复 epsilon,并获得一个将在整个训练会话中工作的数组。⑥ 这里我们只是在设置变量,包括 Q 函数。⑦ 这是一个 epsilon-贪婪策略,尽管我们在每个循环而不是每一步衰减 epsilon。⑧ 继续…… |
| 我会说 PythonMonte Carlo 控制 2/2 | |
|---|---|
for e in tqdm(range(n_episodes), leave=False): ⑨⑩
trajectory = generate_trajectory(select_action, ⑪
Q,
epsilons[e],
env,
max_steps) ⑪
visited = np.zeros((nS, nA), dtype=np.bool) ⑫
for t, (state, action, reward, _, _) in enumerate(\ ⑬
trajectory):
if visited[state][action] and first_visit:
continue ⑭
visited[state][action] = True
n_steps = len(trajectory[t:])
G = np.sum(discounts[:n_steps] * trajectory[t:, 2]) ⑮
Q[state][action] = Q[state][action] + \
alphas[e] * (G - Q[state][action]) ⑯
Q_track[e] = Q
pi_track.append(np.argmax(Q, axis=1)) ⑰
V = np.max(Q, axis=1)
pi = lambda s: {s:a for s, a in enumerate(\ ⑱
np.argmax(Q, axis=1))}[s]
return Q, V, pi, Q_track, pi_track
⑨ 重复上一行以便保持缩进⑩ 这里我们进入循环播放。我们将运行 n_episodes。记住,tqdm 会显示一个漂亮的进度条,没有什么超乎寻常的。⑪ 每生成一个新的循环‘e’,我们都会使用由 select_action 函数定义的探索策略生成一个新的轨迹。我们限制轨迹长度为 max_steps。⑫ 我们现在跟踪状态-动作对的访问次数;这是与 mc_prediction 方法相比的另一个重要变化。⑬ 注意这里我们是在离线处理轨迹,即在与环境的交互停止后。⑭ 这里我们检查状态-动作对访问,并相应地采取行动。⑮ 我们以与预测方法相同的方式计算回报,只是这次我们使用了一个 Q 函数。⑯ 注意我们如何使用 alphas。⑰ 之后,就是保存值以供后续分析的问题了。⑱ 最后,我们提取状态值函数和贪婪策略。|
SARSA:在每一步后改进策略
如我们在上一章所讨论的,蒙特卡洛方法的一个缺点是它们在场景到场景的层面上是离线方法。这意味着我们必须等待达到终端状态,才能对我们的价值函数估计进行任何改进。然而,对于策略评估阶段,使用时间差分预测代替蒙特卡洛预测是直接的。通过用TD预测替换 MC,我们现在有一个不同的算法,那就是众所周知的 SARSA 智能体。

计划与控制方法比较
| 我会说 PythonSARSA 智能体 1/2 | |
|---|---|
def sarsa(env, ①
gamma=1.0,
init_alpha=0.5,
min_alpha=0.01,
alpha_decay_ratio=0.5,
init_epsilon=1.0,
min_epsilon=0.1, ②
epsilon_decay_ratio=0.9,
n_episodes=3000):
nS, nA = env.observation_space.n, env.action_space.n ③
pi_track = [] ③
Q = np.zeros((nS, nA), dtype=np.float64) ④
Q_track = np.zeros((n_episodes, nS, nA), dtype=np.float64) ④⑤
select_action = lambda state, Q, epsilon: \ ⑥
np.argmax(Q[state]) \
if np.random.random() > epsilon \
else np.random.randint(len(Q[state])) ⑦
alphas = decay_schedule( ⑧
init_alpha, min_alpha,
alpha_decay_ratio,
n_episodes)
epsilons = decay_schedule( ⑨
init_epsilon, min_epsilon,
epsilon_decay_ratio,
n_episodes)
for e in tqdm(range(n_episodes), leave=False): ⑩
① SARSA 智能体是控制问题的TD的直接转换。也就是说,在其核心,SARSA 是TD,但有两个主要变化。首先,它评估动作值函数 Q。其次,它使用一个探索性的策略改进步骤。② 我们在这里使用 epsilon 与 mc_control 做同样的事情。③ 首先,创建几个有用的变量。记住,pi_track 将保存每个场景的贪婪策略。④ 然后,我们创建 Q 函数。我使用‘np.float64’精度...可能有点过度。⑤ ‘Q_track’将保存每个场景的估计 Q 函数。⑥ select_action 函数与之前相同:一个 epsilon 贪婪策略。⑦ 在 SARSA 中,我们不需要预先计算所有折扣因子,因为我们不会使用完整回报。相反,我们使用估计回报,因此我们可以在线计算折扣。⑧ 注意,我们确实预先计算了所有 alpha。这个函数调用返回一个包含相应 alpha 的向量以供使用。⑨ select_action 函数本身不是一个衰减策略。我们预先计算衰减的 epsilon,因此我们的智能体将使用衰减的 epsilon 贪婪策略。⑩ 让我们继续到下一页。|
| 我会说 PythonSARSA 智能体 2/2 | |
|---|---|
for e in tqdm(range(n_episodes), leave=False): ⑪⑫
state, done = env.reset(), False ⑬
action = select_action(state, Q, epsilons[e]) ⑭
while not done: ⑮
next_state, reward, done, _ = env.step(action) ⑯
next_action = select_action(next_state, ⑰
Q,
epsilons[e])
td_target = reward + gamma * \ ⑱
Q[next_state][next_action] * (not done)
td_error = td_target - Q[state][action] ⑲
Q[state][action] = Q[state][action] + \ ⑳
alphas[e] * td_error
state, action = next_state, next_action ㉑
Q_track[e] = Q
pi_track.append(np.argmax(Q, axis=1)) ㉒
V = np.max(Q, axis=1)
pi = lambda s: {s:a for s, a in enumerate(\ ㉓
np.argmax(Q, axis=1))}[s]
return Q, V, pi, Q_track, pi_track ㉓
⑪ 同一行。你知道该怎么做。⑫ 我们现在处于场景循环中。⑬ 我们通过重置环境和 done 标志来开始每个场景。⑭ 我们为初始状态选择动作(可能是探索性的)。⑮ 我们重复操作,直到达到终端状态。⑯ 首先,执行环境步骤并获取经验。⑰ 注意,在我们进行任何计算之前,我们需要获取下一步的动作。⑱ 我们使用那个下一个状态-动作对来计算 td_target。并且我们对终端状态进行一个小技巧,即乘以表达式(not done),这将零化终端的未来。⑲ 然后计算 td_error 作为目标和当前估计之间的差异。⑳ 最后,通过将估计稍微移动到误差方向来更新 Q 函数。㉑ 我们更新下一个步骤的状态和动作。㉒ 保存 Q 函数和贪婪策略以供分析。㉓ 最后,计算估计的最优 V 函数及其贪婪策略,并返回所有这些。
| ŘŁ | 带有强化学习口音的批量、离线、在线学习问题和方法 |
|---|---|
| 批量学习问题和方法:当你听到“批量学习”这个术语时,人们通常指的是以下两种情况之一:他们指的是一种学习问题,其中经验样本是固定的,并且提前给出,或者他们指的是一种学习方法,该方法针对从一批经验中同步学习进行优化,也称为拟合方法。批量学习方法通常与非交互式学习问题一起研究,更具体地说,是与批量学习问题。但是,批量学习方法也可以应用于交互式学习问题。例如,增长批量方法是同时收集数据的批量学习方法:它们“增长”批量。此外,批量学习问题不必用批量学习方法来解决,就像批量学习方法并不是专门设计来仅解决批量学习问题一样。离线学习问题和方法:当你听到“离线学习”这个术语时,人们通常指的是以下两种情况之一:他们可能正在讨论一个可以用于收集数据的模拟环境(与真实世界的在线环境相对),或者他们可能正在讨论离线学习的方法,这意味着在剧集之间学习,例如。请注意,在离线学习方法中,学习和交互仍然可以交织在一起,但性能仅在收集样本之后才会得到优化,类似于之前描述的增长批量,但不同之处在于,与增长批量方法不同,离线方法通常丢弃旧样本;它们不会增长批量。例如,MC 方法通常被认为是离线的,因为学习和交互是在剧集之间交织的。有两个不同的阶段,交互和学习;MC 是交互式的,但也是一个离线学习方法。在线学习问题和方法:当你听到“在线学习”这个术语时,人们通常指的是以下两种情况之一:要么是在与实时系统(如机器人)交互时学习,要么是在收集经验后立即从经验中学习的方法,在每个时间步长上。请注意,离线和在线学习通常在不同的上下文中使用。我见过离线与在线被用来表示非交互式与交互式,但我也见过它们,正如我提到的,用来区分从模拟器学习与从实时系统学习。我这里的定义与许多强化学习研究者的常用定义一致:理查德·萨顿(2018 年书籍)、大卫·西尔弗(2015 年讲座)、哈德·范·哈塞尔特(2018 年讲座)、迈克尔·利特曼(2015 年论文)和卡萨·塞佩什瓦里(2009 年书籍)。但是要注意术语,那才是重要的。 |
将行为与学习解耦
我希望你花点时间思考一下状态值函数的TD更新方程;记住,它使用r[t+1] + γ**v(S[t+1])作为TD目标。然而,如果你盯着动作值函数的TD更新方程看,它是r[t+1] + γ**Q(S[t+1], A[t+1]),你可能注意到这里有几个更多的可能性。看看所使用的动作及其含义。想想你还能放进去什么。强化学习中最关键的进步之一是Q-learning算法的发展,这是一种无模型、离策略的自举方法,它直接近似最优策略,尽管策略生成经验。是的,这意味着代理,从理论上讲,可以随机行动,仍然可以找到最优值函数和政策。这是怎么可能的?
Q-learning:即使我们选择不这样做,也要学会最优行动
SARSA 算法是一种“在工作中学习”的方法。代理学习的是它用于生成经验的政策。这种学习被称为在策略学习。在策略学习非常出色——我们从自己的错误中学习。但是,让我明确一点,在在策略学习中,我们只从自己的当前错误中学习。如果我们想从自己的过去错误中学习呢?如果我们想从别人的错误中学习呢?在在策略学习中,你做不到。另一方面,离策略学习是一种“从别人那里学习”的方法。代理学习的是与生成经验的政策不同的政策。在离策略学习中,有两种策略:一种行为策略,用于生成经验,与环境交互,另一种是目标策略,这是我们正在学习的策略。SARSA 是一种在策略方法;Q-learning 是一种离策略方法。
| 给我看看数学 SARSA 与 Q-learning 更新方程 | |
|---|---|
![]() |
|
| 我会说 PythonQ-learning 代理 1/2 | |
def q_learning(env, ①
gamma=1.0,
init_alpha=0.5,
min_alpha=0.01,
alpha_decay_ratio=0.5, ②
init_epsilon=1.0,
min_epsilon=0.1,
epsilon_decay_ratio=0.9,
n_episodes=3000):
nS, nA = env.observation_space.n, env.action_space.n ③
pi_track = []
Q = np.zeros((nS, nA), dtype=np.float64) ④
Q_track = np.zeros((n_episodes, nS, nA), dtype=np.float64)
select_action = lambda state, Q, epsilon: \ ⑤
np.argmax(Q[state]) \
if np.random.random() > epsilon \
else np.random.randint(len(Q[state]))
alphas = decay_schedule( ⑥
init_alpha, min_alpha,
alpha_decay_ratio,
n_episodes)
epsilons = decay_schedule( ⑦
init_epsilon, min_epsilon,
epsilon_decay_ratio,
n_episodes)
for e in tqdm(range(n_episodes), leave=False): ⑧
① 注意到 Q-learning 代理的开始与 SARSA 代理的开始是相同的。② 事实上,我甚至为这两个算法使用了完全相同的超参数。③ 这里有几个实用的变量。④ Q 函数和离线分析的跟踪变量⑤ 同样的 epsilon-greedy 动作选择策略⑥ 学习期间要使用的所有 alpha 的向量⑦ 要按需衰减的所有 epsilon 的向量⑧ 让我们继续到下一页。
| 我会说 PythonQ-learning 代理 2/2 | |
|---|---|
for e in tqdm(range(n_episodes), leave=False): ⑨⑩
state, done = env.reset(), False ⑪
while not done: ⑫⑬
action = select_action(state, Q, epsilons[e]) ⑭
next_state, reward, done, _ = env.step(action) ⑮
td_target = reward + gamma * \ ⑯
Q[next_state].max() * (not done) ⑰
td_error = td_target - Q[state][action] ⑱
Q[state][action] = Q[state][action] + \ ⑲
alphas[e] * td_error
state = next_state ⑳
Q_track[e] = Q ㉑
pi_track.append(np.argmax(Q, axis=1))
V = np.max(Q, axis=1) ㉒
pi = lambda s: {s:a for s, a in enumerate(\
np.argmax(Q, axis=1))}[s]
return Q, V, pi, Q_track, pi_track
⑨ 与之前同一行⑩ 我们正在迭代经过的情节。⑪ 我们重置环境并获取初始状态,将完成标志设置为 false。⑫ 现在进入在线学习的交互循环(步骤)。⑬ 我们重复循环,直到达到终端状态并提升完成标志。⑭ 我们首先为当前状态选择一个动作。注意 epsilon 的使用。⑮ 我们对环境进行一步操作,并获取一个完整的经验元组(s, a, s’, r, d)。⑯ 接下来,我们计算TD目标。Q-learning 是一个特殊的算法,因为它试图学习最优动作值函数 q,即使它使用的是探索性策略,例如我们正在运行的衰减 epsilon-greedy。这被称为离策略学习。⑰ 再次,"未完成"确保在终端状态下下一个状态的最大值设置为零。确保代理在死亡后不会期望任何奖励非常重要!!!⑱ 接下来,我们计算TD*误差,即估计值与目标之间的差异。⑲ 然后将状态动作对的 Q 函数移动到更接近误差的位置。⑳ 接下来,我们更新状态。㉑ 保存 Q 函数和政策。㉒ 并在退出时获得 V 函数和最终政策。|
| 米格尔的类比人类也进行策略学习和离策略学习 | |
|---|---|
| 在策略学习中,是关于正在使用的策略进行决策的学习;你可以将其视为“在工作中学习”。离策略学习是关于与用于决策的策略不同的策略的学习。你可以将其视为“从他人的经验中学习”,或者“学习如何做得更好,而不试图做得更好”。这两种都是重要的学习方法,也许对于成为一个优秀的决策者至关重要。有趣的是,你可以很快地看出一个人更喜欢在策略学习还是离策略学习中。例如,我的儿子倾向于偏好在策略学习中。有时我看到他在玩玩具时遇到困难,所以我走过去试图教他如何使用它,但他直到我离开才会抱怨。他一直尝试,最终学会了,但他更喜欢自己的经验而不是别人的。在策略学习是一种直接且稳定的学习方式。另一方面,我的女儿似乎对离策略学习没有问题。她在尝试任务之前就可以从我的演示中学习。我教她如何画房子,然后她尝试。现在,请注意;这是一个牵强的类比。不同,那么你可以将其称为离策略学习。此外,在得出关于哪一种“最好”的结论之前,要知道在强化学习中,两者都有优点和缺点。一方面,在策略学习直观且稳定。如果你想擅长弹钢琴,为什么不练习钢琴呢?另一方面,似乎从除了自己的实际经验之外的其他来源学习是有用的;毕竟,一天中只有那么多时间。也许冥想可以教你一些关于弹钢琴的知识,并帮助你提高弹钢琴的技巧。但是,虽然离策略学习可以帮助你从多个来源(和/或多个技能)中学习,但使用离策略学习的方法通常具有更高的方差,因此收敛速度较慢。此外,要知道离策略学习是导致发散的元素之一:离策略学习、自助学习和函数逼近。这些并不总是相处得很好。你已经了解了前两个,第三个很快就会到来。 | |
| ŘŁ | 带有强化学习口音的贪婪策略在无限探索和随机逼近理论中 |
| | 贪婪策略在无限探索(GLIE)是一组要求,这些要求是策略强化学习算法(如蒙特卡洛控制和 SARSA)必须满足的,以确保收敛到最优策略。这些要求如下:
-
所有状态-动作对都必须无限次地被探索。
-
策略必须收敛到贪婪策略。
实际上这意味着,例如,ε-贪婪探索策略必须缓慢地将ε衰减到零。如果它下降得太快,第一个条件可能无法满足;如果衰减得太慢,那么,收敛需要更长的时间。注意,对于离策略强化学习算法,如 Q 学习,这两个条件中只有第一个是成立的。第二个条件不再是一个要求,因为在离策略学习中,所学习到的策略与我们采样动作的策略不同。例如,Q 学习只需要所有状态-动作对都得到充分更新,而这在本节的第一条条件中已经涵盖。现在,你是否可以确信地使用简单的探索策略,如ε-贪婪,来满足那个要求,这是另一个问题。在简单的网格世界和离散的动作和状态空间中,ε-贪婪很可能是有效的。但是,很容易想象出需要比随机行为更复杂的复杂环境。基于随机逼近理论,对于所有这些方法,还有另一套基于一般收敛的要求。因为我们是从样本中学习的,样本存在一些变异性,除非我们也推动学习率α向零,否则估计不会收敛:
-
学习率的总和必须是无限的。
-
学习率平方的总和必须是有限的。
这意味着你必须选择一个衰减但永远不会达到零的学习率。例如,如果你使用 1/t 或 1/e,学习率最初足够大,以确保算法不会过于紧密地跟随单个样本,但最终变得足够小,以确保它能找到噪声背后的信号。此外,尽管这些收敛性质对于开发强化学习算法的理论是有用的,但在实践中,学习率通常根据问题设置为足够小的常数。另外,要知道对于非平稳环境,小常数更好,这在现实世界中很常见。|
| ŘŁ | 带有强化学习口音的在线策略与离线策略学习 |
|---|---|
| 在线策略学习:指的是尝试评估或改进用于决策的策略的方法。这是直截了当的;想想一个单一的政策。这个政策产生行为。你的智能体评估这种行为,并根据这些估计选择改进的区域。你的智能体学习评估和改进它用于生成数据的同一策略。离线策略学习:指的是尝试评估或改进与用于生成数据的策略不同的策略的方法。这更复杂。想想两个策略。一个产生数据,经验,行为;但你的智能体使用这些数据来评估、改进,并总体上学习关于不同的策略,不同的行为。你的智能体学习评估和改进一个不同于用于生成数据的策略。 |
双 Q 学习:对最大估计值的估计的最大值
Q 学习经常高估价值函数。想想看。在每一步,我们都在下一个状态的动作值函数估计中取最大值。但我们需要的是下一个状态的最大动作值函数的实际值。换句话说,我们正在使用仅仅估计的最大值作为最大值的估计。
这样做不仅是一种不准确估计最大值的方法,而且是一个更严重的问题,因为这些用于形成TD目标的自我引导估计往往是有偏差的。将最大偏差估计用作最大值估计的问题被称为最大化偏差。
这很简单。想象一个动作值函数,其实际值都是零,但估计有偏差:有的正,有的负:例如,总是取最大值,我们总是倾向于取高值,即使它们有最大的偏差,最大的错误。这样做一次又一次地以负面方式累积错误。
我们都知道有些人性格积极,但在生活中却让事情出了差错:有些人被那些并不那么闪亮的东西所迷惑。对我来说,这就是许多人反对过分吹捧人工智能的原因之一;因为高估往往是你的敌人,而且确实需要采取措施来减轻,以改善性能。
| 我会说 Python 双 Q 学习代理 1/3 | |
|---|---|
def double_q_learning(env, ①
gamma=1.0,
init_alpha=0.5,
min_alpha=0.01,
alpha_decay_ratio=0.5,
init_epsilon=1.0,
min_epsilon=0.1,
epsilon_decay_ratio=0.9,
n_episodes=3000):
nS, nA = env.observation_space.n, env.action_space.n ②
pi_track = []
Q1 = np.zeros((nS, nA), dtype=np.float64) ③
Q2 = np.zeros((nS, nA), dtype=np.float64)
Q_track1 = np.zeros((n_episodes, nS, nA), dtype=np.float64)
Q_track2 = np.zeros((n_episodes, nS, nA), dtype=np.float64)
select_action = lambda state, Q, epsilon: \
np.argmax(Q[state]) \ ④
if np.random.random() > epsilon \
else np.random.randint(len(Q[state]))
alphas = decay_schedule(init_alpha,
min_alpha,
alpha_decay_ratio,
n_episodes)
epsilons = decay_schedule(init_epsilon,
min_epsilon,
epsilon_decay_ratio,
n_episodes)
for e in tqdm(range(n_episodes), leave=False): ⑤
① 如你所料,双 Q 学习与 Q 学习具有相同的精确论点。② 我们从相同的旧变量开始。③ 但你应该立即看到这里有一个很大的不同。我们正在使用两个状态值函数 Q1 和 Q2。你可以将其视为交叉验证:一个 Q 函数的估计将帮助我们验证另一个 Q 函数的估计。然而,问题现在是我们正在将经验分割成两个不同的函数。这多少会减慢训练速度。④ 本页的其余部分相当直接,你应该已经知道发生了什么。select_action、alphas 和 epsilons 的计算方式与之前相同。⑤ 继续阅读... |
| 我会说 Python 双 Q 学习代理 2/3 | |
|---|---|
for e in tqdm(range(n_episodes), leave=False): ⑥⑦
state, done = env.reset(), False ⑧
while not done: ⑨
action = select_action(state, ⑩
(Q1 + Q2)/2., ⑪
epsilons[e])
next_state, reward, done, _ = env.step(action) ⑫
if np.random.randint(2): ⑬
argmax_Q1 = np.argmax(Q1[next_state]) ⑭
td_target = reward + gamma * \
Q2[next_state][argmax_Q1] * (not done) ⑮⑯
td_error = td_target - Q1[state][action] ⑰
Q1[state][action] = Q1[state][action] + \ ⑱
alphas[e] * td_error ⑲
⑥ 从上一页⑦ 我们回到了剧集循环中。⑧ 对于每个新的剧集,我们首先重置环境并获取一个初始状态。⑨ 然后我们重复,直到我们达到终端状态(并且 done 标志被设置为 True)。⑩ 对于每一步,我们使用 select_action 函数选择一个动作。⑪ 但请注意一个有趣的事情:我们正在使用两个 Q 函数的均值!!我们也可以在这里使用 Q 函数的总和。它们会给出相似的结果。⑫ 然后我们将动作发送到环境中并获取经验元组。⑬ 现在事情开始发生变化。注意我们抛硬币来决定更新 Q1 或 Q2。⑭ 我们使用 Q1 认为最好的动作 ...⑮ ... 但使用 Q2 的值来计算 TD 目标。⑯ 注意在这里,我们使用 Q2 的值并由 Q1 指定。⑰ 然后计算 Q1 估计的 TD 错误。⑱ 最后,通过使用错误将我们的估计值移动到目标附近。⑲ 这一行将在下一页重复 ... |
| 我会说 Python 双 Q-learning 代理 3/3 | |
|---|---|
Q1[state][action] = Q1[state][action] + \ ⑳
alphas[e] * td_error
else: ㉑
argmax_Q2 = np.argmax(Q2[next_state]) ㉒
td_target = reward + gamma * \
Q1[next_state][argmax_Q2] * (not done) ㉓㉔
td_error = td_target - Q2[state][action] ㉕
Q2[state][action] = Q2[state][action] + \ ㉖
alphas[e] * td_error ㉗
state = next_state ㉘
Q_track1[e] = Q1 ㉙
Q_track2[e] = Q2
pi_track.append(np.argmax((Q1 + Q2)/2., axis=1)) ㉚
Q = (Q1 + Q2)/2\. ㉛
V = np.max(Q, axis=1) ㉜
pi = lambda s: {s:a for s, a in enumerate( \ ㉝
np.argmax(Q, axis=1))}[s]
return Q, V, pi, (Q_track1 + Q_track2)/2., pi_track ㉞
⑳ 好的。从上一页,我们正在计算 Q1。㉑ 如果随机整数是 0(50% 的时间),我们更新另一个 Q 函数,Q2。㉒ 但是,这基本上是另一个更新的镜像。我们得到 Q2 的 argmax。㉓ 然后使用那个动作,但使用另一个 Q 函数 Q1 的估计值。㉔ 再次注意 Q1 和 Q2 在这里的角色是相反的。㉕ 这次我们计算 Q2 的 TD 错误。㉖ 并用它来更新状态-动作对的 Q2 估计值。㉗ 注意我们如何使用‘alphas’向量。㉘ 我们改变状态变量的值并继续循环,直到我们到达一个终端状态并且‘done’变量被设置为 True。㉙ 在这里我们存储 Q1 和 Q2 以供离线分析。㉚ 注意策略是 Q1 和 Q2 均值的 argmax。㉛ 最终的 Q 是均值。㉜ 最终的 V 是 Q 的最大值。㉝ 最终的策略是 Qs 均值的 argmax。㉞ 我们最终返回所有这些。|
处理最大化偏差的一种方法是在两个 Q 函数中跟踪估计值。在每一步,我们选择其中一个来确定动作,以确定根据该 Q 函数哪个估计值是最高的。但是,然后我们使用另一个 Q 函数来获取该动作的估计值。通过这样做,有更低的概率总是有一个正偏差误差。然后,为了选择与环境交互的动作,我们使用该状态的两个 Q 函数的平均值或总和。也就是说,例如,Q1+Q[2](S[t+1*]) 的最大值。使用这两个 Q 函数的技术称为 双学习,实现此技术的算法称为 双 Q-learning。在接下来的几章中,你将了解一种名为 双深度 Q 网络(DDQN)的深度强化学习算法,它使用这种双学习技术的变体。
| 这就是细节 FVMC、SARSA、Q-learning 和双 Q-learning 在 SWS 环境上的应用 | |
|---|---|
让我们把这些都放在一起,并在滑溜的七环境(Slippery Walk Seven)中测试我们刚刚学习到的所有算法。所以,你要知道,我在所有算法中使用了相同的超参数,相同的伽马(gamma)、alpha、epsilon 以及相应的衰减计划。记住,如果你不将 alpha 衰减到 0,算法就不会完全收敛。我将它衰减到 0.01,这对于这个简单环境来说已经足够好了。epsilon 也应该衰减到零以实现完全收敛,但在实践中这很少发生。事实上,最先进的实现通常甚至不会衰减 epsilon,而是使用一个常数值。在这里,我们将其衰减到 0.1。 另一件事:请注意,在这些运行中,我为所有算法设置了相同数量的剧集;它们都在 SWS 环境中运行了 3,000 个剧集。你会发现一些算法在这么多步骤中不会收敛,但这并不意味着它们根本不会收敛。此外,本章笔记本中的其他一些环境,如冰湖(Frozen Lake),在达到一定数量的步骤后终止,也就是说,你的智能体有 100 步来完成每个剧集,否则它会被赋予完成标志。这是一个我们将在后续章节中解决的问题。但请,去笔记本里玩玩!我想你会喜欢在那里玩来玩去。 |
|
| 计算自举和在线策略方法之间的相似趋势 | |
![]() |
|
| 计算在 SWS 环境中学习到的策略 | |
![]() |
|
| 计算在 SWS 环境中学习到的值函数 | |
![]() |
摘要
在本章中,你将迄今为止所学的一切付诸实践。我们学习了通过试错学习优化策略的算法。这些算法从同时具有顺序性和评估性的反馈中学习;也就是说,这些智能体学习同时平衡短期和长期目标以及信息的收集和利用。但与上一章不同,在上一章中,我们将智能体限制在解决预测问题,而在本章中,我们的智能体学会了解决控制问题。
本章中你学习了许多重要的概念。你了解到预测问题由评估策略组成,而控制问题由优化策略组成。你了解到预测问题的解决方案在策略评估方法中,例如前一章中提到的那些。但出乎意料的是,控制问题并不是仅通过你过去学过的策略改进方法来解决的。相反,为了解决控制问题,我们需要使用策略评估方法来学习仅从样本中估计动作价值函数,以及考虑探索需求的策略改进方法。
本章的关键要点是广义策略迭代模式(GPI),它由策略评估和策略改进方法之间的交互组成。虽然策略评估使价值函数与评估的策略一致,但策略改进则反转这种一致性,但产生更好的策略。GPI 告诉我们,通过这两个过程的交互,我们可以迭代地产生更好和更好的策略,直到收敛到最优策略和价值函数。强化学习理论支持这种模式,并告诉我们,确实,我们可以在离散的状态和动作空间中找到最优策略和价值函数,只需满足几个要求。你了解到 GLIE 和随机逼近理论在不同层面上应用于 RL 算法。
你了解了许多其他事物,从在线策略到离线策略,以及更多。双 Q 学习和双学习通常是我们后来构建的关键技术。在下一章中,我们将探讨解决控制问题的先进方法。随着环境变得具有挑战性,我们使用其他技术来学习最优策略。接下来,我们将查看更有效地解决环境的更有效的方法。也就是说,它们解决了这些环境,并且使用比本章中我们学习的方法更少的经验样本。
到现在为止,
-
了解大多数 RL 代理遵循一种称为广义策略迭代的模式
-
了解 GPI 通过策略评估和改进来解决控制问题
-
了解了几种遵循 GPI 模式来解决控制问题的代理
| 推文功能:独自工作并分享你的发现 |
|---|
| | 这里有一些想法,如何将你所学的知识提升到更高层次。如果你愿意,可以与全世界分享你的成果,并确保查看其他人所做的工作。这是一个双赢的局面,希望你能充分利用它。
-
#gdrl_ch06_tf01: 本章中提出的所有算法都使用两个关键变量:学习率,alpha,和折扣因子,gamma。我希望你对这两个变量进行分析。例如,这些变量是如何相互作用的?它们如何影响智能体获得的总奖励和策略成功率?
-
#gdrl_ch06_tf02: 在本章之后,还有一件事需要考虑的是,我们为所有方法使用了相同的探索策略:这是一个指数衰减的ε-greedy 策略。但是,这是否是最好的策略?你将如何使用第四章中的其他策略?自己创建一个探索策略并测试它如何?改变与探索策略相关的超参数,看看结果如何变化?尝试这些应该不会太难。前往本书的 Notebooks,首先改变几个超参数:然后完全改变探索策略,告诉我们你发现了什么。
-
#gdrl_ch06_tf03: 你可能已经猜到了。本章中的算法也没有正确使用时间步限制。确保调查我所暗示的内容,一旦你发现了,就将算法更正为正确使用。结果是否有所改变?智能体现在是否比以前做得更好?它们在估计最优值函数、最优策略或两者方面是否更好?好多少?确保进行调查,或者在第八章之后回来。分享你的发现。
-
#gdrl_ch06_tf04: 在每一章中,我都使用最后的标签作为通用的标签。请随意使用这个标签来讨论与本章相关的任何其他工作。没有什么比你自己创造的任务更令人兴奋的了。确保分享你设定要调查的内容和你的结果。
用你的发现写一条推文,@mimoralea(我会转发),并使用列表中的特定标签来帮助感兴趣的人找到你的结果。没有正确或错误的结果;你分享你的发现并检查他人的发现。利用这个机会社交,做出贡献,让自己脱颖而出!我们正在等待你!以下是一条推文示例:“嘿,@mimoralea。我创建了一个包含学习深度强化学习资源的博客文章列表。查看它吧: #gdrl_ch01_tf01”我会确保转发并帮助他人找到你的工作。|
7 更有效地实现目标
在本章中
-
你将学习如何使强化学习代理在与具有挑战性的环境交互时更有效地达到最佳性能。
-
你将学习如何通过充分利用经验来使强化学习代理更有效地实现目标。
-
你将改进上一章中展示的代理,使它们能够充分利用收集到的数据,从而更快地优化其性能。
效率是正确地做事;有效性是做正确的事。
— 彼得·德鲁克 现代管理之父和自由勋章获得者
在本章中,我们改进了上一章中介绍过的代理。更具体地说,我们采取了两个独立的改进方向。首先,我们使用第五章中学习的λ-回报来满足广义策略迭代模式中的策略评估需求。我们探讨了使用λ-回报来进行在线和离线方法。使用带有资格痕迹的λ-回报比标准方法更快地将信用传播到正确的状态-动作对,从而使价值函数估计更快地接近实际值。
其次,我们探索使用经验样本来学习环境模型,即马尔可夫决策过程(MDP)的算法。通过这样做,这些方法能够从收集的数据中提取出最大价值,并且通常比那些不使用模型的方法更快地达到最优解。试图学习环境模型的算法组被称为基于模型的强化学习。
重要的是要注意,尽管我们分别探索了这些改进方向,但没有任何东西阻止你尝试将它们结合起来,也许在完成本章后你应该这样做。让我们立即深入了解细节。
| ŘŁ | 基于规划的强化学习 vs. 无模型强化学习 vs. 基于模型的强化学习 |
|---|---|
| 规划:指的是需要环境模型来生成策略的算法。规划方法可以是状态空间规划类型,这意味着它们使用状态空间来找到策略,或者它们可以是计划空间规划类型,这意味着它们在所有可能计划的空间中搜索(想想遗传算法)。本书中我们学习过的规划算法示例包括价值迭代和政策迭代。无模型强化学习:指的是不使用环境模型但仍然能够生成策略的算法。这里的独特特性是这些方法在没有地图、模型或 MDP 的情况下获得策略。相反,它们使用试错学习来获得策略。本书中我们探索过的无模型强化学习算法的几个例子是 MC、SARSA 和 Q-learning。基于模型的强化学习:指的是可以学习但不需要环境模型来生成策略的算法。区别在于它们不需要预先模型,但当然可以很好地利用它们,更重要的是,它们试图通过与环境的交互来学习模型。本章我们学习的基于模型的强化学习算法的几个例子是 Dyna-Q 和轨迹采样。 |
使用鲁棒目标改进策略的学习
本章我们讨论的第一个改进方案是在我们的策略评估方法中使用更鲁棒的目标。回想一下,在第五章中,我们探讨了使用不同类型的目标来估计价值函数的策略评估方法。你学习了蒙特卡洛和TD方法,还了解了一种称为λ-回报的目标,它使用所有访问过的状态获得的目标的加权组合。
TD(λ)是我们用于策略评估需要的预测方法。然而,正如你从上一章所记得的,在处理控制问题时,我们需要使用策略评估方法来估计动作值函数,以及允许探索的策略改进方法。在本节中,我们讨论类似于 SARSA 和 Q 学习的控制方法,但使用的是λ-回报。
| 滑坡行走环境 | |
|---|---|
为了介绍本章中的算法,我们使用与上一章相同的同一个环境,称为滑行七(SWS)。然而,在本章结束时,我们在更具挑战性的环境中测试了这些方法。回想一下,SWS 是一种行走,一个单行网格世界环境,有七个非终止状态。记住,这个环境是一个“滑行”的行走,这意味着它是嘈杂的,动作效果是随机的。如果代理选择向左走,它可能会这样做,但也有可能向右走,或者保持在原地。 滑行七环境 MDP 作为一个复习,上面是此环境的 MDP。但请记住,并且始终牢记,代理无法访问过渡概率。这个环境的动态对代理来说是未知的。此外,对代理来说,预先没有状态之间的关系。 |
SARSA(λ):根据多步估计在每一步改进策略
sARSA(λ)是对原始 SARSA 代理的一个简单改进。SARSA 和 SARSA(λ)之间的主要区别在于,在 SARSA(λ)中,我们使用λ-回报,而不是像在 SARSA 中那样使用一步引导目标——TD目标。就是这样;你就有 SARSA(λ)了。真的!你看到学习基础知识是如何让更复杂的概念变得容易了吗?
现在,我想深入探讨一下你最初在第五章中读到的资格迹的概念。我在第五章中介绍的资格迹类型被称为累积迹。然而,在现实中,有多种方式来追踪导致奖励的状态或状态-动作对。
在本节中,我们深入挖掘累积迹,并将其应用于解决控制问题,同时我们还探索了一种不同类型的迹,称为替换迹,并在 SARSA(λ)代理中使用这两种迹。
| 0001 | 历史简介:SARSA 和 SARSA(λ)代理介绍 |
|---|---|
| 1994 年,Gavin Rummery 和 Mahesan Niranjan 发表了一篇题为“使用连接主义系统进行在线 Q 学习”的论文,在论文中,他们介绍了一种当时他们称之为“修改后的连接主义 Q 学习”的算法。1996 年,Singh 和 Sutton 将此算法命名为 SARSA,因为该算法使用了五个事件:(S[t], A[t], R[t+1], S[t+1], A[t+1])。人们常常想知道这些名称的由来,正如你很快就会看到的,强化学习研究人员在这些名称上可以非常富有创意。有趣的是,在这项算法的公开和“未经授权”的更名之前,1995 年,在题为“使用强化学习进行问题解决”的博士论文中,Gavin 向 Sutton 道歉,因为他继续使用“修改后的 Q 学习”这一名称,尽管 Sutton 更喜欢“SARSA”。Sutton 也继续使用 SARSA,这最终成为了强化学习社区中该算法的名称。顺便说一下,Gavin 的论文还介绍了 SARSA(λ)智能体。在 1995 年获得博士学位后,Gavin 成为了一名程序员,后来成为负责《古墓丽影》系列游戏的公司的首席程序员。Gavin 作为一名游戏开发者取得了成功。Mahesan 在 Gavin 原来的导师意外去世后成为他的博士导师,自 1990 年毕业以来,他一直从事更传统的学术生涯,担任讲师和教授等职务。 |
为了将累积跟踪应用于解决控制问题,唯一必要的改变是我们现在必须跟踪访问过的状态-动作对,而不是访问过的状态。我们不再使用资格向量来跟踪访问过的状态,而是使用资格矩阵来跟踪访问过的状态-动作对。
替换跟踪机制也很简单。它包括将资格跟踪剪裁到最大值为一;也就是说,我们不再无限制地累积资格,而是允许跟踪仅增长到一。这种策略的优势在于,如果您的智能体陷入循环,跟踪仍然不会不成比例地增长。总之,在替换跟踪策略中,当访问状态-动作对时,跟踪被设置为 一,并根据 λ 值衰减,就像在累积跟踪策略中一样。
| 0001 | 历史简介:资格跟踪机制的介绍 |
|---|---|
| 可塑性痕迹机制的一般思想可能归功于 A. Harry Klopf,他在 1972 年一篇题为“大脑功能和自适应系统——一种异质理论”的论文中,描述了在强化事件之后,突触将如何成为“可塑性”的。他假设:“当一个神经元放电时,所有在导致响应的电位总和期间活跃的兴奋性和抑制性突触都有资格在它们的传递性上发生变化。”然而,在强化学习的背景下,Richard Sutton 的博士论文(1984 年)引入了可塑性痕迹机制。更具体地说,他引入了你在本书中学到的累积痕迹,也称为传统累积痕迹。另一方面,替换痕迹是由 Satinder Singh 和 Richard Sutton 在 1996 年一篇题为《使用替换可塑性痕迹的强化学习》的论文中引入的,我们将在本章中讨论。他们发现了一些有趣的事实。首先,他们发现替换痕迹机制比累积痕迹机制导致的学习更快、更可靠。他们也发现累积痕迹机制是有偏的,而替换痕迹机制是无偏的。但更有趣的是,他们发现了 TD(1)、MC 和可塑性痕迹之间的关系。更具体地说,他们发现带有替换痕迹的 TD(1)与首次访问 MC 相关,而带有累积痕迹的 TD(1)与每次访问 MC 相关。此外,他们发现离线版本的替换痕迹 TD(1)与首次访问 MC 相同。这是一个小世界! |

SWS 环境中的累积痕迹
| 简化频率和近期启发式在累积痕迹机制中的应用 | |
|---|---|
| 累积痕迹结合了频率和近期启发式。当你的智能体尝试一个状态-动作对时,这个对对的痕迹会增加一。现在,想象一下环境中有一个循环,智能体多次尝试相同的状态-动作对。我们应该使这个状态-动作对“更”多地负责未来获得的奖励,还是让它只负责?累积痕迹允许痕迹值高于一,而替换痕迹则不允许。痕迹有一种方法,在痕迹机制中隐式地编码了频率(你尝试状态-动作对的频率)和近期(你尝试状态-动作对的时间)启发式。 |

SWS 环境中的替换痕迹
| 我会说 PythonSARSA(λ)智能体 | |
|---|---|
def sarsa_lambda(env, ①
gamma=1.0,
init_alpha=0.5,
min_alpha=0.01,
alpha_decay_ratio=0.5,
init_epsilon=1.0,
min_epsilon=0.1,
epsilon_decay_ratio=0.9,
lambda_=0.5, ②
replacing_traces=True, ③
n_episodes=3000):
nS, nA = env.observation_space.n, env.action_space.n ④
pi_track = []
Q = np.zeros((nS, nA), dtype=np.float64) ⑤
Q_track = np.zeros((n_episodes, nS, nA),
dtype=np.float64)
E = np.zeros((nS, nA), dtype=np.float64) ⑥
select_action = lambda state, Q, epsilon: \ ⑦
np.argmax(Q[state]) \
if np.random.random() > epsilon \
else np.random.randint(len(Q[state]))
alphas = decay_schedule(
init_alpha, min_alpha,
alpha_decay_ratio, n_episodes)
epsilons = decay_schedule(
init_epsilon, min_epsilon,
epsilon_decay_ratio, n_episodes)
for e in tqdm(range(n_episodes), leave=False):
E.fill(0) ⑧
state, done = env.reset(), False ⑨
action = select_action(state, Q, epsilons[e]) ⑩
while not done: ⑪
next_state, reward, done, _ = env.step(action) ⑫
next_action = select_action(next_state, ⑬
Q,
epsilons[e]) ⑬
td_target = reward + gamma * \
Q[next_state][next_action] * (not done) ⑭
td_error = td_target - Q[state][action]
E[state][action] = E[state][action] + 1 ⑮
if replacing_traces: E.clip(0, 1, out=E) ⑮
Q = Q + alphas[e] * td_error * E ⑯
E = gamma * lambda_ * E ⑰
state, action = next_state, next_action ⑱
Q_track[e] = Q ⑲
pi_track.append(np.argmax(Q, axis=1))
V = np.max(Q, axis=1) ⑳
pi = lambda s: {s:a for s, a in enumerate(\
np.argmax(Q, axis=1))}[s]
return Q, V, pi, Q_track, pi_track
① SARSA lambda 代理是 SARSA 和 TD lambda 方法的混合体。② 这里是 lambda_ 超参数(以 _ 结尾,因为 lambda 这个词在 Python 中是保留的)。③ replacing_traces 变量设置算法使用替换或积累痕迹。④ 我们使用之前常用的变量...⑤ ...包括 Q 函数和跟踪矩阵。⑥ 这些是允许我们跟踪可更新状态的资格痕迹。⑦ 其余部分与之前相同,包括 select_action 函数以及 alphas 和 epsilons 向量。⑧ 每个新剧集开始时,我们将每个状态的可选性设置为零。⑨ 然后我们像往常一样重置环境和 done 标志。⑩ 我们选择初始状态的动作。⑪ 我们进入交互循环。⑫ 我们将动作发送到环境中,并接收经验元组。⑬ 我们使用 Q 表和对应剧集的 epsilon 选择下一个状态的动作。⑭ 我们像原始 SARSA 一样计算 TD 目标和 TD 错误。⑮ 然后,我们增加状态-动作对的痕迹,并在是替换痕迹的情况下将其剪辑到 1。⑯ 注意这一点!我们一次将 TD 错误应用于所有合格的州-动作对。尽管我们使用整个 Q 表,但 E 将大部分为 0,对于合格的配对大于零。⑰ 我们衰减可选性。⑱ 更新变量。⑲ 保存 Q 和 pi。⑳ 训练结束时提取 V、pi 并返回。 |
| 积累和替换痕迹,以及无麸质和无香蕉饮食 | |
|---|---|
| 几个月前,我的女儿晚上睡觉有困难。每天晚上,她都会多次醒来,大声哭泣,但不幸的是,她并没有告诉我们问题是什么。经过几晚之后,我和妻子决定采取一些措施,试图“追溯”问题,以便我们能更有效地“归因”导致失眠的原因。我们戴上了侦探帽(如果你是父母,你就知道这是什么感觉)并尝试了许多方法来诊断问题。大约一周后,我们将问题缩小到食物上;我们知道她吃某些食物的晚上会不好,但我们无法确定具体是哪些食物要承担责任。我注意到,在整个白天,她会吃很多含有麸质的碳水化合物,比如谷物、面条、饼干和面包。而且,接近睡觉时间,她会吃一些水果。我大脑中的“累积痕迹”指向了碳水化合物。“当然!”我想,“麸质是邪恶的;我们都知道这一点。而且,她整天都在吃那么多麸质。”如果我们追溯并累积她吃麸质的次数,麸质显然是合格的,显然是罪魁祸首,所以我们确实移除了麸质。但是,出乎我们的意料,问题只是有所缓解,并没有完全消失,正如我们希望的那样。几天后,我的妻子回想起她小时候晚上吃香蕉会有困难。我简直不敢相信,我的意思是,香蕉是水果,水果都是有益的,对吧?但有趣的是,最后,移除香蕉消除了那些糟糕的夜晚。难以置信!但是,也许如果我使用的是“替代痕迹”而不是“累积痕迹”,她一天中多次吃的所有碳水化合物都会得到更保守的责备。相反,因为我使用的是累积痕迹,在我看来,她多次吃麸质是罪魁祸首。就是这样。我无法清楚地看到香蕉的近期性发挥了作用。总的来说,累积痕迹在面对频率时可能会“夸大”,而替代痕迹则会减轻频繁事件所分配的责备。这种减轻可以帮助近期但罕见的事件浮出水面,并得到考虑。不要急于下结论。就像生活中的每一件事,在现实生活中的每一件事一样,了解工具并不仅仅是一眼就否定事物是至关重要的。我只是向你展示了可用的选项,但使用正确的工具来实现你的目标取决于你。 |
Watkins 的 Q(λ):再次将行为与学习分离
当然,还有λ算法的离策略控制版本。Q(λ)是 Q 学习的扩展,它使用λ-回报来满足广义策略迭代模式的策略评估需求。记住,我们在这里做的唯一改变是将离策略控制的TD目标(使用下一个状态中动作的最大值)替换为λ-回报。将 Q 学习扩展到资格迹有两种不同的方法,但我只介绍原始版本,通常被称为*Watkins 的 Q(λ)。
| 0001 | 历史简介:Q 学习与Q(λ)代理介绍 |
|---|---|
| 1989 年,Chris Watkins 在他的博士论文《从延迟奖励中学习》中介绍了 Q 学习与Q(λ)方法,这篇论文是当前强化学习理论的基石。Q 学习仍然是最受欢迎的强化学习算法之一,可能是因为它简单且效果良好。Q(λ)现在被称为 Watkins 的Q(λ),因为有一个稍微不同的Q(λ)版本——由 Jing Peng 和 Ronald Williams 在 1993 年至 1996 年之间工作(这个版本被称为 Peng 的Q(λ)。)1992 年,Chris 与 Peter Dayan 一起发表了一篇题为“技术笔记 Q 学习”的论文,其中他们证明了 Q 学习的收敛定理。他们证明了在所有状态-动作对反复采样并离散表示的假设下,Q 学习以概率 1 收敛到最优动作值函数。不幸的是,Chris 在那之后几乎停止了 RL 研究。他继续在伦敦的避险基金工作,然后访问研究实验室,包括由 Yann LeCun 领导的一个小组,一直从事与 AI 相关的问题,但不是那么多的 RL。在过去的 22+年里,Chris 一直是伦敦大学的人工智能讲师。在完成他的 1991 年博士论文《强化连接主义:以统计方式学习》后(是的,那时候他们把神经网络称为连接主义——“深度强化学习”吗?是的!)Peter 继续进行了一两个博士后研究,包括在多伦多大学的 Geoff Hinton 那里。Peter 是 DeepMind 的共同创始人 Demis Hassabis 的博士后导师。Peter 在研究实验室担任了许多董事职位,最新的职位是马克斯·普朗克研究所。自 2018 年以来,他一直是英国皇家学会的院士,这是在英国授予的最高奖项之一。 | |
| 我会说 Python:Watkins 的Q(λ)代理 1/3 | |
def q_lambda(env, ①
gamma=1.0,
init_alpha=0.5,
min_alpha=0.01,
alpha_decay_ratio=0.5,
init_epsilon=1.0,
min_epsilon=0.1,
epsilon_decay_ratio=0.9,
lambda_=0.5, ②
replacing_traces=True, ②
n_episodes=3000):
nS, nA = env.observation_space.n, env.action_space.n ③
pi_track = []
Q = np.zeros((nS, nA), dtype=np.float64) ④
Q_track = np.zeros((n_episodes, nS, nA), dtype=np.float64)
E = np.zeros((nS, nA), dtype=np.float64) ⑤
select_action = lambda state, Q, epsilon: \ ⑥
np.argmax(Q[state]) \
if np.random.random() > epsilon \
else np.random.randint(len(Q[state]))
alphas = decay_schedule(
init_alpha, min_alpha,
alpha_decay_ratio, n_episodes)
epsilons = decay_schedule(
init_epsilon, min_epsilon,
epsilon_decay_ratio, n_episodes)
for e in tqdm(range(n_episodes), leave=False): ⑦
① Q lambda 代理是 Q 学习和TD lambda 方法的混合体。② 这里是 lambda_ 和 replacing_traces 超参数。③ 有用的变量④ Q 表⑤ 所有状态-动作对的资格迹矩阵⑥ 常见的问题⑦ 待续... |
| 我会说 Python:Watkins 的Q(λ)代理 2/3 | |
|---|---|
for e in tqdm(range(n_episodes), leave=False): ⑧
E.fill(0) ⑨
state, done = env.reset(), False ⑩
action = select_action(state, ⑪
Q,
epsilons[e])
while not done: ⑫
next_state, reward, done, _ = env.step(action) ⑬
next_action = select_action(next_state, ⑭
Q,
epsilons[e])
next_action_is_greedy = \ ⑮
Q[next_state][next_action] == Q[next_state].max()
td_target = reward + gamma * \
Q[next_state].max() * (not done) ⑯
td_error = td_target - Q[state][action] ⑰⑱
⑧ 在场景循环中继续⑨ 好的。因为 Q lambda 是一种离策略方法,我们必须谨慎地使用 E。我们正在学习贪婪策略,但遵循探索性策略。首先,我们像之前一样用零填充 E。⑩ 重置环境并完成。⑪ 但是,注意我们是如何像 SARSA 那样预先选择动作的,但在 Q-learning 中我们没有这样做。这是因为我们需要检查我们的下一个动作是否是贪婪的!⑫ 进入交互循环。⑬ 步进环境并获得经验。⑭ 我们以 SARSA 风格选择下一个动作!⑮ 并用它来验证在下一步中采取的动作仍然来自贪婪策略。⑯ 在这一步,我们仍然像常规 Q-learning 那样计算TD目标,使用最大值。⑰ 并使用TD目标来计算TD误差。⑱ 我们从下一页的这条线继续。 |
| 我会说 PythonWatkins 的Q(λ)智能体 3/3 | |
|---|---|
td_error = td_target - Q[state][action] ⑲
if replacing_traces: E[state].fill(0) ⑳
E[state][action] = E[state][action] + 1 ㉑
Q = Q + alphas[e] * td_error * E ㉒
if next_action_is_greedy:
E = gamma * lambda_ * E ㉓
else:
E.fill(0)
state, action = next_state, next_action ㉔
Q_track[e] = Q
pi_track.append(np.argmax(Q, axis=1)) ㉕
V = np.max(Q, axis=1) ㉖
pi = lambda s: {s:a for s, a in enumerate(\
np.argmax(Q, axis=1))}[s]
return Q, V, pi, Q_track, pi_track ㉗
⑲ 再次,使用目标和当前状态-动作对的当前估计来计算TD误差。注意,这不是下一个状态,这是状态!!!⑳ 替换迹控制方法的另一种方法是清除当前状态的所有动作值,然后增加当前动作。㉑ 我们将当前状态-动作对的资格增量增加 1。㉒ 并且像之前一样,我们将整个资格迹矩阵乘以错误和对应于该段落的错误率,然后将整个 Q 移动到那个错误。通过这样做,我们实际上是将信号降低到所有访问状态的各种程度。㉓ 注意这一点。如果我们将在下一个状态采取的动作(我们已选择)是贪婪动作,那么我们像往常一样衰减资格矩阵,否则,我们必须将资格矩阵重置为零,因为我们不再学习关于贪婪策略。㉔ 步骤结束时,我们更新状态和动作,使它们成为下一个状态和动作。㉕ 我们保存 Q 和 pi。㉖ 并且在训练结束时,我们也保存 V 和最终的 pi。㉗ 最后,我们返回所有这些。 |
交互、学习和规划智能体
在第三章中,我们讨论了如值迭代(VI)和政策迭代(PI)之类的规划算法。这些是规划算法,因为它们需要一个环境模型,一个 MDP。规划方法在离线计算最优策略。另一方面,在上一章中,我介绍了无模型的强化学习方法,也许甚至暗示它们是规划方法的改进。但它们是吗?
无模型 RL 相对于规划方法的优点是,前者不需要 MDP。通常,MDP 在事先很难获得;有时 MDP 甚至无法创建。想象一下用10¹⁷⁰可能的状态来表示围棋或用10¹⁶⁸⁵状态来表示星际争霸 II。这些是很大的数字,而且这还不包括动作空间或转换函数,想象一下!事先不需要 MDP 是一个实际的好处。
但是,让我们稍微思考一下:如果我们事先不需要 MDP,而是在与环境交互的过程中学习一个 MDP 会怎样呢?想想看:当你在一个新区域散步时,你开始在脑海中构建地图。你走了会儿,找到了一家咖啡馆,喝了咖啡,然后你知道怎么回去。学习地图的技能对你来说应该是直观的。强化学习代理能否做类似的事情?
在本节中,我们探索与环境交互的代理,如无模型方法,但它们也通过这些交互学习环境的模型,MDP。通过学习地图,代理通常需要更少的经验样本来学习最优策略。这些方法被称为基于模型的强化学习。请注意,在文献中,你经常看到 VI 和 PI 被引用为规划方法,但你也可能看到它们被引用为基于模型的方法。我更喜欢划清界限,将它们称为规划方法,因为它们需要 MDP 才能做任何有用的事情。SARSA 和 Q-learning 算法是无模型的,因为它们不需要也不学习 MDP。在本节中你将学习的方法是基于模型的,因为它们不需要,但学习并使用 MDP(或者至少是 MDP 的近似)。|
| ŘŁ | 带有 RL 口音的采样模型与分布模型 |
|---|---|
| 采样模型:指的是环境模型,它根据某些概率产生环境如何转移的单个样本;你从模型中采样一个转移。分布模型:指的是环境模型,它产生转移和奖励函数的概率分布。 |
Dyna-Q:学习样本模型
将规划和无模型方法统一的最著名的架构之一被称为dyna-Q。Dyna-Q 由交替使用无模型 RL 方法,如 Q-learning,和类似于价值迭代的规划方法组成,使用从环境中采样的经验和从学习模型中采样的经验来改进动作值函数。
在 Dyna-Q 中,我们跟踪状态、动作和下一个状态的三维张量,作为转移和奖励函数。转移张量记录了看到三元组(s, a, s')的次数,表示在执行动作a时从状态s到达状态s'的次数。奖励张量持有我们在三元组(s, a, s')上获得的平均奖励,表示当我们选择动作a在状态s并转移到状态s'时的预期奖励。

基于模型的强化学习架构
| 0001 | 一点历史介绍 Dyna-Q 代理 |
|---|---|
| 与基于模型的强化学习方法相关的想法可以追溯到很多年,归功于几位研究人员,但有三篇主要论文为 Dyna 架构奠定了基础。第一篇是 1981 年由理查德·萨顿和安德鲁·巴特罗撰写的论文“An Adaptive Network that Constructs and Uses an Internal Model of Its World”,然后是 1990 年由理查德·萨顿撰写的论文“Integrated Architectures for Learning, Planning, and Reacting Based on Approximating Dynamic Programming”,最后是 1991 年由理查德·萨顿撰写的论文“Dyna, an Integrated Architecture for Learning, Planning, and Reacting”,其中介绍了导致特定 Dyna-Q 代理的通用架构。 | |
| 我会说 PythonDyna-Q 代理 1/3 | |
def dyna_q(env, ①
gamma=1.0,
init_alpha=0.5,
min_alpha=0.01,
alpha_decay_ratio=0.5,
init_epsilon=1.0,
min_epsilon=0.1,
epsilon_decay_ratio=0.9,
n_planning=3, ②
n_episodes=3000):
nS, nA = env.observation_space.n, env.action_space.n ③
pi_track = []
Q = np.zeros((nS, nA), dtype=np.float64) ④
Q_track = np.zeros((n_episodes, nS, nA), dtype=np.float64)
T_count = np.zeros((nS, nA, nS), dtype=np.int) ⑤
R_model = np.zeros((nS, nA, nS), dtype=np.float64) ⑥
select_action = lambda state, Q, epsilon: \
np.argmax(Q[state]) \ ⑦
if np.random.random() > epsilon \
else np.random.randint(len(Q[state]))
alphas = decay_schedule(
init_alpha, min_alpha,
alpha_decay_ratio, n_episodes)
epsilons = decay_schedule(
init_epsilon, min_epsilon,
epsilon_decay_ratio, n_episodes)
for e in tqdm(range(n_episodes), leave=False): ⑧
① Dyna-Q 与 Q-learning 代理类似,但它还学习环境模型,并使用该模型来改进估计。② 这个 n_planning 超参数是运行从学习模型中得到的估计更新的次数。③ 算法的前大部分是相同的。④ 我们将 Q 函数初始化为零,等等。⑤ 但然后,我们创建一个函数来跟踪转移函数。⑥ 另一个来跟踪奖励信号。⑦ 然后初始化探索策略 select_action,以及 alpha 和 epsilon 向量,就像往常一样。⑧ 待续 ... |
| 我会说 PythonDyna-Q 代理 2/3 | |
|---|---|
for e in tqdm(range(n_episodes), leave=False): ⑨
state, done = env.reset(), False ⑩
while not done:
action = select_action(state, Q, epsilons[e]) ⑪
next_state, reward, done, _ = env.step(action) ⑫
T_count[state][action][next_state] += 1 ⑬
r_diff = reward - \ ⑭
R_model[state][action][next_state]
R_model[state][action][next_state] += \ ⑮
(r_diff / T_count[state][action][next_state])
td_target = reward + gamma * \ ⑯
Q[next_state].max() * (not done)
td_error = td_target - Q[state][action] ⑰
Q[state][action] = Q[state][action] + \ ⑱
alphas[e] * td_error
backup_next_state = next_state ⑲
for _ in range(n_planning): ⑳
⑨ 在情节循环中继续⑩ 对于每个新的情节,我们首先重置环境并获得初始状态。我们还将“完成”标志设置为 False 并进入步骤交互循环。⑪ 我们选择动作,就像原始 Q-learning(只在循环内)一样。⑫ 我们对环境进行步骤操作并获得经验元组。⑬ 然后,开始学习模型!我们增加状态-动作-下一个状态三元组的转移计数,表示完整的转移再次发生。⑭ 我们还尝试计算奖励信号的增量平均值。获取差异。⑮ 然后使用该差异和转移计数来学习奖励信号。⑯ 我们像通常的 Q-learning 风格一样计算TD目标(离线策略,使用最大值)...⑰ ... 以及TD误差,也使用TD目标和当前估计。⑱ 最后,更新 Q 函数。⑲ 在我们进入规划步骤之前,我们备份下一个状态变量。⑳ 待续 ... |
| 我会说 PythonDyna-Q 代理 3/3 | |
|---|---|
for _ in range(n_planning): ㉑
if Q.sum() == 0: break ㉒
visited_states = np.where( \ ㉓
np.sum(T_count, axis=(1, 2)) > 0)[0]
state = np.random.choice(visited_states) ㉓
actions_taken = np.where( \ ㉔
np.sum(T_count[state], axis=1) > 0)[0]
action = np.random.choice(actions_taken) ㉔
probs = T_count[state][action] / \
T_count[state][action].sum() ㉕
next_state = np.random.choice( \
np.arange(nS), size=1, p=probs)[0]
reward = R_model[state][action][next_state] ㉖
td_target = reward + gamma * \
Q[next_state].max()
td_error = td_target - Q[state][action]
Q[state][action] = Q[state][action] + \ ㉗
alphas[e] * td_error
state = backup_next_state ㉘
Q_track[e] = Q ㉙
pi_track.append(np.argmax(Q, axis=1))
V = np.max(Q, axis=1)
pi = lambda s: {s:a for s, a in enumerate( \
np.argmax(Q, axis=1))}[s]
return Q, V, pi, Q_track, pi_track
㉑ 我们从规划循环继续。㉒ 首先,我们想要确保 Q 函数之前已经进行了更新,否则,就没有太多可以规划的。㉓ 然后我们从智能体在经验中已经访问过的状态列表中选择一个状态。㉔ 我们然后选择在该状态下已经采取的动作。㉕ 我们使用计数矩阵来计算下一个状态的概率,然后是下一个状态。㉖ 使用奖励模型作为奖励。㉗ 然后使用那个模拟经验来更新 Q 函数!㉘ 在规划步骤结束时,我们将状态设置为下一个状态。㉙ 其余的都是一样的。 |
| 总结结果模型方法学习转移和奖励函数(转移见下文) | |
|---|---|
![]() |
轨迹采样:为立即未来制定计划
在 Dyna-Q 中,我们学习模型的方式如前所述,调整动作值函数的方式与 vanilla Q-learning 相同,然后在算法的末尾运行几个规划迭代。请注意,如果我们从代码中移除模型学习和规划行,我们将剩下与上一章相同的 Q-learning 算法。
在规划阶段,我们只从已经访问过的状态-动作对中进行采样,这样智能体就不会在模型没有信息的状态-动作对上浪费资源。从那些访问过的状态-动作对中,我们随机均匀地采样一个状态,然后从之前选定的动作中随机采样动作。最后,我们从给定状态-动作对的转移概率中采样下一个状态和奖励。但这看起来似乎直观上是不正确的?我们是通过使用随机选择的状态来规划!
如果我们使用在当前场景中预期会遇到的状态,这种技术是否会更有效?思考一下。你更愿意优先规划你的日子、周、月和年,还是更愿意规划一个“可能”发生在你生活中的随机事件?比如说你是一名软件工程师:你更愿意规划阅读编程书籍,并着手那个副项目,还是规划一个可能的未来职业转变到医学?为立即未来制定计划是一种更明智的方法。轨迹采样是一种基于模型的强化学习方法,正是如此。
| 简化问题轨迹采样 | |
|---|---|
| 当 Dyna-Q 随机均匀地采样学习到的 MDP 时,轨迹采样收集轨迹,即未来可能遇到的转换和奖励。你正在规划你的周计划,而不是你生活中的某个随机时间。这样做更有意义。传统的轨迹采样方法是从初始状态开始,使用在线策略轨迹采样直到达到终端状态,换句话说,在给定的时间步中从相同的行为策略中采样动作。然而,你不应该局限于这种方法;你应该尝试。例如,我的实现是从当前状态开始采样,而不是从初始状态开始,到预设步数内的终端状态,采样一个相对于当前估计的贪婪策略。但你可以尝试其他方法。只要你在采样轨迹,你就可以称之为轨迹采样。 | |
| 我会说 Python 轨迹采样代理 1/3 | |
def trajectory_sampling(env, ①
gamma=1.0,
init_alpha=0.5,
min_alpha=0.01,
alpha_decay_ratio=0.5,
init_epsilon=1.0,
min_epsilon=0.1,
epsilon_decay_ratio=0.9,
max_trajectory_depth=100, ②
n_episodes=3000):
nS, nA = env.observation_space.n, env.action_space.n ③
pi_track = []
Q = np.zeros((nS, nA), dtype=np.float64) ④
Q_track = np.zeros((n_episodes, nS, nA), dtype=np.float64)
T_count = np.zeros((nS, nA, nS), dtype=np.int) ⑤
R_model = np.zeros((nS, nA, nS), dtype=np.float64) ⑥
select_action = lambda state, Q, epsilon: \ ⑦
np.argmax(Q[state]) \
if np.random.random() > epsilon \
else np.random.randint(len(Q[state]))
alphas = decay_schedule(
init_alpha, min_alpha,
alpha_decay_ratio, n_episodes)
epsilons = decay_schedule(
init_epsilon, min_epsilon,
epsilon_decay_ratio, n_episodes)
for e in tqdm(range(n_episodes), leave=False): ⑧
① 轨迹采样在大多数情况下与 Dyna-Q 相同,但有几点例外。② 我们使用 max_trajectory_depth 来限制轨迹长度,而不是 n_planning。③ 算法的大部分与 Dyna-Q 相同。④ Q 函数,等等⑤ 我们创建相同的变量来模拟转换函数...⑥ ...以及另一个用于奖励信号。⑦ select_action 函数、alphas 向量和 epsilons 向量都是相同的。⑧ 待续... |
| 我会说 Python 轨迹采样代理 2/3 | |
|---|---|
for e in tqdm(range(n_episodes), leave=False): ⑨
state, done = env.reset(), False ⑩
while not done:
action = select_action(state, Q, epsilons[e]) ⑪
next_state, reward, done, _ = env.step(action) ⑫
T_count[state][action][next_state] += 1 ⑬
r_diff = reward - \ ⑭
R_model[state][action][next_state]
R_model[state][action][next_state] += \ ⑮
(r_diff / T_count[state][action][next_state])
td_target = reward + gamma * \ ⑯
Q[next_state].max() * (not done)
td_error = td_target - Q[state][action] ⑰
Q[state][action] = Q[state][action] + \ ⑱
alphas[e] * td_error
backup_next_state = next_state ⑲
for _ in range(max_trajectory_depth): ⑳
⑨ 继续在剧集循环中⑩ 再次,每个新的剧集,我们首先重置环境并获得初始状态。我们还将 done 标志设置为 False 并进入步骤交互循环。⑪ 我们选择动作。⑫ 我们对环境进行步骤操作并获得经验元组。⑬ 我们像在 Dyna-Q 中一样学习模型:增加状态-动作-下一个状态三元组的转换计数,表示完整转换发生。⑭ 然后,再次计算奖励信号的增量平均值;首先,获取差异。⑮ 然后,使用该差异和转换计数来学习奖励信号。⑯ 我们像往常一样计算TD目标。⑰ 使用TD目标和当前估计的TD误差⑱ 然后,更新 Q 函数。⑲ 在我们进入规划步骤之前,我们备份下一个状态变量。⑳ 待续... |
| 我会说 Python 轨迹采样代理 3/3 | |
|---|---|
for _ in range(max_trajectory_depth): ㉑
if Q.sum() == 0: break ㉒
# action = select_action(state, Q, epsilons[e])
action = Q[state].argmax() ㉓
if not T_count[state][action].sum(): break ㉔
probs = T_count[state][action] / \
T_count[state][action].sum() ㉕
next_state = np.random.choice( \
np.arange(nS), size=1, p=probs)[0]
reward = R_model[state][action][next_state] ㉖
td_target = reward + gamma * \ ㉗
Q[next_state].max()
td_error = td_target - Q[state][action]
Q[state][action] = Q[state][action] + \
alphas[e] * td_error
state = next_state ㉘
state = backup_next_state ㉙
Q_track[e] = Q ㉚
pi_track.append(np.argmax(Q, axis=1))
V = np.max(Q, axis=1)
pi = lambda s: {s:a for s, a in enumerate( \
np.argmax(Q, axis=1))}[s]
return Q, V, pi, Q_track, pi_track
㉑ 注意我们现在正在使用 max_trajectory_depth 变量,但我们仍在规划。㉒ 我们仍然检查 Q 函数是否有任何差异,所以它值得我们的计算。㉓ 选择动作要么在策略内,要么在策略外(使用贪婪策略)。㉔ 如果我们没有经历过转换,规划将会一团糟,所以退出。㉕ 否则,我们得到 next_state 的概率并相应地采样模型。㉖ 然后,根据奖励信号模型获得奖励。㉗ 并且继续像真实经验一样更新 Q 函数。㉘ 注意在这里我们在循环之前更新状态变量,并继续策略内规划步骤。㉙ 在规划循环之外,我们恢复状态,并继续真实的交互步骤。㉚ 其他一切照旧 |
| 计数器增加 Dyna-Q 和轨迹采样以不同的方式采样学习模型 | |
|---|---|
![]() |
|
| 具体示例冻结湖环境 | |
在第二章中,我们为称为冻结湖(FL)的环境开发了 MDP。正如你所记得的,FL 是一个简单的网格世界(GW)环境。它具有离散的状态和动作空间,有 16 个状态和四个动作。代理的目标是从起始位置移动到目标位置,同时避免掉入坑中。在这个特定的冻结湖环境实例中,目标是从状态 0 移动到状态 15。挑战在于湖面被冰封,因此非常滑。 冻结湖环境 FL 环境是一个 4×4 的网格,有 16 个单元格,状态 0-15,从左上角到底部右侧。状态 0 是初始状态分布中的唯一状态,这意味着在每次新的一集中,代理都会出现在那个起始状态。状态 5、7、11、12 和 15 是终端状态:一旦代理落在这些状态中的任何一个,集会就结束了。状态 5、7、11 和 12 是坑,状态 15 是“目标”。使“坑”和“目标”不同的因素是奖励函数。所有落在目标状态,即状态 15 的转换都提供+1 的奖励,而整个网格世界中其他所有的转换都提供 0 奖励,没有奖励。代理自然会尝试到达那个+1 的转换,这涉及到避开坑。环境的挑战在于动作具有随机效果,因此代理只有三分之一的动作是按照预期移动的。其他三分之二均匀地分布在正交方向上。如果代理试图离开网格世界,它将弹回到它试图移动的单元格。 |
|
| 细节之处 Hyperparameter values for the frozen lake environment | |
冰湖(FL)环境比例如滑行七(SWS)环境更具挑战性。因此,我们需要做出的最重要的改变之一是增加智能体与环境交互的回合数。在 SWS 环境中,我们允许智能体仅交互 3,000 回合;在 FL 环境中,我们让智能体积累 10,000 回合的经验。这个简单的改变也自动调整了 alpha 和 epsilon 的衰减计划。将 n_episodes 参数的值从 3,000 改为 10,000 会自动改变智能体的探索和学习量。Alpha 现在在总回合数的 50%后(即 5,000 回合)从初始值 0.5 衰减到最小值 0.01,而 epsilon 在总回合数的 90%后(即 9,000 回合)从初始值 1.0 衰减到最小值 0.1。 最后,重要的是要提到,我使用的是 gamma 值为 0.99,并且当使用 OpenAI Gym 时,冰湖环境会自动用时间限制 Gym Wrapper 包装。这个“时间包装器”实例确保智能体在不超过 100 步的情况下结束回合。从技术上来说,这两个决定(gamma 和时间包装器)改变了智能体学习的最优策略和值函数,不应轻视。我建议在第七章的笔记本中尝试 FL 环境,并将 gamma 值改为不同的值(1,0.5,0),并且通过获取环境实例属性“unwrapped”,例如,“env = env.unwrapped”,来移除时间包装器。尝试理解这两者如何影响找到的策略和值函数。 |
|
| 总结一下:基于模型的 RL 方法在更少的回合中获得更接近实际的估计 | |
![]() |
|
| 总结一下:轨迹和基于模型的方法都有效地处理经验 | |
![]() |
|
| 一个具体的例子:8 x 8 的冰湖环境 | |
我们要不要提高难度,尝试在这些具有挑战性的环境中应用这些算法呢?这个环境被称为 8×8 冰湖(FL8×8)。正如你所预期的那样,这是一个 8×8 的网格世界,其属性与 FL 相似。初始状态是状态 0,位于左上角;终止和目标状态是状态 63,位于右下角。动作效果的不确定性是相同的:代理以仅仅 33.33%的概率移动到目标单元格,其余的以均匀的方式分布在正交方向上。 正如你所见,这个环境的主要区别在于有很多更多的洞,而且显然它们位于不同的位置。状态 19、29、35、41、42、46、49、52、54 和 59 是洞;总共有 10 个洞!与原始 FL 环境相似,在 FL8×8 中,正确的策略允许代理在 100%的回合中达到终止状态。然而,在 OpenAI Gym 的实现中,学习到最优策略的代理并没有找到这些特定的策略,这是由于我们讨论过的 gamma 和时间包装器。想想看:考虑到这些环境的不确定性,一个安全的策略可能会因为时间包装器而在这个回合中获得零奖励。此外,给定一个小于一的 gamma 值,代理采取的步骤越多,奖励对回报的影响就越低。因此,安全的策略不一定是最优策略;因此,代理没有学习到它们。记住,目标不仅仅是找到一个 100%达到目标的策略,而是在 FL 中在 100 步内,在 FL8×8 中在 200 步内达到目标。代理可能需要冒险才能实现这个目标。 |
|
| 它在于细节冰湖 8×8 环境的超参数值 | |
冰冻湖 8×8(FL8×8)环境是我们在这本书中讨论的最具挑战性的离散状态空间和动作空间环境。这个环境之所以具有挑战性,有以下几个原因:首先,64 个状态是我们处理过的状态数量最多的情况,但更重要的是,只有一个非零奖励使得这个环境特别具有挑战性。这实际上意味着智能体只有在第一次达到终端状态时才会知道他们已经做对了。记住,这是随机的!在他们找到非零奖励的转换之后,像 SARSA 和 Q-learning(不是 lambda 版本,而是原始版本)这样的智能体只会更新智能体转换到目标状态的那些状态的价值。这比奖励退回了一步。然后,为了使那个价值函数再退回一步,猜猜看,智能体需要随机地击中那个倒数第二个状态。但是,这是对于非 lambda 版本的情况。对于 SARSA(λ)和 Q(λ),价值的传播取决于 lambda 的值。在本章的所有实验中,我使用了一个 lambda 值为 0.5,这大致告诉智能体传播价值占轨迹的一半(也取决于所使用的痕迹类型,但作为一个大致估计)。令人惊讶的是,我们对这些智能体所做的唯一改变是允许它们与环境交互的次数。在 SWS 环境中,我们只允许智能体进行 3,000 次交互,在 FL 环境中,我们让智能体积累 10,000 次经验;在 FL8×8 中,我们让这些智能体积累 30,000 次经验。这意味着 alpha 现在从初始值 0.5 衰减到最小值 0.01,这是总交互次数的 50%,现在是 15,000 次,而 epsilon 在总交互次数的 90%后衰减到最小值 0.1,现在是 27,000 次。![]() |
|
| 总结一下,基于策略的方法不再跟上,基于痕迹的离策略和基于模型的方法可以 | |
![]() |
|
| 总结一下,一些基于模型的方法会显示出大的误差峰值以引起注意 | |
![]() |
摘要
在本章中,你学习了如何使强化学习(RL)更加有效和高效。这里的“有效”指的是本章中提到的智能体能够在有限的交互次数内解决环境问题。其他智能体,如原始的 SARSA、Q-learning,甚至是蒙特卡洛控制,在有限的步骤内解决这些挑战会有困难;至少,对于 FL8x8 环境来说,它们在只有 30,000 次交互的情况下会遇到困难。这就是本章中“有效”的含义;智能体能够成功产生预期的结果。
我们还探讨了更高效的算法。在这里,我指的是数据效率;我的意思是,我们在这章中引入的代理可以用相同的数据做更多的事情,比其他代理。例如,SARSA(λ)和 Q(λ)可以比它们的原始版本 SARSA 和 Q-learning 更快地将奖励传播到价值函数估计中。通过调整λ超参数,你甚至可以将信用分配给一个场景中访问过的所有状态。λ的值为 1 并不总是最好的,但至少在使用 SARSA(λ)和 Q(λ)时你有这个选项。
你还学习了基于模型的方法,例如 Dyna-Q 和轨迹采样。这些方法以不同的方式提高样本效率。它们使用样本来学习环境模型;如果你的代理在状态s中采取行动a时,将 100%的 100 万个样本落在状态s'上,为什么不利用这些信息来改进价值函数和政策呢?高级基于模型的深度强化学习方法通常用于收集经验样本成本高昂的环境:例如机器人领域,或者没有高速模拟的问题,或者硬件需要大量资金资源。
在本书的剩余部分,我们将讨论在使用非线性函数近似时出现的微妙之处。你之前学到的所有内容仍然适用。唯一的区别是,我们现在从监督学习和函数近似的世界开始,而不是使用向量和矩阵来存储价值函数和政策。记住,在 DRL 中,代理从同时具有顺序性(与一次性相反)、评价性(与监督相反)和样本性(与穷举相反)的反馈中学习。我们还没有触及“样本性”的部分;代理总是能够访问所有状态或状态-动作对,但从下一章开始,我们将专注于无法穷举样本的问题。
到现在为止,你已经
-
知道如何开发更有效的 RL 代理以实现其目标
-
知道如何创建更高效的样本 RL 代理
-
知道如何处理同时具有顺序性和评价性的反馈
| 在自己的工作上努力,并分享你的发现 |
|---|
| | 这里有一些想法,如何将你所学的知识提升到下一个层次。如果你愿意,可以与世界分享你的成果,并确保查看其他人所做的事情。这是一个双赢的局面,希望你能充分利用它。
-
#gdrl_ch07_tf01: 我只测试了本章中提出的算法在冻结的 8 × 8 湖环境中的效果,但你好奇上一章的算法如何吗?好吧,那就这么做!去书中的 Notebooks,将上一章的算法复制到这一章的 Notebook 中,然后运行并收集信息来比较所有算法。
-
#gdrl_ch07_tf02: 表格情况下的高级算法还有很多。整理一份有趣的算法列表,并与世界分享。
-
#gdrl_ch07_tf03: 现在,从你的列表中实现一个算法,并从别人的列表中实现另一个算法。如果你是第一个使用这个标签的人,那么就实现你列表中的两个算法。
-
#gdrl_ch07_tf04: 有一个基本的算法叫做优先遍历。你能调查这个算法,并告诉我们更多关于它的信息吗?确保你分享你的实现,将其添加到本章的笔记本中,并与本章的其他算法进行比较。
-
#gdrl_ch07_tf05: 创建一个类似于 8×8 的 Frozen Lake 环境,但更复杂,比如 16×16 的 Frozen Lake?现在测试所有算法,看看它们的性能如何。有没有哪个算法在本章中明显优于其他算法?
-
#gdrl_ch07_tf06: 在每一章中,我都使用最后一个标签作为通用的标签。请随意使用这个标签来讨论与本章相关的任何其他工作。没有比你自己创造的任务更令人兴奋的作业了。确保分享你设定要调查的内容和你的结果。
用你的发现写一条推文,@mimoralea(我会转发),并使用列表中的特定标签来帮助感兴趣的人找到你的结果。没有正确或错误的结果;你分享你的发现并检查别人的发现。利用这个机会社交,做出贡献,让自己脱颖而出!我们正在等待你!以下是一条推文示例:“嘿,@mimoralea。我创建了一个包含资源列表的博客文章,用于研究深度强化学习。查看它在这里<链接>。#gdrl_ch01_tf01”我会确保转发并帮助其他人找到你的作品。|
8 基于价值的深度强化学习简介
本章内容
-
你将理解使用非线性函数逼近器训练强化学习代理的内在挑战。
-
你将创建一个深度强化学习代理,当从头开始训练并最小调整超参数时,它可以解决不同类型的问题。
-
你将识别在使用基于价值的方法解决强化学习问题时,其优缺点。
人类行为源于三个主要来源:欲望、情感和知识。
— 柏拉图,古希腊哲学家,雅典学院创始人
我们已经取得了很大的进步,你现在准备好真正深入理解深度强化学习了。在第二章,你学习了如何用马尔可夫决策过程(MDP)的方式表示问题,以便强化学习代理能够解决。在第三章,你开发了解决这些 MDP 的算法:也就是说,找到序列决策问题中最佳行为的代理。在第四章,你学习了关于解决一步 MDP 的算法,而这些算法没有访问这些 MDP。这些问题是不确定的,因为代理没有访问 MDP。代理通过试错学习来学习找到最佳行为。在第五章,我们将这两种类型的问题——序列和不确定——混合在一起,因此我们探索了学习评估策略的代理。代理没有找到最佳策略,但能够准确评估策略和估计价值函数。在第六章,我们研究了在不确定性下,找到序列决策问题中最佳策略的代理。这些代理通过与环境的互动和故意收集经验来学习,从随机到最佳。在第七章,我们学习了通过充分利用经验来找到最佳策略的代理。
第二章是本书所有章节的基础。第三章是关于处理序列反馈的规划算法。第四章是关于处理评估反馈的赌博机算法。第 5、6、7 章是关于强化学习算法,这些算法处理同时具有序列和评估性的反馈。这类问题就是人们所说的表格强化学习。从本章开始,我们深入探讨深度强化学习的细节。
更具体地说,在本章中,我们开始探讨使用深度神经网络解决强化学习问题。在深度强化学习中,有不同方式利用高度非线性的函数逼近器(如深度神经网络)的力量。它们是价值基础、策略基础、演员-评论家、基于模型和无梯度方法。本章深入探讨了基于价值的深度强化学习方法。

本书介绍的各种算法方法
深度强化学习代理使用的反馈类型
在深度强化学习中,我们构建能够从同时具有评估性、顺序性和样本性的反馈中学习的代理。我在整本书中都强调了这一点,因为你需要理解这意味着什么。
在第一章中,我提到深度强化学习是在不确定性下的复杂顺序决策问题。你可能想,“这么多词。”但正如我承诺的,所有这些词都有意义。“顺序决策问题”是你在第三章中学到的。“不确定性下的问题”是你在第四章中学到的。在第五章、第六章和第七章中,你学习了“不确定性下的顺序决策问题”。在这一章中,我们将“复杂”这个词重新加回到整个句子中。让我们利用这个介绍部分再次回顾一下深度强化学习代理使用的三种类型的学习反馈。
| 深度强化学习中反馈的类型 | |
|---|---|
| 顺序的(与一次性相对) | |
| 监督学习 | × |
| 规划(第三章) | ✓ |
| 多臂老丨虎丨机(第四章) | × |
| 表格式强化学习(第五章、第六章、第七章) | ✓ |
| 深度强化学习(第八章、第九章、第十章、第十一章、第十二章) | ✓ |
深度强化学习代理处理顺序反馈
深度强化学习代理必须处理顺序反馈。顺序反馈的一个主要挑战是代理可以接收延迟信息。
你可以想象一场棋局,你一开始走了几步错棋,但这些错棋的后果只有在游戏结束时才会显现,如果你真的输掉了比赛。
延迟反馈使得难以解释反馈的来源。顺序反馈引发了时间信用分配问题,这是确定哪个状态、动作或状态-动作对负责奖励的挑战。当问题有时间成分且动作有延迟后果时,为奖励分配信用变得具有挑战性。

顺序反馈
但是,如果不是顺序的,那会是什么呢?
延迟反馈的反面是即时反馈。换句话说,顺序反馈的反面是一次性反馈。在处理一次性反馈的问题中,如监督学习或多臂老丨虎丨机,决策没有长期后果。例如,在分类问题中,无论正确与否对图像进行分类,都不会影响未来的性能;例如,在下一个模型中展示的图像与模型是否正确分类前一个批次无关。在深度强化学习中,这种顺序依赖性存在。

分类问题
此外,在多臂老丨虎丨机问题中,也没有长期后果,尽管这可能有点难以看到原因。老丨虎丨机是一状态一步马尔可夫决策过程(MDP),其中在单次动作选择后立即结束。因此,在该次期间,动作没有长期后果。

双臂老丨虎丨机
深度强化学习代理处理评估反馈
我们学到的第二个特性是评估反馈。深度强化学习、表格强化学习和老丨虎丨机都处理评估反馈。评估反馈的核心是反馈的好坏只是相对的,因为环境是不确定的。我们不知道环境的实际动态;我们没有访问到转换函数和奖励信号。
因此,我们必须探索我们周围的环境,以了解那里有什么。问题是,通过探索,我们错过了利用我们当前的知识,因此很可能会积累遗憾。从所有这些中,探索-利用权衡产生了。它是不确定性的一个持续副产品。在没有访问到环境模型的情况下,我们必须探索以收集新信息或改进当前信息。

评估反馈
但是,如果不是评估性的,那它是什么呢?
评估反馈的对立面是监督反馈。在分类问题中,你的模型接收监督;也就是说,在学习过程中,你的模型会为提供的每个样本提供正确的标签。没有猜测的空间。如果你的模型犯了错误,正确的答案会立即提供。多么美好的生活啊!

分类是“监督的”
将正确答案提供给学习算法的事实使得监督反馈比评估反馈更容易处理。这是监督学习问题与评估反馈问题(如多臂老丨虎丨机、表格强化学习和深度强化学习)之间的一个明显区别。
多臂老丨虎丨机问题可能不需要处理顺序反馈,但它们确实从评估反馈中学习。这是多臂老丨虎丨机问题要解决的问题的核心。在评估反馈下,代理必须平衡探索和利用的需求。如果反馈既是评估性的又是顺序的,那么挑战就更大了。算法必须同时平衡短期和长期目标以及信息的收集和利用。表格强化学习和深度强化学习代理都从同时是顺序性和评估性的反馈中学习。

老丨虎丨机处理评估反馈
深度强化学习代理处理采样反馈
深度强化学习与表格强化学习区别在于问题的复杂性。在深度强化学习中,智能体不太可能全面地采样所有可能的反馈。智能体需要使用收集到的反馈进行推广,并基于这种推广做出明智的决策。
想想看。你不能期望从生活中得到全面反馈。你不能同时成为一名医生、律师和工程师,至少如果你想在任何一个领域都做得很好,你不能。你必须利用你早期积累的经验来为你的未来做出更明智的决策。这是基本的。你在高中数学好吗?很好,那么,就追求一个与数学相关的学位。你在艺术方面更擅长?那么,就选择那条道路。推广可以帮助你通过帮助你找到模式、做出假设和连接帮助你达到最佳自我的点来缩小你前进的道路。
顺便说一下,监督学习处理的是样本反馈。确实,监督学习的核心挑战是从样本反馈中学习:能够推广到新的样本,这是多臂老丨虎丨机和表格强化学习问题都无法做到的。

样本反馈
但是,如果不是样本化的,那会是什么呢?
样本反馈的对立面是全面反馈。要全面地采样环境意味着智能体可以访问所有可能的样本。例如,表格强化学习和老丨虎丨机智能体只需要采样足够长的时间来收集所有必要的信息以实现最佳性能。收集全面反馈也是表格强化学习中存在最优收敛保证的原因。在有限的状态和动作空间的小网格世界中,常见的假设,如“无限数据”或“无限频繁地采样每个状态-动作对”,是合理的假设。

顺序性、评估性和全面反馈
我们之前还没有处理过这个维度。到目前为止,在这本书中,我们调查了表格强化学习问题。表格强化学习从评估性、顺序性和全面反馈中学习。但是,当我们遇到我们无法假设智能体将全面采样环境的更复杂问题时会发生什么?如果状态空间是高维的,比如有 10¹⁷⁰个状态的围棋棋盘呢?关于以 60 Hz 运行(255³)²¹⁰ × ¹⁶⁰的 Atari 游戏呢?如果环境状态空间有连续变量,比如表示关节角度的机械臂呢?关于具有高维和连续状态或甚至高维和连续动作的问题呢?这些复杂问题是深度强化学习领域存在的原因。
强化学习函数逼近简介
理解为什么我们首先使用函数逼近来解决强化学习问题至关重要。人们常常因为词汇而迷失方向,并因为炒作而选择解决方案。你知道,如果你听到“深度学习”,你会比听到“非线性函数逼近”更兴奋,尽管它们是相同的。这是人的本性。这发生在我身上,我相信它发生在许多人身上。但我们的目标是去除冗余,简化我们的思考。
在本节中,我提供了使用函数逼近来解决强化学习问题的一般动机。也许比 RL 整体更具体一些,但基本的动机适用于所有形式的 DRL。
强化学习问题可以具有高维的状态空间和动作空间
表格式强化学习的主要缺点是,在复杂问题中使用表格来表示价值函数已不再实用。环境可以具有高维的状态空间,这意味着构成单个状态变量的数量非常庞大。例如,上面提到的 Atari 游戏是高维的,因为它们有 210×160 像素和三个颜色通道。无论我们谈论维度时这些像素可以取什么值,我们指的是构成单个状态变量的数量。

高维状态空间
强化学习问题可以具有连续的状态空间和动作空间
环境还可以有连续变量,这意味着一个变量可以取无限多个值。为了澄清,状态空间和动作空间可以是高维的,包含离散变量,也可以是低维的,包含连续变量,等等。
即使变量不是连续的,因此不是无限大的,它们仍然可以取大量的值,使得没有函数逼近就无法学习。例如,Atari 的情况就是这样,每个图像像素可以取 256 个值(0-255 的整数值。)那里有一个有限的状态空间,但足够大,以至于需要函数逼近才能进行任何学习。
但是,有时,即使是低维状态空间也可以是无限大的状态空间。例如,想象一个只有机器人的 x、y、z 坐标组成状态空间的问题。当然,三个变量的状态空间是一个相当低维的状态空间环境,但如果任何一个变量以连续形式提供,也就是说,该变量可以是无限小的精度呢?比如说,它可以是 1.56,或者 1.5683,或者 1.5683256,等等。那么,你如何制作一个考虑所有这些值的表格呢?是的,你可以对状态空间进行离散化,但让我节省你的时间,直接说:你需要函数逼近。

连续状态空间
| 一个具体例子小车-杆环境 |
|---|
| | 小车-杆环境是强化学习中的一个经典案例。状态空间低维但连续,使其成为开发算法的绝佳环境;训练速度快,但仍有一定挑战性,函数逼近可以帮助!这是小车-杆环境其状态空间由四个变量组成:
-
小车在轨道上的位置(x 轴)范围从 -2.4 到 2.4
-
小车沿轨道(x 轴)的速度范围从 -inf 到 inf
-
杆的角度范围大约在 ~-40 度到 ~40 度之间
-
杆尖的速度范围从 -inf 到 inf
在每个状态下都有两个可用的动作:
-
Action 0 对小车施加 -1 的力(向左推)
-
Action 1 对小车施加 +1 的力(向右推)
如果你达到终端状态
-
杆的角度与垂直位置超过 12 度
-
小车中心距离轨道中心超过 2.4 个单位
-
播放次数达到 500 个时间步(关于这一点稍后讨论)
奖励函数是
- 每个时间步 +1
|
使用函数逼近有优势
我相信你明白了,在高维或连续状态空间的环境中,没有实际的理由不使用函数逼近。在早期章节中,我们讨论了规划和强化学习算法。所有这些方法都使用表格来表示值函数。
虽然价值迭代和 Q 学习在具有采样反馈的问题上无法解决问题,这使得它们不实用,但缺乏泛化使得它们效率低下。我的意思是,我们可以找到在具有连续变量状态的环境中使用表格的方法,但我们会为此付出代价。离散化值确实可以使表格成为可能,例如。但是,即使我们能够设计出一种使用表格并存储价值函数的方法,通过这样做,我们会失去泛化的优势。
例如,在购物车-杆环境中,函数逼近可以帮助我们的智能体学习 x 距离中的关系。智能体可能会发现,距离中心 2.35 个单位比距离中心 2.2 个单位更危险。我们知道 2.4 是 x 边界。这个使用泛化的额外理由不容忽视。价值函数通常具有智能体可以学习和利用的潜在关系。函数逼近器,如神经网络,可以发现这些潜在关系。
| 简化原因:使用函数逼近的理由 | |
|---|---|
| 我们使用函数逼近的动机不仅是为了解决其他方法无法解决的问题,而且是为了更有效地解决问题。 |
NFQ:基于价值的深度强化学习的第一次尝试
以下算法被称为 神经拟合 Q (NFQ) 迭代,它可能是第一个成功使用神经网络作为函数逼近来解决强化学习问题的算法之一。
在本章的剩余部分,我将讨论大多数基于价值的深度强化学习算法都具有的几个组件。我希望你能将其视为一个机会,决定我们可以使用的不同部分。例如,当我介绍使用 NFQ 的损失函数时,我会讨论一些替代方案。我的选择并不一定是算法最初引入时所做的选择。同样,当我选择优化方法,无论是均方根传播 (RMSprop) 还是自适应矩估计 (Adam) 时,我会给出我使用的原因,但更重要的是,我会给你提供背景信息,以便你可以根据自己的需要选择和决定。
我希望你能注意到,我的目标不仅仅是教你这个特定的算法,更重要的是,向你展示你可以尝试不同事物的不同地方。许多 RL 算法都有这种“即插即用”的感觉,所以请注意。
第一个决策点:选择一个要逼近的价值函数
使用神经网络逼近价值函数可以以许多不同的方式完成。首先,我们可以逼近许多不同的价值函数。
| 刷新我的记忆:价值函数 |
|---|
| | 你已经学习了以下价值函数:
-
状态价值函数 v(s)
-
动作价值函数 q(s,a)
-
动作优势函数 a(s,a)
你可能还记得,状态值函数 v(s),虽然对许多目的来说很有用,但仅凭它本身并不能解决控制问题。找到 v(s) 帮助你知道从状态 s 出发并使用策略 π 后可以获得多少期望的总折现奖励。但是,为了确定使用 V 函数采取哪个动作,你还需要环境的 MDP,这样你就可以进行一步前瞻,并在选择每个动作后考虑所有可能的后继状态。你可能也记得,动作值函数 (s,a) 允许我们解决控制问题,所以它更像是我们需要解决小车-杆环境的问题:在小车-杆环境中,我们希望通过控制小车来学习所有状态的动作值,以平衡杆。如果我们有了状态-动作对的值,我们就可以区分那些会导致我们获得信息(在探索性动作的情况下)或最大化期望回报(在贪婪动作的情况下)的动作。我还想让你注意到,我们想要估计的是最优动作值函数,而不仅仅是动作值函数。然而,正如我们在广义策略迭代模式中学到的,我们可以使用 ε-贪婪策略进行在线学习并直接估计其值,或者我们可以进行离线学习,并始终估计相对于当前估计的贪婪策略,这随后成为最优策略。最后,我们还学习了动作优势函数 a(s,a),它可以帮助我们区分不同动作的值,并让我们很容易地看到动作的平均优势。|
我们将在接下来的几章中研究如何使用 v(s) 和 a(s) 函数。现在,让我们确定估计动作值函数 (s,a),就像在 Q-learning 中一样。我们将近似动作值函数的估计称为 Q(s,a; θ),这意味着 Q 估计由 θ 参数化,即神经网络的权重,状态 s 和动作 a。
第二个决策点:选择神经网络架构
我们决定学习近似动作值函数 Q(s,a; θ)。尽管我建议该函数应该由 θ、s 和 a 参数化,但这并不一定非得如此。接下来我们要讨论的是神经网络架构。

状态-动作-值-输出架构
当我们实现 Q-learning 代理时,你注意到了包含动作值函数的矩阵是如何由状态和动作对索引的。一个直接的神经网络架构是输入状态(小车-杆环境中的四个状态变量),以及要评估的动作。输出将是代表该状态-动作对的 Q 值的一个节点。

状态-值-输出架构
这个架构对于小车-杆环境来说效果很好。但是,一个更高效的架构只将状态(对于小车-杆环境是四个)输入到神经网络,并输出该状态下所有动作的 Q 值(对于小车-杆环境是两个)。当使用 epsilon-greedy 或 softmax 等探索策略时,这显然是有优势的,因为只需要进行一次前向传递就能得到任何给定状态下所有动作的值,这在动作数量众多的环境中尤其如此。
对于我们的 NFQ 实现,我们使用的是 状态-值-输出架构:也就是说,对于小车-杆环境,有四个输入节点和两个输出节点。
| 我会说 Python 全连接 Q 函数(状态-值-输出) | |
|---|---|
class FCQ(nn.Module):
def __init__(self,
input_dim,
output_dim,
hidden_dims=(32,32),
activation_fc=F.relu):
super(FCQ, self).__init__()
self.activation_fc = activation_fc
self.input_layer = nn.Linear(input_dim,
hidden_dims[0]) ①
self.hidden_layers = nn.ModuleList()
for i in range(len(hidden_dims)-1): ②
hidden_layer = nn.Linear(
hidden_dims[i], hidden_dims[i+1])
self.hidden_layers.append(hidden_layer)
self.output_layer = nn.Linear( ③
hidden_dims[-1], output_dim)
def forward(self, state):
x = state
if not isinstance(x, torch.Tensor): ④
x = torch.tensor(x,
device=self.device,
dtype=torch.float32)
x = x.unsqueeze(0)
x = self.activation_fc(self.input_layer(x)) ⑤
for hidden_layer in self.hidden_layers: ⑥
x = self.activation_fc(hidden_layer(x))
x = self.output_layer(x) ⑦
return x
① 这里你只是在定义输入层。看看我们如何接收 input_dim 并输出 hidden_dims 向量的第一个元素。② 我们接着创建隐藏层。注意这个类是多么灵活,它允许你改变层数和每层的单元数。将不同的元组(例如(64,32,16))传递给 hidden_dims 变量,它将创建一个具有三个隐藏层(64、32 和 16 个单元)的网络。③ 然后我们将最后一个隐藏层连接到输出层。④ 在前向函数中,我们首先接收原始状态并将其转换为张量。⑤ 我们将其通过输入层,然后通过激活函数。⑥ 然后对所有的隐藏层做同样的处理。⑦ 最后,对于输出层,注意我们没有对输出应用激活函数,而是直接返回它。|
第三决策点:选择要优化的内容
让我们假设一下,小车-杆环境是一个监督学习问题。假设你有一个包含状态作为输入和值函数作为标签的数据集。你希望标签中包含哪个值函数?
| 显示我数学理想目标 | |
|---|---|
![]() |
当然,学习最优动作值函数的理想标签是对应的状态-动作输入对的优化 Q 值(注意,小写 q 指的是真实值;大写通常用来表示估计)。这正是您所知道的最优动作值函数 q**(s,a*) 所表示的内容。
如果我们能访问最优动作值函数,我们会使用它,但如果我们能采样最优动作值函数,我们就可以最小化近似动作值函数和最优动作值函数之间的损失,这样就可以了。
我们追求的是最优动作值函数。
| 刷新我的记忆最优动作值函数 | |
|---|---|
![]() |
但为什么这是一个不可能的梦想呢?好吧,明显的是我们没有最优的动作值函数 (s,a),但更糟糕的是,我们甚至无法采样这些最优 Q 值,因为我们也没有最优策略。
幸运的是,我们可以使用在广义策略迭代中学习到的相同原则,在这些原则中我们交替进行策略评估和策略改进过程以找到好的策略。但要知道,因为我们使用的是非线性函数逼近,所以不再存在收敛保证。这是“深度”世界的狂野西部。
对于我们的 NFQ 实现,我们正是这样做的。我们从随机初始化的动作值函数(和隐含策略)开始。然后,我们通过从策略中采样动作来评估策略,就像我们在第五章中学到的那样。然后,我们使用 epsilon-greedy 等探索策略来改进它,就像我们在第四章中学到的那样。最后,继续迭代直到达到期望的性能,就像我们在第六章和第七章中学到的那样。
| 简化它我们无法使用理想的目标 | |
|---|---|
| 我们无法使用理想的目标,因为我们无法访问最优的动作值函数,我们甚至没有最优策略可以从中采样。相反,我们必须在评估策略(通过从中采样动作)和改进策略(使用探索策略,如 epsilon-greedy)之间交替。就像你在第六章中学到的,在广义策略迭代模式中。 |
第四个决策点:选择政策评估的目标
我们有多种方法可以评估一个策略。更具体地说,我们可以使用不同的 目标 来估计策略π的动作值函数。你学到的核心目标是蒙特卡洛(MC)目标、时序差分(TD)目标、n-步目标和 lambda 目标。

MC、TD、n-步和 lambda 目标
我们可以使用这些目标中的任何一个并得到可靠的结果,但这次在我们的 NFQ 实现中,我们保持简单,使用TD目标进行实验。
你记得 TD 目标可以是策略内或策略外,这取决于你如何自举目标。自举 TD 目标的主要两种方式是使用代理在最终状态采取的动作的动作值函数,或者,使用下一个状态估计值最高的动作的价值。
在文献中,这个目标的策略内版本通常被称为 SARSA 目标,而策略外版本被称为 Q-learning 目标。
| 展示数学方法在策略和离策略 TD 目标 | |
|---|---|
![]() |
在我们的 NFQ 实现中,我们使用与 Q 学习算法中相同的离策略 TD 目标。在这个阶段,为了得到一个目标函数,我们需要将作为理想目标方程的最优动作值函数(s,a)替换为 Q 学习目标。
| 展示数学 Q 学习目标,一个离策略 TD 目标 | |
|---|---|
![]() |
|
| 我会说 PythonQ 学习目标 | |
q_sp = self.online_model(next_states).detach() ①②
max_a_q_sp = q_sp.max(1)[0].unsqueeze(1) ③④
max_a_q_sp =* (1 - is_terminals) ⑤⑥
target_q_s = rewards + self.gamma * max_a_q_sp ⑦
q_sa = self.online_model(states).gather(1, actions) ⑧
① 首先,我们获取在 s'(下一个状态)处的 Q 函数的值。next_states 中的 s 表示这是一个 next_state 的批次。②这里的'detach'很重要。我们不应该通过这个值进行传播。我们只是在计算目标。③然后,我们获取下一个状态的最大值 max_a。④unsqueeze 向向量添加一个维度,这样后续的操作才能在正确的元素上工作。⑤一个重要但常被忽视的步骤是确保终端状态被归零。⑥注意 is_terminals 是 is_terminal 标志的批次,这些标志仅指示下一个状态是否是终端状态。⑦我们现在计算目标。⑧最后,我们获取当前对 Q(s,a)的估计。在这个时候,我们准备好创建我们的损失函数。|
我想提醒你两个问题,不幸的是,我在使用 TD 目标的 DRL 算法实现中经常看到。|
首先,你需要确保你只反向传播通过预测值。让我解释一下。你知道在监督学习中,你有来自学习模型的预测值和通常提前提供的常量真实值。在强化学习中,通常“真实值”依赖于预测值本身:它们来自模型。
例如,当你形成一个 TD 目标时,你使用一个奖励,这是一个常量,以及下一个状态的折扣值,这个值来自模型。请注意,这个值也不是一个真实值,这将会在下一章中解决各种问题。但我也想让你现在注意到的是,预测值来自神经网络。你必须将这个预测值变成一个常量。在 PyTorch 中,你只能通过调用detach方法来做这件事。请查看前两个框并理解这些要点。它们对于 DRL 算法的可靠实现至关重要。
在我们继续之前,我想提出两个问题,不幸的是,我在使用 TD 目标算法的 DRL 实现中经常看到。这两个问题是关于终端状态的处理方式,当使用 OpenAI Gym 环境时。OpenAI Gym 步骤,用于与环境交互,在每一步后返回一个方便的标志,指示代理是否刚刚到达终端状态。这个标志帮助代理将终端状态的价值强制设为零,正如你从第二章中记得的,这是保持价值函数不发散的要求。你知道死后生命的价值为零。

这个状态的价值是多少?
难题在于,一些 OpenAI Gym 环境,例如 cart-pole,有一个包装代码,在经过一定的时间步数后会人为地终止一个回合。在 CartPole-v0 中,时间步数限制是 200,而在 CartPole-v1 中是 500。这个包装代码有助于防止智能体花费太长时间完成一个回合,这可能是有用的,但它也可能给你带来麻烦。想想看:你认为在时间步数 500 时,让杆子竖直起来的价值是多少?我的意思是,如果杆子竖直,并且每一步都得到 +1,那么竖直的价值就是无限的。然而,由于在时间步数 500 时你的智能体超时,并且传递给智能体的终端标志,如果你不小心,你将基于零进行引导。这是不好的。我无法强调这一点。你可以用几种方法来处理这个问题,这里有两种常见的方法。与其基于零进行引导,不如基于网络预测的下一个状态值进行引导,如果你(1)达到了环境的最大时间步数限制,或者(2)在信息字典中找到了“TimeLimit.truncated”这个关键字。让我给你展示第二种方法。
| 我说 PythonProperly handling terminal states | |
|---|---|
new_state, reward, is_terminal, info = env.step(action) ①
is_truncated = 'TimeLimit.truncated' in info and \ ②
info['TimeLimit.truncated']
is_failure = is_terminal and not is_truncated ③
experience = (state, action, reward, new_state, float(is_failure)) ④
① 我们像往常一样收集一个经验元组。② 然后检查信息字典中的“TimeLimit.truncated”关键字。③ 失败的定义如下。④ 最后,如果回合以失败结束,我们添加终端标志。如果不是失败,我们希望基于新状态的价值进行引导。|
第五个决策点:选择探索策略
我们还需要决定为我们的广义策略迭代需要使用哪种策略改进步骤。你知道这一点来自第六章和第七章,其中我们交替使用策略评估方法,如 MC 或 TD,以及考虑探索的策略改进方法,如衰减的 epsilon-greedy。
在第四章中,我们调查了许多不同的方法来平衡探索-利用权衡,几乎所有这些技术都可以很好地工作。但是,为了保持简单,我们将在我们的 NFQ 实现中使用 epsilon-greedy 策略。
但是,我想强调的是,我们在这里训练的是一个离线策略学习算法的事实所具有的含义。这意味着有两个策略:一个生成行为的策略,在这个案例中是一个 epsilon-greedy 策略,以及我们正在学习的策略,即贪婪策略(最终是最佳策略)。
你在第六章中学习的离线学习算法的一个有趣的事实是,生成行为的策略可以是任何东西。也就是说,只要它有广泛的支持,这意味着它必须确保对所有状态-动作对的足够探索。在我们的 NFQ 实现中,我在训练期间使用了一个 epsilon-greedy 策略,该策略在 50% 的时间内随机选择动作。然而,在评估智能体时,我使用的是与学习到的动作值函数相关的贪婪动作。
| 我会说 PythonEpsilon-greedy 探索策略 | |
|---|---|
class EGreedyStrategy():
<...>
def select_action(self, model, state):
with torch.no_grad():
q_values = model(state).cpu().detach() ①
q_values = q_values.data.numpy().squeeze() ②
if np.random.rand() > self.epsilon: ③
action = np.argmax(q_values)
else:
action = np.random.randint(len(q_values)) ④
<...> ⑤
return action
① epsilon-greedy 策略的 select_action 函数首先提取状态 s 的 Q 值。② 我将这些值调整为“NumPy 友好”并移除一个额外的维度。③ 然后,获取一个随机数,如果大于 epsilon,则贪婪地行动。④ 否则,在动作数量中随机行动。⑤ 注意:我总是查询模型来计算统计数据。但,如果你的目标是性能,那么你不应该这样做!
第六个决策点:选择损失函数
损失函数是我们神经网络预测好坏的度量。在监督学习中,解释损失函数更为直接:给定一批预测值及其对应的真实值,损失函数计算一个距离分数,表示网络在这批数据上的表现如何。
有许多不同的方法来计算这个距离分数,但我在这章中继续保持简单,并使用最常见的一种:MSE(均方误差,或 L2 损失)。不过,让我重申一下,与监督学习相比,强化学习中的一个挑战是,我们的“真实值”使用了来自网络的预测。
均方误差(MSE)或 L2 损失定义为预测值和真实值之间平均平方差的度量;在我们的情况下,预测值是直接来自神经网络的动作值函数的预测值:一切正常。但真实值是,是的,TD目标,这取决于来自网络的下一个状态的价值。

动作值函数的循环依赖性
正如你可能所想的,这种循环依赖性是坏的。它表现不佳,因为它不尊重监督学习问题中做出的几个假设。我们将在本章后面讨论这些假设,以及我们在下一章违反这些假设时出现的问题。
第七个决策点:选择优化方法
在给定一些假设的情况下,梯度下降是一种稳定的优化方法:数据必须是独立同分布的(IID),目标必须是平稳的。然而,在强化学习中,我们无法确保这些假设中的任何一个成立,因此选择一个鲁棒的优化方法来最小化损失函数,通常可以在收敛和发散之间产生差异。
如果你将损失函数想象成一个有山谷、山峰和平地的景观,那么优化方法就是寻找感兴趣区域的徒步策略,通常是在这个景观中的最低点或最高点。
监督学习中的一个经典优化方法被称为批量梯度下降。批量梯度下降算法一次性取整个数据集,计算给定数据集的梯度,并逐步朝这个梯度移动。然后,它重复这个循环直到收敛。在景观类比中,这个梯度代表一个信号,告诉我们需要移动的方向。批量梯度下降不是研究人员的首选,因为它不实用,无法一次性处理大量数据集。当你有一个包含数百万样本的大量数据集时,批量梯度下降太慢,不实用。此外,在强化学习中,我们甚至事先没有数据集,所以批量梯度下降也不是我们目的的实用方法。

批量梯度下降
一种能够处理较小数据批次的优化方法称为小批量梯度下降。在小批量梯度下降中,我们一次只使用数据的一部分。我们处理一个小批量的样本以找到其损失,然后反向传播来计算这个损失的梯度,然后调整网络的权重,使网络更好地预测这个小批量的值。使用小批量梯度下降,你可以控制小批量的大小,这允许处理大型数据集。

小批量梯度下降
在一个极端情况下,你可以将你的小批量大小设置为数据集的大小,这样你又会回到批量梯度下降。在另一个极端情况下,你可以将小批量大小设置为每步一个样本。在这种情况下,你正在使用一种称为随机梯度下降的算法。

随机梯度下降
批次越大,优化方法的步骤方差就越低。但是,如果使用过大的批次,学习速度会显著减慢。在实践中,这两种极端情况都太慢了。因此,常见的小批量大小范围在 32 到 1024 之间。

小批量梯度下降的 Z 字形模式
一种改进的梯度下降算法被称为带有动量的梯度下降,或简称动量。这种方法是一种小批量梯度下降算法,它更新网络权重的方式是朝着梯度的移动平均值的方向,而不是梯度本身。

小批量梯度下降与动量
使用动量的替代方法是称为均方根传播(RMSprop)。RMSprop 和动量都做同样的事情,即减少振荡并更直接地向目标移动,但它们以不同的方式做到这一点。
当动量沿着梯度移动平均的方向迈步时,RMSprop 则采取更安全的做法,按比例缩放梯度,比例是梯度幅度的移动平均。它通过按比例缩放梯度来减少振荡,比例是梯度平方的移动平均的平方根,或者更简单地说,是最近梯度幅度的平均值。
| 基于价值型深度强化学习的优化方法 | |
|---|---|
| 为了可视化 RMSprop,想象一下你的损失函数表面的陡峭度变化。如果梯度很高,例如在下坡时,表面变为一个平坦的谷地,其中梯度很小,那么梯度移动平均的幅度将高于最近的梯度;因此,步长的大小会减小,从而防止振荡或超调。如果梯度很小,例如在接近平坦的表面上,并且它们变为一个显著的梯度,就像在下坡时,那么梯度平均的幅度将很小,而新的梯度很大,因此增加步长并加快学习速度。 |
我想介绍的最后一种优化方法称为 自适应动量估计(Adam)。Adam 是 RMSprop 和动量的结合。Adam 方法沿着梯度速度的方向迈步,就像动量一样。但是,它按比例缩放更新,比例是梯度幅度的移动平均,就像 RMSprop 一样。这些特性使得 Adam 作为优化方法比 RMSprop 更具侵略性,但不如动量那么具侵略性。
在实践中,Adam 和 RMSprop 都是价值型深度强化学习方法的合理选择。我在接下来的章节中广泛使用了这两种方法。然而,我更倾向于使用 RMSprop 作为价值型方法,这一点你很快就会注意到。RMSprop 稳定且对超参数的敏感性较低,这在价值型深度强化学习中尤为重要。
| 0001 | 一点历史介绍 NFQ 算法 |
|---|---|
| NFQ 由 Martin Riedmiller 在 2005 年发表的一篇名为“Neural Fitted Q Iteration − First Experiences with a Data Efficient Neural Reinforcement Learning Method”的论文中提出。在担任欧洲多所大学教授 13 年后,Martin 在 Google DeepMind 找到了一份研究科学家的职位。 | |
| 详细介绍完整的神经拟合 Q 迭代(NFQ)算法 |
| | 目前,我们已经做出了以下选择:
-
近似动作值函数 Q(s,a; θ)。
-
使用状态-值输出架构(节点:4, 512, 128, 2)。
-
优化动作值函数以近似最优动作值函数 q**(s,a*)。
-
使用离策略 TD 目标(r + γmax_a’Q(s’,a’; θ*)来评估策略。
-
使用 ε-贪婪策略(ε 设置为 0.5)来改进策略。
-
使用均方误差(MSE)作为我们的损失函数。
-
使用 RMSprop 作为我们的优化器,学习率为 0.0005。
NFQ 有三个主要步骤:
-
收集 E 个经验:(s, a, r, s’,d) 元组。我们使用 1024 个样本。
-
计算离策略 TD 目标:r + γmax_a’Q(s’,a’; θ*)。
-
使用 MSE 和 RMSprop 来拟合动作值函数 Q(s,a; θ)。
此算法在返回到步骤 1 之前重复步骤 2 和 3 K 次数。这就是它被拟合的原因:嵌套循环。我们将使用 40 次拟合步骤 K。
NFQ |
| 计数器:NFQ 通过了小车-杆环境 | |
|---|---|
虽然 NFQ 远非最先进的基于价值的深度强化学习方法,但在一个相对简单的环境中,如小车-杆,NFQ 表现出相当不错的性能。![]() |
可能(确实)出错的事情
我们算法有两个问题。首先,因为我们使用了一个强大的函数逼近器,我们可以泛化到状态-动作对,这是非常好的,但也意味着神经网络会一次性调整所有相似状态的价值。
现在,考虑一下这个问题:我们的目标值取决于下一个状态的价值,我们可以安全地假设这些状态与我们最初调整价值的状态相似。换句话说,我们正在为我们的学习更新创建一个非平稳的目标。当我们更新近似 Q 函数的权重时,目标也会移动,并使我们的最新更新过时。因此,训练很快变得不稳定。

非平稳目标
其次,在 NFQ 中,我们批量处理了在线收集的 1024 个经验样本,并从该小批量更新网络。正如你可以想象的那样,这些样本是相关的,因为大多数这些样本来自相同的轨迹和政策。这意味着网络从相似样本的小批量中学习,后来使用的是也内部相关的不同小批量,但可能与先前的小批量不同,主要是在收集样本的政策不同或较旧时。
所有这些都意味着我们没有保持 IID 假设,这是一个问题,因为优化方法假设他们用于训练的数据样本是独立且同分布的。但我们在几乎完全相反的情况下进行训练:我们的分布上的样本不是独立的,因为新状态 s 的结果取决于我们的当前状态 s。
此外,我们的样本并非同分布,因为生成数据的底层过程,即我们的策略,是随时间变化的。这意味着我们没有固定的数据分布。相反,负责生成数据的策略在定期变化,并有望得到改进。每次我们的策略发生变化时,我们都会获得新的、可能不同的经验。优化方法允许我们在一定程度上放宽独立同分布的假设,但强化学习问题一直存在,因此我们也需要对此采取一些措施。

与时间相关的数据
在下一章中,我们将探讨缓解这两个问题的方法。我们首先通过改进 NFQ 算法,即有争议地开启了深度强化学习革命的 DQN 算法,来提升 NFQ。随后,我们将探讨多年来对原始 DQN 算法提出的许多改进。在下一章中,我们还将探讨双 DQN,然后在第十章中,我们将探讨对抗 DQN 和 PER。
摘要
在本章中,我们概述了采样反馈如何与顺序反馈和评估反馈相互作用。我们在介绍一个简单的深度强化学习代理时这样做,该代理近似 Q 函数,在之前的章节中,我们会用表格形式或查找表来表示。本章是关于基于价值的深度强化学习方法的介绍。
你了解到高维和连续状态空间和动作空间之间的区别。前者表示组成单个状态的大量值;后者暗示至少有一个变量可以取无限多个值。你了解到决策问题可以是高维和连续变量,这使得使用非线性函数近似变得有趣。
你了解到函数近似不仅对估计只有少量样本的值的期望值有益,而且对学习状态和动作维度中的潜在关系也有益。通过拥有一个好的模型,我们可以估计我们从未收到样本的值,并利用所有经验。
你对构建深度强化学习代理时常用的不同组件有了深入的了解。你了解到你可以近似不同类型的值函数,从状态值函数 v(s) 到动作值 q(s, a)。而且,你可以使用不同的神经网络架构来近似这些值函数;我们探讨了状态-动作对输入,值输出,到更有效的状态输入,值输出。你了解到我们可以使用与 Q 学习相同的目标,使用 TD 目标进行离策略控制。而且,你知道有许许多多的目标可以用来训练你的网络。你调查了探索策略、损失函数和优化方法。你了解到深度强化学习代理容易受到我们选择的损失和优化方法的影响。你了解到 RMSprop 和 Adam 是优化方法的稳定选项。
你学会了将这些组件组合成一个名为神经拟合 Q 迭代的算法。你了解了基于值的方法在深度强化学习中常见的问题。你学习了独立同分布假设和目标状态的稳定性。你了解到如果不注意这两个问题,可能会陷入麻烦。
到现在为止,
-
理解从序列性、评估性和抽样反馈中学习是什么
-
可以解决具有连续状态空间的强化学习问题
-
了解基于值的 DRL 方法中的组件和问题
| 可分享的工作:自己动手并分享你的发现 |
|---|
| | 这里有一些想法,如何将你学到的知识提升到下一个层次。如果你愿意,与世界分享你的结果,并确保查看其他人做了什么。这是一个双赢的局面,希望你能充分利用它。
-
#gdrl_ch08_tf01: 在表格式强化学习之后,在深度强化学习之前,有一些事情需要探索。使用这个标签,探索并分享关于状态离散化和瓦片编码技术的结果。这些是什么?还有其他我们应该了解的技术吗?
-
#gdrl_ch08_tf02: 我还希望你探索的是使用线性函数逼近,而不是深度神经网络。你能告诉我们其他函数逼近技术如何比较吗?哪些技术显示出有希望的结果?
-
#gdrl_ch08_tf03: 在本章中,我介绍了梯度下降作为我们在本书剩余部分使用的优化方法。然而,梯度下降并不是优化神经网络的唯一方法;你知道吗?无论如何,你应该去探索其他优化神经网络的方法,从黑盒优化方法,如遗传算法,到不那么流行的其他方法。分享你的发现,创建一个包含示例的笔记本,并分享你的结果。
-
#gdrl_ch08_tf04: 我以一个更好的方法开始了这一章,用于使用函数逼近进行 Q 学习。同样重要的是要知道一个更好的方法,还要有一个实现最简单方法但没有成功的实现。对 Q 学习进行最小修改,使其与神经网络一起工作:即,像在第六章中学到的,使用在线经验进行 Q 学习。测试并分享你的结果。
-
#gdrl_ch08_tf05: 在每一章中,我都使用最后的标签作为一个总标签。你可以自由使用这个标签来讨论与本章相关的任何其他工作。没有比你自己创造的任务更令人兴奋的作业了。确保分享你设定要调查的内容和你的结果。
用你的发现写一条推文,@提及我 @mimoralea(我会转发),并使用列表中的特定标签来帮助感兴趣的人找到你的结果。没有对错之分;你分享你的发现并检查他人的发现。利用这个机会社交,做出贡献,让自己更受关注!我们正在等待你的加入!以下是一条推文示例:“嘿,@mimoralea。我创建了一篇博客文章,列出了学习深度强化学习的资源列表。查看它在这里<链接>。#gdrl_ch01_tf01”我会确保转发并帮助他人找到你的作品。|
9 更稳定的基于价值的方法
在本章中
-
你将通过使它们更稳定来改进你在上一章中学到的方法,从而减少它们发散的可能性。
-
你将探索高级基于价值的深度强化学习方法,以及使基于价值的方法变得更好的许多组成部分。
-
你将以更少的样本数量解决 cart-pole 环境,并得到更可靠和一致的结果。
让你的步伐缓慢而稳健,以免跌倒。
——德川家康 日本德川幕府的创始人、第一任将军,也是日本三位统一者之一
在上一章中,你学习了基于价值的深度强化学习。NFQ,我们开发的算法,是解决基于价值的方法面临的最常见问题的简单解决方案:首先,RL 中的数据不是独立同分布的问题。这可能是完全相反的。经验依赖于生成它们的策略。而且,由于策略在整个训练过程中发生变化,它们不是同分布的。其次,我们使用的目标也不是平稳的。优化方法需要固定的目标才能有稳健的性能。在监督学习中,这一点很容易看出。我们有一个带有预制标签的常量数据集,我们的优化方法使用这些固定目标来随机近似潜在的数据生成函数。另一方面,在 RL 中,目标如TD目标使用奖励和从着陆状态的折现预测回报作为目标。但是,这个预测回报来自我们正在优化的网络,每次执行优化步骤时都会发生变化。这个问题创建了一个移动的目标,在训练过程中产生了不稳定性。
NFQ 解决这些问题的方法是通过批处理。通过增长一个批次,我们有同时优化多个样本的机会。批次越大,收集到的经验样本集的多样性就越多。这在一定程度上解决了独立同分布的假设问题。NFQ 通过在多个连续优化步骤中使用相同的迷你批次来解决目标要求的平稳性。记住,在 NFQ 中,每个 E 个片段,我们“拟合”神经网络到相同的迷你批次K次。这个K允许优化方法更稳定地向目标移动。收集一个批次并对模型进行多次迭代拟合,类似于我们训练监督学习方法的步骤,其中我们收集数据集并进行多次 epoch 的训练。
NFQ 做得还不错,但我们能做得更好。既然我们知道问题所在,我们就可以使用更好的技术来解决它们。在本章中,我们探讨了不仅解决这些问题,还解决其他问题的算法,这些问题是你在学习如何使基于价值的方法更稳定时遇到的。
DQN:使强化学习更像监督学习
本章讨论的第一个算法被称为深度 Q 网络(DQN)。DQN 是最受欢迎的 DRL 算法之一,因为它开启了一系列研究创新,标志着强化学习的历史。DQN 首次在 Atari 基准测试中实现了超人类水平的表现,其中智能体从仅图像的原始像素数据中学习。
几年来,针对 DQN 提出了许多改进。尽管如今原始形式的 DQN 不再是首选算法,但随着这些改进(其中许多你将在本书中学到),该算法仍然在表现最佳的 DRL 智能体中占有一席之地。
基于价值的深度强化学习中的常见问题
我们必须明确并理解在基于价值的深度强化学习中持续出现的前两个最常见问题:违反独立同分布假设和目标的不变性。
在监督学习中,我们事先获得完整的数据集。我们对其进行预处理,打乱顺序,然后将其分成训练集。这个过程中的一个关键步骤是数据集的打乱。通过这样做,我们允许我们的优化方法避免发展过度拟合偏差;减少训练过程的方差;加快收敛速度;并且总体上学习一个更通用的底层数据生成过程的表示。不幸的是,在强化学习中,数据通常是在线收集的;因此,在时间步 t+1 生成的经验样本与在时间步 t 生成的经验样本相关联。此外,由于策略是为了改进,它也会改变底层的数据生成过程,这意味着新数据是局部相关的,并且分布不均匀。
| 简化数据不是独立同分布(IID)的 | |
|---|---|
| 数据的假设。优化方法是在假设我们训练数据集中的样本是独立同分布的情况下开发的。然而,我们知道我们的样本并不是独立的,而是来自一个序列,一个时间序列,一个轨迹。时间步 t+1 的样本依赖于时间步 t 的样本。样本是相关的,我们无法阻止这种情况发生;这是在线学习的自然后果。但样本也不是同分布的,因为它们依赖于生成动作的策略。我们知道策略是随时间变化的,对我们来说这是好事。我们希望策略得到改进。但这也意味着随着我们不断改进,样本的分布(访问过的状态-动作对)也会发生变化。 |
此外,在监督学习中,用于训练的目标是在你的数据集上的固定值;在整个训练过程中都是固定的。在强化学习一般情况中,尤其是在在线学习的极端情况下,目标随着网络的每次训练步骤而移动。在每次训练更新步骤中,我们优化近似值函数,因此改变函数的形状,可能是整个值函数的形状。改变值函数意味着目标值也会改变,这反过来又意味着所使用的目标不再有效。因为目标来自网络,甚至在我们在使用它们之前,我们可以假设目标至少是无效或有偏的。
| 简化目标非平稳性 | |
|---|---|
| 目标非平稳性问题描述。这些是我们用来训练网络的目标,但这些目标是使用网络本身计算得出的。因此,函数随着每次更新而改变,进而改变目标。 |
在 NFQ 中,我们通过使用批量处理并将网络拟合到一个小型固定数据集进行多次迭代来减轻这个问题。在 NFQ 中,我们收集一个小数据集,计算目标,并在收集更多样本之前优化网络几次。通过在大量样本上这样做,神经网络更新的变化由函数的许多点组成,从而使得变化更加稳定。
DQN 是一个解决如何使强化学习看起来更像监督学习问题的算法?考虑这个问题,并思考你会对数据进行哪些调整以使其看起来是独立同分布的,并且目标值是固定的。
使用目标网络
使目标值更加平稳的一个简单方法是有一个新的网络,我们可以固定它多次,并保留它来计算更平稳的目标。在 DQN 中,具有这种目的的网络被称为目标网络。

无目标网络 Q 函数优化

使用目标网络的 Q 函数逼近
通过使用目标网络来固定目标,我们通过人为地创建几个依次呈现给代理的小型监督学习问题来缓解“追逐自己的尾巴”的问题。我们的目标固定与我们的目标网络固定的时间一样长。这提高了我们收敛的机会,但不是到最优值,因为非线性函数逼近中不存在这样的东西,但通常收敛。但更重要的是,它大大减少了发散的机会,这在基于价值的深度强化学习方法中并不罕见。
| 展示数学目标网络梯度更新 | |
|---|---|
![]() |
重要的是要注意,在实践中,我们并没有两个“网络”,而是有两个神经网络的权重实例。我们使用相同的模型架构,并频繁更新目标网络的权重以匹配在线网络的权重,这是我们在每一步上优化的网络。“频繁”在这里的含义取决于问题,不幸的是。通常,我们会冻结这些目标网络权重 10 到 10,000 步,这再次取决于问题。(那是时间步,不是回合。请小心。)如果你使用卷积神经网络,例如用于学习 Atari 游戏的网络,那么 10,000 步的频率是正常的。但对于更直接的问题,如小车-杆环境,10-20 步更为合适。
通过使用目标网络,我们防止训练过程陷入螺旋,因为我们正在固定多个时间步的目标,从而允许在线网络权重在更新改变优化问题之前,一致地向目标移动,然后设置一个新的目标。通过使用目标网络,我们稳定了训练,但同时也减慢了学习速度,因为你不再基于最新的值进行训练;目标网络的冻结权重可能一次滞后多达 10,000 步。平衡稳定性和速度并调整这个超参数是至关重要的。
| 我会说 Python 使用目标网络和在线网络在 DQN 中的应用 | |
|---|---|
def optimize_model(self, experiences):
states, actions, rewards, \
next_states, is_terminals = experiences
batch_size = len(is_terminals)
q_sp = self.target_model(next_states).detach() ①
max_a_q_sp = q_sp.max(1)[0].unsqueeze(1) ②
max_a_q_sp *= (1 - is_terminals)
target_q_sa = rewards + self.gamma * max_a_q_sp ③
q_sa = self.online_model(states).gather(1, actions) ④
td_error = q_sa - target_q_sa ⑤
value_loss = td_error.pow(2).mul(0.5).mean()
self.value_optimizer.zero_grad()
value_loss.backward() ⑥
self.value_optimizer.step()
def interaction_step(self, state, env):
action = self.training_strategy.select_action(
self.online_model, state) ⑦
new_state, reward, is_terminal, _ = env.step(action)
<...>
return new_state, is_terminal
def update_network(self): ⑧
for target, online in zip(
self.target_model.parameters(),
self.online_model.parameters()):
target.data.copy_(online.data)
① 注意我们现在是如何查询目标网络来获取下一个状态的估计。② 我们获取这些值的最大值,并确保适当地处理终端状态。③ 最后,我们创建TD目标。④ 查询当前的“在线”估计。⑤ 使用这些值来创建误差。⑥ 计算损失,并优化在线网络。⑦ 注意我们是如何使用在线模型来选择动作的。⑧ 这就是目标网络(滞后网络)如何通过在线网络(最新网络)进行更新的。 |
使用更大的网络
另一种在一定程度上减轻非平稳性问题的方法,是使用更大的网络。具有更强大网络的微妙状态差异更有可能被检测到。更大的网络减少了状态-动作对的混叠;网络越强大,混叠越低;混叠越低,连续样本之间的相关性就越不明显。所有这些都可以使目标值和当前估计看起来更独立于彼此。
在这里,“别名”一词指的是两个状态可能看起来对神经网络来说是相同的(或者相当相似),但仍然可能需要不同的动作。当网络缺乏表示能力时,可能会发生状态别名。毕竟,神经网络试图找到相似之处以进行泛化;它们的任务是找到这些相似之处。但是,网络太小可能会导致泛化出错。网络可能会对简单、容易找到的模式产生固定。
使用目标网络的一个动机是它们允许你更容易地区分相关状态。使用一个更强大的网络也有助于你的网络学习细微的差异。
然而,一个更强大的神经网络需要更长的时间来训练。它不仅需要更多的数据(交互时间),还需要更多的计算(处理时间)。使用目标网络是减轻非平稳问题的更稳健的方法,但我希望你知道所有的技巧。了解你的代理(网络的规模、使用目标网络以及更新频率)的这两个属性如何相互作用并影响最终性能是很有利的。
| 简化目标 Ways to mitigate the fact that targets in reinforcement learning are non-stationary |
|---|
| | 允许我重申,为了减轻非平稳性问题,我们可以
-
创建一个目标网络,为我们提供一个暂时平稳的目标值。
-
创建足够大的网络,以便它们可以“看到”相似状态之间的微小差异(如那些时间上相关联的)。
目标网络效果很好,并且已经被证明多次有效。技术“更大的网络”更像是一种模糊的解决方案,而不是每次都经过科学验证的解决方案。请随意实验本章的笔记本。你会发现改变值和测试假设很容易。 |
使用经验回放
在我们的 NFQ 实验中,我们使用一个包含 1,024 个样本的小批量,并以此进行 40 次迭代训练,交替进行计算新目标和优化网络。这 1,024 个样本在时间上是相关的,因为它们大多数属于同一条轨迹,而购物车-杆子实验中的最大步数是 500 步。改进这一点的办法之一是使用一种称为经验回放的技术。经验回放包括一种数据结构,通常被称为回放缓冲区或回放内存,它可以存储多个步骤的经验样本(远多于 1,024 步),从而允许从广泛的过去经验中采样小批量。拥有回放缓冲区让智能体获得两个关键的好处。首先,训练过程可以使用更多样化的小批量进行更新。其次,智能体不再需要在多次迭代中使模型适应相同的小批量。适当地采样足够大的回放缓冲区会产生一个缓慢移动的目标,因此智能体现在可以以较低的风险在每个时间步进行采样和训练。
| 0001 | 一点历史介绍经验回放 |
|---|---|
| 经验回放是由 Long-Ji Lin 在一份题为“基于强化学习、规划和教学的自我改进反应性智能体,”的论文中引入的,无论你信不信,这篇论文发表于1992 年!没错,1992 年!再次强调,那正是神经网络被称为“连接主义”的时候...悲伤的时代!在从 CMU 获得博士学位后,林博士在多家公司担任了多个技术角色。目前,他是 Signifyd 的首席科学家,领导着一个致力于预测和防止在线欺诈的系统团队。 |
使用经验回放有多个好处。通过随机采样,我们增加了我们的神经网络更新具有低方差的概率。当我们使用 NFQ 中的批量时,该批量中的大多数样本都是相关的和相似的。使用相似样本进行更新会将变化集中在函数的有限区域内,这可能会过度强调更新的幅度。另一方面,如果我们从大量的缓冲区中均匀随机采样,那么我们的网络更新更有可能分布得更均匀,因此更能代表真实的价值函数。
使用回放缓冲区也给人一种我们的数据是独立同分布的印象,这样优化方法就稳定了。由于从多个轨迹甚至策略中同时采样,样本看起来是独立且同分布的。
通过存储经验和稍后均匀采样,我们使进入优化方法的数据看起来是独立且同分布的。在实践中,回放缓冲区需要相当大的容量才能发挥最佳效果,从 10,000 到 1,000,000 个经验样本,具体取决于问题。一旦达到最大容量,在插入新经验之前,你需要移除最老的经验。

带有重放缓冲区的 DQN
不幸的是,当与高维观察结果一起工作时,实现变得有点具有挑战性,因为实现不佳的重放缓冲区在高维环境中很快就会达到硬件内存限制。例如,在基于图像的环境中,例如,每个状态表示是四个最新图像帧的堆栈,这在 Atari 游戏中很常见,你可能在个人电脑上没有足够的内存来天真地存储 1,000,000 个经验样本。对于小车-杆环境,这并不是一个大问题。首先,我们不需要 1,000,000 个样本,我们使用一个大小为 50,000 的缓冲区。但更重要的是,状态由四个元素的向量表示,因此并没有太多的实现性能挑战。
| 展示数学重放缓冲区梯度更新 | |
|---|---|
![]() |
尽管如此,通过使用重放缓冲区,你的数据看起来比现实中更符合独立同分布(IID),而目标更稳定。通过从均匀采样的迷你批次中进行训练,你使得在线收集的强化学习经验看起来更像是一个具有独立同分布数据和固定目标的传统监督学习数据集。当然,随着你添加新的样本和丢弃旧的样本,数据仍在变化,但这些变化发生得较慢,因此神经网络和优化器几乎注意不到。
| 简化经验重放使数据看起来符合独立同分布,目标相对稳定 | |
|---|---|
| 解决数据不是独立同分布问题的最佳方案被称为经验重放。这项技术很简单,并且已经存在了几十年:当你的智能体在线收集经验元组 e[t]=(S[t],A[t],R[t+1],S[t+1]) 时,我们将它们插入到一个数据结构中,通常称为重放缓冲区 d,使得 d={e[1], e[2] , ... , e[M]}. m,重放缓冲区的大小,通常在 10,000 到 1,000,000 之间,这取决于问题。然后我们通过从缓冲区中采样的迷你批次来训练智能体,通常随机均匀地采样,使得每个样本被选中的概率相等。尽管如此,正如你将在下一章中学到的,你可能会以另一种分布进行采样。但请注意,这并不那么简单。我们将在下一章中讨论细节。 | |
| 我会说 Python 一个简单的重放缓冲区 | |
class ReplayBuffer():
def __init__(self,
m_size=50000, ①
batch_size=64):
self.ss_mem = np.empty(shape=(m_size), dtype=np.ndarray)
self.as_mem = np.empty(shape=(m_size), dtype=np.ndarray)
<...> ②
self.m_size, self.batch_size = m_size, batch_size ③
self._idx, self.size = 0, 0
def store(self, sample):
s, a, r, p, d = sample ④
self.ss_mem[self._idx] = s
self.as_mem[self._idx] = a
<...> ⑤
self._idx += 1 ⑥
self._idx = self._idx % self.m_size
self.size += 1 ⑦
self.size = min(self.size, self.m_size)
def sample(self, batch_size=None):
if batch_size == None: ⑧
batch_size = self.batch_size
idxs = np.random.choice(
self.size, batch_size, replace=False) ⑨
experiences = np.vstack(self.ss_mem[idxs]), \ ⑩
np.vstack(self.as_mem[idxs]), \
np.vstack(self.rs_mem[idxs]), \
np.vstack(self.ps_mem[idxs]), \
np.vstack(self.ds_mem[idxs])
return experiences ⑪
def __len__(self): ⑫
return self.size
① 这是一个简单的重放缓冲区,默认最大大小为 50,000,默认批处理大小为 64 个样本。② 我们初始化五个数组来存储状态、动作、奖励、下一个状态和完成标志。为了简洁起见,省略了部分内容。③ 我们初始化几个变量来进行存储和采样。④ 当我们存储一个新的样本时,我们首先展开样本变量,然后设置每个数组的元素为其相应的值。⑤ 再次省略了部分内容⑥ _idx 指向下一个要修改的索引,因此我们增加它,并确保在达到最大大小时(缓冲区的末尾)循环回 0。⑦ 每存储一个新的样本,大小也会增加,但不会循环回 0;它停止增长。⑧ 在 sample 函数中,我们首先确定批处理大小。如果没有传递其他内容,我们使用默认的 64。⑨ 从 0 到大小中采样 batch_size 个 id。⑩ 然后,使用采样 id 从缓冲区中提取经验。⑪ 并返回这些经验。⑫ 这是一个方便的函数,当调用 len(buffer)时,返回缓冲区的正确大小。 |
使用其他探索策略
探索是强化学习的一个关键组成部分。在 NFQ 算法中,我们使用 epsilon-greedy 探索策略,该策略包括以 epsilon 概率随机行动。我们从(0, 1)的均匀分布中抽取一个数字。如果这个数字小于超参数常量,称为 epsilon,你的智能体将以均匀随机的方式选择一个动作(包括贪婪动作);否则,它将贪婪地行动。
对于 DQN 实验,我在第九章的笔记本中添加了一些在第四章中介绍的其他探索策略。我将它们调整为与神经网络一起使用,并在下面重新介绍。请确保查看所有笔记本并进行实验。
| 我会说 Python 线性衰减的 epsilon-greedy 探索策略 | |
|---|---|
class EGreedyLinearStrategy(): ①
<...>
def _epsilon_update(self):
self.epsilon = 1 - self.t / self.max_steps
self.epsilon = (self.init_epsilon - self.min_epsilon) * \
self.epsilon + self.min_epsilon
self.epsilon = np.clip(self.epsilon, ②
self.min_epsilon,
self.init_epsilon)
self.t += 1 ③
return self.epsilon
def select_action(self, model, state): ④
self.exploratory_action = False
with torch.no_grad():
q_values = model(state).cpu().detach() ⑤
q_values = q_values.data.numpy().squeeze()
if np.random.rand() > self.epsilon: ⑥
action = np.argmax(q_values)
else: ⑦
action = np.random.randint(len(q_values))
self._epsilon_update()
self.exploratory_action = action != np.argmax(q_values)
return action ⑧
① 在线性衰减的 epsilon-greedy 策略中,我们从一个较高的 epsilon 值开始,并以线性方式衰减其值。② 我们将 epsilon 裁剪到初始值和最小值之间。③ 这是一个变量,用于存储 epsilon 更新的次数。④ 在 select_action 方法中,我们使用一个模型和一个状态。⑤ 为了记录日志,我总是提取 q_values。⑥ 我们从均匀分布中抽取一个随机数,并将其与 epsilon 进行比较。⑦ 如果更高,我们使用 q_values 的 argmax;否则,执行随机动作。⑧ 最后,我们更新 epsilon,设置一个用于记录的变量,并返回所选动作。 |
| 我会说 Python 指数衰减的 epsilon-greedy 探索策略 | |
|---|---|
class EGreedyExpStrategy():
<...>
def _epsilon_update(self):
self.epsilon = max(self.min_epsilon, ①
self.decay_rate * self.epsilon)
return self.epsilon
# def _epsilon_update(self): ②
# self.decay_rate = 0.0001
# epsilon = self.init_epsilon * np.exp( \
# -self.decay_rate * self.t)
# epsilon = max(epsilon, self.min_epsilon)
# self.t += 1
# return epsilon
def select_action(self, model, state): ③
self.exploratory_action = False
with torch.no_grad():
q_values = model(state).cpu().detach()
q_values = q_values.data.numpy().squeeze()
if np.random.rand() > self.epsilon:
action = np.argmax(q_values)
else:
action = np.random.randint(len(q_values))
self._epsilon_update()
self.exploratory_action = action != np.argmax(q_values)
return action ④
① 在指数衰减策略中,唯一的不同是现在 ε 正在以指数曲线衰减。② 这又是另一种指数衰减 ε 的方法,这种方法使用指数函数。ε 值将非常相似,但衰减率将需要不同的尺度。③ 这个 select_action 函数与之前的策略完全相同。我想强调的是,我之所以每次都查询 q_values,只是因为我正在收集信息向您展示。但如果你关心性能,这并不是一个好主意。一个更快的实现只会在网络确定需要贪婪动作时才查询网络。④ exploratory_action 是一个变量,用于计算每集探索动作的百分比。仅用于记录信息。 |
| 我会说 PythonSoftmax 探索策略 | |
|---|---|
class SoftMaxStrategy():
<...>
def _update_temp(self):
temp = 1 - self.t / (self.max_steps * self.explore_ratio) ①
temp = (self.init_temp - self.min_temp) * \
temp + self.min_temp
temp = np.clip(temp, self.min_temp, self.init_temp) ②
self.t += 1
return temp
def select_action(self, model, state):
self.exploratory_action = False
temp = self._update_temp()
with torch.no_grad():
q_values = model(state).cpu().detach() ③
q_values = q_values.data.numpy().squeeze()
scaled_qs = q_values/temp ④
norm_qs = scaled_qs - scaled_qs.max() ⑤
e = np.exp(norm_qs) ⑥
probs = e / np.sum(e) ⑦
assert np.isclose(probs.sum(), 1.0)
action = np.random.choice(np.arange(len(probs)), ⑧
size=1, p=probs)[0]
self.exploratory_action = action != np.argmax(q_values) ⑨
return action
① 在 softmax 策略中,我们使用一个温度参数,越接近 0,值的差异将越明显,从而使动作选择更加贪婪。温度线性衰减。② 在这里,在温度线性衰减后,我们将其值剪辑到可接受的范围内。③ 注意,在 softmax 策略中,我们实际上没有避免从模型中提取 q_values 的机会。毕竟,动作直接取决于值。④ 提取值后,我们希望强调它们的差异(除非 temp 等于 1)。⑤ 我们将它们归一化以避免 exp 操作溢出。⑥ 计算指数。⑦ 转换为概率。⑧ 最后,我们使用概率来选择动作。注意我们如何将 probs 变量传递给 p 函数参数。⑨ 如前所述:动作是贪婪的还是探索性的? |
| 它在于细节探索策略对性能有显著影响 | |
|---|---|
![]() |
|
| 它在于细节完整的深度 Q 网络(DQN)算法 |
| | 我们的 DQN 实现具有与我们的 NFQ 相似的功能和设置:
-
近似动作值函数 Q(s,a; θ)。
-
使用状态-值输出架构(节点:4, 512, 128, 2)。
-
优化动作值函数以逼近最优动作值函数 *(s,a)。
-
使用离策略 TD 目标 (r + gammamax_a’Q(s’,a’; θ*)) 来评估策略。
-
使用均方误差(MSE)作为我们的损失函数。
-
使用学习率为 0.0005 的 RMSprop 作为我们的优化器。
一些不同之处在于,在 DQN 实现中我们现在
-
使用指数衰减的 ε-greedy 策略来改进策略,大约在 20,000 步内从 1.0 衰减到 0.3。
-
使用具有 320 个最小样本、50,000 个最大样本和 64 个小批次的回放缓冲区。
-
使用每 15 步更新一次的目标网络。
DQN 有三个主要步骤:
-
收集经验:(S[t] , A[t] , R[t+1], S[t+1], D[t+1]), 并将其插入回放缓冲区。
-
从缓冲区中随机抽取一个迷你批次,并计算整个批次的离线 TD 目标:r + gammamax_a’Q(s’,a’; θ*)。
-
使用均方误差(MSE)和 RMSprop 对动作值函数 Q(s,a; θ) 进行拟合。
|
双重 DQN:缓解动作值函数的过度估计
在本节中,我们介绍 DQN 在多年间提出的主要改进之一,称为双深度 Q 网络(double DQN,或 DDQN)。这种改进包括向我们的 DQN 代理添加双重学习。它易于实现,并且产生的代理性能比 DQN 更稳定。所需的变化与用于开发双重 Q 学习的 Q 学习应用的变化类似;然而,有几个差异我们需要讨论。
过度估计的问题,有两个
如您可能从第六章中记得,Q 学习往往会高估动作值函数。我们的 DQN 代理也不例外;毕竟,我们使用的是相同的离线策略 TD 目标,带有那个最大操作符。问题的核心很简单:我们在估计值中取最大值。估计值通常偏离中心,有些高于真实值,有些低于真实值,但关键是它们是偏离的。问题是我们总是取这些值的最大值,所以我们偏好更高的值,即使它们并不正确。我们的算法显示出正偏差,性能受到影响。
| 米格尔的类比关于过度乐观的代理和人们的问题 | |
|---|---|
| 我曾经喜欢超级积极的人,直到我了解到双 DQN。不,说真的,想象一下你遇到一个非常乐观的人;让我们称她为 DQN。DQN 非常乐观。她经历了生活中许多事情,从最艰难的失败到最高的成功。然而,DQN 的问题是她期望她所做的一切都能得到最甜美的结果,无论她实际上做了什么。这是问题吗?有一天,DQN 去了当地的一家赌场。这是第一次,但幸运的 DQN 在老丨虎丨机上赢得了大奖。尽管她如此乐观,DQN 立即调整了她的价值函数。她心想,“去赌场是非常有回报的(Q(s, a)的值应该很高),因为在赌场你可以去老丨虎丨机(下一个状态 s’),通过玩老丨虎丨机,你可以得到大奖 [Q(s’, a’)]”。但是,这种想法存在多个问题。首先,DQN 并不每次去赌场都玩老丨虎丨机。她也喜欢尝试新事物(她进行探索),有时她尝试轮盘赌、扑克或 21 点(尝试不同的行动)。有时老丨虎丨机区域正在维护,无法访问(环境将她转移到其他地方)。此外,大多数时候当 DQN 玩老丨虎丨机时,她并没有赢得大奖(环境是随机的)。毕竟,老丨虎丨机被称为强盗,不是那些强盗,而是其他——不必在意。 |
分离动作选择和动作评估
要更好地理解正偏差以及在使用函数近似时如何解决它,一种方法是在目标计算中展开 max 操作符。Q 函数的 max 与 argmax 行动的 Q 函数相同。
| 刷新我的记忆再次什么是 argmax? | |
|---|---|
| argmax 函数定义为最大值的参数。argmax 动作值函数,argmax Q 函数,argmax[a]Q(s, a)是在给定状态 s 下具有最大值的动作的索引。例如,如果你有一个Q(s),对于动作 0–3,其值分别为[–1, 0, –4, –9],则max[a]Q(s, a)是 1,这是最大值的索引。 |
让我们用 max 和 argmax 来解开前面的句子。注意,当我们从 Q 学习到双 Q 学习时,我们几乎做了同样的改变,但鉴于我们使用函数近似,我们需要谨慎。起初,这种展开可能看起来像是一个愚蠢的步骤,但它帮助我理解如何减轻这个问题。
| 展示数学公式展开 argmax | |
|---|---|
![]() |
|
| 我会说 Python 在 DQN 中展开 max | |
q_sp = self.target_model(next_states).detach() ①②③
max_a_q_sp = q_sp.max(1)[0].unsqueeze(1) ①③
max_a_q_sp *= (1 - is_terminals) ①④
target_q_sa = rewards + self.gamma * max_a_q_sp ①④
argmax_a_q_sp = self.target_model(next_states).max(1)[1]⑤⑥
q_sp = self.target_model(next_states).detach() ⑤⑦
max_a_q_sp = q_sp[np.arange(batch_size), argmax_a_q_sp] ⑤⑧
max_a_q_sp = max_a_q_sp.unsqueeze(1) ⑤⑨
max_a_q_sp *= (1 - is_terminals) ⑤⑨
target_q_sa = rewards + self.gamma * max_a_q_sp ⑤
① 这就是原始的 DQN 计算目标值的方法。② 重要的是我们要断开目标,这样我们就不通过它进行反向传播。③ 我们获取下一个状态的 Q 值并取其最大值。④ 将终端状态的值设为 0,并计算目标值。⑤ 这是一种等效的计算目标值的方法,“解开最大值。”⑥ 首先,获取下一个状态的 argmax 动作。⑦ 然后,像之前一样获取下一个状态的 Q 值。⑧ 现在,我们使用索引来获取下一个状态的最大值。⑨ 然后继续之前的步骤。 |
我们在这里所说的就是取 max 就像问网络,“在状态中价值最高的动作的值是多少?”
但,我们实际上用一个问题问了两个问题。首先,我们进行了一个 argmax,这相当于问,“在状态 s 中哪个动作是价值最高的动作?”
然后,我们使用该动作来获取其值,相当于问,“在状态 s 中这个动作(碰巧是价值最高的动作)的值是多少?”
问题是,我们向同一个 Q 函数问了两个问题,这导致两个答案都显示出相同方向的偏差。换句话说,函数近似器会回答,“我认为在状态 s 中这个动作是价值最高的,这是它的值。”
一个解决方案
减少正偏差的一种方法是在第六章中我们做的那样,有两个动作值函数的实例。
如果你还有其他来源的估计值,你可以向一个提问一个问题,向另一个提问另一个问题。这有点像投票,或者像“我切,你先选”的程序,或者像在健康问题上寻求第二位医生的意见。
在双重学习中,一个估计器选择它认为价值最高的动作的索引,而另一个估计器给出这个动作的值。
| 双重学习过程 |
|---|
| | 我们在第六章中介绍了使用表格强化学习进行此过程,在双重 Q-learning 代理下进行。过程是这样的:
-
你创建两个动作值函数,Q[A] 和 Q[B]。
-
你掷硬币来决定更新哪个动作值函数。例如,正面更新 Q[A],反面更新 Q[B]。
-
如果你掷出正面并因此更新 Q[A]:你从 Q[B] 中选择要评估的动作索引,并使用 Q[A] 预测的估计值来评估它。然后,你像往常一样更新 Q[A],而 Q[B] 保持不变。
-
如果你掷出反面并因此更新 Q[B],你反过来操作:从 Q[A] 获取索引,从 Q[B] 获取值估计。Q[B] 被更新,而 Q[A] 保持不变。
|
然而,当使用函数近似(对于 DQN)时,如果完全按照描述实现这种双重学习过程,将会产生不必要的开销。如果我们这样做,最终会有四个网络:两个用于训练的网络(Q[A],Q[B])和两个目标网络,每个在线网络一个。
此外,它还会减慢训练过程,因为我们一次只会训练这些网络中的一个。因此,每一步只会提高一个网络。这无疑是浪费。
尽管有额外的开销,但使用函数逼近器进行双重学习程序可能仍然比完全不进行更好。幸运的是,我们对原始的双重学习程序进行了简单的修改,使其适应 DQN,并在没有额外开销的情况下提供了实质性的改进。
一个更实际的解决方案
而不是增加这种损害训练速度的开销,我们可以使用我们已有的另一个网络进行双重学习,即目标网络。然而,我们不是同时训练在线和目标网络,而是继续只训练在线网络,但使用目标网络帮助我们,从某种意义上说,交叉验证估计。
我们需要谨慎选择用于动作选择和动作评估的网络。最初,我们添加目标网络以通过避免追逐移动目标来稳定训练。为了继续这一路径,我们想要确保我们使用正在训练的网络,即在线网络,来回答第一个问题。换句话说,我们使用在线网络来找到最佳动作的索引。然后,我们使用目标网络来回答第二个问题,即评估之前选定的动作。
这是在实践中效果最好的排序方式,它之所以有效是有道理的。通过使用目标网络进行价值估计,我们确保目标值在需要时被冻结以保证稳定性。如果我们反过来实施,值将来自在线网络,该网络在每一步都会更新,因此会持续变化。

选择动作,评估动作
| 0001 | 一点历史介绍双重 DQN 算法 |
|---|---|
| 双重 DQN 是在 2015 年由 Hado van Hasselt 提出的,紧随 2015 版 DQN 的发布之后。(2015 版的 DQN 有时被称为 Nature DQN——因为它发表在 Nature 科学期刊上,有时也称为 Vanilla DQN——因为它是在多年来的许多改进中的第一个。)在 2010 年,Hado 还撰写了双重 Q 学习算法(表格情况下的双重学习),作为 Q 学习算法的改进。这是你在第六章中学到并实现的内容。双重 DQN,也称为 DDQN,是多年来为 DQN 提出的许多改进中的第一个。在 2015 年首次提出时,DDQN 在 Atari 领域获得了当时最先进的(最好的)结果。Hado 在荷兰乌得勒支大学获得了人工智能(强化学习)博士学位。在作为博士后研究员的几年后,他在谷歌 DeepMind 担任研究科学家。 | |
| 给我看数学 DDQN 梯度更新 | |
![]() |
|
| 我会说 PythonDouble DQN | |
def optimize_model(self, experiences):
states, actions, rewards, \
next_states, is_terminals = experiences
batch_size = len(is_terminals)
#argmax_a_q_sp = self.target_model(next_states).max(1)[1]
argmax_a_q_sp = self.online_model(next_states).max(1)[1] ①
q_sp = self.target_model(next_states).detach() ②
max_a_q_sp = q_sp[np.arange(batch_size), argmax_a_q_sp] ③
max_a_q_sp = max_a_q_sp.unsqueeze(1)
max_a_q_sp *= (1 - is_terminals)
target_q_sa = rewards + (self.gamma * max_a_q_sp) ④
q_sa = self.online_model(states).gather(1, actions) ⑤
td_error = q_sa - target_q_sa
value_loss = td_error.pow(2).mul(0.5).mean()
self.value_optimizer.zero_grad() ⑥
value_loss.backward()
self.value_optimizer.step()
def interaction_step(self, state, env):
action = self.training_strategy.select_action(
self.online_model, state) ⑦
new_state, reward, is_terminal, _ = env.step(action)
return new_state, is_terminal
def update_network(self): ⑧
for target, online in zip(
self.target_model.parameters(),
self.online_model.parameters()):
target.data.copy_(online.data)
① 在 Double DQN 中,我们使用在线网络获取下一个状态中最高价值动作的索引,即 argmax。注意我们并没有分离 argmax,因为它们不可微分。max(1)[1]返回了最大值的索引,这已经是“分离”的。② 然后,根据目标网络提取下一个状态的 Q 值。③ 我们然后使用在线网络提供的动作索引来索引目标网络提供的 Q 值。④ 然后像往常一样设置目标。⑤ 获取当前估计。注意这是梯度流动的地方。⑥ 计算损失,并更新优化器。⑦ 这里我们继续使用在线网络进行动作选择。⑧ 更新目标网络的方式仍然和以前一样。|
更宽容的损失函数
在上一章中,我们选择了 L2 损失,也称为均方误差(MSE),作为我们的损失函数,主要是因为其广泛的应用和简单性。实际上,在像购物车-杆环境这样的问题中,可能没有很好的理由再进一步探索。然而,因为我不仅教你“如何锤钉”,还教你算法的细节,所以我希望让你了解可用的不同旋钮,这样你就可以在解决更具挑战性的问题时进行尝试。

MSE 是一个无处不在的损失函数,因为它简单、合理且效果良好。但是,使用 MSE 进行强化学习的一个问题是,它对大错误的惩罚比对小错误的惩罚更重。在执行监督学习时这是有意义的,因为我们的目标是从一开始就是真实值,并且在整个训练过程中保持固定。这意味着我们确信,如果模型非常错误,那么它应该比只是错误时受到更重的惩罚。
但正如已经多次提到的,在强化学习中,我们没有这些真实值,我们用来训练网络的值依赖于智能体本身。这是一个思维转变。此外,目标不断变化;即使在使用目标网络时,它们也经常变化。在强化学习中,我们期望并欢迎犯错误。最终,如果你仔细想想,我们并不是“训练”智能体;我们的智能体是自行学习的。想想看。
一个不那么严厉的损失函数,并且对异常值更鲁棒的函数是平均绝对误差,也称为 MAE 或 L1 损失。MAE 被定义为预测值和真实值之间平均绝对差,即预测的动作值函数和TD目标。鉴于 MAE 是一个线性函数,而不是像 MSE 那样的二次函数,我们可以预期 MAE 在处理大误差和小误差方面会更为成功。这在我们的情况下很有用,因为我们预计动作值函数在训练过程中某些时刻会给出错误值,尤其是在开始时。对异常值有更强的抵抗力通常意味着误差对我们的网络变化的影响较小,与 MSE 相比,这意味着更稳定的学习。

现在,从另一方面来看,MSE 的一个有助于 MAE 的特性是,随着损失趋近于零,其梯度会减小。这个特性对优化方法很有帮助,因为它使得达到最优值变得更容易:较小的梯度意味着网络的小幅变化。但幸运的是,有一个损失函数是 MSE 和 MAE 的某种混合体,称为 Huber 损失。
Huber 损失具有与 MSE 相同的有用特性,即对接近零的误差进行二次惩罚,但它并不是对所有巨大误差都进行二次惩罚。相反,Huber 损失在接近零的误差处是二次(曲线)的,而对于大于预设阈值的误差,它变为线性(直线)。两者兼而有之使得 Huber 损失对异常值具有鲁棒性,就像 MAE 一样,并且在 0 处可微分,就像 MSE 一样。

Huber 损失使用一个超参数δ来设置损失从二次变为线性的阈值,基本上是从 MSE 变为 MAE。如果δ为零,你将得到精确的 MAE,如果δ是无穷大,那么你将得到精确的 MSE。δ的一个典型值是 1,但请注意,你的损失函数、优化和学习率以复杂的方式相互作用。如果你改变其中一个,你可能需要调整其他几个。查看本章的笔记本,这样你可以进行一些实验。

有趣的是,至少有两种不同的实现 Huber 损失函数的方法。你可以按照定义计算 Huber 损失,或者计算 MSE 损失,然后将所有大于阈值的梯度设置为固定幅度值。你剪裁了梯度的幅度。前者取决于你使用的深度学习框架,但问题是某些框架不提供对δ超参数的访问,所以你只能将δ设置为 1,这并不总是有效,也不是总是最好的。后者通常被称为损失剪裁,或者更好的是梯度剪裁,它更灵活,因此我在笔记本中实现了这种方法。
| 我会说 Python 双 DQN 与 Huber 损失 | |
|---|---|
def optimize_model(self, experiences):
states, actions, rewards, \
next_states, is_terminals = experiences
batch_size = len(is_terminals)
<...> ①
td_error = q_sa - target_q_sa
value_loss = td_error.pow(2).mul(0.5).mean() ②
self.value_optimizer.zero_grad() ③
value_loss.backward()
torch.nn.utils.clip_grad_norm_( ④
self.online_model.parameters(),
self.max_gradient_norm)
self.value_optimizer.step() ⑤
① 首先,你计算目标值并像之前一样使用双重学习得到当前值。② 然后,像之前一样计算损失函数,即均方误差。③ 将优化器归零并在反向步骤中计算梯度。④ 现在,将梯度裁剪到 max_gradient_norm。这个值可以是任何值,但要知道这个值与其他超参数,如学习率相互作用。⑤ 最后,移动优化器。|
知道有一种叫做奖励裁剪的东西,这与梯度裁剪不同。这两者是非常不同的,所以要注意。一个作用于奖励,另一个作用于误差(损失)。现在,最重要的是不要将这两者与Q 值裁剪混淆,这无疑是一个错误。
记住,在我们的案例中,目标是防止梯度变得过大。为此,我们可以在给定的绝对TD误差阈值之外使损失线性化,或者使梯度在最大梯度幅度阈值之外保持恒定。
在笔记本中你找到的 cart-pole 环境实验中,我通过梯度裁剪技术实现了 Huber 损失函数。也就是说,我首先计算均方误差(MSE),然后裁剪梯度。然而,正如我之前提到的,我将最大梯度值的超参数设置为了无穷大。因此,实际上是在使用传统的 MSE。但是,请务必进行实验,玩耍,探索!我创建的笔记本应该能帮助你学到与书籍几乎一样多的知识。在那里,让自己自由发挥吧。
| 这就是细节全双深度 Q 网络(DDQN)算法 |
|---|
| | DDQN 几乎与 DQN 相同,但仍有几个区别:
-
近似动作值函数 Q(s, a; θ)。
-
使用状态-值输出架构(节点:4, 512, 128, 2)。
-
优化动作值函数以近似最优动作值函数 q**(s,a*)。
-
使用离线 TD 目标(r + gammamax_a’Q(s’,a’; θ*))来评估策略。
注意,我们现在
-
使用可调整的 Huber 损失,由于我们将 max_gradient_norm 变量设置为“float(‘inf’),”因此我们实际上在损失函数中使用的是均方误差(MSE)。
-
使用学习率为 0.0007 的 RMSprop 作为我们的优化器。请注意,在我们之前使用的是 0.0005,因为没有双重学习(vanilla DQN),如果使用学习率为 0.0007 进行训练,则几个种子会失败。也许是因为稳定性?另一方面,在 DDQN 中,使用更高的学习率效果最好。
在 DDQN 中我们仍然使用
-
一种指数衰减的 epsilon-greedy 策略(从 1.0 到 0.3 大约需要 20,000 步)来改进策略。
-
一个具有 320 个最小样本、50,000 个最大样本和 64 个批次的重放缓冲区。
-
目标网络冻结 15 步后完全更新。
DDQN 与 DQN 类似,具有相同的三个主要步骤:
-
收集经验:(S[t], A[t], R[t+1], S[t+1], D[t+1]),并将其插入到重放缓冲区中。
-
从缓冲区中随机抽取一个迷你批次,并计算整个批次的离策略TD目标:r + gammamax_a’Q(s’,a’; θ*)。
-
使用均方误差(MSE)和 RMSprop 来拟合动作值函数Q(s, a; θ)。
底线是,DDQN 的实现和超参数与 DQN 相同,只是我们现在使用双重学习,因此以略高的学习率进行训练。添加 Huber 损失并没有改变什么,因为我们“剪辑”梯度到无限大的最大值,这相当于使用 MSE。然而,对于许多其他环境,你会发现它很有用,所以调整这个超参数。|
| 累计它 DDQN 比 NFQ 或 DQN 更稳定 | |
|---|---|
在小车-杆环境中,DQN 和 DDQN 有相似的性能。然而,这是一个具有平滑奖励函数的简单环境。在现实中,DDQN 应该总是给出更好的性能!![]() |
我们还可以改进的地方
当然,我们的当前基于价值的深度强化学习方法并不完美,但它相当稳固。DDQN 在许多 Atari 游戏中可以达到超人类的表现。为了复制这些结果,你必须将网络改为接受图像作为输入(四个图像的堆叠,以便能够从图像中推断出方向和速度等),当然,还需要调整超参数。
然而,我们还可以更进一步。至少还有其他几个易于实现且对性能有积极影响的改进可以考虑。
第一个改进要求我们重新考虑当前的神经网络架构。到目前为止,我们在神经网络架构中对 Q 函数有一个天真表示。
| 刷新我的记忆当前神经网络架构 | |
|---|---|
我们实际上是在“让强化学习看起来像监督学习。”但,我们可以,也应该打破这种限制,跳出思维定式! 值-状态输出架构有没有更好的方法来表示 Q 函数?当你看到下一页的图像时,请思考一下这个问题。 |
右边的图像是条形图,表示小车-杆环境中状态接近垂直时的估计动作值函数 Q、状态值函数 V 和动作优势函数 A。

注意不同的函数和值,并开始思考如何更好地设计神经网络架构,以便更有效地使用数据。作为一个提示,让我提醒你,状态 Q 值通过 V 函数相关联。也就是说,动作值函数Q与状态值函数 V 有本质的联系,因为Q(s)中的动作都由相同的状态s索引(在右边的例子中s=[0.02, –0.01, –0.02, –0.04])。

问题是,如果你使用的是 Q(s, 1) 样本,你能否了解关于 Q(s, 0) 的任何信息?看看展示动作-优势函数 A(s) 的图表,注意相对于使用动作值函数 Q(s) 的图表,你如何更容易地通过直观判断来选择贪婪动作。你能做些什么呢?在下一章中,我们将探讨一种名为 dueling network 的网络架构,它有助于我们利用这些关系。

另一个需要考虑改进的是我们从重放缓冲区中采样经验的方式。到目前为止,我们是从缓冲区中随机均匀地抽取样本,我相信你的直觉会质疑这种方法,并建议我们可以做得更好,我们确实可以。
人类不会在世界各地随意记住随机的事情以随机的时间学习。智能代理“重放记忆”的方式更为系统。我相当确信我的狗在梦中追逐兔子。某些经历对我们实现目标来说比其他经历更重要。人类经常重放那些给他们带来意外喜悦或痛苦的经历。这是有道理的,你需要从这些经历中学习,以产生更多或更少的这些经历。在下一章中,我们将探讨优先采样经验的方法,以从每个样本中获得最大收益,当我们了解优先经验重放(PER)方法时。
摘要
在本章中,你了解了基于价值的深度强化学习方法中普遍存在的问题。在线数据非平稳性,以及它也不是大多数优化方法所期望的独立同分布,为基于价值的方法带来了大量问题。
你通过使用在多个基准测试中具有实证结果的多种技术来稳定基于价值的深度强化学习方法,并深入研究了使基于价值方法更稳定的这些组件。具体来说,你了解了在名为 DQN 的算法中使用目标网络和重放缓冲区的优势(自然 DQN 或纯 DQN)。你了解到,通过使用目标网络,我们使目标对优化器看起来是平稳的,这对稳定性有利,尽管这牺牲了收敛速度。你还了解到,通过使用重放缓冲区,在线数据看起来更像是独立同分布的,正如你所了解的,这是基于价值的自举方法中重大问题的来源。这两种技术的结合使算法足够稳定,能够在多个深度强化学习任务中表现良好。
然而,还有许多其他潜在的方法可以改进基于价值的方法。你实施了一个简单改变,这对性能产生了重大影响。你向基线 DQN 代理添加了双重学习策略,当使用函数逼近时,它被称为 DDQN 代理,这有助于缓解离策略基于价值方法中的高估问题。
除了这些新算法之外,你还学习了与基于价值的策略一起使用的不同探索策略。你学习了线性衰减和指数衰减的ε-greedy 以及 softmax 探索策略,这次是在函数逼近的背景下。你还学习了不同的损失函数,以及哪些损失函数对强化学习更有意义以及为什么。你了解到 Huber 损失函数允许你通过单个超参数在均方误差(MSE)和绝对误差(MAE)之间进行调整,并且它是基于价值的深度强化学习方法中首选的损失函数之一。
到现在为止,
-
理解为什么使用在线数据训练期望平稳和独立同分布数据的神经网络的优化器在基于价值的深度强化学习方法中是一个问题
-
可以使用更稳定且因此能给出更一致结果的算法来解决具有连续状态空间的强化学习问题
-
你已经理解了最先进的基于价值的深度强化学习方法,并能解决复杂问题
| 独立工作并分享你的发现 |
|---|
| | 这里有一些想法,可以帮助你将所学知识提升到更高层次。如果你愿意,可以将你的成果分享给全世界,并确保查看其他人所做的工作。这是一个双赢的局面,希望你能充分利用它。
-
#gdrl_ch09_tf01: 在本章和下一章中,我们仅在购物车-杆环境中测试算法。找到其他几个环境并在其中测试智能体,例如,这里提供的月球着陆器环境:
gym.openai.com/envs/#box2d,以及这里提供的山车环境:gym.openai.com/envs/#classic_control。你除了超参数外,是否需要对智能体进行任何更改才能使其在这些环境中工作?确保找到一组可以解决所有环境的超参数。为了澄清,我的意思是使用一组超参数,并在每个环境中从头开始训练一个智能体,而不是一个在所有环境中都表现良好的训练好的智能体。 -
#gdrl_ch09_tf02: 在本章和下一章中,我们在连续但低维度的环境中测试算法。你知道什么是高维环境吗?Atari 环境。在这里查找它们(非“ram”):
gym.openai.com/envs/#atari。现在,修改本章中的网络、重放缓冲区和智能体代码,以便智能体可以解决基于图像的环境。请注意,这不是一个简单任务,训练将需要一段时间,从数小时到数天不等。 -
#gdrl_ch09_tf03: 我提到基于价值的算法对超参数很敏感。实际上,存在一个被称为“致命三角”的东西,它基本上告诉我们使用具有引导和离线策略的神经网络是糟糕的。去调查一下吧!
-
#gdrl_ch09_tf04: 在每一章中,我都在使用最后的标签作为通用的标签。请随意使用这个标签来讨论与本章相关的任何其他你工作过的事情。没有什么比为自己创造作业更令人兴奋的了。确保分享你打算调查的内容以及你的结果。
用你的发现写一条推文,@mimoralea(我会转发),并使用列表中的特定标签来帮助感兴趣的人找到你的结果。没有正确或错误的结果;你分享你的发现并检查他人的发现。利用这个机会社交,做出贡献,让自己脱颖而出!我们正在等待着你!以下是一条推文示例:“嘿,@mimoralea。我创建了一个包含学习深度强化学习资源的博客文章。查看它吧,链接: #gdrl_ch01_tf01”我会确保转发并帮助他人找到你的作品。 |
10 种样本高效的基于价值的方法
在本章中
-
你将实现一个深度神经网络架构,该架构利用了基于价值的深度强化学习方法中存在的一些细微差别。
-
你将创建一个优先级回放缓冲区,根据经验的新颖性来优先处理。
-
你将构建一个代理,它将在比我们讨论的所有基于价值的深度强化学习代理更少的回合中训练到接近最优策略。
智力基于一个物种在完成生存所需的事情上的效率。
—— 查尔斯·达尔文 英国自然学家、地质学家和生物学家,以对进化科学的贡献而闻名
在上一章中,我们通过实现 DQN 和 DDQN 改进了 NFQ。在本章中,我们继续改进之前的算法,通过介绍两种额外的技术来提高基于价值的深度强化学习方法。然而,这次改进并不那么多的关于稳定性,尽管这很容易成为副产品。但更准确地说,本章中介绍的技术提高了 DQN 和其他基于价值的 DRL 方法的样本效率。
首先,我们介绍了一种功能神经网络架构,该架构将 Q 函数表示分为两个流。一个流近似 V 函数,另一个流近似 A 函数。V 函数是每个状态的价值,而 A 函数表示每个动作与它们的 V 函数的距离。
这是一个设计 RL 专用架构的实用事实,这种架构能够从给定状态的所有动作中提取信息,并将其压缩到该状态的 V 函数中。这意味着一个单一的经验元组可以帮助提高该状态下所有动作的价值估计。这提高了代理的样本效率。
本章中我们引入的第二项改进与回放缓冲区有关。正如你从上一章中记得的那样,DQN 中的标准回放缓冲区在随机均匀地采样经验。理解这一点至关重要,即随机均匀采样对于保持梯度与真实数据生成的基础分布成比例是好事,因此保持更新无偏。然而,问题在于,如果我们能够设计一种优先处理经验的方法,我们就可以使用最有希望用于学习的样本。因此,在本章中,我们介绍了一种不同的采样经验技术,使我们能够抽取那些似乎为代理提供最多信息的样本,以实际上进行改进。
Dueling DDQN:一种强化学习感知的神经网络架构
现在我们来深入探讨这种称为 对抗网络架构 的专用神经网络架构的细节。对抗网络是一种仅适用于网络架构而不适用于算法的改进。也就是说,我们不会对算法进行任何修改,但所有的修改都集中在网络架构上。这一特性使得对抗网络可以与多年来对原始 DQN 算法提出的几乎所有改进相结合。例如,我们可以有一个对抗 DQN 代理,一个对抗双 DQN 代理(或者我所说的对抗 DDQN),等等。许多这些改进都是即插即用的,我们将在本章中利用这一点。现在让我们实现一个对抗架构,用于我们的实验,并在构建过程中了解它。
强化学习不是一个监督学习问题
在上一章中,我们集中精力使强化学习看起来更像一个监督学习问题。通过使用重放缓冲区,我们使在线数据,即代理按顺序体验和收集的数据,看起来更像一个独立同分布的数据集,这在监督学习中很常见。
我们还使目标看起来更静态,这也是监督学习问题的一个常见特征。这无疑有助于稳定训练,但忽视强化学习问题是其自身的问题这一事实,并不是解决这些问题的最佳方法。
基于价值的深度强化学习代理具有的一个微妙之处,并且我们将在本章中利用这一点,就是价值函数之间的关系。更具体地说,我们可以利用状态值函数 V(s) 和动作值函数 Q(s, a) 通过动作优势函数 A(s, a) 相互关联的事实。
| 刷新我的记忆价值函数回顾 | |
|---|---|
![]() |
基于价值的深度强化学习方法的细微差别
动作值函数 Q(s, a) 可以定义为状态值函数 V(s) 和动作优势函数 A(s, a) 的和。这意味着我们可以将 Q 函数分解为两个部分:一个在所有动作中共享,另一个对每个动作都是独特的;或者说,一个依赖于动作,另一个不依赖于动作。
目前,我们正在分别学习每个动作的动作-值函数 Q(s, a),但这效率不高。当然,由于网络内部是相互连接的,所以会有一些泛化发生。因此,网络节点之间会共享信息。但是,当我们学习 Q(s, a[1]) 时,我们忽略了这样一个事实:我们可以使用相同的信息来学习关于 Q(s, a[2])、Q(s, a[3]) 以及在状态 s 中可用的所有其他动作的信息。事实上,V(s) 对所有动作 a[1]、a[2]、a[3],...、a[N] 都是通用的。

有效利用经验
| 简化它动作-值函数 Q(s, a) 依赖于状态-值函数 V(s) | |
|---|---|
| 核心问题是动作的值取决于状态的值,我们希望利用这一事实。最终,在好状态下采取最差动作可能比在坏状态下采取最佳动作更好。你看到了“动作的值取决于状态的值”吗?对抗网络架构利用动作-值函数 Q(s, a) 对状态-值函数 V(s) 的这种依赖性,以便每次更新都改进状态-值函数 V(s) 的估计,这对于所有动作都是通用的。 |
使用优势的优势
现在,让我给你举一个例子。在购物车-杆环境中,当杆处于直立位置时,左右动作的值几乎相同。当杆精确直立时(为了论证,假设购物车精确位于轨道中间,并且所有速度都是 0),无论你做什么都无关紧要。在这种完美的状态下,向左或向右移动应该具有相同的值。
然而,当杆向右倾斜 10 度时,采取的动作确实很重要。在这种情况下,推动购物车向右以抵消倾斜是代理可以采取的最佳动作。相反,向左移动,并因此加剧倾斜,可能是一个糟糕的主意。
注意,这正是动作-优势函数 A(s, a) 所表示的:在当前状态 s 中采取特定动作 a 比平均水平好多少?

值函数之间的关系
强化学习感知架构
对抗网络架构包括创建两个独立的估计器,一个是状态-值函数 V(s),另一个是动作-优势函数 A(s, a)。然而,在分割网络之前,你想要确保你的网络共享内部节点。例如,如果你使用图像作为输入,你想要共享卷积,以便特征提取层可以共享。在购物车-杆环境中,我们共享隐藏层。
在共享了大部分内部节点和层之后,输出层之前的层分为两个流:一个用于状态值函数V(s)的流,另一个用于动作优势函数A(s, a)的流。V 函数输出层总是以单个节点结束,因为状态的价值始终是一个单一的数字。然而,Q 函数的输出层输出一个与动作数量相同的向量。在 cart-pole 环境中,动作优势函数流的输出层有两个节点,一个用于左边的动作,另一个用于右边的动作。

对弈网络架构
| 0001 | 一点历史介绍对弈网络架构 |
|---|---|
| 对弈神经网络架构是在 2015 年由 Ziyu Wang 在牛津大学攻读博士学位时,在名为“用于深度强化学习的对弈网络架构”的论文中提出的。这篇论文可以说是第一篇介绍专门为基于价值的深度强化学习方法设计的自定义深度神经网络架构的论文。Ziyu 现在是 Google DeepMind 的研究科学家,在那里他继续为深度强化学习领域做出贡献。 |
构建对弈网络
构建对弈网络很简单。我注意到你可以在输入层之后任何地方分割网络,它仍然可以正常工作。我可以想象你甚至可以有两个独立的网络,但我看不到这样做的好处。总的来说,我的建议是尽可能多地共享层,只在输出层之前的一个层中分割成两个头部。
| 我会说 Python 构建对弈网络 | |
|---|---|
class FCDuelingQ(nn.Module): ①
def __init__(self, ①
input_dim, ①
output_dim, ①
hidden_dims=(32,32),
activation_fc=F.relu):
super(FCDuelingQ, self).__init__()
self.activation_fc = activation_fc
self.input_layer = nn.Linear(input_dim,
hidden_dims[0]) ②
self.hidden_layers = nn.ModuleList()
for i in range(len(hidden_dims)-1): ③
hidden_layer = nn.Linear(
hidden_dims[i], hidden_dims[i+1])
self.hidden_layers.append(hidden_layer)
self.value_output = nn.Linear(hidden_dims[-1], 1) ④
self.advantage_output = nn.Linear(
hidden_dims[-1], output_dim)
① 对弈网络与常规网络类似。我们需要为输入层和输出层的节点数量、隐藏层的形状以及激活函数设置变量,就像我们之前做的那样。② 接下来,我们创建输入层并将其“连接”到第一个隐藏层。在这里,input_dim 变量是输入节点的数量,而 hidden_dims[0]是第一个隐藏层的节点数量。nn.Linear 创建了一个具有输入和输出的层。③ 我们通过创建隐藏层来创建隐藏层,这些层由 hidden_dims 变量定义。例如,值为(64, 32, 16)将创建一个具有 64 个输入节点和 32 个输出节点的层,然后是一个具有 32 个输入节点和 16 个输出节点的层。④ 最后,我们构建两个输出层,它们都“连接”到最后的隐藏层。value_output 有一个节点输出,而 advantage_output 有 output_dim 个节点。在 cart-pole 环境中,这个数字是两个。|
重建动作值函数
首先,让我澄清一下,对抗架构的动机是创建一个新的网络,这个网络在改进先前网络的同时,无需改变底层控制方法。我们需要的是不破坏性的变化,并且与先前的方法兼容。我们希望替换神经网络,然后完成它。
为了实现这一点,我们需要找到一种方法来聚合网络输出的两个结果,并重建动作值函数 Q(s, a),以便任何先前的方法都可以使用对抗网络模型。这样,当使用对抗架构与 DDQN 代理结合时,我们就创建了对抗 DDQN 代理。一个对抗网络和 DQN 代理将构成对抗 DQN 代理。
| 展示数学公式:对抗架构聚合方程 | |
|---|---|
![]() |
但是,我们如何将输出合并呢?有些人可能会想,把它们加起来,对吧?我的意思是,毕竟这是我提供的定义。尽管如此,你们中的一些人可能已经注意到,仅凭 Q(s, a) 无法唯一地恢复 V(s) 和 A(s, a)。想想看;如果你给 V(s) 加上+10,并从 A(s, a) 中减去它,你将得到相同的 Q(s, a),但 V(s) 和 A(s, a) 的值却不同。
在对抗架构中,我们通过从聚合的动作值函数 Q(s, a) 估计中减去优势的平均值来解决这个问题的。这样做会将 V(s) 和 A(s, a) 偏移一个常数,但也会稳定优化过程。
当估计值有常数偏差时,它们不会改变 A(s, a) 的相对排名,因此 Q(s, a) 也具有适当的排名。所有这些,同时仍然使用相同的控制算法。这是一个大胜利。
| 我会说 Python:对抗网络的正向传播 | |
|---|---|
class FCDuelingQ(nn.Module):
<...> ①
def forward(self, state):
x = state
if not isinstance(x, torch.Tensor):
x = torch.tensor(x, ②
device=self.device,
dtype=torch.float32)
x = x.unsqueeze(0)
x = self.activation_fc(self.input_layer(x)) ③
for hidden_layer in self.hidden_layers: ④
x = self.activation_fc(hidden_layer(x))
a = self.advantage_output(x) ⑤
v = self.value_output(x)
v = v.expand_as(a)
q = v + a - a.mean(1, keepdim=True).expand_as(a) ⑥
return q
① 注意,这与之前相同。为了简洁,我移除了构建网络的代码。② 在前向传递中,我们首先确保网络的输入,即‘状态’,是预期的类型和形状。我们这样做是因为有时我们输入的是状态批次(训练),有时是单个状态(交互)。有时这些是 NumPy 向量。③ 在这一点上,我们已经将输入变量x(再次是单个或状态批次)准备成网络所期望的。我们将变量x传递到输入层,记住,输入层接受 input_dim 变量并输出 hidden_dim[0]变量;这些将随后通过激活函数。④ 我们使用那个输出作为我们第一个隐藏层的输入。我们将变量x(你可以将其视为脉冲波从输入到网络输出的当前状态)依次传递到每个隐藏层和激活函数。⑤ x现在包含了来自最后一个隐藏层及其相应激活的值。我们使用这些值作为优势输出和价值输出层的输入。由于v是一个将添加到a的单个值,我们将其扩展。⑥ 最后,我们将v和a相加,并从其中减去a的均值。这就是我们的Q(s, .)估计,包含了所有状态下所有动作的估计。|
持续更新目标网络
目前,我们的代理正在使用一个目标网络,在它与在线网络同步时,可能需要经过几步更新才能获得较大的权重更新。在 CartPole 环境中,这仅仅是相隔~15 步,但在更复杂的环境中,这个数字可能会上升到数万。

完整的目标网络更新
这种方法至少存在两个问题。一方面,我们在几步内冻结了权重,并使用逐渐增加的过时数据来计算估计值。当我们达到更新周期的末尾时,估计值对网络训练进展没有帮助的可能性更高。另一方面,网络每隔一段时间就会进行一次巨大的更新。进行一次大的更新可能会一次性改变损失函数的全局景观。这种更新风格似乎既过于保守又过于激进,如果可能的话。
我们遇到这个问题是因为我们希望我们的网络不要移动得太快,从而避免产生不稳定性,同时我们还想保留那些理想的特性。但是,你能想到其他我们可以以平滑方式实现类似目标的方法吗?比如,减缓目标网络的更新速度,而不是将其冻结?
我们可以做到。这种技术被称为 Polyak 平均,它包括在每一步将在线网络权重混合到目标网络中。另一种看待它的方法是,每一步,我们创建一个新的目标网络,该网络由大量目标网络权重和少量在线网络权重组成。我们每步向网络添加约 1%的新信息。因此,网络总是落后,但差距要小得多。此外,我们现在可以在每一步更新网络。
| 展示数学 Polyak 平均 | |
|---|---|
![]() |
|
| 我会说 Python 混合目标和在线网络权重 | |
class DuelingDDQN():
<...> ①
def update_network(self, tau=None):
tau = self.tau if tau is None else tau
for target, online in zip( ②③
self.target_model.parameters(),
self.online_model.parameters()):
target_ratio = (1.0 - self.tau) * target.data
online_ratio = self.tau * online.data ④
mixed_weights = target_ratio + online_ratio
target.data.copy_(mixed_weights) ⑤
① 这与相同的对抗式 DDQN 类相同,但为了简洁起见,删除了大部分代码。② tau 是一个表示将混合到目标网络中的在线网络权重的比例的变量。值为 1 相当于完全更新。③ zip 接受可迭代对象并返回一个元组的迭代器。④ 现在,我们计算从目标和在线权重中取出的比率。⑤ 最后,我们混合权重并将新值复制到目标网络中。 |
对抗式网络带来了什么?
行动优势在您自己看到的情况下,当您有许多类似价值的行动时尤其有用。从技术上来说,对抗式架构提高了策略评估,尤其是在面对许多具有相似价值的行动时。使用对抗式网络,我们的智能体可以更快、更准确地比较具有相似价值的行动,这在小车-杆环境中是有用的。
函数逼近器,如神经网络,都有误差;这是预期的。在我们之前使用的架构的网络中,这些误差对于所有状态-动作对可能是不同的,因为它们都是分开的。但是,鉴于状态值函数是动作值函数中在状态中所有动作都共有的部分,通过使用对抗式架构,我们减少了函数误差和误差方差。这是因为现在具有最大显著程度的相似价值动作的组成部分(状态值函数 V(s))对所有动作都是相同的。
如果对弈网络在我们的智能体中提高了策略评估,那么当左右动作几乎具有相同的价值时,一个完全训练好的对弈 DDQN 智能体应该比 DDQN 表现更好。我通过收集 DDQN 和对弈 DDQN 智能体的 100 个回合的状态来运行了一个实验。我的直觉告诉我,如果一个智能体在评估类似价值的动作方面比另一个智能体更好,那么更好的智能体在轨迹上的范围应该更小。这是因为更好的智能体应该学会在杆子完全直立时区分左右移动,警告!我没有做消融研究,但我的手波实验的结果表明,对弈 DDQN 智能体确实能够更好地评估这些状态。

完全训练的 cart-pole 智能体访问的状态空间
| 在细节中 The dueling double deep Q-network (dueling DDQN)算法 |
|---|
| | 对弈 DDQN 几乎与 DDQN 和 DQN 相同,只有一些小的调整。我的意图是在保持算法差异最小化的同时,仍然向您展示可以做出的许多不同改进。我确信,仅通过稍微调整几个超参数就可以对许多这些算法的性能产生重大影响;因此,我没有优化智能体。话虽如此,现在让我来谈谈仍然与之前相同的事情:
-
网络输出动作值函数 Q(s, a; θ)。
-
优化动作值函数以逼近最优动作值函数 q**(s, a*)。
-
使用离线 TD 目标(r + gammamax_a’Q(s’,a’; θ*)来评估策略。
-
使用可调整的 Huber 损失,但仍然将 max_gradient_norm 变量设置为 float(‘inf’)。因此,我们使用 MSE。
-
使用 RMSprop 作为我们的优化器,学习率为 0.0007。
-
一个指数衰减的 epsilon-greedy 策略(在约 20,000 步内从 1.0 衰减到 0.3)来改进策略。
-
用于评估步骤的贪婪动作选择策略。
-
一个具有最小样本数 320、最大样本数 50,000 和批次大小 64 的重放缓冲区。
我们替换了
-
神经网络架构。我们现在使用状态-值-输出对弈网络架构(节点:4, 512, 128, 1; 2, 2)。
-
之前用于冻结 15 步并完全更新的目标网络现在使用 Polyak 平均:每一步,我们将 0.1 的在线网络和 0.9 的目标网络混合形成新的目标网络权重。
对弈 DDQN 与 DDQN 完全相同的算法,但网络不同:
-
收集经验:(S[t], A[t], R[t+1], S[t+1], D[t+1]),并将其插入重放缓冲区。
-
从缓冲区中拉取一批并计算离线 TD 目标:R + gammamax_a’Q(s’,a’; θ*),使用双学习。
-
使用 MSE 和 RMSprop 拟合动作值函数 Q(s,a; θ)。
一个值得注意的酷点是,所有这些改进都像是乐高™积木,供你发挥创意。也许你想尝试对抗式 DQN,但不使用双重学习;也许你想让 Huber 损失函数剪辑梯度;或者也许你喜欢 Polyak 平均化,每 5 个时间步混合 50:50。这取决于你!希望我组织代码的方式能给你尝试新事物的自由。 |
| 累计优势对抗式 DDQN 比所有先前方法都更有效率 | |
|---|---|
对抗式 DDQN 和 DDQN 在 Cart-Pole 环境中表现相似。对抗式 DDQN 在数据效率上略高。DDQN 需要通过环境的样本数量高于对抗式 DDQN。然而,对抗式 DDQN 的运行时间略长于 DDQN。![]() |
PER:优先回放有意义经验
在本节中,我们介绍了一种更智能的经验回放技术。目标是分配资源给那些具有最大学习潜力的经验元组。优先经验回放(PER)是一个专门用于此的回放缓冲区。
更智能的回放经验方式
目前,我们的智能体从回放缓冲区中均匀随机地抽取经验元组。从数学上讲,这似乎是正确的,确实如此。但直观上,这似乎是一种回放经验的次优方式。均匀随机回放将资源分配给了不重要的经验。感觉上,我们的智能体花费时间和计算能力“学习”那些对当前智能体状态毫无帮助的事情是不合适的。
但,在这里我们要小心:虽然均匀随机并不足够好,但人类直觉在确定更好的学习信号时可能也不太有效。当我第一次实现优先回放缓冲区,在阅读 PER 论文之前,我的第一个想法是,“嗯,我想让智能体获得尽可能高的累积折现奖励;我应该只让它回放高奖励的经验。”是的,这没有奏效。然后我意识到智能体也需要负面经验,所以我想到,“啊!我应该让智能体回放奖励幅度最高的经验!此外,我喜欢使用那个‘abs’函数!”但这也没有奏效。你能想出为什么这些实验没有奏效吗?如果我想让智能体学会体验奖励状态,我应该让它回放最多的那些,这样它才能学会到达那里。对吧?
| 米格尔的类比人类直觉和对幸福的执着追求 | |
|---|---|
| 我爱我女儿。我非常爱她。事实上,如此之爱,以至于我希望她只体验生活中美好的事物。不,说真的,如果你是父母,你就知道我的意思。我发现她非常喜欢巧克力,或者像她说的那样,“一大堆”,所以我开始时不时地给她一些糖果。然后,大多数时候都是这样。但是,然后当她觉得我不应该给她糖果时,她开始生我的气。太多的高奖励体验,你认为吗?没错!代理(甚至可能是人类)需要经常提醒他们好与坏的经验,但他们也需要低强度奖励的平凡经验。最终,这些经验都没有给你带来最多的学习,这正是我们追求的目标。这不是反直觉吗? |
那么,“重要”经验的好度量是什么?
我们所寻找的是从意外价值、令人惊讶的经验、我们认为应该这样评价的经验中学习,结果却评价了那样。这更有意义;这些经验将现实带给我们。我们有一个对世界的看法,我们预测结果,当期望与现实之间的差异很大时,我们知道我们需要从中学到一些东西。
在强化学习中,这种惊喜的度量由 TD 错误给出!好吧,技术上讲,是 绝对 TD 错误。TD 错误为我们提供了代理当前估计值和目标值之间的差异。当前的估计值表示代理认为它将以某种特定方式行动所能获得的价值。目标值建议对相同的状态-动作对的新估计,这可以看作是一种现实检查。这些值之间的绝对差异表明我们偏离了多少,这种体验有多么出乎意料,我们获得了多少新信息,这使得它成为学习机会的良好指标。
| 展示数学公式 The absolute TD error is the priority | |
|---|---|
![]() |
TD 错误并不是最高学习机会的完美指标,但可能是最好的合理代理。实际上,学习最多最好的标准在网络的内部,隐藏在参数更新之后。但是,每次时间步都计算回放缓冲区中所有经验的梯度似乎不太实际。TD 错误的好处是计算它的机制已经存在,当然,TD 错误仍然是优先回放经验的良好信号。
通过 TD 错误进行贪婪优先级排序
让我们假设我们使用 TD 错误来优先处理经验如下:
-
在状态 s 中采取行动 a,并接收一个新的状态 s'),一个奖励 r,以及一个完成标志 d。
-
查询网络以获取当前状态 Q(s, a; θ) 的估计。
-
为该经验计算一个新的目标值,即 target = r + gammamax_a'Q* (s',a'; θ)。
-
计算绝对TD误差为atd_err = abs(Q(s, a; θ) – target)。
-
将经验作为一个元组(s, a, r, s', d, atd_err)插入到重放缓冲区中。
-
按照排序后的atd_err从缓冲区中提取最优先的经验。
-
使用这些经验进行训练,并重复。
这种方法存在多个问题,但让我们逐一解决。首先,我们正在计算TD误差两次:我们在将其插入缓冲区之前计算TD误差,但然后在用网络训练时再次计算。此外,我们忽略了TD误差会随着网络的变化而变化的事实,因为它们是使用网络计算的。但是,解决方案不能是每次时间步更新所有TD误差。这根本不划算。
解决这两个问题的方法是为用于更新网络(重放的经验)的经验更新TD误差,并在缓冲区中插入具有最大幅度TD误差的新经验,以确保它们至少被重放一次。
然而,从这个解决方案中,其他问题也随之产生。首先,第一次更新中的TD误差为零意味着经验很可能永远不会再次被重放。其次,当使用函数逼近器时,误差缩小得很慢,这意味着更新主要集中在重放缓冲区的小子集。最后,TD误差是嘈杂的。
由于这些原因,我们需要一个基于TD误差的采样策略,但必须是随机的,而不是贪婪的。如果我们随机采样优先经验,我们就可以同时确保所有经验都有机会被重放,并且采样经验的概率在绝对TD误差上是单调的。
| 简化 TD 误差、优先级和概率 | |
|---|---|
| 本页最重要的启示是 TD 误差不足以解决问题;我们将使用 TD 误差来计算优先级,然后从优先级计算概率。 |
随机采样优先经验
让我深入探讨一下为什么我们需要随机优先级。在高度随机的环境中,基于TD误差贪婪采样的经验可能会导致我们走向噪声所引导的方向。
TD 误差取决于一步奖励和下一个状态的动作值函数,这两者都可以是非常随机的。高度随机的环境可以有更高的方差TD误差。在这样的环境中,如果我们让我们的智能体严格遵循TD误差,我们可能会陷入麻烦。我们不希望我们的智能体对意外情况产生固定观念;那不是我们的目标。TD误差中的另一个噪声来源是神经网络。使用高度非线性的函数逼近器也导致了TD误差中的噪声,尤其是在训练早期,错误最高的时候。如果我们仅仅基于TD误差贪婪地采样,大部分的训练时间将花费在那些可能具有不准确的大幅度TD误差的经验上。
| 简化优先经验随机采样 | |
|---|---|
| TD 误差是有噪声且缓慢缩小的。我们不希望因为噪声而停止重放那些由于噪声而得到零 TD 误差值的经验。我们不希望陷入由于噪声而得到显著 TD 误差的噪声经验。而且,我们也不希望专注于初始 TD 误差高的经验。 | |
| 0001 | 一点历史介绍优先经验重放缓冲区 |
| 2015 年,与对抗网络论文同时由谷歌 DeepMind 团队引入的“优先经验重放”论文。汤姆·沙乌尔(Tom Schaul),谷歌 DeepMind 的高级研究科学家,是 PER 论文的主要作者。汤姆于 2011 年从慕尼黑工业大学获得博士学位。在纽约大学担任博士后两年后,汤姆加入了 DeepMind Technologies,六个月后,该公司被谷歌收购,并发展成为今天的谷歌 DeepMind。汤姆是 PyBrain 框架的核心开发者,这是一个用于 Python 的模块化机器学习库。PyBrain 可能是最早实现机器学习、强化学习和黑盒优化算法的框架之一。他也是 PyVGDL 的核心开发者,这是一个基于 pygame 的高级视频游戏描述语言。 |
比例优先级
让我们根据TD误差计算缓冲区中每个样本的优先级。一个这样做的方法是按比例根据它们的绝对TD误差来采样经验。我们可以使用每个经验的绝对TD误差并添加一个小的常数 epsilon,以确保零TD误差样本仍有被重放的机会。
| 展示数学比例优先级 | |
|---|---|
![]() |
我们通过将优先级值指数化为 alpha(一个介于零和一之间的超参数)来缩放这个优先级值。这允许我们在均匀采样和优先级采样之间进行插值。它允许我们执行我们讨论过的随机优先级。
当 alpha 为零时,所有值都变为 1,因此,具有相等的优先级。当 alpha 为一时,所有值保持与绝对 TD 错误相同;因此,优先级与绝对 TD 错误成比例——介于两者之间的值混合了两种采样策略。
这些缩放后的优先级只有通过将它们的值除以值的总和才能转换为实际概率。然后,我们可以使用这些概率从重放缓冲区中抽取样本。
| 将优先级转换为概率 | |
|---|---|
![]() |
基于排名的优先级
比例优先级方法的一个问题是它对异常值敏感。这意味着具有比其他经验更高的 TD 错误(无论是事实还是噪声)的经验被采样的频率更高,这可能是不可期望的副作用。
在计算优先级时,一种略微不同的经验优先级方法是通过使用按绝对 TD 错误排序的样本的排名来采样。
这里的排名意味着按绝对 TD 错误降序排序的样本的位置——没有其他含义。例如,基于排名的优先级使得具有最高绝对 TD 错误的经验排名为 1,第二名为 2,依此类推。
| 展示数学计算 | |
|---|---|
![]() |
在我们按 TD 错误对它们进行排名后,我们计算它们的优先级为排名的倒数。同样,在计算优先级时,我们通过使用 alpha(与比例策略相同)对优先级进行缩放来继续进行。然后,我们像以前一样从这些优先级计算实际概率,也进行归一化,使得总和为 1。
| 简化优先级排序 | |
|---|---|
| 虽然比例优先级使用绝对 TD 错误和一个小常数来包含零 TD 错误经验,但基于排名的优先级使用按绝对 TD 错误降序排序的样本的排名的倒数。两种优先级策略随后以相同的方式从优先级创建概率。 |
优先级偏差
使用一个分布来估计另一个分布会引入估计偏差。因为我们根据这些概率、优先级和 TD 错误进行采样,所以我们需要考虑这一点。
首先,让我更深入地解释一下这个问题。更新的分布必须与其期望值来自相同的分布。当我们更新状态 s 和动作 a 的动作值函数时,我们必须意识到我们总是使用目标值进行更新。
目标是期望值的样本。这意味着下一步的奖励和状态可能是随机的;在状态 s 中采取动作 a 时,可能会有许多可能的不同的奖励和状态。
如果我们忽略这个事实,并且比在期望中出现得更频繁地更新单个样本,我们就会对这个值产生偏差。这个问题在训练结束时尤其有影响,那时我们的方法接近收敛。
要减轻这种偏差,可以使用一种称为加权重要性采样的技术。它包括通过每个样本的概率来计算权重,然后对TD误差进行缩放。
加权重要性采样所做的就是改变更新的幅度,使得样本看起来来自均匀分布。
| 展示数学公式:加权重要性采样权重计算 | |
|---|---|
![]() |
为了有效地使用优先回放缓冲区进行加权重要性采样,我们添加了一个方便的超参数,beta,它允许我们调整校正的程度。当 beta 为零时,没有校正;当 beta 为一时,进行完全的偏差校正。
此外,我们还想通过它们的最大值来归一化权重,使得最大权重变为 1,其他所有权重都按比例缩小TD误差。这样,我们就能防止TD误差增长过大,并保持训练的稳定性。
这些重要性采样权重用于损失函数中。在 PER 中,我们不是直接在梯度更新中使用TD误差,而是将它们乘以重要性采样权重,并将所有TD误差缩小以补偿分布的不匹配。
| 展示数学公式:对抗性 DDQN 与 PER 梯度更新 | |
|---|---|
![]() |
|
| 我会说 Python:优先回放缓冲区 1/2 | |
class PrioritizedReplayBuffer():
<...>
def store(self, sample):
priority = 1.0
if self.n_entries > 0: ①
priority = self.memory[
:self.n_entries,
self.td_error_index].max()
self.memory[self.next_index,
self.td_error_index] = priority ②
self.memory[self.next_index,
self.sample_index] = np.array(sample)
self.n_entries = min(self.n_entries + 1, ③
self.max_samples)
self.next_index += 1 ④
self.next_index = self.next_index % self.max_samples
def update(self, idxs, td_errors):
self.memory[idxs, ⑤
self.td_error_index] = np.abs(td_errors)
if self.rank_based:
sorted_arg = self.memory[:self.n_entries, ⑥
self.td_error_index].argsort()[::-1]
self.memory[:self.n_entries] = self.memory[
sorted_arg]
① PrioritizedReplayBuffer 类的存储函数很简单。我们首先做的事情是计算样本的优先级。记住,我们将优先级设置为最大值。下面的代码显示了默认值为 1;然后它被覆盖为最大值。② 拥有优先级和样本(经验)后,我们将它插入到内存中。③ 我们增加表示缓冲区中经验数量变量的值,但我们需要确保缓冲区不会超过 max_samples。④ 下一个变量表示下一个经验将被插入的索引。这个变量从 max_samples 循环回 0,然后再次上升。⑤ 更新函数接受一个经验 ID 数组和新TD误差值。然后,我们将绝对TD误差插入到正确的位置。⑥ 如果我们进行基于排名的采样,我们还会对数组进行排序。请注意,数组在实现优先回放缓冲区时不是最优的,主要是因为这个依赖于样本数量的排序。这对性能不好。|
| 我会说 Python:优先回放缓冲区 2/2 | |
|---|---|
class PrioritizedReplayBuffer():
<...>
def sample(self, batch_size=None):
batch_size = self.batch_size if batch_size == None \
else batch_size
self._update_beta() ①
entries = self.memory[:self.n_entries]
if self.rank_based: ②
priorities = 1/(np.arange(self.n_entries) + 1)
else: # proportional
priorities = entries[:, self.td_error_index] + EPS
scaled_priorities = priorities**self.alpha
pri_sum = np.sum(scaled_priorities) ③
probs = np.array(scaled_priorities/pri_sum,
dtype=np.float64)
weights = (self.n_entries * probs)**-self.beta ④
normalized_weights = weights/weights.max() ⑤
idxs = np.random.choice(self.n_entries, ⑥
batch_size, replace=False, p=probs)
samples = np.array([entries[idx] for idx in idxs]) ⑦
samples_stacks = [np.vstack(batch_type) for \ ⑧
batch_type in np.vstack(samples[:, self.sample_index]).T] ⑧
idxs_stack = np.vstack(idxs) ⑧
weights_stack = np.vstack(normalized_weights[idxs]) ⑧
return idxs_stack, weights_stack, samples_stacks ⑧
① 计算 batch_size、衰减‘beta’并从条目中删除零行。② 现在,我们计算优先级。如果是基于排名的优先级,则是排名的倒数(我们在更新函数中已对这些进行排序)。比例是绝对 TD 误差加上一个小的常数 epsilon,以避免优先级为零。③ 现在,我们将优先级转换为概率。首先,我们与均匀分布混合,然后是 probs。④ 然后,我们使用概率计算重要性采样权重。⑤ 归一化权重。最大权重将是 1。⑥ 我们使用概率从缓冲区中采样经验的索引。⑦ 从缓冲区中获取样本。⑧ 最后,通过 ids、权重和经验元组堆叠样本,并返回它们。|
| 我会说 Python 优先级重放缓冲区损失函数 1/2 | |
|---|---|
class PER():
<...> ①
def optimize_model(self, experiences):
idxs, weights, \ ②
(states, actions, rewards,
next_states, is_terminals) = experiences
<...>
argmax_a_q_sp = self.online_model(next_states).max(1)[1]
q_sp = self.target_model(next_states).detach()
max_a_q_sp = q_sp[np.arange(batch_size), argmax_a_q_sp]
max_a_q_sp = max_a_q_sp.unsqueeze(1)
max_a_q_sp *= (1 - is_terminals)
target_q_sa = rewards + (self.gamma * max_a_q_sp) ③
q_sa = self.online_model(states).gather(1, actions) ④
td_error = q_sa - target_q_sa ⑤
value_loss = (weights * td_error).pow(2).mul(0.5).mean() ⑥
self.value_optimizer.zero_grad()
value_loss.backward()
torch.nn.utils.clip_grad_norm_( ⑦
self.online_model.parameters(),
self.max_gradient_norm)
self.value_optimizer.step()
priorities = np.abs(td_error.detach().cpu().numpy()) ⑧
self.replay_buffer.update(idxs, priorities)
① 正如我在其他场合所指出的,这是代码的一部分。这些是我认为值得在这里展示的代码片段。② 注意现在我们有了与经验一起到来的 ids 和权重。③ 我们计算目标值,就像之前一样。④ 我们查询当前的估计:没有新内容。⑤ 我们以同样的方式计算 TD 误差。⑥ 但是,现在损失函数有通过权重缩放的 TD 误差。⑦ 我们继续之前的优化。⑧ 我们使用绝对 TD 误差更新重放批次的优先级。|
| 我会说 Python 优先级重放缓冲区损失函数 2/2 | |
|---|---|
class PER():
<...>
def train(self, make_env_fn, make_env_kargs, seed, gamma, ①
max_minutes, max_episodes, goal_mean_100_reward):
<...>
for episode in range(1, max_episodes + 1): ②
<...>
for step in count(): ③
state, is_terminal = \
self.interaction_step(state, env)
<...> ④
if len(self.replay_buffer) > min_samples:
experiences = self.replay_buffer.sample() ⑤
idxs, weights, samples = experiences ⑥
experiences = self.online_model.load( ⑥
samples)
experiences = (idxs, weights) + \ ⑦
(experiences,)
self.optimize_model(experiences) ⑧
if np.sum(self.episode_timestep) % \ ⑨
self.update_target_every_steps == 0: ⑨
self.update_network() ⑨
if is_terminal:
break
① 这是相同的 PER 类,但现在我们处于训练函数中。② 在剧集循环内。③ 在时间步循环内。④ 在训练时间中的每个时间步。⑤ 看看我们是怎样从缓冲区中提取经验的。⑥ 从经验中,我们提取 idxs、权重和经验元组。注意我们是如何将样本变量加载到 GPU 中的。⑦ 然后,我们再次堆叠变量。请注意,我们这样做只是为了将样本加载到 GPU 中,并使其准备好训练。⑧ 然后,我们优化模型(这是上一页中的函数)。⑨ 然后,一切照常进行。|
| 这就是细节双 DQN 与优先级重放缓冲区算法 |
|---|
| | 最后一次,我们改进了所有之前的价值型深度强化学习方法。这次,我们通过改进重放缓冲区来实现。正如你可以想象的那样,大多数超参数与之前的方法相同。让我们深入了解。这些是仍然与之前相同的事情:
-
网络输出动作值函数 Q(s, a; θ)。
-
我们使用状态-值输出对冲网络架构(节点:4, 512,128, 1; 2, 2)。
-
优化动作值函数以逼近最优动作值函数 q**(s, a*)。
-
使用离线策略 TD 目标(r + gammamax_a’Q(s’,a’; θ*)来评估策略。
-
使用可调整的 Huber 损失,将 max_gradient_norm 变量设置为 float(‘inf’)。因此,我们使用 MSE。
-
使用学习率为 0.0007 的 RMSprop 作为我们的优化器。
-
一种指数退化的ε-greedy 策略(从 1.0 到 0.3 大约需要 20,000 步)来改进策略。
-
用于评估步骤的贪婪动作选择策略。
-
一个目标网络,每次时间步更新时使用 Polyak 平均,τ(混合因子)为 0.1。
-
至少包含 320 个样本的回放缓冲区和 64 个批次的样本。
我们所做的改变:
-
使用加权重要性采样来调整 TD 误差(这会改变损失函数)。
-
使用具有比例优先级的优先回放缓冲区,最大样本数为 10,000,优先级(相对于均匀的优先级程度——1 是完全优先级)的α值为 0.6,β值(β的初始值,用于偏差校正——1 是完全校正)为 0.1,β退火率为 0.99992(在大约 30,000 个时间步长后完全退火)。
PER 与对抗 DDQN、DDQN 和 DQN 的基算法相同:
-
收集经验:(S[t], A[t], R[t+][1], S[t+][1], D[t+1]),并将其插入回放缓冲区。
-
从缓冲区中抽取一个批次,并使用双学习计算离策略 TD 目标:r + gammamax_a’Q(s’,a’; θ)。
-
使用 MSE 和 RMSprop 拟合动作价值函数Q(s,a; θ)。
-
调整回放缓冲区中的 TD 误差。
|
| 累计它 UPPER 进一步提高数据效率 | |
|---|---|
| 优先回放缓冲区使用的样本比之前任何方法都少。正如你在下面的图表中可以看到的,它甚至使事物看起来更加稳定。也许?! |
摘要
本章总结了基于价值的 DRL 方法的调查。在本章中,我们探讨了使基于价值的方法更数据高效的方法。你了解了对抗架构,以及它是如何通过将Q(s, a)分解为其两个组成部分:状态价值函数V(s)和动作优势函数A(s, a)来利用基于价值的 RL 的细微差别。这种分离使得每个用于更新网络的体验都能为状态价值函数V(s)的估计添加信息,这对于所有动作都是共同的。通过这样做,我们能够更快地得到正确的估计,从而降低样本复杂度。
你还研究了经验的优先级。你了解到 TD 误差是创建优先级的好标准,并且你可以从优先级中计算出概率。你了解到我们必须补偿我们估计的期望分布的变化。因此,我们使用重要性采样,这是一种纠正偏差的技术。
在过去的三章中,我们深入研究了基于价值的 DRL 领域。我们从简单的方法 NFQ 开始。然后,我们通过 DQN 和 DDQN 的改进使这项技术更加稳定。然后,我们通过对抗 DDQN 和 PER 使其更高效地采样。总的来说,我们有一个相当稳健的算法。但是,就像所有事情一样,基于价值的方法也有缺点。首先,它们对超参数敏感。这是众所周知的,但你应该亲自尝试;改变任何超参数。你可以找到更多不工作的值,而不是工作的值。其次,基于价值的方法假设它们与马尔可夫环境交互,即状态包含代理所需的所有信息。当我们远离自举和基于价值的方法时,这种假设就会消失。最后,自举、离线学习和函数逼近器的组合共同被称为“致命的三位一体”。虽然已知致命的三位一体会产生发散,但研究人员仍然不知道如何确切地防止它。
绝对不是在说基于价值的算法比我们在未来章节中探讨的方法差。那些方法也有它们自己的问题。基本的启示是,基于价值的深度强化学习方法众所周知会发散,这是它们的弱点。如何修复它仍然是一个研究问题,但合理的实用建议是使用目标网络、重放缓冲区、双重学习、足够小的学习率(但不是太小),也许还需要一点耐心。对此我感到抱歉;我并不是制定规则的人。
到目前为止,你
-
能够解决具有连续状态空间的强化学习问题
-
了解如何稳定基于价值的 DRL 代理
-
了解如何使基于价值的 DRL 代理更高效地采样
| 在自己的工作上努力并分享你的发现 |
|---|
| | 这里有一些关于如何将你所学的内容提升到下一个层次的想法。如果你愿意,与世界分享你的结果,并确保查看其他人所做的事情。这是一个双赢的局面,希望你能利用它。
-
#gdrl_ch10_tf01: 本章和上一章中使用的重放缓冲区对于小车-杆环境和其他低维环境来说是足够的。然而,你可能已经注意到,优先级缓冲区成为任何更复杂环境的瓶颈。尝试自己重写所有重放缓冲区代码以加快速度。现在不要查找他人的代码;尝试使重放缓冲区更快。在优先级缓冲区中,你可以看到瓶颈是样本的排序。找到使这部分更快的方法。
-
#gdrl_ch10_tf02: 当尝试解决高维环境,例如 Atari 游戏时,本章和上一章中的重放缓冲区代码变得极其缓慢,完全不实用。现在怎么办?研究其他人如何解决这个问题,这是优先级缓冲区的一个阻碍性问题。分享你的发现,并自己实现这些数据结构。深入了解它们,并创建一篇博客文章,详细解释使用它们的优点。
-
#gdrl_ch10_tf03: 在上一两章中,你已经学习了可以解决具有高维和连续状态空间问题的方法,但动作空间呢?这些算法一次只能选择一个动作,而且这些动作具有离散值,这似乎很无趣。但是等等,像 DQN 这样的方法只能解决大小为 1 的离散动作空间的问题吗?调查并告诉我们!
-
#gdrl_ch10_tf04: 在每一章中,我都在使用最后一个标签作为通用的标签。请随意使用这个标签来讨论任何与本章相关的工作。没有比你自己创造的任务更令人兴奋的作业了。确保分享你打算调查的内容和你的结果。
用你的发现写一条推文,@我 @mimoralea(我会转发),并使用列表中的特定标签,以帮助感兴趣的人找到你的结果。没有对错之分;你分享你的发现,并检查他人的发现。利用这个机会社交,做出贡献,让自己脱颖而出!我们正在等待你!以下是一条推文示例:“嘿,@mimoralea。我创建了一篇博客文章,列出了学习深度强化学习的资源列表。查看它吧: #gdrl_ch01_tf01”我会确保转发并帮助他人找到你的工作。|
11 策略梯度与 actor-critic 方法
在本章
-
你将了解一类可以直接优化其性能的深度强化学习方法,而无需价值函数。
-
你将学习如何使用价值函数使这些算法变得更好。
-
你将实现使用多个进程同时进行以实现非常快速学习的深度强化学习算法。
没有什么比逆境更好。每一次失败、每一次心碎、每一次损失,都包含着自己的种子,自己的教训,告诉你如何在下一次做得更好。
—— 马尔科姆·X 美国穆斯林牧师和人权活动家
在本书中,我们探讨了在价值函数的帮助下可以找到最优和近似最优策略的方法。然而,所有这些算法在学习价值函数时,我们需要的却是策略。
在本章中,我们探索光谱的另一端以及中间部分的内容。我们开始探讨直接优化策略的方法。这些方法被称为基于策略或策略梯度方法,它们参数化一个策略并调整它以最大化预期回报。
在介绍了基础策略梯度方法之后,我们探索了一类结合的方法,这些方法同时学习策略和价值函数。这些方法被称为 actor-critic,因为选择动作的策略可以被视为 actor,而评估策略的价值函数可以被视为 critic。actor-critic 方法在许多深度强化学习基准测试中通常比单独的基于值或策略梯度方法表现更好。了解这些方法可以使你应对更具挑战性的问题。
这些方法结合了你在前三章中学到的关于学习价值函数的知识,以及在本章第一部分学到的关于学习策略的知识。actor-critic 方法在多样化的深度强化学习基准测试中通常能取得最先进的性能。

基于策略、基于值和 actor-critic 方法
REINFORCE:基于结果的策略学习
在本节中,我们开始介绍基于策略的方法的使用动机,首先是一个介绍;然后我们讨论使用这类方法时可以期待的一些优点;最后,我们介绍了最简单的策略梯度算法,称为rEINFORCE。
策略梯度方法简介
我首先想强调的是,在策略梯度方法中,与基于值的方法不同,我们试图最大化性能目标。在基于值的方法中,主要关注的是学习评估策略。为此,目标是最小化预测值和目标值之间的损失。更具体地说,我们的目标是匹配给定策略的真实动作值函数,因此我们参数化了一个值函数,并最小化了预测值和目标值之间的均方误差。请注意,我们没有真正的目标值,而是使用蒙特卡洛方法中的实际回报或自举方法中的预测回报。
相反,在基于策略的方法中,目标是最大化参数化策略的性能,因此我们执行梯度上升(或对负性能执行常规梯度下降)。很明显,代理的性能是从初始状态期望的总折现奖励,这与从给定策略的所有初始状态的期望状态值函数是同一回事。
| 展示数学:基于值的方法与基于策略的方法的目标 | |
|---|---|
![]() |
|
| ŘŁ | 带有强化学习口音的基于值的方法与基于策略的方法与策略梯度方法与演员-评论家方法 |
| 基于值的方法:指的是学习值函数并仅学习值函数的算法。Q-learning、SARSA、DQN 等都是基于值的方法。基于策略的方法:指的是优化策略的广泛算法,包括如遗传算法这样的黑盒优化方法。策略梯度方法:指的是在参数化策略的性能梯度上解决优化问题的方法,你将在本章学习这些方法。演员-评论家方法:指的是学习策略和值函数的方法,主要是在值函数通过自举学习并用作随机策略梯度评分的情况下。你将在本章和下一章学习这些方法。 |
策略梯度方法的优势
学习参数化策略的主要优势是,策略现在可以是任何可学习的函数。在基于值的方法中,我们处理离散动作空间,主要是因为我们计算动作的最大值。在高维动作空间中,这个最大值可能过于昂贵。此外,在连续动作空间的情况下,基于值的方法受到严重限制。
另一方面,基于策略的方法可以更容易地学习随机策略,这反过来又具有多个额外的优点。首先,学习随机策略意味着在部分可观察环境中具有更好的性能。直觉是,因为我们可以学习任意动作的概率,所以智能体对马尔可夫假设的依赖性较低。例如,如果智能体无法区分几个状态及其发出的观察结果,最佳策略通常是按照特定概率随机行动。

学习随机策略可能帮助我们摆脱困境
有趣的是,尽管我们在学习随机策略,但没有任何东西阻止学习算法接近确定性策略。这与基于价值的 方法不同,在训练过程中,我们必须通过某种概率强制进行探索以确保最优性。在具有随机策略的基于策略的方法中,探索被嵌入到学习函数中,并且在训练过程中对于给定状态收敛到确定性策略是可能的。
学习随机策略的另一个优点是,与表示价值函数相比,函数逼近表示策略可能更加直接。有时价值函数包含的信息过多,对于真正需要的信息来说是不必要的。可能计算状态或状态-动作对的精确价值是复杂或不必要的。

学习策略可能是一个更容易、更易于推广的问题来解决
最后要提到的一个优点是,由于策略是用连续值参数化的,动作概率会随着学习参数的变化而平滑地变化。因此,基于策略的方法通常具有更好的收敛特性。正如你从之前的章节中记得的那样,基于价值的方法容易产生振荡甚至发散。其中一个原因是价值函数空间中微小的变化可能意味着动作空间中显著的变化。动作的显著差异可以产生全新的轨迹,因此造成不稳定性。
在基于价值的方法中,我们使用激进的算子来改变价值函数;我们对 Q 值估计取最大值。在基于策略的方法中,我们相反地跟随随机策略相对于梯度,这只会逐渐和平滑地改变动作。如果你直接跟随策略的梯度,你将保证收敛到至少一个局部最优解。
| 我会说 Python 随机策略的离散动作空间 1/2 | |
|---|---|
class FCDAP(nn.Module): ①
def __init__(self,
input_dim, ②
output_dim,
hidden_dims=(32,32),
init_std=1,
activation_fc=F.relu):
super(FCDAP, self).__init__()
self.activation_fc = activation_fc
self.input_layer = nn.Linear( ③
input_dim, hidden_dims[0])
self.hidden_layers = nn.ModuleList() ④
for i in range(len(hidden_dims)-1):
hidden_layer = nn.Linear(
hidden_dims[i], hidden_dims[i+1])
self.hidden_layers.append(hidden_layer)
self.output_layer = nn.Linear( ⑤
hidden_dims[-1], output_dim)
def forward(self, state): ⑥
x = state
if not isinstance(x, torch.Tensor): ⑦
x = torch.tensor(x, dtype=torch.float32)
x = x.unsqueeze(0)
x = self.activation_fc(self.input_layer(x)) ⑧
for hidden_layer in self.hidden_layers: ⑨
x = self.activation_fc(hidden_layer(x))
return self.output_layer(x) ⑩
① 这个类,FCDAP 代表全连接离散动作策略。② 参数允许你指定一个全连接架构、激活函数以及权重和偏差的最大幅度。③ __init__ 函数在输入和第一个隐藏层之间创建线性连接。④ 然后,它创建所有隐藏层之间的连接。⑤ 最后,它将最终隐藏层连接到输出节点,创建输出层。⑥ 这里我们有处理正向功能的方法。⑦ 首先,我们确保状态是我们期望的类型和形状,然后我们才能将其通过网络。⑧ 接着,我们将格式正确的状态传递到输入层,然后通过激活函数。⑨ 然后,我们将第一个激活的输出传递到一系列隐藏层和相应的激活函数。⑩ 最后,我们获得输出,即 logits,对动作的偏好。 |
| 我会说 Python 离散动作空间的随机策略 1/2 | |
|---|---|
return self.output_layer(x) ⑪
def full_pass(self, state): ⑫
logits = self.forward(state) ⑬
dist = torch.distributions.Categorical(logits=logits) ⑭
action = dist.sample() ⑭
logpa = dist.log_prob(action).unsqueeze(-1) ⑮
entropy = dist.entropy().unsqueeze(-1) ⑯
is_exploratory = action != np.argmax( \ ⑰
logits.detach().numpy())
return action.item(), is_exploratory.item(), \ ⑱
logpa, entropy
def select_action(self, state): ⑲
logits = self.forward(state)
dist = torch.distributions.Categorical(logits=logits)
action = dist.sample()
return action.item()
def select_greedy_action(self, state): ⑳
logits = self.forward(state)
return np.argmax(logits.detach().numpy())
⑪ 这一行重复了上一页的最后一行。⑫ 这里我们进行完整的正向传播。这是一个方便的函数,用于获取概率、动作以及训练所需的一切。⑬ 正向传播返回的是 logits,即对动作的偏好。⑭ 接下来,我们从概率分布中采样动作。⑮ 然后,计算该动作的对数概率并将其格式化为训练格式。⑯ 这里我们计算策略的熵。⑰ 在这里,为了统计,我们确定所选策略是探索性的还是不是。⑱ 最后,我们返回一个可以直接传递到环境中的动作,一个标志表示动作是否是探索性的,动作的对数概率以及策略的熵。⑲ 这是一个辅助函数,当我们只需要采样动作时使用。⑳ 这一个是为了根据策略选择贪婪动作。 |
直接学习策略
直接优化策略的主要优势是,嗯,这是正确的目标。我们学习一个直接优化价值函数的策略,而不学习价值函数,也不考虑环境的动态。这是如何可能的?让我来展示给你看。
| 展示数学推导策略梯度 | |
|---|---|
![]() |
降低策略梯度的方差
有一种方法可以计算策略梯度,而无需了解任何关于环境转移函数的信息。这个算法增加了轨迹中所有动作的 log 概率,与完整回报的优良程度成正比。换句话说,我们首先收集一个完整的轨迹并计算完整的折现回报,然后使用这个分数来加权轨迹中每个动作的对数概率:A[t][t],A[t+1],...,A[T–1]。

让我们只使用由动作产生的奖励
| 展示数学 Reducing the variance of the policy gradient | |
|---|---|
![]() |
|
| 0001 | 一点历史介绍 REINFORCE 算法 |
| Ronald J. Williams 于 1992 年在一篇题为“Simple Statistical Gradient-Following Algorithms for Connectionist Reinforcement Learning”的论文中介绍了 REINFORCE 算法系列。1986 年,他与 Geoffrey Hinton 等人合著了一篇名为“Learning representations by back-propagating errors”的论文,这引发了当时人工神经网络(ANN)研究的发展。 | |
| 我会说 PythonREINFORCE | |
class REINFORCE(): ①
<...>
def optimize_model(self):
T = len(self.rewards)
discounts = np.logspace(0, T, num=T, base=self.gamma, ②
endpoint=False)
returns = np.array( ③
[np.sum(discounts[:T-t] * self.rewards[t:]) \ ④
for t in range(T)])
<...>
policy_loss = -(discounts * returns * \ ⑤⑥
self.logpas).mean()
self.policy_optimizer.zero_grad()
policy_loss.backward() ⑦
self.policy_optimizer.step()
def interaction_step(self, state, env): ⑧
action, is_exploratory, logpa, _ = \
self.policy_model.full_pass(state)
new_state, reward, is_terminal, _ = env.step(action)
<...>
return new_state, is_terminal
class REINFORCE(): ⑨
<...>
def train(self, make_env_fn, make_env_kargs, seed, gamma, ⑩
max_minutes, max_episodes, goal_mean_100_reward):
for episode in range(1, max_episodes + 1): ⑪
state, is_terminal = env.reset(), False ⑫
<...> ⑫
self.logpas, self.rewards = [], [] ⑫
for step in count(): ⑬
state, is_terminal = \
self.interaction_step(state, env)⑭
if is_terminal:
break ⑭
self.optimize_model() ⑮
def evaluate(self, eval_policy_model, ⑯
eval_env, n_episodes=1,
greedy=True):
rs = []
for _ in range(n_episodes):
<...>
for _ in count():
if greedy:
a = eval_policy_model.\
select_greedy_action(s)
else:
a = eval_policy_model.select_action(s)
s, r, d, _ = eval_env.step(a)
<...>
return np.mean(rs), np.std(rs)
① 这就是 REINFORCE 算法。当你看到<...>时,这意味着为了简化,代码已被删除。请前往该章节的笔记本以获取完整的代码。② 首先,我们像所有蒙特卡洛方法一样计算折扣。具有这些参数的 logspace 函数返回每一步的伽玛序列;例如,[1, 0.99, 0.9801, ...]。③ 接下来,我们计算所有时间步的折扣回报总和。④ 为了强调,这是在每一时间步的回报,从时间步 0 的初始状态到终端 T-1 之前。⑤ 注意这里我们使用的是数学上正确的策略梯度更新,这并不是你通常能找到的。额外的折扣假设我们试图优化从初始状态期望的折扣回报,因此,在场景中较晚的回报会得到折扣。⑥ 这是策略损失;它是选择动作的对数概率,加权之后是选择该动作后获得的回报。注意,由于 PyTorch 默认使用梯度下降,并且我们希望最大化性能,所以我们使用性能的负平均值来翻转函数。把它看作是在性能上进行梯度上升。此外,我们考虑了折扣策略梯度,因此我们将回报乘以折扣。⑦ 在这三个步骤中,我们首先在优化器中将梯度置零,然后进行反向传播,然后沿着梯度的方向进行步进。⑧ 这个函数获取要传递给环境的动作以及所有训练所需的变量。⑨ 仍在探索 REINFORCE 类的功能⑩ 训练方法是训练代理的入口点。⑪ 我们首先通过循环遍历场景。⑫ 对于每个新的场景,我们初始化训练和统计所需的变量。⑬ 然后,对每个时间步执行以下操作。⑭ 首先,我们收集经验,直到达到终端状态。⑮ 然后,我们使用场景中所有时间步的批次运行一个优化步骤。⑯ 我还希望让你看到我在评估期间选择策略的方式。我并不是选择贪婪策略,而是从学习到的随机策略中进行采样。这里正确的做法取决于环境,但采样是安全的赌注。 |
VPG:学习一个值函数
你在上一个部分学到的 REINFORCE 算法在简单问题中表现良好,并且有收敛保证。但由于我们使用完整的蒙特卡洛回报来计算梯度,其方差是一个问题。在本节中,我们将讨论几种处理这种方差的方法,这些方法被称为纯策略梯度或带有基线的 REINFORCE。
进一步减少策略梯度的方差
REINFORCE 是一个原则性的算法,但它具有很高的方差。你可能还记得第五章中关于蒙特卡洛目标的讨论,但让我们再重申一遍。沿着轨迹随机事件的累积,包括从初始状态分布中采样的初始状态——转换函数概率,但现在在本章中是具有随机策略的——是动作选择添加到混合中的随机性。所有这些随机性都累积在回报中,使其成为一个高方差信号,难以解释。
减少方差的一种方法是用部分回报而不是完整回报来改变动作的对数概率。我们已经实现了这一改进。但另一个问题是动作对数概率会随着回报的比例而变化。这意味着,如果我们收到一个显著的正面回报,导致该回报的动作概率会大幅增加。而如果回报是显著的负值,那么概率会大幅降低。
然而,想象一个像小车-杆这样的环境,其中所有奖励和回报都是正的。为了准确地区分可接受的动作和最佳动作,我们需要大量的数据。否则,方差很难被抑制。如果我们能够不用噪声回报,而用某种允许我们在相同状态下区分动作值的东西,那就方便多了。还记得吗?
| 刷新我的记忆使用策略梯度方法中的估计优势 | |
|---|---|
![]() |
学习值函数
正如你在上一页看到的,我们可以通过使用动作优势函数的估计值来进一步减少策略梯度的方差,而不是实际回报。使用优势函数可以在一定程度上使分数围绕零中心;优于平均的动作有正分数,而低于平均的动作有负分数。前者会降低概率,而后者会增加概率。
我们将做的是确切如此。现在,让我们创建两个神经网络,一个用于学习策略,另一个用于学习状态值函数 V。然后,我们使用状态值函数和回报来计算优势函数的估计值,就像我们接下来看到的那样。

两个神经网络,一个用于策略,一个用于值函数
| ŘŁ | 带有强化学习口音的 REINFORCE、纯策略梯度、基线、演员-评论家 |
|---|---|
| 一些有先前 DRL 经验的人可能会想,这是所谓的“actor-critic”吗?它正在学习策略和值函数,所以看起来应该是这样的。不幸的是,这是那些“RL 口音”会混淆新手的那些概念之一。原因如下。首先,根据 RL 的一个创始人 Rich Sutton,策略梯度方法近似性能度量的梯度,无论它们是否学习近似值函数。然而,DRL 中最杰出的代表人物之一,Sutton 的前学生 David Silver,不同意这种说法。他说,基于策略的方法不会额外学习值函数,只有 actor-critic 方法才会。但是,Sutton 进一步解释说,只有使用自举学习值函数的方法才能被称为 actor-critic,因为自举会给值函数添加偏差,从而使其成为一个“critic”。我喜欢这种区分;因此,本书中介绍的 REINFORCE 和 VPG 不被视为 actor-critic 方法。但请注意术语,它并不一致。 |
鼓励探索
对策略梯度方法的一个基本改进是向损失函数中添加熵项。我们可以从许多不同的角度来解释熵,从从分布中采样可以获得的信息量到对集合进行排序的方式数。

我喜欢将熵想得很简单。均匀分布,即样本均匀分布,具有高熵,实际上是最高的。例如,如果你有两个样本,并且两者都有 50% 的概率被抽取,那么熵是两个样本集可能达到的最高值。如果你有四个样本,每个样本有 25% 的概率,熵是相同的,是四个样本集可能达到的最高值。相反,如果你有两个样本,一个有 100% 的概率,另一个有 0%,那么熵是最低的,总是零。在 PyTorch 中,使用自然对数来计算熵而不是二进制对数。这主要是因为自然对数使用欧拉数 e,使数学更“自然”。然而,从实际的角度来看,这并没有区别,效果是相同的。在有两个动作的 cart-pole 环境中,熵介于 0 和 0.6931 之间。
在策略梯度方法中使用熵的方法是将负加权熵添加到损失函数中,以鼓励动作分布均匀。这样,具有均匀分布动作的策略,即产生最高熵的策略,有助于最小化损失。另一方面,收敛到单个动作,即熵为零,不会减少损失。在这种情况下,智能体最好收敛到最优动作。
| 使用 VPG 的数学损失 | |
|---|---|
![]() |
|
| 我会说 Python 状态值函数神经网络模型 | |
class FCV(nn.Module): ①
def __init__(self,
input_dim,
hidden_dims=(32,32), ②
activation_fc=F.relu):
super(FCV, self).__init__()
self.activation_fc = activation_fc
self.input_layer = nn.Linear(input_dim, ③
hidden_dims[0])
self.hidden_layers = nn.ModuleList()
for i in range(len(hidden_dims)-1):
hidden_layer = nn.Linear( ④
hidden_dims[i], hidden_dims[i+1])
self.hidden_layers.append(hidden_layer)
self.output_layer = nn.Linear( ⑤
hidden_dims[-1], 1) ⑤
def forward(self, state): ⑥
x = state
if not isinstance(x, torch.Tensor):
x = torch.tensor(x, dtype=torch.float32) ⑦
x = x.unsqueeze(0)
x = self.activation_fc(self.input_layer(x))
for hidden_layer in self.hidden_layers: ⑧
x = self.activation_fc(hidden_layer(x))
return self.output_layer(x) ⑨
① 这是状态价值函数神经网络。它与过去我们使用的 Q 函数网络类似。② 注意我为你留下了方便的超参数,你可以随意调整。③ 在这里,我们在输入节点和第一个隐藏层之间创建线性连接。④ 在这里,我们在隐藏层之间创建连接。⑤ 在这里,我们将最后一个隐藏层连接到输出层,输出层只有一个节点,表示状态的价值。⑥ 这是前向传递函数。⑦ 这是按照我们期望的格式化输入。⑧ 进行完整的正向传递...⑨ ...并返回状态的价值。 |
| 我会说 PythonVanilla 策略梯度,即带有基线的 REINFORCE | |
|---|---|
class VPG(): ①
<...> ①
def optimize_model(self):
T = len(self.rewards)
discounts = np.logspace(0, T, num=T, base=self.gamma,
endpoint=False)
returns = np.array( ②
[np.sum(discounts[:T-t] * self.rewards[t:]) for t in range(T)]) ③
value_error = returns - self.values
policy_loss = -( ④
discounts * value_error.detach() * self.logpas).mean()
entropy_loss = -self.entropies.mean() ⑤
loss = policy_loss + \
self.entropy_loss_weight * entropy_loss
self.policy_optimizer.zero_grad()
loss.backward() ⑥
torch.nn.utils.clip_grad_norm_(
self.policy_model.parameters(),
self.policy_model_max_grad_norm)
self.policy_optimizer.step() ⑦
value_loss = value_error.pow(2).mul(0.5).mean()
self.value_optimizer.zero_grad()
value_loss.backward()
torch.nn.utils.clip_grad_norm_( ⑧
self.value_model.parameters(),
self.value_model_max_grad_norm)
self.value_optimizer.step()
① 这是 VPG 算法。我移除了很多代码,所以如果你想要完整的实现,请查看该章节的笔记本。② 这是一个计算从时间步 0 到 T 的折扣奖励总和的非常方便的方法。③ 我想强调的是,这个循环正在遍历所有步骤从 0,然后 1,2,3 一直到最后的状态T,并计算从该状态开始的回报,即从时间步 t 到最终状态T的折扣奖励总和。④ 首先,计算价值误差;然后使用它来评分动作的对数概率。然后,将这些概率折扣以与折扣策略梯度兼容。然后,使用负平均值。⑤ 计算熵,并将其添加到损失中。⑥ 现在,我们优化策略。将优化器置零,进行反向传播,然后(如果需要)裁剪梯度。⑦ 我们移动优化器。⑧ 最后,我们优化价值函数神经网络。 |
A3C:并行策略更新
VPG 对于简单问题来说是一个相当稳健的方法;它大部分是无偏的,因为它使用无偏的目标来学习策略和价值函数。也就是说,它使用蒙特卡洛回报,这是在环境中直接体验到的完整实际回报,没有任何自举。整个算法中唯一的偏差是因为我们使用了函数逼近,这本身是有偏差的,但由于 ANN 只是用作基线以减少实际回报的方差,因此引入的偏差很小,如果有的话。
然而,避免有偏见的算法是必要的。通常,为了减少方差,我们会引入偏差。一种名为异步优势演员-评论家(A3C)的算法采取了一些措施来进一步减少方差。首先,它使用带有自举的n-步回报来学习策略和价值函数,其次,它使用并发演员并行生成一组广泛的经验样本。让我们深入了解细节。
使用演员工作者
在 DRL 算法中,方差的主要来源之一是在线样本的相关性和非平稳性。在基于价值的方法中,我们使用重放缓冲区来均匀地采样迷你批次的数据,这些数据大部分是独立且同分布的。不幸的是,使用这种经验重放方案来减少方差仅限于离策略方法,因为在线策略代理不能重用先前策略生成的数据。换句话说,每个优化步骤都需要一个新的在线经验批次。
与使用重放缓冲区不同,在策略方法中,例如我们在本章中学习的策略梯度算法,我们可以让多个工作员并行生成经验,并异步更新策略和价值函数。让多个工作员在环境的多个实例上并行生成经验,可以解耦用于训练的数据,并减少算法的方差。

异步模型更新
| 我会说 PythonA3C 工作逻辑 1/2 | |
|---|---|
class A3C(): ①
<...> ②
def work(self, rank): ③
local_seed = self.seed + rank ④
env = self.make_env_fn(
**self.make_env_kargs, ⑤
seed=local_seed)
torch.manual_seed(local_seed)
np.random.seed(local_seed) ⑥
random.seed(local_seed)
nS = env.observation_space.shape[0] ⑦
nA = env.action_space.n
local_policy_model = self.policy_model_fn(nS, nA)
local_policy_model.load_state_dict( ⑧
self.shared_policy_model.state_dict())
local_value_model = self.value_model_fn(nS)
local_value_model.load_state_dict( ⑨
self.shared_value_model.state_dict())
while not self.get_out_signal: ⑩
state, is_terminal = env.reset(), False ⑪
n_steps_start = 0 ⑫
logpas, entropies, rewards, values = [], [], [], []
for step in count(start=1): ⑬
① 这是 A3C 代理。② 如同往常,这些都是代码片段。您知道在哪里可以找到工作代码。③ 这是每个工作员循环的工作函数。rank 参数用作工作员的 ID。④ 看看我们如何为每个工作员创建一个唯一的种子。我们希望有各种各样的经验。⑤ 我们为每个工作员创建一个唯一种子的环境。⑥ 我们还使用这个唯一的种子为 PyTorch、NumPy 和 Python。⑦ 有用的变量⑧ 在这里,我们创建一个本地策略模型。看看我们如何用共享策略网络的权重初始化它的权重。这个网络允许我们定期同步代理。⑨ 我们对价值模型做同样的事情。注意我们不需要 nA 作为输出维度。⑩ 我们开始训练循环,直到工作员被信号通知退出。⑪ 首先,重置环境,并将完成或 is_terminal 标志设置为 false。⑫ 正如您接下来看到的,我们使用 n-步回报来训练策略和价值函数。⑬ 让我们在下一页继续。|
| 我会说 PythonA3C 工作逻辑 2/2 | |
|---|---|
for step in count(start=1): ⑭ ⑮
state, reward, is_terminal, is_truncated, \
is_exploratory = self.interaction_step( ⑮
state, env, local_policy_model,
local_value_model, logpas,
entropies, rewards, values)
if is_terminal or step - n_steps_start == \ ⑯
self.max_n_steps:
is_failure = is_terminal and not is_truncated ⑰
next_value = 0 if is_failure else \ ⑱
local_value_model(state).detach().item()
rewards.append(next_value) ⑲
self.optimize_model( ⑳
logpas, entropies, rewards, values,
local_policy_model, local_value_model)
logpas, entropies, rewards, values = [], [], [], [] ㉑
n_steps_start = step ㉑
if is_terminal: ㉒
break
<...> ㉓
⑭ 我从缩进中移除了八个空格,以便更容易阅读。⑮ 我们处于剧集循环中。首先,收集一步的经验。⑯ 我们收集 n-步的最大值。如果我们遇到终端状态,我们就停止。⑰ 我们检查是否触发了时间包装器或这是一个真正的终端状态。⑱ 如果是失败,则下一个状态的价值为 0;否则,我们进行引导。⑲ 看!我在这里偷偷地将下一个 _value 附加到奖励上。通过这样做,VPG 中的优化代码保持基本不变,您很快就会看到。确保您看到了。⑳ 接下来,我们优化模型。我们稍后会深入研究该函数。㉑ 优化步骤后,我们重置变量并继续。㉒ 当然,如果状态是终端状态,退出剧集循环。㉓ 有很多东西被移除了。|
使用 n-步估计
在上一页,你注意到我将下一个状态(无论是否为终止状态)的值附加到奖励序列中。这意味着奖励变量包含了部分轨迹中所有奖励以及最后一个状态的状态值估计。我们也可以将其视为在相同位置具有部分回报和预测的剩余回报。部分回报是奖励的序列,预测的剩余回报是一个单一数值估计。唯一的原因是这并不是回报,因为它不是一个折现总和,但我们也可以处理这个问题。
你应该意识到这是一个 n-步回报,这是你在第五章中学到的。我们走出 n-步收集奖励,然后在第 n 个状态之后自举,或者如果我们落在终止状态,则在此之前,哪个先到就先做。
A3C 利用与蒙特卡洛回报相比的 n-步回报的较低方差。我们使用价值函数来预测用于更新策略的回报。你还记得自举可以减少方差,但会增加偏差。因此,我们在策略梯度算法中添加了一个评论家。欢迎来到演员-评论家方法的世界。
| 展示数学使用 n-步自举估计 | |
|---|---|
![]() |
|
| 我会说 PythonA3C 优化步骤 1/2 | |
class A3C(): ①
<...>
def optimize_model( ①
self, logpas, entropies, rewards, values,
local_policy_model, local_value_model):
T = len(rewards) ②
discounts = np.logspace(0, T, num=T, base=self.gamma, ③
endpoint=False)
returns = np.array(
[np.sum(discounts[:T-t] * rewards[t:]) for t in range(T)]) ④
discounts = torch.FloatTensor(
discounts[:-1]).unsqueeze(1) ⑤
returns = torch.FloatTensor(returns[:-1]).unsqueeze(1)
value_error = returns - values ⑥
policy_loss = -(discounts * value_error.detach() * \
logpas).mean()
entropy_loss = -entropies.mean() ⑦
loss = policy_loss + self.entropy_loss_weight * \
entropy_loss
self.shared_policy_optimizer.zero_grad()
loss.backward() ⑧
torch.nn.utils.clip_grad_norm_(
local_policy_model.parameters(), ⑨
self.policy_model_max_grad_norm)
for param, shared_param in zip( ⑩
① A3C,优化函数② 首先获取奖励的长度。记住,奖励包括自举值。③ 接下来,我们计算到 n+1 的所有折扣。④ 现在就是 n-步预测回报。⑤ 为了继续,我们需要移除额外的元素,并按预期格式化变量。⑥ 现在,我们计算价值误差,即预测回报减去估计值。⑦ 我们像以前一样计算损失。⑧ 注意我们现在将共享策略优化器归零,然后计算损失。⑨ 然后,剪辑梯度幅度。⑩ 在下一页继续。 |
| 我会说 PythonA3C 优化步骤 2/2 | |
|---|---|
for param, shared_param in zip( ⑪
local_policy_model.parameters(),
self.shared_policy_model.parameters()): ⑪
if shared_param.grad is None: ⑫
shared_param._grad = param.grad
self.shared_policy_optimizer.step() ⑬
local_policy_model.load_state_dict( ⑭
self.shared_policy_model.state_dict())
value_loss = value_error.pow(2).mul(0.5).mean() ⑮
self.shared_value_optimizer.zero_grad() ⑯
value_loss.backward() ⑰
torch.nn.utils.clip_grad_norm_( ⑱
local_value_model.parameters(),
self.value_model_max_grad_norm)
for param, shared_param in zip(
local_value_model.parameters(),
self.shared_value_model.parameters()):
if shared_param.grad is None:
shared_param._grad = param.grad ⑲
self.shared_value_optimizer.step() ⑳
local_value_model.load_state_dict( ㉑
self.shared_value_model.state_dict())
⑪ 好的,那么看看这里。我们在这里做的是迭代所有局部和共享策略网络参数。⑫ 我们想要做的是将每个梯度从局部模型复制到共享模型。⑬ 一旦梯度被复制到共享优化器中,我们就运行一个优化步骤。⑭ 立即之后,我们将共享模型加载到局部模型中。⑮ 接下来,我们用状态值网络做同样的事情。计算损失。⑯ 将共享值优化器归零。⑰ 反向传播梯度。⑱ 然后,剪辑它们。⑲ 然后,将所有梯度从局部模型复制到共享模型。⑳ 步进优化器。㉑ 最后,将共享模型加载到局部变量中。 |
非阻塞模型更新
A3C 最关键的特点之一是其网络更新是异步和无锁的。拥有一个共享模型会使得有能力的软件工程师倾向于想要一个阻塞机制来防止工作者覆盖其他更新。有趣的是,A3C 使用了一种名为 Hogwild!的更新风格,它不仅实现了接近最优的收敛速率,而且比使用锁定的替代方案快一个数量级。
GAE:鲁棒的优势估计
A3C 使用n-步回报来减少目标的方差。然而,正如你可能从第五章中记得的那样,还有一种更稳健的方法,它将多个n-步自举目标组合成一个单一的目标,从而创建比单个n-步更稳健的目标:λ-目标。广义优势估计(GAE)在 TD(λ)中的λ-目标类似,但用于优势。
广义优势估计
GAE 本身不是一个代理,而是一种估计优势函数目标的方法,大多数 actor-critic 方法都可以利用。更具体地说,GAE 使用n-步动作优势函数目标的指数加权组合,就像λ-目标是一组n-步状态值函数目标的指数加权组合一样。这种类型的目标,我们以与λ-目标相同的方式调整,可以显著减少策略梯度估计的方差,但会牺牲一些偏差。
| 展示数学可能的策略梯度估计器 | |
|---|---|
![]() |
|
| 展示数学 GAE 是优势函数的鲁棒估计 | |
![]() |
|
| 展示数学 GAE 的可能值目标 | |
![]() |
|
| 0001 | 一点历史介绍广义优势估计 |
| 约翰·舒尔曼(John Schulman)等人在 2015 年发表了一篇题为“使用广义优势估计进行高维连续控制”的论文,其中他介绍了 GAE。约翰是 OpenAI 的研究科学家,也是 GAE、TRPO 和 PPO 算法的主要发明者,这些算法你将在下一章中学习。2018 年,约翰因其创建这些算法而获得 35 岁以下创新者奖,这些算法至今仍然是业界最先进的。 | |
| 我会说 PythonGAE 的策略优化步骤 | |
class GAE(): ①
<...>
def optimize_model(
self, logpas, entropies, rewards, values,
local_policy_model, local_value_model):
T = len(rewards)
discounts = np.logspace(0, T, num=T, base=self.gamma, ②
endpoint=False)
returns = np.array(
[np.sum(discounts[:T-t] * rewards[t:]) for t in range(T)])
np_values = values.view(-1).data.numpy()
tau_discounts = np.logspace(0, T-1, num=T-1, ③
base=self.gamma*self.tau, endpoint=False)
advs = rewards[:-1] + self.gamma * \ ④
np_values[1:] - np_values[:-1]
gaes = np.array( ⑤
[np.sum(tau_discounts[:T-1-t] * advs[t:]) for t in range(T-1)])
<...> ⑥
policy_loss = -(discounts * gaes.detach() * \
logpas).mean()
entropy_loss = -entropies.mean()
loss = policy_loss + self.entropy_loss_weight * \
entropy_loss
value_error = returns - values ⑦
value_loss = value_error.pow(2).mul(0.5).mean() ⑦
<...> ⑦
① 这是 GAE 优化模型逻辑。② 首先,我们创建折扣回报,就像我们在 A3C 中所做的那样。③ 这两行首先创建一个包含所有状态值的 NumPy 数组,然后创建一个包含 (gammalambda)^t 的数组。注意,lambda 也常被称为 tau,我也在使用它。④ 这行创建一个 TD 误差数组:R_t + gamma * value_t+1 - value_t,对于 t=0 到 T。⑤ 这里我们通过乘以 tau 折扣和 TD 误差来创建 GAEs。⑥ 我们现在使用 gaes 来计算策略损失。⑦ 然后按常规进行。 |
A2C: 同步策略更新
在 A3C 中,工作者异步更新神经网络。但,异步工作者可能并不是使 A3C 成为一个高性能算法的原因。优势演员-评论家(A2C)是 A3C 的同步版本,尽管编号较低,但它是 A3C 之后提出的,并且表现出与 A3C 相当的性能。在本节中,我们将探讨 A2C,以及我们可以应用于策略梯度方法的一些其他改动。
权重共享模型
对我们当前算法的一个改动是使用单个神经网络来处理策略和值函数。当从图像中学习时,共享模型可以特别有益,因为特征提取可能非常计算密集。然而,由于策略和值函数更新的可能不同规模,模型共享可能具有挑战性。

在策略和值输出之间共享权重
| 我会说 Python 共享权重演员-评论家神经网络模型 1/2 | |
|---|---|
class FCAC(nn.Module): ①
def __init__(
self, input_dim, output_dim,
hidden_dims=(32,32), activation_fc=F.relu):
super(FCAC, self).__init__()
self.activation_fc = activation_fc
self.input_layer = nn.Linear(input_dim, hidden_dims[0])
self.hidden_layers = nn.ModuleList()
for i in range(len(hidden_dims)-1): ②
hidden_layer = nn.Linear(
hidden_dims[i], hidden_dims[i+1])
self.hidden_layers.append(hidden_layer)
self.value_output_layer = nn.Linear( ③
① 这是一个全连接的演员-评论家模型。② 这是网络实例化过程。这与独立网络模型类似。③ 继续…… |
| 我会说 Python 共享权重演员-评论家神经网络模型 2/2 | |
|---|---|
self.value_output_layer = nn.Linear(
hidden_dims[-1], 1) ④
self.policy_output_layer = nn.Linear(
hidden_dims[-1], output_dim)
def forward(self, state):
x = state ⑤
if not isinstance(x, torch.Tensor):
x = torch.tensor(x, dtype=torch.float32)
if len(x.size()) == 1:
x = x.unsqueeze(0)
x = self.activation_fc(self.input_layer(x))
for hidden_layer in self.hidden_layers:
x = self.activation_fc(hidden_layer(x))
return self.policy_output_layer(x), \
self.value_output_layer(x) ⑥
def full_pass(self, state):
logits, value = self.forward(state)
dist = torch.distributions.Categorical(logits=logits)
action = dist.sample()
logpa = dist.log_prob(action).unsqueeze(-1)
entropy = dist.entropy().unsqueeze(-1)
action = action.item() if len(action) == 1 \
else action.data.numpy()
is_exploratory = action != np.argmax(
logits.detach().numpy(), axis=int(len(state)!=1))
return action, is_exploratory, logpa, entropy, value ⑦
def select_action(self, state):
logits, _ = self.forward(state)
dist = torch.distributions.Categorical(logits=logits)
action = dist.sample()
action = action.item() if len(action) == 1 \
else action.data.numpy()
return action ⑧
④ 好的。这里就是构建的地方,值输出和政策输出都连接到隐藏层的最后一层。⑤ 前向传递首先通过重塑输入以匹配预期的变量类型和形状开始。⑥ 注意它从策略和值层输出。⑦ 这是一个方便的函数,可以一次性获取对数概率、熵和其他变量。⑧ 这选择给定状态或状态批次的动作或动作。 |
在策略更新中恢复秩序
以 Hogwild!风格更新神经网络可能会很混乱,但引入锁机制会显著降低 A3C 的性能。在 A2C 中,我们将工作者从代理移动到环境。我们不是有多个演员-学习者,而是有多个演员和一个学习者。结果证明,工作者推出经验是策略梯度方法中收益所在的地方。

| 我会说 Python 多进程环境包装 1/2 | |
|---|---|
class MultiprocessEnv(object):
def __init__(self, make_env_fn,make_env_kargs,
seed, n_workers):
self.make_env_fn = make_env_fn
self.make_env_kargs = make_env_kargs
self.seed = seed
self.n_workers = n_workers
self.pipes = [
mp.Pipe() for rank in range(self.n_workers)]①
self.workers = [ ②
mp.Process(target=self.work,
args=(rank, self.pipes[rank][1])) \
for rank in range(self.n_workers)]
[w.start() for w in self.workers] ③
① 这是我们创建与工作者通信的管道并创建工作者的多进程环境类。② 这里我们创建工作者。③ 这里我们启动它们。|
| 我会说 Python 多进程环境包装器 2/2 | |
|---|---|
[w.start() for w in self.workers] ④
def work(self, rank, worker_end):
env = self.make_env_fn(
**self.make_env_kargs, seed=self.seed + rank) ⑤
while True: ⑥
cmd, kwargs = worker_end.recv()
if cmd == 'reset':
worker_end.send(env.reset(**kwargs)) ⑦
elif cmd == 'step':
worker_end.send(env.step(**kwargs))
elif cmd == '_past_limit':
# Another way to check time limit truncation
worker_end.send(\
env._elapsed_steps >= env._max_episode_steps)
else:
env.close(**kwargs) ⑦
del env
worker_end.close()
break
def step(self, actions): ⑧
assert len(actions) == self.n_workers
[self.send_msg(('step',{'action':actions[rank]}),rank)\ ⑨
for rank in range(self.n_workers)]
results = []
for rank in range(self.n_workers): ⑩
parent_end, _ = self.pipes[rank]
o, r, d, _ = parent_end.recv()
if d:
self.send_msg(('reset', {}), rank) ⑪
o = parent_end.recv()
results.append((o, ⑫
np.array(r, dtype=np.float),
np.array(d, dtype=np.float), _))
return \
[np.vstack(block) for block in np.array(results).T] ⑫
④ 续集⑤ 工作者首先创建环境。⑥ 进入这个循环监听命令。⑦ 每个命令调用相应的 env 函数并将响应发送回父进程。⑧ 这是主步骤函数,例如。⑨ 当被调用时,它向工作者广播命令和参数。⑩ 工作者完成他们的部分并发送数据,这些数据在这里收集。⑪ 我们在完成时自动重置。⑫ 最后,通过观察、奖励、完成和 infos 来附加和堆叠结果。|
| 我会说 PythonA2C 训练逻辑 | |
|---|---|
class A2C():
def train(self, make_envs_fn, make_env_fn, ①
make_env_kargs, seed, gamma, max_minutes,
max_episodes, goal_mean_100_reward):
envs = self.make_envs_fn(make_env_fn, ②
make_env_kargs, self.seed,
self.n_workers)
<...>
self.ac_model = self.ac_model_fn(nS, nA) ③
self.ac_optimizer = self.ac_optimizer_fn(
self.ac_model, self.ac_optimizer_lr)
states = envs.reset() ④
for step in count(start=1):
states, is_terminals = \ ⑤
self.interaction_step(states, envs)
if is_terminals.sum() or \
step - n_steps_start == self.max_n_steps:
past_limits_enforced = envs._past_limit()
failure = np.logical_and(is_terminals,
np.logical_not(past_limits_enforced)) ⑤
next_values = self.ac_model.evaluate_state( ⑤
states).detach().numpy() * (1 - failure)
self.rewards.append(next_values)
self.values.append(torch.Tensor(next_values))
self.optimize_model()
self.logpas, self.entropies = [], []
self.rewards, self.values = [], [] ⑥
n_steps_start = step
① 这是我们如何使用多处理器环境进行训练的方式。② 这里,看看如何创建,基本上,向量化的环境。③ 这里我们创建一个单一模型。这是一个具有策略和价值输出的演员-评论员模型。④ 看看,我们重置多处理器环境并获取一系列状态。⑤ 主要的是我们现在处理堆栈。⑥ 但,在其核心,一切都是相同的。|
| 我会说 PythonA2C 优化模型逻辑 | |
|---|---|
class A2C(): ①
def optimize_model(self):
T = len(self.rewards)
discounts = np.logspace(0, T, num=T, base=self.gamma,
endpoint=False)
returns = np.array(
[[np.sum(discounts[:T-t] * rewards[t:, w])
for t in range(T)] \ ②
for w in range(self.n_workers)]) ②
np_values = values.data.numpy()
tau_discounts = np.logspace(0, T-1, num=T-1,
base=self.gamma*self.tau, endpoint=False)
advs = rewards[:-1] + self.gamma * np_values[1:] \ ③
- np_values[:-1]
gaes = np.array(
[[np.sum(tau_discounts[:T-1-t] * advs[t:, w]) \
for t in range(T-1)]
for w in range(self.n_workers)]) ④
discounted_gaes = discounts[:-1] * gaes
value_error = returns - values ⑤
value_loss = value_error.pow(2).mul(0.5).mean()
policy_loss = -(discounted_gaes.detach() * \
logpas).mean()
entropy_loss = -entropies.mean()
loss = self.policy_loss_weight * policy_loss + \ ⑤
self.value_loss_weight * value_loss + \
self.entropy_loss_weight * entropy_loss
self.ac_optimizer.zero_grad()
loss.backward() ⑥
torch.nn.utils.clip_grad_norm_(
self.ac_model.parameters(),
self.ac_model_max_grad_norm)
self.ac_optimizer.step()
① 这是我们如何在 A2C 中优化模型的方式。② 主要要注意的是,我们现在使用的是每个工作者的时间步向量矩阵。③ 一些操作以完全相同的方式进行,令人惊讶。④ 对于某些操作,我们只需要添加一个循环来包含所有工作者。⑤ 看看我们如何构建单个损失函数。⑥ 最后,我们优化单个神经网络。|
| 细节之处 Running all policy-gradient methods in the CartPole-v1 environment |
|---|
| | 为了展示策略梯度算法,并使与之前章节中探索的基于价值的方法的比较更容易,我使用了与基于价值方法实验相同的配置进行了实验。以下是详细信息:REINFORCE:
-
运行具有 4-128-64-2 节点的策略网络,使用 Adam 优化器,学习率 lr 为 0.0007。
-
在每个回合结束时使用蒙特卡洛回报进行训练。没有基线。
VPG(使用蒙特卡洛基线的 REINFORCE):
-
与 REINFORCE 相同的策略网络,但现在我们在损失函数中添加了一个熵项,权重为 0.001,并裁剪梯度范数为 1。
-
我们现在学习一个价值函数并将其用作基线,而不是作为评论员。这意味着 MC 回报在没有重新启动的情况下使用,价值函数仅减少回报的规模。价值函数使用 4-256-128-1 网络,RMSprop 优化器,学习率为 0.001。虽然没有梯度裁剪,但这是可能的。
A3C:
-
我们以完全相同的方式训练策略网络和价值网络。
-
我们现在每 50 步最多(或当达到终端状态时)重新启动回报。这是一个演员-评论员方法。
-
我们使用八个工人,每个工人都有网络的副本,并执行 Hogwild!更新。
GAE:
-
与其他算法相同的超参数。
-
主要区别在于 GAE 添加了一个 tau 超参数来折现优势。我们在这里使用 0.95 作为 tau。请注意,代理风格具有相同的n步自举逻辑,这可能不会使这是一个纯粹的 GAE 实现。通常,你会看到一次处理完整的批次。它仍然表现相当不错。
A2C:
-
A2C 确实改变了大多数超参数。首先,我们有一个单一的网络:4-256-128-3(2 和 1)。使用 Adam 进行训练,学习率为 0.002,梯度范数为 1。
-
策略权重为 1.0,价值函数为 0.6,熵为 0.001。
-
我们采用 10 步自举,八个工人,以及 0.95 的 tau。
这些算法并没有独立调优;我确信它们可以做得更好。|
| CartPole-v1 环境上的策略梯度法和演员-评论家方法汇总 | |
|---|---|
![]() |
概述
在本章中,我们概述了策略梯度法和演员-评论家方法。首先,我们用几个理由来介绍考虑策略梯度法和演员-评论家方法。你了解到直接学习策略是强化学习方法的真正目标。你了解到通过学习策略,我们可以使用随机策略,这在部分可观察环境中可能比基于价值的方法表现更好。你了解到尽管我们通常学习随机策略,但没有任何东西阻止神经网络学习确定性策略。
你还学习了四种算法。首先,我们研究了 REINFORCE 及其如何是改进策略的直接方法。在 REINFORCE 中,我们可以使用完整的回报或奖励到去作为改进策略的分数。
然后,你学习了 vanilla 策略梯度,也称为带有基线的 REINFORCE。在这个算法中,我们使用蒙特卡洛回报作为目标来学习一个价值函数。然后,我们使用价值函数作为基线,而不是作为评论家。在 VPG 中我们不进行自举;相反,我们使用奖励到去,就像在 REINFORCE 中一样,并减去学习到的价值函数以减少梯度的方差。换句话说,我们使用优势函数作为策略分数。
我们还研究了 A3C 算法。在 A3C 中,我们自举价值函数,无论是为了学习价值函数还是为了评分策略。更具体地说,我们使用n步回报来改进模型。此外,我们使用多个演员-学习者,每个演员-学习者都执行策略,评估回报,并使用 Hogwild!方法更新策略和价值模型。换句话说,工人更新无锁模型。
我们接着学习了 GAE,以及这是如何估计类似于 TD(λ) 和 λ-回报的优势的方法。GAE 使用所有 n-步优势的指数加权混合,以创建一个更稳健的优势估计,可以轻松调整以使用更多的自举和因此偏差,或者实际的回报和因此方差。
最后,我们学习了 A2C 以及移除 A3C 的异步部分可以得到一个无需实现自定义优化器的相当算法。
到现在为止,
-
理解基于值、基于策略、策略梯度法和演员-评论家方法之间的主要区别
-
可以自己实现基本的策略梯度法和演员-评论家方法
-
可以调整策略梯度法和演员-评论家算法以通过各种环境
| 可分享的工作在自己的工作上努力,并分享你的发现 |
|---|
| | 这里有一些关于如何将你所学的内容提升到下一个层次的想法。如果你愿意,与世界分享你的结果,并确保查看其他人做了什么。这是一个双赢的局面,希望你能充分利用它。
-
#gdrl_ch11_tf01: 在本章的早期,我提到了一个虚构的雾蒙蒙的湖环境,但嘿,它之所以是虚构的,是因为你还没有实现它,对吧?现在就去实现一个雾蒙蒙的湖环境,以及一个雾蒙蒙的冰冻湖环境。对于这个环境,确保传递给智能体的观察与环境的实际内部状态不同。例如,如果智能体位于单元格 3,内部状态是保密的,智能体只能观察到它位于一个雾蒙蒙的单元格中。在这种情况下,所有雾蒙蒙的单元格应该发出相同的观察,这样智能体就无法判断自己的位置。实现这个环境后,测试只能学习确定性策略的 DRL 智能体(如前几章所述),以及可以学习随机策略的智能体(如本章所述)。你将需要对观察进行 one-hot 编码,以便将其传递到神经网络中。创建一个包含环境的 Python 包,以及一个包含有趣的测试和结果的 Notebook。
-
#gdrl_ch11_tf02: 在本章中,我们仍然使用 CartPole-v1 环境作为测试平台,但你应该知道环境交换应该是简单的。首先,在类似的环境中测试相同的智能体,例如 LunarLander-v2 或 MountainCar-v0。注意使其相似的是观察是低维连续的,动作是低维离散的。其次,在不同的环境中测试它们,这些环境具有高维观察或动作。
-
#gdrl_ch11_tf03: 在每一章中,我都使用最后的标签作为通用的标签。请随意使用这个标签来讨论与本章相关的任何其他内容。没有比你自己创造的任务更令人兴奋的作业了。确保分享你为自己设定的研究内容和你的结果。
用你的发现写一条推文,@提及我 @mimoralea(我会转发),并使用列表中的特定标签来帮助感兴趣的人找到你的结果。没有对错之分;你分享你的发现并检查他人的发现。利用这个机会社交,做出贡献,让自己更受关注!我们正在等待你的到来!以下是一条推文示例:“嘿,@mimoralea。我创建了一篇博客文章,列出了学习深度强化学习的资源列表。查看它在这里 。#gdrl_ch01_tf01”我会确保转发并帮助他人找到你的作品。 |
12 高级演员-评论家方法
在本章中
-
你将了解更多高级深度强化学习方法,这些方法至今仍然是深度强化学习中的最先进算法进步。
-
你将学习解决各种深度强化学习问题,从具有连续动作空间的问题到具有高维动作空间的问题。
-
你将从头开始构建最先进的演员-评论家方法,并打开理解与人工通用智能相关的高级概念的大门。
批评可能并不令人愉快,但它是必要的。它起着与人体疼痛相同的作用。它引起了人们对事物不健康状态的注意。
— 温斯顿·丘吉尔 英国政治家、军官、作家和英国首相
在上一章中,你了解了一种不同的、更直接的技术来解决深度强化学习问题。你首先被介绍到策略梯度方法,其中智能体通过直接近似来学习策略。在纯策略梯度方法中,我们不使用价值函数作为寻找策略的代理,实际上,我们根本不使用价值函数。我们而是直接学习随机策略。
然而,你很快注意到价值函数仍然可以发挥重要作用,并使策略梯度方法得到改进。因此,你被引入了演员-评论家方法。在这些方法中,智能体学习策略和价值函数。通过这种方法,你可以利用一个函数近似的优点来减轻另一个近似的缺点。例如,在某些环境中,学习策略可能比学习足够准确的价值函数更直接,因为动作空间中的关系可能比价值的关系更紧密。尽管如此,即使精确地知道状态的价值可能更复杂,但粗略的近似对于减少策略梯度目标方差是有用的。正如你在上一章中所探索的,学习价值函数并将其用作基线或用于计算优势可以显著减少用于策略梯度更新的目标方差。此外,减少方差通常会导致学习速度更快。
然而,在上一章中,我们专注于使用价值函数作为更新随机策略的评论家。我们为学习价值函数使用了不同的目标,并以几种不同的方式并行化了工作流程。然而,算法以相同的一般方式使用学习到的价值函数来训练策略,并且学习到的策略具有相同的属性,因为它是一个随机策略。我们只是触及了使用学习策略和价值函数的表面。在本章中,我们将更深入地探讨演员-评论家方法的范式,并在四个不同的具有挑战性的环境中进行训练:摆锤、跳跃者、猎豹和月球着陆器。正如你很快就会看到的,除了是更具挑战性的环境之外,大多数这些环境都具有连续的动作空间,这是我们第一次面对的,并且需要使用独特的策略模型。
为了解决这些环境,我们首先探索了学习确定性策略的方法;也就是说,当呈现相同的状态时,返回相同动作的策略,即被认为是最优的动作。我们还研究了一系列改进,使确定性策略梯度算法成为迄今为止解决深度强化学习问题的最先进方法之一。然后,我们探索了一种演员-评论家方法,它不是使用损失函数中的熵,而是直接使用价值函数方程中的熵。换句话说,它最大化回报以及策略的长期熵。最后,我们以一个算法结束,该算法通过将策略的更新限制为小的变化,从而允许更稳定的策略改进步骤。策略中的小变化使得策略梯度方法在性能上表现出稳定且通常是单调的改进,从而在几个 DRL 基准测试中实现了最先进的性能。
DDPG:逼近确定性策略
在本节中,我们探讨了一种名为深度确定性策略梯度(DDPG)的算法。DDPG 可以被视为一个近似的 DQN,或者更好的说法,是一个适用于连续动作空间的 DQN。DDPG 使用了 DQN 中发现的许多相同技术:它使用重放缓冲区以离线方式训练动作值函数,并使用目标网络来稳定训练。然而,DDPG 还训练了一个逼近最优动作的策略。正因为如此,DDPG 是一个限制在连续动作空间中的确定性策略梯度方法。
DDPG 借鉴了 DQN 的许多技巧
首先,将 DDPG 想象成一个与 DQN 具有相同架构的算法。训练过程也是类似的:智能体以在线方式收集经验,并将这些在线经验样本存储到重放缓冲区中。在每一步,智能体从重放缓冲区中抽取一个迷你批次,通常是随机均匀抽取的。然后,智能体使用这个迷你批次来计算自举的TD目标并训练一个 Q 函数。
DQN 和 DDPG 之间的主要区别在于,虽然 DQN 使用目标 Q 函数通过 argmax 获取贪婪动作,而 DDPG 使用一个目标确定性策略函数,该函数被训练来近似贪婪动作。与我们在 DQN 中使用下一个状态的 Q 函数的 argmax 获取贪婪动作不同,在 DDPG 中,我们直接使用策略函数来近似下一个状态中的最佳动作。然后,在两者中,我们都使用该动作与 Q 函数结合来获取最大值。
| 展示 MathDQN 与 DDPG 的价值函数目标 | |
|---|---|
![]() |
|
| 我会说 PythonDDPG 的 Q 函数网络 | |
class FCQV(nn.Module): ①
def __init__(self,
input_dim,
output_dim,
hidden_dims=(32,32),
activation_fc=F.relu):
super(FCQV, self).__init__()
self.activation_fc = activation_fc
self.input_layer = nn.Linear(input_dim, hidden_dims[0]) ②
self.hidden_layers = nn.ModuleList()
for i in range(len(hidden_dims)-1):
in_dim = hidden_dims[i]
if i == 0: ③
in_dim += output_dim
hidden_layer = nn.Linear(in_dim, hidden_dims[i+1])
self.hidden_layers.append(hidden_layer)
self.output_layer = nn.Linear(hidden_dims[-1], 1) ④
<...>
def forward(self, state, action): ⑤
x, u = self._format(state, action)
x = self.activation_fc(self.input_layer(x))
for i, hidden_layer in enumerate(self.hidden_layers):
if i == 0:
x = torch.cat((x, u), dim=1) ⑥
x = self.activation_fc(hidden_layer(x)) ⑦
return self.output_layer(x) ⑧
① 这是 DDPG 中使用的 Q 函数网络。② 我们像往常一样开始构建架构。③ 这里有一个例外。我们将第一隐藏层的维度增加到输出维度。④ 注意网络的输出是一个节点,表示状态-动作对的价值。⑤ 前向传播开始如预期。⑥ 但是,我们在第一隐藏层直接将动作连接到状态。⑦ 然后,继续如预期。⑧ 最后,返回输出。
学习确定性策略
现在,我们需要添加到这个算法中的一个东西是策略网络。我们希望训练一个网络,能够在给定状态下给出最佳动作。该网络必须相对于动作是可微分的。因此,动作必须是连续的,以便进行高效的基于梯度的学习。目标是简单的;我们可以使用策略网络 mu 的期望 Q 值。也就是说,智能体试图找到最大化这个值的动作。注意,在实践中,我们使用最小化技术,因此最小化这个目标的负值。
| 展示 MathDDPG 的确定性策略目标 | |
|---|---|
![]() |
还要注意,在这种情况下,我们不需要使用目标网络,而是使用在线网络,即策略网络(动作选择部分)和价值函数(动作评估部分)。此外,鉴于我们需要为训练价值函数采样状态的小批量,我们可以使用这些相同的状态来训练策略网络。
| 0001 | 一点历史介绍 DDPG 算法 |
|---|---|
| DDPG 是在 2015 年一篇题为“使用深度强化学习的连续控制”的论文中提出的。这篇论文由当时在谷歌 DeepMind 担任研究科学家的蒂莫西·利利克拉普(Timothy Lillicrap)等人撰写。自 2016 年以来,蒂姆一直担任谷歌 DeepMind 的高级研究科学家和伦敦大学学院的客座教授。蒂姆为 DeepMind 的几篇其他论文做出了贡献,例如 A3C 算法、AlphaGo、AlphaZero、Q-Prop 和星际争霸 II 等。最有趣的事实之一是,蒂姆在认知科学和系统神经科学方面有背景,而不是传统计算机科学路径进入深度强化学习。 | |
| 我会说 PythonDDPG 的确定性策略网络 | |
class FCDP(nn.Module): ①
def __init__(self,
input_dim,
action_bounds,
hidden_dims=(32,32),
activation_fc=F.relu, ②
out_activation_fc=F.tanh):
super(FCDP, self).__init__()
self.activation_fc = activation_fc
self.out_activation_fc = out_activation_fc
self.env_min, self.env_max = action_bounds ③
self.input_layer = nn.Linear(input_dim, hidden_dims[0]) ④
self.hidden_layers = nn.ModuleList()
for i in range(len(hidden_dims)-1):
hidden_layer = nn.Linear(hidden_dims[i],
hidden_dims[i+1])
self.hidden_layers.append(hidden_layer)
self.output_layer = nn.Linear(hidden_dims[-1],
len(self.env_max)) ④
def forward(self, state): ⑤
x = self._format(state)
x = self.activation_fc(self.input_layer(x)) ⑥
for hidden_layer in self.hidden_layers: ⑦
x = self.activation_fc(hidden_layer(x))
x = self.output_layer(x) ⑧
x = self.out_activation_fc(x) ⑨
return self.rescale_fn(x) ⑩
① 这是 DDPG 中使用的策略网络:全连接确定性策略。② 注意这次输出层的激活函数不同。我们使用 tanh 激活函数将输出压缩到(-1, 1)范围内。③ 我们需要获取动作的最小值和最大值,以便将网络的输出(-1, 1)重新缩放到预期的范围。④ 架构正如预期:输入状态,输出动作。⑤ 前向传播也很直接。⑥ 输入⑦ 隐藏⑧ 输出⑨ 注意,然而,我们使用输出激活函数激活输出。⑩ 同样重要的是,我们将动作从-1 到 1 的范围重新缩放到特定于环境的范围。rescale_fn 函数在此处未显示,但你可以去笔记本中查看详细信息。|
| 我会说 PythonDDPG 的模型优化步骤 | |
|---|---|
def optimize_model(self, experiences): ①
states, actions, rewards, \
next_states, is_terminals = experiences
batch_size = len(is_terminals)
argmax_a_q_sp = self.target_policy_model(next_states)
max_a_q_sp = self.target_value_model(next_states,
argmax_a_q_sp) ②
target_q_sa = rewards + self.gamma * max_a_q_sp * \
(1 - is_terminals)
q_sa = self.online_value_model(states, actions)
td_error = q_sa - target_q_sa.detach() ③
value_loss = td_error.pow(2).mul(0.5).mean()
self.value_optimizer.zero_grad() ④
value_loss.backward()
torch.nn.utils.clip_grad_norm_(
self.online_value_model.parameters(),
self.value_max_grad_norm)
self.value_optimizer.step()
argmax_a_q_s = self.online_policy_model(states)
max_a_q_s = self.online_value_model(states,
argmax_a_q_s) ⑤
policy_loss = -max_a_q_s.mean() ⑥
self.policy_optimizer.zero_grad()
policy_loss.backward()
torch.nn.utils.clip_grad_norm_( ⑦
self.online_policy_model.parameters(),
self.policy_max_grad_norm)
self.policy_optimizer.step()
① optimize_model 函数接收一个经验的小批量。② 使用它,我们根据策略预测的下一个状态的最大值、根据策略的动作和根据 Q 函数的值来计算目标。③ 然后我们获取预测值,计算误差和损失。注意我们使用目标网络和在线网络的位置。④ 优化步骤与其他网络类似。⑤ 接下来,我们获取在线策略预测的迷你批量中的状态的动作,然后使用这些动作通过在线价值网络获取价值估计。⑥ 接下来,我们获取策略损失。⑦ 最后,我们将优化器置零,对损失进行反向传播,裁剪梯度,并更新优化器。|
| 探索确定性策略 |
在 DDPG 中,我们训练确定性贪婪策略。在一个理想的世界里,这种策略接受一个状态并返回该状态的最优动作。但是,在一个未经训练的策略中,返回的动作不会足够准确,但仍然是确定的。如前所述,智能体需要平衡利用知识和探索。但是,由于 DDPG 智能体学习的是确定性策略,它不会在策略上进行探索。想象一下,智能体很固执,总是选择相同的动作。为了解决这个问题,我们必须进行离策略探索。因此,在 DDPG 中,我们在策略选择的动作中注入高斯噪声。
你已经了解了多个 DRL 智能体中的探索。在 NFQ、DQN 等中,我们使用基于 Q 值的探索策略。我们使用学习的 Q 函数来获取给定状态的动作值,并根据这些值进行探索。在 REINFORCE、VPG 等中,我们使用随机策略,因此探索是在策略上的。也就是说,探索由策略本身处理,因为它具有随机性;它有随机性。在 DDPG 中,智能体通过向动作添加外部噪声来进行探索,使用离策略探索策略。
| 我会说 Python 确定性策略梯度探索 | |
|---|---|
class NormalNoiseDecayStrategy(): ①
def select_action(self, model,
state, max_exploration=False):
if max_exploration:
noise_scale = self.high ②
else:
noise_scale = self.noise_ratio * self.high ③
with torch.no_grad():
greedy_action = model(state).cpu().detach().data
greedy_action = greedy_action.numpy().squeeze() ④
noise = np.random.normal(loc=0,
scale=noise_scale, ⑤
size=len(self.high))
noisy_action = greedy_action + noise ⑥
action = np.clip(noisy_action, self.low, self.high)
self.noise_ratio = self._noise_ratio_update() ⑦
return action ⑧
① 这是策略的 select_action 函数。② 为了最大化探索,我们将噪声尺度设置为最大动作。③ 否则,我们将噪声尺度降低。④ 我们直接从网络中获取贪婪动作。⑤ 接下来,我们使用尺度和 0 均值来获取动作的高斯噪声。⑥ 将噪声添加到动作中,并将其剪辑到范围内。⑦ 接下来,我们更新噪声比例计划。这可以是常数、线性、指数等。 |
| 一个具体例子摆锤环境 | |
|---|---|
Pendulum-v0 环境包含一个倒立摆,智能体需要将其摆动起来,使其尽可能少地消耗力气保持直立。状态空间是一个包含三个变量的向量(cos(theta),sin(theta),theta dot),表示杆的夹角余弦值、正弦值和角速度。动作空间是一个从-2 到 2 的单个连续变量,表示关节力。这个关节就是杆底部的那个黑点。动作是顺时针或逆时针的力。奖励函数是基于角度、速度和力度的方程。目标是完美平衡地直立,不消耗任何力气。在这样的理想时间步长中,智能体获得 0 奖励,这是它能做到的最好的。智能体可能获得的最高成本(最低奖励)大约是-16 奖励。精确的方程是–(theta² + 0.1theta_dt² + 0.001action²)。 这是一个持续任务,因此没有终止状态。然而,环境在 200 步后超时,这起到了相同的作用。环境被认为是未解决的,这意味着没有目标回报。然而,-150 是一个合理的阈值。 |
|
| 总结 DDPG 在摆动环境中的应用 | |
![]() |
TD3:在 DDPG 之上的最新改进
DDPG 已经成为了控制领域几年来的最先进的深度强化学习方法之一。然而,已经提出了改进,这些改进在性能上产生了重大影响。在本节中,我们讨论了一系列改进,这些改进共同形成了一个新的算法,称为twin-delayed DDPG(TD3)。TD3 对主要的 DDPG 算法引入了三个主要变化。首先,它增加了一种双重学习技术,类似于你在双重 Q 学习和 DDQN 中学到的,但这次有一个独特的“双胞胎”网络架构。其次,它向动作和环境中的目标动作添加了噪声,这使得策略网络对近似误差更加鲁棒。第三,它延迟了对策略网络、目标网络和双胞胎目标网络的更新,从而使双胞胎网络更新得更频繁。
DDPG 中的双重学习
在TD3 中,我们使用了一种特殊的 Q 函数网络,它有两个独立的流,分别结束于对所讨论的状态-动作对的两个不同估计。在大多数情况下,这两个流是完全独立的,所以可以认为它们是两个独立的网络。然而,如果环境是基于图像的,那么共享特征层是有意义的。这样,CNN 可以提取共同的特征,并可能更快地学习。尽管如此,共享层通常更难训练,所以这需要你自己实验和决定。
在以下实现中,两个流是完全独立的,这两个网络之间唯一共享的是优化器。正如你在双网络损失函数中看到的,我们将每个网络的损失加起来,并在联合损失上优化这两个网络。
| 展示给我 TD3 中的双目标 | |
|---|---|
![]() |
|
| 我会说 PythonTD3 的双 Q 网络 1/2 | |
class FCTQV(nn.Module): ①
def __init__(self,
input_dim,
output_dim,
hidden_dims=(32,32),
activation_fc=F.relu):
super(FCTQV, self).__init__()
self.activation_fc = activation_fc
self.input_layer_a = nn.Linear(input_dim + output_dim,
hidden_dims[0]) ②
self.input_layer_b = nn.Linear(input_dim + output_dim,
hidden_dims[0])
self.hidden_layers_a = nn.ModuleList()
self.hidden_layers_b = nn.ModuleList()
for i in range(len(hidden_dims)-1):
hid_a = nn.Linear(hidden_dims[i], hidden_dims[i+1]) ③
self.hidden_layers_a.append(hid_a)
hid_b = nn.Linear(hidden_dims[i], hidden_dims[i+1])
self.hidden_layers_b.append(hid_b)
self.output_layer_a = nn.Linear(hidden_dims[-1], 1)
self.output_layer_b = nn.Linear(hidden_dims[-1], 1) ④
def forward(self, state, action):
x, u = self._format(state, action) ⑤
x = torch.cat((x, u), dim=1)
xa = self.activation_fc(self.input_layer_a(x)) ⑥
xb = self.activation_fc(self.input_layer_b(x))
for hidden_layer_a, hidden_layer_b in zip(
self.hidden_layers_a, self.hidden_layers_b): ⑦
① 这是完全连接的双 Q 值网络。这是 TD3 使用双流来近似 Q 值的方式。② 注意我们有两个输入层。再次强调,这些流实际上是两个独立的网络。③ 接下来,为每个流创建隐藏层。④ 最后,我们有两个输出层,每个输出层都有一个节点代表 Q 值。⑤ 我们开始正向传播,格式化输入以匹配网络期望的格式。⑥ 接下来,我们将状态和动作连接起来,并通过每个流传递。⑦ 继续…… |
| 我会说 PythonTD3 的双 Q 网络 2/2 | |
|---|---|
for hidden_layer_a, hidden_layer_b in zip(
self.hidden_layers_a, self.hidden_layers_b):
xa = self.activation_fc(hidden_layer_a(xa)) ⑧
xb = self.activation_fc(hidden_layer_b(xb))
xa = self.output_layer_a(xa)
xb = self.output_layer_b(xb)
return xa, xb ⑨
def Qa(self, state, action): ⑩
x, u = self._format(state, action)
x = torch.cat((x, u), dim=1) ⑪
xa = self.activation_fc(self.input_layer_a(x))
for hidden_layer_a in self.hidden_layers_a: ⑫
xa = self.activation_fc(hidden_layer_a(xa)) ⑫
return self.output_layer_a(xa) ⑬
⑧ 我们通过所有隐藏层及其相应的激活函数。⑨ 最后,我们通过输出层并返回它们的直接输出。⑩ 这是 Qa 流的正向传播。这在计算目标以更新策略时很有用。⑪ 我们格式化输入,并在通过 a 流之前将它们连接起来。⑫ 然后通过“a”隐藏层……⑬ ……一直通过输出层,就像我们一开始只有一个网络一样。 |
使用平滑的目标进行策略更新
记住,为了提高 DDPG 中的探索性,我们在用于环境的动作中注入高斯噪声。在 TD3 中,我们进一步发展了这个概念,不仅向用于探索的动作添加噪声,还向用于计算目标值的动作添加噪声。
使用带噪声的目标训练策略可以看作是一个正则化器,因为现在网络被迫对类似动作进行泛化。这项技术防止策略网络收敛到错误动作,因为在训练的早期阶段,Q 函数可能会过早地不准确地评估某些动作。动作上的噪声将这个值传播到一个更广泛的相关动作范围。
| 展示给我数学目标平滑过程 | |
|---|---|
![]() |
|
| 我会说 PythonTD3 的模型优化步骤 1/2 | |
def optimize_model(self, experiences): ①
states, actions, rewards, \
next_states, is_terminals = experiences
batch_size = len(is_terminals)
with torch.no_grad():
env_min = self.target_policy_model.env_min ② ③
env_max = self.target_policy_model.env_max ②
a_ran = env_max - env_min
a_noise = torch.randn_like(actions) * \
self.policy_noise_ratio * a_ran ③
n_min = env_min * self.policy_noise_clip_ratio ④
n_max = env_max * self.policy_noise_clip_ratio ④
a_noise = torch.max(
torch.min(a_noise, n_max), n_min) ⑤
argmax_a_q_sp = self.target_policy_model(
next_states) ⑥
noisy_argmax_a_q_sp = argmax_a_q_sp + a_noise
noisy_argmax_a_q_sp = torch.max(torch.min( ⑦
noisy_argmax_a_q_sp, env_max), env_min)
① 为了优化 TD3 模型,我们接收一个经验的小批量。② 我们首先获取环境的最大值和最小值。③ 获取噪声并将其缩放到动作的范围。④ 获取噪声的裁剪最小值和最大值。⑤ 然后,裁剪噪声。⑥ 从目标策略模型获取动作。⑦ 然后,将噪声添加到动作中,并裁剪动作。 |
| 我会说 Python TD3 的模型优化步骤 2/2 | |
|---|---|
noisy_argmax_a_q_sp = torch.max(torch.min( noisy_argmax_a_q_sp, env_max), env_min)
max_a_q_sp_a, max_a_q_sp_b = \
self.target_value_model(next_states, ⑧
noisy_argmax_a_q_sp)
max_a_q_sp = torch.min(max_a_q_sp_a, max_a_q_sp_b) ⑨
target_q_sa = rewards + self.gamma * max_a_q_sp * \
(1 - is_terminals)
q_sa_a, q_sa_b = self.online_value_model(states,
actions)
td_error_a = q_sa_a - target_q_sa ⑩
td_error_b = q_sa_b - target_q_sa
value_loss = td_error_a.pow(2).mul(0.5).mean() + \
td_error_b.pow(2).mul(0.5).mean()
self.value_optimizer.zero_grad()
value_loss.backward()
torch.nn.utils.clip_grad_norm_( ⑪
self.online_value_model.parameters(),
self.value_max_grad_norm)
self.value_optimizer.step()
if np.sum(self.episode_timestep) % \ ⑫
self.train_policy_every_steps == 0:
argmax_a_q_s = self.online_policy_model(states) ⑬
max_a_q_s = self.online_value_model.Qa( ⑬
states, argmax_a_q_s)
policy_loss = -max_a_q_s.mean() ⑭
self.policy_optimizer.zero_grad()
policy_loss.backward()
torch.nn.utils.clip_grad_norm_( ⑮
self.online_policy_model.parameters(),
self.policy_max_grad_norm)
self.policy_optimizer.step()
⑧ 我们使用夹具噪声动作来获取最大值。⑨ 回想一下,我们通过获取两个流之间的最小预测值来获取最大值,并用于目标。⑩ 接下来,我们获取两个流中来的预测值来计算误差和联合损失。⑪ 然后,我们对双网络进行标准的反向传播步骤。⑫ 注意我们在这里延迟了策略更新。我将在下一页上对此进行更多解释。⑬ 更新类似于 DDPG,但使用单流‘Qa。’⑭ 但是,损失是相同的。⑮ 这里是策略优化的步骤。标准的东西。 |
延迟更新
TD3 相对于 DDPG 的最终改进是延迟策略网络和目标网络的更新,以便在线 Q 函数的更新速率高于其他部分。延迟这些网络是有益的,因为在线 Q 函数在训练过程的早期往往会突然改变形状。放慢策略,使其在几个值函数更新之后更新,可以让值函数在引导策略之前稳定到更准确的价值。策略和目标网络的推荐延迟是每两次更新一次在线 Q 函数。
你可能在策略更新中注意到的另一件事是,我们必须使用在线值模型的一个流来获取来自策略的动作的估计 Q 值。在TD3 中,我们使用两个流中的一个,但每次都使用相同的流。
| 0001 | 一点历史 TD3 代理的介绍 |
|---|---|
| TD3 是由 Scott Fujimoto 等人于 2018 年在一篇题为“Addressing Function Approximation Error in Actor-Critic Methods”的论文中引入的。Scott 是麦吉尔大学的一名研究生,正在攻读计算机科学博士学位,由 David Meger 教授和 Doina Precup 教授指导。 | |
| 一个具体的例子 溜槽环境 | |
我们使用的跳跃者环境是 MuJoCo 和 Roboschool Hopper 环境的开源版本,由 Bullet 物理引擎提供支持。MuJoCo 是一个具有多种模型和任务的物理引擎。虽然 MuJoCo 在 DRL 研究中被广泛使用,但它需要许可证。如果您不是学生,这可能需要花费您几千美元。Roboschool 是 OpenAI 尝试创建 MuJoCo 环境开源版本的一次尝试,但后来因为 Bullet 而被放弃。Bullet 物理引擎是一个开源项目,其中包含与 MuJoCo 中相同的大多数环境。 HopperBulletEnv-v0 环境具有一个包含 15 个连续变量的向量作为无界观察空间,代表跳跃者机器人的不同关节。它具有一个介于-1 和 1 之间的三个连续变量的向量,代表大腿、小腿和脚关节的动作。请注意,单个动作是一个包含三个元素的向量。智能体的任务是使跳跃者向前移动,奖励函数强化这一点,同时也促进最小化能量成本。 |
|
| 细节中的训练 TD3 在跳跃者环境中 | |
| 如果您查看该章节的笔记本,您可能会注意到我们训练智能体直到它在 100 个连续的回合中达到平均奖励为 1,500。实际上,推荐的阈值是 2,500。然而,因为我们使用五个不同的种子进行训练,并且每次训练运行大约需要一个小时,所以我想到通过仅仅降低阈值来减少完成笔记本所需的时间。即使是在 1,500 的情况下,跳跃者也能很好地向前移动,正如您可以在笔记本中的 GIF 中看到的那样。现在,您必须知道,书中所有的实现都需要很长时间,因为它们在每个回合之后都会执行一次评估回合。在每个回合上评估性能并不是必要的,并且对于大多数目的来说可能是过度杀戮。对于我们来说,这是可以接受的,但如果您想重用代码,我建议您移除该逻辑,并在大约每 10-100 个回合检查一次评估性能。此外,请查看实现细节。书中的 TD3 分别优化策略和价值网络。如果您想使用 CNN 进行训练,例如,您可能希望共享卷积并在一次中优化所有内容。但再次强调,这需要大量的调整。 | |
| 累计 TD3 在跳跃者环境中的表现 | |
![]() |
| | SAC:最大化期望回报和熵
前两个算法,DDPG 和TD3,是离策略方法,用于训练确定性策略。回想一下,离策略意味着该方法使用由与优化策略不同的行为策略生成的经验。在 DDPG 和TD3 的情况下,它们都使用一个包含由几个先前策略生成的经验的回放缓冲区。此外,因为正在优化的策略是确定性的,这意味着每次查询时它都返回相同的动作,因此它们都使用离策略探索策略。在我们的实现中,它们都使用高斯噪声注入到进入环境的动作向量中。
为了更具体地说明,你之前章节中学到的智能体是按策略学习的。记住,它们训练随机策略,这些策略本身引入了随机性和探索性。为了在随机策略中促进随机性,我们在损失函数中添加了一个熵项。
在本节中,我们讨论了一个名为软演员-评论家(SAC)的算法,它是这两种范例的混合体。SAC 是一个类似于 DDPG 和TD3 的离策略算法,但它像 REINFORCE、A3C、GAE 和 A2C 一样训练随机策略,而不是像 DDPG 和TD3 那样训练确定性策略。
将熵添加到贝尔曼方程中
SAC 最关键的特征是随机策略的熵成为智能体试图最大化的值函数的一部分。正如你在本节中看到的那样,联合最大化期望总奖励和期望总熵自然地鼓励尽可能多样化的行为,同时仍然最大化期望回报。
| 显示数学智能体需要最大化熵 | |
|---|---|
![]() |
学习动作值函数
实际上,SAC 以类似于TD3 的方式学习值函数。也就是说,我们使用两个近似 Q 函数的网络,并取大多数计算的最低估计值。然而,有几个不同之处,即在使用 SAC 时,独立优化每个 Q 函数会产生更好的结果,这正是我们所做的。其次,我们将熵项添加到目标值中。最后,我们不像在TD3 中那样直接使用目标动作平滑。除此之外,模式与TD3 相同。
| 显示数学 Action-value 函数目标(我们在该目标上训练 MSE) | |
|---|---|
![]() |
学习策略
这次为了学习随机策略,我们使用了一个压缩高斯策略,在正向传递中输出均值和标准差。然后我们可以使用这些值从这个分布中进行采样,使用双曲正切函数 tanh 压缩值,然后将值重新缩放到环境预期的范围内。
为了训练策略,我们使用重新参数化技巧。这个“技巧”包括将随机性从网络中移出,放入输入中。这样,网络是确定性的,我们可以没有问题地训练它。这个技巧在 PyTorch 中直接实现,正如您接下来看到的。
| 展示数学策略目标(我们通过最小化这个目标函数进行训练) | |
|---|---|
![]() |
自动调整熵系数
SAC 的甜点是 alpha,即熵系数,可以自动调整。SAC 采用基于梯度的 alpha 优化,以向启发式期望熵进行优化。推荐的目标熵基于动作空间形状;更具体地说,是动作形状的向量积的负值。使用这个目标熵,我们可以自动优化 alpha,从而几乎没有与调节熵项相关的超参数需要调整。
| 展示数学 Alpha 目标函数(我们通过最小化这个目标函数进行训练) | |
|---|---|
![]() |
|
| 我会说 PythonSAC 高斯策略 1/2 | |
class FCGP(nn.Module): ①
def __init__(self,
<...>
self.input_layer = nn.Linear(input_dim, ②
hidden_dims[0])
self.hidden_layers = nn.ModuleList()
for i in range(len(hidden_dims)-1):
hidden_layer = nn.Linear(hidden_dims[i],
hidden_dims[i+1])
self.hidden_layers.append(hidden_layer) ②
self.output_layer_mean = nn.Linear(hidden_dims[-1], ③
len(self.env_max))
self.output_layer_log_std = nn.Linear( ③
hidden_dims[-1],
len(self.env_max))
① 这是我们 SAC 中使用的高斯策略。② 我们以与其他策略网络相同的方式开始一切:输入,到隐藏层。③ 但隐藏层连接到两个流。一个表示动作的均值,另一个表示对数标准差。
| 我会说 PythonSAC 高斯策略 2/2 | |
|---|---|
self.output_layer_log_std = nn.Linear( ④
hidden_dims[-1],
len(self.env_max))
self.target_entropy = -np.prod(self.env_max.shape) ⑤
self.logalpha = torch.zeros(1, ⑥
requires_grad=True,
device=self.device)
self.alpha_optimizer = optim.Adam([self.logalpha],
lr=entropy_lr)
def forward(self, state): ⑦
x = self._format(state) ⑧
x = self.activation_fc(self.input_layer(x))
for hidden_layer in self.hidden_layers:
x = self.activation_fc(hidden_layer(x))
x_mean = self.output_layer_mean(x)
x_log_std = self.output_layer_log_std(x)
x_log_std = torch.clamp(x_log_std, ⑨
self.log_std_min,
self.log_std_max)
return x_mean, x_log_std ⑩
def full_pass(self, state, epsilon=1e-6): ⑪
mean, log_std = self.forward(state)
pi_s = Normal(mean, log_std.exp()) ⑫
pre_tanh_action = pi_s.rsample() ⑬
tanh_action = torch.tanh(pre_tanh_action) ⑭
action = self.rescale_fn(tanh_action) ⑮
log_prob = pi_s.log_prob(pre_tanh_action) - torch.log( ⑯
(1 - tanh_action.pow(2)).clamp(0, 1) + epsilon)
log_prob = log_prob.sum(dim=1, keepdim=True)
return action, log_prob, self.rescale_fn(
torch.tanh(mean)) ⑯
④ 同一行以帮助您保持代码的流程⑤ 这里我们计算H,目标熵启发式。⑥ 接下来,我们创建一个变量,初始化为零,并创建一个优化器来优化 log alpha。⑦ 前向函数是我们预期的。⑧ 我们格式化输入变量,并将它们通过整个网络传递。⑨ 将 log std 限制在-20 到2之间,以控制 std 的范围在合理的值。⑩ 并返回这些值。⑪ 在完整传递中,我们得到均值和 log std。⑫ 获取具有这些值的正态分布。⑬ r样本在这里执行重新参数化技巧。⑭ 然后,我们将动作压缩到-1 到 1 的范围内。⑮ 然后,将其缩放到环境期望的范围。⑯ 我们还需要缩放 log 概率和均值。
| 我会说 PythonSAC 优化步骤 1/2 | |
|---|---|
def optimize_model(self, experiences): ①
states, actions, rewards, \
next_states, is_terminals = experiences ②
batch_size = len(is_terminals)
current_actions, \
logpi_s, _ = self.policy_model.full_pass(states) ③
target_alpha = (logpi_s + \
self.policy_model.target_entropy).detach() ④
alpha_loss = -(self.policy_model.logalpha * \
target_alpha).mean()
self.policy_model.alpha_optimizer.zero_grad()
alpha_loss.backward() ④
self.policy_model.alpha_optimizer.step()
alpha = self.policy_model.logalpha.exp() ⑤
current_q_sa_a = self.online_value_model_a(
states, current_actions) ⑥
current_q_sa_b = self.online_value_model_b(
states, current_actions)
current_q_sa = torch.min(current_q_sa_a, ⑦
current_q_sa_b)
policy_loss = (alpha * logpi_s - current_q_sa).mean() ⑧
ap, logpi_sp, _ = self.policy_model.full_pass( ⑨
next_states)
① 这是 SAC 中的优化步骤。② 首先,从迷你批次中获取经验。③ 接下来,我们获取当前的动作,a-hat,以及状态s的对数概率。④ 这里,我们计算 alpha 的损失,并在这里执行 alpha 优化器的步骤。⑤ 这是我们获取当前 alpha 值的方法。⑥ 在这些行中,我们使用在线模型和 a-hat 获取 Q 值。⑦ 然后,我们使用最小 Q 值估计。⑧ 这里,我们使用那个最小 Q 值估计来计算策略损失。⑨ 在下一页,我们计算 Q 函数损失。
| 我会说 PythonSAC 优化步骤 2/2 | |
|---|---|
ap, logpi_sp, _ = self.policy_model.full_pass( ⑩
next_states)
q_spap_a = self.target_value_model_a(next_states, ap) ⑪
q_spap_b = self.target_value_model_b(next_states, ap)
q_spap = torch.min(q_spap_a, q_spap_b) - \ ⑫
alpha * logpi_sp
target_q_sa = (rewards + self.gamma * \ ⑬
q_spap * (1 - is_terminals)).detach()
q_sa_a = self.online_value_model_a(states, actions) ⑭
q_sa_b = self.online_value_model_b(states, actions) ⑭
qa_loss = (q_sa_a - target_q_sa).pow(2).mul(0.5).mean() ⑮
qb_loss = (q_sa_b - target_q_sa).pow(2).mul(0.5).mean() ⑮
self.value_optimizer_a.zero_grad()
qa_loss.backward()
torch.nn.utils.clip_grad_norm_(
self.online_value_model_a.parameters(),
self.value_max_grad_norm)
self.value_optimizer_a.step()
self.value_optimizer_b.zero_grad() ⑯
qb_loss.backward() ⑯
torch.nn.utils.clip_grad_norm_( ⑯
self.online_value_model_b.parameters(), ⑯
self.value_max_grad_norm) ⑯
self.value_optimizer_b.step() ⑯
self.policy_optimizer.zero_grad() ⑰
policy_loss.backward() ⑰
torch.nn.utils.clip_grad_norm_( ⑰
self.policy_model.parameters(), ⑰
self.policy_max_grad_norm) ⑰
self.policy_optimizer.step() ⑰
⑩ 为了计算价值损失,我们得到预测的下一个动作。⑪ 使用目标价值模型,我们计算下一个状态-动作对的 Q 值估计。⑫ 获取最小的 Q 值估计,并考虑熵。⑬ 这就是我们的目标计算方法,使用奖励加上下一个状态的折扣最小值以及熵。⑭ 在这里,我们使用在线模型得到状态-动作对的预测值。⑮ 计算损失并分别优化每个 Q 函数。首先,a:⑯ 然后,b:⑰ 最后,策略:|
| 0001 | 一点历史介绍 SAC 代理 |
|---|---|
| SAC 是由 Tuomas Haarnoja 在 2018 年在一篇题为“Soft actor-critic: Off-policy maximum entropy deep reinforcement learning with a stochastic actor”的论文中提出的。在发表时,Tuomas 是加州大学伯克利分校的一名研究生,在 Pieter Abbeel 教授和 Sergey Levine 教授的指导下攻读计算机科学博士学位,并在谷歌担任研究实习生。自 2019 年以来,Tuomas 成为谷歌 DeepMind 的研究科学家。 | |
| 一个具体的例子猎豹环境 | |
HalfCheetahBulletEnv-v0 环境具有一个包含 26 个连续变量的向量,代表机器人的关节,动作空间是一个介于 -1 和 1 之间的 6 个连续变量的向量。代理的任务是使猎豹向前移动,与 hopper 一样,奖励函数也强化了这一点,促进最小能量成本。![]() |
|
| 总结猎豹环境上的 SAC | |
![]() |
PPO:限制优化步骤
在本节中,我们介绍了一种称为近端策略优化(PPO)的演员-评论家算法。将 PPO 视为一个与 A2C 具有相同底层架构的算法。PPO 可以重用为 A2C 开发的大部分代码。也就是说,我们可以并行使用多个环境进行模拟,将经验汇总成 mini-batch,使用评论家获取 GAE 估计,并以类似于 A2C 训练的方式训练演员和评论家。
PPO 中的关键创新是一个代理目标函数,它允许在线策略算法在相同的经验 mini-batch 上执行多个梯度步骤。正如你在上一章所学到的,A2C 作为一种在线策略方法,不能重复使用经验进行优化步骤。一般来说,在线策略方法需要在优化器步骤后立即丢弃经验样本。
然而,PPO 引入了一个剪裁的目标函数,防止策略在优化步骤后变得过于不同。通过保守地优化策略,我们不仅防止了由于策略梯度方法的内在高方差导致的性能崩溃,而且可以重用经验的小批量,并在每个小批量中执行多个优化步骤。重用经验的能力使 PPO 比其他策略方法(如你在上一章中学到的那些方法)更有效率。
使用与 A2C 相同的演员-评论家架构
将 PPO 视为 A2C 的改进。我的意思是,尽管在本章中我们学习了 DDPG、TD3 和 SAC,并且所有这些算法都有共性。PPO 不应被视为 SAC 的改进。TD3 是 DDPG 的直接改进。SAC 是与TD3 同时开发的。然而,SAC 的作者在第一篇 SAC 论文发布后不久,又发布了一个 SAC 的第二版论文,其中包含了TD3 的一些特性。虽然 SAC 不是TD3 的直接改进,但它确实共享了一些特性。然而,PPO 是 A2C 的改进,我们重用了 A2C 的部分代码。更具体地说,我们采样并行环境以收集数据的小批量,并使用 GAE 作为策略目标。
| 0001 | 一点历史介绍 PPO 代理 |
|---|---|
| PPO 是由 John Schulman 等人于 2017 年在一篇题为“Proximal Policy Optimization Algorithms”的论文中提出的。John 是一位研究科学家,OpenAI 的共同创始人之一,也是强化学习团队的共同负责人。他在加州大学伯克利分校获得计算机科学博士学位,导师是 Pieter Abbeel。 |
批量经验
PPO 的一个特点,A2C 没有的是,我们可以重用经验样本。为了处理这个问题,我们可以像 NFQ 那样收集大的轨迹批次,并对数据进行“拟合”,反复优化。然而,更好的方法是创建一个重放缓冲区,并在每次优化步骤中从中采样一个大的迷你批次。这给每个迷你批次带来了随机性的效果,因为样本并不总是相同的,但从长远来看,我们很可能重用所有样本。
| 我说 Python 第 1/4 集 | |
|---|---|
class EpisodeBuffer(): ①
def fill(self, envs, policy_model, value_model):
states = envs.reset()
we_shape = (n_workers, self.max_episode_steps) ②
worker_rewards = np.zeros(shape=we_shape,
dtype=np.float32)
worker_exploratory = np.zeros(shape=we_shape,
dtype=np.bool)
worker_steps = np.zeros(shape=(n_workers),
dtype=np.uint16)
worker_seconds = np.array([time.time(),] * n_workers,
dtype=np.float64)
buffer_full = False
while not buffer_full and \ ③
len(self.episode_steps[self.episode_steps>0]) < \
self.max_episodes/2:
with torch.no_grad():
actions, logpas, \ ④
are_exploratory = policy_model.np_pass(states)
values = value_model(states)
next_states, rewards, terminals, \
infos = envs.step(actions) ⑤
self.states_mem[self.current_ep_idxs,
worker_steps] = states
self.actions_mem[self.current_ep_idxs,
worker_steps] = actions
self.logpas_mem[self.current_ep_idxs,
worker_steps] = logpas ⑥
① 这是 EpisodeBuffer 类的填充。② 变量用于将工作信息分组。③ 我们进入主循环以填充缓冲区。④ 我们首先获取当前的动作、对数概率和统计数据。⑤ 将动作传递到环境中并获取经验。⑥ 然后,将经验存储到重放缓冲区中。 |
| 我说 Python 第 2/4 集 | |
|---|---|
self.logpas_mem[self.current_ep_idxs, ⑦
worker_steps] = logpas
worker_exploratory[np.arange(self.n_workers),
worker_steps] = are_exploratory ⑧
worker_rewards[np.arange(self.n_workers),
worker_steps] = rewards
for w_idx in range(self.n_workers):
if worker_steps[w_idx] + 1 == self.max_episode_steps:
terminals[w_idx] = 1 ⑨
infos[w_idx]['TimeLimit.truncated'] = True
if terminals.sum(): ⑩
idx_terminals = np.flatnonzero(terminals)
next_values = np.zeros(shape=(n_workers))
truncated = self._truncated_fn(infos)
if truncated.sum():
idx_truncated = np.flatnonzero(truncated)
with torch.no_grad():
next_values[idx_truncated] = value_model(\ ⑪
next_states[idx_truncated]).cpu().numpy()
states = next_states ⑫
worker_steps += 1 ⑫
if terminals.sum(): ⑬
new_states = envs.reset(ranks=idx_terminals)
states[idx_terminals] = new_states
for w_idx in range(self.n_workers):
if w_idx not in idx_terminals:
continue ⑭
e_idx = self.current_ep_idxs[w_idx]
⑦ 同一行。此外,我移除了空格以使其更容易阅读。⑧ 我们为每个工作者创建这两个变量。记住,工作者在环境中。⑨ 这里我们手动截断步骤过多的剧集。⑩ 我们检查终端状态并预处理它们。⑪ 如果终端状态被截断,我们进行自举。⑫ 我们更新状态变量并增加步数。⑬ 如果我们有终端状态,我们处理工作者。⑭ 我们逐个处理每个终端工作者。 |
| 我说 Python 第 3 集回放缓冲区 3/4 | |
|---|---|
e_idx = self.current_ep_idxs[w_idx] ⑮
T = worker_steps[w_idx]
self.episode_steps[e_idx] = T
self.episode_reward[e_idx] = worker_rewards[w_idx,:T].sum()
self.episode_exploration[e_idx] = worker_exploratory[\
w_idx, :T].mean() ⑯
self.episode_seconds[e_idx] = time.time() - \
worker_seconds[w_idx]
ep_rewards = np.concatenate((worker_rewards[w_idx, :T],
[next_values[w_idx]])) ⑰
ep_discounts = self.discounts[:T+1]
ep_returns = np.array(\ ⑰
[np.sum(ep_discounts[:T+1-t] * ep_rewards[t:]) \
for t in range(T)])
self.returns_mem[e_idx, :T] = ep_returns
ep_states = self.states_mem[e_idx, :T]
with torch.no_grad():
ep_values = torch.cat((value_model(ep_states), ⑱
torch.tensor(\
[next_values[w_idx]], ⑱
device=value_model.device,
dtype=torch.float32)))
np_ep_values = ep_values.view(-1).cpu().numpy()
ep_tau_discounts = self.tau_discounts[:T]
deltas = ep_rewards[:-1] + self.gamma * \
np_ep_values[1:] - np_ep_values[:-1]
gaes = np.array(\ ⑲
[np.sum(self.tau_discounts[:T-t] * deltas[t:]) \
for t in range(T)])
self.gaes_mem[e_idx, :T] = gaes
worker_exploratory[w_idx, :] = 0 ⑳
worker_rewards[w_idx, :] = 0 ⑳
worker_steps[w_idx] = 0 ⑳
worker_seconds[w_idx] = time.time() ⑳
⑮ 进一步移除空格。⑯ 这里我们收集统计信息以事后显示和分析。⑰ 我们将自举值添加到奖励向量中。计算预测回报。⑱ 这里我们获取预测值,并将自举值添加到向量中。⑲ 这里我们计算广义优势估计值,并将它们保存到缓冲区中。⑳ 并开始重置所有工作者变量以处理下一个剧集。 |
| 我说 Python 第 4 集回放缓冲区 4/4 | |
|---|---|
worker_seconds[w_idx] = time.time() ㉑
new_ep_id = max(self.current_ep_idxs) + 1
if new_ep_id >= self.max_episodes:
buffer_full = True
break ㉒
self.current_ep_idxs[w_idx] = new_ep_id ㉓
ep_idxs = self.episode_steps > 0 ㉔
ep_t = self.episode_steps[ep_idxs] ㉔
self.states_mem = [row[:ep_t[i]] for i, \ ㉕
row in enumerate(self.states_mem[ep_idxs])] ㉕
self.states_mem = np.concatenate(self.states_mem) ㉕
self.actions_mem = [row[:ep_t[i]] for i, \ ㉕
row in enumerate(self.actions_mem[ep_idxs])] ㉕
self.actions_mem = np.concatenate(self.actions_mem) ㉕
self.returns_mem = [row[:ep_t[i]] for i, \ ㉕
row in enumerate(self.returns_mem[ep_idxs])] ㉕
self.returns_mem = torch.tensor(np.concatenate(\ ㉕
self.returns_mem), device=value_model.device) ㉕
self.gaes_mem = [row[:ep_t[i]] for i, \ ㉕
row in enumerate(self.gaes_mem[ep_idxs])] ㉕
self.gaes_mem = torch.tensor(np.concatenate(\ ㉕
self.gaes_mem), device=value_model.device) ㉕
self.logpas_mem = [row[:ep_t[i]] for i, \ ㉕
row in enumerate(self.logpas_mem[ep_idxs])] ㉕
self.logpas_mem = torch.tensor(np.concatenate(\ ㉕
self.logpas_mem), device=value_model.device) ㉕
ep_r = self.episode_reward[ep_idxs]
ep_x = self.episode_exploration[ep_idxs] ㉖
ep_s = self.episode_seconds[ep_idxs]
return ep_t, ep_r, ep_x, ep_s ㉗
㉑ 同一行,再次调整缩进。㉒ 检查下一个排队中的剧集,如果你有太多的话就中断。㉓ 如果缓冲区未满,我们将新剧集的 ID 设置给工作者。㉔ 如果我们处于这些行,这意味着剧集已满,因此我们处理内存以进行采样。㉕ 因为我们是同时初始化整个缓冲区,所以我们需要从剧集和步骤维度中移除所有非数字的内容。㉖ 最后,我们提取统计信息以显示。㉗ 然后返回统计信息。 |
裁剪策略更新
正则策略梯度的主要问题是,参数空间中的微小变化可能导致性能的巨大差异。参数空间与性能之间的差异是我们需要在策略梯度方法中使用小学习率的原因,即便如此,这些方法的方差仍然可能太大。裁剪 PPO 的整个目的是对目标函数进行限制,使得在每个训练步骤中,策略只能偏离这么多。直观地说,你可以将这种裁剪目标视为教练防止对结果过度反应。昨晚球队用新战术得分好吗?很好,但不要夸张。不要因为一个新结果而放弃整个赛季的结果。相反,每次只稍微改进一点。
| 给我看数学裁剪策略目标 | |
|---|---|
![]() |
裁剪值函数更新
我们可以将类似的裁剪策略应用于值函数,核心概念相同:让参数空间的变化只改变 Q 值这么多,但不能更多。正如你可以看到的,这种裁剪技术保持了我们所关心的事物变化的平滑性,无论参数空间的变化是否平滑。我们不一定需要参数空间的小变化;然而,我们希望性能和值的变化是水平的。
| 给我看数学裁剪值损失 | ||
|---|---|---|
![]() |
||
| 我会说 PythonPPO 优化步骤 1/3 | ||
def optimize_model(self): ①
states, actions, returns, \ ②
gaes, logpas = self.episode_buffer.get_stacks()
values = self.value_model(states).detach() ③
gaes = (gaes - gaes.mean()) / (gaes.std() + EPS) ④
n_samples = len(actions)
for i in range(self.policy_optimization_epochs): ⑤
batch_size = int(self.policy_sample_ratio * \
n_samples)
batch_idxs = np.random.choice(n_samples, ⑥
batch_size,
replace=False)
states_batch = states[batch_idxs]
actions_batch = actions[batch_idxs] ⑦
gaes_batch = gaes[batch_idxs]
logpas_batch = logpas[batch_idxs]
logpas_pred, entropies_pred = \
self.policy_model.get_predictions( \ ⑧
states_batch, actions_batch)
ratios = (logpas_pred - logpas_batch).exp() ⑨
pi_obj = gaes_batch * ratios ⑩
pi_obj_clipped = gaes_batch * ratios.clamp( \ ⑩
1.0 - self.policy_clip_range,
1.0 + self.policy_clip_range)
① 现在,让我们看看这些代码中的两个方程。② 首先,从缓冲区中提取完整的经验批次。③ 在开始优化模型之前获取值。④ 获取 gaes 并对批次进行归一化。⑤ 现在,首先对策略进行最多预设的轮次优化。⑥ 从完整批次中子采样一个迷你批次。⑦ 使用随机采样的索引提取迷你批次。⑧ 我们使用在线模型来获取预测值。⑨ 在这里,我们计算比率:对数概率与概率比。⑩ 然后,计算目标和裁剪目标。 |
| 我会说 PythonPPO 优化步骤 2/3 | ||
|---|---|---|
pi_obj_clipped = gaes_batch * ratios.clamp( \
1.0 - self.policy_clip_range,
1.0 + self.policy_clip_range)
policy_loss = -torch.min(pi_obj, ⑪
pi_obj_clipped).mean()
entropy_loss = -entropies_pred.mean() * \ ⑫
self.entropy_loss_weight
self.policy_optimizer.zero_grad()
(policy_loss + entropy_loss).backward() ⑬
torch.nn.utils.clip_grad_norm_( \
self.policy_model.parameters(),
self.policy_model_max_grad_norm)
self.policy_optimizer.step()
with torch.no_grad():
logpas_pred_all, _ = \ ⑭
self.policy_model.get_predictions(states,
actions)
kl = (logpas - logpas_pred_all).mean() ⑮
if kl.item() > self.policy_stopping_kl:
break ⑯
for i in range(self.value_optimization_epochs): ⑰
batch_size = int(self.value_sample_ratio * \ ⑰
n_samples) ⑰
batch_idxs = np.random.choice(n_samples, ⑱
batch_size,
replace=False)
states_batch = states[batch_idxs]
⑪ 我们使用目标的最小值的负值来计算损失。⑫ 同时,我们计算熵损失,并相应地加权。⑬ 将优化器置零并开始训练。⑭ 在步进优化器之后,我们执行这个巧妙的技巧,确保只有当新策略在原始策略的范围内时才再次优化。⑮ 在这里,我们计算两个策略的 kl 散度。⑯ 如果它大于停止条件,则跳出训练循环。⑰ 在这里,我们开始对价值函数进行类似的更新。⑱ 我们从完整批次中获取迷你批次,就像策略一样。 |
| 我会说 PythonPPO 优化步骤 3/3 | ||
|---|---|---|
states_batch = states[batch_idxs]
returns_batch = returns[batch_idxs]
values_batch = values[batch_idxs]
values_pred = self.value_model(states_batch) ⑲
v_loss = (values_pred - returns_batch).pow(2) ⑲
values_pred_clipped = values_batch + \ ⑳
(values_pred - values_batch).clamp( \
-self.value_clip_range,
self.value_clip_range)
v_loss_clipped = (values_pred_clipped - \ ㉑
returns_batch).pow(2)
value_loss = torch.max(\ ㉒
v_loss, v_loss_clipped).mul(0.5).mean()
self.value_optimizer.zero_grad()
value_loss.backward()
torch.nn.utils.clip_grad_norm_( \ ㉓
self.value_model.parameters(),
self.value_model_max_grad_norm)
self.value_optimizer.step()
with torch.no_grad():
values_pred_all = self.value_model(states) ㉔
mse = (values - values_pred_all).pow(2)
mse = mse.mul(0.5).mean()
if mse.item() > self.value_stopping_mse: ㉕
break
⑲ 根据模型获取预测值,并计算标准损失。⑳ 在这里,我们计算裁剪预测值。㉑ 然后,计算裁剪损失。㉒ 我们使用标准损失和裁剪损失之间的最大值的均方误差。㉓ 最后,我们将优化器置零,反向传播损失,裁剪梯度,并步进。㉔ 我们可以做一些类似早期停止的事情,但使用价值函数。㉕ 基本上,我们检查新旧策略预测值的新旧均方误差。 |
| 一个具体例子 LunarLander 环境 | ||
|---|---|---|
与本章中我们探索的所有其他环境不同,LunarLander 环境具有离散动作空间。例如 DDPG 和TD3 这样的算法,仅适用于连续动作环境,无论是单变量,如摆锤,还是向量,如 hopper 和 cheetah。像 DQN 这样的智能体仅适用于离散动作空间环境,如 cart-pole。演员-评论家方法如 A2C 和 PPO 有一个很大的优点,即可以使用与几乎任何动作空间兼容的随机策略模型! 在这个环境中,智能体需要在每一步选择四种可能动作中的一种。这包括 0 表示什么都不做;或 1 表示点燃左侧引擎;或 2 表示点燃主引擎;或 3 表示点燃右侧引擎。观察空间是一个包含八个元素的向量,表示坐标、角度、速度以及其腿是否接触地面。奖励函数基于与着陆板的距离和燃料消耗。解决环境的奖励阈值是 200,时间步限制是 1,000。 |
||
| 在 LunarLander 环境中的 PPO 计数 | ||
![]() |
摘要
在本章中,我们概述了最先进的演员-评论家和深度强化学习方法。您首先了解了 DDPG 方法,其中学习了一个确定性策略。因为这些方法学习确定性策略,所以它们使用离策略探索策略和更新方程。例如,在 DDPG 和TD3 中,我们在动作选择过程中注入高斯噪声,使确定性策略变得具有探索性。
此外,您还了解到TD3 通过三个关键调整改进了 DDPG。首先,TD3 使用与 DDQN 类似的双学习技术,其中我们通过使用双 Q 网络“交叉验证”价值函数输出的估计。其次,TD3 除了向传递给环境的动作添加高斯噪声外,还向目标动作添加高斯噪声,以确保策略不会基于虚假的 Q 值估计来学习动作。第三,TD3 延迟对策略网络的更新,这样在用它们改变策略之前,价值网络可以得到更好的估计。
我们随后探索了一种名为 SAC 的熵最大化方法,该方法包括最大化价值函数和政策熵的联合目标,直观上可以理解为以最多样化的策略获得最多的奖励。SAC 智能体与 DDPG 和TD3 类似,以离策略的方式进行学习,这意味着这些智能体可以重用经验来改进策略。然而,与 DDPG 和TD3 不同,SAC 学习一个随机策略,这意味着探索可以是按策略的,并嵌入到学习到的策略中。
最后,我们探索了一种称为 PPO 的算法,它是 A2C 的直接后裔,是一种基于策略的学习方法,同时也使用基于策略的探索策略。然而,由于剪裁目标使得 PPO 更保守地改进学习策略,因此 PPO 能够重用过去的经验来改进其策略。
在下一章中,我们将回顾围绕 DRL 的几个研究领域,这些领域正在推动许多人称之的“通用人工智能”(AGI)领域的边缘。AGI 是通过重新创造它来理解人类智能的机会。物理学家理查德·费曼说:“我不能创造的东西,我就不理解。”理解智能不是很好吗?
到现在为止,
-
理解更高级的演员-评论家算法和相关技巧
-
可以实现最先进的深度强化学习方法,并可能设计出可以与他人分享的这些算法的改进
-
可以将最先进的深度强化学习算法应用于各种环境,希望甚至包括你自己的环境
| 可分享的工作在自己的领域内工作并分享你的发现 |
|---|
| | 这里有一些想法,如何将你所学的内容提升到下一个层次。如果你愿意,与世界分享你的结果,并确保查看其他人所做的事情。这是一个双赢的局面,希望你能充分利用它。
-
#gdrl_ch12_tf01: 选择一个连续动作空间环境,并在该环境中测试本章中你所学到的所有智能体。注意,你可能需要为 PPO 更改它。但是,了解这些算法如何比较是值得的。
-
#gdrl_ch12_tf02: 获取 PPO,并将其添加到上一章的笔记本中。在类似的环境中测试它,并比较结果。注意,这个 PPO 实现会在进行任何更新之前缓冲一些经验。确保调整代码或超参数,以使比较公平。PPO 的表现如何?请确保在比 cart-pole 更具挑战性的环境中进行测试!
-
#gdrl_ch12_tf03: 还有其他最大熵深度强化学习方法,例如软 Q 学习。找到实现这种最大熵目标的算法列表,选择其中一个,并自己实现它。测试它,并将你的实现与其他智能体(包括 SAC)进行比较。创建一篇博客文章,解释这些方法的优缺点。
-
#gdrl_ch12_tf04: 在一个具有高维观察空间和连续动作空间的高维观察空间环境中测试本章中的所有算法。例如,查看赛车环境(
gym.openai.com/envs/CarRacing-v0/)。任何类似的都可以。修改代码,让智能体在这些环境中学习。 -
#gdrl_ch12_tf05:在每一章中,我都使用最后的标签作为通用的标签。请随意使用这个标签来讨论任何与本章相关的工作。没有比为自己创造作业更令人兴奋的了。确保分享你打算调查的内容和你的结果。
用你的发现写一条推文,@提及我 @mimoralea(我会转发),并使用列表中的特定标签来帮助感兴趣的人找到你的结果。没有对错之分;你分享你的发现并检查他人的发现。利用这个机会社交,做出贡献,让自己更受关注!我们正在等待你的加入!以下是一条推文示例:“嘿,@mimoralea。我创建了一篇博客文章,列出了学习深度强化学习的资源列表。查看它在这里<链接>。#gdrl_ch01_tf01”我会确保转发并帮助他人找到你的作品。|
13 向着通用人工智能迈进
在这一章节
-
你将回顾本书中学到的算法,以及了解那些没有深入探讨的深度强化学习方法。
-
你将了解高级深度强化学习技术,当这些技术结合在一起时,能够让智能体展现出更广泛的智能。
-
你将得到我关于如何追逐梦想并为这些令人惊叹的人工智能和深度强化学习领域做出贡献的离别建议。
我们的最终目标是创建出能够像人类一样有效地从经验中学习的程序。
—— 约翰·麦卡锡 人工智能领域的创始人 Lisp 编程语言的发明者
在这本书中,我们调查了广泛的决策算法和强化学习智能体;从你在第三章中学到的规划方法到我们在上一章中涵盖的最先进的深度强化学习智能体。本书的重点是教授算法的细节;然而,深度强化学习(DRL)的内容远不止本书所涵盖的,我希望你能有一个前进的方向。
我设计这一章节是为了强调几个要点。在第一部分,我们回顾了整本书。我希望你能退后一步,再次审视全局。我希望你能看到你所学到的,以便你可以自己决定下一步该去哪里。我还提到了几种我在书页用尽之前未能涵盖的显著类型的智能体。但要知道,尽管有更多类型的算法,本书中所学的内容涵盖了基础的方法和概念。
在概述了所涵盖的内容和未涵盖的内容之后,我介绍了几个在深度强化学习(DRL)中更为高级的研究领域,这些领域可能最终会导致通用人工智能(AGI)的创造。我知道 AGI 是一个热门话题,很多人都在误导性地使用它。作为一个既令人兴奋又具有争议的话题,人们用它来吸引注意力。不要把你的精力浪费在那些人身上;不要被误导;不要分心。相反,专注于眼前的事情。朝着你的目标前进,无论你的目标是什么。
我确实相信人类能够创造出通用人工智能(AGI),因为我们将会永远地尝试。理解智能和自动化任务是我们长久以来渴望并努力追求的目标,而且这种情况永远不会改变。我们试图通过哲学和自我理解来理解智能。我们通过内省寻找关于智能的答案。我认为大多数人工智能研究者本质上也是哲学家。他们利用他们在强化学习中学到的知识来提升自己,反之亦然。
此外,人类喜欢自动化;这就是智能让我们能够做到的事情。我们将继续尝试自动化生活,我们最终会实现这一点。现在,虽然我们可以争论通用人工智能(AGI)是否是超越世界的类人机器人的开始,但今天,我们仍然无法训练一个代理以超人类水平玩所有 Atari 游戏。也就是说,单个训练过的代理不能玩所有游戏,尽管可以独立训练单个通用算法。但是,在考虑 AGI 时,我们应该谨慎。
为了结束这一章和整本书,我为你提供了前进的想法。我收到了很多关于如何将 DRL 应用于自定义问题、你自己的环境的提问。我在全职工作中以此为生,所以我可以分享我的看法,关于如何着手去做。我还为有兴趣的人提供职业建议,以及一个告别信息。这是另一个章节:让我们这么做吧。
覆盖了什么,又明显没有涵盖什么?
本书涵盖了深度强化学习的大部分基础,从马尔可夫决策过程(MDPs)及其内部工作原理到最先进的演员-评论家算法以及如何在复杂环境中训练它们。深度强化学习是一个活跃的研究领域,每个月都会发布新的算法。该领域正在快速发展,不幸的是,不可能在单本书中提供对一切的高质量解释。
幸运的是,大多数被省略的内容都是高级概念,在大多数应用中并不需要。但这并不意味着它们不重要;我强烈建议你继续学习深度强化学习(DRL)的旅程。你可以依赖我在路上的帮助;我很容易找到。然而,就这本书省略的内容而言,我只认为两个是基本要素;它们是基于模型的深度强化学习方法和无导数优化方法。
在本节中,我们快速回顾了你在本书中学到的算法和方法,并简要介绍了这两个明显缺失的基本方法。

深度强化学习不同算法方法的比较
马尔可夫决策过程
前两章是强化学习领域的介绍以及我们描述试图解决的问题的方式。MDPs 是一个需要记住的基本概念,即使它们看起来简单且有限,它们也是强大的。我们在这个领域可以探索的还有很多。我希望你从这些概念中获得的能力是能够将问题视为 MDPs。自己练习一下。思考一个问题,并将其分解为状态、观察、动作以及使该问题成为 MDPs 的所有组成部分。

冰冻湖环境的转换函数
你会注意到,尽管世界看起来是非平稳和非马尔可夫的,但我们仍然可以转换一些使其看起来如此的东西,然后将其视为一个 MDP。现实世界的概率分布是否改变,或者是我们没有足够的数据来确定实际的分布?未来是否取决于过去的状态,或者状态空间如此高维,以至于我们无法想象世界的历史是单个状态的一部分?再次,作为一个练习,思考问题并尝试将它们拟合到这个 MDP 框架中。如果你想要将 DRL 应用于你的问题,这可能是有用的。
规划方法
在第三章中,我们讨论了帮助你找到具有 MDP 的问题的最优策略的方法。这些方法,如值迭代和政策迭代,迭代地计算最优值函数,从而允许快速提取最优策略。策略不过是普遍的计划——针对每种情况的计划。

在 SWF 环境中对始终向左策略进行策略评估
本节最重要的两点收获是首先。这些算法隔离了序列决策问题。由于它们需要 MDP,因此没有不确定性,由于它们只适用于离散状态和动作空间,因此没有复杂性。其次,这里有一个普遍的模式可以观察;我们对评估行为感兴趣,可能与我们改进它们的兴趣一样大。这种认识是我一段时间后才意识到的。对我来说,改进、优化听起来更有趣,所以策略评估方法没有引起我的注意。但后来你理解,如果你正确评估,改进就变得轻而易举。主要的挑战通常是准确和精确地评估策略。但是,如果你有 MDP,你就可以直接正确地计算这些值。
Bandit methods
第四章讲述了从评估反馈中进行学习。在这种情况下,我们通过移除 MDP 来了解强化学习的不确定性方面。我们隐藏了 MDP,但使其超级简单;一个单状态单步长 MDP,其中的挑战是在最少的回合数中找到最优动作或动作分布;也就是说,最小化总后悔。

在第四章,你学习了处理探索-利用权衡的更有效的方法
我们研究了多种不同的探索策略,并在几个 bandit 环境中进行了测试。但最终,我对那一章的目标是向你展示,不确定性本身就是一个值得单独研究的挑战。关于这个主题有一些非常好的书籍,如果你对此感兴趣,你应该追求这条道路;这是一个需要大量关注的合理路径。

10 臂高斯 bandit
从本章中提炼出正确的要点进入强化学习是,强化学习之所以具有挑战性,是因为我们没有访问到第三章中规划方法中的 MDP。没有 MDP 会产生不确定性,而我们只能通过探索来解决不确定性。探索策略是我们代理能够通过试错学习自行学习的理由,这也是这个领域令人兴奋的原因。
表格强化学习
第五章、第六章和第七章都是关于强化学习的顺序性和不确定性方面的细微差别。在可以更容易研究的方式下,即没有大型和高维状态或动作空间复杂性的情况下,不确定性下的顺序决策问题是强化学习的核心。
第五章讲述了评估策略,第六章讲述了优化策略,第七章讲述了评估和优化策略的高级技术。对我来说,这是强化学习的核心,而且对这些概念的理解有助于你更快地理解深度强化学习。不要将深度强化学习视为与表格强化学习分离的东西;那是错误的想法。复杂性只是问题的一个维度,但它是完全相同的问题。你经常看到顶级深度强化学习研究实验室发布解决离散状态和动作空间问题的论文。这没有什么可耻的。这通常是明智的方法,当你进行实验时,你应该记住这一点。不要从最高复杂性的问题开始;相反,先隔离,然后解决,最后再增加复杂性。
在这三章中,我们涵盖了各种算法。我们涵盖了评估方法,如首次访问和每次访问蒙特卡洛预测、时序差分预测、n-步时序差分(TD)和TD(λ))。我们还涵盖了控制方法,如首次访问和每次访问蒙特卡洛控制、SARSA、Q 学习、双 Q 学习以及更高级的方法,如 SARSA(λ)和 Q(λ),这两种方法都涉及替换和累积轨迹。我们还涵盖了基于模型的方法,如 Dyna-Q 和轨迹采样。

深度强化学习是强化学习更大领域的一部分
基于价值的深度强化学习
第八章、第九章和第十章都是关于基于价值的深度强化学习方法的细微差别。我们提到了神经拟合 Q 迭代(NFQ)、深度 Q 网络(DQN)、双深度 Q 网络(DDQN)、DDQN 中的对抗架构(对抗 DDQN)和优先级经验回放(PER)。我们从 DQN 开始,并逐个添加改进到这个基线方法。我们在小车-杆环境中测试了所有算法。
可以对这个基线算法实施许多改进,我建议你尝试一下。查看一个名为 Rainbow 的算法,并实现一些书中未提到的 DQN 的改进。写一篇关于它的博客文章,并与世界分享。在实现基于价值的深度强化学习方法时学到的技术对于其他深度强化学习方法至关重要,包括演员-评论家方法中的学习评论家。有许多改进和技术需要发现。继续对这些方法进行实验。
基于策略的演员-评论家深度强化学习
第十一章是关于基于策略的演员-评论家方法的介绍。在书的那个阶段,基于策略是强化学习的一个新方法,所以我们通过一个简单的算法介绍了这些概念,即 REINFORCE,它只参数化策略。为此,我们直接近似策略,完全没有使用任何价值函数。在 REINFORCE 中,我们用来优化策略的信号是蒙特卡洛回报,即代理在一段时间内实际体验到的回报。
接着,我们探索了一种学习价值函数以减少 MC 回报方差的算法。我们称这个算法为 vanilla 策略梯度(VPG)。这个名字有些随意,也许更好的名字应该是rEINFORCE with Baseline。尽管如此,重要的是要注意,尽管这个算法学习了一个价值函数,但它不是一个演员-评论家方法,因为它使用价值函数作为基线而不是作为评论家。这里的关键洞察是我们不使用价值函数进行自举,而且因为我们还使用 MC 回报来训练价值函数模型,所以它的偏差最小。算法中的唯一偏差是由神经网络引入的,没有其他。
然后,我们介绍了更多使用自举的先进演员-评论家方法。A3C 使用n-步回报;GAE,它是一种用于策略更新的 lambda 回报形式;以及 A2C,它使用同步更新策略。总的来说,这些是当前最先进的方法,你应该知道它们是可靠的方法,并且仍然被广泛使用。例如,A3C 的一个主要优点和独特特性是它只需要 CPU,并且可以比其他方法更快地训练,如果你没有 GPU 的话。
先进的演员-评论家技术
即使 A3C、GAE 和 A2C 都是演员-评论家方法,它们在独特地使用评论家方面并不相同。在第十二章中,我们探讨了使用独特方法的情况。例如,许多人认为 DDPG 和TD3 是演员-评论家方法,但它们更适合作为连续动作空间的基于价值的方法。如果你看看 A3C 使用演员和评论家的方式,例如,你会在 DDPG 中发现实质性的差异。无论如何,DDPG 和TD3 都是最先进的方法,无论是否是演员-评论家,在解决问题时影响不大。主要的注意事项是这两种方法只能解决连续动作空间的环境。它们可能是高维动作空间,但动作必须是连续的。其他方法,如 A3C,可以解决连续和离散动作空间。
SAC 是一种独特的动物。它之所以跟在 DDPG 和TD3 之后,仅仅是因为 SAC 使用了与 DDPG 和TD3 许多相同的技巧。但 SAC 的独特特性是它是一种熵最大化方法。值函数不仅最大化回报,还最大化策略的熵。这类方法很有前景,我不惊讶会看到源自 SAC 的新最先进的方法。
最后,我们探讨了另一种令人兴奋的演员-评论家方法,即 PPO。PPO 是一种演员-评论家方法,你可能已经注意到了,因为我们重用了 A3C 的大部分代码。PPO 的关键洞察力在于策略更新步骤。简而言之,PPO 一次只改进策略一点;我们确保策略在更新时不会改变太多。你可以将其视为一种保守的策略优化方法。PPO 可以轻松应用于连续和离散动作空间,并且 PPO 是 DRL 中一些最激动人心的结果背后的原因,例如 OpenAI Five。
在这些章节中,我们介绍了许多优秀的方法,但更重要的是,我们介绍了使你能够继续了解该领域的基础方法。许多现有的算法都源自本书中介绍过的算法,有一些例外,即基于模型的深度强化学习方法和无导数优化方法。在接下来的两个部分中,我将提供关于这些方法的信息,以便你能够继续探索深度强化学习之旅。

本书中的 DRL 算法
基于模型的深度强化学习
在第七章中,你学习了基于模型的重强化学习方法,例如 Dyna-Q 和轨迹采样。基于模型的重深度强化学习在本质上是你所预期的;使用深度学习技术来学习状态转移、奖励函数或两者,然后使用这些信息进行决策。与你在第七章中学到的那些方法一样,基于模型的重深度强化学习的一个显著优点是样本效率;基于模型的方法在强化学习中是最有效的样本使用方法。

需要考虑的基于模型的重强化学习算法
| 米格尔的类比如何无导数方法工作 | |
|---|---|
| 要直观地比较基于梯度和无梯度方法的差异,想象一下“热与冷”这个游戏。是的,就是孩子们玩的那种游戏,其中一个孩子,猎人,需要找到隐藏的感兴趣物品,而其他知道物品位置的孩子,如果猎人离物品远,就喊“冷”,如果猎人靠近物品,就喊“热”。在这个类比中,猎人的位置是神经网络的参数。感兴趣的隐藏物品是全局最优解,要么是损失函数的最小值,要么是目标函数的最大值。目的是优化猎人和物品之间的距离。为此,在游戏中,你使用孩子们喊“冷”或“热”来优化猎人的位置。这里这个类比变得有趣起来。想象一下,有孩子们,除了喊“冷”或“热”,当猎人靠近物品时,他们会变得更响亮。你知道,孩子们很容易兴奋,而且不能保守秘密。当你听到他们从轻声说“冷”到每秒声音越来越大时,你知道,作为猎人,你正在朝着正确的方向前进。可以使用那个“梯度”信息来最小化距离。使用这些信息到达感兴趣物品的方法就是基于梯度的方法。如果信息以连续的形式出现,意味着孩子们每秒喊几次,声音变大或变小,从喊“冷”到喊“热”随着距离变化,那么你可以使用信息幅度的增加或减少,即梯度,来到达物品。太棒了!另一方面,想象一下,喊信息的孩子们很刻薄,或者可能只是不完美。想象一下,他们给出的是不连续的信息。例如,当猎人处于某些区域时,他们可能不允许说任何话。他们从“轻声冷”到一段时间什么也不说,然后再回到“轻声冷”。或者,也许想象一下,物品就在一堵长墙的中间。即使猎人靠近物品,猎人也无法用梯度信息触及物品。猎人会靠近物品,所以正确喊的应该是“热”,但现实中,物品在墙的另一边,无法触及。在所有这些情况下,可能基于梯度的优化方法并不是最好的策略,无梯度方法,即使移动是随机的,也可能更适合找到物品。无梯度方法的方法可能就像这样简单。猎人会随机选择一个地方去,在到达那里时忽略“梯度”信息,然后检查喊叫的孩子们,然后尝试另一个随机位置。在得到几个随机位置的概念后,比如 10 个,猎人会选取前 3 个,并尝试从这 3 个明显更好的位置中随机变化。在这种情况下,梯度信息是没有用的。相信我,这个类比可以继续下去,但我将在这里停止。底线是,基于梯度和无梯度方法只是到达感兴趣点的策略。这些策略的有效性取决于手头的问题。 |
除了样本效率之外,使用基于模型的方法的另一个固有优势是可迁移性。学习世界动态的模型可以帮助你完成不同的相关任务。例如,如果你训练一个智能体来控制机械臂去抓取一个物体,一个学习环境如何对智能体的移动尝试做出反应的基于模型的智能体可能会更容易在后续任务中学会抓取那个物体。请注意,在这种情况下,学习奖励函数的模型对迁移并没有帮助。然而,学习环境对其运动命令的反应是可迁移的知识,这可以允许完成其他任务。上次我检查时,物理定律已经几百年来没有更新了——这真是一个缓慢发展的领域!
值得一提的还有几个优点。首先,学习模型通常是一个监督学习任务,这比强化学习更加稳定和表现良好。其次,如果我们有一个准确的环境模型,我们可以使用理论上有根据的规划算法,如轨迹优化、模型预测控制,甚至启发式搜索算法,如蒙特卡洛树搜索。最后,通过学习模型,我们总体上更好地利用经验,因为我们从环境中提取了最多的信息,这意味着有更多更好的决策可能性。
但并非一切尽善尽美;基于模型的学习也有挑战。在使用基于模型的方法时,有几个缺点需要考虑。首先,除了策略、价值函数或两者之外,学习环境动态的模型在计算上更加昂贵。而且,如果你只学习动态模型,那么模型误差的累积会使你的算法变得不切实际。
动态的各个方面并不都是对策略有直接益处的。我们在主张直接学习策略而不是学习价值函数时已经讨论了这个问题。想象一下倒水任务;如果你需要首先学习流体动力学、流体的粘度和流体流动,而你只想拿起一个杯子倒水,那么我们就过于复杂化了任务。试图学习环境的模型比直接学习策略更复杂。
必须记住,深度学习模型是数据饥渴的。正如你所知,为了从深度神经网络中获得最佳效果,你需要大量的数据,这对基于模型的深度强化学习方法来说是一个挑战。这个问题还与在神经网络中很难估计模型不确定性的事实相叠加。因此,鉴于神经网络试图泛化,无论模型不确定性如何,你可能会得到长期预测结果完全是垃圾。
这个问题提出了一个论点,即基于模型的方法是最具样本效率的,这是值得怀疑的,因为你可能最终需要比在无模型方法下学习良好策略所需的数据更多的数据来学习一个有用的模型。然而,如果你拥有那个模型,或者独立于任务获取该模型,那么你可以将该模型用于其他任务。此外,如果你要使用“浅层”模型,例如高斯过程或高斯混合模型,那么我们又回到了起点,基于模型的方法再次成为最具样本效率的方法。
我希望你从这个部分开始,知道这并不是关于基于模型与无模型之争。尽管你可以结合基于模型和无模型的方法,得到有吸引力的解决方案,但最终,工程学并不是关于这个的,同样,这也不是基于价值与基于策略之争,也不是演员-评论家之争。当你需要螺丝刀时,不要用锤子。我的工作是描述每种算法适合什么,但如何正确使用这些知识取决于你。当然,探索、享受乐趣很重要,但在解决问题的时候,要明智地选择。
无导数优化方法
深度学习是使用多层函数逼近器来学习函数。一个传统的深度学习用例如下。首先,我们创建一个参数模型,它反映了感兴趣的函数。然后,我们定义一个目标函数,以了解模型在任何给定时间有多错误。接下来,我们通过计算参数移动的方向来迭代优化模型,使用反向传播。最后,我们使用梯度下降更新参数。
反向传播和梯度下降是优化神经网络的实用算法。这些方法对于在给定范围内找到函数的最低点或最高点非常有价值;例如,损失函数或目标函数的局部最优。但有趣的是,它们并不是优化参数模型(如深度神经网络)的唯一方法,更重要的是,它们并不总是最有效的方法。
无导数优化,如遗传算法或进化策略,是一种近年来受到深度强化学习社区关注的不同的模型优化技术。无导数方法,也称为无梯度、黑盒和零阶方法,不需要导数,在基于梯度的优化方法受限制的情况下可能很有用。例如,在优化离散、不连续或多模型函数时,基于梯度的优化方法会遇到困难。
无导数方法在许多情况下可能是有用且直接的。即使随机扰动神经网络的权重,如果计算资源足够,也能完成任务。无导数方法的主要优势是它们可以优化任意函数。它们不需要梯度来工作。另一个优势是这些方法易于并行化。使用无导数方法时,听到数百或数千个 CPU 并不罕见。另一方面,它们易于并行化是好事,因为它们是样本低效的。作为黑盒优化方法,它们没有利用强化学习问题的结构。它们忽略了强化学习问题的顺序性质,否则这些信息可以为优化方法提供有价值的信息。

无导数方法是极端情况
向 AGI 迈进的高级概念
在上一节中,我们回顾了本书涵盖的深度强化学习的基础概念,并简要提到了两种我们没有深入探讨的方法类型。但是,正如我之前提到的,仍然有许多高级概念,尽管它们对于深度强化学习的介绍不是必需的,但对于设计通用人工智能(AGI)至关重要,这是大多数 AI 研究者的最终目标。
在本节中,我们首先深入一步探讨 AGI,并论证 AI 代理需要具备的一些特质以应对需要更广泛智能的任务。我以高层次解释了这些特质及其意图,以便你能够继续你的 AI 学习之旅,也许有一天能贡献于这些尖端研究领域。
通用人工智能(AGI)是什么,再次?
在本书中,你已经看到了许多 AI 代理的例子,它们一开始看起来令人印象深刻。同一个计算机程序能够学习解决各种各样的问题,这是非常了不起的。此外,当你转向更复杂的环境时,很容易被这些结果冲昏头脑:AlphaZero 学会了下国际象棋、围棋和将棋。OpenAI Five 在 Dota2 游戏中击败了人类团队。AlphaStar 在星际争霸 II 游戏中击败了顶尖职业选手。这些都是令人信服的通用算法。但这些通用算法是否显示出任何通用智能的迹象?首先,什么是通用智能?
通用智能是指结合各种认知能力来解决新问题的能力。对于通用人工智能(AGI),我们期望计算机程序展现出通用智能。好的。现在,让我们提出以下问题:本书中提出的任何算法,或者甚至像 AlphaZero、OpenAI Five 和 AlphaStar 这样的最先进方法,是否是通用人工智能的例子?嗯,并不清楚,但我认为不是。
你看,一方面,许多这些算法可以使用“多种认知能力”,包括感知和学习来解决新的任务,比如说,玩乒乓球。如果我们坚持我们的定义,算法使用多种认知能力来解决新问题是加分项。然而,这些算法中最令人不满意的部分之一是,除非你训练它们,否则这些训练过的代理在解决新问题上都不擅长,而这通常需要数百万个样本才能得到任何令人印象深刻的结果。换句话说,如果你训练一个 DQN 代理从像素中玩乒乓球,那么这个训练过的代理,在乒乓球上可以达到超人的水平,但对如何玩好《Breakout》一无所知,并且必须训练数百万帧才能显示出任何技能。
人类没有这个问题。如果你学会了玩乒乓球,我相当确信你可以在两秒钟内学会玩《Breakout》。两款游戏都有相同的任务,那就是用球拍击球。另一方面,即使是 AlphaZero 代理,这个在多个根本不同的棋类游戏中拥有史上最令人印象深刻技能的计算机程序,并且能够打败那些致力于这些游戏的职业玩家,但它永远不会帮你洗衣服。
某些 AI 研究人员表示,他们的目标是创建能够感知、学习、思考和甚至像人类一样感受情绪的 AI 系统。能够学习、思考、感受,甚至可能看起来像人的机器,无疑是一个令人兴奋的想法。其他研究人员则采取更实际的方法;他们并不一定想要一个像人类一样思考的 AI,除非像人类一样思考是做出一顿好午餐的必要条件。也许情感是造就一位伟大厨师的因素,谁知道呢。关键是,尽管有些人希望 AGI(通用人工智能)可以委托,停止做日常任务,但其他人则有一个更哲学的目标。创建 AGI 可能是理解智能本身、理解自我的一条途径,而这本身就是人类的一项非凡成就。
无论哪种方式,每个 AI 研究人员都会同意,无论最终目标是什么,我们仍然需要展示更多通用和可迁移技能的 AI 算法。在 AI 系统能够执行更类似人类任务的许多特质中,有许多特质是 AI 系统可能需要的,例如洗衣服、做午餐或洗碗。有趣的是,这些日常任务对于 AI 来说是最难解决的。让我们回顾一下目前正在推动深度强化学习和人工智能展示通用智能迹象的几个研究领域。
以下章节介绍了几个你可能希望在深入学习将人工智能领域推向人类水平智能的先进深度强化学习技术时进一步探索的概念。我只用了几句话,以便让你尽可能多地了解它们。我的目的是指给你门路,而不是门里面的东西。决定打开哪扇门,由你自己来定。

劳动力革命
高级探索策略
研究领域中的一个令人兴奋的成果与奖励函数有关。在这本书中,你已经看到了从奖励信号中学习的智能体,但有趣的是,最近的研究表明智能体可以在没有任何奖励的情况下学习。从除了奖励之外的事物中学习是一个令人兴奋的想法,这可能是开发类似人类智能的必要条件。如果你观察一个婴儿学习,会发现有很多无监督和自监督学习在进行。当然,在他们生命中的某个时刻,我们会奖励我们的孩子。你知道你得到 A,你得到 B;你的薪水是 x,你的薪水是 y。但智能体并不总是追求我们为他们设定的奖励。生活的奖励函数是什么?是职业成功吗?是有孩子吗?这并不清楚。
现在,从强化学习问题中移除奖励函数可能会有些可怕。如果我们没有为智能体定义要最大化的奖励函数,我们如何确保他们的目标与我们的目标一致?我们如何创造适合人类目标的人工通用智能?也许,为了创造类似人类的智能,我们需要给智能体选择命运的自由。无论如何,对我来说,这是需要追求的关键研究领域之一。
反向强化学习
没有奖励函数也可以学习行为,尽管我们通常更喜欢奖励函数,但首先学习模仿人类可以帮助用更少的样本学习策略。这里有几个相关的领域可以寻找。行为克隆是将监督学习技术应用于从演示中学习策略,通常是从人类那里学习。正如其名所示,这里没有推理发生,只是泛化。一个相关的领域,称为反向强化学习,包括从演示中推断奖励函数。在这种情况下,我们不仅仅是复制行为,而是在学习另一个智能体的意图。推断意图可以是实现多个目标的有力工具。例如,在多智能体强化学习中,对于对抗和协作设置,了解其他智能体的目标可以是有用的信息。如果我们知道一个智能体想要做什么,以及它想要做的事情是否与我们的目标相悖,我们可以在为时已晚之前制定阻止它的策略。
但是,逆强化学习允许智能体学习新的策略。从另一个智能体(如人类)那里学习奖励函数,并从这个学到的奖励函数中学习策略,这种技术通常被称为学徒学习。在学习逆强化学习时,一个值得考虑的有趣点是,奖励函数通常比最优策略更简洁。尝试学习奖励函数在多种情况下是有意义的。从演示中学习策略的技术也被称为模仿学习,通常是在学习策略之前推断奖励函数还是直接进行行为克隆。模仿学习的常见用例是将智能体初始化到足够好的策略。例如,如果一个智能体必须从随机行为中学习,那么它可能需要很长时间才能学习到一个好的策略。想法是模仿人类,即使不是最优的,也可能通过与环境更少的交互导致最优策略。然而,这并不总是情况,由人类演示预训练的策略可能会引入不希望的偏差,并阻止智能体找到最优策略。
迁移学习
你可能已经注意到,在某个环境中训练的智能体通常不能转移到新的环境中。强化学习算法是通用目的的,意味着同一个智能体可以在不同的环境中进行训练,但它们没有通用智能,它们学到的知识不能直接转移到新的环境中。
迁移学习是一个研究领域,它探讨将知识从一组环境转移到新环境的方法。例如,如果你有深度学习背景,可能会觉得以下方法直观:所谓的微调。类似于在监督学习中重用预训练网络的权重,在相关环境中训练的智能体可以重用在不同任务上卷积层学到的特征。如果环境相关,例如在 Atari 游戏中,那么一些特征可能是可迁移的。在某些环境中,甚至策略也可以迁移。

实际世界中的仿真到现实迁移学习任务是一个常见需求
研究使智能体学习更通用技能的一般领域被称为迁移学习。迁移学习的另一常见用途是将在模拟中学习到的策略转移到现实世界。从模拟到现实的迁移学习在机器人领域是一个常见需求,其中训练控制机器人的智能体可能很棘手、成本高昂且危险。它也不像在模拟中的训练那样可扩展。一个常见需求是在模拟中训练智能体,然后将策略转移到现实世界。一个常见的误解是,为了将智能体从模拟转移到现实世界,模拟需要具有高保真度和现实性。有研究表明情况正好相反。是观察的多样性、多样性使得智能体更具迁移性。如领域随机化等技术处于这一研究领域的最前沿,并显示出巨大的潜力。
多任务学习
另一个被称为多任务学习的研究领域,从不同的角度研究迁移学习。在多任务学习中,目标是训练多个任务,而不是一个,然后将它们转移到新任务。在这种情况下,基于模型的强化学习方法会浮现在脑海中。例如,在机器人领域,使用同一台机器人学习各种任务可以帮助智能体学习环境的动态的鲁棒模型。智能体了解重力、如何向右或向左移动等等。无论任务如何,学习的动态模型都可以转移到新任务。

多任务学习包括在多个相关任务上训练并在新任务上测试
课程学习
多任务学习的常见用例场景是将任务分解为按难度级别排序的多个任务。在这种情况下,智能体会经历一个课程,逐步学习更复杂的任务。课程学习在开发场景时是有意义的,并且可能很有用。如果你需要为智能体创建一个解决问题的环境,那么创建一个最直接的场景并具有密集的奖励函数通常是有意义的。通过这样做,你的智能体可以快速展示学习目标方面的进步,这验证了你的环境运行良好。然后,你可以增加复杂性并使奖励函数更稀疏。在你为几个场景这样做之后,你自然会创建一个智能体可以使用的课程。然后,你可以在越来越复杂的环境中训练你的智能体,并希望智能体能够更快地达到期望的行为。
元学习
另一个超级激动人心的研究领域被称为元学习。如果您这么想,我们正在手动编码智能体来学习许多不同的任务。在某个时刻,我们成为了瓶颈。如果我们能够开发出一个智能体,它不是学习解决具有挑战性的任务,而是学习如何学习自己,我们就可以从方程式中移除人类;好吧,不是完全移除,但朝着那个方向迈出一步。学习如何学习是使用从学习多个任务中获得的经验来提高自身学习能力的激动人心的方法。这从直觉上是有意义的。从元学习中出现的一些其他激动人心的研究路径是自动发现神经网络架构和优化方法。请密切关注这些。
层次化强化学习
通常,我们发现自己在开发具有多个视野问题的环境。例如,如果我们想让智能体找到最佳的高级策略,但只给它低级控制命令来执行动作,那么智能体需要学会从低级动作空间到高级动作空间的转换。直观地说,大多数智能体的策略中存在层次结构。当我规划时,我是在一个更高级的动作空间中进行的。我想的是去商店,而不是移动我的手臂去商店。层次化强化学习使智能体能够内部创建一个动作层次结构来处理长期问题。智能体不再推理左右命令,而是更多地考虑去这里或那里。
多智能体强化学习
没有其他智能体,世界就不会那么有趣。在多智能体强化学习中,我们研究智能体在周围有多个智能体时如何学习的技术。在多智能体环境中学习时出现的一个主要问题是,随着您的智能体学习,其他智能体也在学习,因此改变它们的行为。问题是这种变化使得观察变得非平稳,因为您的智能体学习的内容在其他智能体学习后立即过时,因此学习变得具有挑战性。
一种令人兴奋的协作多智能体强化学习方法是在训练过程中让评论家使用所有智能体的完整状态信息。这里的优势是您的智能体通过评论家学会合作,然后我们可以在测试期间使用更真实的观察空间来应用策略。共享完整状态可能看起来不切实际,但您可以将其视为类似于团队练习的方式。在练习期间,一切都被允许。比如说,您是一名足球运动员,当您做出这个动作时,您可以告诉其他智能体您打算在边路跑动,等等。在训练期间,您可以得到关于动作的完整信息进行练习;然后,在测试期间,您只能使用有限信息的策略。
在研究多智能体强化学习时,另一个吸引人的想法是分层强化学习可以被视为多智能体强化学习的另一种案例。如何做到这一点呢?想想多个智能体在不同时间范围内做出决定。多时间结构类似于大多数公司做生意的方式。顶层的人为未来几年设定更高层次的目标,其他人则决定如何按月和按日实现目标。顶层的人为底层的人设定目标。整个系统因所有智能体的表现而获得奖励。
当然,多智能体强化学习不仅适用于合作案例,也适用于对抗案例,这可能是最令人兴奋的。人类通常将竞争和对手视为本质上不幸的事情,但多智能体强化学习表明,我们的对手往往是让我们变得更好的最佳方式。许多最近的强化学习成功故事背后的训练技术包括对手:要么是同一智能体的先前版本,例如在自我对战中,要么是在所有比赛结束后形成的整个锦标赛式的其他智能体分布——只有最好的智能体才能生存。对手往往使我们变得更好,不管好坏,它们可能对于最佳行为是必需的。
可解释人工智能、安全性、公平性和伦理标准
研究中还有一些其他的关键领域,尽管它们并非直接推动人类水平智能的发展,但对于人工智能解决方案的成功开发、部署和采用是基础性的。
可解释人工智能 是一个研究领域,试图创造更易于人类理解的智能体。动机是明显的。法律法庭可以审问任何违法的人;然而,机器学习模型并不是为了可解释性而设计的。为了确保人工智能解决方案能够快速被社会接受,研究人员必须研究减轻可解释性问题的方法。为了明确起见,我认为这不是一个必要条件。我更喜欢让人工智能给我提供准确的股市预测,无论它能否向我解释原因。然而,这两个决定都不是直截了当的。在涉及人类生死存亡的决定中,事情会迅速变得复杂。
安全性是另一个应得到更多关注的研究领域。通常情况下,人工智能以对人类来说过于明显的方式失败。此外,人工智能容易受到人类无法抵御的攻击。我们需要确保当人工智能被部署时,我们知道系统如何对各种情况进行反应。目前人工智能还没有通过经典验证和验证(V&V)软件方法的方式,这对人工智能的采用构成了重大挑战。
公平性是另一个关键问题。我们需要开始思考谁控制着人工智能。如果一家公司为了社会利益最大化利润而创建人工智能,那么人工智能技术的意义何在?我们已经在广告中看到了类似的情况。顶级公司使用人工智能通过一种操纵方式来最大化收益。这些公司是否应该被允许为了利润而这样做?当人工智能越来越好时呢?这样做的目的是什么,通过操纵来摧毁人类?这些都是需要认真考虑的事情。
最后,人工智能伦理标准也是近期受到关注的另一个问题,这得益于蒙特利尔人工智能负责任发展宣言。这些是针对人工智能的 10 项伦理原则,旨在服务于社会的利益,而不仅仅是盈利公司。当你准备贡献时,这些是几个需要考虑的顶级领域。
接下来会发生什么?
虽然这一部分标志着这本书的结束,但它应该只是你为人工智能和 DRL 领域做出贡献的开始或延续。我写这本书的意图不仅是让你理解 DRL 的基础知识,而且是要让你加入这个精彩的社区。你需要的不仅仅是继续旅程的承诺。你可以做很多事情,在这一部分,我想给你一些想法,帮助你开始。记住,世界是一个需要各种声音类型和才能的合唱团;你的工作是接受赋予你的才能,尽你所能发展它们,并全力以赴地扮演你的角色。虽然我可以给你提供想法,但接下来发生什么取决于你;世界需要并等待着你的声音。
如何使用深度强化学习(DRL)解决定制问题
强化学习(RL)算法有一些非常酷的特点,我想在你了解其他类型的智能体时让你记住。事实是,大多数 RL 智能体可以解决你选择的任何问题,只要你能将问题表示为一个正确的 MDP(马尔可夫决策过程),就像我们在第二章讨论的那样。当你问自己,“X 或 Y 算法能解决什么问题?”时,答案是其他算法能解决的问题。虽然在这本书中,我们专注于一些算法,但所有展示的智能体都可以通过一些超参数调整来解决许多其他环境。解决定制环境的需求是许多人想要解决的问题,但可能需要另一本书来正确处理。我的建议是查看一些在线可用的示例。例如,Atari 环境在后台使用名为 Stella 的模拟器。环境通过模拟器与环境之间传递图像进行观察和行动。同样,MuJoCo 和 Bullet 物理仿真引擎是驱动连续控制环境的后端。看看这些环境是如何工作的。
注意观察是如何从模拟传递到环境,然后再传递到智能体的。然后,智能体选择的动作传递到环境中,然后再传递到仿真引擎。这种模式很普遍,所以如果你想创建一个自定义环境,调查其他人是如何做到的,然后自己动手做。你想要为智能体创建一个学习在股市中投资的环境吗?考虑一下哪些平台有 API 允许你这样做。然后,你可以使用相同的 API 创建不同的环境。例如,一个环境可以购买股票,另一个可以购买期权,等等。最先进的深度强化学习方法有如此多的潜在应用,而我们可用的优质环境数量有限,这真是遗憾。这个领域的贡献无疑是受欢迎的。如果你想要创建一个环境,但没有找到,考虑投入时间创建自己的,并与世界分享。
展望未来
你已经学到了很多,这一点毫无疑问。但是,如果你从大局出发,你会发现还有更多东西需要学习。现在,如果你进一步放大视角,你会意识到还有更多未知的领域;是之前没有人学习过的东西。你会发现,人工智能社区追求的目标并不容易;我们正在试图理解大脑是如何工作的。
事实上,即使是其他领域,比如心理学、哲学、经济学、语言学、运筹学、控制理论等等,它们的目标都是相同的,每个领域都从自己的角度出发,使用自己的语言。但归根结底,所有这些领域都会从理解大脑如何工作、人类如何做决策以及如何帮助他们做出最佳决策中受益。以下是我们前进的一些想法。
首先,找到你的动力、你的抱负和专注力。有些人唯一的愿望就是探索;去发现关于大脑的事实是令人兴奋的。其他人则希望留下一个更美好的世界。无论你的动力是什么,找到它。找到你的驱动力。如果你不习惯阅读研究论文,除非你知道你的动机,否则你不会喜欢它们。当你找到你的动力和驱动力时,你必须保持冷静、谦逊和透明;你需要你的驱动力来集中精力,努力实现你的目标。不要让你的兴奋阻碍你。你必须学会将你的动力放在心中,同时继续前进。我们能够集中精力的能力经常受到无数现成的干扰的威胁。我保证每 15 分钟我的手机上都会有新的通知。而我们被训练去认为这是好事。这并不是。我们必须重新控制我们的生活,能够长时间、专注地投入到我们感兴趣、我们热爱的事物中。练习专注力。
其次,平衡学习和贡献,并给自己留出休息的时间。你认为如果接下来 30 天,我每天摄入 5000 卡路里,消耗 1000 卡路里,会发生什么?如果相反,我每天摄入 1000 卡路里,消耗 5000 卡路里呢?如果我是一个运动员,每天消耗 5000 卡路里,但每周都进行训练呢?没错,所有这些对身体的危害都是显而易见的。同样,对心智来说也是如此;有些人认为他们需要学习多年才能有所作为,所以他们阅读、观看视频,但并不付诸实践。另一些人认为他们不再需要阅读任何论文;毕竟,他们已经实现了一个 DQN 智能体,并撰写了一篇关于它的博客文章。他们很快就会变得过时,缺乏思考的动力。有些人做对了这两件事,但从未包括放松、享受家庭和反思的时间。这是错误的方法。找到一种平衡你所得和所给予的方法,并留出休息的时间。我们也是心智的运动员,心智中过多的“脂肪”——没有目的性地输入过多信息,会让你变得迟钝和缓慢。写太多没有进行研究的博客文章,会让你变得过时、重复和枯燥。休息不足,你将无法为长期规划做出充分的准备。
此外,要知道你不可能学会一切。再次强调,我们是在学习心智,而关于心智的信息有很多。要明智,对阅读的内容要挑剔。作者是谁?她的背景是什么?尽管如此,你可以阅读它,但要有更高的自我意识。尽量多给予。你应该能够用另一种方式解释你所学的知识。这句话“不要重复造轮子”至多有些误导性。最好是你自己尝试一些事情;这是至关重要的。如果你早期就开始探索,你可能会发现自己有一个很好的想法,后来你意识到这个想法之前已经被研究过了。这没有什么可耻的;对你来说,继续前进比等待一个解决世界饥饿问题的顿悟时刻更为关键。我听说 Rich Sutton 说过类似的话:“对你来说显而易见的是你最大的贡献。”但如果你不允许自己“重复造轮子”,你就有可能不分享“对你来说显而易见”的东西,认为它可能不值得。我并不是说你需要发表一篇关于你思考过的这个新算法——Q-learning——的论文。我是在请求,请不要让害怕做“无价值”的工作阻止你进行实验。底线是继续阅读,继续探索,继续贡献,让这一切都沉淀下来。这是一个循环;这是一个流程,所以让它继续下去。
第三点也是最后一点,拥抱这个过程,让自己沉浸在这条道路上。你的梦想只是让你开始行动的一种方式,但你的梦想是在行动中实现的。深入其中;不要只是跟随别人的做法,要跟随你的兴趣。批判性地思考你的想法,进行实验,收集数据,尝试理解结果,并从结果中抽离出来。不要让你的实验带有偏见,去发现事实。如果你能长时间地沉浸其中,你开始专业化,这是好事。这个领域如此广泛,不可能在所有事情上都做得很好。然而,如果你长时间地跟随你的兴趣和直觉,你自然会花更多的时间在某些事情上,而不是其他事情上。继续前进。我们中的一些人感到需要保持知情,一旦我们开始提出没有答案的问题,我们就感到需要回到岸边。不要害怕提出难题,并努力寻找答案。没有愚蠢的问题;每个问题都是解开谜团的一个线索。继续提问。继续玩游戏,并享受它。
现在就让自己出去吧!
假设你刚刚完成了这本书,确保立即出去,思考如何将这些事情组合起来,并为这个了不起的社区做出贡献。比如写一篇关于你感兴趣的、这本书没有涵盖的算法的博客文章?或者调查本章讨论的一些高级概念,并分享你发现的内容?写一篇博客文章,制作一个视频,让世界知道这件事。成为运动的一部分;让我们找出什么是智能,让我们一起构建智能系统。除非现在是正确的时机,否则永远不会是正确的时机。
摘要
就这些了!你做到了!这本书到此结束。球现在在你这边。
在第一章中,我将深度强化学习定义为:“深度强化学习是一种机器学习方法,旨在创建能够解决需要智能问题的计算机程序。DRL 程序的独特属性是通过利用强大的非线性函数逼近,从同时具有序列性、评估性和采样的反馈中通过试错进行学习。”
我提到对我来说成功就是,在你完成这本书之后,你应该能够回到这个定义并精确地理解它。我说你应该能够说出我为什么使用这些词,以及这些词在深度强化学习背景下的含义。
我成功了吗?你现在直觉上理解这个定义了吗?现在轮到你将你的奖励发送给这本书背后的代理人了。这个项目是-1,0,还是+1?无论你的评论如何,我都会像 DRL 代理一样从反馈中学习,我期待阅读你的评论和你的想法。现在,我的部分已经完成了。
在本章的最后,我们回顾了本书所教授的所有内容,我们还讨论了我们跳过的核心方法,以及一些可能最终在人工通用智能智能体的创建中发挥作用的先进概念。
作为告别信息,我想首先感谢你给我分享我对深度强化学习领域的看法的机会。我还想鼓励你继续前进,专注于日常,更多地思考你接下来能做什么,用你当前的能力,用你独特的才能。
到现在为止,你已经
-
直观地理解深度强化学习是什么;你知道最关键的深度强化学习方法的细节,从最基本和基础到最前沿。
-
有一个明确的方向感,因为你理解我们所学的知识如何融入深度强化学习和人工智能领域的整体图景。
-
准备好向我们展示你所拥有的,你独特的才能和兴趣。加油,让 RL 社区为你感到自豪。现在,轮到你了!
| 在自己的工作上努力,分享你的发现 |
|---|
| | 这里有一些想法,如何将你所学的知识提升到下一个层次。如果你愿意,与世界分享你的成果,并确保查看其他人所做的事情。这是一个双赢的局面,希望你能充分利用它。
-
#gdrl_ch13_tf01: 实现基于模型的深度强化学习方法。
-
#gdrl_ch13_tf02: 实现无梯度深度强化学习方法。
-
#gdrl_ch13_tf03: 实现一个多智能体环境或智能体,并分享它。
-
#gdrl_ch13_tf04: 使用本书未讨论的先进深度学习技术,以获得深度强化学习智能体的更好结果。为了给你一个想法,变分自编码器(VAE)可能是压缩观察空间的一个有希望的方法。这样,智能体可以更快地学习。还有其他深度学习技术吗?
-
#gdrl_ch13_tf05: 创建一个资源列表,用于学习几个更高级的技术,以开发人工通用智能,无论是否提及。
-
#gdrl_ch13_tf06: 从本章中关于 AGI 的某个方法中挑选你喜欢的算法,创建一个笔记本,并写一篇博客解释细节。
-
#gdrl_ch13_tf07: 创建一个现有有趣环境的列表。
-
#gdrl****_ch13_tf08: 创建一个你热衷于的定制环境,可能是独特的,也许是为 AI 玩游戏或股市等创建的包装器。
-
#gdrl_ch13_tf09: 更新你的简历并发给我,我会转发它。确保包括你参与的一些项目,当然,与 DRL 相关。
-
#gdrl_ch13_tf10: 在每一章中,我都在使用最后的标签作为通用的标签。请随意使用这个标签来讨论与本章相关的工作。没有什么比为自己创造作业更令人兴奋的了。确保分享你打算调查的内容和你的结果。
用你的发现写一条推文,@mimoralea(我会转发),并使用列表中的特定标签来帮助感兴趣的人找到你的结果。没有正确或错误的结果;你分享你的发现并检查他人的发现。利用这个机会社交,做出贡献,让自己脱颖而出!我们正在等待你!以下是一条推文示例:“嘿,@mimoralea。我创建了一篇博客文章,列出了学习深度强化学习的资源列表。查看它吧,。#gdrl_ch01_tf01”我会确保转发并帮助他人找到你的作品。 |



老丨虎丨机滑动步走(BSW)环境与 BW 环境相同!有趣的是……我们如何知道动作效果是随机的?我们如何表示这个问题的“滑”的部分?表格表示可以帮助我们做到这一点。
冰冻湖(FL)环境 FL 是一个 4×4 的网格(它有 16 个单元格,id 为 0-15)。每个新剧集开始时,智能体都会出现在起始单元格中。到达目标单元格会得到+1 的奖励;其他任何情况都是 0。由于表面很滑,智能体的移动只有三分之一的预期效果。其他三分之二均匀地分布在正交方向上。例如,如果智能体选择向下移动,有 33.3%的概率它会向下移动,33.3%的概率它会向左移动,33.3%的概率它会向右移动。湖周围有一道栅栏,所以如果智能体试图离开网格世界,它会弹回到它试图移动的单元格。湖中有四个洞。如果智能体掉入其中一个洞,游戏就结束了。你准备好开始构建这些动态的表示了吗?我们需要一个 Python 字典来表示这里描述的 MDP。让我们开始构建 MDP。

奖励函数

滑稽的走步五环境智能体从s, H开始,H是洞,g是目标,提供+1 奖励。







老丨虎丨机滑动步环境记住,BSW 是一个单行的网格世界,因此是一个步。但这个步的特殊之处在于,智能体从中间开始,任何动作都会立即将智能体送入终端状态。因为它是一次性步,所以它是一个老丨虎丨机环境。BSW 是一个双臂老丨虎丨机,它对智能体来说可能看起来像是一个双臂伯努利老丨虎丨机。伯努利老丨虎丨机以概率p支付+1 的奖励,以概率q = 1 – p支付 0 的奖励。换句话说,奖励信号是一个伯努利分布。在 BSW 中,两个终端状态支付 0 或+1。如果你做数学计算,你会注意到选择动作 0 时获得+1 奖励的概率是 0.2,选择动作 1 时是 0.8。但你的智能体不知道这一点,我们也不会分享这个信息。我们试图问的问题是:你的智能体需要多快才能找出最佳动作?在学会最大化期望奖励的过程中,智能体会积累多少总遗憾?让我们来看看!
老丨虎丨机滑动步图
双臂伯努利赌博机环境重要的是要注意,有几种不同的方式来表示这个环境。实际上,这并不是我在代码中这样写的,因为这里有很多冗余和不必要的信息。例如,考虑两个终止状态。两个动作可以转换到同一个终止状态。但是,你知道,画出来会让图变得太复杂了。这里的重要教训是你可以自由地以自己的方式构建和表示环境;没有唯一的正确答案。当然有多个错误的方式,但也有多个正确的方式。确保要探索!是的,我提到了那里。
在这个实验中表现最好的策略是具有 1.0 初始 Q 值和 10 个初始计数的乐观方法。所有策略都表现得很不错,这些策略并没有经过高度调整,所以这只是为了乐趣,没有其他原因。前往第四章的笔记本,玩一玩,享受乐趣。


看吧,几个最直接策略在五个不同场景中具有最低的总遗憾值和最高的预期奖励。想想看!
这次只有高级策略取得了领先,总体遗憾值相当不错。现在你应该去笔记本上享受乐趣!请记住,如果你运行了额外的实验,也请与社区分享你的结果。我迫不及待地想看看你如何扩展这些实验。享受吧!
随机游走环境 MDP 但对我来说,这并不是对 RW 令人满意的解释,也许是因为我喜欢代理控制某物的想法。在没有任何可能控制的环境中进行 RL(一个用于学习最优控制的框架)研究有什么意义呢!因此,你可以将 RW 视为一个具有确定性转移函数的环境(意味着如果代理选择左转,代理就会左转,如果选择右转,就会右转——正如预期的那样)。但假设代理想要评估一个随机策略,该策略以均匀随机的方式选择动作。一半的时间选择左转,另一半时间选择右转。无论如何,概念是相同的:我们有一个五个非终端状态的游走,其中代理以均匀随机的方式左右移动。目标是估计在这些情况下代理可以获得的总折现奖励的期望值。

然而,我选择衰减 alpha 以展示收敛。我衰减 alpha 的方式有助于算法接近收敛,但由于我没有将 alpha 减少到零,它们并没有完全收敛。除此之外,这些结果应该能帮助你了解这些方法之间的差异。






拉塞尔和诺维格的 Gridworld 这个环境,我将称之为拉塞尔和诺维格的网格世界(RNG),是一个 3 x 4 的网格世界,其中智能体从左下角开始,必须到达右上角。目标南方有一个洞,类似于冰冻湖环境,起点附近有一堵墙。转移函数有 20%的噪声;也就是说,80%的动作成功,20%的动作以随机均匀的方式在正交方向上失败。奖励函数是一个-0.04 的生存惩罚,在目标上获得+1 分,在洞上获得-1 分。现在,我们正在评估一个策略。我恰好将最优策略包含在第三章的笔记本中:在那个章节我没有空间来讨论它。实际上,确保你检查了书中提供的所有笔记本!



回报是整个过程中收到的所有奖励的总和。你的代理收到奖励 R[t+1],然后 R[t+2],等等,直到它获得在终端状态 S[T] 之前最后的奖励 RT。回报是整个过程中所有奖励的总和。回报通常定义为折扣总和,而不是简单的总和。折扣总和优先考虑在过程中早期找到的奖励(当然,这取决于折扣因子。)从技术上讲,折扣总和是对回报的更一般定义,因为折扣因子为 1 时,它就是一个简单的总和!
价值函数是期望回报。期望是通过将所有可能值相加并乘以其发生的概率来计算的。将期望视为无限多个样本的平均值;回报的期望就像是从无限多个回报中进行采样并取平均值。当你从选择行动之后开始计算回报时,期望是该状态-行动对的行动价值函数,Q(s, a)。如果你忽略采取的行动并从状态 s 开始计数,那么它就变成了状态价值函数 V(s)。
蒙特卡洛目标由实际回报组成:实际上,没有其他东西。蒙特卡洛估计包括使用经验(观察)平均回报代替期望(如果你能平均无限样本)回报来调整价值函数的估计!
时序差分目标由一个估计回报组成。还记得“自举”吗?它基本上意味着使用后续状态的估计期望回报来估计当前状态的期望回报。TD 就是这样做的:从一个猜测中学习一个猜测。TD 目标是通过使用单个奖励和下一个状态的估计期望回报(使用运行价值函数估计)来形成的。
滑溜行走七环境 MDPSWS 环境与我们之前章节中学到的随机行走(RW)环境类似,但具有控制能力。记住,随机行走是一个环境,在执行左行动作时向左走的概率等于向右走的概率。而在执行右行动作时向右走的概率等于向左走的概率,因此没有控制。这个环境是有噪声的,但智能体选择的动作会影响其性能。此外,这个环境有七个非终止状态,而 RW 环境有五个。
另一件事:请注意,在这些运行中,我为所有算法设置了相同数量的剧集;它们都在 SWS 环境中运行了 3,000 个剧集。你会发现一些算法在这么多步骤中不会收敛,但这并不意味着它们根本不会收敛。此外,本章笔记本中的其他一些环境,如冰湖(Frozen Lake),在达到一定数量的步骤后终止,也就是说,你的智能体有 100 步来完成每个剧集,否则它会被赋予完成标志。这是一个我们将在后续章节中解决的问题。但请,去笔记本里玩玩!我想你会喜欢在那里玩来玩去。


滑行七环境 MDP 作为一个复习,上面是此环境的 MDP。但请记住,并且始终牢记,代理无法访问过渡概率。这个环境的动态对代理来说是未知的。此外,对代理来说,预先没有状态之间的关系。

冻结湖环境 FL 环境是一个 4×4 的网格,有 16 个单元格,状态 0-15,从左上角到底部右侧。状态 0 是初始状态分布中的唯一状态,这意味着在每次新的一集中,代理都会出现在那个起始状态。状态 5、7、11、12 和 15 是终端状态:一旦代理落在这些状态中的任何一个,集会就结束了。状态 5、7、11 和 12 是坑,状态 15 是“目标”。使“坑”和“目标”不同的因素是奖励函数。所有落在目标状态,即状态 15 的转换都提供+1 的奖励,而整个网格世界中其他所有的转换都提供 0 奖励,没有奖励。代理自然会尝试到达那个+1 的转换,这涉及到避开坑。环境的挑战在于动作具有随机效果,因此代理只有三分之一的动作是按照预期移动的。其他三分之二均匀地分布在正交方向上。如果代理试图离开网格世界,它将弹回到它试图移动的单元格。
最后,重要的是要提到,我使用的是 gamma 值为 0.99,并且当使用 OpenAI Gym 时,冰湖环境会自动用时间限制 Gym Wrapper 包装。这个“时间包装器”实例确保智能体在不超过 100 步的情况下结束回合。从技术上来说,这两个决定(gamma 和时间包装器)改变了智能体学习的最优策略和值函数,不应轻视。我建议在第七章的笔记本中尝试 FL 环境,并将 gamma 值改为不同的值(1,0.5,0),并且通过获取环境实例属性“unwrapped”,例如,“env = env.unwrapped”,来移除时间包装器。尝试理解这两者如何影响找到的策略和值函数。

正如你所见,这个环境的主要区别在于有很多更多的洞,而且显然它们位于不同的位置。状态 19、29、35、41、42、46、49、52、54 和 59 是洞;总共有 10 个洞!与原始 FL 环境相似,在 FL8×8 中,正确的策略允许代理在 100%的回合中达到终止状态。然而,在 OpenAI Gym 的实现中,学习到最优策略的代理并没有找到这些特定的策略,这是由于我们讨论过的 gamma 和时间包装器。想想看:考虑到这些环境的不确定性,一个安全的策略可能会因为时间包装器而在这个回合中获得零奖励。此外,给定一个小于一的 gamma 值,代理采取的步骤越多,奖励对回报的影响就越低。因此,安全的策略不一定是最优策略;因此,代理没有学习到它们。记住,目标不仅仅是找到一个 100%达到目标的策略,而是在 FL 中在 100 步内,在 FL8×8 中在 200 步内达到目标。代理可能需要冒险才能实现这个目标。












值-状态输出架构有没有更好的方法来表示 Q 函数?当你看到下一页的图像时,请思考一下这个问题。





















这是一个持续任务,因此没有终止状态。然而,环境在 200 步后超时,这起到了相同的作用。环境被认为是未解决的,这意味着没有目标回报。然而,-150 是一个合理的阈值。


HopperBulletEnv-v0 环境具有一个包含 15 个连续变量的向量作为无界观察空间,代表跳跃者机器人的不同关节。它具有一个介于-1 和 1 之间的三个连续变量的向量,代表大腿、小腿和脚关节的动作。请注意,单个动作是一个包含三个元素的向量。智能体的任务是使跳跃者向前移动,奖励函数强化这一点,同时也促进最小化能量成本。








在这个环境中,智能体需要在每一步选择四种可能动作中的一种。这包括 0 表示什么都不做;或 1 表示点燃左侧引擎;或 2 表示点燃主引擎;或 3 表示点燃右侧引擎。观察空间是一个包含八个元素的向量,表示坐标、角度、速度以及其腿是否接触地面。奖励函数基于与着陆板的距离和燃料消耗。解决环境的奖励阈值是 200,时间步限制是 1,000。
浙公网安备 33010602011771号