06.md 24.2 KB
Newer Older
W
wizardforcel 已提交
1 2
# 异步方法

W
wizardforcel 已提交
3
到目前为止,我们已经涵盖了大多数重要主题,例如马尔可夫决策过程,值迭代,Q 学习,策略梯度,深度 Q 网络和参与者批评算法。 这些构成了强化学习算法的核心。 在本章中,我们将继续从 Actor Critic Algorithms 中停止的地方继续搜索,并深入研究用于深度强化学习的高级**异步方法**及其最著名的变体**异步优势演员评论家算法**,通常称为 **A3C 算法**
W
wizardforcel 已提交
4

W
wizardforcel 已提交
5
但是,在开始使用 A3C 算法之前,让我们修改第 4 章和“策略梯度”中涵盖的 Actor Critic 算法的基础。 如果您还记得,演员评论算法有两个组成部分:
W
wizardforcel 已提交
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

*   演员
*   评论家

参与者采取当前的环境状态并确定要采取的最佳行动,而评论家则通过采取环境状态和行动来扮演策略评估角色,并返回一个分数,以描述该状态对某项行动的良好程度。 如下图所示:

![](img/5b448889-61bf-43ca-82f5-ce5d63662d33.png)

换句话说,演员的行为像孩子,而评论家的行为像父母,孩子探索多种行为,父母批评不良行为并补充良好行为。 因此,行动者批判算法既学习了策略功能,又学习了状态作用值函数。 像策略梯度一样,参与者评论算法也通过梯度上升来更新其参数。 在高维连续状态和动作空间的情况下,Actor-critic 方法非常有效。

因此,让我们从 Google DeepMind 发布的深度强化学习中的异步方法开始,该方法在性能和计算效率方面都超过了 DQN。

我们将在本章介绍以下主题:

*   为什么使用异步方法?
*   异步一站式 Q 学习
*   异步一步式 SARSA
*   异步 n 步 Q 学习
*   异步优势演员评论家
W
wizardforcel 已提交
25
*   OpenAI 体育馆中用于摆锤 v0 的 A3C
W
wizardforcel 已提交
26 27 28

# 为什么使用异步方法?

