1 前置知识点
基本概念
https://www.yuque.com/docs/share/04b60c4c-90ec-49c7-8a47-0dae7d3c78c7?#
(部分符合的定义在这里)
要理解PPO,就必须先理解Actor-Critic.
Actor负责输出policy,也就是在某个状态下执行各种action的概率分布
Critic负责输出Vaue of state。
Actor和Critic的默契:Actor相信Critic给的状态的value就是真的; Critic也相信Actor选送过来的(s,a)中的a就是最优的action。通过不断的迭代,强化这个信任关系的正确性。
(这体现了我们的价值观 [因为信任,所以简单],哈哈哈~)
所以这样就不难理解Critic的Loss是怎么来的了,Critic的输出就是state的Value,那就让Critic模型的输出使得以下公式成立:
$$V_s=r_{s,a}+\gamma V_{s'}$$
其中,$r_{s,a}, s,a,s'$是训练Critic需要的数据,$s'$是在状态$s$下执行动作$a$得到新状态, $r_{s,a}$是reward, $\gamma$ 是discount factor。
跟基础概念的区别是,这里的系统假定是执行动作$a$只能到$s'$, 没有体现执行$a$可以得到不同的状态; (但是其实这种概率可以体现在训练数据中,因为$(s,a,r_{s,a})
$$和$$s'$ 不一定是一一对应,其概率可以通过sampling得到的数据分布体现)
所以Critic的Loss就是$|r_{s,a}+\gamma V_{s'}-Vs|$,也就是所谓的TD(Time Difference)-Error的L1,或者L2也可以.
那么Actor的Loss怎么计算呢?
这里就先来明白Advantage的概念,其实也就是TD-Error
$$Adv=r_{s,a}+\gamma V_{s'}-Vs$$
之所以称之为Advantage,是因为假如Advantage>0, 就是说实际执行$a$之后,发现当前的状态Value实际上比当前Critic估计出来的要大,所以这是个好的Action,它能够让$V_s$ 变大,Actor应该增大这个action的概率;反之,Advantage<0,这个action就是不好的,应该减小执行它概率。
所以Actor的Loss就是$$-log(\pi(a|s))*Adv$$, 因为要最小化Loss,所以前面加个负号;Adv的符号代表了应该增大这个action的输出概率,还是减小这个action的输出概率;Adv的大小代表了增加或减小的幅度应该有多大。
2 Proximal Policy Optimization(PPO)
2.1 PPO主要是来解决什么问题?
它是为了让我们在训练Agent的时候能够take the biggest possible improvement step on a policy using the data we currently have, without stepping so far that we accidentally cause performance collapse。就是更新policy的时候尽可能步子迈大点,但也要防止扯着蛋,即模型参数崩了。
2.2 PPO怎么解决这个问题的?
简单来说,相同的网络结构,维护两组模型参数Old和New,在每次迭代的时候去更新New的参数,但是要控制New的模型输出Policy和Old的Policy不要差距太大,本轮迭代结束后,把New的参数覆盖掉Old的参数。
怎么去控制差距不要太大呢?作者给了两种方式: PPO-Penalty, PPO-Clip
2.2.1 PPO-Clip
先说PPO-Clip, 它通过下面的公式来更新策略:
$$\theta_{k+1}=arg max_{\theta}E_{s,a \sim \pi_{\theta_k}}[L(s,a,\theta_k,\theta)]$$
就是最大化$L(s,a,\theta_k,\theta)$,
$$L(s,a,\theta_k,\theta)=min \left( \frac{\pi_{\theta}(a|s)}{\pi_{\theta_k}(a|s)}A^{\pi_{\theta_k}}(s,a) , clip \left( \frac{\pi_{\theta}(a|s)}{\pi_{\theta_k}(a|s)}, 1-\epsilon, 1+\epsilon \right)A^{\pi_{\theta_k}}(s,a) \right)$$
这个形式主要是为了让我们理解为啥叫PPO-Clip(我感觉直接用后面那个Clip项其实就够了,这个表达有点冗余),$\theta_k$ 就是当前Old的参数,$\theta$ 是New的参数。$\pi_{\theta}(a|s)$ 是New Actor输出的Policy上状态$s$时执行$a$的概率,$\pi_{\theta_k}(a|s)$ 表示的Old Actor输出的Policy上状态$s$时执行$a$的概率。$A^{\pi_{\theta_k}}(s,a)$是基于Old Critic得到的Advantage.
对这个公式进行改写,更容易看出它的真实目的,
$$L(s,a,\theta_k,\theta)=min \left( \frac{\pi_{\theta}(a|s)}{\pi_{\theta_k}(a|s)}A^{\pi_{\theta_k}}(s,a) , g \left( \epsilon, A^{\pi_{\theta_k}}(s,a) \ \right) \right)$$
其中,
$$g \left( \epsilon, A \right)=\left\{ \begin{aligned} &(1+\epsilon)A & A\ge 0 \\ &(1-\epsilon)A & A< 0 \end{aligned} \right.$$ 当Advantage>=0的时候, $$L(s,a,\theta_k,\theta)=min \left( \frac{\pi_{\theta}(a|s)}{\pi_{\theta_k}(a|s)}, (1+\epsilon) \right)A^{\pi_{\theta_k}}(s,a) $$ 这就清楚的说明,这时候应该增大$\pi_{\theta}(a|s)$,也就是认为这个action是好的,增加选择$a$的概率。但是通过$1+\epsilon$ 限制增大的幅度。 同理,当Advantage<0的时候 $$L(s,a,\theta_k,\theta)=min \left( \frac{\pi_{\theta}(a|s)}{\pi_{\theta_k}(a|s)}, (1-\epsilon) \right)A^{\pi_{\theta_k}}(s,a) $$ 缩小$\pi_{\theta}(a|s)$,但是幅度不能小于$1-\epsilon$ 另外,根据我的理解,$\pi_{\theta_k}(a|s)$应该截断梯度,也就是反向传到的时候用不着去更新Old Actor的参数。在OpenAI Spinningup的代码([https://github.com/openai/spinningup/blob/master/spinup/algos/pytorch/ppo/ppo.py](https://github.com/openai/spinningup/blob/master/spinup/algos/pytorch/ppo/ppo.py))确实是这样处理的,但是在Tianshou的代码里([https://github.com/thu-ml/tianshou/blob/master/tianshou/policy/ppo.py](https://github.com/thu-ml/tianshou/blob/master/tianshou/policy/ppo.py))没有做截断,结果也OK,想来对于$\pi_{\theta}(a|s)$来说,$\pi_{\theta_k}(a|s)$就是一个scalar factor, 这个factor是变量还是静态值,也许影响不那么大,而且本轮迭代结束后$\theta_k$也会被覆盖掉,反向传导更新了也白搭。 到这里,其实说的都是如何更新Actor。 怎么更新Critic的参数呢? $$L_c(s,a,r_{s,a},s')=|r_{s,a}+V^{\pi_{\theta_k}}_{s'}-V^{\pi_{\theta}}|$$ 唯一的不同是target value是用Old Critic计算的,这也是DRL领域的常规操作了. 小结一下,PPO-Clip就是通过Clip操作来防止步子迈太大的。作者实验证明Clip的效果比Penalty好。 ### 2.2.2 PPO-Penalty $$L^{KLPEN}(\theta)=\frac{\pi_{\theta}(a|s)}{\pi_{\theta_k}(a|s)}A^{\pi_{\theta_k}}(s,a) -\beta KLD\left( \pi_{\theta}(*|s), \pi_{\theta_k}(*|s) \right)$$ 理解上上面的,这个理解起来也就容易了,就是增加一个新旧Policy差异的惩罚项,差异通过KL divergence来衡量 (PS: 如理解有误支持,欢迎批评指正~)