在前面的章节里,我们已经介绍了基于策略的强化学习算法,也提到了异策略强化学习需要满足的条件:由于重要性采样的关系我们希望每次更新的时候策略分布之间差距并不是很大,这实际上是一种约束,即我们希望能每次更新的时候不大幅度地改变分布的形态,基于这种考虑openai的前辈们提出了TRPO算法,但是TRPO算法会有一些缺陷,他拿二次函数去近似约束条件,拿一次函数近似待优化的损失函数,这种近似会造成收敛上的困难,于是便有了第二次smart的改进,得到PPO系列的算法,PPO算法也是当前openai的默认算法,是策略算法的最好实现
PPO的完整代码,我已放在我的github上了,诚意之作,欢迎点星,欢迎fork,这份代码的可读性要好于openai的baseline,更适合于初学者以及想弄明白其中原理的人,不到500行代码。
回顾策略梯度的方法,根据之前的介绍我们知道在策略梯度中我们的更新满足如下关系:
θ n e w = θ o l d + α ∇ θ J \theta_{new}=\theta_{old}+\alpha\nabla_\theta J θnew=θold+α∇θJ
策略梯度的硬伤就在于更新步长 α \alpha α,当步长选的不合适的时候更新的参数会更差,因此很容易导致越学越差,最后崩溃,那什么样的步长叫做合适的步长呢,试想我们如果能找到一种步长,使他每次更新时都能保证回报函数单调递增,这样的步长就是好步长。TRPO的核心就是解决这个问题。
我们用 τ \tau τ来表示一个状态行为序列,或者说一条轨迹,那么某种策略下的期望回报可以看做是如下式子:
η ( π ~ ) = E τ ∣ π ~ [ ∑ t = 0 ∞ γ t ( r ( s t ) ) ] \eta(\tilde \pi)= E_{\tau|\tilde\pi}[\sum\limits_{t=0}^{\infty}\gamma^t(r(s_t))] η(π~)=Eτ∣π~[t=0∑∞γt(r(st))]
既然TRPO的根本目的是为了使每次更新的回报函数单调不减,那么一个很自然的想法是将新的策略对应的回报函数分解成原来策略的回报函数加一个其他项,那么只要保证新的策略的其他项是大于零的,我们就得到了一个一直提升策略的方案。
在这种思想的引导下,我们可以得到如下等式:
η ( π ~ ) = η ( π ) + E τ ∈ π ~ [ ∑ t = 0 ∞ γ t A π ( s t , a t ) ] \eta(\tilde\pi)=\eta(\pi)+E_{\tau\in{\tilde\pi}}[\sum\limits_{t=0}^{\infty}\gamma^tA_{\pi}(s_t,a_t)] η(π~)=η(π)+Eτ∈π~[t=0∑∞γtAπ(st,at)]
其中
A π ( s , a ) = Q π ( s , a ) − V π ( s ) = E s ′ ∼ P ( s ′ ∣ s , a ) [ r ( s ) + γ V π ( s ′ ) − V π ( s ) ] A_{\pi}(s,a) = Q_{\pi}(s,a) - V_{\pi}(s) = E_{s'\sim P(s'|s,a)}[r(s)+\gamma V^{\pi}(s')-V^{\pi}(s)] Aπ(s,a)=Qπ(s,a)−Vπ(s)=Es′∼P(s′∣s,a)[r(s)+γVπ(s′)−Vπ(s)]
整个公式的证明稍许复杂不做详述。
我们将公式写开可以得到:
η ( π ~ ) = η ( π ) + ∑ t = 0 ∞ ∑ s P ( s t = s ∣ π ~ ) ∑ a π ~ ( a ∣ s ) γ t A π ( s , a ) \eta(\tilde\pi)=\eta(\pi)+\sum_{t=0}^{\infty}\sum\limits_s P(s_t=s|\tilde\pi)\sum\limits_a\tilde\pi(a|s)\gamma^t A_{\pi}(s,a) η(π~)=η(π)+t=0∑∞s∑P(st=s∣π~)a∑π~(a∣s)γtAπ(s,a)
很容易进一步变形得到
η ( π ~ ) = η ( π ) + ∑ s ρ π ~ ( s ) ∑ a π ~ ( a ∣ s ) A π ( s , a ) \eta(\tilde\pi)=\eta(\pi)+\sum_s\rho_{\tilde\pi}(s)\sum\limits_a\tilde\pi(a|s)A^{\pi}(s,a) η(π~)=η(π)+s∑ρπ~(s)a∑π~(a∣s)Aπ(s,a)
其中 ρ π ( s ) = P ( s 0 = s ) + γ P ( s 1 = s ) + γ 2 P ( s 2 = s ) + . . . \rho_{\pi}(s)=P(s_0=s)+\gamma P(s_1=s)+\gamma^2P(s_2=s)+... ρπ(s)=P(s0=s)+γP(s1=s)+γ2P(s2=s)+...
注意这里s是由新分布产生的,对新分布有很强的依赖性。这个公式其实在应用中完全无法达到,因为我们是为了得到新的策略,所以这里的其他项完全无从所知,为此,TRPO采取了一些技巧来解决这个问题。
下面我们来介绍TRPO论文中的四个技巧:
对于一个初探强化学习的旁观者已经能够感受到TRPO实现上的种种近似所带来的误差,以及求解一个约束优化问题的困难,因此一种相比于TRPO理论更简洁,具体操作更简单,实验效果更优的算法应运而生,这就是PPO算法,PPO算法也是目前openai的默认算法,在迁移场景的时候,他们会默认优先使用这种算法。
我们首先将TRPO需要处理的问题列一下:
L K L P E N ( θ ) = E t [ π ~ θ ( a ∣ s n ) π θ o l d ( a ∣ s n ) A θ o l d ( s n , a ) − β K L [ π θ o l d , π θ ] ] L^{KLPEN}(\theta)={E}_t[\frac{\tilde\pi_{\theta}(a|s_n)}{\pi_{\theta_{old}}(a|s_n)}A_{\theta_{old}}(s_n,a)-\beta KL[\pi_{\theta_{old}},\pi_{\theta}]] LKLPEN(θ)=Et[πθold(a∣sn)π~θ(a∣sn)Aθold(sn,a)−βKL[πθold,πθ]]
PPO算法的思想很简单,既然TRPO认为在惩罚的时候有一个超参数 β \beta β难以确定,因而选择了限制而非惩罚,所以造成了很多羁绊,那么有没有什么好的方法能够避免超参数的选择而自适应地决定 β \beta β 呢,答案是有的,我们记
d = E t [ K L [ π θ o l d , π θ ] ] d=E_t[KL[\pi_{\theta_{old}},\pi_{\theta}]] d=Et[KL[πθold,πθ]]
d t a r g e t d_{target} dtarget为一个目标值,当我们的 d ≤ d t a r g e t / 1.5 d \le d_{target}/1.5 d≤dtarget/1.5时,用 β / 2 \beta/2 β/2更新beta,反之,用 2 β 2\beta 2β更新 β \beta β
除此之外,原论文中还提出了另一种的方法来限制每次更新的步长,我们一般称之为PPO2,论文里说PPO2的效果要比PPO1要好,所以我们平时说PPO都是指的是PPO2,PPO2的思想也很简单,思想的起点来源于对表达式的观察。我么记 r t ( θ ) = π θ ( a t ∣ s t ) π θ o l d ( a t ∣ s t ) r_t(\theta)=\frac{\pi_{\theta}(a_t|s_t)}{\pi_{\theta_{old}}(a_t|s_t)} rt(θ)=πθold(at∣st)πθ(at∣st),所以 r ( θ o l d ) = 1 r(\theta_{old})=1 r(θold)=1
我们需要考虑
L C L I P ( θ ) = E t [ min ( r t ( θ ) A t , c l i p ( r t ( θ ) , 1 − ϵ , 1 + ϵ ) A t ) ] L^{CLIP}(\theta)=E_{t}[\min(r_t(\theta)A_t,clip(r_t(\theta),1-\epsilon,1+\epsilon)A_t)] LCLIP(θ)=Et[min(rt(θ)At,clip(rt(θ),1−ϵ,1+ϵ)At)]
在这种夹的情况下可以保证两次更新之间的分布差距不大