强化学习笔记 [15] | A3C

注意
本文最后更新于 2024-02-25,文中内容可能已过时。

0. 引言

强化学习 (十四) Actor-Critic中,我们讨论了 Actor-Critic 的算法流程,但是由于普通的 Actor-Critic 算法难以收敛,需要一些其他的优化。而 Asynchronous Advantage Actor-critic (以下简称 A3C) 就是其中比较好的优化算法。本文我们讨论 A3C 的算法原理和算法流程。

本文主要参考了 A3C 的论文,以及 ICML 2016 的 deep RL tutorial

1. A3C 的引入

上一篇 Actor-Critic 算法的代码,其实很难收敛,无论怎么调参,最后的 CartPole 都很难稳定在 200 分,这是 Actor-Critic 算法的问题。但是我们还是有办法去有优化这个难以收敛的问题的。

回忆下之前的 DQN 算法,为了方便收敛使用了经验回放的技巧。那么我们的 Actor-Critic 是不是也可以使用经验回放的技巧呢?当然可以!不过 A3C 更进一步,还克服了一些经验回放的问题。经验回放有什么问题呢? 回放池经验数据相关性太强,用于训练的时候效果很可能不佳。举个例子,我们学习下棋,总是和同一个人下,期望能提高棋艺。这当然没有问题,但是到一定程度就再难提高了,此时最好的方法是另寻高手切磋。

A3C 的思路也是如此,它利用多线程的方法,同时在多个线程里面分别和环境进行交互学习,每个线程都把学习的成果汇总起来,整理保存在一个公共的地方。并且,定期从公共的地方把大家的齐心学习的成果拿回来,指导自己和环境后面的学习交互。

通过这种方法,A3C 避免了经验回放相关性过强的问题,同时做到了异步并发的学习模型。

2. A3C 的算法优化

现在我们来看看相比 Actor-Critic,A3C 到底做了哪些具体的优化。

相比 Actor-Critic,A3C 的优化主要有 3 点,分别是异步训练框架,网络结构优化,Critic 评估点的优化。其中异步训练框架是最大的优化。

我们首先来看这个异步训练框架,如下图所示:



异步训练框架

图中上面的 Global Network 就是上一节说的共享的公共部分,主要是一个公共的神经网络模型,这个神经网络包括 Actor 网络和 Critic 网络两部分的功能。下面有 n 个 worker 线程,每个线程里有和公共的神经网络一样的网络结构,每个线程会独立的和环境进行交互得到经验数据,这些线程之间互不干扰,独立运行。

每个线程和环境交互到一定量的数据后,就计算在自己线程里的神经网络损失函数的梯度,但是这些梯度却并不更新自己线程里的神经网络,而是去更新公共的神经网络。也就是 n 个线程会独立的使用累积的梯度分别更新公共部分的神经网络模型参数。每隔一段时间,线程会将自己的神经网络的参数更新为公共神经网络的参数,进而指导后面的环境交互。

可见,公共部分的网络模型就是我们要学习的模型,而线程里的网络模型主要是用于和环境交互使用的,这些线程里的模型可以帮助线程更好的和环境交互,拿到高质量的数据帮助模型更快收敛。

现在我们来看看第二个优化,网络结构的优化。之前在强化学习 (十四) Actor-Critic中,我们使用了两个不同的网络 Actor 和 Critic。在 A3C 这里,我们把两个网络放到了一起,即输入状态 SS, 可以输出状态价值 VV, 和对应的策略 ππ, 当然,我们仍然可以把 Actor 和 Critic 看做独立的两块,分别处理,如下图所示:



把 Actor 和 Critic 看做独立的两块,分别处理

第三个优化点是 Critic 评估点的优化,在强化学习 (十四) Actor-Critic第 2 节中,我们讨论了不同的 Critic 评估点的选择,其中 d 部分讲到了使用优势函数 AA 来做 Critic 评估点,优势函数 AA 在时刻 t 不考虑参数的默认表达式为:

A(S,A,t)=Q(S,A)V(S)A(S,A,t)=Q(S,A)-V(S)

Q(S,A)Q(S,A) 的值一般可以通过单步采样近似估计,即:

