Task03
本次学习主要参照Datawhale开源学习及强化学习蘑菇书Easy RL
第4章 策略梯度 Policy Gradient
4.1 策略梯度算法
在强化学习中有 3 个组成部分:演员(actor)
、环境(environment)
和 奖励函数(reward function)
。
在强化学习里面,环境跟奖励函数不是你可以控制的,环境跟奖励函数是在开始学习之前,就已经事先给定的。你唯一能做的事情是调整演员里面的策略(policy),使得演员可以得到最大的奖励。演员里面会有一个策略,这个策略决定了演员的行为。给定一个外界的输入,策略会输出演员现在应该要执行的行为。
策略一般写成 π \pi π。假设你是用深度学习的技术来做强化学习的话,策略就是一个网络。网络里面就有一堆参数,我们用 θ \theta θ 来代表 π \pi π 的参数。
网络的输入就是现在机器看到的东西,如果让机器打电玩的话,机器看到的东西就是游戏的画面。机器看到什么东西,会影响你现在训练到底好不好训练。
输出的就是机器要采取什么样的行为。
上图就是具体的例子,策略就是一个网络;输入就是游戏的画面,它通常是由像素(pixels)所组成的;输出就是看看说有哪些选项是你可以去执行的,输出层就有几个神经元。假设你现在可以做的行为有 3 个,输出层就是有 3 个神经元。每个神经元对应到一个可以采取的行为。输入一个东西后,网络就会给每一个可以采取的行为一个分数。你可以把这个分数当作是概率。演员就是看这个概率的分布,根据这个概率的分布来决定它要采取的行为。比如说 70% 会向左走,20% 向右走,10% 开火等等。概率分布不同,演员采取的行为就会不一样。
接下来用一个例子来说明演员是怎么样跟环境互动的。
首先演员会看到一个游戏画面,我们用 s 1 s_1 s1 来表示游戏初始的画面。接下来演员看到这个游戏的初始画面以后,根据它内部的网络,根据它内部的策略来决定一个动作。假设它现在决定的动作是向右,它决定完动作以后,它就会得到一个奖励,代表它采取这个动作以后得到的分数。
我们把一开始的初始画面记作 s 1 s_1 s1, 把第一次执行的动作记作 a 1 a_1 a1,把第一次执行动作完以后得到的奖励记作 r 1 r_1 r1。不同的书会有不同的定义,有人会觉得说这边应该要叫做 r 2 r_2 r2,这个都可以。演员决定一个行为以后,就会看到一个新的游戏画面,这边是 s 2 s_2 s2。然后把这个 s 2 s_2 s2 输入给演员,这个演员决定要开火,然后它可能杀了一只怪,就得到五分。这个过程就反复地持续下去,直到今天走到某一个时间点执行某一个动作,得到奖励之后,这个环境决定这个游戏结束了。比如说,如果在这个游戏里面,你是控制绿色的船去杀怪,如果你被杀死的话,游戏就结束,或是你把所有的怪都清空,游戏就结束了。
一场游戏叫做一个 回合(episode)
或者 试验(trial)
。把这场游戏里面所有得到的奖励都加起来,就是 总奖励(total reward)
,我们称其为回报(return)
,用 R 来表示它。 演员要想办法去最大化它可以得到的奖励。
环境
是一个函数
,游戏的程序也可以把它看作是一个函数,虽然它不一定是神经网络,可能是基于规则的(rule-based)规则,但你可以把它看作是一个函数。这个函数一开始就先吐出一个状态,也就是游戏的画面,接下来你的演员看到这个游戏画面 s 1 s_1 s1 以后,它吐出 a 1 a_1 a1,然后环境把 a 1 a_1 a1 当作它的输入,然后它再吐出 s 2 s_2 s2,吐出新的游戏画面。演员看到新的游戏画面,再采取新的行为 a 2 a_2 a2,然后 环境再看到 a 2 a_2 a2,再吐出 s 3 s_3 s3。这个过程会一直持续下去,直到环境觉得说应该要停止为止。
在一场游戏里面,我们把环境输出的 s s s 跟演员输出的行为 a a a,把 s s s 跟 a a a 全部串起来, 叫做一个 Trajectory(轨迹)
,如下式所示。
Trajectory τ = { s 1 , a 1 , s 2 , a 2 , ⋯ , s t , a t } \text { Trajectory } \tau=\left\{s_{1}, a_{1}, s_{2}, a_{2}, \cdots, s_{t}, a_{t}\right\} Trajectory τ={
s1,a1,s2,a2,⋯,st,at}
你可以计算每一个轨迹发生的概率。假设现在演员的参数已经被给定了的话,就是 θ \theta θ。根据 θ \theta θ,你其实可以计算某一个轨迹发生的概率,你可以计算某一个回合里面发生这样子状况的概率。
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 ) \begin{aligned} p_{\theta}(\tau) &=p\left(s_{1}\right) p_{\theta}\left(a_{1} | s_{1}\right) p\left(s_{2} | s_{1}, a_{1}\right) p_{\theta}\left(a_{2} | s_{2}\right) p\left(s_{3} | s_{2}, a_{2}\right) \cdots \\ &=p\left(s_{1}\right) \prod_{t=1}^{T} p_{\theta}\left(a_{t} | s_{t}\right) p\left(s_{t+1} | s_{t}, a_{t}\right) \end{aligned} pθ(τ)=p(s1)pθ(a1∣s1)p(s2∣s1,a1)pθ(a2∣s2)p(s3∣s2,a2)⋯=p(s1)t=1∏Tpθ(at∣st)p(st+1∣st,at)
怎么算呢,如上式所示。在假设演员的参数就是 θ \theta θ 的情况下,某一个轨迹 τ \tau τ 的概率就是这样算的,你先算环境输出 s 1 s_1 s1 的概率,再计算根据 s 1 s_1 s1 执行 a 1 a_1 a1 的概率,这是由你策略里面的网络参数 θ \theta θ 所决定的, 它是一个概率,因为你的策略的网络的输出是一个分布,演员是根据这个分布去做采样,决定现在实际上要采取的动作是哪一个。接下来环境根据 a 1 a_1 a1 跟 s 1 s_1 s1 产生 s 2 s_2 s2,因为 s 2 s_2 s2 跟 s 1 s_1 s1 还是有关系的,下一个游戏画面跟前一个游戏画面通常还是有关系的,至少要是连续的, 所以给定前一个游戏画面 s 1 s_1 s1 和现在演员采取的行为 a 1 a_1 a1,就会产生 s 2 s_2 s2。
这件事情可能是概率,也可能不是概率,这个取决于环境,就是程序它内部设定是怎样。如果没有概率的话,这个游戏的每次的行为都一样,你只要找到一条路径就可以过关了 。所以游戏里面通常还是有一些概率的,你做同样的行为,给同样的前一个画面, 下次产生的画面不见得是一样的。过程就反复继续下去,你就可以计算一个轨迹 s 1 s_1 s1, a 1 a_1 a1, s 2 s_2 s2, a 2 a_2 a2 出现的概率有多大。
这个概率取决于两部分:环境的行为和 agent 的行为,
环境的行为
。环境的函数内部的参数或内部的规则长什么样子。 p ( s t + 1 ∣ s t , a t ) p(s_{t+1}|s_t,a_t) p(st+1∣st,at)这一项代表的是环境,环境这一项通常你是无法控制它的,因为那个是人家写好的,你不能控制它。agent 的行为
。你能控制的是 p θ ( a t ∣ s t ) p_\theta(a_t|s_t) pθ(at∣st)。给定一个 s t s_t st,演员要采取什么样的 a t a_t at 会取决于演员的参数 θ \theta θ, 所以这部分是演员可以自己控制的。随着演员的行为不同,每个同样的轨迹, 它就会有不同的出现的概率。
在强化学习里面,除了环境跟演员以外, 还有奖励函数(reward function)
。
奖励函数根据在某一个状态采取的某一个动作决定说现在这个行为可以得到多少的分数。 它是一个函数,给它 s 1 s_1 s1, a 1 a_1 a1,它告诉你得到 r 1 r_1 r1。给它 s 2 s_2 s2 , a 2 a_2 a2,它告诉你得到 r 2 r_2 r2。 把所有的 r r r 都加起来,我们就得到了 R ( τ ) R(\tau) R(τ) ,代表某一个轨迹 τ \tau τ 的奖励。
在某一场游戏里面, 某一个回合里面,我们会得到 R。我们要做的事情就是调整演员内部的参数 θ \theta θ, 使得 R 的值越大越好。 但实际上奖励并不只是一个标量,奖励其实是一个随机变量。R 其实是一个随机变量,因为演员在给定同样的状态会做什么样的行为,这件事情是有随机性的。环境在给定同样的观测要采取什么样的动作,要产生什么样的观测,本身也是有随机性的,所以 R 是一个随机变量。你能够计算的是 R 的期望值。你能够计算的是说,在给定某一组参数 θ \theta θ 的情况下,我们会得到的 R θ R_{\theta} Rθ 的期望值是多少。
R ˉ θ = ∑ τ R ( τ ) p θ ( τ ) \bar{R}_{\theta}=\sum_{\tau} R(\tau) p_{\theta}(\tau) Rˉθ=τ∑R(τ)pθ(τ)
这个期望值的算法如上式所示。我们要穷举所有可能的轨迹 τ \tau τ, 每一个轨迹 τ \tau τ 都有一个概率。
比如 θ \theta θ 是一个很强的模型,它都不会死。因为 θ \theta θ 很强,所以:
- 如果有一个回合 θ \theta θ 很快就死掉了,因为这种情况很少会发生,所以该回合对应的轨迹 τ \tau τ 的概率就很小;
- 如果有一个回合 θ \theta θ 都一直没有死,因为这种情况很可能发生,所以该回合对应的轨迹 τ \tau τ 的概率就很大。
你可以根据 θ \theta θ 算出某一个轨迹 τ \tau τ 出现的概率,接下来计算这个 τ \tau τ 的总奖励是多少。总奖励使用这个 τ \tau τ 出现的概率进行加权,对所有的 τ \tau τ 进行求和,就是期望值。给定一个参数,你会得到的期望值。
R ˉ θ = ∑ τ R ( τ ) p θ ( τ ) = E τ ∼ p θ ( τ ) [ R ( τ ) ] \bar{R}_{\theta}=\sum_{\tau} R(\tau) p_{\theta}(\tau)=E_{\tau \sim p_{\theta}(\tau)}[R(\tau)] Rˉθ=τ∑R(τ)pθ(τ)=Eτ∼pθ(τ)[R(τ)]
我们还可以写成上式那样,从 p θ ( τ ) p_{\theta}(\tau) pθ(τ) 这个分布采样一个轨迹 τ \tau τ,然后计算 R ( τ ) R(\tau) R(τ) 的期望值,就是你的期望的奖励。 我们要做的事情就是最大化期望奖励。
怎么最大化期望奖励呢?我们用的是 梯度上升(gradient ascent)
,因为要让它越大越好,所以是梯度上升。梯度上升在更新参数的时候要加。要进行梯度上升,我们先要计算期望的奖励(expected reward) R ˉ \bar{R} Rˉ 的梯度。我们对 R ˉ \bar{R} Rˉ 取一个梯度,这里面只有 p θ ( τ ) p_{\theta}(\tau) pθ(τ) 是跟 θ \theta θ 有关,所以梯度就放在 p θ ( τ ) p_{\theta}(\tau) pθ(τ) 这个地方。 R ( τ ) R(\tau) R(τ) 这个奖励函数不需要是可微分的(differentiable),这个不影响我们解接下来的问题。举例来说,如果是在 GAN 里面, R ( τ ) R(\tau) R(τ) 其实是一个 discriminator,它就算是没有办法微分,也无所谓,你还是可以做接下来的运算。
取梯度之后,我们背一个公式:
∇ f ( x ) = f ( x ) ∇ log f ( x ) \nabla f(x)=f(x)\nabla \log f(x) ∇f(x)=f(x)∇logf(x)
我们可以对 ∇ p θ ( τ ) \nabla p_{\theta}(\tau) ∇pθ(τ) 使用这个公式,然后会得到 ∇ p θ ( τ ) = p θ ( τ ) ∇ log p θ ( τ ) \nabla p_{\theta}(\tau)=p_{\theta}(\tau) \nabla \log p_{\theta}(\tau) ∇pθ(τ)=pθ(τ)∇logpθ(τ),进一步地,我们可以得到下式:
∇ p θ ( τ ) p θ ( τ ) = ∇ log p θ ( τ ) \frac{\nabla p_{\theta}(\tau)}{p_{\theta}(\tau)}=\nabla \log p_{\theta}(\tau) pθ(τ)∇pθ(τ)=∇logpθ(τ)
如下式所示,对 τ \tau τ 进行求和,把 R ( τ ) R(\tau) R(τ) 和 log p θ ( τ ) \log p_{\theta}(\tau) logpθ(τ) 这两项使用 p θ ( τ ) p_{\theta}(\tau) pθ(τ) 进行加权, 既然使用 p θ ( τ ) p_{\theta}(\tau) pθ(τ) 进行加权 ,它们就可以被写成期望的形式。也就是你从 p θ ( τ ) p_{\theta}(\tau) pθ(τ) 这个分布里面采样 τ \tau τ 出来, 去计算 R ( τ ) R(\tau) R(τ) 乘上 ∇ log p θ ( τ ) \nabla\log p_{\theta}(\tau) ∇logpθ(τ),然后把它对所有可能的 τ \tau τ 进行求和,就是这个期望的值(expected value)。
∇ R ˉ θ = ∑ τ R ( τ ) ∇ p θ ( τ ) = ∑ τ R ( τ ) p θ ( τ ) ∇ p θ ( τ ) p θ ( τ ) = ∑ τ R ( τ ) p θ ( τ ) ∇ log p θ ( τ ) = E τ ∼ p θ ( τ ) [ R ( τ ) ∇ log p θ ( τ ) ] \begin{aligned} \nabla \bar{R}_{\theta}&=\sum_{\tau} R(\tau) \nabla p_{\theta}(\tau)\\&=\sum_{\tau} R(\tau) p_{\theta}(\tau) \frac{\nabla p_{\theta}(\tau)}{p_{\theta}(\tau)} \\&= \sum_{\tau} R(\tau) p_{\theta}(\tau) \nabla \log p_{\theta}(\tau) \\ &=E_{\tau \sim p_{\theta}(\tau)}\left[R(\tau) \nabla \log p_{\theta}(\tau)\right] \end{aligned} ∇Rˉθ=τ∑R(τ)∇pθ(τ)=τ∑R(τ)pθ(τ)pθ(τ)∇pθ(τ)=τ∑R(τ)pθ(τ)∇logpθ(τ)=Eτ∼pθ(τ)[R(τ)∇logpθ(τ)]
实际上这个期望值没有办法算,所以你是用采样的方式来采样一大堆的 τ \tau τ。你采样 N N N 笔 τ \tau τ, 然后你去计算每一笔的这些值,然后把它全部加起来,就可以得到梯度。你就可以去更新参数,你就可以去更新你的 agent,如下式所示:
E τ ∼ p θ ( τ ) [ R ( τ ) ∇ log p θ ( τ ) ] ≈ 1 N ∑ n = 1 N R ( τ n ) ∇ log p θ ( τ n ) = 1 N ∑ n = 1 N ∑ t = 1 T n R ( τ n ) ∇ log p θ ( a t n ∣ s t n ) \begin{aligned} E_{\tau \sim p_{\theta}(\tau)}\left[R(\tau) \nabla \log p_{\theta}(\tau)\right] &\approx \frac{1}{N} \sum_{n=1}^{N} R\left(\tau^{n}\right) \nabla \log p_{\theta}\left(\tau^{n}\right) \\ &=\frac{1}{N} \sum_{n=1}^{N} \sum_{t=1}^{T_{n}} R\left(\tau^{n}\right) \nabla \log p_{\theta}\left(a_{t}^{n} \mid s_{t}^{n}\right) \end{aligned} Eτ∼pθ(τ)[R(τ)∇logpθ(τ)]≈N1n=1∑NR(τn)∇logpθ(τn)=N1n=1∑Nt=1∑TnR(τn)∇logpθ(atn∣stn)
下面给出 ∇ log p θ ( τ ) \nabla \log p_{\theta}(\tau) ∇logpθ(τ) 的具体计算过程,如下式所示。
∇ log p θ ( τ ) = ∇ ( log p ( s 1 ) + ∑ t = 1 T log p θ ( a t ∣ s t ) + ∑ t = 1 T log p ( s t + 1 ∣ s t , a t ) ) = ∇ log p ( s 1 ) + ∇ ∑ t = 1 T log p θ ( a t ∣ s t ) + ∇ ∑ t = 1 T log p ( s t + 1 ∣ s t , a t ) = ∇ ∑ t = 1 T log p θ ( a t ∣ s t ) = ∑ t = 1 T ∇ log p θ ( a t ∣ s t ) \begin{aligned} \nabla \log p_{\theta}(\tau) &= \nabla \left(\log p(s_1)+\sum_{t=1}^{T}\log p_{\theta}(a_t|s_t)+ \sum_{t=1}^{T}\log p(s_{t+1}|s_t,a_t) \right) \\ &= \nabla \log p(s_1)+ \nabla \sum_{t=1}^{T}\log p_{\theta}(a_t|s_t)+ \nabla \sum_{t=1}^{T}\log p(s_{t+1}|s_t,a_t) \\ &=\nabla \sum_{t=1}^{T}\log p_{\theta}(a_t|s_t)\\ &=\sum_{t=1}^{T} \nabla\log p_{\theta}(a_t|s_t) \end{aligned} ∇logpθ(τ)=∇(logp(s1)+t=1∑Tlogpθ(at∣st)+t=1∑Tlogp(st+1∣st,at))=∇logp(s1)+∇t=1∑Tlogpθ(at∣st)+∇t=1∑Tlogp(st+1∣st,at)=∇t=1∑Tlogpθ(at∣st)=t=1∑T∇logpθ(at∣st)
注意, p ( s 1 ) p(s_1) p(s1) 和 p ( s t + 1 ∣ s t , a t ) p(s_{t+1}|s_t,a_t) p(st+1∣st,at) 来自于环境, p θ ( a t ∣ s t ) p_\theta(a_t|s_t) pθ(at∣st) 是来自于 agent。 p ( s 1 ) p(s_1) p(s1) 和 p ( s t + 1 ∣ s t , a t ) p(s_{t+1}|s_t,a_t) p(st+1∣st,at) 由环境决定,所以与 θ \theta θ 无关,因此 ∇ log p ( s 1 ) = 0 \nabla \log p(s_1)=0 ∇logp(s1)=0 , ∇ ∑ t = 1 T log p ( s t + 1 ∣ s t , a t ) = 0 \nabla \sum_{t=1}^{T}\log p(s_{t+1}|s_t,a_t)=0 ∇∑t=1Tlogp(st+1∣st,at)=0。
∇ R ˉ θ = ∑ τ R ( τ ) ∇ p θ ( τ ) = ∑ τ R ( τ ) p θ ( τ ) ∇ p θ ( τ ) p θ ( τ ) = ∑ τ R ( τ ) p θ ( τ ) ∇ log p θ ( τ ) = E τ ∼ p θ ( τ ) [ R ( τ ) ∇ log p θ ( τ ) ] ≈ 1 N ∑ n = 1 N R ( τ n ) ∇ log p θ ( τ n ) = 1 N ∑ n = 1 N ∑ t = 1 T n R ( τ n ) ∇ log p θ ( a t n ∣ s t n ) \begin{aligned} \nabla \bar{R}_{\theta}&=\sum_{\tau} R(\tau) \nabla p_{\theta}(\tau)\\&=\sum_{\tau} R(\tau) p_{\theta}(\tau) \frac{\nabla p_{\theta}(\tau)}{p_{\theta}(\tau)} \\&= \sum_{\tau} R(\tau) p_{\theta}(\tau) \nabla \log p_{\theta}(\tau) \\ &=E_{\tau \sim p_{\theta}(\tau)}\left[R(\tau) \nabla \log p_{\theta}(\tau)\right]\\ &\approx \frac{1}{N} \sum_{n=1}^{N} R\left(\tau^{n}\right) \nabla \log p_{\theta}\left(\tau^{n}\right) \\ &=\frac{1}{N} \sum_{n=1}^{N} \sum_{t=1}^{T_{n}} R\left(\tau^{n}\right) \nabla \log p_{\theta}\left(a_{t}^{n} \mid s_{t}^{n}\right) \end{aligned} ∇Rˉθ=τ∑R(τ)∇pθ(τ)=τ∑R(τ)pθ(τ)pθ(τ)∇pθ(τ)=τ∑R(τ)pθ(τ)∇logpθ(τ)=Eτ∼pθ(τ)[R(τ)∇logpθ(τ)]≈N1n=1∑NR(τn)∇logpθ(τn)=N1n=1∑Nt=1∑TnR(τn)∇logpθ(atn∣stn)
我们可以直观地来理解上面这个式子,也就是在你采样到的数据里面, 你采样到在某一个状态 s t s_t st 要执行某一个动作 a t a_t at, 这个 s t s_t st 跟 a t a_t at 它是在整个轨迹 τ \tau τ 的里面的某一个状态和动作的对。
- 假设你在 s t s_t st 执行 a t a_t at,最后发现 τ \tau τ 的奖励是正的, 那你就要增加这一项的概率,你就要增加在 s t s_t st 执行 a t a_t at 的概率。
- 反之,在 s t s_t st 执行 a t a_t at 会导致 τ \tau τ 的奖励变成负的, 你就要减少这一项的概率。
这个怎么实现呢? 你用梯度上升来更新你的参数,你原来有一个参数 θ \theta θ ,把你的 θ \theta θ 加上你的梯度这一项,那当然前面要有个学习率,学习率也是要调整的,你可用 Adam、RMSProp 等方法对其进行调整。
我们可以套下面这个公式来把梯度计算出来:
∇ R ˉ θ = 1 N ∑ n = 1 N ∑ t = 1 T n R ( τ n ) ∇ log p θ ( a t n ∣ s t n ) \nabla \bar{R}_{\theta}=\frac{1}{N} \sum_{n=1}^{N} \sum_{t=1}^{T_{n}} R\left(\tau^{n}\right) \nabla \log p_{\theta}\left(a_{t}^{n} | s_{t}^{n}\right) ∇Rˉθ=N1n=1∑Nt=1∑TnR(τn)∇logpθ(atn∣stn)
实际上,要套上面这个公式, 首先你要先收集一大堆的 s 跟 a 的对(pair),你还要知道这些 s 跟 a 在跟环境互动的时候,你会得到多少的奖励。 这些资料怎么收集呢?你要拿你的 agent,它的参数是 θ \theta θ,去跟环境做互动, 也就是拿你已经训练好的 agent 先去跟环境玩一下,先去跟那个游戏互动一下, 互动完以后,你就会得到一大堆游戏的纪录,你会记录说,今天先玩了第一场,在第一场游戏里面,我们在状态 s 1 s_1 s1 采取动作 a 1 a_1 a1,在状态 s 2 s_2 s2 采取动作 a 2 a_2 a2 。
玩游戏的时候是有随机性的,所以 agent 本身是有随机性的,在同样状态 s 1 s_1 s1,不是每次都会采取 a 1 a_1 a1,所以你要记录下来。在状态 s 1 1 s_1^1 s11 采取 a 1 1 a_1^1 a11,在状态 s 2 1 s_2^1 s21 采取 a 2 1 a_2^1 a21。整场游戏结束以后,得到的分数是 R ( τ 1 ) R(\tau^1) R(τ1)。你会采样到另外一笔数据,也就是另外一场游戏。在另外一场游戏里面,你在状态 s 1 2 s_1^2 s12 采取 a 1 2 a_1^2 a12,在状态 s 2 2 s_2^2 s22 采取 a 2 2 a_2^2 a22,然后你采样到的就是 τ 2 \tau^2 τ2,得到的奖励是 R ( τ 2 ) R(\tau^2) R(τ2)。
你就可以把采样到的东西代到这个梯度的式子里面,把梯度算出来。也就是把这边的每一个 s 跟 a 的对拿进来,算一下它的对数概率(log probability)。你计算一下在某一个状态采取某一个动作的对数概率,然后对它取梯度,然后这个梯度前面会乘一个权重,权重就是这场游戏的奖励。 有了这些以后,你就会去更新你的模型。
更新完你的模型以后。你要重新去收集数据,再更新模型。注意,一般 policy gradient(PG)
采样的数据就只会用一次。你把这些数据采样起来,然后拿去更新参数,这些数据就丢掉了。接着再重新采样数据,才能够去更新参数,等一下我们会解决这个问题。
接下来讲一些实现细节。
我们可以把它想成一个分类的问题,在分类里面就是输入一个图像,然后输出决定说是 10 个类里面的哪一个。在做分类时,我们要收集一堆训练数据,要有输入跟输出的对。
在实现的时候,你就把状态当作是分类器的输入。 你就当在做图像分类的问题,只是现在的类不是说图像里面有什么东西,而是说看到这张图像我们要采取什么样的行为,每一个行为就是一个类。比如说第一个类叫做向左,第二个类叫做向右,第三个类叫做开火。
在做分类的问题时,要有输入和正确的输出,要有训练数据。而这些训练数据是从采样的过程来的。假设在采样的过程里面,在某一个状态,你采样到你要采取动作 a, 你就把这个动作 a 当作是你的 ground truth。你在这个状态,你采样到要向左。 本来向左这件事概率不一定是最高, 因为你是采样,它不一定概率最高。假设你采样到向左,在训练的时候,你告诉机器说,调整网络的参数, 如果看到这个状态,你就向左。在一般的分类问题里面,其实你在实现分类的时候,你的目标函数都会写成最小化交叉熵(cross entropy),其实最小化交叉熵就是最大化对数似然(log likelihood)。
做分类的时候,目标函数就是最大化或最小化的对象, 因为我们现在是最大化似然(likelihood),所以其实是最大化, 你要最大化的对象,如下式所示:
1 N ∑ n = 1 N ∑ t = 1 T n log p θ ( a t n ∣ s t n ) \frac{1}{N} \sum_{n=1}^{N} \sum_{t=1}^{T_{n}} \log p_{\theta}\left(a_{t}^{n} \mid s_{t}^{n}\right) N1n=1∑Nt=1∑Tnlogpθ(atn∣stn)
像这种损失函数,你可在 TensorFlow 里调用现成的函数,它就会自动帮你算,然后你就可以把梯度计算出来。这是一般的分类问题,RL 唯一不同的地方是 loss 前面乘上一个权重:整场游戏得到的总奖励 R,它并不是在状态 s 采取动作 a 的时候得到的奖励,如下式所示:
1 N ∑ n = 1 N ∑ t = 1 T n R ( τ n ) log p θ ( a t n ∣ s t n ) \frac{1}{N} \sum_{n=1}^{N} \sum_{t=1}^{T_{n}} R\left(\tau^{n}\right) \log p_{\theta}\left(a_{t}^{n} \mid s_{t}^{n}\right) N1n=1∑Nt=1∑TnR(τn)logpθ(atn∣stn)
你要把你的每一笔训练数据,都使用这个 R 进行加权。然后你用 TensorFlow 或 PyTorch 去帮你算梯度就结束了,跟一般分类差不多。
4.2 蒙特卡洛策略梯度 REINFORCE
MC 可以理解为算法完成一个回合之后,再拿这个回合的数据来去 learn 一下,做一次更新。因为我们已经拿到了一整个回合的数据的话,也能够拿到每一个步骤的奖励,我们可以很方便地去计算每个步骤的未来总收益,就是我们的期望,就是我们的回报 G t G_t Gt 。 G t G_t Gt 是我们的未来总收益, G t G_t Gt 代表是从这个步骤后面,我能拿到的收益之和是多少。 G 1 G_1 G1是说我从第一步开始,往后能够拿到多少的收益。 G 2 G_2 G2 是说从第二步开始,往后一共能够拿到多少的收益。
相比 MC 还是一个回合更新一次这样子的方式,TD 就是每个步骤都更新一下。每走一步,我就更新下,这样的更新频率会更高一点。它拿的是 Q-function 来去近似地表示我的未来总收益 G t G_t Gt。
我们介绍下策略梯度最简单的也是最经典的一个算法 REINFORCE
。REINFORCE 用的是回合更新的方式。它在代码上的处理上是先拿到每个步骤的奖励,然后计算每个步骤的未来总收益 G t G_t Gt 是多少,然后拿每个 G t G_t Gt 代入公式,去优化每一个动作的输出。所以编写代码时会有这样一个函数,输入每个步骤拿到的奖励,把这些奖励转成每一个步骤的未来总收益。因为未来总收益是这样计算的:
G t = ∑ k = t + 1 T γ k − t − 1 r k = r t + 1 + γ G t + 1 \begin{aligned} G_{t} &=\sum_{k=t+1}^{T} \gamma^{k-t-1} r_{k} \\ &=r_{t+1}+\gamma G_{t+1} \end{aligned} Gt=k=t+1∑Tγk−t−1rk=rt+1+γGt+1
上一个步骤和下一个步骤的未来总收益可以有这样子的一个关系。所以在代码的计算上,我们就是从后往前推,一步一步地往前推,先算 G T G_T GT,然后往前推,一直算到 G 1 G_1 G1 。
上图是 REINFORCE 的流程图。首先我们需要一个 policy model 来输出动作概率,输出动作概率后,我们 sample()
函数去得到一个具体的动作,然后跟环境交互过后,我们可以得到一整个回合的数据。拿到回合数据之后,我再去执行一下 learn()
函数,在 learn()
函数里面,我就可以拿这些数据去构造损失函数,扔给这个优化器去优化,去更新我的 policy model。
第5章 近端策略优化 PPO
5.1 从同策略到异策略
在讲 PPO 之前,我们先回顾下 on-policy 和 off-policy 这两种训练方法的区别。
在强化学习里面,我们要学习的就是一个 agent。
- 如果要学习的 agent 跟和环境互动的 agent 是同一个的话, 这个叫做
on-policy(同策略)
。 - 如果要学习的 agent 跟和环境互动的 agent 不是同一个的话, 那这个叫做
off-policy(异策略)
。
比较拟人化的讲法是如果要学习的那个 agent,一边跟环境互动,一边做学习这个叫 on-policy。 如果它在旁边看别人玩,通过看别人玩来学习的话,这个叫做 off-policy。
为什么我们会想要考虑 off-policy ?让我们来想想 policy gradient。Policy gradient 是 on-policy 的做法,因为在做 policy gradient 时,我们需要有一个 agent、一个 policy 和一个 actor。这个 actor 先去跟环境互动去搜集资料,搜集很多的 τ \tau τ,根据它搜集到的资料按照 policy gradient 的式子去更新 policy 的参数。所以 policy gradient 是一个 on-policy 的算法。
近端策略优化(Proximal Policy Optimization,简称 PPO)
是 policy gradient 的一个变形,它是现在 OpenAI 默认的强化学习算法。
∇ R ˉ θ = E τ ∼ p θ ( τ ) [ R ( τ ) ∇ log p θ ( τ ) ] \nabla \bar{R}_{\theta}=E_{\tau \sim p_{\theta}(\tau)}\left[R(\tau) \nabla \log p_{\theta}(\tau)\right] ∇Rˉθ=Eτ∼pθ(τ)[R(τ)∇logpθ(τ)]
问题是上面这个更新的式子中的 E τ ∼ p θ ( τ ) E_{\tau \sim p_{\theta}(\tau)} Eτ∼pθ(τ) 应该是你现在的 policy π θ \pi_{\theta} πθ 所采样出来的轨迹 τ \tau τ 做期望(expectation)。一旦更新了参数,从 θ \theta θ 变成 θ ′ \theta' θ′ , p θ ( τ ) p_\theta(\tau) pθ(τ)这个概率就不对了,之前采样出来的数据就变的不能用了。所以 policy gradient 是一个会花很多时间来采样数据的算法,大多数时间都在采样数据,agent 去跟环境做互动以后,接下来就要更新参数。你只能更新参数一次。接下来你就要重新再去收集数据, 然后才能再次更新参数。
这显然是非常花时间的,所以我们想要从 on-policy 变成 off-policy。 这样做就可以用另外一个 policy, 另外一个 actor θ ′ \theta' θ′ 去跟环境做互动( θ ′ \theta' θ′ 被固定了)。用 θ ′ \theta' θ′ 收集到的数据去训练 θ \theta θ。假设我们可以用 θ ′ \theta' θ′ 收集到的数据去训练 θ \theta θ,意味着说我们可以把 θ ′ \theta' θ′ 收集到的数据用非常多次,我们可以执行梯度上升(gradient ascent)好几次,我们可以更新参数好几次, 都只要用同一笔数据就好了。因为假设 θ \theta θ 有能力学习另外一个 actor θ ′ \theta' θ′ 所采样出来的数据的话, 那 θ ′ \theta' θ′ 就只要采样一次,也许采样多一点的数据, 让 θ \theta θ 去更新很多次,这样就会比较有效率。
具体怎么做呢?这边就需要介绍 重要性采样(Importance Sampling,IS)
的概念。
对于ー个随机变量,通常用概率密度函数来刻画该变量的概率分布特性。具体来说,给定随机变量的一个取值,可以根据概率密度函数来计算该值对应的概率(密度)。反过来,也可以根据概率密度函数提供的概率分布信息来生成随机变量的一个取值,这就是采样。因此,从某种意义上来说,采样是概率密度函数的逆向应用。与根据概率密度函数计算样本点对应的概率值不同,采样过程往往没有那么直接,通常需要根据待采样分布的具体特点来选择合适的采样策略。
假设你有一个函数 f ( x ) f(x) f(x),你要计算从 p 这个分布采样 x x x,再把 x x x 带到 f f f 里面,得到 f ( x ) f(x) f(x)。你要该怎么计算这个 f ( x ) f(x) f(x) 的期望值?假设你不能对 p 这个分布做积分的话,那你可以从 p 这个分布去采样一些数据 x i x^i xi。把 x i x^i xi 代到 f ( x ) f(x) f(x) 里面,然后取它的平均值,就可以近似 f ( x ) f(x) f(x) 的期望值。
现在有另外一个问题,我们没有办法从 p 这个分布里面采样数据。假设我们不能从 p 采样数据,只能从另外一个分布 q 去采样数据,q 可以是任何分布。我们不能够从 p 去采样数据,但可以从 q 去采样 x x x。我们从 q 去采样 x i x^i xi 的话就不能直接套下面的式子:
E x ∼ p [ f ( x ) ] ≈ 1 N ∑ i = 1 N f ( x i ) E_{x \sim p}[f(x)] \approx \frac{1}{N} \sum_{i=1}^N f(x^i) Ex∼p[f(x)]≈N1i=1∑Nf(xi)
因为上式是假设你的 x x x 都是从 p 采样出来的。
所以做一个修正,修正是这样子的。期望值 E x ∼ p [ f ( x ) ] E_{x \sim p}[f(x)] Ex∼p[f(x)] 其实就是 ∫ f ( x ) p ( x ) d x \int f(x) p(x) dx ∫f(x)p(x)dx,我们对其做如下的变换:
∫ f ( x ) p ( x ) d x = ∫ f ( x ) p ( x ) q ( x ) q ( x ) d x = E x ∼ q [ f ( x ) p ( x ) q ( x ) ] \int f(x) p(x) d x=\int f(x) \frac{p(x)}{q(x)} q(x) d x=E_{x \sim q}[f(x){\frac{p(x)}{q(x)}}] ∫f(x)p(x)dx=∫f(x)q(x)p(x)q(x)dx=Ex∼q[f(x)q(x)p(x)]
我们就可以写成对 q 里面所采样出来的 x x x 取期望值。我们从 q 里面采样 x x x,然后再去计算 f ( x ) p ( x ) q ( x ) f(x) \frac{p(x)}{q(x)} f(x)q(x)p(x),再去取期望值。所以就算我们不能从 p 里面去采样数据,只要能够从 q 里面去采样数据,然后代入上式,你就可以计算从 p 这个分布采样 x x x 代入 f f f 以后所算出来的期望值。
这边是从 q 做采样,所以从 q 里采样出来的每一笔数据,你需要乘上一个重要性权重(importance weight)
p ( x ) q ( x ) \frac{p(x)}{q(x)} q(x)p(x) 来修正这两个分布的差异。 q ( x ) q(x) q(x) 可以是任何分布,唯一的限制情况就是 q ( x ) q(x) q(x) 的概率是 0 的时候, p ( x ) p(x) p(x) 的概率不为 0,这样会没有定义。假设 q ( x ) q(x) q(x) 的概率是 0 的时候, p ( x ) p(x) p(x) 的概率也都是 0 的话,那这样 p ( x ) p(x) p(x) 除以 q ( x ) q(x) q(x)是有定义的。所以这个时候你就可以使用重要性采样这个技巧。你就可以从 p 做采样换成从 q 做采样。
**重要性采样有一些问题。**虽然理论上你可以把 p 换成任何的 q。但是在实现上,p 和 q 不能差太多。差太多的话,会有一些问题。什么样的问题呢?
E x ∼ p [ f ( x ) ] = E x ∼ q [ f ( x ) p ( x ) q ( x ) ] E_{x \sim p}[f(x)]=E_{x \sim q}\left[f(x) \frac{p(x)}{q(x)}\right] Ex∼p[f(x)]=Ex∼q[f(x)q(x)p(x)]
虽然上式成立(上式左边是 f ( x ) f(x) f(x) 的期望值,它的分布是 p,上式右边是 f ( x ) p ( x ) q ( x ) f(x) \frac{p(x)}{q(x)} f(x)q(x)p(x) 的期望值,它的分布是 q),但如果不是算期望值,而是算方差的话, Var x ∼ p [ f ( x ) ] \operatorname{Var}_{x \sim p}[f(x)] Varx∼p[f(x)] 和 Var x ∼ q [ f ( x ) p ( x ) q ( x ) ] \operatorname{Var}_{x \sim q}\left[f(x) \frac{p(x)}{q(x)}\right] Varx∼q[f(x)q(x)p(x)] 是不一样的。两个随机变量的平均值一样,并不代表它的方差一样。
我们可以代一下方差的公式 Var [ X ] = E [ X 2 ] − ( E [ X ] ) 2 \operatorname{Var}[X]=E\left[X^{2}\right]-(E[X])^{2} Var[X]=E[X2]−(E[X])2,然后得到下式:
Var x ∼ p [ f ( x ) ] = E x ∼ p [ f ( x ) 2 ] − ( E x ∼ p [ f ( x ) ] ) 2 \operatorname{Var}_{x \sim p}[f(x)]=E_{x \sim p}\left[f(x)^{2}\right]-\left(E_{x \sim p}[f(x)]\right)^{2} Varx∼p[f(x)]=Ex∼p[f(x)2]−(Ex∼p[f(x)])2
Var x ∼ q [ f ( x ) p ( x ) q ( x ) ] = E x ∼ q [ ( f ( x ) p ( x ) q ( x ) ) 2 ] − ( E x ∼ q [ f ( x ) p ( x ) q ( x ) ] ) 2 = E x ∼ p [ f ( x ) 2 p ( x ) q ( x ) ] − ( E x ∼ p [ f ( x ) ] ) 2 \begin{aligned} \operatorname{Var}_{x \sim q}\left[f(x) \frac{p(x)}{q(x)}\right] &=E_{x \sim q}\left[\left(f(x) \frac{p(x)}{q(x)}\right)^{2}\right]-\left(E_{x \sim q}\left[f(x) \frac{p(x)}{q(x)}\right]\right)^{2} \\ &=E_{x \sim p}\left[f(x)^{2} \frac{p(x)}{q(x)}\right]-\left(E_{x \sim p}[f(x)]\right)^{2} \end{aligned} Varx∼q[f(x)q(x)p(x)]=Ex∼q[(f(x)q(x)p(x))2]−(Ex∼q[f(x)q(x)p(x)])2=Ex∼p[f(x)2q(x)p(x)]−(Ex∼p[f(x)])2
Var x ∼ p [ f ( x ) ] \operatorname{Var}_{x \sim p}[f(x)] Varx∼p[f(x)] 和 Var x ∼ q [ f ( x ) p ( x ) q ( x ) ] \operatorname{Var}_{x \sim q}\left[f(x) \frac{p(x)}{q(x)}\right] Varx∼q[f(x)q(x)p(x)] 的差别在第一项是不同的, Var x ∼ q [ f ( x ) p ( x ) q ( x ) ] \operatorname{Var}_{x \sim q}\left[f(x) \frac{p(x)}{q(x)}\right] Varx∼q[f(x)q(x)p(x)] 的第一项多乘了 p ( x ) q ( x ) \frac{p(x)}{q(x)} q(x)p(x),如果 p ( x ) q ( x ) \frac{p(x)}{q(x)} q(x)p(x) 差距很大的话, f ( x ) p ( x ) q ( x ) f(x)\frac{p(x)}{q(x)} f(x)q(x)p(x) 的方差就会很大。所以理论上它们的期望值一样,也就是说,你只要对 p 这个分布采样够多次,q 这个分布采样够多,你得到的结果会是一样的。但是如果你采样的次数不够多,因为它们的方差差距是很大的,所以你就有可能得到非常大的差别。
举个例子,当 p ( x ) p(x) p(x) 和 q ( x ) q(x) q(x) 差距很大的时候,会发生什么样的问题。
假设蓝线是 p ( x ) p(x) p(x) 的分布,绿线是 q ( x ) q(x) q(x) 的分布,红线是 f ( x ) f(x) f(x)。如果我们要计算 f ( x ) f(x) f(x)的期望值,从 p ( x ) p(x) p(x) 这个分布做采样的话,那显然 E x ∼ p [ f ( x ) ] E_{x \sim p}[f(x)] Ex∼p[f(x)] 是负的,因为左边那块区域 p ( x ) p(x) p(x) 的概率很高,所以要采样的话,都会采样到这个地方,而 f ( x ) f(x) f(x) 在这个区域是负的, 所以理论上这一项算出来会是负。
接下来我们改成从 q ( x ) q(x) q(x) 这边做采样,因为 q ( x ) q(x) q(x) 在右边这边的概率比较高,所以如果你采样的点不够的话,那你可能都只采样到右侧。如果你都只采样到右侧的话,你会发现说,算 E x ∼ q [ f ( x ) p ( x ) q ( x ) ] E_{x \sim q}\left[f(x) \frac{p(x)}{q(x)}\right] Ex∼q[f(x)q(x)p(x)]这一项,搞不好还应该是正的。你这边采样到这些点,然后你去计算它们的 f ( x ) p ( x ) q ( x ) f(x) \frac{p(x)}{q(x)} f(x)q(x)p(x) 都是正的。你采样到这些点都是正的。 你取期望值以后也都是正的,这是因为你采样的次数不够多。假设你采样次数很少,你只能采样到右边这边。左边虽然概率很低,但也不是没有可能被采样到。假设你今天好不容易采样到左边的点,因为左边的点, p ( x ) p(x) p(x) 和 q ( x ) q(x) q(x) 是差很多的, 这边 p ( x ) p(x) p(x) 很大, q ( x ) q(x) q(x) 很小。今天 f ( x ) f(x) f(x) 好不容易终于采样到一个负的,这个负的就会被乘上一个非常大的权重,这样就可以平衡掉刚才那边一直采样到正的值的情况。最终你算出这一项的期望值,终究还是负的。但前提是你要采样够多次,这件事情才会发生。但有可能采样次数不够多, E x ∼ p [ f ( x ) ] E_{x \sim p}[f(x)] Ex∼p[f(x)] 跟 E x ∼ q [ f ( x ) p ( x ) q ( x ) ] E_{x \sim q}\left[f(x) \frac{p(x)}{q(x)}\right] Ex∼q[f(x)q(x)p(x)] 就有可能有很大的差距。这就是重要性采样的问题。
现在要做的事情就是把重要性采样用在 off-policy 的情况,把 on-policy 训练的算法改成 off-policy 训练的算法。怎么改呢,之前我们是拿 θ \theta θ 这个 policy 去跟环境做互动,采样出轨迹 τ \tau τ,然后计算 R ( τ ) ∇ log p θ ( τ ) R(\tau) \nabla \log p_{\theta}(\tau) R(τ)∇logpθ(τ)。现在我们不用 θ \theta θ 去跟环境做互动,假设有另外一个 policy θ ′ \theta' θ′,它就是另外一个 actor。它的工作是去做示范(demonstration)。 θ ′ \theta' θ′ 的工作是要去示范给 θ \theta θ 看。它去跟环境做互动,告诉 θ \theta θ 说,它跟环境做互动会发生什么事,借此来训练 θ \theta θ。我们要训练的是 θ \theta θ , θ ′ \theta' θ′ 只是负责做示范,跟环境做互动。
我们现在的 τ \tau τ 是从 θ ′ \theta' θ′ 采样出来的,是拿 θ ′ \theta' θ′ 去跟环境做互动。所以采样出来的 τ \tau τ 是从 θ ′ \theta' θ′ 采样出来的,这两个分布不一样。但没有关系,假设你本来是从 p 做采样,但你发现你不能从 p 做采样,所以我们不拿 θ \theta θ 去跟环境做互动。你可以把 p 换 q,然后在后面补上一个重要性权重。现在的状况就是一样,把 θ \theta θ 换成 θ ′ \theta' θ′ 后,要补上一个重要性权重 p θ ( τ ) p θ ′ ( τ ) \frac{p_{\theta}(\tau)}{p_{\theta^{\prime}}(\tau)} pθ′(τ)pθ(τ)。这个重要性权重就是某一个轨迹 τ \tau τ 用 θ \theta θ 算出来的概率除以这个轨迹 τ \tau τ 用 θ ′ \theta' θ′ 算出来的概率。这一项是很重要的,因为你要学习的是 actor θ \theta θ 和 θ ′ \theta' θ′ 是不太一样的, θ ′ \theta' θ′ 会见到的情形跟 θ \theta θ 见到的情形不见得是一样的,所以中间要做一个修正的项。
Q: 现在的数据是从 θ ′ \theta' θ′ 采样出来的,从 θ \theta θ 换成 θ ′ \theta' θ′ 有什么好处?
A: 因为现在跟环境做互动是 θ ′ \theta' θ′ 而不是 θ \theta θ。所以采样出来的东西跟 θ \theta θ 本身是没有关系的。所以你就可以让 θ ′ \theta' θ′ 做互动采样一大堆的数据, θ \theta θ 可以更新参数很多次,一直到 θ \theta θ 训练到一定的程度,更新很多次以后, θ ′ \theta' θ′ 再重新去做采样,这就是 on-policy 换成 off-policy 的妙用。
实际在做 policy gradient 的时候,我们并不是给整个轨迹 τ \tau τ 都一样的分数,而是每一个状态-动作的对(pair)会分开来计算。实际上更新梯度的时候,如下式所示。
= E ( s t , a t ) ∼ π θ [ A θ ( s t , a t ) ∇ log p θ ( a t n ∣ s t n ) ] =E_{\left(s_{t}, a_{t}\right) \sim \pi_{\theta}}\left[A^{\theta}\left(s_{t}, a_{t}\right) \nabla \log p_{\theta}\left(a_{t}^{n} | s_{t}^{n}\right)\right] =E(st,at)∼πθ[Aθ(st,at)∇logpθ(atn∣stn)]
我们用 θ \theta θ 这个 actor 去采样出 s t s_t st 跟 a t a_t at,采样出状态跟动作的对,我们会计算这个状态跟动作对的 advantage A θ ( s t , a t ) A^{\theta}\left(s_{t}, a_{t}\right) Aθ(st,at), 就是它有多好。
A θ ( s t , a t ) A^{\theta}\left(s_{t}, a_{t}\right) Aθ(st,at) 就是累积奖励减掉 bias,这一项就是估测出来的。它要估测的是,在状态 s t s_t st 采取动作 a t a_t at 是好的还是不好的。接下来后面会乘上 ∇ log p θ ( a t n ∣ s t n ) \nabla \log p_{\theta}\left(a_{t}^{n} | s_{t}^{n}\right) ∇logpθ(atn∣stn),也就是说如果 A θ ( s t , a t ) A^{\theta}\left(s_{t}, a_{t}\right) Aθ(st,at) 是正的,就要增加概率, 如果是负的,就要减少概率。
我们通过重要性采样把 on-policy 变成 off-policy,从 θ \theta θ 变成 θ ′ \theta' θ′。所以现在 s t s_t st、 a t a_t at 是 θ ′ \theta' θ′ 跟环境互动以后所采样到的数据。 但是拿来训练要调整参数是模型 θ \theta θ。因为 θ ′ \theta' θ′ 跟 θ \theta θ 是不同的模型,所以你要做一个修正的项。这项修正的项,就是用重要性采样的技术,把 s t s_t st、 a t a_t at 用 θ \theta θ 采样出来的概率除掉 s t s_t st、 a t a_t at 用 θ ′ \theta' θ′ 采样出来的概率。
= E ( s t , a t ) ∼ π θ ′ [ p θ ( s t , a t ) p θ ′ ( s t , a t ) A θ ( s t , a t ) ∇ log p θ ( a t n ∣ s t n ) ] =E_{\left(s_{t}, a_{t}\right) \sim \pi_{\theta^{\prime}}}\left[\frac{p_{\theta}\left(s_{t}, a_{t}\right)}{p_{\theta^{\prime}}\left(s_{t}, a_{t}\right)} A^{\theta}\left(s_{t}, a_{t}\right) \nabla \log p_{\theta}\left(a_{t}^{n} | s_{t}^{n}\right)\right] =E(st,at)∼πθ′[pθ′(st,at)pθ(st,at)Aθ(st,at)∇logpθ(atn∣stn)]
A θ ( s t , a t ) A^{\theta}(s_t,a_t) Aθ(st,at) 有一个上标 θ \theta θ, θ \theta θ 代表说这个是 actor θ \theta θ 跟环境互动的时候所计算出来的 A。但是实际上从 θ \theta θ 换到 θ ′ \theta' θ′ 的时候, A θ ( s t , a t ) A^{\theta}(s_t,a_t) Aθ(st,at) 应该改成 A θ ′ ( s t , a t ) A^{\theta'}(s_t,a_t) Aθ′(st,at),为什么?A 这一项是想要估测说现在在某一个状态采取某一个动作,接下来会得到累积奖励的值减掉 baseline 。你怎么估 A 这一项,你就会看在状态 s t s_t st,采取动作 a t a_t at,接下来会得到的奖励的总和,再减掉 baseline。之前是 θ \theta θ 在跟环境做互动,所以你观察到的是 θ \theta θ 可以得到的奖励。但现在是 θ ′ \theta' θ′ 在跟环境做互动,所以你得到的这个 advantage, 其实是根据 θ ′ \theta' θ′ 所估计出来的 advantage。但我们现在先不要管那么多,我们就假设这两项可能是差不多的。
接下来,我们可以拆解 p θ ( s t , a t ) p_{\theta}\left(s_{t}, a_{t}\right) pθ(st,at) 和 p θ ′ ( s t , a t ) p_{\theta'}\left(s_{t}, a_{t}\right) pθ′(st,at),即
p θ ( s t , a t ) = p θ ( a t ∣ s t ) p θ ( s t ) p θ ′ ( s t , a t ) = p θ ′ ( a t ∣ s t ) p θ ′ ( s t ) \begin{aligned} p_{\theta}\left(s_{t}, a_{t}\right)&=p_{\theta}\left(a_{t}|s_{t}\right) p_{\theta}(s_t) \\ p_{\theta'}\left(s_{t}, a_{t}\right)&=p_{\theta'}\left(a_{t}|s_{t}\right) p_{\theta'}(s_t) \end{aligned} pθ(st,at)pθ′(st,at)=pθ(at∣st)pθ(st)=pθ′(at∣st)pθ′(st)
于是我们得到下式:
= E ( s t , a t ) ∼ π θ ′ [ p θ ( a t ∣ s t ) p θ ′ ( a t ∣ s t ) p θ ( s t ) p θ ′ ( s t ) A θ ′ ( s t , a t ) ∇ log p θ ( a t n ∣ s t n ) ] =E_{\left(s_{t}, a_{t}\right) \sim \pi_{\theta^{\prime}}}\left[\frac{p_{\theta}\left(a_{t} | s_{t}\right)}{p_{\theta^{\prime}}\left(a_{t} | s_{t}\right)} \frac{p_{\theta}\left(s_{t}\right)}{p_{\theta^{\prime}}\left(s_{t}\right)} A^{\theta^{\prime}}\left(s_{t}, a_{t}\right) \nabla \log p_{\theta}\left(a_{t}^{n} | s_{t}^{n}\right)\right] =E(st,at)∼πθ′[pθ′(at∣st)pθ(at∣st)pθ′(st)pθ(st)Aθ′(st,at)∇logpθ(atn∣stn)]
这边需要做一件事情是,假设模型是 θ \theta θ 的时候,你看到 s t s_t st 的概率,跟模型是 θ ′ \theta' θ′ 的时候,你看到 s t s_t st 的概率是差不多的,即 p θ ( s t ) = p θ ′ ( s t ) p_{\theta}(s_t)=p_{\theta'}(s_t) pθ(st)=pθ′(st)。因为它们是一样的,所以你可以把它删掉,即
= E ( s t , a t ) ∼ π θ ′ [ p θ ( a t ∣ s t ) p θ ′ ( a t ∣ s t ) A θ ′ ( s t , a t ) ∇ log p θ ( a t n ∣ s t n ) ] (1) =E_{\left(s_{t}, a_{t}\right) \sim \pi_{\theta^{\prime}}}\left[\frac{p_{\theta}\left(a_{t} | s_{t}\right)}{p_{\theta^{\prime}}\left(a_{t} | s_{t}\right)} A^{\theta^{\prime}}\left(s_{t}, a_{t}\right) \nabla \log p_{\theta}\left(a_{t}^{n} | s_{t}^{n}\right)\right] \tag{1} =E(st,at)∼πθ′[pθ′(at∣st)pθ(at∣st)Aθ′(st,at)∇logpθ(atn∣stn)](1)
Q: 为什么可以假设 p θ ( s t ) p_{\theta}(s_t) pθ(st) 和 p θ ′ ( s t ) p_{\theta'}(s_t) pθ′(st) 是差不多的?
A: 因为你会看到什么状态往往跟你会采取什么样的动作是没有太大的关系的。比如说你玩不同的 Atari 的游戏,其实你看到的游戏画面都是差不多的,所以也许不同的 θ \theta θ 对 s t s_t st 是没有影响的。但更直觉的理由就是 p θ ( s t ) p_{\theta}(s_t) pθ(st) 很难算,想想看这项要怎么算,这一项你还要说我有一个参数 θ \theta θ,然后拿 θ \theta θ 去跟环境做互动,算 s t s_t st 出现的概率,这个你很难算。尤其如果输入是图片的话, 同样的 s t s_t st 根本就不会出现第二次。你根本没有办法估这一项, 所以干脆就无视这个问题。
但是 p θ ( a t ∣ s t ) p_{\theta}(a_t|s_t) pθ(at∣st)很好算。你手上有 θ \theta θ 这个参数,它就是个网络。你就把 s t s_t st 带进去, s t s_t st 就是游戏画面,你把游戏画面带进去,它就会告诉你某一个状态的 a t a_t at 概率是多少。我们有个 policy 的网络,把 s t s_t st 带进去,它会告诉我们每一个 a t a_t at 的概率是多少。所以 p θ ( a t ∣ s t ) p θ ′ ( a t ∣ s t ) \frac{p_{\theta}\left(a_{t} | s_{t}\right)}{p_{\theta^{\prime}}\left(a_{t} | s_{t}\right)} pθ′(at∣st)pθ(at∣st) 这一项,你只要知道 θ \theta θ 和 θ ′ \theta' θ′ 的参数就可以算。
现在我们得到一个新的目标函数。
J θ ′ ( θ ) = E ( s t , a t ) ∼ π θ ′ [ p θ ( a t ∣ s t ) p θ ′ ( a t ∣ s t ) A θ ′ ( s t , a t ) ] J^{\theta^{\prime}}(\theta)=E_{\left(s_{t}, a_{t}\right) \sim \pi_{\theta^{\prime}}}\left[\frac{p_{\theta}\left(a_{t} | s_{t}\right)}{p_{\theta^{\prime}}\left(a_{t} | s_{t}\right)} A^{\theta^{\prime}}\left(s_{t}, a_{t}\right)\right] Jθ′(θ)=E(st,at)∼πθ′[pθ′(at∣st)pθ(at∣st)Aθ′(st,at)]
式(1)是梯度,其实我们可以从梯度去反推原来的目标函数,我们可以用如下的公式来反推目标函数:
∇ f ( x ) = f ( x ) ∇ log f ( x ) \nabla f(x)=f(x) \nabla \log f(x) ∇f(x)=f(x)∇logf(x)
要注意一点,对 θ \theta θ 求梯度时, p θ ′ ( a t ∣ s t ) p_{\theta^{\prime}}(a_{t} | s_{t}) pθ′(at∣st) 和 A θ ′ ( s t , a t ) A^{\theta^{\prime}}\left(s_{t}, a_{t}\right) Aθ′(st,at) 都是常数。
所以实际上,当我们使用重要性采样的时候,要去优化的那一个目标函数就长这样子,我们把它写作 J θ ′ ( θ ) J^{\theta^{\prime}}(\theta) Jθ′(θ)。为什么写成 J θ ′ ( θ ) J^{\theta^{\prime}}(\theta) Jθ′(θ) 呢,这个括号里面那个 θ \theta θ 代表我们要去优化的那个参数。 θ ′ \theta' θ′ 是说我们拿 θ ′ \theta' θ′ 去做示范,就是现在真正在跟环境互动的是 θ ′ \theta' θ′。因为 θ \theta θ 不跟环境做互动,是 θ ′ \theta' θ′ 在跟环境互动。
然后你用 θ ′ \theta' θ′ 去跟环境做互动,采样出 s t s_t st、 a t a_t at 以后,你要去计算 s t s_t st 跟 a t a_t at 的 advantage,然后你再去把它乘上 p θ ( a t ∣ s t ) p θ ′ ( a t ∣ s t ) \frac{p_{\theta}\left(a_{t} | s_{t}\right)}{p_{\theta^{\prime}}\left(a_{t} | s_{t}\right)} pθ′(at∣st)pθ(at∣st)。 p θ ( a t ∣ s t ) p θ ′ ( a t ∣ s t ) \frac{p_{\theta}\left(a_{t} | s_{t}\right)}{p_{\theta^{\prime}}\left(a_{t} | s_{t}\right)} pθ′(at∣st)pθ(at∣st) 是好算的, A θ ′ ( s t , a t ) A^{\theta^{\prime}}\left(s_{t}, a_{t}\right) Aθ′(st,at) 可以从这个采样的结果里面去估测出来的,所以 J θ ′ ( θ ) J^{\theta^{\prime}}(\theta) Jθ′(θ) 是可以算的。实际上在更新参数的时候,就是按照式(1) 来更新参数。
5.2 近端策略优化
我们可以通过重要性采样把 on-policy 换成 off-policy,但重要性采样有一个问题:如果 p θ ( a t ∣ s t ) p_{\theta}\left(a_{t} | s_{t}\right) pθ(at∣st) 跟 p θ ′ ( a t ∣ s t ) p_{\theta'}\left(a_{t} | s_{t}\right) pθ′(at∣st) 这两个分布差太多的话,重要性采样的结果就会不好。怎么避免它差太多呢?这个就是 Proximal Policy Optimization (PPO)
在做的事情。注意,由于在 PPO 中 θ ′ \theta' θ′ 是 θ old \theta_{\text{old}} θold,即 behavior policy 也是 θ \theta θ,所以 PPO 是 on-policy 的算法。
PPO 实际上做的事情就是这样,在 off-policy 的方法里要优化的是 J θ ′ ( θ ) J^{\theta^{\prime}}(\theta) Jθ′(θ)。但是这个目标函数又牵涉到重要性采样。在做重要性采样的时候, p θ ( a t ∣ s t ) p_{\theta}\left(a_{t} | s_{t}\right) pθ(at∣st) 不能跟 p θ ′ ( a t ∣ s t ) p_{\theta'}\left(a_{t} | s_{t}\right) pθ′(at∣st)差太多。你做示范的模型不能够跟真正的模型差太多,差太多的话,重要性采样的结果就会不好。我们在训练的时候,多加一个约束(constrain)。这个约束是 θ \theta θ 跟 θ ′ \theta' θ′ 输出的动作的 KL 散度(KL divergence),简单来说,这一项的意思就是要衡量说 θ \theta θ 跟 θ ′ \theta' θ′ 有多像。
然后我们希望在训练的过程中,学习出来的 θ \theta θ 跟 θ ′ \theta' θ′ 越像越好。因为如果 θ \theta θ 跟 θ ′ \theta' θ′ 不像的话,最后的结果就会不好。所以在 PPO 里面有两个式子,一方面是优化本来要优化的东西,但再加一个约束。这个约束就好像正则化(regularization) 的项(term) 一样,在做机器学习的时候不是有 L1/L2 的正则化。这一项也很像正则化,这样正则化做的事情就是希望最后学习出来的 θ \theta θ 不要跟 θ ′ \theta' θ′ 太不一样。
PPO 有一个前身叫做信任区域策略优化(Trust Region Policy Optimization,TRPO)
,TRPO 的式子如下式所示:
J T R P O θ ′ ( θ ) = E ( s t , a t ) ∼ π θ ′ [ p θ ( a t ∣ s t ) p θ ′ ( a t ∣ s t ) A θ ′ ( s t , a t ) ] K L ( θ , θ ′ ) < δ \begin{aligned} J_{T R P O}^{\theta^{\prime}}(\theta)=E_{\left(s_{t}, a_{t}\right) \sim \pi_{\theta^{\prime}}}\left[\frac{p_{\theta}\left(a_{t} | s_{t}\right)}{p_{\theta^{\prime}}\left(a_{t} | s_{t}\right)} A^{\theta^{\prime}}\left(s_{t}, a_{t}\right)\right] \\ \\ \mathrm{KL}\left(\theta, \theta^{\prime}\right)<\delta \end{aligned} JTRPOθ′(θ)=E(st,at)∼πθ′[pθ′(at∣st)pθ(at∣st)Aθ′(st,at)]KL(θ,θ′)<δ
它与 PPO 不一样的地方是约束摆的位置不一样,PPO 是直接把约束放到你要优化的那个式子里面,然后你就可以用梯度上升的方法去最大化这个式子。但 TRPO 是把 KL 散度当作约束,它希望 θ \theta θ 跟 θ ′ \theta' θ′ 的 KL 散度小于一个 δ \delta δ。如果你使用的是基于梯度的优化时,有约束是很难处理的。
TRPO 是很难处理的,因为它把 KL 散度约束当做一个额外的约束,没有放目标(objective)里面,所以它很难算,所以一般就用 PPO 而不是 TRPO。看文献上的结果是,PPO 跟 TRPO 性能差不多,但 PPO 在实现上比 TRPO 容易的多。
Q: KL 散度到底指的是什么?
A:
这边我是直接把 KL 散度当做一个函数,输入是 θ \theta θ 跟 θ ′ \theta' θ′,但我的意思并不是说把 θ \theta θ 或 θ ′ \theta' θ′ 当做一个分布,算这两个分布之间的距离。所谓的 θ \theta θ 跟 θ ′ \theta' θ′ 的距离并不是参数上的距离,而是行为(behavior)上的距离。
假设你有两个 actor,它们的参数分别为 θ \theta θ 和 θ ′ \theta' θ′,所谓参数上的距离就是你算这两组参数有多像。这里讲的不是参数上的距离, 而是它们行为上的距离。你先代进去一个状态 s,它会对这个动作的空间输出一个分布。假设你有 3 个动作,3 个可能的动作就输出 3 个值。今天所指的距离是行为距离(behavior distance),也就是说,给定同样的状态,输出动作之间的差距。这两个动作的分布都是一个概率分布,所以就可以计算这两个概率分布的 KL 散度。把不同的状态输出的这两个分布的 KL 散度平均起来才是我这边所指的两个 actor 间的 KL 散度。
Q: 为什么不直接算 θ \theta θ 和 θ ′ \theta' θ′ 之间的距离?算这个距离的话,甚至不要用 KL 散度算,L1 跟 L2 的范数(norm)也可以保证 θ \theta θ 跟 θ ′ \theta' θ′ 很接近。
A: 在做强化学习的时候,之所以我们考虑的不是参数上的距离,而是动作上的距离,是因为很有可能对 actor 来说,参数的变化跟动作的变化不一定是完全一致的。有时候你参数小小变了一下,它可能输出的行为就差很多。或者是参数变很多,但输出的行为可能没什么改变。**所以我们真正在意的是这个 actor 的行为上的差距,而不是它们参数上的差距。**所以在做 PPO 的时候,所谓的 KL 散度并不是参数的距离,而是动作的距离。
5.2.1 近端策略优化惩罚 PPO-Penalty
PPO 算法有两个主要的变种:PPO-Penalty 和 PPO-Clip。
我们来看一下 PPO1
的算法,即 PPO-Penalty
。它先初始化一个 policy 的参数 θ 0 \theta^0 θ0。然后在每一个迭代里面,你要用参数 θ k \theta^k θk, θ k \theta^k θk 就是你在前一个训练的迭代得到的 actor 的参数,你用 θ k \theta^k θk 去跟环境做互动,采样到一大堆状态-动作的对。
然后你根据 θ k \theta^k θk 互动的结果,估测一下 A θ k ( s t , a t ) A^{\theta^{k}}\left(s_{t}, a_{t}\right) Aθk(st,at)。然后你就使用 PPO 的优化的公式。但跟原来的 policy gradient 不一样,原来的 policy gradient 只能更新一次参数,更新完以后,你就要重新采样数据。但是现在不用,你拿 θ k \theta^k θk 去跟环境做互动,采样到这组数据以后,你可以让 θ \theta θ 更新很多次,想办法去最大化目标函数。这边 θ \theta θ 更新很多次没有关系,因为我们已经有做重要性采样,所以这些经验,这些状态-动作的对是从 θ k \theta^k θk 采样出来的没有关系。 θ \theta θ 可以更新很多次,它跟 θ k \theta^k θk 变得不太一样也没有关系,你还是可以照样训练 θ \theta θ。
在 PPO 的论文里面还有一个 adaptive KL divergence
。这边会遇到一个问题就是 β \beta β 要设多少,它就跟正则化一样。正则化前面也要乘一个权重,所以这个 KL 散度前面也要乘一个权重,但 β \beta β 要设多少呢?所以有个动态调整 β \beta β 的方法。
- 在这个方法里面,你先设一个你可以接受的 KL 散度的最大值。假设优化完这个式子以后,你发现 KL 散度的项太大,那就代表说后面这个惩罚的项没有发挥作用,那就把 β \beta β 调大。
- 另外,你设一个 KL 散度的最小值。如果优化完上面这个式子以后,你发现 KL 散度比最小值还要小,那代表后面这一项的效果太强了,你怕他只弄后面这一项,那 θ \theta θ 跟 θ k \theta^k θk 都一样,这不是你要的,所以你要减少 β \beta β。
所以 β \beta β 是可以动态调整的。这个叫做 adaptive KL penalty
。
5.2.2 近端策略优化裁剪 PPO-Clip
如果你觉得算 KL 散度很复杂,有一个PPO2
,PPO2 即 PPO-Clip
。PPO2 要去最大化的目标函数如下式所示,它的式子里面就没有 KL 散度 。
J P P O 2 θ k ( θ ) ≈ ∑ ( s t , a t ) min ( p θ ( a t ∣ s t ) p θ k ( a t ∣ s t ) A θ k ( s t , a t ) , clip ( p θ ( a t ∣ s t ) p θ k ( a t ∣ s t ) , 1 − ε , 1 + ε ) A θ k ( s t , a t ) ) \begin{aligned} J_{P P O 2}^{\theta^{k}}(\theta) \approx \sum_{\left(s_{t}, a_{t}\right)} \min &\left(\frac{p_{\theta}\left(a_{t} | s_{t}\right)}{p_{\theta^{k}}\left(a_{t} | s_{t}\right)} A^{\theta^{k}}\left(s_{t}, a_{t}\right),\right.\\ &\left.\operatorname{clip}\left(\frac{p_{\theta}\left(a_{t} | s_{t}\right)}{p_{\theta^{k}}\left(a_{t} | s_{t}\right)}, 1-\varepsilon, 1+\varepsilon\right) A^{\theta^{k}}\left(s_{t}, a_{t}\right)\right) \end{aligned} JPPO2θk(θ)≈(st,at)∑min(pθk(at∣st)pθ(at∣st)Aθk(st,at),clip(pθk(at∣st)pθ(at∣st),1−ε,1+ε)Aθk(st,at))
这个式子看起来有点复杂,但实际实现就很简单。我们来看一下这个式子到底是什么意思。
- Min 这个操作符(operator)做的事情是第一项跟第二项里面选比较小的那个。
- 第二项前面有个 clip 函数,clip 函数的意思是说,
- 在括号里面有三项,如果第一项小于第二项的话,那就输出 1 − ε 1-\varepsilon 1−ε 。
- 第一项如果大于第三项的话,那就输出 1 + ε 1+\varepsilon 1+ε。
- ε \varepsilon ε 是一个超参数,你要 tune 的,你可以设成 0.1 或 设 0.2 。
假设这边设 0.2 的话,如下式所示
clip ( p θ ( a t ∣ s t ) p θ k ( a t ∣ s t ) , 0.8 , 1.2 ) \operatorname{clip}\left(\frac{p_{\theta}\left(a_{t} | s_{t}\right)}{p_{\theta^{k}}\left(a_{t} | s_{t}\right)}, 0.8, 1.2\right) clip(pθk(at∣st)pθ(at∣st),0.8,1.2)
如果 p θ ( a t ∣ s t ) p θ k ( a t ∣ s t ) \frac{p_{\theta}\left(a_{t} | s_{t}\right)}{p_{\theta^{k}}\left(a_{t} | s_{t}\right)} pθk(at∣st)pθ(at∣st) 算出来小于 0.8,那就当作 0.8。如果算出来大于 1.2,那就当作1.2。
我们先看看下面这项这个算出来到底是什么东西:
clip ( p θ ( a t ∣ s t ) p θ k ( a t ∣ s t ) , 1 − ε , 1 + ε ) \operatorname{clip}\left(\frac{p_{\theta}\left(a_{t} | s_{t}\right)}{p_{\theta^{k}}\left(a_{t} | s_{t}\right)}, 1-\varepsilon, 1+\varepsilon\right) clip(pθk(at∣st)pθ(at∣st),1−ε,1+ε)
上图的横轴是 p θ ( a t ∣ s t ) p θ k ( a t ∣ s t ) \frac{p_{\theta}\left(a_{t} | s_{t}\right)}{p_{\theta^{k}}\left(a_{t} | s_{t}\right)} pθk(at∣st)pθ(at∣st),纵轴是 clip 函数的输出。
- 如果 p θ ( a t ∣ s t ) p θ k ( a t ∣ s t ) \frac{p_{\theta}\left(a_{t} | s_{t}\right)}{p_{\theta^{k}}\left(a_{t} | s_{t}\right)} pθk(at∣st)pθ(at∣st) 大于 1 + ε 1+\varepsilon 1+ε,输出就是 1 + ε 1+\varepsilon 1+ε。
- 如果小于 1 − ε 1-\varepsilon 1−ε, 它输出就是 1 − ε 1-\varepsilon 1−ε。
- 如果介于 1 + ε 1+\varepsilon 1+ε 跟 1 − ε 1-\varepsilon 1−ε 之间, 就是输入等于输出。
- p θ ( a t ∣ s t ) p θ k ( a t ∣ s t ) \frac{p_{\theta}\left(a_{t} | s_{t}\right)}{p_{\theta^{k}}\left(a_{t} | s_{t}\right)} pθk(at∣st)pθ(at∣st) 是绿色的线;
- clip ( p θ ( a t ∣ s t ) p θ k ( a t ∣ s t ) , 1 − ε , 1 + ε ) \operatorname{clip}\left(\frac{p_{\theta}\left(a_{t} | s_{t}\right)}{p_{\theta^{k}}\left(a_{t} | s_{t}\right)}, 1-\varepsilon, 1+\varepsilon\right) clip(pθk(at∣st)pθ(at∣st),1−ε,1+ε) 是蓝色的线;
- 在绿色的线跟蓝色的线中间,我们要取一个最小的。假设前面乘上的这个项 A,它是大于 0 的话,取最小的结果,就是红色的这一条线。
如果 A 小于 0 的话,取最小的以后,就得到红色的这一条线。
虽然这个式子看起来有点复杂,实现起来是蛮简单的,因为这个式子想要做的事情就是希望 p θ ( a t ∣ s t ) p_{\theta}(a_{t} | s_{t}) pθ(at∣st) 跟 p θ k ( a t ∣ s t ) p_{\theta^k}(a_{t} | s_{t}) pθk(at∣st),也就是你拿来做示范的模型跟你实际上学习的模型,在优化以后不要差距太大。
怎么让它做到不要差距太大呢?
- 如果 A > 0,也就是某一个状态-动作的对是好的,那我们希望增加这个状态-动作对的概率。也就是说,我们想要让 p θ ( a t ∣ s t ) p_{\theta}(a_{t} | s_{t}) pθ(at∣st) 越大越好,但它跟 p θ k ( a t ∣ s t ) p_{\theta^k}(a_{t} | s_{t}) pθk(at∣st) 的比值不可以超过 1 + ε 1+\varepsilon 1+ε。如果超过 1 + ε 1+\varepsilon 1+ε 的话,就没有 benefit 了。红色的线就是我们的目标函数,我们希望目标越大越好,我们希望 p θ ( a t ∣ s t ) p_{\theta}(a_{t} | s_{t}) pθ(at∣st) 越大越好。但是 p θ ( a t ∣ s t ) p θ k ( a t ∣ s t ) \frac{p_{\theta}\left(a_{t} | s_{t}\right)}{p_{\theta^{k}}\left(a_{t} | s_{t}\right)} pθk(at∣st)pθ(at∣st) 只要大过 1 + ε 1+\varepsilon 1+ε,就没有 benefit 了。所以今天在训练的时候,当 p θ ( a t ∣ s t ) p_{\theta}(a_{t} | s_{t}) pθ(at∣st) 被训练到 p θ ( a t ∣ s t ) p θ k ( a t ∣ s t ) > 1 + ε \frac{p_{\theta}\left(a_{t} | s_{t}\right)}{p_{\theta^{k}}\left(a_{t} | s_{t}\right)}>1+\varepsilon pθk(at∣st)pθ(at∣st)>1+ε 时,它就会停止。假设 p θ ( a t ∣ s t ) p_{\theta}(a_{t} | s_{t}) pθ(at∣st) 比 p θ k ( a t ∣ s t ) p_{\theta^k}(a_{t} | s_{t}) pθk(at∣st) 还要小,并且这个 advantage 是正的。因为这个动作是好的,我们当然希望这个动作被采取的概率越大越好,我们希望 p θ ( a t ∣ s t ) p_{\theta}(a_{t} | s_{t}) pθ(at∣st) 越大越好。所以假设 p θ ( a t ∣ s t ) p_{\theta}(a_{t} | s_{t}) pθ(at∣st) 还比 p θ k ( a t ∣ s t ) p_{\theta^k}(a_{t} | s_{t}) pθk(at∣st) 小,那就尽量把它挪大,但只要大到 1 + ε 1+\varepsilon 1+ε 就好。
- 如果 A < 0,也就是某一个状态-动作对是不好的,我们希望把 p θ ( a t ∣ s t ) p_{\theta}(a_{t} | s_{t}) pθ(at∣st) 减小。如果 p θ ( a t ∣ s t ) p_{\theta}(a_{t} | s_{t}) pθ(at∣st) 比 p θ k ( a t ∣ s t ) p_{\theta^k}(a_{t} | s_{t}) pθk(at∣st) 还大,那你就尽量把它压小,压到 p θ ( a t ∣ s t ) p θ k ( a t ∣ s t ) \frac{p_{\theta}\left(a_{t} | s_{t}\right)}{p_{\theta^{k}}\left(a_{t} | s_{t}\right)} pθk(at∣st)pθ(at∣st) 是 1 − ϵ 1-\epsilon 1−ϵ 的时候就停了,就不要再压得更小。
这样的好处就是,你不会让 p θ ( a t ∣ s t ) p_{\theta}(a_{t} | s_{t}) pθ(at∣st) 跟 p θ k ( a t ∣ s t ) p_{\theta^k}(a_{t} | s_{t}) pθk(at∣st) 差距太大。要实现这个东西,很简单。
上图是 PPO 跟其它方法的比较。Actor-Critic 和 A2C+Trust Region 方法是基于 actor-critic 的方法。PPO 是紫色线的方法,这边每张图就是某一个 RL 的任务,你会发现说在多数的情况(cases)里面,PPO 都是不错的,不是最好的,就是第二好的。
文章评论