实用深度强化学习实现技术
强化学习(RL)是一种通过智能体与环境交互来学习最优决策的机器学习范式。而深度强化学习(DRL)则将深度学习技术引入RL领域,利用深度神经网络强大的函数拟合能力来处理高维观察空间,取得了显著的成功。本章我们将重点介绍一种经典的DRL算法:Q-Learning及其变体,探讨其背后的原理、实现技巧以及代表性应用。
1. 价值函数与Q-Learning原理
在介绍Q-Learning之前,我们先来回顾一下强化学习中的一些基本概念。
1.1 马尔可夫决策过程
马尔可夫决策过程(MDP)提供了一个标准的RL问题数学框架。一个MDP由一个五元组 M = ⟨ S , A , P , R , γ ⟩ \mathcal{M}=\langle\mathcal{S},\mathcal{A},\mathcal{P},\mathcal{R},\gamma\rangle M=⟨S,A,P,R,γ⟩ 定义,其中:
- 状态空间 S \mathcal{S} S:表示智能体所处的环境状态集合。
- 动作空间 A \mathcal{A} A:表示智能体可采取的动作集合。
- 转移概率 P \mathcal{P} P: P ( s ′ ∣ s , a ) \mathcal{P}(s'|s,a) P(s′∣s,a) 表示在状态 s s s 下执行动作 a a a 后转移到状态 s ′ s' s′ 的概率。
- 奖励函数 R \mathcal{R} R: R ( s , a ) \mathcal{R}(s,a) R(s,a) 表示在状态 s s s 下执行动作 a a a 后获得的即时奖励。
- 折扣因子 γ ∈ [ 0 , 1 ] \gamma\in[0,1] γ∈[0,1]:表示未来奖励的折现程度,用于平衡即时奖励和长期奖励。
MDP的目标是寻找一个最优策略 π ∗ : S → A \pi^*:\mathcal{S}\rightarrow\mathcal{A} π∗:S→A,使得智能体遵循该策略能获得最大的累积奖励:
π ∗ = arg max π E π [ ∑ t = 0 ∞ γ t r t ] \pi^* = \arg\max_{\pi} \mathbb{E}_{\pi}[\sum_{t=0}^{\infty} \gamma^t r_t] π∗=argπmaxEπ[t=0∑∞γtrt]
其中 r t r_t rt 表示在时刻 t t t 获得的奖励。
1.2 价值函数
为了评估一个状态或者一个状态-动作对的好坏,我们引入了价值函数的概念。在给定策略 π \pi π 的情况下,有两种常见的价值函数定义:
-
状态价值函数 V π ( s ) V^{\pi}(s) Vπ(s): 表示从状态 s s s 开始,遵循策略 π \pi π 能获得的期望累积奖励。
V π ( s ) = E π [ ∑ k = 0 ∞ γ k r t + k ∣ s t = s ] V^{\pi}(s)=\mathbb{E}_{\pi}[\sum_{k=0}^{\infty}\gamma^k r_{t+k}|s_t=s] Vπ(s)=Eπ[k=0∑∞γkrt+k∣st=s] -
动作价值函数 Q π ( s , a ) Q^{\pi}(s,a) Qπ(s,a): 表示在状态 s s s 下采取动作 a a a,并继续遵循策略 π \pi π 能获得的期望累积奖励。
Q π ( s , a ) = E π [ ∑ k = 0 ∞ γ k r t + k ∣ s t = s , a t = a ] Q^{\pi}(s,a)=\mathbb{E}_{\pi}[\sum_{k=0}^{\infty}\gamma^k r_{t+k}|s_t=s,a_t=a] Qπ(s,a)=Eπ[k=0∑∞γkrt+k∣st=s,at=a]
两个价值函数之间满足如下关系:
V π ( s ) = ∑ a ∈ A π ( a ∣ s ) Q π ( s , a ) V^{\pi}(s) = \sum_{a\in\mathcal{A}} \pi(a|s) Q^{\pi}(s,a) Vπ(s)=a∈A∑π(a∣s)Qπ(s,a)
对于最优策略 π ∗ \pi^* π∗,对应的状态价值函数 V ∗ V^* V∗ 和动作价值函数 Q ∗ Q^* Q∗ 满足贝尔曼最优方程:
V ∗ ( s ) = max a ∈ A Q ∗ ( s , a ) Q ∗ ( s , a ) = R ( s , a ) + γ ∑ s ′ ∈ S P ( s ′ ∣ s , a ) V ∗ ( s ′ ) = R ( s , a ) + γ ∑ s ′ ∈ S P ( s ′ ∣ s , a ) max a ′ ∈ A Q ∗ ( s ′ , a ′ ) \begin{aligned} V^*(s) &= \max_{a\in\mathcal{A}} Q^*(s,a) \\ Q^*(s,a) &= \mathcal{R}(s,a) + \gamma \sum_{s'\in\mathcal{S}} \mathcal{P}(s'|s,a) V^*(s')\\ &= \mathcal{R}(s,a) + \gamma \sum_{s'\in\mathcal{S}} \mathcal{P}(s'|s,a) \max_{a'\in\mathcal{A}} Q^*(s',a') \end{aligned} V∗(s)Q∗(s,a)=a∈AmaxQ∗(s,a)=R(s,a)+γs′∈S∑P(s′∣s,a)V∗(s′)=R(s,a)+γs′∈S∑P(s′∣s,a)a′∈AmaxQ∗(s′,a′)
这两个方程揭示了最优价值函数的递归结构,为我们后续推导Q-Learning算法奠定了理论基础。如果我们能准确估计出 Q ∗ ( s , a ) Q^*(s,a) Q∗(s,a),那么最优策略可以通过贪心法(greedy)直接得到:
π ∗ ( s ) = arg max a ∈ A Q ∗ ( s , a ) \pi^*(s) = \arg\max_{a\in\mathcal{A}} Q^*(s,a) π∗(s)=arga∈AmaxQ∗(s,a)
1.3 Q-Learning算法
Q-Learning是一种经典的值迭代(value iteration)算法,它直接估计最优动作价值函数 Q ∗ ( s , a ) Q^*(s,a) Q∗(s,a),然后根据 Q ∗ Q^* Q∗ 得到最优策略。
传统的Q-Learning使用一个表格(Q-table)来存储每个状态-动作对的Q值估计。初始时 Q ( s , a ) Q(s,a) Q(s,a) 可以被随机初始化。在与环境交互的每个时间步,智能体根据当前的Q值贪心地选择动作 a t a_t at,观察到奖励 r t r_t rt 和下一个状态 s t + 1 s_{t+1} st+1,然后通过如下的时序差分(TD)更新来优化Q值:
Q ( s t , a t ) ← Q ( s t , a t ) + α [ r t + γ max a Q ( s t + 1 , a ) − Q ( s t , a t ) ] Q(s_t,a_t) \leftarrow Q(s_t,a_t) + \alpha[r_t + \gamma \max_{a} Q(s_{t+1},a) - Q(s_t,a_t)] Q(st,at)←Q(st,at)+α[rt+γamaxQ(st+1,a)−Q(st,at)]
其中 α ∈ ( 0 , 1 ] \alpha\in(0,1] α∈(0,1] 为学习率。这个更新过程可以被解释为:用 r t + γ max a Q ( s t + 1 , a ) r_t + \gamma \max_{a} Q(s_{t+1},a) rt+γmaxaQ(st+1,a) 作为 Q ( s t , a t ) Q(s_t,a_t) Q(st,at) 的目标值进行监督学习,使得 Q ( s t , a t ) Q(s_t,a_t) Q(st,at) 逼近它的真实值 Q ∗ ( s t , a t ) Q^*(s_t,a_t) Q∗(st,at)。可以证明,在适当的条件下,Q-Learning最终会收敛到 Q ∗ Q^* Q∗。
然而,当状态空间和动作空间很大时,用表格存储Q值是不现实的。这时,我们可以用一个参数化的函数 Q θ ( s , a ) Q_{\theta}(s,a) Qθ(s,a) 来近似Q值,其中 θ \theta θ 为函数的参数。近年来,随着深度学习的兴起,使用深度神经网络(DNN)作为Q函数的近似器得到了广泛应用,这就是著名的DQN算法。
2. 深度Q网络(DQN)
深度Q网络(DQN)由[Mnih et al., 2013]提出,是将Q-Learning与深度学习结合的代表性算法。它使用一个卷积神经网络(CNN)来拟合Q函数,并引入了两个重要的技巧:经验回放(experience replay)和目标网络(target network),极大地提升了训练的稳定性。
2.1 Q网络结构
在DQN中,Q函数 Q θ ( s , a ) Q_{\theta}(s,a) Qθ(s,a) 被参数化为一个CNN,它以状态 s s s (通常是原始像素)为输入,输出各个动作 a a a 对应的Q值。Q网络可以表示为:
Q θ ( s , ⋅ ) = f θ ( s ) ∈ R ∣ A ∣ Q_{\theta}(s,\cdot) = f_{\theta}(s) \in \mathbb{R}^{|\mathcal{A}|} Qθ(s,⋅)=fθ(s)∈R∣A∣
其中 f θ f_{\theta} fθ 为CNN的前向传播函数。在实践中,Q网络的具体结构需要根据任务的特点来设计。
Q网络的训练目标是最小化TD误差,即让 Q θ ( s , a ) Q_{\theta}(s,a) Qθ(s,a) 尽可能逼近贝尔曼最优方程的右侧:
L ( θ ) = E ( s , a , r , s ′ ) ∼ D [ ( r + γ max a ′ Q θ − ( s ′ , a ′ ) − Q θ ( s , a ) ) 2 ] \mathcal{L}(\theta) = \mathbb{E}_{(s,a,r,s')\sim \mathcal{D}} \left[ (r + \gamma \max_{a'} Q_{\theta^-}(s',a') - Q_{\theta}(s,a))^2 \right] L(θ)=E(s,a,r,s′)∼D[(r+γa′maxQθ−(s′,a′)−Qθ(s,a))2]
其中 D \mathcal{D} D 为经验回放池, θ − \theta^- θ− 为目标网络的参数。接下来我们详细介绍这两个技巧。
2.2 经验回放(Experience Replay)
在Q-Learning中,我们通常假设训练数据 ( s , a , r , s ′ ) (s,a,r,s') (s,a,r,s′) 是独立同分布的。但在实际的在线学习中,智能体往往是连续与环境交互的,产生一个状态序列 { s 1 , s 2 , ⋯ , s t , ⋯ } \{s_1,s_2,\cdots,s_t,\cdots\} {s1,s2,⋯,st,⋯},相邻的状态之间具有很强的相关性。如果直接用序列数据训练神经网络,会导致训练过程不稳定,难以收敛。
经验回放(ER)通过构建一个 经验回放池 D \mathcal{D} D 来打破数据之间的相关性。具体来说,在每个时间步 t t t,智能体将当前的转移样本 ( s t , a t , r t , s t + 1 ) (s_t,a_t,r_t,s_{t+1}) (st,at,rt,st+1) 存入 D \mathcal{D} D。训练时,从 D \mathcal{D} D 中随机采样一个批量(batch)的样本来更新Q网络参数。这个过程类似于监督学习,可以使用标准的优化算法如SGD、Adam等。
ER的优势包括:
- 打破了样本之间的相关性,使训练更稳定。
- 样本可以被多次使用,提高数据利用效率。
- 可以使用off-policy数据,如专家示范数据、历史版本策略的数据等。
在实现中,经验回放池 D \mathcal{D} D 通常被实现为一个固定大小的循环队列。当 D \mathcal{D} D 被填满时,新的样本会覆盖最老的样本。一个常见的技巧是在初始探索阶段,先不更新策略,只往 D \mathcal{D} D 中填充数据,直到收集到足够的样本才开始训练。
2.3 目标网络(Target Network)
Q-Learning本质上是一个回归问题,即用 r + γ max a ′ Q θ ( s ′ , a ′ ) r + \gamma \max_{a'} Q_{\theta}(s',a') r+γmaxa′Qθ(s′,a′) 作为Q值 Q θ ( s , a ) Q_{\theta}(s,a) Qθ(s,a) 的目标值。然而在DQN中,目标值本身也在随着参数 θ \theta θ 的更新而变化,这就引入了一个不稳定的"移动目标"问题。
为了缓解这个问题,DQN引入了一个目标网络 Q θ − Q_{\theta^-} Qθ−,它的结构与Q网络相同,但参数更新频率较低。在计算TD目标值时使用目标网络的输出:
y = r + γ max a ′ Q θ − ( s ′ , a ′ ) y = r + \gamma \max_{a'} Q_{\theta^-}(s',a') y=r+γa′maxQθ−(s′,a′)
这使得目标值在一段时间内保持不变,类似于监督学习。目标网络的参数 θ − \theta^- θ− 每隔一定的时间步(如1000步)从Q网络复制一次:
θ − ← θ \theta^- \leftarrow \theta θ−←θ
或者采用软更新的方式:
θ − ← τ θ + ( 1 − τ ) θ − , τ ≪ 1 \theta^- \leftarrow \tau \theta + (1-\tau) \theta^-, \quad \tau \ll 1 θ−←τθ+(1−τ)θ−,τ≪1
目标网络的引入极大地提升了DQN的性能,成为了许多DRL算法的标准配置。
2.4 DQN算法
结合经验回放和目标网络,DQN的完整算法描述如下:
算法 DQN
初始化Q网络参数 θ \theta θ, 目标网络参数 θ − ← θ \theta^- \leftarrow \theta θ−←θ
初始化经验回放池 D \mathcal{D} D
for episode = 1 to M do:
初始化初始状态 s 1 s_1 s1
for t = 1 to T do:
根据 ϵ -greedy \epsilon\text{-greedy} ϵ-greedy 策略选择动作 a t = { arg max a Q θ ( s t , a ) , with prob. 1 − ϵ random action , with prob. ϵ a_t=\begin{cases} \arg\max_a Q_{\theta}(s_t,a), & \text{with prob. } 1-\epsilon \\ \text{random action}, & \text{with prob. } \epsilon \end{cases} at={argmaxaQθ(st,a),random action,with prob. 1−ϵwith prob. ϵ
执行动作 a t a_t at,观察奖励 r t r_t rt 和下一状态 s t + 1 s_{t+1} st+1
将样本 ( s t , a t , r t , s t + 1 ) (s_t,a_t,r_t,s_{t+1}) (st,at,rt,st+1) 存入 D \mathcal{D} D
从 D \mathcal{D} D 中随机采样一个批量的样本 ( s , a , r , s ′ ) (s,a,r,s') (s,a,r,s′)
计算TD目标: y = { r , if s ′ is terminal r + γ max a ′ Q θ − ( s ′ , a ′ ) , otherwise y=\begin{cases} r, & \text{if } s' \text{ is terminal}\\ r + \gamma \max_{a'} Q_{\theta^-}(s',a'), & \text{otherwise} \end{cases} y={r,r+γmaxa′Qθ−(s′,a′),if s′ is terminalotherwise
最小化TD误差: L ( θ ) = 1 N ∑ ( y − Q θ ( s , a ) ) 2 \mathcal{L}(\theta)=\frac{1}{N}\sum(y-Q_{\theta}(s,a))^2 L(θ)=N1∑(y−Qθ(s,a))2
每隔C步更新目标网络: θ − ← θ \theta^- \leftarrow \theta θ−←θ
end for
end for
其中,超参数包括:
- M M M: 训练的episode数
- T T T: 每个episode的最大时间步
- ϵ \epsilon ϵ: ϵ -greedy \epsilon\text{-greedy} ϵ-greedy 探索中选择随机动作的概率
- γ \gamma γ: 奖励折扣因子
- C C C: 目标网络更新频率
- N N N: 批量大小(batch size)
DQN在Atari游戏上取得了超越人类的表现,证明了端到端深度强化学习的有效性,掀起了DRL研究的高潮。此后,各种DQN的改进变体不断涌现,极大地推动了DRL技术的发展。
3. DQN变体与改进
尽管DQN取得了巨大成功,但它仍然存在一些问题,如过估计(overestimation)、采样效率低、探索不足等。研究者针对这些问题提出了许多改进方法。本节我们介绍几个代表性的DQN变体算法。
3.1 Double DQN
Q-Learning算法(包括DQN)存在一个固有的过估计问题。在计算TD目标值时,我们用 max a ′ Q ( s ′ , a ′ ) \max_{a'} Q(s',a') maxa′Q(s′,a′) 来估计 max a ′ q ∗ ( s ′ , a ′ ) \max_{a'} q_*(s',a') maxa′q∗(s′,a′) ( q ∗ q_* q∗ 表示真实的Q值)。然而,由于对 Q ( s ′ , a ′ ) Q(s',a') Q(s′,a′) 估计的误差,这个最大化操作会导致Q值的过估计,使得学到的策略次优。
Double DQN(DDQN) [Van Hasselt et al., 2016]通过解耦动作选择和动作评估来缓解过估计问题。具体来说,DDQN维护两个Q网络 Q θ 1 Q_{\theta_1} Qθ1 和 Q θ 2 Q_{\theta_2} Qθ2,它们独立学习、互为目标网络。在计算TD目标时,一个网络负责选择动作,另一个网络负责评估动作的值:
a ∗ = arg max a ′ Q θ 1 ( s ′ , a ′ ) y = r + γ Q θ 2 ( s ′ , a ∗ ) \begin{aligned} a^* &= \arg\max_{a'} Q_{\theta_1}(s',a') \\ y &= r + \gamma Q_{\theta_2}(s',a^*) \end{aligned} a∗y=arga′maxQθ1(s′,a′)=r+γQθ2(s′,a∗)
这里动作选择和评估使用了不同的Q网络,从而减少了过估计。DDQN在Atari游戏上的表现优于DQN,成为了DRL领域的标配技术之一。
3.2 Prioritized Experience Replay
传统的经验回放对回放池 D \mathcal{D} D 中的样本一视同仁,uniformly随机采样。然而直觉上,有些样本可能比其他样本更"重要",值得被多次采样学习。
Prioritized Experience Replay(PER)[Schaul et al., 2016]基于这个想法,根据样本的TD误差来衡量其重要性,对 D \mathcal{D} D 中的样本赋予不同的采样概率。给定一个样本 ( s , a , r , s ′ ) (s,a,r,s') (s,a,r,s′), 它的优先级定义为:
p = ∣ δ ∣ + ϵ p = |\delta| + \epsilon p=∣δ∣+ϵ
其中 δ = r + γ max a ′ Q θ − ( s ′ , a ′ ) − Q θ ( s , a ) \delta=r+\gamma \max_{a'}Q_{\theta^-}(s',a')-Q_{\theta}(s,a) δ=r+γmaxa′Qθ−(s′,a′)−Qθ(s,a) 为TD误差, ϵ \epsilon ϵ 是一个小的正常数,以确保每个样本都有被采样的可能。采样概率正比于优先级:
P ( i ) = p i α ∑ j p j α P(i) = \frac{p_i^{\alpha}}{\sum_j p_j^{\alpha}} P(i)=∑jpjαpiα
其中 α \alpha α 控制了优先级的强度。当 α = 0 \alpha=0 α=0 时退化为uniform采样。
为了校正优先级采样引入的偏差,PER在更新Q网络时对不同样本引入了重要性权重(importance weight):
w i = ( 1 N ⋅ 1 P ( i ) ) β w_i = (\frac{1}{N} \cdot \frac{1}{P(i)})^{\beta} wi=(N1⋅P(i)1)β
最小化加权的TD误差:
L ( θ ) = 1 N ∑ w i ⋅ ( y i − Q θ ( s i , a i ) ) 2 \mathcal{L}(\theta) = \frac{1}{N}\sum w_i \cdot (y_i-Q_{\theta}(s_i,a_i))^2 L(θ)=N1∑wi⋅(yi−Qθ(si,ai))2
其中 β \beta β 控制了重要性权重的强度,从初始值 β 0 \beta_0 β0 线性增加到1。
PER能更有效地利用样本,在许多游戏上取得了sota的表现。此外它是一个通用的技术,可以和其他DRL算法结合,如DDQN、Dueling DQN等。
3.3 Dueling DQN
在许多RL任务中,状态价值 V ( s ) V(s) V(s) 和状态-动作价值 Q ( s , a ) Q(s,a) Q(s,a) 都包含有用的信息。标准的DQN只学习 Q ( s , a ) Q(s,a) Q(s,a),忽略了 V ( s ) V(s) V(s)。
Dueling DQN[Wang et al., 2016]通过将Q网络分解为两个流来显式地学习 V ( s ) V(s) V(s) 和 A ( s , a ) A(s,a) A(s,a):
Q ( s , a ) = V ( s ) + A ( s , a ) Q(s,a) = V(s) + A(s,a) Q(s,a)=V(s)+A(s,a)
其中 V ( s ) V(s) V(s) 表示状态价值, A ( s , a ) = Q ( s , a ) − V ( s ) A(s,a)=Q(s,a)-V(s) A(s,a)=Q(s,a)−V(s) 称为优势函数(advantage function),表示动作 a a a 相对于状态平均的优劣程度。网络包含一个共享的卷积层,然后分为两个独立的全连接层,分别输出 V ( s ) V(s) V(s) 和 A ( s , a ) A(s,a) A(s,a),最后组合成Q值。
实践中为了保持恒等性,使用如下的组合方式:
Q ( s , a ) = V ( s ) + ( A ( s , a ) − max a ′ A ( s , a ′ ) ) Q(s,a) = V(s) + (A(s,a)-\max_{a'} A(s,a')) Q(s,a)=V(s)+(A(s,a)−a′maxA(s,a′))
Dueling DQN在Atari游戏中有超过一半的任务上取得了优于DQN的表现。它也可以跟其他技术结合,如DDQN、PER等。
3.4 其他改进
除了上述经典变体,DQN还有许多其他的改进方法,如:
- Multi-step learning: 使用多步回报 ∑ k = 0 n γ k r t + k + γ n max a ′ Q θ − ( s t + n , a ′ ) \sum_{k=0}^{n} \gamma^k r_{t+k} + \gamma^n \max_{a'}Q_{\theta^-}(s_{t+n},a') ∑k=0nγkrt+k+γnmaxa′Qθ−(st+n,a′) 作为目标值,权衡bias和variance。
- Distributional DQN: 学习值分布 Z ( s , a ) Z(s,a) Z(s,a) 而不是期望 Q ( s , a ) Q(s,a) Q(s,a),捕捉环境的随机性。
- Noisy DQN: 在Q网络中加入参数噪声,自适应地调节探索。
- Rainbow: 将DDQN、PER、Dueling、Multi-step、Distributional、Noisy结合在统一的框架下。
这些改进极大地提升了Q学习的性能,推动了DRL在游戏、机器人、自然语言等领域的应用。但Q学习仍然有其局限性,如采样效率不高、难以适用于连续动作空间等。为此,研究者探索了策略梯度、actor-critic等其他类型的DRL算法。
4. 从Atari到AlphaGo
DQN在Atari游戏中的成功彰显了深度强化学习的威力,掀起了将DRL应用于各种领域的浪潮。本节我们简要回顾几个里程碑式的工作。
4.1 Atari Games
Atari 2600是一款经典的游戏机,包含数十个游戏。[Bellemare et al., 2013]基于这些游戏构建了Arcade Learning Environment(ALE),将其作为RL算法的测试平台。玩家通过原始像素感知游戏状态,用操纵杆控制角色的行动。
DQN首次在ALE上实现了超越人类的表现[Mnih et al., 2015],之后各种DQN改进算法纷纷在ALE上刷新纪录[Hessel et al., 2018]。Atari游戏具有复杂的状态空间和稀疏的奖励信号,对探索提出了挑战,因此成为了DRL算法的标准测试任务。
4.2 AlphaGo
围棋是一种古老的棋类游戏,状态空间和动作空间极其庞大,被认为是人工智能的巅峰挑战。DeepMind提出的AlphaGo系列算法[Silver et al., 2016, 2017, 2018]将深度学习与蒙特卡洛树搜索相结合,在围棋上成功击败了人类顶尖高手。
AlphaGo使用深度卷积网络来逼近策略网络 p σ ( a ∣ s ) p_{\sigma}(a|s) pσ(a∣s) 和价值网络 v θ ( s ) v_{\theta}(s) vθ(s),并用它们指导树搜索,不断完善估值和走法选择。策略网络通过监督学习人类专家棋谱和自对弈数据来训练,价值网络则类似DQN用TD learning来训练。
AlphaGo证明了DRL可以在超大规模问题上取得成功,揭示了深度学习、强化学习、树搜索、领域知识相结合的威力,被誉为人工智能发展的里程碑。
小结
我们深入探讨了DRL中的Q学习方法,重点介绍了DQN及其变体。Q学习通过值迭代来逼近最优Q函数,再用Q函数生成最优策略,是一种简洁有效的RL算法。DQN利用深度卷积网络来泛化Q学习,并引入了经验回放、目标网络等技术来提高训练稳定性。此后,各种改进技术如Double DQN、Prioritized replay、Dueling network等进一步提升了DQN的性能。
5. Q-Learning在连续控制中的应用
之前我们主要讨论了Q-Learning在离散动作空间中的应用,如Atari游戏。但在实际应用如机器人控制中,动作空间往往是连续的。为了将Q-Learning扩展到连续动作空间,一个简单的方法是将动作空间离散化。然而这会带来维度灾难,限制了策略的表达能力。
QT-Opt[Kalashnikov et al., 2018]提出一种适用于连续动作空间的Q-Learning变体,并将其应用于机器人抓取任务。QT-Opt用一个Q网络 Q θ ( s , a ) Q_{\theta}(s,a) Qθ(s,a) 来拟合Q值,其中状态 s s s 为机器人摄像头拍摄的RGB图像,动作 a a a 为抓取位置在相机坐标系下的平移和转动参数。
5.1 任务建模
QT-Opt将机器人抓取建模为一个MDP:
- 状态 s s s: 俯视RGB相机图像,不包括深度信息
- 动作 a a a: 末端执行器的4-DoF变换(3个平移自由度+1个转动自由度)
- 奖励 r r r: 如果目标物体被成功抓起,给予+1的稀疏奖励,否则为0。成功的判定通过检测物体是否离开桌面来自动完成。
- 折扣因子 γ \gamma γ: 令为1,只关注最终的抓取效果
在这个MDP中,状态与真实环境存在偏差(没有深度信息),奖励函数也是稀疏的。这对Q-Learning算法提出了挑战。
5.2 连续动作空间优化
QT-Opt使用Cross-Entropy Method(CEM)来处理连续动作空间上的优化问题。CEM是一种基于采样的黑箱优化算法:
- 从一个高斯分布 N ( μ , Σ ) \mathcal{N}(\mu,\Sigma) N(μ,Σ) 中采样N个动作 { a i } i = 1 N \{a_i\}_{i=1}^N {ai}i=1N
- 用Q网络评估这些动作的Q值 { Q θ ( s , a i ) } i = 1 N \{Q_{\theta}(s,a_i)\}_{i=1}^N {Qθ(s,ai)}i=1N
- 选出Q值最高的前K个动作,用它们更新高斯分布的均值和协方差
- 重复步骤1-3直到高斯分布收敛
CEM通过迭代优化一个采样分布来逼近最优动作:
a ∗ ( s ) = arg max a Q θ ( s , a ) ≈ arg max a N ( μ ∗ , Σ ∗ ∣ μ ∗ , Σ ∗ ) a^*(s) = \arg\max_a Q_{\theta}(s,a) \approx \arg\max_a \mathcal{N}(\mu^*,\Sigma^*|\mu^*,\Sigma^*) a∗(s)=argamaxQθ(s,a)≈argamaxN(μ∗,Σ∗∣μ∗,Σ∗)
相比随机采样,CEM能更有效地集中在高Q值区域进行采样。QT-Opt将CEM嵌入到Q-Learning的训练循环中,交替地更新Q网络和最优化动作分布。
5.3 海量离线数据
QT-Opt充分利用了仿真和真实环境中收集的海量离线数据。这些数据来自之前的各种抓取策略,质量参差不齐。QT-Opt将所有数据汇总到一个巨大的离线经验池中,供Q网络训练。
离线数据的优势在于:
- 提高样本利用效率,减少真实环境交互
- 汇聚多种策略探索出的数据,增加样本多样性
- 在仿真环境中预训练,减少真实环境的磨合成本
QT-Opt表明,即使在没有在线探索的情况下,也可以从大规模离线数据中学到一个鲁棒的抓取策略。
5.4 实验结果
QT-Opt在7个Kuka机械臂上进行了抓取实验,总计收集了58万次抓取样本,其中48万次来自仿真,10万次来自真实机器人。所有样本被汇总到一个离线经验池中用于训练。
测试时,QT-Opt在48个陌生物体上的平均成功率达到96%,超过了人类专家的表现。大规模的仿真和真实数据结合DRL算法,使得端到端地从视觉信息中学习抓取控制策略成为可能。QT-Opt为将DRL应用于真实世界机器人控制任务提供了新的思路。
小结
QT-Opt展示了Q-Learning在连续动作空间上的扩展及其在机器人抓取任务上的应用。为了处理连续动作空间,QT-Opt引入了交叉熵方法(CEM)来优化动作分布。此外,QT-Opt还利用了大规模的仿真和真实数据,通过离线学习来提高样本效率。
基于视觉的机器人学习控制是一个极具挑战性的任务,需要克服视觉和控制的双重难题。QT-Opt的成功得益于:
- 用深度神经网络将高维视觉信息映射到紧凑的状态表征
- 通过离线学习充分利用多源异构的历史数据
- 有效的连续动作空间优化算法如CEM
- 大规模分布式训练系统和高效的数据管理
展望未来,视觉驱动的机器人学习控制仍有许多亟待解决的问题:
- 提高泛化能力,使策略能适应动态变化的环境
- 实现长期规划能力,求解多阶段复杂任务
- 确保安全性,避免意外碰撞和损坏
- 引入语言指令,实现人机交互和协作
- 提高策略的可解释性,便于调试和优化
6. 代码实战
本章我们通过几个具体的代码示例,演示如何用Python和PyTorch实现DQN及其变体算法。
6.1 DQN
我们首先实现一个基本的DQN算法,并将其应用于CartPole游戏环境。
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
import gym# Q网络
class QNetwork(nn.Module):def __init__(self, state_dim, action_dim):super(QNetwork, self).__init__()self.fc1 = nn.Linear(state_dim, 64)self.fc2 = nn.Linear(64, 64)self.fc3 = nn.Linear(64, action_dim)def forward(self, x):x = torch.relu(self.fc1(x))x = torch.relu(self.fc2(x))x = self.fc3(x)return x# DQN智能体
class DQNAgent:def __init__(self, state_dim, action_dim):self.state_dim = state_dimself.action_dim = action_dimself.gamma = 0.95self.epsilon = 1.0self.epsilon_decay = 0.995self.epsilon_min = 0.01self.batch_size = 64self.train_start = 1000self.memory = []self.model = QNetwork(state_dim, action_dim)self.target_model = QNetwork(state_dim, action_dim)self.optimizer = optim.Adam(self.model.parameters())self.loss_fn = nn.MSELoss()def get_action(self, state):if np.random.rand() <= self.epsilon:return np.random.randint(self.action_dim)else:state = torch.tensor(state, dtype=torch.float32)q_value = self.model(state)action = q_value.argmax().item()return actiondef train_model(self):if len(self.memory) < self.train_start:returnbatch_data = np.random.choice(self.memory, self.batch_size, replace=False)states = torch.tensor(np.array([data[0] for data in batch_data]), dtype=torch.float32)actions = torch.tensor(np.array([data[1] for data in batch_data]), dtype=torch.int64)rewards = [data[2] for data in batch_data]next_states = torch.tensor(np.array([data[3] for data in batch_data]), dtype=torch.float32)dones = [data[4] for data in batch_data]# 计算当前状态的Q值q_values = self.model(states).gather(1, actions.unsqueeze(1)).squeeze(1)# 计算下一状态的最大Q值max_next_q_values = self.target_model(next_states).max(1)[0]max_next_q_values[dones] = 0.0max_next_q_values = max_next_q_values.detach()# 计算目标Q值target_q_values = rewards + self.gamma * max_next_q_valuestarget_q_values = target_q_values.to(torch.float32)# 更新Q网络loss = self.loss_fn(q_values, target_q_values)self.optimizer.zero_grad()loss.backward()self.optimizer.step()if self.epsilon > self.epsilon_min:self.epsilon *= self.epsilon_decaydef update_target_model(self):self.target_model.load_state_dict(self.model.state_dict())if __name__ == "__main__":env = gym.make("CartPole-v1") agent = DQNAgent(state_dim=4, action_dim=2)for episode in range(1000):state = env.reset()episode_reward = 0done = Falsewhile not done:action = agent.get_action(state)next_state, reward, done, _ = env.step(action)agent.memory.append((state, action, reward, next_state, done))episode_reward += rewardstate = next_stateagent.train_model()if episode % 10 == 0:agent.update_target_model()print(f"Episode: {episode}, Reward: {episode_reward}, Epsilon: {agent.epsilon:.2f}")
这个示例代码实现了一个基本的DQN算法,包括经验回放、目标网络、epsilon贪婪探索等。我们将其应用于CartPole环境,智能体通过DQN学习控制一根杆子保持平衡。代码的主要组成部分有:
- QNetwork: 定义了一个简单的MLP作为Q网络,输入状态,输出各个动作的Q值。
- DQNAgent: 实现了DQN算法的核心逻辑,包括经验回放、模型训练、目标网络更新、动作选择等。
- 训练循环: 智能体与环境交互,将转移样本存入回放缓冲区,并进行Q网络训练。每隔一定episode更新目标网络。
运行这个代码,你可以看到智能体的性能随着训练不断提高,最终能够使杆子长时间保持平衡。你可以尝试调整超参数如batch_size、learning_rate、epsilon_decay等,观察它们对算法性能的影响。
6.2 Double DQN
下面我们在DQN的基础上实现Double DQN算法,只需对calculate_target函数做少量修改:
def train_model(self):...# 计算下一状态的最大Q值max_action = self.model(next_states).max(1)[1].unsqueeze(1)max_next_q_values = self.target_model(next_states).gather(1, max_action).squeeze(1)max_next_q_values[dones] = 0.0max_next_q_values = max_next_q_values.detach()...
相比DQN,Double DQN用Q网络来选择下一状态的最优动作,再用目标网络来评估该动作的Q值。这个简单的改动可以显著减少Q值的过估计问题,提高算法的稳定性和性能。你可以对比运行DQN和Double DQN,体会它们在训练过程中的差异。
6.3 DQN with Prioritized Replay
接下来我们实现优先经验回放(Prioritized Replay),为experience replay引入非均匀采样,提高样本利用效率。
首先定义一个概率分布类SumTree,用于管理样本的优先级:
class SumTree:def __init__(self, capacity):self.capacity = capacityself.tree = np.zeros(2 * capacity - 1)self.data = np.zeros(capacity, dtype=object) self.size = 0self.ptr = 0def add(self, p, data):tree_idx = self.ptr + self.capacity - 1self.data[self.ptr] = dataself.update(tree_idx, p)self.ptr += 1if self.ptr >= self.capacity:self.ptr = 0self.size = min(self.size + 1, self.capacity)def update(self, tree_idx, p):change = p - self.tree[tree_idx]self.tree[tree_idx] = pwhile tree_idx != 0:tree_idx = (tree_idx - 1) // 2self.tree[tree_idx] += changedef get_leaf(self, v):parent_idx = 0while True:left_idx = 2 * parent_idx + 1right_idx = left_idx + 1if left_idx >= len(self.tree):leaf_idx = parent_idxbreakif v <= self.tree[left_idx]:parent_idx = left_idxelse:v -= self.tree[left_idx]parent_idx = right_idxdata_idx = leaf_idx - self.capacity + 1return leaf_idx, self.tree[leaf_idx], self.data[data_idx]@propertydef total_p(self):return self.tree[0]
然后修改DQNAgent,将普通的经验回放替换为优先经验回放:
class DQNAgent:def __init__(self, state_dim, action_dim):...self.alpha = 0.6self.beta = 0.4self.beta_increment = 0.001self.epsilon = 1e-6self.memory = SumTree(capacity=10000)...def store_transition(self, state, action, reward, next_state, done):transition = (state, action, reward, next_state, done)self.memory.add(self.max_priority, transition)def train_model(self):if self.memory.size < self.train_start:returnself.beta = min(1.0, self.beta + self.beta_increment)batch_idx, batch_priorities, batch_data = self.sample_batch()states = torch.tensor(np.array([data[0] for data in batch_data]), dtype=torch.float32)actions = torch.tensor(np.array([data[1] for data in batch_data]), dtype=torch.int64)rewards = [data[2] for data in batch_data]next_states = torch.tensor(np.array([data[3] for data in batch_data]), dtype=torch.float32)dones = [data[4] for data in batch_data]... # 计算Q值与目标Q值# 计算prioritiespriorities = (torch.abs(target_q_values - q_values) + self.epsilon).cpu().data.numpy()# 更新prioritiesfor i in range(self.batch_size):idx = batch_idx[i]self.memory.update(idx, priorities[i])def sample_batch(self):batch_idx = np.empty((self.batch_size,), dtype=np.int32)batch_priorities = np.empty((self.batch_size,), dtype=np.float32)batch_data = []segment = self.memory.total_p / self.batch_sizeself.max_priority = max(self.memory.tree[-self.memory.capacity:])p_sum = self.memory.tree[0]for i in range(self.batch_size):a = segment * ib = segment * (i + 1)upperbound = random.uniform(a, b)idx, p, data = self.memory.get_leaf(upperbound)batch_idx[i] = idxbatch_priorities[i] = p / p_sumbatch_data.append(data)batch_priorities /= p_sum # normalizebatch_weights = (p_sum * batch_priorities) ** (-self.beta)batch_weights /= batch_weights.max()return batch_idx, batch_priorities, batch_data
优先经验回放的核心是SumTree数据结构,它以树形结构组织样本,树节点存储样本priority的累加和,叶节点存储样本priority。这种结构能够以O(logN)的复杂度实现权重采样和priority更新。
DQNAgent的主要变化包括:
- store_transition: 将样本存储到SumTree中,优先级初始化为当前最大值
- train_model: 从SumTree中采样一个batch,并根据TD误差更新样本的优先级
- sample_batch: 基于SumTree实现加权采样,权重与优先级成正比
与普通的经验回放相比,优先经验回放能够更频繁地采样到有信息量的样本(如TD误差大的样本),从而加速训练收敛。你可以通过调节alpha和beta系数来权衡优先级分布的陡峭程度。
6.4 Dueling DQN
最后我们来实现Dueling DQN,修改Q网络的结构,显式估计状态值函数和优势函数。
class DuelingQNetwork(nn.Module):def __init__(self, state_dim, action_dim):super(DuelingQNetwork, self).__init__()self.feature = nn.Sequential(nn.Linear(state_dim, 64),nn.ReLU(),nn.Linear(64, 64),nn.ReLU())self.advantage = nn.Sequential(nn.Linear(64, 64),nn.ReLU(),nn.Linear(64, action_dim))self.value = nn.Sequential(nn.Linear(64, 64),nn.ReLU(),nn.Linear(64, 1))def forward(self, x):feature = self.feature(x)advantage = self.advantage(feature)value = self.value(feature)q = value + advantage - advantage.mean()return q
将QNetwork替换为DuelingQNetwork,其他部分代码保持不变即可。
Dueling网络将Q函数分解为状态值函数V(s)和优势函数A(s,a),其中A(s,a)表示动作a相对于状态s的平均优势。这种分解使得网络能够学到更稳定、更健壮的值函数,提高策略的质量。你可以思考为什么Dueling架构能学到更优的策略。
总结
通过以上代码示例,相信你已经掌握了DQN及其变体算法的核心思想和实现要点。这些示例代码提供了一个可扩展的框架,你可以在此基础上轻松实现其他DRL算法,或将它们应用到不同的环境任务中。
当然,实际应用还需要考虑诸多工程细节,如超参数调优、模型存储、日志记录、可视化分析等。这需要你在实践中不断摸索和积累。但只要掌握了DQN的核心原理,相信你一定能够驾驭复杂的DRL系统,让智能体学会从环境中汲取知识,实现智能决策。
让我们以DQN为起点,一起探索DRL的精彩世界,用智能点亮未来!
7. 前沿探索
强化学习是一个蓬勃发展的研究领域,DQN的提出掀起了深度强化学习的浪潮。近年来,研究者们不断探索DRL的新方法、新架构、新应用,取得了一系列令人瞩目的成果。本章我们简要介绍几个有前景的研究方向,帮助读者把握DRL的最新进展。
7.1 分层强化学习
现实世界中很多任务都具有层次结构,可以分解为多个子任务。例如做饭可以分解为采购食材、清洗切配、烹饪等步骤。标准的DRL算法通常学习一个扁平的策略,缺乏对任务内在结构的建模,难以应对复杂的长期规划问题。
分层强化学习(Hierarchical RL)旨在学习多层次的策略,自底向上地解决子任务,再组合成整体的解决方案。一个代表性工作是Feudal Networks(FuNs)[Vezhnevets et al., 2017],它引入了君臣网络结构,高层网络设置子目标,低层网络执行基本动作,实现了策略的分层抽象。这种分层结构使得智能体能够学到更高效、更具可解释性的策略。
分层强化学习让智能体掌握了"分而治之"的解题思想,有望攻克现实世界中的复杂规划难题。未来的一个重要方向是探索自适应的层次结构,让智能体自主地发现任务的内在逻辑。
7.2 元强化学习
元学习(Meta Learning)的目标是学会如何学习,让模型具备快速适应新任务的能力。将元学习思想引入强化学习,就得到了元强化学习(Meta RL)。
Model-Agnostic Meta-Learning(MAML)[Finn et al., 2017]是一个著名的元学习框架。它通过两级优化来学习一个任务初始化参数,使其能在新任务上通过少量梯度下降快速适应。这种思想也被引入强化学习,称之为Meta RL[Finn et al., 2017]。Meta RL旨在学习一个初始化策略,使其能在相似的MDP家族上快速适应。具体来说,它在一个分布的MDP上训练,优化策略在新MDP上经过少量梯度下降后的累积回报。
Meta RL让智能体具备了"学习转移"的技能,可大大减少新环境中所需的数据量和训练时间,提高学习的泛化能力。未来需进一步提高元策略的适应性和鲁棒性,实现跨领域的知识迁移。
7.3 多智能体强化学习
很多实际应用如自动驾驶、智慧城市等,都涉及多个智能体的协作与博弈。将DRL拓展到多智能体场景,就得到了多智能体强化学习(Multi-Agent RL, MARL)。
经典的MARL算法如Independent Q-Learning(IQL)简单地为每个智能体训练一个独立的Q网络,缺乏对其他智能体行为的建模,难以学到协作策略。为此,研究者们提出了一系列协作感知(cooperation-aware)的MARL算法。其中Value Decomposition Networks(VDN)[Sunehag et al., 2017]假设联合动作值函数可分解为各智能体值函数之和,从而将联合Q学习简化为分布式单智能体学习。QMIX[Rashid et al., 2018]进一步放宽了可加性约束,引入一个非线性混合网络来组合各智能体的值函数,增强了值函数的表达能力。
多智能体学习让智能体掌握了"协同合作"的能力,是应对复杂社会性任务的关键。未来的重点是提高算法的可扩展性和鲁棒性,在面对大规模非稳态环境时,依然能学到有效的协作策略。
7.4 安全强化学习
在高风险领域如自动驾驶、医疗诊断部署DRL系统时,不仅要考虑性能,还要确保策略的安全性。让智能体规避危险行为,一直是强化学习的难题。
安全强化学习(Safe RL)[Garcıa et al., 2015]旨在学习满足安全约束的最优策略。一种常见做法是将约束建模为惩罚项,纳入环境奖励。但这种软约束难以严格保证安全性。另一种思路是将安全判别器嵌入策略搜索过程[Dalal et al., 2018],引导智能体远离危险状态。但判别器的准确性依赖专家知识,且难以适应环境变化。
如何在保证安全的前提下,最大化任务性能,是一个亟待攻克的难题。未来需进一步完善安全理论与算法,在实践中平衡安全性、样本效率与计算效率,最终实现可信、可用、可靠的DRL系统。
展望未来
纵观全书,我们系统地介绍了DRL的基本原理与核心算法,聚焦DQN及其变体,展示了DRL在游戏、机器人等领域的应用进展,探讨了若干有前景的研究方向。DRL作为连接深度学习与强化学习的桥梁,大大拓展了智能系统的应用边界,使之有能力直接从原始高维数据中学习复杂的策略,实现端到端的决策优化。
当前,DRL在解决现实世界复杂问题的道路上仍面临诸多挑战:
- 样本效率:DRL需要大量的数据来学习值函数或策略,在很多实际任务中代价难以承受。元学习、迁移学习等技术有望缓解这一问题。
- 泛化能力:DRL模型在训练环境中表现优异,但迁移到新环境后往往难以适应。增强学习(augmented learning)等方法通过数据增强来提高模型鲁棒性。
- 安全可信:DRL系统缺乏可解释性,难以验证其安全性与正确性。将因果推理、逻辑规划等符号化方法与DRL相结合,有望实现可解释、可证明的策略学习。
- 工程实现:DRL涉及环境构建、神经网络设计、分布式训练等诸多工程问题。TensorFlow、PyTorch等深度学习框架对DRL的支持还不够完善。
尽管道阻且长,但DRL作为通用人工智能的核心技术之一,正在深刻影响和重塑人类社会。智能助理、无人驾驶、智能医疗等领域已初见DRL的身影。随着5G、物联网、大数据等新一代信息技术的发展,DRL将迎来更广阔的应用空间。DRL与多智能体系统、群体智能的结合,将开创性地模拟复杂社会系统,优化资源配置。DRL与知识图谱、逻辑推理等符号AI技术的融合,将从数据驱动进化到知识驱动,实现更高层次的认知与规划。