学习资源站

13-第7章 深度Q网络进阶技巧

13-第7章 深度Q网络进阶技巧

第7章 深度Q网络进阶技巧

7.1 双深度Q网络

本章我们介绍训练深度Q网络的一些技巧。第一个技巧是双深度Q网络(double DQN,DDQN)。为什么要有DDQN呢?因为在实现上,Q 值往往是被高估的。如图 7.1 所示,这里有 4 个不同的小游戏,横轴代表迭代轮次,红色锯齿状的一直在变的线表示Q函数对不同的状态估计的平均 Q 值,有很多不同的状态,每个状态我们都进行采样,算出它们的 Q 值,然后进行平均。这条红色锯齿状的线在训练的过程中会改变,但它是不断上升的。因为Q函数是取决于策略的,在学习的过程中策略越来越强,我们得到的 Q 值会越来越大。在同一个状态, 我们得到奖励的期望会越来越大,所以一般而言,Q值都是上升的,但这是深度Q网络预估出来的值。接下来我们就用策略去玩游戏,玩很多次,比如100万次,然后计算在某一个状态下,我们得到的 Q 值是多少。我们会得到在某一个状态采取某一个动作的累积奖励是多少。预估出来的值远比真实值大,且大很多,在每一个游戏中都是这样。所以DDQN的方法可以让预估值与真实值比较接近。

图 7.1 被高估的Q值

图 7.1 中蓝色的锯齿状的线是 DDQN 的Q网络所估测出来的 Q 值,蓝色的无锯齿状的线是真正的 Q 值,它们是比较接近的。我们不用管用网络估测的值,它比较没有参考价值。我们用DDQN得出的真正的Q值在图 7.1 的3 种情况下都是比原来的深度Q网络高的,代表DDQN学习出来的策略比较强,所以实际上得到的奖励是比较大的。虽然一般的 深度Q网络 的 Q网络高估了自己会得到的奖励,但实际上它得到的奖励是比较低的。

Q: 为什么 Q 值总是被高估了?

A:因为实际在训练的时候,如式(7.1)所示,我们要让左式与右式(目标)越接近越好。但目标的值很容易被设得太高,因为在计算目标的时候,我们实际上在做的,是看哪一个 $a$ 可以得到最大的 Q 值,就把它加上去变成目标。

$$ Q(s_t, a_t) \longleftrightarrow r_t + \max_{a} Q(s_{t+1}, a) \tag{7.1} $$

例如,假设我们现在有 4 个动作,本来它们得到的Q值都是差不多的,它们得到的奖励也是差不多的。但是在估计的时候,网络是有误差的。如图 7.2(a)所示,假设是第一个动作被高估了,绿色代表是被高估的量,智能体就会选这个动作,就会选这个高估的 Q 值来加上 $r_t$ 来当作目标。如图 7.2(b)所示,如果第四个动作被高估了,智能体就会选第四个动作来加上 $r_t$ 当作目标。所以智能体总是会选那个 Q 值被高估的动作,总是会选奖励被高估的动作的Q值当作最大的结果去加上 $r_t$ 当作目标,所以目标值总是太大。

图 7.2 Q值被高估的问题

Q: 怎么解决目标值总是太大的问题呢?

A: 在DDQN里面,选动作的Q函数与计算值的Q函数不是同一个。在原来的 深度Q网络 里面,我们穷举所有的 $a$,把每一个 $a$ 都代入Q函数,看哪一个 $a$ 可以得到的 Q 值最高,就把那个 Q 值加上 $r_t$。但是在 DDQN 里面有两个 Q网络,第一个 Q网络 Q 决定哪一个动作的 Q 值最大(我们把所有的 $a$ 代入 Q 函数中,看看哪一个 $a$ 的Q 值最大)。我们决定动作以后,Q 值是用 $Q'$ 算出来的。

如式(7.2)所示,假设我们有两个Q函数————$Q$ 和 $Q'$,如果 $Q$ 高估了它选出来的动作 $a$,只要 $Q'$ 没有高估动作 $a$ 的值,算出来的就还是正常的值。假设 $Q'$ 高估了某一个动作的值,也是没问题的,因为只要 $Q$ 不选这个动作就可以,这就是DDQN神奇的地方。

