7.md 33.3 KB
Newer Older
W
wizardforcel 已提交
1
# 七、深度强化学习
W
wizardforcel 已提交
2

W
wizardforcel 已提交
3
在本章中,我们将探索**神经网络****NNs**)在使用 PyTorch 进行**强化学习****RL**)上的应用。
W
wizardforcel 已提交
4

W
wizardforcel 已提交
5
RL 是**人工智能****AI**)的领域,与我们在前面各章中介绍的其他机器学习格式不同。 RL 是机器学习算法的子类,它通过最大化环境中的奖励来进行学习。 当问题涉及决策或采取行动时,这些算法很有用。
W
wizardforcel 已提交
6

W
wizardforcel 已提交
7
在本章中,我们将介绍以下秘籍:
W
wizardforcel 已提交
8

W
wizardforcel 已提交
9
*   OpenAI Gym 简介– CartPole
W
wizardforcel 已提交
10
*   DQN 简介
W
wizardforcel 已提交
11
*   实现 DQN 类
W
wizardforcel 已提交
12
*   训练 DQN
W
wizardforcel 已提交
13 14 15
*   深度 GA 简介
*   生成智能体
*   选择智能体
W
wizardforcel 已提交
16
*   使智能体突变
W
wizardforcel 已提交
17 18 19 20
*   训练深度 GA

# 深入了解 RL

W
wizardforcel 已提交
21
**智能体**是任何 RL 问题的核心。 它是 RL 算法的一部分,它处理输入信息以执行操作。 它探索和利用重复试验中的知识,以学习如何最大化奖励。 智能体必须处理的场景称为**环境**,而**动作**是智能体可以在给定环境中进行的动作。 采取行动后从环境中获得的回报称为**奖励**,智能体根据当前状态决定使用下一行动的行动过程称为**策略**。 相对于短期奖励,带有折扣的预期长期回报称为**值****Q 值**与该值相似,但具有附加的当前动作参数。
W
wizardforcel 已提交
22

W
wizardforcel 已提交
23
现在,我们已经了解了 RL 的上下文,让我们理解为什么我们应该使用**深度 RL****DRL**)而不是 RL。 DRL 将 RL 与深度学习相结合,后者使用 NN 解决 RL 问题。
W
wizardforcel 已提交
24 25 26

深度学习算法可以学习抽象出环境状态的细节,然后学习状态的重要特征。 由于深度学习算法仅具有有限数量的参数,因此我们可以使用它将可能的状态压缩为更少的状态,然后使用该新表示形式来选择一个动作。

W
wizardforcel 已提交
27
RL 解决方案涉及将反复试验的结果存储在查找表中,当环境变得越来越复杂时,查找表将变得非常庞大。 深度神经网络可能会学会识别程序员自己必须使用查找表方法手动设计的相同高级特征。
W
wizardforcel 已提交
28 29 30

深度学习是 RL 最近取得突破的背后。 它们展现了代表性的功能,效率,灵活性,并为眼前的问题提供了简化的解决方案。

W
wizardforcel 已提交
31
# OpenAI Gym 简介 – CartPole
W
wizardforcel 已提交
32

W
wizardforcel 已提交
33
在本秘籍中,我们将实现两种不同的 RL 算法。 我们将需要一个环境来运行我们的算法,以便我们创建的模型将获得最大的回报。
W
wizardforcel 已提交
34

W
wizardforcel 已提交
35
我们将使用 OpenAI 的体育馆库,该库是我们可以用来训练模型的环境的集合。 我们将专注于称为`Cartpole-v1`的特定环境。
W
wizardforcel 已提交
36

W
wizardforcel 已提交
37
杆子是一个倒立的摆锤,其重心高于其枢轴点。 为了控制倒立摆的不稳定位置,我们必须将枢轴点移动到质心下方。 目的是施加适当的力,以使柱杆在枢轴点上保持平衡。
W
wizardforcel 已提交
38

W
wizardforcel 已提交
39
下图显示了 OpenAI Gym 的 Cartpole:
W
wizardforcel 已提交
40 41 42

![](img/20943d9b-be6d-4bb1-b4df-0dc04bf896ed.png)

W
wizardforcel 已提交
43
图 1:OpenAI Gym– Cartpole
W
wizardforcel 已提交
44

W
wizardforcel 已提交
45
撑杆通过未激活的接头连接到推车,该接头在水平轨道上自由移动。 摆锤垂直于水平轨道开始。 目的是通过施加力 +1 和 -1 来防止跌落。
W
wizardforcel 已提交
46 47 48 49 50

当杆与垂直位置的夹角超过 15 度时,或者手推车从中心移出的单位超过 2.4 个单位时,则认为手推车未能达到目标。

对于杆保持直立的每个时间步,都会获得+1 的奖励。 现在我们有了上下文,我们将尝试解决 OpenAI Gym 的 Cartpole 问题的代码。

W
wizardforcel 已提交
51
# 准备
W
wizardforcel 已提交
52 53 54 55 56 57 58 59 60

首先,我们需要安装`gym`。 我们将使用`pip`管理器安装它:

```py
pip install gym
```

现在我们已经安装了`gym`,让我们跳入`gym`库。

W
wizardforcel 已提交
61
# 操作步骤
W
wizardforcel 已提交
62

W
wizardforcel 已提交
63
在本秘籍中,我们将了解棘突环境。 请按照以下步骤操作:
W
wizardforcel 已提交
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 108 109 110 111 112 113 114 115 116

1.  我们将从导入`gym`模块开始:

```py
>>import gym
```

2.  接下来,我们将创建环境:

```py
>>env = gym.make('CartPole-v0')
```

3.  接下来,我们将重置环境:

```py
>>env.reset()
```

4.  现在,我们将渲染环境:

```py
>>for _ in range(1000):
    env.render()
    action = env.action_space.sample()
    observation, reward, done, info = env.step(action)
    if done:
        env.reset()
>>env.close()
```

这将为我们提供以下输出:

![](img/836ad5fa-0348-4d74-80bc-958a98387a14.png)

图 2:柱极环境的输出

5.  获取可以执行的可能动作的数量:

```py
>>env.action_space.n
2
```

6.  获取观察状态参数的数量:

```py
>>env.observation_space.shape[0]
4
```

这样,您将看到一个窗口,该窗口显示不稳定的枢轴,并且枢轴点随机移动。

W
wizardforcel 已提交
117
# 工作原理
W
wizardforcel 已提交
118

W
wizardforcel 已提交
119
在本秘籍中,我们探讨了 OpenAI Gym 的一些功能。 我们首先使用`Cartpole-v0`创建一个环境,该环境的最高可得分为 200,然后环境终止。 我们使用`env.reset()`命令将环境置于初始状态,即线杆处于直立位置。 然后,我们开始执行 1000 个步骤的循环,其中使用`render()`渲染了当前环境的开始,并且使用`env.action_space.sample()`为当前状态选择了一个随机动作。 然后,我们将选定的动作传递到环境的`step`方法中。 `step`方法告诉我们在对环境的当前状态执行当前操作时环境发生了什么。 `step`方法返回以下内容:
W
wizardforcel 已提交
120 121 122 123 124 125 126 127 128 129

*   **观察**:这是一个对象,它告诉我们有关环境的新状态。 该对象特定于我们选择的环境。
*   **奖励**:这给了我们所选动作所获得的奖励。 如果是直杆,则直杆直立时每个时间步长为 1.0,否则为 0.0。
*   **完成**:这是一个布尔值,它告诉我们环境是否已达到最终状态,可能是由于手头的任务失败而导致的(如果是小刀杆,则是当刀杆无法保持直立位置时) ,或者从完成手头的任务开始(对于小偷来说,就是达到最大时间步长 200 的时间)。
*   **信息**:对调试有用的诊断信息。

在循环中,每当极点翻倒时,我们都会将环境重置为其初始状态。

最后,我们关闭了环境。 我们使用`env.action_space.n`查看了环境中可能采取的行动,并使用`env.observation_space.shape[0]`查看了处于观察状态的参数。 现在我们已经了解了环境,我们可以开始实现各种深度 RL 算法。

W
wizardforcel 已提交
130
# 更多
W
wizardforcel 已提交
131 132 133

您可以通过更改环境名称来尝试其他环境。 试用`Cartpole-v1`对您来说将是有益的。

W
wizardforcel 已提交
134
# 另见
W
wizardforcel 已提交
135