Q(S,A)=R+γV(S)Q(S,A)=R+\gamma V(S^{\prime})

这样优势函数去掉动作可以表达为:

A(S,t)=R+γV(S)V(S)A(S,t)=R+\gamma V(S^{\prime})-V(S)

其中 V(S)V(S) 的值需要通过 Critic 网络来学习得到。

在 A3C 中,采样更进一步,使用了 N 步采样,以加速收敛。这样 A3C 中使用的优势函数表达为:

A(S,t)=Rt++γRt+1+γn1Rt+n1+γnV(S)V(S)A(S,t)=R_t++\gamma R_{t+1}+\ldots\gamma^{n-1}R_{t+n-1}+\gamma^nV(S^{\prime})-V(S)

对于 Actor 和 Critic 的损失函数部分,和 Actor-Critic 基本相同。有一个小的优化点就是在 Actor-Critic 策略函数的损失函数中,加入了策略 ππ 的熵项,系数为 cc, 即策略参数的梯度更新和 Actor-Critic 相比变成了这样:

θ=θ+αθlogπθ(st,at)A(S,t)+cθH(π(St,θ))\theta=\theta+\alpha\nabla_\theta log\pi_\theta(s_t,a_t)A(S,t)+c\nabla_\theta H(\pi(S_t,\theta))

以上就是 A3C 和 Actor-Critic 相比有优化的部分。下面我们来总价下 A3C 的算法流程。

3. A3C 算法流程

这里我们对 A3C 算法流程做一个总结,由于 A3C 是异步多线程的,我们这里给出任意一个线程的算法流程。

  • 输入:公共部分的 A3C 神经网络结构,对应参数位 θθ , ww,本线程的 A3C 神经网络结构,对应参数 θθ’, ww’, 全局共享的迭代轮数 TT,全局最大迭代次数 TmaxT_{max}, 线程内单次迭代时间序列最大长度 TlocalT_{local}, 状态特征维度 nn, 动作集 AA, 步长 αα, ββ,熵系数 cc, 衰减因子 γγ

  • 输出:公共部分的 A3C 神经网络参数 θθ, ww

    • (1). 更新时间序列 t=1t=1
    • (2). 重置 Actor 和 Critic 的梯度更新量: dθ0dθ←0,dw0dw←0
    • (3). 从公共部分的 A3C 神经网络同步参数到本线程的神经网络:θ=θ,w=wθ’=θ,w’=w
    • (4). tstart=tt_{start}=t,初始化状态 sts_t
    • (5). 基于策略 π(atst;θ)π(at|st;θ) 选择出动作 ata_t
    • (6). 执行动作 ata_t得到奖励 rtr_t 和新状态 st+1s_{t+1}
    • (7). tt+1t←t+1, TT+1T←T+1
    • (8). 如果 sts_t是终止状态,或 ttstart==tlocalt − t_{start}==t_{local}, 则进入步骤 (9),否则回到步骤 (5)
    • (9). 计算最后一个时间序列位置 sts_tQ(s,t)Q(s,t):
      • $$\left.Q(s,t)=\left\{\begin{array}{ll}0&terminalstate\\V(s_t,w^{\prime})&noneterminal~state,bootstrapping\end{array}\right.\right.$$
    • (10). for i(t1,t2,tstart)i∈(t−1,t−2,…t_{start}):
      • 1). 计算每个时刻的 Q(s,i)Q(s,i)Q(s,i)=ri+γQ(s,i+1)Q(s,i)=r_i+\gamma Q(s,i+1)
      • 2). 累计 Actor 的本地梯度更新:
        • dθdθ+θlogπθ(si,ai)(Q(s,i)V(Si,w))+cθH(π(si,θ))d\theta\leftarrow d\theta+\nabla_{\theta^{\prime}}log\pi_{\theta^{\prime}}(s_i,a_i)(Q(s,i)-V(S_i,w^{\prime}))+c\nabla_{\theta^{\prime}}H(\pi(s_i,\theta^{\prime}))
      • 3). 累计 Critic 的本地梯度更新:
        • dwdw+(Q(s,i)V(Si,w))2w\begin{aligned}dw&\leftarrow dw+\frac{\partial(Q(s,i)-V(S_i,w^{\prime}))^2}{\partial w^{\prime}}\end{aligned}
    • (11). 更新全局神经网络的模型参数:
      • θ=θ+αdθ, w=wβdw\theta=\theta+\alpha d\theta,~w=w-\beta dw
    • (12). 如果 T>TmaxT>T_{max}, 则算法结束,输出公共部分的 A3C 神经网络参数 θθ, ww, 否则进入步骤 (3)