$$ Q(s_t, a_t) \longleftrightarrow r_t + Q'\!\left(s_{t+1}, \arg\max_{a} Q(s_{t+1}, a)\right) \tag{7.2} $$

我们动手实现的时候,有两个Q网络:会更新的Q网络和目标Q网络。所以在DDQN里面,我们会用会更新参数的Q网络去选动作,用目标Q网络(固定住的网络)计算值。

DDQN相较于原来的深度Q网络的更改是最少的,它几乎没有增加任何的运算量,也不需要新的网络,因为原来就有两个网络。我们只需要做一件事:本来是用目标网络 $Q'$ 来找使 Q 值最大的 $a$,现在改成用另外一个会更新的Q网络来找使 Q 值最大的 $a$。如果只选一个技巧,我们一般都会选DDQN,因为其很容易实现。

7.2 竞争深度Q网络

第二个技巧是竞争深度Q网络(dueling DQN),相较于原来的 深度Q网络,它唯一的差别是改变了网络的架构。Q网络输入状态,输出的是每一个动作的 Q 值。如图 7.3 所示,原来的深度Q网络直接输出 Q 值,竞争深度Q网络不直接输出 Q 值,而是分成两条路径运算。第一条路径会输出一个标量 $V(\boldsymbol{s})$,因为它与输入 $\boldsymbol{s}$ 是有关系的,所以称为 $V(\boldsymbol{s})$。第二条路径会输出一个向量 $\boldsymbol{A}(\boldsymbol{s},\boldsymbol{a})$,它的每一个动作都有一个值。我们再把 $V(\boldsymbol{s})$ 和 $\boldsymbol{A}(\boldsymbol{s},\boldsymbol{a})$ 加起来就可以得到 Q 值 $\boldsymbol{Q}(\boldsymbol{s},\boldsymbol{a})$。

图 7.3 竞争深度Q网络的网络结构

我们假设状态是离散的(实际上状态不是离散的),为了说明方便,我们假设就只有 4 个不同的状态,只有 3 个不同的动作,所以 $\boldsymbol{Q}(\boldsymbol{s},\boldsymbol{a})$ 可以看成一个表格,如图 7.4 所示。

我们知道 $$ \boldsymbol{Q}(\boldsymbol{s},\boldsymbol{a}) = V(\boldsymbol{s}) + \boldsymbol{A}(\boldsymbol{s},\boldsymbol{a}) $$ 其中,$V(\boldsymbol{s})$ 对不同的状态,都有一个值。 $\boldsymbol{A}(\boldsymbol{s},\boldsymbol{a})$ 对不同的状态、不同的动作都有一个值。我们把 $V(\boldsymbol{s})$ 的每一列的值加到$\boldsymbol{A}(\boldsymbol{s},\boldsymbol{a})$的每一列就可以得到 Q 值,以第一列为例,有 2+1、2+($-$1)、2+0,可以得到 3、1、2,以此类推。

如图 7.4 所示,假设我们在训练网络的时候,目标是希望Q表格中第一行第二列的值变成 4,第二行第二列的值变成 0。但是我们实际上能修改的并不是 Q 值,能修改的是 $V(\boldsymbol{s})$ 与$\boldsymbol{A}(\boldsymbol{s},\boldsymbol{a})$的值。根据网络的参数,$V(\boldsymbol{s})$ 与$\boldsymbol{A}(\boldsymbol{s},\boldsymbol{a})$的值输出以后,就直接把它们加起来,所以其实不是修改 Q 值。在学习网络的时候,假设我们希望 Q 表格中的 3 增加 1 变成 4、$-$1 增加 1 变成 0。最后我们在训练网络的时候,我们可能就不用修改 $\boldsymbol{A}(\boldsymbol{s},\boldsymbol{a})$的值,就修改 $V(\boldsymbol{s})$ 的值,把 $V(\boldsymbol{s})$ 的值从 0 变成 1。从 0 变成 1 有什么好处呢?本来只想修改两个值,但 Q表格中的第三个值也被修改了:$-$2 变成了 $-$1。所以有可能我们在某一个状态下,只采样到这两个动作,没采样到第三个动作,但也可以更改第三个动作的 Q 值。这样的好处就是我们不需要把所有的状态-动作对都采样,可以用比较高效的方式去估计 Q 值。因为有时候我们更新的时候,不一定是更新Q表格,而是只更新了 $V(\boldsymbol{s})$,但更新 $V(\boldsymbol{s})$ 的时候,只要修改 $V(\boldsymbol{s})$的值,Q表格的值也会被修改。竞争深度Q网络是一个使用数据比较有效率的方法。

