深度强化学习从入门到秃头--中篇
深度强化学习从入门到秃头–中篇
跟深度强化学习 (Deep Reinforcement Learning, DRL) 相爱相杀已经四年了,如果把本科毕业设计那半年也算上就有四年半了,放在科研这种“长途旅行”上也算是有一段时间了。DRL于我,更多的像是一种解决问题的工具,我在学习这个“工具”的过程中走过弯路踩过坑,也用这个“工具”解决了一些问题,回头望去,还是有些想法的,所以想记录一下,就算是科研回忆录吧。本系列文章计划包含三篇:《上篇》会试图用简洁的语言描述出DRL的轮廓;《中篇》会试图简明扼要地讲述一些重要的DRL算法;《下篇》会分享一些我在实践过程中的“个人经验”。与本系列文章相辅相成的是我站在巨人们 (Cart-Park, MrSyee, ElegantRL“小雅”等) 的肩膀上根据个人需求、习惯写的一个DRL项目,ZRayRL (https://github.com/ZhangRui111/ZRayRL)。最后,欢迎项目共建,欢迎文章讨论,转载请注明出处。
写在前面
施工中 (施工进度30%,更新日期 2022/9/13)
施工完成(更新日期 2023/6/10,hhh,我可太能拖了)
ZRayRL的代码结构
首先介绍下ZRayRL项目的代码结构,方便读者快速上手代码,也希望想参与项目维护/更新的朋友在发起pull request时遵照这一规范。整体思路上,出于实际应用的考虑,将算法的核心代码置于algorithms/
之下,而把网络结构置于networks/
之下,在此基础上,进一步把所有强化学习算法按照应用于连续动作空间 (continuous action space) 还是离散动作空间 (discrete action space) 进行分类组织,分别置于continuous/
和discrete/
之下,对于可以同时应用到两种动作空间的算法,例如PPO,则在两个目录下分别保持一份独立代码。除了algorithms/
和networks/
之外,根目录下比较重要的文件还有run.py
和zrayrl.yaml
,其中,run.py
是所有算法的运行入口,zrayrl.yaml
是环境配置清单 (搭配anaconda/miniconda使用)。整体代码结构如下所示。
1 |
|
强化学习算法的核心代码集成在命名为agent.py
的文件中,每个Agent类,例如A2CAgent
,PPOAgent
等继承自位于algorithms/base_agent.py
的抽象父类BaseAgent
。BaseAgent
抽象父类包含5个必须被子类实现的方法,分别是:
- 根据状态 (state) 选择动作 (action)
1 |
|
- 执行动作并返回环境的反馈,通常包含:新的状态,即时奖励和回合结束信号
1 |
|
- 更新value/policy模型
1 |
|
- 训练 (train) 智能体
1 |
|
- 测试 (test) 智能体
1 |
|
I. Q-Learning DQN DQNs
《上篇》有对Q-Learning的过程进行详细的介绍,建议先回顾一下。DQN[1]的全称叫做Deep Q Network,可以认为DQN是深度学习赋能Q-Learning后的版本。Q-Learning使用表格来记录每个“状态-动作”对 (state-action pair) 的Q值,Q值又称state-action value,是价值函数的一种,而state value常用V来表示。对于有 个状态,每个状态有 个动作的任务,Q表的大小为 ,不难看出,Q-Learning只能用于状态/动作空间较小的任务。为了将Q-Learning算法扩展到能够解决更为复杂的任务,借助深度神经网络的发展,DQN使用一个深度神经网络来取代了Q表,并承担预测Q值,或者说拟合Q函数的作用,从而帮助Q-Learning这一古老的算法在深度学习时代大放异彩。
在使用深度模型取代Q表后,研究人员发现RL智能体的训练变得极不稳定且容易崩溃/发散 (diverge)。经过分析后,人们认为有以下几个原因:
-
相互关联的数据:RL智能体跟环境交互产生的数据是典型的有强相互关联的时序数据,在时序上相邻生成的两个 (s, a, r, s’) 数据组 (transition) 极有可能具有相关性。这种相邻数据间的强相关性破坏了深度学习所要求的,数据样本的独立性。
-
动态变化的数据分布:DQN属于value-based RL算法,使用Q函数来对policy进行间接学习,policy决定了智能体的行为。而Q值的微小变化可能导致智能体的行为发生突变 (见图1),智能体行为的改变会改变训练数据的分布,而深度模型通常更适合拟合固定的潜在分布。
-
预测Q值跟目标Q (target Q) 值的关联性:预测Q值即,目标Q值表示为 ,导致变大的某次更新通常也会增大,因此也会增加目标Q值,进而导致policy学习过程的振荡/发散。

图1. Q值的微小变化可能导致智能体的行为发生突变。
为了解决上述问题,DQN提出了两项关键改进:经验重放 (experience replay) 跟单独的“固定的”目标Q网络 (fixed target Q network)。
-
Experience replay
Experience replay通过一个memory结构来储存过去的数据,并采用均匀随机采样 (uniform sampling) 的数据更新模型,这样可以打破数据的相关性,memory存储的数据其分布相对稳定,缓解相互关联的数据以及动态变化的数据分布这两个问题。
一个典型的借助numpy.ndarray存储数据的结构可以参照
algorithms/discrete/replay_buffer.py
,deque和list也能实现类似的功能,不过numpy.ndarray在速度上有一定的优势。 -
fixed target Q network
fixed target Q network通过为Q网络创建一个延后更新的副本,来解决预测Q值跟目标Q (target Q) 值的关联性问题。
在应用fixed target Q network之前,驱动模型更新的error term (又称TD-error) 如下所示,
1
2
3
4
5
6curr_q_value = self.dqn(state).gather(1, action)
next_q_value = self.dqn(next_state).max(dim=1, keepdim=True)[0].detach()
mask = 1 - done
target = (reward + self.gamma * next_q_value * mask).to(self.device)
# error term = target - curr_q_value应用fixed target Q network后,驱动模型更新的error term变为,
1
2
3
4
5
6curr_q_value = self.dqn(state).gather(1, action)
next_q_value = self.dqn_target(next_state).max(dim=1, keepdim=True)[0].detach()
mask = 1 - done
target = (reward + self.gamma * next_q_value * mask).to(self.device)
# error term = target - curr_q_value跟代表深度模型的参数,应用fixed target Q network后,每经过一段固定的间隔后会同步,即 ,这种方式又叫做硬更新 (hard update)。还有一些强化学习算法采用软更新 (soft update),两者的区别在代码里体现的比较明显。
1
2
3
4
5
6
7def _target_hard_update(self):
""" Apply hard update to the target model. """
self.dqn_target.load_state_dict(self.dqn.state_dict())
# if hard update is needed
if update_counter % self.target_update == 0:
self._target_hard_update()
以上便是对DQN算法的简要概述,以DQN为基础衍生出了规模庞大的DQN算法家族,仅比较知名的就有Double DQN[2],Dueling DQN[3],DQN with Prioritized Experience Replay[4], Noisy DQN[5]等,上述算法均在某些方面对DQN进行了程度不一的改进,后来出现的DQN算法家族的集大成者Rainbow[6]算法更是一次集成了多种改进。接下来,我将结合代码简明扼要地讲述一下这些算法的改进。
-
Double DQN
DQN在计算target Q值时使用了操作,即
这样做相当于使用相同的Q值来同时选择和评估动作,这使得DQN更有可能选择高估的值,从而导致过度乐观的Q值估计。为了更清楚的展示这一点,我们可以把公式(3)换一种等价的表达方式,即
Double DQN的思想是将计算的过程中的操作分解为动作选择和动作评价两个操作,动作选择和动作评价分别使用两套不同的权重和,从而减少高估误差。与公式(4)形式类似,在Double DQN中,
由于DQN中天然地具有两个Q网络,因此,动作选择和动作评价分别由这两个Q网络来做。与DQN相比,Double DQN仅在计算的代码部分有所不同,如下所示:
1
2
3
4# DQN
next_q_value = self.dqn_target(next_state).max(dim=1, keepdim=True)[0].detach()
mask = 1 - done
target = (reward + self.gamma * next_q_value * mask).to(self.device)1
2
3
4
5# Double DQN
selected_action = self.dqn(next_state).argmax(dim=1, keepdim=True)
next_q_value = self.dqn_target(next_state).gather(1, selected_action).detach()
mask = 1 - done
target = (reward + self.gamma * next_q_value * mask).to(self.device)可以看出,DQN中的计算只涉及到了target Q网络,即,用target Q同时选择和评估动作。而在Double DQN中,的计算涉及到了两个Q网络,两个Q网络分别承担选择动作和评估动作的任务。
-
Dueling DQN
图2. DQN (上图) 和Dueling DQN (下图) 的网络结构图。
Dueling DQN对Q值的估计更加细化、精确。上图展示了DQN和Dueling DQN的网络结构图,传统的DQN直接输出个动作的Q值,而Dueling DQN则应用了优势 (advantage) 的概念,使用两个网络通路 (stream) 分别输出跟状态 (state) 对应的value值,以及跟个动作对应的advantage值,注意,虽然Dueling DQN中一个stream也有个输出,但是这一stream并不是输出Q值而是advantage值。优势被定义为
优势代表了以状态为基准的各个动作的Q值。通过简单等式变换可以得到
对应的,在Dueling DQN的网络结构里,两个stream的输出相加的结果才是跟DQN对应的Q值。
总结一下,Dueling DQN的思想就是分别学习Value (用于预测state的好坏) 和Advantage (用于预测在该state下每个action的相对重要性),将它们加起来组成Q,而不是像传统DQN那样直接学出所有的Q值。这种做法有助于学习到,当某一个Q(s, a)值很高时,到底是这个是一个很好的状态,无论做什么动作得到很高的回报,还是说只是这个很好,在并不太好的下找到了出路。
在实际训练时,神经网络模型在上加一个常数,再在上减去同样的常数,输出保持不变,但是Dueling DQN分别学习状态跟动作回报的设计初衷就受到了破坏;极端情况下,神经网络模型把value stream的输出调整为,且让和相等,这样输出保持不变,Dueling DQN就退化为了DQN。为了避免这一情况的出现,需要减去所有动作的平均优势,这样,V就成了Q的均值,迫使神经网络模型必须学习准确的,进而保证了也是准确的,即
与DQN相比,Dueling DQN仅在搭建网络模型的代码部分有所不同,Dueling DQN采用了two-head network结构,读者可参照DQN network跟Dueling DQN network自行对比,出于篇幅的考虑不再贴到本文中。
针对Dueling DQN的推荐参考:
-
DQN with Prioritized Experience Replay (DQNwPER)
DQN的experience replay采用均匀/随机采样的数据更新模型参数,但是在memory储存的大量数据里面,不同的数据组 (s, a, r, s’) 的重要性必然是不同的。那么一个很自然的想法就是在采样的时候优先采样那些更“重要”的数据,从而提高训练效率。那么接下来的问题便是如何定义这个“重要性”。DQNwPER针对这一问题给出的答案是用TD-error来衡量一组 (s, a, r, s’) 的重要性,TD-error即上文的公式(2)。在理想情况下,若Q网络的预测完全正确,根据贝尔曼方程,TD-error应该等于零。所以,TD-error表明某个数据组在当前的Q网络来衡量下的偏差有多大,或者说惊讶程度有多大,也就说明这个数据组对于修正Q网络的预测更有价值,也就是更有优先采样的价值。
与DQN相比,DQNwPER使用带有优先级的
PrioritizedReplayBuffer
取代了DQN使用的ReplayBuffer
。同时,由于DQNwPER的采样比例与TD-error相对应,不能均匀随机地对过往经验/数据进行采样,这种做法引入了偏差 (bias),DQNwPER引入了重要性抽样(Importance-Sampling, IS)权重来纠正这种偏差。 -
Noisy DQN
Noisy DQN 是为了增强 DQN 的探索能力而设计的方法。Noisy DQN 在模型训练过程中,会在模型参数中添加随机噪声以增强输出的不确定性。
噪声网络的应用不局限于 DQN ,它可以用于几乎所有的强化学习方法。
最后,在实际应用中,考虑到算法的复杂度以及稳定性,比较推荐的是集成了Double DQN,Dueling DQN的Double Dueling DQN (D3QN)算法,D3QN在某种程度上也算是丐版的Rainbow算法了,关于算法选择的更详细的分析可参照《下篇》。
II. 发展脉络之 DQN

图3. 发展脉络之 DQN。
-
DQN DDPG
为了支持连续动作空间,DDPG 在 DQN 的基础上增加了一个策略网络,大概如下所示

图4. DQN 到 DDPG。
-
DDPG TD3
在 DDPG 的基础上,主要针对梯度计算进行了改进,致力于降低目标值计算时的 Bias 和 Variance。更具体的,分为三部分:
- 孪生Q网络:设置两个完全相同的Q网络和配套的两个目标Q网络,并对它们分别作独立更新,每次计算目标值时总是选择两个网络中较小的那个
- 降低来自 Bootstrap 方法中普遍存在的 Over-Estimation 导致的 Bias
- 目标策略平滑:在状态s’下的目标策略输出的基础上添加一个随机高斯噪声,并进行截断操作。
- 降低目标值计算的 Variance
- “近似动作应有近似值估计”思想的实践
- 不要把目标平滑噪声与探索噪声混为一谈
- 延迟更新:每更新d次孪生Q网络再更新一次策略网络。
- 孪生Q网络:设置两个完全相同的Q网络和配套的两个目标Q网络,并对它们分别作独立更新,每次计算目标值时总是选择两个网络中较小的那个
-
DDPG SAC
在 DDPG 的基础上,在常规强化学习目标 (即,最大化折扣累计奖励期望) 的基础上引入了最大熵 (Maximum Entropy) 目标;并采用随机策略取代了 DDPG 的确定性策略,从而可以同时支持离散和连续动作空间。
注意的是,SAC 通过引入最大熵学习目标极大的改善了探索效率;某些算法中 (比如 A3C) 中用于鼓励策略随机性的策略熵损失并未改变值函数的学习目标,这与 SAC 有明显区别。
III. 发展脉络之 REINFORCE

图5. 发展脉络之 REINFORCE。
-
REINFORCE A3C
- A3C 继承了经典的 REINFORCE 的策略梯度项 ,其中为蒙特卡洛采样得到的多个episode的折扣累计回报。A3C 添加了一个神经网络拟合的值函数 作为基准来降低 的方差,即,,其中, 又称“优势估计”。所以,A3C 的策略梯度项为。
- 为了避免策略过早陷入局部最优而退化为确定性策略,A3C 引入了策略熵损失来鼓励策略网络保持随机性,即,。
- A3C 采用了多环境并行采样的方案来提高在线采样效率并提高探索效率。
A3C 支持多种概率分布,包括针对One-Hot编码离散动作的类别分布(Categorical distribution)、针对二进制离散动作编码的伯努利分布(Bernoulli distribution),以及针对连续动作的多变量高斯分布,因此具有良好的通用性。
-
A3C A2C
A3C 在每个采样进程独立计算梯度,并在主线程聚合后用来更新值网络和策略网络的参数。A3C 默认采用异步梯度聚合的方式;A2C 是 A3C 的同步梯度聚合版本。
-
A2C PPO
PPO 在 A2C 的基础上对样本管理和梯度计算做出了改进:
- PPO 将同一批采样的样本分为 Mini-Batch 并重复利用多次,提高了样本利用率。
- PPO 继承了 TRPO “置信区间”的思想,在计算策略梯度时限制更新幅度,从而避免产生训练不稳定性。具体可参考图6:
图6. PPO 算法对新旧策略比值的截断操作。
是我们想 maximum 的目标,而 是新策略和旧策略的比值,其在每轮更新的初始值为1,图6把两者之间的关系表现为函数图像的形式,可以有进一步分析:
- 当 A > 0 时,maximum 的目标下,策略梯度算法将向着增加 的方向变化,而一旦 ,那么 关于 的梯度为0,这次反向传播不会对模型参数产生影响,如果小于 则正常更新。
- 当 A < 0 时,maximum 的目标下,策略梯度算法将向着减小 的方向变化,而一旦 ,那么 关于 的梯度为0,这次反向传播不会对模型参数产生影响,如果大于 则正常更新。
关于“置信区间”:
我们经常会遇到这种情况,智能体训练得好好的,突然效果就不行了。从另一个角度来说,也可以理解为策略的参数空间与策略空间不一致,导致策略的参数只变了一点但是策略的行为却改变了很多 (其实还是策略的参数变得太多了,只是从欧式距离上看参数变得不多)。因此,PPO 通过限制新旧策略的重要性采样比,来限制策略的改进,让算法训练得很稳。本质上来说, PPO 是一种保守策略梯度方法。 ---- 来自知乎 SHELCLin
IV. 小结

图7. 算法总结。
-
注释
①:DDPG 也可以被推广到离散动作空间,但最好还是“专业的算法做专业的事”
②:为了提高在线采样样本的使用效率,PPO 会把同一批样本重复利用多次,严格意义上说,PPO 只有样本第一次利用时才是On-Policy -
其他:
- 采样成本容忍度:off-policy 好于 on-policy
- 运算资源要求:on-policy 需要更多的 CPU 核,“并行采样”
- 训练稳定性:
- on-policy 好于 off-policy
- 随机策略好于确定性策略
- off-policy 容易受到多智能体强化学习任务中环境不稳定性的影响
参考 (Reference)
- [1] Mnih, Volodymyr, et al. “Human-level control through deep reinforcement learning.” nature 518.7540 (2015): 529-533.
- [2] Van Hasselt, Hado, Arthur Guez, and David Silver. “Deep reinforcement learning with double q-learning.” Proceedings of the AAAI conference on artificial intelligence. Vol. 30. No. 1. 2016.
- [3] Wang, Ziyu, et al. “Dueling network architectures for deep reinforcement learning.” International conference on machine learning. PMLR, 2016.
- [4] Schaul, Tom, et al. “Prioritized experience replay.” arXiv preprint arXiv:1511.05952 (2015).
- [5] Fortunato, Meire, et al. “Noisy networks for exploration.” arXiv preprint arXiv:1706.10295 (2017).
-
[6] Hessel, Matteo, et al. “Rainbow: Combining improvements in deep reinforcement learning.” Thirty-second AAAI conference on artificial intelligence. 2018.
-
[7] 魏宁. 《深度强化学习落地指南》.
-
[8] Lillicrap, Timothy P., et al. “Continuous control with deep reinforcement learning.” arXiv preprint arXiv:1509.02971 (2015).
-
[9] Fujimoto, Scott, Herke Hoof, and David Meger. “Addressing function approximation error in actor-critic methods.” International conference on machine learning. PMLR, 2018.
-
[10] Haarnoja, Tuomas, et al. “Soft actor-critic: Off-policy maximum entropy deep reinforcement learning with a stochastic actor.” International conference on machine learning. PMLR, 2018.
-
[11] Sutton, Richard S., et al. “Policy gradient methods for reinforcement learning with function approximation.” Advances in neural information processing systems 12 (1999).
-
[12] Mnih, Volodymyr, et al. “Asynchronous methods for deep reinforcement learning.” International conference on machine learning. PMLR, 2016.
-
[13] Schulman, John, et al. “Proximal policy optimization algorithms.” arXiv preprint arXiv:1707.06347 (2017).