• 五十二.PPO算法原理和实战


    基于表格的方法:动态规划法、蒙特卡罗法、时序差分法等。
    基于值函数近似的方法:DQN及其改进方法。
    两类方法都基本遵循了“策略评估-策略改进”交替循环的算法框架。
    基于值函数的算法在实际应用中也存在一些不足,如算法难以高效处理连续动作空间任务和只能处理确定性策略而不能处理随机策略等。
    强化学习的最终目标是获得最优策略
    将策略本身作为迭代对象,通过迭代的方式获得一个策略序列,当策略序列收敛时,其极限就是最优策略。
    借鉴值函数近似的思路,可以将策略看成一个函数,然后用某种数学模型来近似该函数,并基于近似的策略函数设计一个目标函数,通过优化这一目标函数,实现对策略函数进行迭代优化。称这种基于策略函数近似的方法叫作策略梯度(Policy Gradient,PG)算法

    1.策略梯度算法原理

    1.1基本概念

    对策略函数进行近似的过程如下:
    设真实策略为:
    π ( a ∣ s ) \pi(a|s) π(as)
    表示在状态 s s s时执行动作 a a a的概率。
    真实策略一般是未知且难以计算的,所以用一个参数为 θ \theta θ的函数来近似它,即:
    π ( a ∣ s ) ≃ π ^ ( a ∣ s ; θ ) \pi(a|s) \simeq \hat{\pi} (a|s;\theta ) π(as)π^(as;θ)
    显然,将 π ^ ( a ∣ s ; θ ) \hat{\pi} (a|s;\theta ) π^(as;θ)建模成一种状态s时执行动作a的条件概率函数是合理的选择,即:
    π ^ ( a ∣ s ; θ ) ≜ P [ a = a t ∣ s = s t ; θ ] \hat{\pi} (a|s;\theta )\triangleq P[a=a_{t}|s=s_{t};\theta ] π^(as;θ)P[a=ats=st;θ]
    强化学习的目标是找到一个最优策略,也就是找到一个用于描述最优策略的最优参数 θ ∗ \theta^{*} θ
    为了实现这一目标,可以设计一个目标函数,其值是由策略 π ^ θ \hat{\pi}_{\theta} π^θ来确定的,记为 J ( θ ) J(θ) J(θ),称其为策略目标函数
    策略目标函数的一个重要性质是其值是和一个策略的优劣正相关的,即策略越好,策略目标函数值越大。这样就可以通过最大化策略目标函数来对策略进行优化。
    J ( θ ) J(θ) J(θ)的最大值对应的最优解就是参数 θ ∗ \theta^{*} θ,而 θ ∗ \theta^{*} θ对应的就是最佳策略。
    这样,求解最佳策略的强化学习问题就转化成了最大化策略目标函数的优化问题。可以使用梯度上升算法来最大化 J ( θ ) J(θ) J(θ)

    1.2策略函数

    按照强化学习动作空间连续性来分,强化学习可以分为离散型动作空间强化学习任务和连续型动作空间强化学习任务。
    针对离散型动作空间强化学习任务,可以定义Softmax策略函数;针对连续型动作空间强化学习任务,可以定义高斯策略函数。

    1.2.1Softmax(离散动作空间)

    在离散型动作空间强化学习任务中,动作之间不相关,如机器人选择往左或往右移动这两个动作之间是不相关的。
    将动作选择问题和状态-动作对的特征向量在一定权重下的线性加权和联系起来,即:
    ϕ ( s , a ) T θ \phi (s,a) ^{T}\theta ϕ(s,a)Tθ
    其中, θ \theta θ为权重向量, ϕ ( s , a ) \phi (s,a) ϕ(s,a)为状态-动作对的特征向量。
    假设在状态 s s s时选择动作 a a a的概率和 ϕ ( s , a ) T θ \phi (s,a) ^{T}\theta ϕ(s,a)Tθ的值正相关,由于 ϕ ( s , a ) T θ \phi (s,a) ^{T}\theta ϕ(s,a)Tθ可正可负,故使用:
    ϕ ( s , a ) T θ ∣ a ∈ A {\phi (s,a) ^{T}\theta|a\in A} ϕ(s,a)TθaA
    为基本数据集的Softmax模型来描述选择动作的概率,即:
    π ^ ( a ∣ s ; θ ) ≜ exp ⁡ ( ϕ ( s , a ) T θ ) ∑ a ∈ A exp ⁡ ( ϕ ( s , a ) T θ ) \hat{\pi} (a|s;\theta )\triangleq\frac{\exp (\phi (s,a) ^{T}\theta)}{\sum_{a\in A} \exp (\phi (s,a) ^{T}\theta)} π^(as;θ)aAexp(ϕ(s,a)Tθ)exp(ϕ(s,a)Tθ)
    离散型动作空间强化学习任务的Softmax策略函数。

    1.2.2高斯函数(连续动作空间)

    与离散型动作空间强化学习任务不同,连续型动作空间强化学习任务的动作之间是相关的
    连续型动作空间强化学习任务无法计算每个动作发生的概率,因为有无限个动作,每个动作发生的概率的理论值均为0,因此,只能获得动作空间上所有动作发生的概率分布,通过概率分布函数来表征连续型动作发生的分布情况。
    针对连续型动作空间强化学习任务,通常采用高斯分布来描述其动作发生的概率分布。
    由于均值和标准差可以描述概率密度函数的范围集中点,因此,可以通过动作空间的均值和标准差来参数化一个策略函数,即(以一维为例):
    π ^ ( a ∣ s ; θ ) = π ^ ( a ∣ s ; θ μ , θ σ ) ≜ 1 2 π σ ( s ; θ σ ) exp ⁡ [ − ( a − μ ( s ; θ μ ) ) 2 2 σ ( s ; θ σ ) 2 ] \hat{\pi} (a|s;\theta ) =\hat{\pi} (a|s;\theta_{\mu },\theta_{\sigma } ) \triangleq \frac{1}{\sqrt{2\pi }\sigma (s;\theta _{\sigma }) } \exp [ -\frac{(a-\mu (s;\theta _{\mu }))^{2}}{2\sigma (s;\theta _{\sigma })^{2}} ] π^(as;θ)=π^(as;θμ,θσ)2π σ(s;θσ)1exp[2σ(s;θσ)2(aμ(s;θμ))2]
    其中, μ ( s ; θ μ ) \mu (s;\theta_{\mu}) μ(s;θμ) σ ( s ; θ σ ) \sigma (s;\theta_{\sigma}) σ(s;θσ)分别用来近似连续动作空间的均值和标准差。
    上式并不是对动作概率的准确描述,只是近似地用高斯分布的概率密度函数来表示动作选择的概率。
    均值的具体模型可以是线性函数:
    μ ( s ; θ μ ) ≜ ϕ ( s ) T θ μ \mu (s;\theta_{\mu})\triangleq \phi(s)^{T}\theta_{\mu} μ(s;θμ)ϕ(s)Tθμ
    因为标准差必须为正,所以用指数形式的模型来近似标准差,即:
    σ ( s ; θ σ ) ≜ e ϕ ( s ) T θ σ \sigma (s;\theta_{\sigma })\triangleq e^{\phi(s)^{T}\theta _{\sigma}} σ(s;θσ)eϕ(s)Tθσ
    也可以使用神经网络来近似均值和标准差,神经网络使用一个公共特征函数网络和均值、标准差两个分支网络的结构,如下图所示:
    在这里插入图片描述
    在实际应用中为了简化计算,也可将标准差设为定值 σ σ σ。这样,高斯分布策略函数具有更简单的形式,即:
    π ^ ( a ∣ s ; θ ) ≜ 1 2 π σ exp ⁡ [ − ( a − μ ( s ; θ μ ) ) 2 2 σ 2 ] \hat{\pi} (a|s;\theta ) \triangleq \frac{1}{\sqrt{2\pi }\sigma } \exp [ -\frac{(a-\mu (s;\theta _{\mu }))^{2}}{2\sigma^{2}} ] π^(as;θ)2π σ1exp[2σ2(aμ(s;θμ))2]

    1.3策略目标函数

    策略目标函数的主要作用是用来衡量策略的优劣程度,通过回报的多少来度量,也就是说,策略目标函数体现了在某一策略下的回报情况。
    对不同的任务有3种策略目标函数可供选择:起始价值函数、平均价值函数和时间步平均奖励函数。
    针对不同的任务有3种策略目标函数可供选择:起始价值函数、平均价值函数和时间步平均奖励函数

    1.3.1起始价值函数

    对于起始状态固定为 s 0 s_{0} s0,并能够产生完整经验轨迹的环境(智能体总是能够从状态 s 0 s_{0} s0出发,到达终止状态 s T s_{T} sT),智能体在整个经验轨迹下所获得的累积折扣奖励的期望称为起始价值(Start Value),用 V ( s 0 ) V(s_{0}) V(s0)表示,而策略目标函数为起始价值的值,即:
    J s v ( θ ) ≜ V ( s 0 ) ≜ E A 0 ∼ π ^ ( ⋅ ∣ s 0 ; θ ) [ Q ( s 0 , a 0 ) ] = ∑ a ∈ A π ^ ( a 0 ∣ s 0 ; θ ) Q ( s 0 , a 0 )

    Jsv(θ)V(s0)EA0π^(|s0;θ)[Q(s0,a0)]=aAπ^(a0|s0;θ)Q(s0,a0)" role="presentation" style="position: relative;">Jsv(θ)V(s0)EA0π^(|s0;θ)[Q(s0,a0)]=aAπ^(a0|s0;θ)Q(s0,a0)
    Jsv(θ)=V(s0)EA0π^(s0;θ)[Q(s0,a0)]aAπ^(a0s0;θ)Q(s0,a0)

    1.3.2平均价值函数

    对于没有固定起始状态的强化学习任务,环境在时刻 t t t可能处于状态空间中的任何一种状态,但服从某个概率分布,设 S t S_t St服从概率分布 μ ( ⋅ ) \mu(\cdot) μ()
    设从时刻 t t t开始,智能体与环境交互直到终止状态所获得的累积折扣奖励为 V ( s t ) V(s_{t}) V(st),则平均价值函数(Average Value)可定义为 V ( s t ) V(s_{t}) V(st)在分布律 μ ( ⋅ ) \mu(\cdot) μ()下的期望,即:
    J a v g   v ( θ ) ≜ E S t ∼ μ ( ⋅ ) [ V ( S t ) ] = E S t ∼ μ ( ⋅ ) [ E A t ∼ π ^ ( ⋅ ∣ s t ; θ ) [ Q ( s t , a t ) ] = E S t ∼ μ ( ⋅ ) [ ∑ a t ∈ A π ^ ( a t ∣ s t ; θ ) Q ( s t , a t ) ]

    Javg v(θ)EStμ()[V(St)]=EStμ()[EAtπ^(|st;θ)[Q(st,at)]=EStμ()[atAπ^(at|st;θ)Q(st,at)]" role="presentation" style="position: relative;">Javg v(θ)EStμ()[V(St)]=EStμ()[EAtπ^(|st;θ)[Q(st,at)]=EStμ()[atAπ^(at|st;θ)Q(st,at)]
    Javg v(θ)==EStμ()[V(St)]EStμ()[EAtπ^(st;θ)[Q(st,at)]EStμ()[atAπ^(atst;θ)Q(st,at)]
    S t S_t St固定地取某种状态(不妨设为 s 0 s_{0} s0)时,平均价值函数便退化为起始价值函数。

    1.3.3.时间步平均奖励函数

    起始价值函数和平均价值函数都是基于价值函数定义的,这要求从初始状态或从时刻 t t t的状态出发的累积折扣奖励是可计算的,但许多强化学习任务并不能达到这个要求,例如无限时域(Infinite-Horizon)强化学习任务,也就是智能体和环境交互会一直进行下去。
    对于没有特定的开始状态和结束状态的强化学习任务,当前的即时奖励和未来的延时奖励应该被视为同等重要
    使用时间步平均奖励函数(Average Reward)作为策略目标函数。时间步平均奖励函数是指在策略 π ^ θ \hat{\pi}_{\theta} π^θ下,一个时间步的即时奖励的期望,记作 r ( π ^ θ ) r(\hat{\pi}_{\theta}) r(π^θ),即:
    J a v g   R ( θ ) ≜ r ( π ^ θ ) ≜ lim ⁡ h → ∞ 1 h ∑ t = 1 h E [ r t ∣ S 0 , A 0 : t − 1 ∼ π ^ θ ] = lim ⁡ t → ∞ E [ r t ∣ S 0 , A 0 : t − 1 ∼ π ^ θ ] = ∑ s ∈ S μ ( s ) ∑ a ∈ A π ^ ( a ∣ s ; θ ) ∑ s ′ ∈ S p ( s ′ ∣ s , a ) r ( s , a , s ′ )

    Javg R(θ)r(π^θ)limh1ht=1hE[rt|S0,A0:t1π^θ]=limtE[rt|S0,A0:t1π^θ]=sSμ(s)aAπ^(a|s;θ)sSp(s|s,a)r(s,a,s)" role="presentation" style="position: relative;">Javg R(θ)r(π^θ)limh1ht=1hE[rt|S0,A0:t1π^θ]=limtE[rt|S0,A0:t1π^θ]=sSμ(s)aAπ^(a|s;θ)sSp(s|s,a)r(s,a,s)
    Javg R(θ)==r(π^θ)hlimh1t=1hE[rtS0,A0:t1π^θ]tlimE[rtS0,A0:t1π^θ]sSμ(s)aAπ^(as;θ)sSp(ss,a)r(s,a,s)
    其中
    E [ r t ∣ S 0 , A 0 : t − 1 ∼ π ^ θ ] E[r_{t}|S_{0},A_{0:t-1}\sim \hat{\pi }_{\theta } ] E[rtS0,A0:t1π^θ]
    指从状态 s 0 s_{0} s0开始,按照策略 π ^ θ \hat{\pi}_{\theta} π^θ,选择一系列动作 A 0 , A 1 , . . . , A t − 1 A_{0},A_{1},...,A_{t-1} A0,A1,...,At1,最后得到第 t t t个时刻的即时奖励 r t r_{t} rt的期望。
    分布律 μ \mu μ是指马尔可夫链达到稳态时状态 S S S的分布,称为稳态分布,定义为
    μ ( s ) = lim ⁡ t → ∞ P ( S t = s ∣ A 0 : t − 1 ∼ π ^ θ ) \mu(s) = \lim _{t\to \infty}P(S_{t}=s|A_{0:t-1}\sim \hat{\pi } _{\theta }) μ(s)=tlimP(St=sA0:t1π^θ)
    显然,稳态分布和初始状态KaTeX parse error: Expected '}', got 'EOF' at end of input: s_{0]及早期的状态均无关系,只与策略和状态转移概率相关,它是智能体和环境在长期交互过程中最终收敛到的一个稳定状态分布。
    无限时域强化学习任务的设定下,累积奖励定义为即时奖励与时间步平均奖励的差值求和,即:
    G t = ( R t + 1 − r ( π ^ θ ) ) + ( R t + 2 − r ( π ^ θ ) ) + ( R t + 3 − r ( π ^ θ ) ) + . . . G_{t}=(R_{t+1}-r(\hat{\pi}_{\theta}))+(R_{t+2}-r(\hat{\pi}_{\theta}))+(R_{t+3}-r(\hat{\pi}_{\theta}))+... Gt=(Rt+1r(π^θ))+(Rt+2r(π^θ))+(Rt+3r(π^θ))+...
    称此 G t G_{t} Gt为差分回报(Differential Return)。

    1.4策略梯度算法框架

    将策略用策略函数近似后,优化策略就转化成对策略函数的参数进行迭代更新
    策略优化问题就转化成了一个目标函数求最大值的问题,即:
    max ⁡ θ J ( θ ) \max_{\theta } J(\theta ) θmaxJ(θ)
    这种通过最大化策略目标函数求解最优策略的方法称为策略梯度法(Policy Gradient,PG)。根据梯度上升算法,可以很容易地写出策略梯度法的算法框架:
    在这里插入图片描述

    1.5策略梯度算法的评价

    策略梯度法和值函数近似法都是基于函数近似的方法。
    动态规划法、蒙特卡罗法、时序差分法等经典强化学习方法都是基于表格的方法。
    基于函数近似和基于表格的方法的主要区别如下:
    (1)基于表格的方法一般只能处理状态、动作空间较小且离散的强化学习任务,而基于函数近似的方法则可以处理状态、动作空间离散且较大或状态、动作空间连续的强化学习任务。
    2)基于表格的强化学习算法一般可以通过迭代收敛到问题的全局最优解,但基于函数近似的强化学习算法则不具备这一优势。一般只能找到损失函数或策略目标函数的局部最优解,有时甚至连这一点也很难做到,所以基于函数近似的强化学习算法的收敛性问题一直是一个尚未解决的问题。
    (3)在每次迭代中,基于表格的强化学习算法只改变某个动作值或状态值,其他的状态或动作值不会改变。也就是说,表格中各状态或动作值是相互独立的,而基于函数近似的强化学习算法每次迭代后改变的是近似函数的参数这意味着每种状态或动作值都会发生变化,状态或动作值之间是相互关联的。这就可能造成一部分状态或动作值增加,另一部分状态或动作值减小的情况。这也正是基于函数近似的算法收敛困难的原因所在,因为最优解可能根本不存在。
    值函数近似法和策略梯度法是两种典型的基于函数近似的算法。它们的主要区别如下:
    (1)算法框架不同,值函数近似法由策略迭代算法衍生而来,使用了“策略评估-策略改进”交替进行的迭代框架,而基本的策略梯度法只迭代策略,不评估策略。应该说,这两个算法框架各有优缺点,针对不同特点的问题应该选择不同的方法。
    (2)算法支持的策略类型不同,值函数近似法一般支持的是贪婪策略,在某种状态下选择一个确定的动作,而策略梯度法支持随机策略,在某种状态下可以按照一定概率分布选择该状态下任意合法动作。从这一点来看,策略梯度法的策略搜索空间更大,可以找到不差于基于值函数近似法得到的策略。
    (3)处理问题的类型不同。值函数近似法和策略梯度法都可以处理状态空间连续的强化学习任务,但值函数近似法只能处理离散动作空间问题,而策略梯度法则可以处理连续动作空间问题。这种对动作空间的不同处理能力也源自于它们处理的策略类型不同。

    2.策略梯度定理

    策略梯度定理主要解决策略目标函数关于参数 θ \theta θ梯度求解问题。一般可以分为三类:离散情形下的策略梯度定理、连续情形下的策略梯度定理和策略梯度的近似估计。

    2.1离散型策略梯度定理

    策略梯度定理
    设目标函数为:
    J ( θ ) = E S ∼ μ ( ⋅ ) [ V π ( S ) ] J(\theta ) = E_{S\sim \mu(\cdot ) }[V_{\pi }(S)] J(θ)=ESμ()[Vπ(S)]
    其中 μ ( ⋅ ) μ(·) μ()为马尔可夫链达到稳态分布的概率密度函数,那么:
    在这里插入图片描述
    简化的策略梯度定理
    设目标函数为:
    J ( θ ) = E S ∼ μ ( ⋅ ) [ V π ( S ) ] J(\theta ) = E_{S\sim \mu(\cdot ) }[V_{\pi }(S)] J(θ)=ESμ()[Vπ(S)]
    其中 μ ( ⋅ ) μ(·) μ()为马尔可夫链达到稳态分布的概率密度函数,那么:
    在这里插入图片描述

    2.2连续型策略梯度定理

    在无限时域强化学习任务的设定下,设策略目标函数为连续型,即时间步平均奖励函数:
    J a v g   R ( θ ) ≜ ∑ s ∈ S μ ( s ) ∑ a ∈ A π ^ ( a ∣ s ; θ ) ∑ s ′ ∈ S p ( s ′ ∣ s , a ) r ( s , a , s ′ )

    Javg R(θ)sSμ(s)aAπ^(a|s;θ)sSp(s|s,a)r(s,a,s)" role="presentation" style="position: relative;">Javg R(θ)sSμ(s)aAπ^(a|s;θ)sSp(s|s,a)r(s,a,s)
    Javg R(θ)sSμ(s)aAπ^(as;θ)sSp(ss,a)r(s,a,s)
    那么策略目标函数关于参数 θ \theta θ的梯度为:
    在这里插入图片描述
    在不考虑系数的前提下,离散型策略梯度和连续型策略梯度是一样的。

    2.3近似策略梯度和评价函数

    无论是在离散情况下还是在连续情况下,策略目标函数关于参数 θ \theta θ的梯度都可以表示成:
    在这里插入图片描述

    但这个结论仍然不能完全解决策略梯度的计算问题,因为状态 S S S服从的概率分布 μ \mu μ是未知的,即使 μ \mu μ已知,也需要通过连加或定积分来计算期望,而这样做的计算代价是巨大的,也就是说,解析地计算策略梯度是不可行的。
    假设从环境观测到状态 s s s,然后根据当前策略随机抽样得到动作 a ~ π ( ⋅ ∣ s ; θ ) a~\pi(\cdot|s;\theta) aπ(sθ),定义随机梯度:
    g ^ ( s , a ; θ ) ≜ ▽ θ ln ⁡ π ^ ( a ∣ s ; θ ) Q ( s , a ) \hat{g}(s,a;\theta ) \triangleq \triangledown _{\theta }\ln \hat{\pi } (a|s;\theta )Q(s,a) g^(s,a;θ)θlnπ^(as;θ)Q(s,a)
    显然,随机梯度 g ^ ( s , a ; θ ) \hat{g}(s,a;\theta ) g^(s,a;θ)是策略梯度 ▽ θ J ( θ ) \triangledown_{\theta}J(\theta) θJ(θ)的一个无偏估计,所以可以用每次迭代中得到的随机梯度 g ^ ( s , a ; θ ) \hat{g}(s,a;\theta ) g^(s,a;θ)来近似策略梯度。
    由上式可知,随机梯度由两部分组成,评价函数 ▽ θ ln ⁡ π ^ ( a ∣ s ; θ ) \triangledown _{\theta }\ln \hat{\pi } (a|s;\theta ) θlnπ^(as;θ)和动作值 Q ( s , a ) Q(s,a) Q(s,a)

    2.3.1离散情形下的评价函数

    若策略函数为Softmax策略函数,即:
    π ^ ( a ∣ s ; θ ) ≜ exp ⁡ ( ϕ ( s , a ) T θ ) ∑ a ∈ A exp ⁡ ( ϕ ( s , a ) T θ ) \hat{\pi} (a|s;\theta )\triangleq\frac{\exp (\phi (s,a) ^{T}\theta)}{\sum_{a\in A} \exp (\phi (s,a) ^{T}\theta)} π^(as;θ)aAexp(ϕ(s,a)Tθ)exp(ϕ(s,a)Tθ)
    则:
    ln ⁡ π ^ ( a ∣ s ; θ ) = ln ⁡ exp ⁡ ( ϕ ( s , a ) T θ ) ∑ a ∈ A exp ⁡ ( ϕ ( s , a ) T θ ) = ϕ ( s , a ) T θ − ln ⁡ ∑ a ∈ A exp ⁡ ( ϕ ( s , a ) T θ )

    lnπ^(a|s;θ)=lnexp(ϕ(s,a)Tθ)aAexp(ϕ(s,a)Tθ)=ϕ(s,a)TθlnaAexp(ϕ(s,a)Tθ)" role="presentation" style="position: relative;">lnπ^(a|s;θ)=lnexp(ϕ(s,a)Tθ)aAexp(ϕ(s,a)Tθ)=ϕ(s,a)TθlnaAexp(ϕ(s,a)Tθ)
    lnπ^(as;θ)==lnaAexp(ϕ(s,a)Tθ)exp(ϕ(s,a)Tθ)ϕ(s,a)TθlnaAexp(ϕ(s,a)Tθ)
    故:
    在这里插入图片描述

    2.3.2连续情形下的评价函数

    设策略函数为高斯策略函数,即:
    π ^ ( a ∣ s ; θ ) ≜ 1 2 π σ exp ⁡ [ − ( a − ϕ ( s ; θ μ ) ) 2 2 σ 2 ] \hat{\pi} (a|s;\theta ) \triangleq \frac{1}{\sqrt{2\pi }\sigma } \exp [ -\frac{(a-\phi (s;\theta _{\mu }))^{2}}{2\sigma^{2}} ] π^(as;θ)2π σ1exp[2σ2(aϕ(s;θμ))2]
    则:
    ln ⁡ π ^ ( a ∣ s ; θ ) = ln ⁡ 1 2 π σ − 1 2 ( a − ϕ ( s ) T θ ) 2 σ 2 \ln \hat{\pi} (a|s;\theta ) = \ln \frac{1}{\sqrt{2\pi }\sigma } - \frac{\frac{1}{2} (a-\phi (s)^{T}\theta )^{2}}{\sigma^{2}} lnπ^(as;θ)=ln2π σ1σ221(aϕ(s)Tθ)2
    故:
    ▽ θ ln ⁡ π ^ ( a ∣ s ; θ ) = a − ϕ ( s ) T θ σ 2 ϕ ( s ) \triangledown _{\theta }\ln \hat{\pi} (a|s;\theta ) = \frac{a-\phi (s)^{T}\theta }{\sigma^{2}} \phi (s) θlnπ^(as;θ)=σ2aϕ(s)Tθϕ(s)

    3.PPO(近端策略优化算法)

    3.1算法原理

    近端策略优化(Proximal Policy Optimization,PPO)算法,一种基于策略梯度算法(Policy Gradient,PG)衍生出的深度强化学习算法
    一个完整的eposide序列,用 τ \tau τ来表示,而一个特定的 τ \tau τ序列发生的概率为:
    p θ ( τ ) = p ( s 1 ) p θ ( a 1 ∣ s 1 ) p ( s 2 ∣ s 1 , a 1 ) p θ ( a 2 ∣ s 2 ) p ( s 3 ∣ s 2 , a 2 ) ⋯ = p ( s 1 ) ∏ t = 1 T p θ ( a t ∣ s t ) p ( s t + 1 ∣ s t , a t )

    pθ(τ)=p(s1)pθ(a1|s1)p(s2|s1,a1)pθ(a2|s2)p(s3|s2,a2)=p(s1)t=1Tpθ(at|st)p(st+1|st,at)" role="presentation" style="position: relative;">pθ(τ)=p(s1)pθ(a1|s1)p(s2|s1,a1)pθ(a2|s2)p(s3|s2,a2)=p(s1)t=1Tpθ(at|st)p(st+1|st,at)
    pθ(τ)=p(s1)pθ(a1s1)p(s2s1,a1)pθ(a2s2)p(s3s2,a2)=p(s1)t=1Tpθ(atst)p(st+1st,at)
    对于一个完整的 τ \tau τ序列,他在整个游戏期间获得的总的奖励用 R ( τ ) R(\tau) R(τ)来表示。
    对于给定参数 θ \theta θ的策略,我们评估其应该获得的每局中的总奖励是:对每个采样 τ \tau τ得到的序列(即每一局)的加权和, 即:
    R ˉ θ = ∑ τ R ( τ ) p θ ( τ ) = E τ ∼ p θ ( τ ) [ p θ ( τ ) ] \bar{R}_{\theta}=\sum_{\tau}R(\tau)p_{\theta }(\tau )=E_{\tau\sim p_{\theta}(\tau)}[p_{\theta }(\tau )] Rˉθ=τR(τ)pθ(τ)=Eτpθ(τ)[pθ(τ)]
    对于 θ \theta θ R ˉ θ \bar{R}_{\theta} Rˉθ越大,意味着策略参数能平均获得更多奖励。可以使用梯度下降法,用期望的每局奖励 R ˉ θ \bar{R}_{\theta} Rˉθ θ \theta θ求导:
    ▽ θ R ˉ θ = ∑ τ R ( τ ) ▽ θ p θ ( τ ) = ∑ τ R ( τ ) p θ ( τ ) 1 p θ ( τ ) ▽ θ p θ ( τ ) = ∑ τ R ( τ ) p θ ( τ ) ▽ θ ln ⁡ p θ ( τ ) = E τ ∼ p τ ( θ ) [ ∑ τ R ( τ ) ▽ θ ln ⁡ p θ ( τ ) ] ≈ 1 N ∑ n = 1 N R ( τ n ) ▽ θ ln ⁡ p θ ( τ n ) ] = 1 N ∑ t = 1 T n R ( τ n ) ▽ θ ln ⁡ p θ ( a t n ∣ s t n )
    θR¯θ=τR(τ)θpθ(τ)=τR(τ)pθ(τ)1pθ(τ)θpθ(τ)=τR(τ)pθ(τ)θlnpθ(τ)=Eτpτ(θ)[τR(τ)θlnpθ(τ)]1Nn=1NR(τn)θlnpθ(τn)]=1Nt=1TnR(τn)θlnpθ(atn|stn)" role="presentation" style="position: relative;">θR¯θ=τR(τ)θpθ(τ)=τR(τ)pθ(τ)1pθ(τ)θpθ(τ)=τR(τ)pθ(τ)θlnpθ(τ)=Eτpτ(θ)[τR(τ)θlnpθ(τ)]1Nn=1NR(τn)θlnpθ(τn)]=1Nt=1TnR(τn)θlnpθ(atn|stn)
    θRˉθ=τR(τ)θpθ(τ)=τR(τ)pθ(τ)pθ(τ)1θpθ(τ)=τR(τ)pθ(τ)θlnpθ(τ)=Eτpτ(θ)[τR(τ)θlnpθ(τ)]N1n=1NR(τn)θlnpθ(τn)]=N1t=1TnR(τn)θlnpθ(atnstn)

    倒数第二步是从采集到的 N N N条数据取样。
    每一条采样到的数据序列都会希望 θ \theta θ的向着自己的方向进行更新,总体上,我们希望更加靠近奖励比较大的那条序列,因此用每条序列的奖励来加权平均他们的更新方向。比如我们假设第三条数据的奖励很大,通过上述公式更新后的策略,使得 p θ ( a t 3 ∣ s t 3 ) p_{\theta }(a_{t}^{3} |s_{t}^{3} ) pθ(at3st3)发生的概率更大。
    一个完整的PG方法就可以表示成:
    在这里插入图片描述
    优势函数:之前用的方法,对于同一个采样序列中的数据点,我们使用相同的奖励 R ( τ ) R(\tau) R(τ)。这样的做法实在有点粗糙,更细致的做法是:将这里的奖励替换成关于 ( s t , a t ) (s_{t},a_{t}) (st,at)的函数,我们把这个函数叫优势函数, 用 A θ ( s t , a t ) A^{\theta}(s_{t},a_{t}) Aθ(st,at)来表示:
    A θ ( s t , a t ) = ∑ t ′ > t γ t ′ − t r t ′ − V ϕ ( s t ) A^{\theta}(s_{t},a_{t})=\sum_{t^{'}>t}\gamma ^{t^{'}-t}r_{t^{'}}-V_{\phi }(s_{t}) Aθ(st,at)=t>tγttrtVϕ(st)
    其中, V ϕ ( s t ) V_{\phi }(s_{t}) Vϕ(st)是通过critic来计算得到的,它由一个结构与策略网络相同但参数不同的神经网络构成,主要是来拟合从 s t s_{t} st到终局的折扣奖励,表示了在 s t s_{t} st下所有动作得到的折扣奖励的期望。
    A θ A^{\theta} Aθ前半部分是实际的采样折扣奖励,后半部分是拟合的折扣奖励。
    A θ A^{\theta} Aθ表示了 s t s_{t} st下采取动作 a t a_{t} at ,实际得到的折扣奖励相对于模拟的折扣奖励下的优势,也就是说,表示了 a t a_{t} at相对于 a 1 , a 2 , a 3 , . . . , a n a_{1},a_{2},a_{3},...,a_{n} a1,a2,a3,...,an这些动作的平均优势。这个优势函数由一个critic(评价者)来给出。
    重要采样:对于一个服从概率 p p p分布的变量 x x x, 要估计 f ( x ) f(x) f(x)的期望。直接想到的是,我们采用一个服从 p p p的随机产生器,直接产生若干个变量 x x x的采样,然后计算他们的函数值 f ( x ) f(x) f(x),最后求均值就得到结果。但这里有一个问题是,对于每一个给定点 x x x,我们知道其发生的概率,但是我们并不知道 p p p的分布,也就无法构建这个随机数发生器。因此需要转换思路:从一个已知的分布q中进行采样。通过对采样点的概率进行比较,确定这个采样点的重要性。
    在这里插入图片描述
    这种采样方式的分布p和q不能差距过大,否则,会由于采样的偏离带来谬误。
    PG的缺点:参数更新慢,因为每更新一次参数都需要进行重新的采样,这其实是中on-policy的策略。也就是说,PG方法每次更新参数后,都需要重新和环境进行互动来收集数据,然后用的数据进行更新,这样,每次收集的数据使用一次就丢掉,很浪费,使得网络的更新很慢。于是我们考虑把收集到数据进行重复利用。
    假设我们收集数据时使用的策略参数是 θ ′ \theta^{'} θ,此时收集到的数据 τ \tau τ保存到记忆库中,但收集到足够的数据后,我们对参数按照前述的PG方式进行更新,更新后,策略的参数:
    θ ′ → θ \theta^{'}\to\theta θθ
    此时如果采用PG的方式,我们就应该用参数 θ \theta θ的策略重新收集数据,但是我们打算重新利用旧有的数据再更新 θ \theta θ
    注意到我们本来应该是基于 θ \theta θ的策略来收集数据,但实际上我们的数据是由 θ ′ \theta^{'} θ收集的,所以就需要引入重要性采样来修正这二者之间的偏差,这也就是前面要引入重要性采样的原因。
    在这里插入图片描述
    这种方式还是比较原始的,我们通过引入优势函数,更精细的控制更新,则更新的梯度变为:
    ▽ R ˉ = E τ ∼ p θ ′ ( τ ) [ p θ p θ ′ A ] = ∑ t = 1 T p θ ( a t ∣ s t ) p θ ′ ( a t ∣ s t ) A t ( s t , a t ) \triangledown \bar{R} =E_{\tau \sim p_{\theta ^{'}}(\tau)}[\frac{p_{\theta}}{ p_{\theta ^{'}}} A]= \sum_{t=1}^{T} \frac{p_{\theta }(a_{t}|s_{t})}{p_{\theta^{'} }(a_{t}|s_{t})} A_{t}(s_{t},a_{t}) Rˉ=Eτpθ(τ)[pθpθA]=t=1Tpθ(atst)pθ(atst)At(st,at)
    同时,根据重要性采样来说, θ \theta θ θ ′ \theta^{'} θ不能差太远了,因为差太远了会引入谬误,所以我们要用KL散度来惩罚二者之间的分布偏差:
    ▽ R ˉ = E τ ∼ p θ ′ ( τ ) [ p θ p θ ′ A ] = ∑ t = 1 T p θ ( a t ∣ s t ) p θ ′ ( a t ∣ s t ) A t ( s t , a t ) − λ K L [ θ , θ ′ ] \triangledown \bar{R} =E_{\tau \sim p_{\theta ^{'}}(\tau)}[\frac{p_{\theta}}{ p_{\theta ^{'}}} A]= \sum_{t=1}^{T} \frac{p_{\theta }(a_{t}|s_{t})}{p_{\theta^{'} }(a_{t}|s_{t})} A_{t}(s_{t},a_{t}) -\lambda KL[\theta ,\theta ^{'}] Rˉ=Eτpθ(τ)[pθpθA]=t=1Tpθ(atst)pθ(atst)At(st,at)λKL[θ,θ]
    其中:
    A θ ( s t , a t ) = ∑ t ′ > t γ t ′ − t r t ′ − V ϕ ( s t ) A^{\theta}(s_{t},a_{t})=\sum_{t^{'}>t}\gamma ^{t^{'}-t}r_{t^{'}}-V_{\phi }(s_{t}) Aθ(st,at)=t>tγttrtVϕ(st)
    其中,前半部分就是我们收集到的数据中的一个序列 τ \tau τ中的某一个动作点之后总的折扣奖励。
    后半部分是critic网络对 s t s_{t} st这个状态的评价。
    这里的 V ϕ ( s t ) V_{\phi }(s_{t}) Vϕ(st)也可以看成是对 s t s_{t} st这个状态的后续所有折扣奖励的期望,奖励的基准。

    3.2PPO流程

    PPO的更新策略有三套网络参数:
    一套策略参数 θ \theta θ,他与环境交互收集批量数据,然后批量数据关联 θ \theta θ的副本中。他每次都会被更新。
    一套策略参数的副本 θ ′ \theta^{'} θ,他是策略参数与环境互动后收集的数据的关联参数,相当于重要性采样中的 q q q分布。
    一套评价网络的参数 ϕ \phi ϕ ,他是基于收集到的数据,用监督学习的方式来更新对状态的评估。他也是每次都更新。
    在这里插入图片描述

    4.PPO案例

    pytorch+ppo2+Pendulum-v1

    import gym
    import argparse
    import torch
    import torch.nn as nn
    import torch.nn.functional as F
    import numpy as np
    import matplotlib.pyplot as plt
    
    
    class ActorNet(nn.Module):
        def __init__(self, n_states, bound):
            super(ActorNet, self).__init__()
            self.n_states = n_states
            self.bound = bound
    
            self.layer = nn.Sequential(
                nn.Linear(self.n_states, 128),
                nn.ReLU()
            )
    
            self.mu_out = nn.Linear(128, 1)
            self.sigma_out = nn.Linear(128, 1)
    
        def forward(self, x):
            x = F.relu(self.layer(x))
            mu = self.bound * torch.tanh(self.mu_out(x))
            sigma = F.softplus(self.sigma_out(x))
            return mu, sigma
    
    
    class CriticNet(nn.Module):
        def __init__(self, n_states):
            super(CriticNet, self).__init__()
            self.n_states = n_states
    
            self.layer = nn.Sequential(
                nn.Linear(self.n_states, 128),
                nn.ReLU(),
                nn.Linear(128, 1)
            )
    
        def forward(self, x):
            v = self.layer(x)
            return v
    
    
    class PPO(nn.Module):
        def __init__(self, n_states, n_actions, bound, args):
            super().__init__()
            self.n_states = n_states
            self.n_actions = n_actions
            self.bound = bound
            self.lr = args.lr
            self.gamma = args.gamma
            self.epsilon = args.epsilon
            self.a_update_steps = args.a_update_steps
            self.c_update_steps = args.c_update_steps
    
            self._build()
    
        def _build(self):
            self.actor_model = ActorNet(n_states, bound)
            self.actor_old_model = ActorNet(n_states, bound)
            self.actor_optim = torch.optim.Adam(self.actor_model.parameters(), lr=self.lr)
    
            self.critic_model = CriticNet(n_states)
            self.critic_optim = torch.optim.Adam(self.critic_model.parameters(), lr=self.lr)
    
        def choose_action(self, s):
            s = torch.FloatTensor(s)
            mu, sigma = self.actor_model(s)
            dist = torch.distributions.Normal(mu, sigma)
            action = dist.sample()
            return np.clip(action, -self.bound, self.bound)
    
        def discount_reward(self, rewards, s_):
            s_ = torch.FloatTensor(s_)
            target = self.critic_model(s_).detach()  # torch.Size([1])
            target_list = []
            for r in rewards[::-1]:
                target = r + self.gamma * target
                target_list.append(target)
            target_list.reverse()
            target_list = torch.cat(target_list)  # torch.Size([batch])
    
            return target_list
    
        def actor_learn(self, states, actions, advantage):
            states = torch.FloatTensor(states)
            actions = torch.FloatTensor(actions).reshape(-1, 1)
    
            mu, sigma = self.actor_model(states)
            pi = torch.distributions.Normal(mu, sigma)
    
            old_mu, old_sigma = self.actor_old_model(states)
            old_pi = torch.distributions.Normal(old_mu, old_sigma)
    
            ratio = torch.exp(pi.log_prob(actions) - old_pi.log_prob(actions))
            surr = ratio * advantage.reshape(-1, 1)  # torch.Size([batch, 1])
            loss = -torch.mean(
                torch.min(surr, torch.clamp(ratio, 1 - self.epsilon, 1 + self.epsilon) * advantage.reshape(-1, 1)))
    
            self.actor_optim.zero_grad()
            loss.backward()
            self.actor_optim.step()
    
        def critic_learn(self, states, targets):
            states = torch.FloatTensor(states)
            v = self.critic_model(states).reshape(1, -1).squeeze(0)
    
            loss_func = nn.MSELoss()
            loss = loss_func(v, targets)
    
            self.critic_optim.zero_grad()
            loss.backward()
            self.critic_optim.step()
    
        def cal_adv(self, states, targets):
            states = torch.FloatTensor(states)
            v = self.critic_model(states)  # torch.Size([batch, 1])
            advantage = targets - v.reshape(1, -1).squeeze(0)
            return advantage.detach()  # torch.Size([batch])
    
        def update(self, states, actions, targets):
            self.actor_old_model.load_state_dict(self.actor_model.state_dict())  # 首先更新旧模型
            advantage = self.cal_adv(states, targets)
    
            for i in range(self.a_update_steps):  # 更新多次
                self.actor_learn(states, actions, advantage)
    
            for i in range(self.c_update_steps):  # 更新多次
                self.critic_learn(states, targets)
    
    
    if __name__ == '__main__':
        parser = argparse.ArgumentParser()
        parser.add_argument('--n_episodes', type=int, default=600)
        parser.add_argument('--len_episode', type=int, default=200)
        parser.add_argument('--lr', type=float, default=0.0001)
        parser.add_argument('--batch', type=int, default=32)
        parser.add_argument('--gamma', type=float, default=0.9)
        parser.add_argument('--seed', type=int, default=10)
        parser.add_argument('--epsilon', type=float, default=0.2)
        parser.add_argument('--c_update_steps', type=int, default=10)
        parser.add_argument('--a_update_steps', type=int, default=10)
        args = parser.parse_args()
    
        env = gym.make('Pendulum-v1')
    
        n_states = env.observation_space.shape[0]
        n_actions = env.action_space.shape[0]
        bound = env.action_space.high[0]
    
        agent = PPO(n_states, n_actions, bound, args)
    
        all_ep_r = []
        for episode in range(args.n_episodes):
            ep_r = 0
            s = env.reset()[0]
            states, actions, rewards = [], [], []
            for t in range(args.len_episode):
                a = agent.choose_action(s)
                s_, r, done, _,_ = env.step(a)
                ep_r += r
                states.append(s)
                actions.append(a)
                rewards.append((r + 8) / 8)  # 参考了网上的做法
    
                s = s_
    
                if (t + 1) % args.batch == 0 or t == args.len_episode - 1:  # N步更新
    
                    targets = agent.discount_reward(rewards, s_)  # 奖励回溯
                    agent.update(states, actions, targets)  # 进行actor和critic网络的更新
                    states, actions, rewards = [], [], []
    
            print('Episode {:03d} | Reward:{:.03f}'.format(episode, ep_r))
    
            if episode == 0:
                all_ep_r.append(ep_r)
            else:
                all_ep_r.append(all_ep_r[-1] * 0.9 + ep_r * 0.1)  # 平滑
    
        plt.plot(np.arange(len(all_ep_r)), all_ep_r)
        plt.show()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185

    在这里插入图片描述

    在这里插入图片描述

  • 相关阅读:
    ChatGPT之父被OpenAI解雇
    每日练习------随机产生一个1-100之间的整数,看能几次猜中。要求:猜的次数不能超过7次,每次猜完之后都要提示“大了”或者“小了”。
    C语言数组清零----使用memset函数
    html如何携带参数自动跳转页面
    在Linux系统中创建虚拟串口
    el-form动态表单嵌套验证
    “向美好女人致敬”粉红丝带主题活动,谈水果养生之道
    一个超好看的音乐网站设计与实现(HTML+CSS)
    好玩的js特效
    外卖打印机wtn6040语音方案——让餐厅运营更高效
  • 原文地址:https://blog.csdn.net/weixin_36128607/article/details/132363222