图 7.4 竞争深度Q网络训练

可能会有人认为使用竞争深度Q网络会有一个问题,竞争深度Q网络最后学习的结果可能是这样的:智能体就学到 $V(\boldsymbol{s})$ 等于 0, $\boldsymbol{A}(\boldsymbol{s},\boldsymbol{a})$ 等于 Q,使用任何竞争深度Q网络就没有任何好处,就和原来的深度Q网络一样。

为了避免这个问题出现,实际上我们要给 $\boldsymbol{A}(\boldsymbol{s},\boldsymbol{a})$ 一些约束,让 $\boldsymbol{A}(\boldsymbol{s},\boldsymbol{a})$的更新比较麻烦,让网络倾向于使用$V(\boldsymbol{s})$来解决问题。

例如,我们有不同的约束,一个最直觉的约束是必须要让 $\boldsymbol{A}(\boldsymbol{s},\boldsymbol{a})$ 的每一列的和都是 0,所以看我这边举的例子,列的和都是 0。如果这边列的和都是 0,我们就可以把$V(\boldsymbol{s})$的值想成是上面 Q 的每一列的平均值。这个平均值,加上$\boldsymbol{A}(\boldsymbol{s},\boldsymbol{a})$的值才会变成是 Q 的值。所以假设在更新参数的时候,要让整个列一起被更新,更新 $\boldsymbol{A}(\boldsymbol{s},\boldsymbol{a})$ 的某一列比较麻烦,所以我们就不会想要更新 $\boldsymbol{A}(\boldsymbol{s},\boldsymbol{a})$ 的某一列。因为 $\boldsymbol{A}(\boldsymbol{s},\boldsymbol{a})$ 的每一列的和都要是 0,所以我们无法让$\boldsymbol{A}(\boldsymbol{s},\boldsymbol{a})$的某列的值都加1,这是做不到的,因为它的约束就是和永远都是 0,所以不可以都加1,这时候就会强迫网络去更新$V(\boldsymbol{s})$的值,让我们可以用比较有效率的方法去使用数据。

实现时,我们要给这个$\boldsymbol{A}(\boldsymbol{s},\boldsymbol{a})$一个约束。例如,如图 7.5 所示,假设有 3 个动作,输出的向量是 $[7,3,2]^{\mathrm{T}}$,我们在把 $\boldsymbol{A}(\boldsymbol{s},\boldsymbol{a})$与 $V(\boldsymbol{s})$ 加起来之前,先进行零均值化。

零均值化的过程如下:

(1)计算均值(7+3+2)/3=4;

(2)向量$[7,3,2]^{\mathrm{T}}$的每个元素的值都减去均值4,于是零均值化的向量为 $[3,-1,2]^{\mathrm{T}}$。

接着我们将向量$[3,-1,2]^{\mathrm{T}}$中的每个元素的值加上 1,就可以得到最后的 Q 值。这个零均值化的步骤就是网络的其中一部分,在训练的时候,我们也使用反向传播,只是零均值化是没有参数的,它只是一个操作,可以把它放到网络里面,与网络的其他部分共同训练,这样$\boldsymbol{A}(\boldsymbol{s},\boldsymbol{a})$就会有比较大的约束,网络就会给它一些好处,让它倾向于去更新$V(\boldsymbol{s})$的值,这就是竞争深度Q网络。

图 7.5 竞争深度Q网络约束

7.3 优先级经验回放

第三个技巧称为优先级经验回放(prioritized experience replay,PER)。如图 7.6 所示,我们原来在采样数据训练 Q 网络的时候,会均匀地从回放缓冲区里面采样数据。这样不一定是最好的, 因为也许有一些数据比较重要。假设有一些数据,我们之前采样过,发现这些数据的时序差分误差特别大(时序差分误差就是网络的输出与目标之间的差距),这代表我们在训练网络的时候,这些数据是比较不好训练的。既然比较不好训练,就应该给它们比较大的概率被采样到,即给它优先权(priority)。这样在训练的时候才会多考虑那些不好训练的数据。实际上在做 PER 的时候,我们不仅会更改采样的过程,还会因为更改了采样的过程,而更改更新参数的方法。所以PER不仅改变了采样数据的分布,还改变了训练过程。

