文章目录
- 前言
- 一、计算优势函数
- 二、比较buffer的存储
- 三、小批量更新网络的实现中
- 四、细节GAE的实现
- 五、对于PPO必须收敛的关键为V_target的定义
- 六、参数敏感
- 七、仿照《动手学强化学习中的代码》实现
- 八、补充tricks的效果
前言
主要是对PPO论文里的PPO复刻,和实现时遇到的障碍记录。
代码实现在:https://github.com/wild-firefox/FreeRL/tree/main/PPO_file
原论文:https://arxiv.org/pdf/1707.06347
trick参考:影响PPO算法性能的10个关键技巧(附PPO算法简洁Pytorch实现)
(其中有3个trick原论文中有提及,所以直接集成在原代码里了。)
下述环境均为环境为 Pendulum-v1
一、计算优势函数
PPO论文中提到
计算优势函数 有两种方法1.generalized advantage estimation 2.finite-horizon estimators
第2种实现方法在(github上)许多代码上的实现方法不一,有buffer中存入return和value值的方法,也有在buffer里不存,而在在更新时计算的方法。
这里我们选择第1种,在buffer中不会存在上述争议。
论文中:
二、比较buffer的存储
通常ppo的buffer中存储的是obs, action, reward, next_obs, done, log_pi ;
但也有见到不存储log_pi的方法。
比较1.不存储log_pi,而是在更新时计算出log_pi_old, 2.存储log_pi,将此作为log_pi_old 发现2更好 采用2。
两者代码区别,是否在learn中加入以下算法。
'''
## 计算log_pi_old 比较1
mean , std = self.agent.actor(obs)
dist = Normal(mean, std)
log_pi_old = dist.log_prob(action).sum(dim = 1 ,keepdim = True)
action_log_pi = log_pi_old.detach()
'''
PPO_11 为方法2 PPO_13 为方法1 两者大体一致,但方法二更稳定点。
三、小批量更新网络的实现中
1.使用小批量更新较快, --> for i in range(K) 小批量随机 for j in range(horizon // minibatch): 更新小批量i (直至到整个horizon结束)
2.使用整块整块更新较慢。 但效果稍好 --> for i in range(K) for j in range(horizon // minibatch): 整块更新
论文采用的是方法1。
橙色方法1 粉紫色方法2 (实现在PPO_no_minibatch.py)
四、细节GAE的实现
GAE的done为一个时长为T的回合的结束
而TD_error的done以是否能获得下次奖励为结束。
红色为不区分GAE的done。
不区分时,下图两者都是done.。
效果如下
五、对于PPO必须收敛的关键为V_target的定义
v_target = adv + v(t) #正确
v_target = reward + gamma * (1.0 - done) * v(t+1) #错误 一点不收敛
紫色为上面错误的写法。
六、参数敏感
对horizon 即临时buffer容量参数比较敏感
蓝色:horizon=2048 绿色 horizon=256 其余参数一样 minibatch 均为64
七、仿照《动手学强化学习中的代码》实现
PPO_d.py 利用https://hrl.boyuai.com/chapter/2/trpo%E7%AE%97%E6%B3%95 的compute_advantage的代码
和 不存储log_pi,而是在更新时计算出log_pi_old 的方法
效果如下:(PPO_d.py 为灰色,略逊于PPO.py)
八、补充tricks的效果
在\MountainCarContinuous-v0的环境下
将所有的trick都试了一遍,效果如下:发现ObsNorm 这个trick对于收敛至关重要。
parser.add_argument("--trick", type=dict, default={'adv_norm':False,
'ObsNorm':False,'Batch_ObsNorm':False, # or 两者择1
'reward_norm':False, 'reward_scaling':False, # or
'lr_decay':False,'orthogonal_init':False,'adam_eps':False,'tanh':False})
其他,可以看出adv_norm的效果也还是比较明显的。
至于Batch_ObsNorm与ObsNorm的区别,在于ObsNorm是每一步更新,而Batch_ObsNorm是每次buffer满后更新一次,所以效果不如ObsNorm。