关于AC,很多书籍和教程都说AC是DQN和PG的结合,虽然正确但从这个角度去看不便于理解,安装我们之前的思路,PG利用带权重的梯度下降方法更新策略,而获得权重的方法是蒙地卡罗计算G值。蒙地卡罗需要完成整个游戏过程,直到最终状态,才能通过回溯计算G值。这使得PG方法的效率被限制。那我们可不可以更快呢?相信大家已经想到了,那就是改为TD。

Actor-Critic

什么是AC

但改为TD还有一个问题需要解决,就是:在PG,我们需要计算G值;那么在TD中,我们应该怎样估算每一步的Q值呢?

熟悉的问题:其实这个问题我们在学DQN的时候也遇到过。 熟悉的套路:我们用上万能的神经网络解决。

也就是说,Actor-Critic,其实是用了两个网络:

两个网络有一个共同点,输入状态S: 一个输出策略,负责选择动作,我们把这个网络成为Actor; 一个负责计算每个动作的分数,我们把这个网络成为Critic。

TD-error

注意:这是AC的重点。很多同学在这里会和DQN搞乱,也就是容易产生误解的地方。在DQN预估的是Q值,在AC中的Critic,估算的是V值。你可能会说,为什么不是Q值呢?说好是给动作评价呢。我们可以直接用network估算的Q值作为更新值,但效果会不太好。原因我们可以看下图:

image-20230514105431293

假设我们用Critic网络,预估到S状态下三个动作A1,A2,A3的Q值分别为1,2,10。但在开始的时候,我们采用平均策略,于是随机到A1。于是我们用策略梯度的带权重方法更新策略,这里的权重就是Q值。于是策略会更倾向于选择A1,意味着更大概率选择A1。结果A1的概率就持续升高…这就掉进了正数陷阱。我们明明希望A3能够获得更多的机会,最后却是A1获得最多的机会。

这是为什么呢?这是因为Q值用于是一个正数,如果权重是一个正数,那么我们相当于提高对应动作的选择的概率。权重越大,我们调整的幅度将会越大。其实当我们有足够的迭代次数,这个是不用担心这个问题的。因为总会有机会抽中到权重更大的动作,因为权重比较大,抽中一次就能提高很高的概率。但在强化学习中,往往没有足够的时间让我们去和环境互动。这就会出现由于运气不好,使得一个很好的动作没有被采样到的情况发生。要解决这个问题,我们可以通过减去一个baseline,令到权重有正有负。而通常这个baseline,我们选取的是权重的平均值。减去平均值之后,值就变成有正有负了。而Q值的期望(均值)就是V。

image-20230514105718124

所以我们可以得到更新的权重$Q(s,a)-V(s)$随之而来的问题是,这就需要两个网络来估计Q和V了。但马尔科夫告诉我们,很多时候,V和Q是可以互相换算的。$Q(s,a)$用$gamma V(s’) + r $来代替,于是整理后就可以得到:$gamma V(s’) + r - V(s) $ 我们把这个差,叫做TD-error,这个和之前DQN的更新公式非常像,只不过DQN的更新用了Q,而TD-error用的是V。眼尖的同学可能已经发现,如果Critic是用来预估V值,而不是原来讨论的Q值。那么,这个TD-error是用来更新Critic的loss了!没错,Critic的任务就是让TD-error尽量小。然后TD-error给Actor做更新。

算法

  1. 定义两个network:Actor 和 Critic

  2. j进行N次更新。

    1. 从状态s开始,执行动作a,得到奖励r,进入状态s’
    2. 记录的数据。
    3. 把输入到Critic,根据公式: $TD{error} = gamma * V(s’) + r - V(s)$ 求$TD{error}$,并缩小$TD_{error}$
    4. 把输入到Actor,计算策略分布 。

image-20230514110611974