图 7.6 优先级经验回放

7.4 在蒙特卡洛方法和时序差分方法中取得平衡

蒙特卡洛方法与时序差分方法各有优劣,因此我们可以在蒙特卡洛方法和时序差分方法中取得平衡,这个方法也被称为多步方法。我们的做法如图 7.7 所示,在时序差分方法里面,在某一个状态 $s_t$ 采取某一个动作$a_t$ 得到奖励 $r_t$,接下来进入状态 $s_{t+1}$。但是我们可以不只保存一个步骤的数据,可保存 $N$ 个步骤的数据。

我们记录在 $s_t$ 采取 $a_t$,得到 $r_t$时,会进入的 $s_{t+1}$。一直记录到第 $N$ 个步骤以后,在 $s_{t+N}$采取 $a_{t+N}$,得到 $r_{t+N}$,进入 $s_{t+N+1}$ 的这些经验,把它们保存下来。实际上在做更新的时候,在做Q网络学习的时候,我们要让 $Q(s_t,a_t)$ 与目标值越接近越好。$\hat{Q}$ 所计算的不是 $s_{t+1}$的,而是 $s_{t+N+1}$的奖励。我们会把 $N$ 个步骤以后的状态$s_{t+N+1}$ 输入到 $\hat{Q}$ 中去计算 $N$ 个步骤以后会得到的奖励。如果要算目标值,要再加上多步(multi-step)的奖励 $\sum_{t^{\prime}=t}^{t+N} r_{t^{\prime}}$ ,多步的奖励是从时间 $t$ 一直到 $t+N$ 的 $N+1$ 个奖励的和。我们希望 $Q(s_t,a_t)$ 和目标值越接近越好。

多步方法就是蒙特卡洛方法与时序差分方法的结合,因此它不仅有蒙特卡洛方法的好处与坏处,还有时序差分方法的好处与坏处。我们先看看多步方法的好处,之前只采样了某一个步骤,所以得到的数据是真实的,接下来都是 Q 值估测出来的。现在采样比较多的步骤,采样 $N$ 个步骤才估测值,所以估测的部分所造成的影响就会比较小。当然多步方法的坏处就与蒙特卡洛方法的坏处一样,因为 $r$ 有比较多项,所以我们把 $N$ 项的 $r$ 加起来,方差就会比较大。但是我们可以调整 $N$ 的值,在方差与不精确的 Q 值之间取得一个平衡。$N$ 就是一个超参数,我们可以对其进行调整。

图 7.7 在蒙特卡洛方法和时序差分方法中取得平衡

7.5 噪声网络

我们还可以改进探索。$\varepsilon$-贪心这样的探索就是在动作的空间上加噪声,但是有一个更好的方法称为噪声网络(noisy net),它是在参数的空间上加噪声。噪声网络是指,每一次在一个回合开始的时候,在智能体要与环境交互的时候,智能体使用Q函数来采取动作,Q函数里面就是一个网络,我们在网络的每一个参数上加上一个高斯噪声(Gaussian noise),就把原来的Q函数变成 $\tilde{Q}$ 。因为我们已经用 $\hat{Q}$ 来表示目标网络,所以我们用 $\tilde{Q}$ 来表示噪声Q函数(noisy Q-function)。我们把每一个参数都加上一个高斯噪声,就得到一个新的网络 $\tilde{Q}$。使用噪声网络执行的动作为 $$ a=\underset{a}{\arg \max} \tilde{Q}(s, a) $$

