【问题标题】:What is the way to understand Proximal Policy Optimization Algorithm in RL?如何理解 RL 中的近端策略优化算法?
【发布时间】:2018-03-07 11:08:37
【问题描述】:

我知道强化学习的基础知识,但是需要了解哪些术语才能阅读arxiv PPO paper

学习和使用PPO的路线图是什么?

【问题讨论】:

  • 有时我真的不了解 SO 社区。虽然提供了一个很好的答案,但这个问题是如此广泛,但却得到了如此多的赞成票。除了这个问题更适合其他 SE 社区之外,一个如此广泛且研究如此之少的编程问题将立即被关闭,只是说......

标签: machine-learning reinforcement-learning


【解决方案1】:

PPO 是一种简单的算法,属于策略优化算法类(与 DQN 等基于值的方法相反)。如果您“了解”RL 基础知识(我的意思是,如果您至少仔细阅读了 Sutton's book 的一些第一章),那么合乎逻辑的第一步就是熟悉策略梯度算法。您可以阅读this paperSutton's book 新版的第13 章。此外,您还可以阅读 TRPO 上的this paper,这是 PPO 第一作者以前的作品(本文有许多符号错误;请注意)。希望有帮助。 --迈赫迪

【讨论】:

    【解决方案2】:

    PPO(包括 TRPO)会尝试保守地更新策略,而不会在每次策略更新之间对其性能产生负面影响。

    为此,您需要一种方法来衡量每次更新后政策发生了多少变化。这种测量是通过查看更新策略和旧策略之间的 KL 差异来完成的。

    这变成了一个受约束的优化问题,我们希望根据我的新策略和旧策略之间的 KL 散度不超过某个预定义(或自适应)阈值的约束,将策略朝着最大性能的方向改变。

    使用 TRPO,我们在更新期间计算 KL 约束并找到该问题的学习率(通过 Fisher 矩阵和共轭梯度)。这实施起来有些混乱。

    使用 PPO,我们通过将 KL 散度从约束转换为惩罚项来简化问题,类似于 L1、L2 权重惩罚(以防止权重增大值)。 PPO 进行了额外的修改,消除了计算 KL 散度的需要,通过将策略比率(更新策略与旧策略的比率)硬限制在 1.0 左右的小范围内,其中 1.0 表示新策略与旧策略相同。

    【讨论】:

      【解决方案3】:

      为了更好地理解 PPO,看看这篇论文的主要贡献是有帮助的,它们是:(1) Clipped Surrogate Objective 和 (2) the使用“随机梯度上升的多个时期来执行每个策略更新”。


      来自原PPO paper

      我们引入了 [PPO],这是一系列策略优化方法,它使用多个随机梯度上升时期来执行每个策略更新。这些方法具有 trust-region [TRPO] 方法的稳定性和可靠性,但实现起来要简单得多,只需更改几行代码即可实现普通策略梯度,适用于更普遍设置(例如,当为策略和价值函数使用联合架构时),并具有更好的整体性能。


      1.裁剪的代理目标

      Clipped Surrogate Objective 是策略梯度目标的直接替代品,旨在通过限制您在每个步骤中对策略所做的更改来提高训练稳定性。

      对于普通策略梯度(例如,REINFORCE)——您应该熟悉,或者在阅读本文之前familiarize yourself with --- 用于优化神经网络的目标如下所示:

      这是您将在 Sutton bookother resources 中看到的标准公式,其中 A-hat 可以是折扣回报(如 REINFORCE)或优势函数(如 @987654328 @) 例如。通过相对于网络参数对这种损失采取梯度上升步骤,您将激励导致更高回报的行动。

      普通策略梯度方法使用您的操作的对数概率 (log π(a | s)) 来跟踪操作的影响,但您可以想象使用另一个函数来执行此操作。在this paper 中引入的另一个此类函数使用当前策略 下的动作概率 (π(a|s)) 除以先前策略下的动作概率 (π_old(a|s))。如果你熟悉的话,这看起来有点类似于重要性抽样:

      当您的当前政策比您的政策更可能执行此操作时,此 r(θ) 将大于 1政策;当您当前的政策不太可能采取行动时,它将介于 0 和 1 之间。

      现在用这个 r(θ) 构建一个目标函数,我们可以简单地将它换成 log π(a|s) 项。这就是 TRPO 中所做的:

      但是,如果您的行动对于您当前的策略来说更有可能(比如 100 倍以上),会发生什么情况? r(θ) 往往会非常大,并导致采取较大的梯度步骤,可能会破坏您的政策。为了解决这个问题和其他问题,TRPO 添加了一些额外的花里胡哨(例如,KL Divergence 约束)来限制策略可以更改的数量并帮助保证它是单调改进的。

      与其添加所有这些额外的花里胡哨,不如将这些稳定属性构建到目标函数中呢?正如您可能猜到的,这就是 PPO 所做的。它获得了与 TRPO 相同的性能优势,并通过优化这个简单(但看起来有点滑稽)的 Clipped Surrogate 目标避免了复杂性:

      最小化中的第一项(蓝色)与我们在 TRPO 目标中看到的 (r(θ)A) 项相同。第二项(红色)是 (r(θ)) 被限制在 (1 - e, 1 + e) 之间的版本。 (在论文中,他们指出 e 的良好值约为 0.2,因此 r 可以在 ~(0.8, 1.2) 之间变化)。然后,最后,对这两个项进行最小化(绿色)。

      花点时间仔细查看方程式,确保您知道所有符号的含义,以及数学上正在发生的事情。查看代码也可能有所帮助;这是 OpenAI baselinesanyrl-py 实现中的相关部分。

      太棒了。

      接下来,让我们看看 L 剪辑功能会产生什么效果。这是论文中的图表,它绘制了优势为正和负时的剪辑目标值:

      在图表的左半部分,其中 (A > 0),这是行动对结果产生积极影响的估计位置。在图的右半部分,(A

      注意在左半边,如果 r 值太高,它会被剪掉。如果在当前政策下采取的行动比在旧政策下更有可能发生,就会发生这种情况。当这种情况发生时,我们不想贪婪而走得太远(因为这只是我们策略的局部近似和样本,所以如果我们走得太远将不准确),因此我们将目标裁剪为防止它从成长。 (这会在反向传播中起到阻挡渐变的效果---导致渐变为0的平线)。

      在图表的右侧,动作对结果产生了估计的负面影响,我们看到剪辑在 0 附近激活,在当前策略下的动作不太可能发生。在我们刚刚迈出了一大步降低动作的可能性之后,这个剪辑区域同样会阻止我们更新太多以降低动作的可能性。

      因此,我们看到这两个裁剪区域都可以防止我们变得过于贪婪并尝试一次更新太多,从而离开该样本提供良好估计的区域。

      但是为什么我们让 r(θ) 在图的最右侧无限增长呢?这看起来很奇怪,但是在这种情况下,什么会导致 r(θ) 变得非常大? r(θ) 在这个区域的增长将是由使我们的操作很多的梯度步骤引起的更有可能,结果证明我们的政策更糟。如果是这种情况,我们希望能够撤消该梯度步骤。碰巧 L 剪辑功能允许这样做。这里的函数是负数,所以梯度会告诉我们往另一个方向走,并且按照与我们搞砸的程度成正比的程度降低动作的可能性。 (请注意,图的最左侧有一个类似的区域,该区域的动作很好,但我们不小心降低了它的可能性。)

      这些“撤销”区域解释了为什么我们必须在目标函数中包含奇怪的最小化项。它们对应于未剪辑的 r(θ)A,其值低于剪辑版本并通过最小化返回。这是因为它们是朝错误方向迈出的一步(例如,动作很好,但我们不小心降低了它的可能性)。如果我们没有在目标函数中包含最小值,这些区域将是平坦的(梯度 = 0),我们将无法修复错误。

      这是一个总结的图表:

      这就是它的要点。 Clipped Surrogate Objective 只是您可以在 vanilla 策略梯度中使用的替代品。剪裁限制了您可以在每个步骤中进行的有效更改以提高稳定性,并且最小化使我们能够纠正我们的错误,以防我们搞砸了。我没有讨论的一件事是 PPO 目标是什么意思,形成了论文中讨论的“下限”。有关更多信息,我建议this part 作者的讲座。

      2.策略更新的多个时期

      与普通策略梯度方法不同,并且由于裁剪代理目标函数,PPO 允许您在样本上运行多个梯度上升时期,而不会导致破坏性的大策略更新。这使您可以从数据中提取更多信息并减少样本效率低下的情况。

      PPO 使用 N 个并行参与者运行策略,每个参与者收集数据,然后它对这些数据的小批量进行采样,以使用 Clipped Surrogate Objective 函数训练 K 个 epoch .请参阅下面的完整算法(近似参数值为:K = 3-15, M = 64-4096, T (horizo​​n) = 128 -2048):

      parallel actor部分被A3C paper普及,已经成为一种相当标准的数据收集方式。

      较新的部分是他们能够在轨迹样本上运行 K 个梯度上升时期。正如他们在论文中所说,对数据进行多次传递的普通策略梯度优化会很好,这样您就可以从每个样本中了解更多信息。然而,这在实践中对于普通方法通常会失败,因为它们对本地样本采取了太大的步骤,这会破坏策略。另一方面,PPO 具有防止过多更新的内置机制。

      对于每次迭代,在使用 π_old 对环境进行采样后(第 3 行)以及当我们开始运行优化(第 6 行)时,我们的策略 π 将完全等于 π_old。所以一开始,我们的更新都不会被剪裁,我们保证从这些例子中学到一些东西。然而,当我们使用多个 epoch 更新 π 时,目标将开始达到裁剪限制,这些样本的梯度将变为 0,训练将逐渐停止……直到我们继续进行下一次迭代并收集新样本.

      ....

      这就是现在的全部内容。如果您有兴趣获得更好的理解,我建议您深入研究original paper,尝试自己实现它,或者深入研究baselines implementation 并使用代码。

      [编辑:2019/01/27]:为了更好地了解背景以及 PPO 与其他 RL 算法的关系,我还强烈建议查看 OpenAI 的 Spinning Up resources and implementations

      【讨论】:

      • 你写了一篇漂亮的答案。谢谢!
      • 由于裁剪样本的梯度为零,这是否意味着您实际上丢弃了所有被裁剪的样本?
      • @jaromiru 这是个好问题。我认为可以公平地说你有效地丢弃了这些样本,因为梯度会变为 0。这种看起来像是在浪费它们,但我认为这是为了确保你不会踩踏而做出的牺牲朝那个方向走得很远,并使您的政策变得更糟。换句话说:你有时会牺牲移动来确保你永远不会走错方向。
      • 而且由于您一直在比较当前政策和旧政策,因此您将赶上下一步,如果结果仍然很好,您可以继续朝那个方向前进。
      • @tryingtolearn 如果 r(θ) 非常大,并不总是意味着更新错误。这可能是一个不好的信号,因为这意味着您的参考旧策略 (π_old) 发生了很大变化。但是算法/损失函数只关心这种情况,如果(A
      【解决方案4】:

      我认为像 Cartpole-v1 这样的离散动作空间的实现比连续动作空间更容易。但是对于连续动作空间,这是我在 Pytorch 中找到的最直接的实现,因为您可以清楚地看到它们是如何得到 mustd 的,而我无法使用更知名的实现,例如 Openai Baselines 和 Spinning up 或稳定的基线。

      RL-Adventure PPO

      以上链接中的这些行:

      class ActorCritic(nn.Module):
          def __init__(self, num_inputs, num_outputs, hidden_size, std=0.0):
              super(ActorCritic, self).__init__()
      
              self.critic = nn.Sequential(
                  nn.Linear(num_inputs, hidden_size),
                  nn.ReLU(),
                  nn.Linear(hidden_size, 1)
              )
      
              self.actor = nn.Sequential(
                  nn.Linear(num_inputs, hidden_size),
                  nn.ReLU(),
                  nn.Linear(hidden_size, num_outputs),
              )
              self.log_std = nn.Parameter(torch.ones(1, num_outputs) * std)
      
              self.apply(init_weights)
      
          def forward(self, x):
              value = self.critic(x)
              mu    = self.actor(x)
              std   = self.log_std.exp().expand_as(mu)
              dist  = Normal(mu, std)
              return dist, value
      

      和剪辑:

      def ppo_update(ppo_epochs, mini_batch_size, states, actions, log_probs, returns, advantages, clip_param=0.2):
          for _ in range(ppo_epochs):
              for state, action, old_log_probs, return_, advantage in ppo_iter(mini_batch_size, states, actions, log_probs, returns, advantages):
                  dist, value = model(state)
                  entropy = dist.entropy().mean()
                  new_log_probs = dist.log_prob(action)
      
                  ratio = (new_log_probs - old_log_probs).exp()
                  surr1 = ratio * advantage
                  surr2 = torch.clamp(ratio, 1.0 - clip_param, 1.0 + clip_param) * advantage
      

      我在 Youtube 上的这个视频的 cmets 上方找到了链接:

      arxiv insights PPO

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-09-20
        • 1970-01-01
        • 2017-04-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-08-14
        • 1970-01-01
        相关资源
        最近更新 更多