更新流程 我们可以把更新流程和PG做对比: 在PG,智能体需要从头一直跑到尾,直到最终状态才开始进行学习。 在AC,智能体采用是每步更新的方式。但要注意,我们需要先更新Critic,并计算出TD-error。再用TD-error更新Actor。

但在实做得时候,很多时候我们会把Actor和Critic公用网络前面的一些层。例如state是一张图片,我们可以先通过几层的CNN进行特征的提取,再分别输出Actor的动作概率分布和Critic的V值。

小结

  1. 为了避免正数陷阱,我们希望Actor的更新权重有正有负。因此,我们把Q值减去他们的均值V。有:$Q(s,a)-V(s)$
  2. 为了避免需要预估V值和Q值,我们希望把Q和V统一;由于$Q(s,a) = gamma V(s’) + r - V(s)$。所以我们得到TD-error公式: $TD-error = gamma V(s’) + r - V(s)$
  3. TD-error就是Actor更新策略时候,带权重更新中的权重值;
  4. 现在Critic不再需要预估Q,而是预估V。而根据马可洛夫链所学,我们知道TD-error就是Critic网络需要的loss,也就是说,Critic函数需要最小化TD-error。

PPO

on-policy与off-policy

邻近策略优化(Proximal Policy Optimization,PPO)算法的网络结构有两个。PPO算法解决的问题是 离散动作空间和连续动作空间 的强化学习问题,是 on-policy 的强化学习算法。论文原文见《Proximal Policy Optimization Algorithms

关于on-policy和off-policy的定义,网上有很多不同的讨论,比较常见的说法是看behavior policy(行为策略,即与环境进行交互的策略)和target policy(目标策略,即学习准确地评估Q值的策略)是否为同一个,如果为同一个,那么就为on-policy,反之为off-policy。我认为, 更加通俗一点的理解是,on-policy和off-policy的差异 在于 训练目标策略 所用到的数据$ (s,a,r,s′)$(有时候也表现为数据 $(s,a,r,s′,a′)$ )是不是当前目标策略(此时还没开始训练)得到的 ,如果是目标策略得到的,那么就是on-policy,如果不是,那么就是off-policy。

这样说有点难以理解,我们举个例子:

如果我们在智能体和环境进行互动时产生的数据打上一个标记。标记这是第几版本的策略产生的数据,例如 1, 2… 10.现在我们的智能体用的策略 10,需要更新到 11。如果算法只能用 10版本的产生的数据来更新,那么这个就是在线策略;如果算法允许用其他版本的数据来更新,那么就是离线策略。所以,我们需要用到重要性更新的,就可以用上之前策略版本的数据了。

我们来看看之前学习到的算法都属于哪一种?

SARSA算法

首先回顾一下SARSA的更新公式:

想一想,在SARSA选择当前状态的策略时是怎么选择的呢?没错,使用epsilon-greedy选择Q值最大的那一个。那再想一想,在更新当前行为状态的Q值时,我们需要用下一个状态的某一个Q值来更新,那选择哪一个Q值呢?使用相同策略下的动作的Q值!那么显然,在Sarsa中更新Q函数时用的A就是贪婪策略得出来的,下一回合也用的是这个A去进行step。两个A一定相同就是(同策略)on-policy。

Q_learning

Q_learning的更新公式为:

image-20230514115222702

同样的分析,在选择策略时同样是使用epsilon-greedy,但是在更新Q值的时候,这里选择的是Q值最大的那一个。这时两者可能不一样,就是(异策略)off-policy。

Proximal Policy Optimization

网络结构

PPO算法的基础框架就是上面提到的AC算法,所以理所当然的ppo的网络结构也分为两部分:Actor网络和Critic网络。

image-20230514125114964

一个actor网络,一个critic网络

actor网络的输入为状态,输出为动作概率 $\pi(a_t|s_t)$ (对于离散动作空间而言)或者动作概率分布参数(对于连续动作空间而言)

critic网络的输入为状态,输出为状态的价值。