W
wizardforcel 已提交
136
您可以在[这个页面](http://gym.openai.com/docs/)上了解有关 OpenAI Gym 的更多信息。
W
wizardforcel 已提交
137

W
wizardforcel 已提交
138
# DQN 介绍
W
wizardforcel 已提交
139

W
wizardforcel 已提交
140
在进入下一个秘籍之前,让我们快速看一下 DQN。
W
wizardforcel 已提交
141

W
wizardforcel 已提交
142
DQN 是一种 RL 技术,旨在为给定的观察选择最佳的动作。 有一个 Q 值,它是与每个可能观察到的每个可能动作相关联的给定移动的质量。 在传统的 RL 算法中,此 Q 值来自 Q 表,该表是一个查找表,其中是包含 Q 值的表。 通过反复玩游戏并使用奖励更新表来迭代更新此查找表。 q 学习算法学习要在此表中填充的最佳值。 我们可以简单地查看给定状态的表并选择具有最大 Q 值的动作,以最大程度地赢得游戏。
W
wizardforcel 已提交
143

W
wizardforcel 已提交
144
Q 值可以更新如下:
W
wizardforcel 已提交
145 146 147

![](img/d2d686eb-44fe-4767-b842-d01fca99d3cf.png)

W
wizardforcel 已提交
148
新的 Q 值是两个部分的总和。 第一部分是`(1 - 学习率) * 旧 Q 值`,它是多少旧值将被记住。 学习率为 0 时,不会学习任何新内容,学习率为 1 时,所有旧值都将被忘记。
W
wizardforcel 已提交
149

W
wizardforcel 已提交
150
第二部分是**学习率*(即时行动奖励+最优未来值的折算估计)**,其中学习值是即时奖励加上最优未来值的折算估计。 未来奖励的重要性由折扣系数决定。
W
wizardforcel 已提交
151

W
wizardforcel 已提交
152
通过深度 Q 学习,我们可以使用深度神经网络来预测动作的 Q 值,并使用深度神经网络来选择动作,而不是使用 Q 表查找给定状态下具有最大可能 Q 值的动作。 给定动作的最大 Q 值。
W
wizardforcel 已提交
153

W
wizardforcel 已提交
154
# 操作步骤
W
wizardforcel 已提交
155

W
wizardforcel 已提交
156
在本秘籍中,我们将为卡特彼勒问题定义神经网络模型。 请按照以下步骤操作:
W
wizardforcel 已提交
157 158 159 160 161 162 163 164

1.  首先,我们将导入`torch`

```py
>>import torch
>>import torch.nn as nn
```

W
wizardforcel 已提交
165
2.  接下来,定义一个函数来返回模型:
W
wizardforcel 已提交
166 167 168 169 170 171 172 173 174 175 176 177

```py
def cartpole_model(observation_space, action_space):
    return nn.Sequential(
        nn.Linear(observation_space, 24),
        nn.ReLU(),
        nn.Linear(24, 24),
        nn.ReLU(),
        nn.Linear(24, action_space)
    )
```

W
wizardforcel 已提交
178
此函数返回模型。
W
wizardforcel 已提交
179

W
wizardforcel 已提交
180
# 工作原理
W
wizardforcel 已提交
181

W
wizardforcel 已提交
182
在此秘籍中,我们定义了一个名为`cartpole_model`的函数,该函数接受`observation_ space``action_space`参数,并返回一个神经网络模型。 在这里,我们使用了`torch.nn``nn.Linear``nn.ReLU`中的`Sequential`模块来完成模型。 给定一个观察值,我们使用该模型来训练和预测每个动作的 Q 值。
W
wizardforcel 已提交
183

W
wizardforcel 已提交
184
# 更多
W
wizardforcel 已提交
185

W
wizardforcel 已提交
186
我们还可以训练将状态作为图像并学习从图像预测 Q 值的模型。 完成此操作后,我们将使用`nn.Conv2d()`来使用卷积神经网络。
W
wizardforcel 已提交
187

W
wizardforcel 已提交
188
# 另见
W
wizardforcel 已提交
189

W
wizardforcel 已提交
190
您可以在[这个页面](https://pytorch.org/tutorials/intermediate/reinforcement_q_learning.html#q-network)上查看替代架构。
W
wizardforcel 已提交
191

W
wizardforcel 已提交
192
# 实现 DQN 类
W
wizardforcel 已提交
193

W
wizardforcel 已提交
194
在本秘籍中,我们将使用神经网络完成 DQN。 为此,我们将执行一些关键任务,包括创建目标和策略网络,损失函数和网络优化器,存储学习过程的状态和奖励,预测行为,经验回放以及控制学习过程。 探索率。
W
wizardforcel 已提交
195

W
wizardforcel 已提交
196
# 准备
W
wizardforcel 已提交
197

W
wizardforcel 已提交
198
在完成本秘籍之前,您应该完成本章的“OpenAI Gym 介绍 – Cartpole”秘籍,以便设置`gym`包。
W
wizardforcel 已提交
199

W
wizardforcel 已提交
200
# 操作步骤
W
wizardforcel 已提交
201

W
wizardforcel 已提交
202
在本秘籍中,我们将研究可用于执行 DQN 的所有关键功能。 按着这些次序:
W
wizardforcel 已提交
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218

1.  我们将从必要的导入开始:

```py
>>import random
>>from collections import deque
>>import numpy as np
>>import torch.optim as optim
```

2.  接下来,我们将定义 DQN 类:

```py
>>class DQN:
```

W
wizardforcel 已提交
219
3.  然后,我们将定义构造器:
W
wizardforcel 已提交
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 270 271 272 273 274 275 276 277 278 279 280 281

```py
>>def __init__(self, observation_space, action_space):
        self.exploration_rate = MAX_EXPLORE
        self.action_space = action_space
        self.observation_space = observation_space
        self.memory = deque(maxlen=MEMORY_LEN)
```

4.  接下来,我们将定义`target_net``policy_net`

```py
self.target_net = cartpole_model(self.observation_space, self.action_space)
self.policy_net = cartpole_model(self.observation_space, self.action_space)
```

5.  现在,我们将复制权重:

```py
self.target_net.load_state_dict(self.policy_net.state_dict())
self.target_net.eval()
```

6.  在这里,我们定义损失函数,优化器和限制标志:

```py
self.criterion = nn.MSELoss()
self.optimizer = optim.Adam(self.policy_net.parameters())

self.explore_limit = False
```

7.  接下来,我们将定义`load_memory`方法:

```py
>>def load_memory(self, state, action, reward, next_state, terminal):
        self.memory.append((state, action, reward, next_state, terminal))
```

8.  现在,我们将定义`predict_action`方法:

```py
>>def predict_action(self, state):
        random_number = np.random.rand()

        if random_number < self.exploration_rate:
            return random.randrange(self.action_space)

        q_values = self.target_net(state).detach().numpy()
        return np.argmax(q_values[0])
```

9.  现在,我们将跳至`experience_replay`方法:

```py
>>def experience_replay(self):
        if len(self.memory) < BATCH_SIZE:
            return

        batch = random.sample(self.memory, BATCH_SIZE)
```

W
wizardforcel 已提交
282
0.  现在,让我们使用批量更新 Q 值:
W
wizardforcel 已提交
283 284 285 286 287 288 289 290 291 292 293

```py
for state, action, reward, next_state, terminal in batch:
    q_update = reward
    if not terminal:
        q_update = reward + GAMMA * self.target_net(next_state).max(axis=1)[0] 

    q_values = self.target_net(state)
    q_values[0][action] = q_update
```

W
wizardforcel 已提交
294
1.  接下来,我们计算损失并更新权重:
W
wizardforcel 已提交
295 296 297 298 299 300 301 302

```py
loss = self.criterion(self.policy_net(state), q_values)
self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()
```

W
wizardforcel 已提交
303
2.  我们还将更新探索率:
W
wizardforcel 已提交
304 305 306 307 308 309 310 311 312 313 314

```py
if not self.explore_limit:
    self.exploration_rate *= EXPLORE_DECAY
    if self.exploration_rate < MIN_EXPLORE:
        self.exploration_rate = MIN_EXPLORE
        self.explore_limit = True
```

至此,我们完成了 DQN 课程。

W
wizardforcel 已提交
315
# 工作原理
W
wizardforcel 已提交
316

W
wizardforcel 已提交
317
在本秘籍中,我们完成了 DQN 类,并添加了所有必需的函数来训练 DQN。 在构造器中,我们初始化了探索的初始状态,观察空间和动作空间,然后定义了一个存储单元来保存 DQN 的经验。 我们创建了称为`policy_net``target_net`的软骨模型的两个实例。 我们需要两个网络,因为在训练的每个步骤中,Q 网络的值都会移动,并且如果我们使用不断变化的目标值来调整我们的网络,则该网络可能会由于陷入此变化的目标与估计的 Q 值之间的反馈回路而变得不稳定。 网络值。 如果发生这种情况,值估计将失去控制。 因此,我们使用了两个网络并将`target_net`保持在`eval`模式。 然后,我们使用`MSELoss()`作为损失函数以及`Adam`优化器来更新权重。
W
wizardforcel 已提交
318

W
wizardforcel 已提交
319
`load_memory()`方法中,我们从环境中存储了状态,操作,奖励,下一个状态和终端,以用于训练网络。 我们使用的下一个方法是`predict_action`。 在此方法中,我们使用`np.random.rand()`选择了`random_number`,这为我们提供了`[0,1)`的值。 如果此`random_number`小于当前的`exploration_rate`,则我们选择一个随机动作,该动作由`exploration_rate`控制。 这就是我们合并探索的方式。 但是,如果`random_number`大于`exploration_rate`,则`target_net`会预测`q_values`并选择具有最大 Q 值的动作。
W
wizardforcel 已提交
320

W
wizardforcel 已提交
321
最后,我们实现了`experience_replay`方法。 在这里,我们等待数据点的数量至少为`BATCH_SIZE`,然后从内存中随机采样一批。 这使它可以从一系列不同的观察中学习,而不是从一系列紧密相关的观察中学习。 遍历批量时,我们使用`target_net`根据 Q 值更新公式更新了 Q 值。 然后,我们根据新 Q 值与`policy_net`预测的 Q 值之间的误差训练了`policy_net`。 之后,通过将其乘以探索衰减量逐渐降低探索速率,直到获得最低探索速率。 我们这样做是因为,在训练的初始阶段,我们希望智能体进行更多探索; 但是,随着训练的进行,我们希望算法能够收敛。 至此,我们已经完成了 DQN 类的所有函数。
W
wizardforcel 已提交
322

W
wizardforcel 已提交
323
# 更多
W
wizardforcel 已提交
324 325 326

您可以在 DQN 类中添加一种方法,以用策略网络更新目标网络的权重。

W
wizardforcel 已提交
327
# 另见
W
wizardforcel 已提交
328

W
wizardforcel 已提交
329
您可以在[这个页面](https://github.com/gsurma/cartpole/blob/master/cartpole.py)上的 Keras 中查看此实现。
W
wizardforcel 已提交
330

W
wizardforcel 已提交
331
# 训练 DQN
W
wizardforcel 已提交
332

W
wizardforcel 已提交
333
在此秘籍中,我们将完成训练 DQN 增强算法的过程,并在训练模型后可视化我们的小车。 我们将使用 DQN 类来预测操作并将该操作应用于环境以获得奖励。 此秘籍的目的是使奖励最大化。 我们将使用经验回放来训练我们的模型以预测 Q 值。
W
wizardforcel 已提交
334

W
wizardforcel 已提交
335
# 操作步骤
W
wizardforcel 已提交
336

W
wizardforcel 已提交
337
在本秘籍中,我们将继续使用“DQN 类”秘籍。 按着这些次序:
W
wizardforcel 已提交
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

1.  首先,我们需要导入`gym`

```py
>>import gym
```

2.  接下来,我们需要初始化常量和环境:

```py
>>ENV_NAME = "CartPole-v1"
>>BATCH_SIZE = 20
>>GAMMA = 0.95
>>LEARNING_RATE = 0.001
>>MAX_EXPLORE = 1.0
>>MIN_EXPLORE = 0.01
>>EXPLORE_DECAY = 0.995
>>MEMORY_LEN = 1_000_000
>>UPDATE_FREQ = 10
```

3.  现在,我们需要初始化环境和 DQN:

```py
>>env = gym.make(ENV_NAME)
>>observation_space = env.observation_space.shape[0]
>>action_space = env.action_space.n
>>dqn = DQN(observation_space, action_space)
```

4.  现在,让我们开始训练循环:

```py
>>for i in range(100):
    state = env.reset()
    state = np.reshape(state, [1, observation_space])
    state = torch.from_numpy(state).float()
```

5.  接下来,我们需要逐步了解环境:

```py
score = 0
while True:
    score += 1
    action = dqn.predict_action(state)
    next_state, reward, terminal, info = env.step(action)
```

6.  在这里,我们必须找到下一个状态:

```py
next_state = torch.from_numpy(np.reshape(next_state, [1, observation_space])).float()
dqn.load_memory(state, action, reward, next_state, terminal)
state = next_state
```

7.  我们将使用以下代码结束无限循环:

```py
if terminal:
    print(f'| {i+1:02} | {dqn.exploration_rate:.4f} | {score:03} |')
    break    
```

W
wizardforcel 已提交
403
8.  接下来,我们需要执行一次经验回放:
W
wizardforcel 已提交
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

```py
dqn.experience_replay()
```

9.  接下来,更新权重:

```py
if steps%UPDATE_FREQ == 0:
    dqn.target_net.load_state_dict(dqn.policy_net.state_dict())
```

以下代码块显示了一些示例输出:

```py
| Run | Exploration Rate | Score |
| 001 |       0.9416     | 032   |
| 002 |       0.8956     | 011   |
| 003 |       0.8061     | 022   |
| 004 |       0.7477     | 016   |
| 005 |       0.6936     | 016   |
| 006 |       0.6498     | 014   |
| 007 |       0.5371     | 039   |
.
.
| 072 |       0.0100     | 256   |
| 073 |       0.0100     | 227   |
| 074 |       0.0100     | 225   |
| 075 |       0.0100     | 238   |
| 076 |       0.0100     | 154   |
| 077 |       0.0100     | 285   |
.
.
.
```

W
wizardforcel 已提交
440
0.  现在,我们将定义一个函数,该函数将可视化小车的表现:
W
wizardforcel 已提交
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

```py
>>def play_agent(dqn, env):
    observation = env.reset()
    total_reward=0
```

我们需要使用以下代码来迭代最多 500 个步骤:

```py

    for _ in range(500):
        env.render()
        observation = torch.tensor(observation).type('torch.FloatTensor').view(1,-1)
        q_values = dqn.target_net(observation).detach().numpy()
        action = np.argmax(q_values[0])
        new_observation, reward, done, _ = env.step(action)
        total_reward += reward
        observation = new_observation

        if(done):
            break
```

最后,关闭环境:

```py

    env.close()
    print("Rewards: ",total_reward)
```

W
wizardforcel 已提交
473
调用`play_agent()`函数:
W
wizardforcel 已提交
474 475 476 477 478 479

```py
>>play_agent(dqn, env)
Rewards: 160.0
```

W
wizardforcel 已提交
480
这样,我们就对 DQN 进行了训练和可视化。
W
wizardforcel 已提交
481

W
wizardforcel 已提交
482
# 工作原理
W
wizardforcel 已提交
483

W
wizardforcel 已提交
484
在本秘籍中,我们首先使用超参数导入并初始化环境。 然后,我们创建了`DQN`类的实例,并开始训练循环,重置环境,并对状态数组进行整形,以便可以将其作为浮点张量输入到模型中。 然后,我们开始了一个无限循环,当`env.step()`方法的返回值将`terminal`设置为`True`时终止。 `predict_action()`方法预测在给定环境当前状态下要采取的措施。 然后,我们使用环境的`step()`方法应用了此操作。 我们采用`step`方法返回的下一个状态,并将其从`numpy`转换为`torch.FloatTensor`,并保存了环境参数。 我们一次又一次地将此新状态传递给模型。 我们还每隔几步就将权重从我们的策略网复制到目标网。
W
wizardforcel 已提交
485

W
wizardforcel 已提交
486
最后,我们编写了一个简单的`play_agent`函数来可视化平衡并在重置环境后运行循环。 我们要求目标网络预测每个可能动作的 Q 值,选择具有最高 Q 值的动作,并使用`step()`将其导入环境。 之后,我们不断增加奖励。 此函数返回柱状体直立的时间步数和柱状体执行平衡动作的视频。
W
wizardforcel 已提交
487

W
wizardforcel 已提交
488
# 更多
W
wizardforcel 已提交
489

W
wizardforcel 已提交
490
您可以编写一个函数来绘制算法的表现,并且仅在模型的得分始终在 450-500 之间时才停止训练过程。
W
wizardforcel 已提交
491

W
wizardforcel 已提交
492
# 另见
W
wizardforcel 已提交
493

W
wizardforcel 已提交
494
您可以在[这里](https://github.com/gsurma/cartpole/blob/master/cartpole.py)的 Keras 中看到此实现。
W
wizardforcel 已提交
495

W
wizardforcel 已提交
496
您可以在[这个页面](https://github.com/pytorch/tutorials/blob/master/intermediate_source/reinforcement_q_learning.py)上看到替代实现。
W
wizardforcel 已提交
497 498 499

# Deep GA 简介

W
wizardforcel 已提交
500
在本秘籍中,我们将探讨**深层遗传算法****Deep GA**),并向您展示当将其应用于 RL 时,它是基于梯度方法的竞争替代品。 我们将使用一组随机生成的网络,而不是使用梯度下降来修改其权重的随机生成的网络,而是创建一个一代,然后对其在给定环境中的表现进行评估。 请注意,一代人中的某些网络将比其他人表现更好。 我们将选择表现最佳的网络,并将其保留给下一代网络。 然后,我们将通过复制它们来创建下一代并对其权重进行随机修改。 由于我们将选择权重较小的最佳网络,因此网络的整体表现将不断提高。
W
wizardforcel 已提交
501

W
wizardforcel 已提交
502
在本秘籍中,我们将定义模型的网络维度,该网络维度将用于预测给定状态时应采取的措施。
W
wizardforcel 已提交
503

W
wizardforcel 已提交
504
# 操作步骤
W
wizardforcel 已提交
505

W
wizardforcel 已提交
506
在本秘籍中,我们将完成网络模型定义。 您需要安装 OpenAI 的体育馆库。 按着这些次序:
W
wizardforcel 已提交
507

W
wizardforcel 已提交
508
1.  我们将从导入开始:
W
wizardforcel 已提交
509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528

```py
>>import torch.nn as nn
>>import torch
```

2.  现在,让我们定义一个返回神经网络的函数:

```py
>>def cartpole_model(observation_space, action_space):
    return nn.Sequential(
        nn.Linear(observation_space, 128),
        nn.ReLU(),
        nn.Linear(128, action_space),
        nn.Softmax(dim=1)
    )
```

这样,我们就完成了模型的定义。

W
wizardforcel 已提交
529
# 工作原理
W
wizardforcel 已提交
530

W
wizardforcel 已提交
531
在此秘籍中,函数处于观察状态,并通过两个线性层和一个 ReLU 单元(在隐藏层中有 128 个单元)运行它。 最后一层的输出通过 softmax 函数传递,以将激活转换为概率,并选择概率最高的动作。
W
wizardforcel 已提交
532

W
wizardforcel 已提交
533
# 更多
W
wizardforcel 已提交
534 535 536

对于复杂模型,您还可以具有多个层和不同数量的单元。

W
wizardforcel 已提交
537
# 另见
W
wizardforcel 已提交
538

W
wizardforcel 已提交
539
您也可以将复杂的网络与卷积层一起使用。 在[这个页面](https://github.com/IBM/distributed-evolutionary-ml/blob/master/nn.py)中显示了一个示例。
W
wizardforcel 已提交
540

W
wizardforcel 已提交
541
# 生成智能体
W
wizardforcel 已提交
542

W
wizardforcel 已提交
543
在本秘籍中,我们将着眼于创建一组智能体以开始我们的进化过程,然后初始化这些智能体的权重。 我们将使用这些智能体来评估模型的表现并生成下一代智能体。
W
wizardforcel 已提交
544

W
wizardforcel 已提交
545
# 操作步骤
W
wizardforcel 已提交
546

W
wizardforcel 已提交
547
在此秘籍中,我们将创建给定数量的智能体。 按着这些次序:
W
wizardforcel 已提交
548

W
wizardforcel 已提交
549
1.  首先,我们将定义一个函数来初始化智能体的权重:
W
wizardforcel 已提交
550 551 552 553 554 555 556 557

```py
>>def init_weight(module):
    if((type(module) == nn.Linear)):
            nn.init.xavier_uniform_(module.weight.data)
            module.bias.data.fill_(0.00)
```

W
wizardforcel 已提交
558
2.  现在,我们将定义一个将创建智能体的函数:
W
wizardforcel 已提交
559 560 561 562 563 564

```py
>>def create_agents(num_agents, observation_space, action_space):
      agents = []
```

W
wizardforcel 已提交
565
3.  接下来,我们将创建`num_agents`个智能体:
W
wizardforcel 已提交
566 567 568 569 570 571 572

```py
for _ in range(num_agents):
    agent = cartpole_model(observation_space, action_space)
    agent.apply(init_weight)
```

W
wizardforcel 已提交
573
4.  我们将关闭智能体的每个层的梯度:
W
wizardforcel 已提交
574 575 576 577 578 579 580 581 582

```py
for param in agent.parameters():
    param.requires_grad = False

agent.eval()
agents.append(agent)
```

W
wizardforcel 已提交
583
5.  最后,我们将返回智能体:
W
wizardforcel 已提交
584 585 586 587 588

```py
   return agents
```

W
wizardforcel 已提交
589
现在,我们的智能体已准备好进行评估。
W
wizardforcel 已提交
590

W
wizardforcel 已提交
591
# 工作原理
W
wizardforcel 已提交
592

W
wizardforcel 已提交
593
在此秘籍中,我们编写了两个函数-第一个函数初始化模型层的权重。 对于模型权重,我们使用`torch.nn.init`中的`xavier_uniform`并用`0`填充偏差。 第二个函数创建`num_agents`个智能体,并使用`cartpole_model()`函数返回它们。 我们使用`init_weight`初始化权重。 然后,对于模型的参数,我们禁用梯度计算,将智能体设置为`eval()`模式,然后返回所有智能体。
W
wizardforcel 已提交
594

W
wizardforcel 已提交
595
# 另见
W
wizardforcel 已提交
596

W
wizardforcel 已提交
597
您可以在[这个页面](https://pytorch.org/docs/stable/nn.init.html)上找到有关其他初始化方法的信息。
W
wizardforcel 已提交
598

W
wizardforcel 已提交
599
# 选择智能体
W
wizardforcel 已提交
600

W
wizardforcel 已提交
601
在本秘籍中,我们将基于适应度函数着眼于智能体选择,在我们的案例中,这意味着在平衡卡特波勒方面得分很高。 这意味着我们将传播得分最高的智能体,而忽略其余的智能体。 我们将评估给定一代中的每个智能体,并多次评估它们,以确保奖励不是偶然的。 最后,我们将使用每个智能体的平均分数来确定表现最佳的智能体。
W
wizardforcel 已提交
602

W
wizardforcel 已提交
603
# 操作步骤
W
wizardforcel 已提交
604

W
wizardforcel 已提交
605
在本秘籍中,我们将编写用于评估智能体,多次评估以及按给定序列评估所有智能体的函数。 按着这些次序:
W
wizardforcel 已提交
606

W
wizardforcel 已提交
607
1.  我们将从导入开始:
W
wizardforcel 已提交
608 609 610 611 612

```py
>>import numpy as np
```

W
wizardforcel 已提交
613
2.  接下来,我们需要定义一个函数来评估智能体的表现:
W
wizardforcel 已提交
614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636

```py
>>def eval_agent(agent, env):
    observation = env.reset()
```

3.  接下来,我们需要在最大可能的时间步长内运行循环:

```py
total_reward = 0
for _ in range(MAX_STEP):
    observation = torch.tensor(observation).type('torch.FloatTensor').view(1,-1)
    action_probablity = agent(observation).detach().numpy()[0]
    action = np.random.choice(range(env.action_space.n), 1, p=action_probablity).item()
    next_observation, reward, terminal, _ = env.step(action)
    total_reward += reward
    observation = next_observation

    if terminal:
        break
return total_reward
```

W
wizardforcel 已提交
637
4.  然后,我们需要定义智能体的平均得分:
W
wizardforcel 已提交
638 639 640 641 642 643 644 645 646 647

```py
>>def agent_score(agent, env, runs):
    score = 0
    for _ in range(runs):
        score += eval_agent(agent, env)

    return score/runs 
```

W
wizardforcel 已提交
648
5.  最后,我们评估所有智能体的分数:
W
wizardforcel 已提交
649 650 651 652 653 654 655 656 657 658

```py
>>def all_agent_score(agents, env, runs):
    agents_score = []
    for agent in agents:
        agents_score.append(agent_score(agent, env, runs))

    return agents_score
```

W
wizardforcel 已提交
659
现在,我们的函数已准备好进行评估。
W
wizardforcel 已提交
660

W
wizardforcel 已提交
661
# 工作原理
W
wizardforcel 已提交
662

W
wizardforcel 已提交
663
在本秘籍中,我们完成了深度遗传算法的一些关键函数。 我们研究了三个不同的函数-第一个函数`eval_agent()`与我们在“训练 DQN”秘籍中看到的函数非常相似,其中我们使用了智能体,该智能体是一种神经网络模型,它预测要采取的动作,并执行到`MAX_STEP`(对于`cartpole-v1`为 500)或终端为`True`并返回分数的动作。
W
wizardforcel 已提交
664

W
wizardforcel 已提交
665
然后,我们使用第二个函数`agent_score()`返回指定数量`runs`之上的平均分数,并返回该平均分数,以确保模型的随机表现不佳。 最后一个函数`all_agent_score()`仅循环遍历一代中的所有智能体,并获得一代中所有智能体的平均分数。
W
wizardforcel 已提交
666

W
wizardforcel 已提交
667
# 使智能体突变
W
wizardforcel 已提交
668

W
wizardforcel 已提交
669
在本秘籍中,我们将介绍使智能体突变。 在从给定的一代中选择表现最佳的模型之后,然后再创建下一代智能体,我们将对这些选定智能体的权重进行轻微的随机变化,这使智能体可以探索更多区域以获得更好的回报,就像生物进化的工作原理一样。
W
wizardforcel 已提交
670

W
wizardforcel 已提交
671
# 操作步骤
W
wizardforcel 已提交
672

W
wizardforcel 已提交
673
在此秘籍中,我们将识别精英智能体并向这些智能体添加突变。 按着这些次序:
W
wizardforcel 已提交
674 675 676 677 678 679 680 681 682 683 684 685 686 687 688

1.  首先,我们将导入`copy``numpy`模块:

```py
>>import copy
>>import numpy
```

2.  接下来,我们将定义`mutation`函数:

```py
>>def mutation(agent):
    child_agent = copy.deepcopy(agent)
```

W
wizardforcel 已提交
689
3.  接下来,我们将遍历智能体的参数:
W
wizardforcel 已提交
690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714

```py
for param in agent.parameters():
    mutation_noise = torch.randn_like(param) * MUTATION_POWER
```

4.  然后,我们将变异噪声添加到参数中:

```py
    param += mutation_noise
return child_agent
```

5.  现在,定义`elite`函数:

```py
>>def elite(agents, top_parents_id, env, elite_id=None, top=10):
    selected_elites = top_parents_id[:top]
    if elite_id:
        selected_elites.append(elite_id)

    top_score = np.NINF
    top_id = None
```

W
wizardforcel 已提交
715
6.  接下来,找到`elite`智能体:
W
wizardforcel 已提交
716 717 718 719 720 721 722 723 724 725 726

```py
for agent_id in selected_elites:
    score = agent_score(agents[agent_id], env, runs=5)
    if score > top_score:
       top_score = score
       top_id = agent_id

return copy.deepcopy(agents[top_id])
```

W
wizardforcel 已提交
727
7.  获取子智能体:
W
wizardforcel 已提交
728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743

```py
>>def child_agents(agents, top_parents_id, env, elite_id=None):
    children = []

    agent_count = len(agents)-1    
    selected_agents_id = np.random.choice(top_parents_id, agent_count)
    selected_agents = [agents[id] for id in selected_agents_id]
    child_agents = [mutate(agent) for agent in selected_agents]

    child_agents.append(elite(agents, top_parents_id, env))
    elite_id = len(child_agents)-1

    return child_agents, elite_id
```

W
wizardforcel 已提交
744
8.  获取顶级父级:
W
wizardforcel 已提交
745 746 747 748 749 750

```py
>>def top_parents(scores, num_top_parents):
    return np.argsort(rewards)[::-1][:num_top_parents]
```

W
wizardforcel 已提交
751
在这里,我们定义了识别精英智能体的函数和为智能体增加噪音的函数。
W
wizardforcel 已提交
752

W
wizardforcel 已提交
753
# 工作原理
W
wizardforcel 已提交
754

W
wizardforcel 已提交
755
在本秘籍中,我们研究了四个不同的函数-`mutation`函数为一个智能体创建一个重复项,并为每个参数附加一个受`MUTATION_POWER`限制的小的随机值。 `rand_like`方法从间隔`[0, 1)`上以与`param`相同的大小从均匀分布返回具有随机值的张量。 最后,该函数返回突变的子智能体。 接下来,我们看到`elite`函数返回表现最佳的智能体中最佳智能体的副本。 在`elite`函数中,我们重新评估智能体以确保得分最高的智能体被选为精英,并作为子智能体传递给下一代。
W
wizardforcel 已提交
756

W
wizardforcel 已提交
757
`child_agent`函数生成的子智能体数量与上一代相同,其中一个子智能体是`elite`函数的精英智能体,其余子智能体则使用`np.random.choice`随机选择。 `selected_agents`保留了表现最佳的选定智能体的列表。 在`[mutate(agent) for agent in selected_agents]`步骤中,使用`mutation`函数对得分最高的智能体进行突变。
W
wizardforcel 已提交
758

W
wizardforcel 已提交
759
然后,我们将精英智能体附加到下一代智能体。 最后,`top_parent`函数返回一代中表现最高的智能体的索引。
W
wizardforcel 已提交
760 761 762

# 训练深度 GA

W
wizardforcel 已提交
763
在本秘籍中,我们将完成深度遗传算法的演化,并可视化执行平衡操作的关键。 我们将使用在本章的秘籍中了解到的所有函数,并针对给定的代数运行这些函数。 这将创建智能体,获取他们的分数,选择表现最佳的智能体,并将其突变为下一代。 在几代人中,我们将看到智能体的分数增加。
W
wizardforcel 已提交
764

W
wizardforcel 已提交
765
# 操作步骤
W
wizardforcel 已提交
766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793

按着这些次序:

1.  首先,我们将导入`gym`

```py
>>import gym
```

2.  接下来,我们将声明超参数:

```py
>>ENV_NAME = "CartPole-v1"
>>MAX_STEP = 500
>>MUTATION_POWER = 0.02
>>num_agents = 500
>>num_top_parents = 20
>>generations = 25
>>elite_agent = None
```

3.  之后,我们将创建环境并禁用梯度计算:

```py
>>torch.set_grad_enabled(False)
>>env = gym.make(ENV_NAME)
```

W
wizardforcel 已提交
794
4.  现在,创建智能体:
W
wizardforcel 已提交
795 796 797 798 799

```py
>>agents = create_agents(num_agents, env.observation_space.shape[0], env.action_space.n)
```

W
wizardforcel 已提交
800
5.  接下来,遍历几代:
W
wizardforcel 已提交
801 802 803 804 805 806

```py
>>print(f'| Generation | Score |')
>>for gen in range(generations):
```

W
wizardforcel 已提交
807
现在,我们可以评估智能体:
W
wizardforcel 已提交
808 809 810 811 812

```py
      rewards = all_agent_score(agents, env, 3)
```

W
wizardforcel 已提交
813
通过这样做,我们得到了最好的智能体:
W
wizardforcel 已提交
814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883

```py
      top_parents_id = top_parents(rewards, num_top_parents)
```

反过来,这将创建下一代:

```py
      agents, elite_agent = child_agents(agents, top_parents_id, env, elite_agent)
      print(f'| {gen+1:03} | {np.mean([rewards[i] for i in top_parents_id[:5]]):.4f} |')
```

以下代码块显示了示例输出:

```py
| Generation |     Score       |
|    001     |    47.0667      |
|    002     |    47.3333      |
|    003     |    55.7333      |
|    004     |    58.2667      |
|    005     |    65.3333      |
|    006     |    88.0000      |
|    007     |    105.5333     |
|    008     |    117.4000     |
|    009     |    109.4000     |
|    010     |    137.6667     |
|    011     |    150.3333     |
|    012     |    168.6000     |
|    013     |    176.2667     |
|    014     |    248.0667     |
|    015     |    281.6667     |
|    016     |    327.9333     |
|    017     |    363.5333     |
|    018     |    375.4000     |
|    019     |    387.0000     |
|    020     |    432.2000     |
|    021     |    454.6000     |
|    022     |    445.9333     |
|    023     |    463.7333     |
|    024     |    482.1333     |
|    025     |    496.2000     |
```

6.  最后,我们将形象化地表现出:

```py
>>def play_agent(agent, env):

    observation = env.reset()
    total_reward=0

    for _ in range(MAX_STEP):
        env.render()
        observation = torch.tensor(observation).type('torch.FloatTensor').view(1,-1)
        output_probabilities = agent(observation).detach().numpy()[0]
        action = np.random.choice(range(2), 1, p=output_probabilities).item()
        new_observation, reward, done, _ = env.step(action)
        total_reward += reward
        observation = new_observation

        if(done):
            break

    env.close()
    print("Rewards: ",total_reward)

>>play_agent(agents[num_agents-1],env)
Rewards: 350.0
```

W
wizardforcel 已提交
884
至此,我们已经完成了 DGA 的训练和可视化。
W
wizardforcel 已提交
885

W
wizardforcel 已提交
886
# 工作原理
W
wizardforcel 已提交
887

W
wizardforcel 已提交
888
在此秘籍中,我们改进了深度遗传算法。 根据论文《深度神经进化:遗传算法是用于训练深度神经网络以进行强化学习的一种竞争选择》,我们将超参数`MUTATION_POWER`设置为`0.02`。 我们不需要使用 PyTorch 来进行梯度计算,因为我们不需要依靠梯度下降来改善模型和创建环境。
W
wizardforcel 已提交
889

W
wizardforcel 已提交
890
然后,我们创建了一些智能体,以开始我们的进化,并通过遍历`generations`将它们运行了预定的代数,在其中我们获得了每一代所有智能体的奖励。 然后,我们选择了得分最高的父母,并通过`child_agent`函数传递这些父母指数以获得下一代。 之后,我们打印出前 5 个得分的平均值。
W
wizardforcel 已提交
891

W
wizardforcel 已提交
892
最后,我们使用了与“训练 DQN”秘籍中相同的`play_agent`函数,并进行了较小的修改,以补偿模型预测值的差异。 在这里,我们使用精英模型来显示卡特彼勒的表现,每一代之后。 位于智能体列表的末尾。 这是使用`play_agent`函数完成的。
W
wizardforcel 已提交
893

W
wizardforcel 已提交
894
# 更多
W
wizardforcel 已提交
895

W
wizardforcel 已提交
896
您可以控制深度遗传算法的各种超参数,以查看表现差异并将分数存储到图表中。
W
wizardforcel 已提交
897

W
wizardforcel 已提交
898
# 另见
W
wizardforcel 已提交
899

W
wizardforcel 已提交
900
您可以在[这里](https://arxiv.org/pdf/1712.06567.pdf)[这里](https://github.com/uber-research/deep-neuroevolution)阅读更多有关 DGA 的信息。