在上一节中,我们主要关注环境设置:环境包括什么以及环境中的奖励如何用于指导智能体的行为。本节我们将重点介绍如何设置智能体,具体是RL工作流中的最后三个步骤。要完全涵盖这些步骤至少需要几个学期的课时,幸运的是,这不是本篇文章要实现的目标,相反,我想介绍一些高层次的主题,这些主题将使你对这些步骤有一个大致了解,这样在你深入学习其他完整的课程时会取得更好的效果。
因此,在这篇文章中,我将讨论以下两个主要问题:第一,为什么要用神经网络来表示函数,而不使用表格或传递函数? 第二,为什么要建立两个神经网络以及它们在强大的称为“执行器—评价器"的系列方法中如何互补?
让我们开始吧。
在上一节课的最后,我们介绍了策略是如何接收状态观测值并输出动作的,然后,我简要介绍了为什么表格和特别定义的函数通常不是一个很好的解决方案,这是因为当状态和动作空间变得非常大时,表格是不切实际的,而且我们很难为复杂的环境构建正确的函数结构。不过,使用神经网络可以解决这两个问题,神经网络是一组节点或人工神经元,它们以一种特定的方式连接在一起,充当通用函数近似器,这意味着,给定正确的节点和连接组合,我们就可以设置网络来模拟任何输入和输出关系,这对我们非常有利,因为我们可以使用。比如说,机器人视觉系统中的数百个像素值作为此函数的输入,输出可以是驱动机器人手臂和腿的动作器指令,尽管这个函数可能非常复杂,但我们知道有某种神经网络可以实现它,如果你不熟悉神经网络的数学原理,我强烈推荐3Blue1Brown的系列视频(【官方双语】深度学习之神经网络的结构 Part 1 ver 2.0_哔哩哔哩_bilibili),他极好地展示了网络的内部运行情况,所以我将跳过这里的大部分内容。
不过,我想强调一下,左边是输入节点,对应函数的每个输入,右边是输出节点,中间是被称为隐藏层的节点列,该网络有2个输入、2个输出和2个隐藏层,每个隐藏层有3个节点。
对于全连接网络,从每个输入节点到下一层的每个节点,然后从这些节点到下一层的节点,以此类推,直到输出节点,都有一个箭头或加权连接。节点的值等于每个输入节点乘以各自的权重因子的总和,加上一个偏差。我们可以对层中的每个节点执行此计算,并将其以紧凑矩阵形式表示为线性方程组。
现在,如果我们像这样简单地计算节点的值,然后将它们作为输入值输入到下十层来执行相同类型的线性操作,然后再输入到输出层,你可能会遇到一个问题,一维线性方程怎么可能用作通用的函数近似器呢? 具体来说,它如何表示一个非线性函数? 那是因为我漏掉了一步,这可能是人工神经网络最重要的方面之一:在计算节点的值之后,需要应用一个激活函数,以某种方式改变节点的值。
两个常用的激活函数是Sigmoid和ReLU函数,前者将节点值压缩到0到1之间,后者将所有负节点值归零。有许多不同的激活函数,但它们都有一个共同点,那就是它们都是非线性的,这对于建立一个可以拟合任何函数的网络来说至关重要。
至于为什么会这样,我非常喜欢Brendon Fortuner和Michael Neilson的解释,他们分别用ReLU和Sigmoid激活来演示了这一点,学习内容参考如下链接:bfortuner (bfortuner) / Repositories (github.com)和迈克尔·尼尔森 (michaelnielsen.org)。
好了,我们来总结一下现在的情况,我们希望找到一个函数,它可以,接收大量的观测值,并将数据转换成一组动作,从而控制一些非线性的环境,由于这个函数的结构通常太复杂,我们无法直接求解,所以我们想用一个神经网络来拟合它,该网络会随着时间的推移来学习这个函数。人们很容易认为,我们可以把任何一个网络放入其中,然后利用强化学习算法来找到权重和偏差的正确组合,然后完事大吉。不幸的是,和往常一样,情况并非如此,我们必须提前对神经网络做出一些选择,以确保它足够复杂,能够接近我们要寻找的功能,但又不至于太复杂,导致无法完成训练或训练速度过慢。例如,正如我们看到的,我们需要选择一个激活函数,隐藏层的数量,以及每一层的神经元数量,但除此之外,我们还可以控制网络的内部结构,它是应该像我所画的全连接网络,还是应该像残差神经网络那样在连接时跳过某些层? 它们是否应该通过递归神经网络对自身进行回环来建立内部记忆? 神经元组是否应该像卷积神经网络那样协同工作?等等......
我们有很多选择,但与其他控制技术一样,没有一种正确的方法,很多时候,可以从一个网络架构着手,该网络曾经解决过你试图解决的问题类型,并在此基础上进行调整。我一直在说,我们使用这些神经网络来表示智能体中的策略,但至于这究竟意味着什么,我们需要看看几个不同类别的强化学习算法的高级描述:基于策略函数、基于价值函数和基于执行器—评价器。
以下内容过于简单化,但是如果你只是想对RL的处理方法有一个基本的了解,我认为这将帮助你入门。在高层次上,我认为基于策略函数的学习算法很有意义,因为我们正试图训练一个神经网络,它会接收状态观测值并输出动作,这与控制器在控制系统中的作用密切相关,我们将这个神经网络称为执行器,因为它直接告诉智能体如何操作,这个结构看起来很简单,所以目前的问题是如何训练这种网络?
为了让大家了解这是如何做到的,让我们看看Atari的游戏Breakout。如果你不熟悉,我简要介绍下,在Breakout这个游戏中,你可以使用挡板来控制回弹球的方向以消除砖块,这款游戏只有三个动作:向左、向右或完全不动,以及一个近乎连续的状态空间,其中包括挡板的位置、球的位置和速度,以及剩余砖块的位置。在这个例子中,观测数据是游戏的屏幕截图,一次一帧,因此每个像素都有一个输入值传入我们的神经网络中。现在,设定好网络后,有很多方法进行训练,但我要重点介绍一个应用很广的方法,它有很多变体,这就是策略梯度方法。
策略梯度方法可以用于随机策略,这意味着策略将输出向左或向右的概率,而不是确定向左或还是向右。随机策略解决的是探索/利用问题,因为探索是建立在概率之上的。
在学习过程中,智能体只需要更新概率,向左移动比向右移动更好吗? 在这种状态下向左移动的概率要高一点,然后,随着时间的推移,智能体将把这些概率推向产生最多奖励的方向,那么它如何知道这些动作是好是坏呢? 设计思路是这样的:执行当前的策略,沿途收集奖励,并更新网络以增加可获得更多奖励的动作的概率,如果挡板向左移动,没击中球,导致负奖励,该怎么办? 这就需要更改神经网络,以增加下次智能体处于该状态时向右移动的概率,本质上,它会求解每个权重和偏差关于奖励的导数,然后将它们调整到正奖励增长的方向。通过这种方式,学习算法将移动网络的权重和偏差,沿着奖励斜率上升的方向,这就是为什么在名称中使用梯度这个术语。这背后的数学原理比我在本节中讲解的要复杂得多,但是我鼓励你们了解策略梯度定理,明确如何在不求导的情况下找到梯度。
策略梯度方法的一个缺点是:仅仅沿着最陡上升方向的朴素方法可能会收敛于局部极值,而不是全局极值,噪声测量的敏感性,它们也可以缓慢地收敛。例如,如果需要大量连续的动作才能获得奖励,并且产生的累积奖励在不同阶段之间具有很高的方差时,就会发生这种情况。
设想在智能体收到一个奖励之前,必须将数百或数千个连续的动作串在一起,你可以看到,使用这种极为稀疏的奖励系统来训练一个智能体非常耗时,记住这一点,在本文章的最后我们会回到这个问题。
让我们继续讨论基于价值函数的学习算法,首先,让我们看一个例子,它使用流行的网格世界作为我们的环境,在这种环境中有两个离散的状态变量:X网格位置和Y网格位置。网格世界中只有一个状态具有正的奖励,其他状态具有负的奖励,我的想法是让我们的智能体得到最多的奖励,这意味着用最少的动作得到正的奖励,智能体一次只能移动一个方块,向上、向下、向左或向右,对于我们这些强大的人类来说,很容易清楚地看到该走哪条路去获得奖励。
但是,我们必须记住,智能体对环境一无所知,它只知道它可以采取四种行动中的一种,并在采取行动后从环境中获得位置和奖励,使用基于价值函数的智能体,函数将接收状态和该状态中的一个可能动作,并输出执行该动作的值。价值是从当前状态起未来一段时间所获得的折扣奖励的总和,就像我们在本系列第一篇文章中讨论的那样。
这样,策略就是检查每个可能动作的值,并选择价值最高的动作,我们可以把这个函数看作是评价器,由于它关注的是可能采取的动作,因此评价的是智能体的选择。由于网格世界中状态和动作的数量有限,我们使用查询表来表示这个函数。这称为Q表格,其中每个状态和动作对都有一个值。
那么,智能体如何学习这些价值呢? 首先,我们可以将所有价值初始化为零,这样在智能体看来所有的动作都是一样的,探索率epsilon将发挥作用,允许智能体采取随机动作,在采取动作之后,进入一个新的状态,并从环境中收集奖励,智能体使用奖励和新信息来更新它刚刚采取的动作的价值,这里,它使用的是著名的贝尔曼方程。
贝尔曼方程允许智能体将整个问题分解成多个简单的步骤来逐步对Q表格求解,因此,智能体不会一步解析状态/动作对的真值,而是通过动态规划在每次访问时更新状态/动作对。我试着用文字来描述这个方程(如下图所示),希望它能讲得通,智能体采取动作后,它会得到奖励,价值不仅仅是某个动作的即时奖励,它是对未来的最大预期奖励,因此,状态/动作对的价值就是智能体刚刚收到的奖励,加上智能体估计未来将获得的奖励,我们用Gamma对未来的奖励进行折扣处理,就像我们说的,智能体不会太依赖于未来的奖励,这是状态/动作对(s, a)的新价值。我们将这个值与Q表格中的价值进行比较,得到误差或者智能体预测值的偏差,我们用误差乘以学习率,并将得出的增量值加到旧的估计值中。当智能体再次发现自己处于相同的状态时,它将拥有这个更新后的价值,当它选择相同的动作时,它将再次调整价值,它会一直重复这样做,直到每个状态/动作对的真值足够明确,可以利用最佳路径。
OK,让我们把这个概念推广到倒立摆。这里仍然有两种状态,角和角速度,除了现在状态是连续的,价值函数可以处理连续的状态空间,但是不使用查找表,所以我们需要神经网络。道理是一样的,我们输入状态观测值和动作,然后神经网络返回一个价值,你可以看到,这种设置对连续的动作空间并不适用,因为你怎么可能尝试无限个动作中的每一个,并找到最大值呢? 即使对于一个很大的动作空间来说,这也会使计算成本过高,不过,我有点超前了。
现在,我们假设动作空间是离散的,智能体可以从5个可能的扭矩值中选择一个,这似乎是合理的。我的想法是这样的:当你将观察到的状态和动作输入网络时,它将返回一个值,我们的策略是再次检查每个可能的动作的价值,并选择具有最大价值的动作,与网格世界一样,我们的神经网络最初会被设置为随机值,学习算法会使用贝尔曼方程的一个版本来确定新值,并相应地更新网络中的权重和偏差,—且智能体探索了足够多的状态空间,它就可以很好地拟合价值函数,并且可以在给定的任何状态下选择最优的动作,很简洁,对吧?
但我们确实遇到了一个缺点,即需要相对较小的动作空间,不过,在控制问题中,我们通常需要连续的动作空间,就像能够对倒立摆问题施加连续的扭矩范围,因此,我们会将这两种技术合并到一类称为执行器/评价器的算法中,执行器是一个网络,它试图采取它认为当前状态下最好的动作,就像我们在策略函数方法中所做的那样。而评价器是第二个网络,它试图估计状态的价值和执行器采取的行动,就像我们在基于价值的方法中所做的那样。这适用于连续的动作空间,因为评价器只需要查看一个动作,即执行器所做的动作,而不是试图通过评估所有动作来找到最佳动作。
基本上,其工作原理是这样:执行器采用与策略函数算法相同的方式选择动作,并将其应用到环境中,评价器会预估它认为的状态-动作对的值,然后使用环境反馈的奖励来确定它预测的价值有多准确,误差是指来自评价器网络的前一状态的新估计值与旧估计值之间的差异,新的估计值是基于收到的奖励和当前状态的折扣值,它可以用误差来判断情况是否比评价器预期的更好或更糟,评价器使用此误差来更新自己,就像价值函数那样,这样,下次当它处于这种状态时就会更好地预测,执行器还用评价器的响应和误差项(如有)进行自我更新,以便调整将来再次采取该动作的概率,也就是说,该策略目前会沿着评价器建议的方向进行梯度上升,而不是直接使用奖励,因此,执行器和评价器都是试图学习最佳行为的神经网络,执行器从评价器的反馈中学习正确的动作,从而了解动作的好坏,而评价器从所获得的奖励中学习价值函数,以便正确地评价执行器所采取的动作,这就是为什么你需要在智能体中建立两个神经网络,每个神经网络各自扮演着非常特殊的角色。通过执行器—评价器方法,智能体可以利用策略和价值函数算法的最佳部分,执行器—评价器能处理连续的状态和动作空间,并加快学习速度。
在下一章节中,我将展示如何使用基于执行器—评价器的算法控制双足机器人行走。在结束本篇文章之前,我想简要讨论RL工作流的最后一步:在目标硬件上部署算法。到目前为止,智能体已经通过与仿真环境的交互进行了离线学习,但是一旦策略足够优化,学习就会停止,静态策略就会被部署到目标上,就像任何已开发的控制律一样,不过,我们也有能力随策略一起部署强化学习算法,并且可以根据实际环境继续在目标上学习,这对于难以准确建模或随时间缓慢变化的环境非常重要,因此智能体需要偶尔继续学习,以便能够适应这些变化。
好了,本节内容到这里,如果不想错过本系列后续内容,欢迎订阅这个专栏。
PS:文章内容的视频链接如下: