强化学习Q-Learning/DQN
本文是一篇学习笔记,主要参考李宏毅老师的强化学习课程。
目前主流的强化学习方法大致可以分为 policy-based 和 value-based 两大类。之前我们介绍的 policy gradient 策略梯度,就是 policy-based 的方法。本文要介绍的 Q-learning 则是 value-based 方法。不同于 policy gradient 直接优化 policy,Q-learning 是要学习一个价值网络,来估计策略的状态价值或动作状态价值,价值网络并不直接决定接下来要采取的动作,我们需要基于价值网络估计出的价值函数,制定新的 policy,并不断优化 policy。
本文将介绍如何训练模型来拟合价值函数,以及 Q-learning 如何基于价值网络不断优化 policy,最后介绍实际实现中常用的几个技巧。
价值函数
价值函数是强化学习中的重要概念,也是 value-based 类方法的基础。价值函数有关于状态 s s s 和关于状态-动作对 ( s , a ) (s,a) (s,a) 两种。这里的价值是指从这个状态 s s s 或从这个状态-动作对 ( s , a ) (s,a) (s,a) 开始,一直按照某个特定的策略进行动作,直到结束,这期间的期望回报就称为价值函数。
状态价值函数, V π ( s ) V^\pi(s) Vπ(s),是指在状态 s s s,接下来按照特定的策略 π \pi π 来进行动作,最终累积回报的期望,表示为:
V π ( s ) = E τ ∼ π [ R ( τ ) ∣ s 0 = s ] V^\pi(s)=\mathbb{E}_{\tau\sim\pi}[R(\tau)|s_0=s] \notag \\ Vπ(s)=Eτ∼π[R(τ)∣s0=s]
其中 τ \tau τ 是在状态 s s s 之后,根据策略 π \pi π 进行动作得到的轨迹, R ( τ ) R(\tau) R(τ) 即该轨迹得到的累积奖励。
动作-状态价值函数, Q π ( s , a ) Q^\pi(s,a) Qπ(s,a),是指在状态 s s s,先强制采取动作 a a a(这个动作不一定符合当前策略 π \pi π,是人为采取的),接下来按照特定的策略 π \pi π 来进行动作,最终累积回报的期望。表示为:
Q π ( s , a ) = E τ ∼ π [ R ( τ ) ∣ s 0 = s , a 0 = a ] Q^\pi(s,a)=\mathbb{E}_{\tau\sim\pi}[R(\tau)|s_0=s,a_0=a] \notag \\ Qπ(s,a)=Eτ∼π[R(τ)∣s0=s,a0=a]
从两种价值函数的定义上不难发现,二者之间存在这样的关系:
V π ( s ) = E a ∼ π [ Q π ( s , a ) ] (1) V^\pi(s)=\mathbb{E}_{a\sim\pi}[Q^\pi(s,a)] \tag{1} \\ Vπ(s)=Ea∼π[Qπ(s,a)](1)
即状态价值函数 V V V 是动作-价值函数 Q Q Q 在策略 π \pi π 下的期望。也可以更直接一点,带进去,写成:
V π ( s ) = Q π ( s , π ( s ) ) (2) V^\pi(s)=Q^\pi(s,\pi(s)) \tag{2} \\ Vπ(s)=Qπ(s,π(s))(2)
还是要强调一句,两种价值函数描述的都是针对某个特定的策略 π \pi π 的价值,对于不同的策略 π \pi π,同一个状态 s s s 的价值很可能是不同的。比如棋魂里的阿光,在刚学棋时可能驾驭不了高难度的棋招 “大飞”,此时策略(actor)“阿光” 对于 “大飞” 这个状态的价值函数估计就比较低,当阿光棋力上涨,能够驾驭这一招时,价值函数估计就比较高。即对于不同的 π \pi π,同样状态 s s s 的价值是不同的,所以价值函数都是针对特定的策略 π \pi π 来说的。
V 以前的阿光 ( 大飞 ) = low V 现在更强的阿光 ( 大飞 ) = high \begin{aligned} V^\text{以前的阿光}(大飞)&=\text{low} \\ \quad V^\text{现在更强的阿光}(大飞)&=\text{high} \end{aligned} \notag \\ V以前的阿光(大飞)V现在更强的阿光(大飞)=low=high
从 Q-learning 的思路来进行强化学习建模,需要两个步骤,首先是我们要如何训练网络来拟合价值函数 V / Q V/Q V/Q,然后是有了一个可靠的价值函数后,如何决定动作。我们接下来分别介绍这两步。
训练价值网络估计价值函数
现在我们一般是用神经网络来估计价值函数(DQN)了,我们需要价值网络是一个回归模型,输入一个状态(和动作),输出一个标量分数。那么如何训练这个神经网络呢?有两种方法,MC(Monte Carlo,蒙特卡洛) 和 TD(Temporal Difference,时序差分)。
MC 方法
MC 来训练价值网络的想法非常直觉,比如我们要估计 V π ( s ) V^\pi(s) Vπ(s),它是策略 π \pi π 在状态 s s s 之后的期望回报,那我们就让 π \pi π 把整个 episode 跑出来,记录下最终的累积回报 R ( τ ) R(\tau) R(τ),让价值网络回归这个累积回报就行了。
V ^ π ( s ) → R ( τ ) \hat{V}^\pi(s)\rightarrow R(\tau) \notag \\ V^π(s)→R(τ)
理想情况下是能遍历所有可能的状态,得到最终累积回报来给价值网络学习,但这显然不可能,因此就采样 N N N 轮来近似。
TD 方法
MC 方法非常直觉,但问题在于每次都至少要等到一整轮 episode 跑完,才能更新一次模型。在很多实际的强化学习任务(如围棋、游戏等)中,跑完一整轮 episode 通常非常久,导致训练效率不高。而 TD 的方法,在每次动作,有了 { s t , a t , r t , s t + 1 } \{s_t, a_t, r_t, s_{t+1}\} {st,at,rt,st+1} 这一组数据之后,就能进行一次更新。
具体来说,我们先改写一下 V π ( s t ) V^\pi(s_t) Vπ(st):
V π ( s t ) = V π ( s t + 1 ) + r t V^\pi(s_t)=V^\pi(s_{t+1})+r_t \notag \\ Vπ(st)=Vπ(st+1)+rt
这个式子的意思很简单:状态 s t s_t st 的价值函数 V π ( s t ) V^\pi(s_t) Vπ(st) 等于当前立即得到的奖励 r t r_t rt 加上下一个状态的价值函数 V π ( s t + 1 ) V^\pi(s_{t+1}) Vπ(st+1)。
这样,在我们有了单步的一组数据之后,就可以将 s t , s t + 1 s_t,s_{t+1} st,st+1 分别送入价值网络,得到相邻两步的价值估计值 V π ( s t ) , V π ( s t + 1 ) V^\pi(s_t),V^\pi(s_{t+1}) Vπ(st),Vπ(st+1),我们的训练目标就是希望这两个估计值的差值尽可能接近当前步的真实奖励 r t r_t rt:
V ^ π ( s t ) − V ^ π ( s t + 1 ) → r t \hat{V}^\pi(s_t)-\hat{V}^\pi(s_{t+1})\rightarrow r_t \notag \\ V^π(st)−V^π(st+1)→rt
对比 MC 和 TD
我们对比一下 MC 和 TD 彼此的优缺点。
首先,MC 需要一整轮的数据才能进行一次更新,而 TD 只需一步的数据即可进行更新,训练效率更高。其次,MC 的目标值是 R ( τ ) R(\tau) R(τ),取决于其后所有步的随机采样结果,方差会比较大,而 TD 的目标是单步采样的数据 r t r_t rt,方差比较小。
但是,毕竟 TD 的目标中 V ^ π ( s t + 1 ) \hat{V}^\pi(s_{t+1}) V^π(st+1) 也是价值网络估计出来,因此 TD 不一定准。而 MC 的 R ( τ ) R(\tau) R(τ) 都是实打实跟环境交互跑出来的,是真实的目标。
总之 MC 和 TD 看起来还是各有优劣,但是现在一般都是采用 TD 方法来训练价值网络。
更好地policy
现在我们已经可以使用 MC 或 TD 方法来训练价值网络,估计价值函数,但是价值网络毕竟只是一个打分的模型,没法直接选择执行哪个动作,在实际中,有了价值函数之后,如何进行动作呢?也就是我们基于估计的价值函数,如何实现 policy 呢?以及我们如何能够在价值网络的引导下,不断地优化 policy 呢?
Q-learning 整体的过程如下图所示。最开始我们有一个初始的策略 π \pi π,我们让它与环境互动,得到一系列经验数据,然后在这些数据上采用 TD 或 MC 的方法,来学习出这个策略 π \pi π 对应的价值函数 Q π ( s , a ) Q^\pi(s,a) Qπ(s,a)。然后根据这个价值函数,得到一个 “更好的” 策略 π ′ \pi' π′,随后再用这个 π ′ \pi' π′ 作为策略,继续上述过程,循环往复,不断地优化策略。这里所谓的 “更好”,指的是对于所有的状态 s s s,都有 V π ′ ( s ) ≥ V π ( s ) V^{\pi'}(s)\ge V^\pi(s) Vπ′(s)≥Vπ(s)。
现在的问题就是,如何根据价值函数,得到一定比现在更好的策略 π ′ \pi' π′。实际上也非常简单,就是根据学习到的关于策略 π \pi π 的价值函数,取 argmax:
π ′ ( s ) = arg max a Q π ( s , a ) \pi'(s)=\arg\max_aQ^\pi(s,a) \notag \\ π′(s)=argamaxQπ(s,a)
直觉上很好理解,每一步我们都取能让 Q Q Q 值最大的动作,最终得到的累积回报,肯定比当前按策略 π \pi π 要高。以下我们来简单证明推导一下:取 π ′ ( s ) = arg max a Q π ( s , a ) \pi'(s)=\arg\max_aQ^\pi(s,a) π′(s)=argmaxaQπ(s,a),可以使得对所有的状态 s s s,都有 V π ′ ( s ) ≥ V π ( s ) V^{\pi'}(s)\ge V^\pi(s) Vπ′(s)≥Vπ(s)。
首先我们写出 V V V 和 Q Q Q 的关系(式 2),状态价值函数 V π ( s ) V^\pi(s) Vπ(s) 相当于状态-动作价值函数 Q π ( s , a ) Q^\pi(s,a) Qπ(s,a) 取 a = π ( s ) a=\pi(s) a=π(s),即在当前步也按照策略 π \pi π 来选取动作执行。注意我们此时已知 Q Q Q 函数了,所以我们是知道当前每个动作所得到的 Q Q Q 值的,因此我们可以直接贪婪地选取一个让 Q Q Q 值最大的动作 a a a,这样肯定是大于等于按照 π \pi π 选取动作的。而这种按照 arg max Q \arg\max Q argmaxQ 来选取动作的策略,正是我们刚刚定义的策略 π ′ \pi' π′ 。
V π ( s ) = Q π ( s , π ( s ) ) ≤ max a ( Q π ( s , a ) ) = Q π ( s , π ′ ( s ) ) V^\pi(s)=Q^\pi(s,\pi(s))\le\max_a(Q^\pi(s,a))=Q^\pi(s,\pi'(s)) \notag \\ Vπ(s)=Qπ(s,π(s))≤amax(Qπ(s,a))=Qπ(s,π′(s))
这是一步的差异。而如果我们每一步都采用 π ′ ( s ) = arg max a Q π ( s , a ) \pi'(s)=\arg\max_a Q^\pi(s,a) π′(s)=argmaxaQπ(s,a) ,自然是更加优于 π \pi π,这最终能推导出 V π ( s ) ≤ V π ′ ( s ) V^\pi(s)\le V^{\pi'}(s) Vπ(s)≤Vπ′(s):
V π ( s ) ≤ Q π ( s , π ′ ( s ) ) = E [ r t + V π ( s t + 1 ) ∣ s t = s , a t = π ′ ( s t ) ] ≤ E [ r t + Q π ( s t + 1 , π ′ ( s t + 1 ) ) ∣ s t = s , a t = π ′ ( s t ) ] = E [ r t + r t + 1 + V π ( s t + 2 ) ∣ . . . ] ≤ E [ r t + r t + 1 + Q π ( s t + 2 , π ′ ( s t + 2 ) ) ∣ . . . ] = . . . ≤ . . . ≤ V π ′ ( s ) \begin{aligned} V^\pi(s)&\le Q^\pi(s,\pi'(s)) \\ &=\mathbb{E}\left[r_t+V^\pi(s_{t+1})|s_t=s,a_t=\pi'(s_t)\right] \\&\le\mathbb{E}\left[r_t+Q^\pi(s_{t+1},\pi'(s_{t+1}))|s_t=s,a_t=\pi'(s_t)\right] \\ &=\mathbb{E}\left[r_t+r_{t+1}+V^\pi(s_{t+2})|...\right] \\ &\le\mathbb{E}\left[r_t+r_{t+1}+Q^\pi(s_{t+2},\pi'(s_{t+2}))|...\right] \\ &=...\le... \\ &\le V^{\pi'}(s) \end{aligned} \notag \\ Vπ(s)≤Qπ(s,π′(s))=E[rt+Vπ(st+1)∣st=s,at=π′(st)]≤E[rt+Qπ(st+1,π′(st+1))∣st=s,at=π′(st)]=E[rt+rt+1+Vπ(st+2)∣...]≤E[rt+rt+1+Qπ(st+2,π′(st+2))∣...]=...≤...≤Vπ′(s)
至此,我们就证明了只要选择 π ′ ( s ) = arg max a Q π ( s , a ) \pi'(s)=\arg\max_a Q^\pi(s,a) π′(s)=argmaxaQπ(s,a),就能保证对所有的状态 s s s 都有 V π ′ ( s ) ≥ V π ( s ) V^{\pi'}(s)\ge V^\pi(s) Vπ′(s)≥Vπ(s),也就是 π ′ \pi' π′ 是一个比 π \pi π 更好的策略。然后我根据上图中的过程,不断循环迭代,直至收敛得到一个最终的策略。
Q-learning 常用技巧
我们已经介绍了 Q-learning 的基本思想,接下来我们介绍几个实际中一般都会用到的 tricks。
Target Network
上面提到,现在我们在训练价值网络时,一般会用 TD 方法,其训练目标(以 Q 为例)为:
Q π ( s t , a t ) → r t + Q π ( s t + 1 , a t + 1 ) Q^\pi(s_t,a_t)\rightarrow r_t+Q^\pi(s_{t+1},a_{t+1}) \notag \\ Qπ(st,at)→rt+Qπ(st+1,at+1)
会发现等式两边都有共享的、一直在更新的网络 Q π Q^\pi Qπ,因此网络的拟合目标是在一直变的,虽然看起来上没什么问题,但实际中这种情况下训练通常会非常不稳定。Target Network 的技巧是我们将其中一个网络(一般是图中右下角那个)固定住,更新另一个网络,这样该网络的拟合目标一直是固定的,比较易于训练。在更新 N N N 轮之后,同步一次固定网络的参数。
Exploration
Exploitation v.s. Exploration 是强化学习中的一个重要课题,Exploitation 指利用已有的经验获取尽可能高的累积回报,Exploration 则是指训练时不只关注短期的回报,而是也以一定概率采样其他动作,尽量地探索是否有其他能赢得更高回报的可能选择。
在之前介绍的 Policy Gradient 类算法中,策略网络 π θ \pi_\theta πθ 输出的是一个动作空间的概率分布,每次执行的动作是从该分布中采样得到,即使是概率相对较低的动作也有机会被采样到,因此 Exploration 是已经被考虑在内的。
在刚刚介绍的 Q-learning 算法中,策略 π \pi π 本身不是一个分布,而是对学习到的 Q 函数在各个动作中 argmax 得到,是一种 greedy 的策略,这就导致同一状态下除了 Q 值最高的其他动作永远不会被尝试。假设初始化后,每个动作的默认 Q 值都是 0,那么最先被采样到且获得正 Q 值的动作就会一直被选中,而其它动作无法被探索到。因此,我们需要在 Q-learning 中引入一些机制来增强其 Exploration 能力。
Q-learning 中引入 Exploration 机制的思路其实很简单,只要在训练时,除了 argmax Q 之外,也给一定概率采样到其他动作即可。一般常用的有两种方法,一是 Epsilon Greedy,二是 Boltzmann Exploration。
Epsilon Greedy 是指选择动作时大部分时候( 1 − ϵ 1-\epsilon 1−ϵ)取 argmax Q 的动作,但是也有 ϵ \epsilon ϵ 的概率不管 Q,直接随机选取一个动作:
a = { arg max a Q ( s , a ) , with probability 1 − ϵ random , others a= \begin{cases} \arg\max_a Q(s,a),&\quad \text{with probability } 1-\epsilon \\ \text{random},&\quad\text{others} \end{cases} \notag \\ a={argmaxaQ(s,a),random,with probability 1−ϵothers
ϵ \epsilon ϵ 是一个超参数,其值一般随训练过程衰减,因为我们在训练前期更希望模型多多 Explore,后期则希望稳定的获取高的回报分数。
Boltzmann Exploration 是指我们直接将各动作的 Q 值归一化为一个分布,选取动作时从其中采样
P ( a ∣ s ) = exp ( Q ( s , a ) ) ∑ a exp ( s , a ) P(a|s)=\frac{\exp(Q(s,a))}{\sum_a\exp(s,a)} \notag \\ P(a∣s)=∑aexp(s,a)exp(Q(s,a))
这两种方法都能使得 Q 值较低的动作也有一定概率选中,进行 Exploration。
Replay Buffer
在强化学习中,样本效率也是一个很重要的问题,强化学习训练过程的很大一部分耗时都花在策略 π \pi π 与环境互动收集 rollout 数据上,真正的 GPU 计算梯度反传更新模型其实是很快的。在 Q-learning 中,每个数据只会使用一次,这样的样本效率就不高。为了提高样本的利用率,我们可以将策略 π \pi π 与环境互动的经验 ( s t , a t , r t , s t + 1 ) (s_t,a_t,r_t,s_{t+1}) (st,at,rt,st+1) 保存到一个 Replay Buffer 中,每次训练时从 Replay Buffer 中 sample 一个 batch 的数据用于训练模型。Replay Buffer 技巧一来可以提高样本的利用率,二来由于 Buffer 中所存的经验样本不一定来自于同一个 policy,因此也能提高每个 batch 内样本的多样性。
总结
本文我们先介绍了强化学习中的价值函数,然后介绍如何训练价值网络来拟合价值函数,以及 Q-learning/DQN 中如何不断地优化 policy,最后介绍了 Q-learning 在实际实现中常用的几个技巧。