机器学习工程师 - Udacity 强化学习 Part Seven
七、连续空间中的强化学习
1.复习强化学习
强化学习问题通常都会转化为马尔可夫决策流程,简称 MDP。一个 MDP 由一组状态 S 动作 A 概率 P 奖励 R 和折扣因子 γ 组成。P 表示不同转换和奖励的发生频率,通常建模为单个联合概率。任何时间步 t+1 的状态和奖励仅依赖于在上个时间步 t 的状态和采取的动作。特定环境的这一特性称之为马尔可夫性。
我们通常对两个量感兴趣:我们尝试估算或预测的状态值 V(S),以及在特定状态下采取的动作的值 Q(S,A)。后者可以帮助我们判断应采取什么样的动作。这两个映射或方法关系非常紧密,可以帮助我们找到问题的最优策略 π*,从而最大化接收的总奖励。
注意,因为 MDP 本质上具有概率性,因此我们无法完全确定地预测未来将获得什么样的奖励以及持续多久,因此我们通常计算的是总预期奖励。这时候就要提到折扣因子 γ 了。在计算状态和动作值时,我们用它来为未来的奖励分配更低的权重。
强化学习算法通常分为两大类别:基于模型的方法,例如策略迭代以及需要已知转换和奖励模型的值迭代。它们本质上通过动态规划并使用该模型以迭代方式计算期望的值函数和最优策略;另一方面,蒙特卡洛方法和时间差分学习等不基于模型的方法不需要明确的模型。它们通过执行探索性动作对环境抽样并使用获得的经验直接估计值函数。
2.资源
编程:OpenAI Gym
在整个课程中,我们将使用 OpenAI Gym 作为编程练习。它是一个开发和分享强化学习算法的开源库和平台。如果你之前没有使用过该平台,建议你现在就熟悉一下该平台。
阅读 OpenAI Gym 文档中的说明,以了解基本语法。
还建议你花时间查看 leaderboard,其中包含了每个任务的最佳解决方案。
请参阅此博客以详细了解如何使用 OpenAI Gym 加快强化学习 (RL) 研究。
教科书:Sutton & Barto,第二版。
建议你阅读这本经典强化学习教科书中的章节。我们在深度强化学习课程中介绍的知识可以在这本书的第 II 部分:逼近解决方案中找到。
请参阅此 GitHub 代码库 以查看教科书中的大部分图表的 Python 实现。
3.Tile Coding
这是一种较通用的离散化方案:
这里的底层状态空间是连续的二维空间。我们在该空间上方叠加多个网格或拼贴,每层都稍微不对齐。状态空间里的任何位置 S 都可以粗略地通过它所激活的拼贴识别。如果我们为每个拼贴分配一个位,则可以将新离散化状态表示为位向量:激活的位置为 1 其他位置为 0。
这种方法本身是一个非常高效的表示法,但是精彩之处在于如何使用该方案计算状态值函数。
它由该状态的位向量和每个拼贴的权重定义,而不是为每个状态 V(s) 存储单独的值。Tile Coding 算法迭代地更新这些权重。这样可以确保共享拼贴的临近位置也共享状态值的一些部分,从而有效地使所学的值函数更加平缓。
5.Adaptive Tile Coding
Tile Coding 也具有一些缺点。和简单的网格方法一样,我们需要提前手动选择拼贴大小、偏移量、拼贴数量等等。一个更加灵活的方式是 Adaptive Tile Coding。
先从非常大的拼贴开始,然后在合适时将每个拼贴一分为二。如何判断何时该拆分呢?我们可以采用试探法。也就是说,当我们意识到通过当前的表示法不再学到很多规律,即值函数不再改变时,我们就需要拆分状态空间。当我们达到拼贴数量上限或迭代上限时可以停止拆分。
为了判断应该拆分哪个拼贴,我们需要确定哪个拼贴最有可能对值函数的影响最大。为此,我们需要跟踪子拼贴以及它们的预测权重,然后我们可以选择子拼贴之间权重差别最大的拼贴,你还可以使用很多其他试探法。
Adaptive Tile Coding 的主要优势是它不需要我们手动提前指定离散化方式,最终得到一个基于空间复杂程度的状态空间的划分。
6.Coarse Coding
Coarse Coding 和 Tile Coding 很像,但是使用一组更稀疏的特征来表示状态空间。
想象一下在二维连续状态空间上放下一堆圆圈,选择任何一个状态 S 即在该空间中的位置,并标记它所属的所有圆圈构建一个位向量,这些圆圈用 1 表示,剩余圆圈用 0 表示。这就是状态的稀疏编码表示法。它也可以扩展到更高维度的空间,圆圈变成球面和超球面。
Coarse Coding 具有一些特性。圆圈越小,空间的泛化程度越低,学习算法需要持续更长时间,但是可以获得更有效的分辨率;圆圈越大,泛化程度越高,通常会形成更平缓的值函数,你可以使用更少的大圆圈来涵盖空间从而缩小表示法,但是可能会失去一定的分辨率。
我们不仅可以更改这些圆圈的大小,还可以从其他方面更改它们,例如更高或更宽,以便在一个维度里获得更高的分辨率。实际上,这种技巧可以泛化到几乎任何形状。
和在 Tile Coding 中一样,Coarse Coding 形成的状态表示法是二元向量。将每个拼贴或圆圈看做一个特征,如果是活跃状态,则用 1 表示,否则用 0 表示。一个比较自然的扩展方法是,根据到每个圆圈中心的距离衡量该特征的活跃程度:
可以使用高斯或钟形曲线使这种衡量方法或响应更加平缓。曲线在圆圈上居中,称之为径向基函数(RBF)。当然,形成的特征值不再离散化,因此又是另一个连续状态向量,但是比较酷的是可以显著降低特征数量。
7.函数逼近
到目前为止,我们已经讨论了离散化连续状态空间的各种方式,这些方式使我们能够几乎不做修改就能使用现有的强化学习算法。但是也存在一些限制。当底层空间很复杂时,所需的离散状态数量可能会很大,因此就失去了离散化的优势。此外,对于状态空间里临近的位置,它们的值应该相似或者平滑地变化,但是离散化并非始终会利用这一特性,无法跨空间地有效泛化。我们要获得的是真实的状态值函数 vπ,或动作值函数 qπ,通常在整个空间内都比较平滑连续,可以想象,除了一些非常简单的问题之外,我们的最佳希望是函数逼近。依然是逼近结果,因为我们不知道真正的底层函数是什么。
定义此类函数逼近的一般方法是引入一个表示函数形状的参数向量 W,我们的任务变成调整这个参数向量,直到找到理想的逼近结果。
注意逼近函数可以将状态映射到其值,或将状态动作对映射到相应的 q 值。另一种形式是从一个状态映射到一堆不同的 q 值,同时映射到每个动作,这对 Q 学习来说尤其实用。
先来看看第一种情况:逼近状态值函数,将状态 S 和参数 W 转换为标量值。但是如何转换呢?首先我们需要确保有一个表示状态的向量。你的状态可能已经是一个向量,这样的话你不需要进行任何处理。通常我们将定义一个转换算法,将任何给定状态 S 转换为特征向量 X(s)。这样也给我们带来了更多的灵活性,因为我们不需要对原始状态值进行运算。我们可以改为使用任何计算或推导的特征。
现在我们已经有了特征向量 X(s) 和参数向量 W,我们想要获得一个标量值。如果我们有两个向量并想要生成标量该怎么办?进行点积运算。没错,这是最简单的方式。实际上,这和计算特征的线性组合一样,将每个特征与相应的权重相乘,然后求和,称之为线性函数逼近,即我们尝试使用线性函数逼近底层值函数。
8.线性函数逼近
我们来仔细研究下线性函数逼近以及如何估算参数向量。你已经知道,线性函数是所有特征乘以相应的权重并求和的结果。假设你已经随机初始化这些权重,并计算了状态值 v^(s,w),如何调整 W 以使逼近函数越来越接近真函数?听起来像一个数值优化问题。我们使用梯度下降来找到最优参数向量。注意,因为 v^ 是一个线性函数,相对于 W 的导数就是特征向量 x(s),这也是线性函数的优势之一,以及为何如此受欢迎的原因。
我们想要缩小真值函数 vπ 和逼近值函数 v^ 之间的差异,我们写成平方差,因为我们不关心误差的符号,只想使差异降低到 0。更确切地来说,因为强化学习领域通常都是随机的,这是预期平方差。
现在我们有了要优化的目标函数,为此我们将使用梯度下降,我们来计算该函数相对于 W 的梯度或导数。使用差分链式法则,结果是 -2 乘以值差异乘以 v^ 的导数,之前我们提到它就是特征向量 (x,s)。注意,我们去掉了这里的期望运算符,侧重于单个状态 s 表示的误差梯度。我们假设状态是随机选择的,如果我们能够抽样足够多的状态,则可以非常接近预期值。我们将这个代入梯度下降更新规则的一般公式。α 是步长或学习速率参数。注意这里的 -1/2是为了消去导数中的 -2。我们将使用这个基本公式迭代地降低每个样本状态的误差,直到逼近函数和真函数几乎相等。
直观地解释下梯度下降如何优化了参数向量。在每次迭代时,朝着误差的相反方向小步地更改权重,因此这里的特征向量可以指出哪个方向不合适,这样我们就可以远离该方向。
到目前为止,我们只讨论了逼近状态值函数,为了解决不基于模型的控制问题,即在未知环境中采取动作,我们需要逼近动作值函数。为此,我们可以定义一个利用状态和动作的特征转换,然后使用状态值函数中用到的梯度下降方法。
最后,我们看看希望逼近函数同时计算所有动作值的情况。可以看做生成动作向量。为此,我们可以继续使用之前的相同特征转换,传入状态和动作。但是如何生成不同的动作值?
一种思考方式是,我们尝试找到 n 个不同的动作值函数,每个动作维度对应一个函数。但是凭直觉,我们知道这些函数有关联性,因此可以同时计算它们。为此,我们可以扩展权重向量并转换为矩阵。矩阵的每列模拟一个单独的线性函数,但是根据状态和动作计算的共同特征使这些函数相互保持关联性,如果我们的问题领域具有连续状态空间和离散动作空间。这很常见。我们可以轻松地选择值最大的动作。没有这种平行处理的话,我们需要挨个解析每个动作,然后找到最大值。如果动作空间也是连续的,那么这种形式使我们能够同时输出多个值。例如,如果我们在开车,则希望同时控制方向盘和油门。
线性函数逼近的主要限制条件是我们只能表示输入和输出之间的线性关系。对于一维输入,则是一条线;对于二维输入,变成平面,等等。如果底层的值函数是非线性形状呢?线性逼近可能会产生非常糟糕的结果,这时候就需要开始研究非线性函数了。
9.对线性函数逼近进行简单的扩展可以帮助我们捕获非线性关系。
该方法的核心是特征转换。还记得我们是如何以一般形式定义它的吗?传入状态或状态动作对并生成特征向量,该向量的每个元素都可以用单独的函数生成,可以是非线性函数。例如,假设状态 S 是一个实数,然后定义为 X1(S) = S,X2(S) = S2,X3(S) = S3 等等。这些叫做内核函数或基函数。它们将输入状态转换为不同的空间。但是注意,因为值函数依然定义为这些特征的线性组合,我们依然可以使用线性函数逼近,这使得值函数能够表示输入状态和输出状态之间的非线性关系。
径向基函数是用于此目的很常见内核。本质上,将当前状态 S 看做连续状态空间内的位置,状态空间表示为矩形平面,每个基函数都显示为一个水泡,状态越接近水泡的中心函数返回结果就越高,距离越远返回结果沿着半径逐渐减小,因此得名径向基函数。从数学上来讲,实现方式是将高斯内核与每个基函数相关联,均值表示水泡的中心,标准偏差决定了返回结果降低的平缓程度。因此,对于任何给定状态,我们可以将状态表示法简化为这些径向基函数的返回结果向量,然后我们可以使用相同的函数逼近方法。
10.非线性函数逼近
在之前的讨论中我们提到如何使用径向基函数等随机内核作为特征转换捕获输入状态和输出值之间的非线性关系,在此模型中,输出值相对于特征来说依然是线性的,如果底层的值函数相对于这些特征值的组合来说是非线性呢?为了捕获这种复杂的关系,我们将通过点积获得的线性返回结果传入某个非线性函数 f 。看起来熟悉吗?没错,它是人工神经网络的基础。此类非线性函数通常称为激活函数,大大提高了逼近器的表示能力。
我们可以使用梯度下降迭代地更新任何此类函数的参数,学习速率 α 乘以值差异乘以函数相对于权重的导数。
posted on 2019-02-27 20:59 paulonetwo 阅读(353) 评论(0) 编辑 收藏 举报