以上就是 A3C 算法单个线程的算法流程。

4. A3C 算法实例

下面我们基于上述算法流程给出 A3C 算法实例。仍然使用了 OpenAI Gym 中的 CartPole-v0 游戏来作为我们算法应用。CartPole-v0 游戏的介绍参见这里。它比较简单,基本要求就是控制下面的 cart 移动使连接在上面的 pole 保持垂直不倒。这个任务只有两个离散动作,要么向左用力,要么向右用力。而 state 状态就是这个 cart 的位置和速度, pole 的角度和角速度,4 维的特征。坚持到 200 分的奖励则为过关。

算法代码大部分参考了莫烦的 A3C 代码,增加了模型测试部分的代码并调整了部分模型参数。完整的代码参见我的 Github:https://github.com/ljpzzz/machinelearning/blob/master/reinforcement-learning/a3c.py

整个算法的 Actor 和 Critic 的网络结构都定义在这里, 所有的线程中的网络结构,公共部分的网络结构都在这里定义。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
def _build_net(self, scope):
  w_init = tf.random_normal_initializer(0., .1)
  with tf.variable_scope('actor'):
    l_a = tf.layers.dense(self.s, 200, tf.nn.relu6, kernel_initializer=w_init, name='la')
    a_prob = tf.layers.dense(l_a, N_A, tf.nn.softmax, kernel_initializer=w_init, name='ap')
  with tf.variable_scope('critic'):
    l_c = tf.layers.dense(self.s, 100, tf.nn.relu6, kernel_initializer=w_init, name='lc')
    v = tf.layers.dense(l_c, 1, kernel_initializer=w_init, name='v')  # state value
  a_params = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, scope=scope + '/actor')
  c_params = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, scope=scope + '/critic')
  return a_prob, v, a_params, c_params

所有线程初始化部分,以及本线程和公共的网络结构初始化部分如下:

1
2
3
4
5
6
7
8
9
with tf.device("/cpu:0"):
  OPT_A = tf.train.RMSPropOptimizer(LR_A, name='RMSPropA')
  OPT_C = tf.train.RMSPropOptimizer(LR_C, name='RMSPropC')
  GLOBAL_AC = ACNet(GLOBAL_NET_SCOPE)  # we only need its params
  workers = []
  # Create worker
  for i in range(N_WORKERS):
    i_name = 'W_%i' % i   # worker name
    workers.append(Worker(i_name, GLOBAL_AC))

本线程神经网络将本地的梯度更新量用于更新公共网络参数的逻辑在 update_global 函数中,而从公共网络把参数拉回到本线程神经网络的逻辑在 pull_global 中。

1
2
3
4
5
def update_global(self, feed_dict):  # run by a local
  SESS.run([self.update_a_op, self.update_c_op], feed_dict)  # local grads applies to global net

def pull_global(self):  # run by a local
  SESS.run([self.pull_a_params_op, self.pull_c_params_op])

详细的内容大家可以对照代码和算法流程一起看。在主函数里我新加了一个测试模型效果的过程,大家可以试试看看最后的模型效果如何。

5. A3C 小结

A3C 解决了 Actor-Critic 难以收敛的问题,同时更重要的是,提供了一种通用的异步的并发的强化学习框架,也就是说,这个并发框架不光可以用于 A3C,还可以用于其他的强化学习算法。这是 A3C 最大的贡献。目前,已经有基于 GPU 的 A3C 框架,这样 A3C 的框架训练速度就更快了。

除了 A3C, DDPG 算法也可以改善 Actor-Critic 难收敛的问题。它使用了 Nature DQN,DDQN 类似的思想,用两个 Actor 网络,两个 Critic 网络,一共 4 个神经网络来迭代更新模型参数。在下一篇我们讨论 DDPG 算法。

Buy me a coffee~
支付宝
微信
0%