显然,如果actor网络输出的动作越能够使优势(优势的定义等下给出)变大,那么就越好。如果critic网络输出的状态价值越准确,那么就越好。

产生连续输出

首先,我们要想办法处理连续动作的输出问题。我们先说离散动作。离散动作就像一个个的按钮,按一个按钮就能智能体就做一个动作。就像在CartPole游戏里的智能体,只有0,1两个动作分别代表向左走,向右走。那什么是连续动作呢。这就相当于这些按钮不但有开关的概念,而且还有力度大小的概念。就像我们开车,不但是前进后退转弯,并且要控制油门踩多深,刹车踩多少的,转弯时候转向转多少的问题。于是问题来了,在离散动作空间的问题中,我们最终输出的策略呈现这样的形式。

image-20230516190405663

假设动作空间有只有action1 和 action2,有40%的概率选择action1 ,60%概率选择action2。也就是说在这个状态下的策略分布: pi = [0.4, 0.6]。但连续型动作怎么表示呢,还记得之前学习DQN的时候,DQN的函数就像一张布覆盖到了Qtable上吗?其实连续型动作的理解也是一样的。我们可以理解,把连续型概率切成很多很多份。每一份的数值,就代表该动作的概率。

在连续型,我们不再用数组表示,而是用函数表示。例如,策略分布函数 : P = (action)代表在策略 下,选择某个action的概率P。但这就有个问题,用神经网络预测输出的策略是一个固定的shape,而不是连续的。那又什么办法可以表示连续型的概率呢?我们先假定策略分布函数服从一个特殊的分布,这个特殊的分布可以用一两个参数表示它的图像。正态分布就是这样一个分布,他只需要两个参数,就可以表示了。

image-20230516190617708

正态分布长得就是这个形状,中间高,两边低。他的形状由两个参数表示sigma,mu。

  • mu表示平均数,也就是整个正态分布的中轴线。mu的变化,表示整个图像向左右移动。
  • sigma表示方差,当sigma越大,图像越扁平;sigma约小,图像越突出。而最大值所在的位置,就是中轴线。

我们的神经网络可以直接输出mu和sigma,就能获得整个策略的概率密度函数了。

现在我们已经有概率密度函数,那么当我们要按概率选出一个动作时,就只需要按照整个密度函数抽样出来就可以了。

Off Policy

如上文中所说,PG,就是一个在线策略。因为PG用于产生数据的策略(行为策略),和需要更新的策略(目标策略)是一致。而DQN则是一个离线策略。我们会让智能体在环境互动一定次数,获得数据。用这些数据优化策略后,继续跑新的数据。但老版本的数据我们仍然是可以用的。也就是说,我们产生数据的策略,和要更新的目标策略不是同一个策略。所以DQN是一个离线策略。

但为什么PG和AC中的Actor更新,就不能像DQN一样,把数据存起来,更新多次呢?答案是在一定条件下,能,PPO做的工作就是这个。在了解在什么条件下可以的时候,我们需要先了解一下,为什么不能。

为了给大家直观的理解,我们来看这么一个简化的例子。

我们来看这么一个简化的例子:

image-20230516191437053

假设,我们已知在同一个环境下,有两个动作可以选择。现在两个策略,分别是P和B:

P: [0.5,0.5] B: [0.1,0.9]

现在我们按照两个策略,进行采样;也就是分别按照这两个策略,以S状态下出发,与环境进行10次互动。获得如图数据。那么,我们可以用B策略下获得的数据,更新P吗?

答案是不行,我们可以回顾一下PG算法,PG算法会按照TD-error作为权重,更新策略。权重越大,更新幅度越大;权重越小,更新幅度越小。

但大家可以从如下示意图看到,如果用行动策略B[0.1,0.9]产出的数据,对目标策略P进行更新,动作1会被更新1次,而动作2会更新9次。虽然动作A的TD-error比较大,但由于动作2更新的次数更多,最终动作2的概率会比动作1的要大。