W
wizardforcel 已提交
29
Google DeepMind 和 MILA 的联合团队于 2016 年 6 月发布了用于深度强化学习的[**同步方法**](https://arxiv.org/pdf/1602.01783.pdf)。 它速度更快,并且能够在多核 CPU 上而不是使用 GPU 上显示出良好的效果。 异步方法也适用于连续动作空间和离散动作空间。
W
wizardforcel 已提交
30 31 32 33 34 35 36

如果我们回想起深度 Q 网络的方法,我们会使用经验重播作为存储所有经验的存储,然后使用其中的随机样本来训练我们的深度神经网络,从而反过来预测最大 Q 值。 有利的行动。 但是,随着时间的流逝,它具有高内存使用和大量计算的缺点。 这背后的基本思想是克服这个问题。 因此,不是使用经验重播,而是创建了环境的多个实例,并且多个代理以异步方式并行执行操作(如下图所示):

![](img/f1e415ce-be75-4cb7-b24e-7e81937e3c04.png)

深度强化学习中异步方法的高级示意图

W
wizardforcel 已提交
37
在异步方法中,为每个线程分配了一个进程,该进程包含一个学习器,该学习器表示与自己的环境副本进行交互的代理网络。 因此,这些多个学习器并行运行以探索他们自己的环境版本。 这种并行方法允许代理在任何给定的时间步长同时经历各种不同的状态,并涵盖了非策略学习和策略学习学习算法的基础。
W
wizardforcel 已提交
38

W
wizardforcel 已提交
39
如上所述,异步方法能够在多核 CPU(而不是 GPU)中显示出良好的效果。 因此,异步方法非常快,因此成为最新的强化学习算法,因为到目前为止,深度强化学习算法的实现依赖于 GPU 驱动的机器和分布式架构,以见证所实现算法的更快收敛。
W
wizardforcel 已提交
40

W
wizardforcel 已提交
41
这些并行运行的多个学习器使用不同的探索策略,从而最大限度地提高了多样性。 不同学习器的不同探索策略会更改参数,并且这些更新与时间相关的机会最少。 因此,不需要经验回放记忆,并且我们依靠使用不同探索策略的并行学习来执行先前在 DQN 中使用的经验回放的角色。
W
wizardforcel 已提交
42 43 44

使用并行学习器的好处如下:

W
wizardforcel 已提交
45
*   减少训练时间。
W
wizardforcel 已提交
46 47 48 49 50 51 52
*   不使用经验重播。 因此,基于策略的强化学习方法也可以用于训练神经网络。

深度强化学习中异步方法的不同变体是:

*   异步一站式 Q 学习
*   异步一步式 SARSA
*   异步 n 步 Q 学习
W
wizardforcel 已提交
53
*   **异步优势演员评论家****A3C**
W
wizardforcel 已提交
54

W
wizardforcel 已提交
55
将变体 A3C 应用于各种 Atari 2600 游戏时,在多核 CPU 上获得了更好的基准测试结果,相对于早期的深度强化学习算法而言,其结果所需的时间要短得多,后者需要在 GPU 驱动的机器上运行。 因此,由于依赖于昂贵的硬件资源(如 GPU)以及不同的复杂分布式架构,因此解决了该问题。 由于所有这些优点,A3C 学习代理是当前最先进的强化学习代理。
W
wizardforcel 已提交
56 57 58

# 异步一站式 Q 学习

W
wizardforcel 已提交
59
异步单步 Q 学习的架构与 DQN 非常相似。 DQN 中的代理由一组主要网络和目标网络表示,其中一步损失的计算方法是主要网络预测的当前状态 s 的状态作用值与目标状态- 目标网络计算的当前状态的动作值。 相对于策略网络的参数来计算损失的梯度,然后使用梯度下降优化器将损失最小化,从而导致主网络的参数更新。
W
wizardforcel 已提交
60

W
wizardforcel 已提交
61
异步单步 Q 学习中的区别在于,有多个此类学习代理,例如,学习器并行运行并计算此损失。 因此,梯度计算也并行发生在不同的线程中,其中每个学习代理都与自己的环境副本进行交互。 这些梯度在多个时间步长上在不同线程中的累积用于在固定时间步长后或情节结束后更新策略网络参数。 梯度的累积优于策略网络参数更新,因为这样可以避免覆盖每个学习器代理执行的更改。
W
wizardforcel 已提交
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107

此外,将不同的探索策略添加到不同的线程可以使学习变得多样化且稳定。 由于更好的探索,因此提高了性能,因为不同线程中的每个学习代理都受到不同的探索策略。 尽管有许多方法可以执行此操作,但一种简单的方法是在使用![](img/396240a6-a09a-4588-b405-54fef7a9be74.png) -greedy 的同时为不同的线程使用不同的 epsilon ![](img/3594b237-6432-4150-bbcb-35b868d50025.png)示例。

异步单步 Q 学习的伪代码如下所示。 这里,以下是全局参数:

*   ![](img/4e2e2f05-a713-40a9-84b4-abd6e47f309f.png):策略网络的参数(权重和偏差)
*   ![](img/ec675ec6-2574-4fe5-b97c-adfdfa98c693.png):目标网络的参数(权重和偏差)
*   T:整体时间步长计数器

```py
// Globally shared parameters , and T 
//  is initialized arbitrarily
// T is initialized 0

pseudo-code for each learner running parallel in each of the threads:

Initialize thread level time step counter t=0
Initialize  = 
Initialize network gradients 
Start with the initial state s

repeat until  :
    Choose action a with -greedy policy such that:

    Perform action a
    Receive new state s' and reward r
    Compute target y : 
    Compute the loss, 
    Accumulate the gradient w.r.t. : 
    s = s'
    T = T + 1
    t = t + 1

    if T mod  : 
        Update the parameters of target network :  = 
        # After every  time steps the parameters of target network is updated

    if t mod  or s = terminal state:
        Asynchronous update of  using 
        Clear gradients : 
        #at every  time step in the thread or if s is the terminal state
        #update  using accumulated gradients 
```

# 异步一步式 SARSA

W
wizardforcel 已提交
108
异步单步 SARSA 的架构几乎与异步单步 Q 学习的架构相似,不同之处在于目标网络计算当前状态的目标状态作用值的方式。 SARSA 并未使用目标网络使用下一个状态`s'`的最大 Q 值,而是使用 ε 贪婪为下一个状态`s'`选择动作`a'`。 下一个状态动作对的 Q 值`Q(s', a')`;![](img/88250d05-5523-43d2-b0fc-20f621b58966.png)用于计算当前状态的目标状态动作值。
W
wizardforcel 已提交
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154

异步单步 SARSA 的伪代码如下所示。 这里,以下是全局参数:

*   ![](img/3b0e84ed-0408-4420-9d16-382655598e8e.png):策略网络的参数(权重和偏差)
*   ![](img/4c7f218e-0d62-44fb-b430-252fac104d1a.png):目标网络的参数(权重和偏差)
*   T:总时间步长计数器

```py
// Globally shared parameters , and T 
//  is initialized arbitrarily
// T is initialized 0

pseudo-code for each learner running parallel in each of the threads:

Initialize thread level time step counter t=0
Initialize  = 
Initialize network gradients 
Start with the initial state s
Choose action a with -greedy policy such that:

repeat until  :
    Perform action a
    Receive new state s' and reward r
    Choose action a' with -greedy policy such that:

    Compute target y : 
    Compute the loss, 
    Accumulate the gradient w.r.t. : 
    s = s'
    T = T + 1
    t = t + 1
    a = a'

    if T mod  : 
         Update the parameters of target network :  = 
         # After every  time steps the parameters of target network is updated

    if t mod  or s = terminal state:
         Asynchronous update of  using 
         Clear gradients : 
         #at every  time step in the thread or if s is the terminal state
         #update  using accumulated gradients 
```

# 异步 n 步 Q 学习

W
wizardforcel 已提交
155
异步 n 步 Q 学习的架构在某种程度上类似于异步单步 Q 学习的架构。 区别在于,使用探索策略最多选择![](img/934eccea-ab73-4d16-8dd9-6ae2bef78aa9.png)步骤或直到达到终端状态,才能选择学习代理动作,以便计算策略网络参数的单个更新。 此过程列出了自上次更新以来![](img/55e36559-5c3e-4598-b8a3-954b2a844161.png)来自环境的奖励。 然后,对于每个时间步长,将损失计算为该时间步长的折现未来奖励与估算 Q 值之间的差。 对于每个时间步长,此损耗相对于特定于线程的网络参数的梯度将被计算和累积。 有多个这样的学习代理并行运行和累积梯度。 这些累积的梯度用于执行策略网络参数的异步更新。
W
wizardforcel 已提交
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210

异步 n 步 Q 学习的伪代码如下所示。 这里,以下是全局参数:

*   ![](img/a8678ea8-ad82-4124-a099-603825a7d2f8.png):策略网络的参数(权重和偏差)
*   ![](img/9008762f-89a2-4438-b034-e141fdf73759.png):目标网络的参数(权重和偏差)
*   T:总时间步长计数器
*   t:线程级时间步长计数器
*   ![](img/1c3d0299-4182-4715-83f4-eecdcd605678.png):总时间步长的最大值
*   ![](img/67ab7590-6e10-4049-b5c6-7cce81f3f339.png):线程中的最大时间步数

```py
// Globally shared parameters , and T 
//  is initialized arbitrarily
// T is initialized 0

pseudo-code for each learner running parallel in each of the threads:

Initialize thread level time step counter t = 1
Initialize  = 
Initialize  = 
Initialize network gradients 

repeat until  :
     Clear gradient : 
     Synchronize thread-specific parameters: 

     Get state 
     r = [] //list of rewards
     a = [] //list of actions
     s = [] //list of actions
     repeat until  is a terminal state or :
             Choose action  with -greedy policy such that:

             Perform action 
             Receive new state  and reward 
             Accumulate rewards by appending  to r
             Accumulate actions by appending  to a
             Accumulate actions by appending  to s
             t = t + 1
             T = T + 1

     Compute returns, R : 
     for  :

            Compute loss, 
            Accumulate gradients w.r.t.  :

     Asynchronous update of  using 
     if T mod  : 
         Update the parameters of target network :  = 
         # After every  time steps the parameters of target network is updated
```

# 异步优势演员评论家

W
wizardforcel 已提交
211
在异步优势参与者批评者的架构中,每个学习代理都包含一个参与者批评者,该学习器结合了基于价值和基于策略的方法的优点。 参与者网络将状态作为输入,并预测该状态的最佳动作,而评论家网络将状态和动作作为输入,并输出动作分数以量化该状态的动作效果。 参与者网络使用策略梯度更新其权重参数,而评论者网络使用`TD(0)`来更新其权重参数,换言之,两个时间步长之间的值估计之差,如第 4 章“策略梯度”中所述。
W
wizardforcel 已提交
212

W
wizardforcel 已提交
213
在第 4 章和“策略梯度”中,我们研究了如何通过从策略梯度的预期未来收益中减去基线函数来更新策略梯度,从而在不影响预期收益的情况下减少方差的情况。 梯度。 预期的未来奖励和基线函数之间的差称为**优势函数**; 它不仅告诉我们状态的好坏,而且还告诉我们该动作的预期好坏。
W
wizardforcel 已提交
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269

卷积神经网络既用于演员网络,又用于评论者网络。 在每个![](img/4a53a424-256c-43ac-98a9-b4b0fe5c0156.png)步骤之后或直到达到终端状态之前,将更新策略和操作值参数。 将与以下伪代码一起说明网络更新,熵和目标函数。 此外,将策略![](img/861ea090-c642-4a1f-b1b0-5082002bc932.png)的熵 H 添加到目标函数中,以通过避免过早收敛到次优策略来改善探索。

因此,有多个这样的学习代理程序运行,每个学习代理程序都包含参与者关键网络,其中策略网络参数(即参与者网络参数)使用策略梯度进行更新,其中优势函数用于计算那些策略梯度。

异步单步 SARSA 的伪代码如下所示。 这里,以下是全局参数:

*   ![](img/b61d023c-24db-426f-b276-170dc20d960a.png):策略网络的参数(权重和偏差)
*   ![](img/9f23bd7c-22ba-4f6c-a6a8-d09a2dd58bfb.png):值函数逼近器的参数(权重和偏差)
*   T:总时间步长计数器

特定于线程的参数如下:

*   ![](img/98afa05f-22c6-488f-8943-f4d2397cb8bc.png):策略网络的线程特定参数
*   ![](img/9ce7dded-df9f-41f8-a8ab-b2950ec62a13.png):值函数近似器的线程特定参数

```py
//Globally shared parameters  and T
//  is initialized arbitrarily
//  is initialized arbitrarily
// T is initialized 0

pseudo-code for each learner running parallel in each of the threads:

//Thread specific parameters  and 
Initialize thread level time step counter t = 1
repeat until  :
    reset gradients :  and 
    synchronize thread specific parameters :  and 

    Get state 
    r = [] //list of rewards
    a = [] //list of actions
    s = [] //list of actions
    repeat until  is a terminal state or :
        Perform  according to policy 
        Receive new state  and reward 
        Accumulate rewards by appending  to r
        Accumulate actions by appending  to a
        Accumulate actions by appending  to s
        t = t + 1
        T = T + 1

    Compute returns, that is expected future rewards R such that:

    for  :

            Accumulate gradients w.r.t.  :

            Accumulate gradients w.r.t.  :

    Asynchronous update of  using  and  using 
```

# OpenAI 体育馆中 Pong-v0 的 A3C

W
wizardforcel 已提交
270
在第 4 章和“策略梯度”中,我们已经讨论过乒乓环境。 我们将使用以下代码在 OpenAI 体育馆中为 Pong-v0 创建 A3C:
W
wizardforcel 已提交
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305

```py
import multiprocessing
import threading
import tensorflow as tf
import numpy as np
import gym
import os
import shutil
import matplotlib.pyplot as plt

game_env = 'Pong-v0'
num_workers = multiprocessing.cpu_count()
max_global_episodes = 100000
global_network_scope = 'globalnet'
global_iteration_update = 20
gamma = 0.9
beta = 0.0001
lr_actor = 0.0001 # learning rate for actor
lr_critic = 0.0001 # learning rate for critic
global_running_rate = []
global_episode = 0

env = gym.make(game_env)

num_actions = env.action_space.n

tf.reset_default_graph()
```

输入状态图像预处理功能:

```py
def preprocessing_image(obs): #where I is the single frame of the game as the input
    """ prepro 210x160x3 uint8 frame into 6400 (80x80) 1D float vector """
W
wizardforcel 已提交
306
    #the values below have been precomputed through trail and error by OpenAI team members
W
wizardforcel 已提交
307
    obs = obs[35:195] 
W
wizardforcel 已提交
308
    #cropping the image frame to an extent where it contains on the paddles and ball and area between them
W
wizardforcel 已提交
309
    obs = obs[::2,::2,0] 
W
wizardforcel 已提交
310
    #downsample by the factor of 2 and take only the R of the RGB channel.Therefore, now 2D frame
W
wizardforcel 已提交
311 312 313 314 315 316
    obs[obs==144] = 0 #erase background type 1
    obs[obs==109] = 0 #erase background type 2
    obs[obs!=0] = 1 #everything else(other than paddles and ball) set to 1
    return obs.astype('float').ravel() #flattening to 1D
```

W
wizardforcel 已提交
317
以下代码显示了`actor-critic`类,其中包含`actor``critic`网络的架构:
W
wizardforcel 已提交
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495

```py
class ActorCriticNetwork(object):
    def __init__(self, scope, globalAC=None):

        if scope == global_network_scope: # get global network
            with tf.variable_scope(scope):
                self.s = tf.placeholder(tf.float32, [None,6400], 'state')
                self.a_params, self.c_params = self._build_net(scope)[-2:]
        else: # local net, calculate losses
            with tf.variable_scope(scope):
                self.s = tf.placeholder(tf.float32, [None,6400], 'state')
                self.a_his = tf.placeholder(tf.int32, [None,], 'action')
                self.v_target = tf.placeholder(tf.float32, [None, 1], 'target_vector')

                self.a_prob, self.v, self.a_params, self.c_params = self._build_net(scope)

                td = tf.subtract(self.v_target, self.v, name='temporal_difference_error')
                with tf.name_scope('critic_loss'):
                    self.c_loss = tf.reduce_mean(tf.square(td))

                with tf.name_scope('actor_loss'):
                    log_prob = tf.reduce_sum(tf.log(self.a_prob) * tf.one_hot(self.a_his, num_actions, dtype=tf.float32), axis=1, keep_dims=True)
                    exp_v = log_prob * td
                    entropy = -tf.reduce_sum(self.a_prob * tf.log(self.a_prob + 1e-5),
                                             axis=1, keep_dims=True) #exploration
                    self.exp_v = beta * entropy + exp_v
                    self.a_loss = tf.reduce_mean(-self.exp_v)

                with tf.name_scope('local_grad'):
                    self.a_grads = tf.gradients(self.a_loss, self.a_params)
                    self.c_grads = tf.gradients(self.c_loss, self.c_params)

            with tf.name_scope('sync'):
                with tf.name_scope('pull'):
                    self.pull_a_params_op = [l_p.assign(g_p) for l_p, g_p in zip(self.a_params, globalAC.a_params)]
                    self.pull_c_params_op = [l_p.assign(g_p) for l_p, g_p in zip(self.c_params, globalAC.c_params)]
                with tf.name_scope('push'):
                    self.update_a_op = actor_train.apply_gradients(zip(self.a_grads, globalAC.a_params))
                    self.update_c_op = critic_train.apply_gradients(zip(self.c_grads, globalAC.c_params))

    def _build_net(self, scope):
        w_init = tf.random_normal_initializer(0., .1)
        with tf.variable_scope('actor_network'):
            l_a = tf.layers.dense(self.s, 300, tf.nn.relu6, kernel_initializer=w_init, name='actor_layer')
            a_prob = tf.layers.dense(l_a, num_actions, tf.nn.softmax, kernel_initializer=w_init, name='ap')
        with tf.variable_scope('critic_network'):
            l_c = tf.layers.dense(self.s, 100, tf.nn.relu6, kernel_initializer=w_init, name='critic_layer')
            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

    def update_global(self, feed_dict): # run local
        session.run([self.update_a_op, self.update_c_op], feed_dict) # local gradient applied to global net

    def pull_global(self): # run local
        session.run([self.pull_a_params_op, self.pull_c_params_op])

    def choose_action(self, s): # run local
        s = np.reshape(s,[-1])
        prob_weights = session.run(self.a_prob, feed_dict={self.s: s[np.newaxis, :]})
        action = np.random.choice(range(prob_weights.shape[1]),
                                  p=prob_weights.ravel()) # select action w.r.t the actions prob
        return action
```

代表每个线程中的进程的 worker 类如下所示:

```py
class Worker(object):
    def __init__(self, name, globalAC):
        self.env = gym.make(game_env).unwrapped
        self.name = name
        self.AC = ActorCriticNetwork(name, globalAC)

    def work(self):
        global global_running_rate, global_episode
        total_step = 1
        buffer_s, buffer_a, buffer_r = [], [], []
        while not coordinator.should_stop() and global_episode < max_global_episodes:
            obs = self.env.reset()
            s = preprocessing_image(obs)
            ep_r = 0
            while True:
                if self.name == 'W_0':
                    self.env.render()
                a = self.AC.choose_action(s)

                #print(a.shape)

                obs_, r, done, info = self.env.step(a)
                s_ = preprocessing_image(obs_)
                if done and r<=0: 
                      r = -20
                ep_r += r
                buffer_s.append(np.reshape(s,[-1]))
                buffer_a.append(a)
                buffer_r.append(r)

                if total_step % global_iteration_update == 0 or done: # update global and assign to local net
                    if done:
                        v_s_ = 0 # terminal
                    else:
                        s_ = np.reshape(s_,[-1])
                        v_s_ = session.run(self.AC.v, {self.AC.s: s_[np.newaxis, :]})[0, 0]
                    buffer_v_target = []
                    for r in buffer_r[::-1]: # reverse buffer r
                        v_s_ = r + gamma * v_s_
                        buffer_v_target.append(v_s_)
                    buffer_v_target.reverse()

                    buffer_s, buffer_a, buffer_v_target = np.vstack(buffer_s), np.array(buffer_a), np.vstack(buffer_v_target)
                    feed_dict = {
                        self.AC.s: buffer_s,
                        self.AC.a_his: buffer_a,
                        self.AC.v_target: buffer_v_target,
                    }
                    self.AC.update_global(feed_dict)

                    buffer_s, buffer_a, buffer_r = [], [], []
                    self.AC.pull_global()

                s = s_
                total_step += 1
                if done:
                    if len(global_running_rate) == 0: # record running episode reward
                        global_running_rate.append(ep_r)
                    else:
                        global_running_rate.append(0.99 * global_running_rate[-1] + 0.01 * ep_r)
                    print(
                        self.name,
                        "Ep:", global_episode,
                        "| Ep_r: %i" % global_running_rate[-1],
                          )
                    global_episode += 1
                    break
```

以下代码显示了`main`函数,该函数创建线程池并将工作线程分配给不同的线程:

```py
if __name__ == "__main__":
    session = tf.Session()

    with tf.device("/cpu:0"):
        actor_train = tf.train.RMSPropOptimizer(lr_actor, name='RMSPropOptimiserActor')
        critic_train = tf.train.RMSPropOptimizer(lr_critic, name='RMSPropOptimiserCritic')
        acn_global = ActorCriticNetwork(global_network_scope) # we only need its params
        workers = []
        # Create worker
        for i in range(num_workers):
            i_name = 'W_%i' % i # worker name
            workers.append(Worker(i_name, acn_global))

    coordinator = tf.train.Coordinator()
    session.run(tf.global_variables_initializer())

    worker_threads = []
    for worker in workers:
        job = lambda: worker.work()
        t = threading.Thread(target=job)
        t.start()
        worker_threads.append(t)
    coordinator.join(worker_threads)

    plt.plot(np.arange(len(global_running_rate)), global_running_rate)
    plt.xlabel('step')
    plt.ylabel('Total moving reward')
    plt.show()
```

根据学习的输出的屏幕截图(绿色桨是我们的学习代理):

![](img/fae36238-a4f7-46b5-aeeb-0110aac10b23.png)

# 概要

W
wizardforcel 已提交
496
我们看到,使用并行学习器更新共享模型可以大大改善学习过程。 我们了解了在深度学习中使用异步方法的原因及其不同的变体,包括异步单步 Q 学习,异步单步 SARSA,异步 n 步 Q 学习和异步优势参与者。 我们还学习了实现 A3C 算法的方法,在该方法中,我们使代理学习了 Breakout and Doom 游戏。
W
wizardforcel 已提交
497 498

在接下来的章节中,我们将重点介绍不同的领域,以及如何以及可以应用深度强化学习。