这里要注意,在每个回合开始的时候,与环境交互之前,我们就采样噪声。接下来我们用固定的噪声网络玩游戏,直到游戏结束,才重新采样新的噪声,噪声在一个回合中是不能被改变的。OpenAI 与 DeepMind 在同时间提出了几乎一模一样的噪声网络方法,并且对应的两篇论文都发表在 ICLR 2018 会议中。不一样的地方是,他们用不同的方法加噪声。OpenAI 的方法比较简单,直接加一个高斯噪声,也就是把每一个参数、每一个权重(weight)都加一个高斯噪声。DeepMind 的方法比较复杂,该方法中的噪声是由一组参数控制的,网络可以自己决定噪声要加多大。但是两种方法的概念都是一样的,总之,我们就是对Q函数里面的网络加上一些噪声,把它变得有点儿不一样,即与原来的Q函数不一样,然后与环境交互。两篇论文里面都强调,参数虽然会被加上噪声,但在同一个回合里面参数是固定的。我们在换回合、玩另一场新的游戏的时候,才会重新采样噪声。在同一场游戏里面就是同一个噪声Q网络在玩该场游戏,这非常重要。因为这导致了噪声网络与原来的$\varepsilon$-贪心或其他在动作上做采样的方法的本质上的差异。

有什么本质上的差异呢?在原来采样的方法中,比如$\varepsilon$-贪心中,就算给定同样的状态,智能体采取的动作也不一定是一样的。因为智能体通过采样来决定动作,给定同一个状态,智能体根据Q函数的网络来输出一个动作,或者采样到随机来输出一个动作。所以给定相同的状态,如果是用$\varepsilon$-贪心的方法,智能体可能会执行不同的动作。但实际上策略并不是这样的,一个真实世界的策略,给定同样的状态,它应该有同样的回应。而不是给定同样的状态,它有时候执行Q函数,有时候又是随机的,这是一个不正常的动作,是在真实的情况下不会出现的动作。但是如果我们是在Q函数的网络的参数上加噪声, 就不会出现这种情况。因为如果在Q函数的网络的参数上加噪声,在整个交互的过程中,在同一个回合里面,它的网络的参数总是固定的,所以看到相同或类似的状态,就会采取相同的动作,这是比较正常的。这被称为依赖状态的探索(state-dependent exploration),我们虽然会做探索这件事,但是探索是与状态有关系的,看到同样的状态,就会采取同样的探索的方式,而噪声(noisy)的动作只是随机乱试。但如果我们是在参数下加噪声,在同一个回合里面,参数是固定的,我们就是系统地尝试。比如,我们每次在某一个状态,都向左试试看。在下一次在玩同样游戏的时候,看到同样的状态,我再向右试试看,是系统地在探索环境。

7.6 分布式Q函数

还有一个技巧称为分布式Q函数(distributional Q-function)。分布式Q函数是比较合理的,但是它难以实现。Q函数是累积奖励的期望值,所以我们算出来的 Q 值其实是一个期望值。如图 7.8 所示,因为环境是有随机性的,所以在某一个状态采取某一个动作的时候,我们把所有的奖励在游戏结束的时候进行统计,得到的是一个分布。也许在奖励得到 0 的概率很高,得到 $-$10 的概率比较低,得到 +10 的概率也比较低,但是它是一个分布。(我们对这个分布计算它的平均值才是这个 Q 值,算出来是累积奖励的期望。所以累积奖励是一个分布,对它取期望,对它取平均值,得到 Q 值)。但不同的分布可以有同样的平均值。也许真正的分布是图 7.8 所示右边的分布,它的平均值与左边的分布的平均值其实是一样的,但它们背后所代表的分布其实是不一样的。假设我们只用 Q 值的期望来代表整个奖励,可能会丢失一些信息,无法对奖励的分布进行建模。

图 7.8 奖励分布

分布式Q函数是对分布(distribution)建模,怎么做呢?如图 7.12a 所示,在原来的Q函数里面,假设我们只能采取 $a_1$、$a_2$、$a_3$ 这3 个动作,我们输入一个状态,输出 3 个值。这3 个值分别代表 3 个动作的 Q 值,但是这些 Q 值是一个分布的期望值。所以分布式Q函数就是直接输出分布。实际上的做法如图 7.12b 所示,假设分布的值就分布在某一个范围里面,比如 $-$10 ~ 10,把 $-$10 ~ 10 拆成一个一个的长条。例如,每一个动作的奖励空间拆成 5 个长条。假设奖励空间可以拆成 5 个长条,Q函数的输出就是要预测我们在某一个状态采取某一个动作得到的奖励,其落在某一个长条里面的概率。所以绿色长条概率的和应该是 1,其高度代表在某一个状态采取某一个动作的时候,它落在某一个长条内的概率。绿色的代表动作 $a_1$,红色的代表动作 $a_2$,蓝色的代表动作 $a_3$。所以我们就可以用Q函数去估计 $a_1$ 的分布、$a_2$ 的分布、$a_3$ 的分布。实际上在做测试的时候,我们选平均值最大的动作执行。