这自然不是我们期望看到的更新结果,因为动作1的TD-error比动作2要大,我们希望选择概率动作1的能更多呀。

从这个例子,大家可以大致明白,为什么我们在更新策略的时候,不能用其他策略产生的数据了。

但为什么DQN可以多次重复使用数据呢?

我们可以从两个角度看:

  1. 更新Q值,和策略无关。 在同一个动作出发,可能会通往不同的state,但其中的概率是有环境所决定的,而不是我们的策略所决定的。所以我们产生的数据和策略并没有关系。
  2. 在DQN的更新中,我们是有”目标”的。 虽然目标比较飘忽,但每次更新,其实都是尽量向目标靠近。无论更新多少次,最终都会在目标附近徘徊。但PG算法,更新是不断远离原来的策略分布的,所以远离多少,远离的次数比例,我们都必须把握好。

Important-sampling

那么,PPO是怎样做到离线更新策略的呢?答案是Important-sampling,重要性采样技术。如果我们想用策略B抽样出来的数据,来更新策略P也不是不可以。但我们要把td-error乘以一个重要性权重(IW:importance weight)。重要性权重:$IW = P(a)/ B(a)$应用在PPO,就是目标策略出现动作a的概率 除以 行为策略出现a的概率。回到我们之前的例子,我们可以计算出,每个动作的 重要性权重,P: [0.5,0.5] B: [0.1,0.9]

我们以a1为例,计算重要性权重$IW = P / B = 0.5/0.1 = 5$我们把重要性权重乘以td-error,我们发现,a1的td-error大幅提升,而a2的td-error减少了。现在即使我们用P策略: [0.5,0.5]进行更新,a1提升的概率也会比a2的更多。PPO应用了importance sampling,使得我们用行为策略获取的数据,能够更新目标策略,把AC从在线策略,变成离线策略。

整体流程

1、将环境信息s输入到actor-new网络, 得到两个值, 一个是mu, 一个是sigma, 然后将这两个值分别当作正态分布的均值和方差构建正态分布(意义是表示action的分布),然后通过这个正态分布sample出来一个action, 再输入到环境中得到奖励r和下一步的状态s,然后存储[(s,a,r),…], 再将s输入到actor-new网络,循环步骤1, 直到存储了一定量的[(s, a, r), …], 注意这个过程中actor-new网络没有更新。

2、将1循环完最后一步得到的s输入到critic-NN网络中, 得到状态的v值, 然后计算折扣奖励:image-20230516193624201其中T为最后一个时间步。

3、将存储的所有s组合输入到critic-NN网络中, 得到所有状态的V值, 计算$At = G – V$

4、求c_loss = mean(square(At )), 然后反向传播更新critic-NN网络。

5、将存储的所有s组合输入actor-old和actor-new网络(网络结构一样), 分别得到正态分布Normal1和Normal2, 将存储的所有action组合为actions输入到正态分布Normal1和Normal2, 得到每个actions对应的prob1和prob2, 然后用prob2除以prob1得到important weight, 也就是ratio。

6、根据论文公式7计算$a_loss = mean(min((ration At, clip(ratio, 1-ξ, 1+ξ) At)))$, 然后反向传播, 更新actor-new网络。

7、循环5-6步骤, 一定步后, 循环结束, 用actor-new网络权重来更新actor-old网络(莫凡代码是在循环开始前更新的, 效果是一样的)。

8、循环1-7步骤。

image-20230516194612456

小结

  1. 我们可以用AC来解决连续型控制问题。方法是输入mu和sigma,构造一个正态分布来表示策略;
  2. PPO延展了TD(0),变成TD(N)的N步更新;
  3. AC是一个在线算法,但为了增加AC的效率,我们希望把它变成一个离线策略,这样就可以多次使用数据了。为了解决这个问题,PPO使用了重要性采样。