1. 资料
PG 方法基础思路:https://pangruitao.com/post/5164
PG 最基础的 REINFORCE 方法实践:https://pangruitao.com/post/5258
2. AC 方法思路
AC (Actor-Critic) 方法是对基本的 REINFORCE 方法的改进。改进前,我们先找到 REINFORCE 的问题:稳定性和收敛能力比较差。
其背后原因被发现的至少有两个。
第一个原因, 即梯度高更新幅度问题,REINFORCE 在更新神经网络参数时采样的价值通常很不稳,忽大忽小。
- 而我们更理想的是每个样本均匀地对模型造成拉扯最终拉到理想的最大化目标函数的状态,而非经常性猛拉导致越过最佳点,模型反而变差。(回忆一下梯度下降/上升)
第二个原因,是采样偏差,由于模型的最后输出是对各行为价值估计取 softmax ,先天值(神经网络初始随机)越大则越容易被取到。但很可能一些实际很优势的行为因为先天不足而被难以采样。而被采样的行为的策略优势在进一步被采样后越来越大。虽然理论上只要采样次数足够多,任意行为都能被采样到,且真正优势的行为的价值估计会在被采样后很快赶超。但“足够次数”往往在现实中的体现则是收敛慢。
对于这两个原因/问题,发现有个方式可以显著减轻其影响
- 减去一个 baseline 就可以减少更新的方差。baseline 通常选择状态 s 的平均期望回报 \(V(s)\)
基于这个方法,修改策略网络的梯度公式修改为:
\(\theta \leftarrow \theta + \alpha \mathbb{E_{\pi_{\theta}}} \left[ \sum_{t=0}^{T} \nabla_{\theta} \log \pi_{\theta}(a|s_t) (\hat{A}(s, a) – V(s)) \right]\)
举一个例子说明为什么减一个 baseline 这样做能有效减少梯度更新时的幅度:
- 在很多实际场景下,蒙特卡洛的采样的价值变动的方差本身可能非常大。
- 比如对状态 s 下选择行为 a 的价值/优势(即 \(\hat{A}(s, a)\))的采样了 4 次,得到值是5, 20, 50, 10。
- REINFORCE 方法中,对应的梯度更新则是
- \(5 * \nabla_{\theta} \log \pi_{\theta}(a|s_t)\)
- \(20 * \nabla_{\theta} \log \pi_{\theta}(a|s_t)\)
- \(50 * \nabla_{\theta} \log \pi_{\theta}(a|s_t)\)
- \(10 * \nabla_{\theta} \log \pi_{\theta}(a|s_t)\)
- 幅度很大。而如果减去一个估计的均值,如 20,则更新变为
- \(-15 * \nabla_{\theta} \log \pi_{\theta}(a|s_t)\)
- \(0\)
- \(30 * \nabla_{\theta} \log \pi_{\theta}(a|s_t)\)
- \(-10 * \nabla_{\theta} \log \pi_{\theta}(a|s_t)\)
- 整体都更接近 0 ,即减小了更新值的波动幅度,而这种小绝对值的更新,间接地提高了梯度估计的稳定性。
现在问题来到如何建模这个 \(V(s)\) ?
可以又一次求助于我们的万能神经网络。AC就是这个思路,用第二个神经网络专门用于估计 \(V(s)\) 的值。在 AC 里被称作 Critic 。
如何更新 Critic 神经网络呢?
可以用时序差分 TD (Temporal Difference) 的方法(TD说明见:https://pangruitao.com/post/5164 蒙特卡洛部分)
\(V(s_t) \leftarrow V(s_t) + \alpha [r + \gamma V(s_{t+1}) – V(s_t)]\)
注:这个 \(r + \gamma V(s_{t+1}) – V(s_t) \) 也被叫作 TD-error
或者设置
\(\text{loss} = (r + \gamma V(s_{t+1}) – V(s_t))^2 \)
3. AC 方法实践
继续用 CartPole-v1 场景
3.1 CartPole-v1 场景说明
先继续和 Q Learning 一样,针对 gym 的 CartPole-v1 场景(控制小车平衡杆)进行学习:
3.2 实现和结果
个人实现基本的 AC 的 Jupyter Notebook 代码和运行结果见:https://github.com/Raytto/my_ml_study/blob/main/pg_study/s8_ac_batch.ipynb
- 策略神经网络仅 1 层隐藏层,且大小仅为 128*128
得到的训练分数情况如下
虽然也不稳定,但勉强确实好了一些。
尝试扩大网络,并通过积累一个 batch 的经验再统一更新追求稳定性,实现 Jupyter Notebook: https://github.com/Raytto/my_ml_study/blob/main/pg_study/s9_ac_batch_256.ipynb
- 每层神经元数量从 128 增加到 256
- 每 20 次游戏更新一次参数
看起来 batch 对稳定性还是有不少帮助。
3.3 优化结束奖励
进一步想到 CartPole-v1 在 500 步的情况下会大成功结束。但对于得分而言,他们两仅有1分之差。这个大成功结束和失败式的结束不应该如此接近。
参考金融中的折现方式。修改大成功结束的得分(用未来无限期得分进行折现),进而提高大成功的得分:
# 计算 TD 误差和优势,如果终止,则忽略下一状态
if not done:
td_target = reward + gamma * next_state_value
elif truncated:
td_target = reward + reward / (1-gamma) # 如果截断,则用无限奖励的折现代表下一状态的值
else:
td_target = reward # 如果死亡,则没有下一状态的值
再尝试一下学习情况,实现见:https://github.com/Raytto/my_ml_study/blob/main/pg_study/s10_ac_batch_256_rwrd_st.ipynb
好吧,并没有任何提升的感觉。
最后尝试进一步提升参数,每层神经元从 256 提升到 512 :https://github.com/Raytto/my_ml_study/blob/main/pg_study/s11_ac_batch_512_rwrd_st.ipynb
结果:
仅短暂触及 500 满分就坠落了。
可见 AC 的稳定性确实不算太好。需要进一步优化。
4. 进一步优化提升
AC 的稳定性或者收敛性依旧非常有限。所以后来有 PPO 基于 AC 通过“限制更新”,“多次小批量更新”等手段去提升算法表现。
PPO 的思路和实现见:https://pangruitao.com/post/5295