图 7.12 分布式Q函数

除了选平均值最大的动作以外,我们还可以对分布建模。例如,我们可以考虑动作的分布,如果分布方差很大,这代表采取这个动作虽然平均而言很不错,但也许风险很高,我们可以训练一个网络来规避风险。在两个动作平均值都差不多的情况下,也许可以选一个风险比较小的动作来执行,这就是分布式Q函数的好处。

7.7 彩虹

最后一个技巧称为彩虹(rainbow),如图 7.10 所示,假设每个方法有一种自己的颜色(如果每一个单一颜色的线代表只用某一个方法),把所有的颜色组合起来,就变成“彩虹”,我们把原来的深度Q网络也算作一种方法,故有 7 种颜色。横轴代表训练过程的帧数,纵轴代表玩十几个雅达利小游戏的平均分数的和,但它取的是分数的中位数。为什么是取中位数而不是直接取平均呢?因为不同小游戏的分数差距很大,如果取平均,某几个游戏可能会控制结果,因此我们取中位数。如果我们使用的是一般的深度Q网络(灰色的线),深度Q网络的性能不是很好。噪声深度Q网络(noisy DQN)比DQN的性能好很多。紫色的线代表 DDQN,DDQN 还挺有效的。优先级经验回放的双深度Q网络(prioritized DDQN)、竞争双深度Q网络(dueling DDQN)和分布式深度Q网络(distributional DQN)性能也挺高的。异步优势演员-评论员(asynchronous advantage actor-critic,A3C)是演员-评论员的方法,A3C算法又被译作异步优势动作评价算法,我们会在第九章详细介绍异步优势演员-评论员算法。单纯的异步优势演员-评论员算法看起来是比深度Q网络强的。图 7.10 中没有多步方法,这是因为异步优势演员-评论员算法本身内部就有多步方法,所以实现异步优势演员-评论员算法就等同于实现多步方法,我们可以把 异步优势演员-评论员算法的结果看成多步方法的结果。这些方法本身之间是没有冲突的,我们把全部方法都用上就变成了七彩的方法,即彩虹方法,彩虹方法的性能很好。

图 7.10 彩虹方法

我们把所有的方法加在一起,模型的表现会提高很多,但会不会有些方法其实是没有用的呢?我们可以去掉其中一种方法来判断这个方法是否有用。如图 7.11 所示,虚线就是彩虹方法去掉某一种方法以后的结果,黄色的虚线去掉多步方法后“掉”很多。彩虹是彩色的实线,去掉多步方法会“掉下来”,去掉优先级经验回放后会“掉下来”,去掉分布也会“掉下来”。 这边有一个有趣的地方,在开始的时候,分布训练的方法与其他方法速度差不多。但是我们去掉分布训练方法的时候,训练不会变慢,但是性能(performance)最后会收敛在比较差的地方。我们去掉噪声网络后性能也差一点儿,去掉竞争深度 Q 网络后性能也差一点儿,去掉双深度 Q 网络却没什么差别。所以我们把全部方法组合在一起的时候,去掉双深度 Q 网络是影响比较小的。当我们使用分布式深度Q网络的时候,本质上就不会高估奖励。我们是为了避免高估奖励才加了DDQN。如果我们使用了分布式深度Q网络,就可能不会有高估的结果,多数的情况是低估奖励的,所以变成DDQN没有用。

为什么分布式深度Q网络不会高估奖励奖励,反而会低估奖励呢?因为分布式深度Q网络输出的是一个分布的范围,输出的范围不可能是无限的,我们一定会设一个限制, 比如最大输出范围就是从 $-$10 ~ 10。假设得到的奖励超过 10,比如 100 怎么办?我们就当作没看到这件事,所以奖励很极端的值、很大的值是会被丢弃的,用分布式深度Q网络的时候,我们不会高估奖励,反而会低估奖励。

图 7.11 彩虹:去掉其中一种方法