提交 def9c9c4 编写于 作者: F friedhelm

累死我了

上级 72c2b4df
...@@ -482,24 +482,251 @@ Q-学习的主要问题是,它不能很好地扩展到具有许多状态和动 ...@@ -482,24 +482,251 @@ Q-学习的主要问题是,它不能很好地扩展到具有许多状态和动
在本章的剩余部分,我们将使用深度Q学习来训练一个智能点去玩 Ms. Pac-Man,就像DeepMind在2013所做的那样。代码可以很容易地调整,调整后学习去玩大多数Atari游戏的效果都相当好。在大多数动作游戏中,它可以达到超人的技能,但它在长卷游戏中却不太好。 在本章的剩余部分,我们将使用深度Q学习来训练一个智能点去玩 Ms. Pac-Man,就像DeepMind在2013所做的那样。代码可以很容易地调整,调整后学习去玩大多数Atari游戏的效果都相当好。在大多数动作游戏中,它可以达到超人的技能,但它在长卷游戏中却不太好。
##学习去使用深度Q学习来玩Ms. Pac-Man ##学习去使用深度Q学习来玩Ms.Pac-Man
由于我们将使用Atari环境,我们必须首先安装OpenAI gym的Atari环境依赖项。当需要玩其他的时候,我们也会为你想玩的其他OpenAI gym环境安装依赖项。在macOS上,假设你已经安装了Homebrew程序,你需要运行:
$ brew install cmake boost boost-python sdl2 swig wget
在Ubuntu上,输入以下命令(如果使用Python 2,用Python替换Python 3):
$ apt-get install -y python3-numpy python3-dev cmake zlib1g-dev libjpeg-dev\ xvfb libav-tools xorg-dev python3-opengl libboost-all-dev libsdl2-dev swig
随后安装额外的python包:
$ pip3 install --upgrade 'gym[all]'
如果一切顺利,你应该能够创造一个Ms.Pac-Man环境:
![图16-7](../images/chapter_16/16-7.png)
```python ```python
>>> import gym >>> env = gym.make("MsPacman-v0")
>>> env = gym.make("CartPole-v0")
[2016-10-14 16:03:23,199] Making new env: MsPacman-v0
>>> obs = env.reset() >>> obs = env.reset()
>>> obs array([-0.03799846,-0.03288115,0.02337094,0.00720711]) >>> obs.shape # [height, width, channels]
>>> env.render() (210, 160, 3)
>>> env.action_space
Discrete(9)
```
正如你所看到的,有九个离散动作可用,它对应于操纵杆的九个可能位置(左、右、上、下、中、左上等),观察结果是Atari屏幕的截图(见图16-9,左),表示为3D Numpy矩阵。这些图像有点大,所以我们将创建一个小的预处理函数,将图像裁剪并缩小到88×80像素,将其转换成灰度,并提高Ms.Pac-Man的对比度。这将减少DQN所需的计算量,并加快培训练。
```python
def preprocess_observation(obs):
img = obs[1:176:2, ::2] # 裁剪
img = img.mean(axis=2) # 灰度化
img[img==mspacman_color] = 0 # 提升对比度
img = (img - 128) / 128 - 1 # 正则化为-1到1.
return img.reshape(88, 80, 1)
```
过程的结果如图16-9所示(右)。
![图16-9](../images/chapter_16/16-9.png)
接下来,让我们创建DQN。它可以只取一个状态动作对(S,A)作为输入,并输出相应的Q值Q(s,a)的估计值,但是由于动作是离散的,所以使用只使用状态S作为输入并输出每个动作的一个Q值估计的神经网络是更方便的。DQN将由三个卷积层组成,接着是两个全连接层,其中包括输出层(如图16-10)。
![图16-10](../images/chapter_16/16-10.png)
正如我们将看到的,我们将使用的训练算法需要两个具有相同架构(但不同参数)的DQNs:一个将在训练期间用于驱动Ms.Pac-Man(演员),另一个将观看演员并从其试验和错误中学习(评论家)。每隔一定时间,我们就把评论家的反馈给演员看。因为我们需要两个相同的DQNs,所以我们将创建一个 *q_network()* 函数来构建它们:
```python
from tensorflow.contrib.layers import convolution2d, fully_connected
input_height = 88
input_width = 80
input_channels = 1
conv_n_maps = [32, 64, 64]
conv_kernel_sizes = [(8,8), (4,4), (3,3)]
conv_strides = [4, 2, 1]
conv_paddings = ["SAME"]*3
conv_activation = [tf.nn.relu]*3
n_hidden_in = 64 * 11 * 10 # conv3 有 64 个 11x10 映射
each n_hidden = 512
hidden_activation = tf.nn.relu n_outputs = env.action_space.n # 9个离散动作
initializer = tf.contrib.layers.variance_scaling_initializer()
def q_network(X_state, scope):
prev_layer = X_state
conv_layers = []
with tf.variable_scope(scope) as scope:
for n_maps, kernel_size, stride, padding, activation in zip(conv_n_maps, conv_kernel_sizes, conv_strides,conv_paddings, conv_activation):
prev_layer = convolution2d(prev_layer, num_outputs=n_maps, kernel_size=kernel_size,stride=stride, padding=padding, activation_fn=activation,weights_initializer=initializer) conv_layers.append(prev_layer)
last_conv_layer_flat = tf.reshape(prev_layer, shape=[-1, n_hidden_in])
hidden = fully_connected(last_conv_layer_flat, n_hidden,activation_fn=hidden_activation, weights_initializer=initializer) outputs = fully_connected(hidden, n_outputs, activation_fn=None,weights_initializer=initializer)
trainable_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES,scope=scope.name)
trainable_vars_by_name = {var.name[len(scope.name):]: var for var in trainable_vars}
return outputs, trainable_vars_by_name
```
该代码的第一部分定义了DQN体系结构的超参数。然后 *q_network()* 函数创建DQN,将环境的状态 *X_state * 作为输入,以及变量范围的名称。请注意,我们将只使用一个观察来表示环境的状态,因为几乎没有隐藏的状态(除了闪烁的物体和鬼魂的方向)。
*trainable_vars_by_name* 字典收集了所有DQN的可训练变量。当我们创建操作以将评论家DQN复制到演员DQN时,这将是有用的。字典的键是变量的名称,去掉与范围名称相对应的前缀的一部分。看起来像这样:
```python
>>> trainable_vars_by_name
{'/Conv/biases:0': <tensorflow.python.ops.variables.Variable at 0x121cf7b50>, '/Conv/weights:0': <tensorflow.python.ops.variables.Variable...>,
'/Conv_1/biases:0': <tensorflow.python.ops.variables.Variable...>, '/Conv_1/weights:0': <tensorflow.python.ops.variables.Variable...>, '/Conv_2/biases:0': <tensorflow.python.ops.variables.Variable...>, '/Conv_2/weights:0': <tensorflow.python.ops.variables.Variable...>, '/fully_connected/biases:0': <tensorflow.python.ops.variables.Variable...>, '/fully_connected/weights:0': <tensorflow.python.ops.variables.Variable...>, '/fully_connected_1/biases:0': <tensorflow.python.ops.variables.Variable...>, '/fully_connected_1/weights:0': <tensorflow.python.ops.variables.Variable...>}
```
现在让我们为两个DQNs创建输入placeholder,以及复制评论家DQN给演员DQN的操作:
```python
X_state = tf.placeholder(tf.float32, shape=[None, input_height, input_width,input_channels]) actor_q_values, actor_vars = q_network(X_state, scope="q_networks/actor")
critic_q_values, critic_vars = q_network(X_state, scope="q_networks/critic")
copy_ops = [actor_var.assign(critic_vars[var_name]) for var_name, actor_var in actor_vars.items()]
copy_critic_to_actor = tf.group(*copy_ops)
```
让我们后退一步:我们现在有两个DQNs,它们都能够将环境状态(即预处理观察)作为输入,并输出在该状态下的每一个可能的动作的估计Q值。另外,我们有一个名为 *copy_critic_to_actor* 的操作,将评论家DQN的所有可训练变量复制到演员DQN。我们使用TensorFlow的 *tf.group()* 函数将所有赋值操作分组到一个方便的操作中。
演员DQN可以用来扮演Ms.Pac-Man(最初非常糟糕)。正如前面所讨论的,你希望它足够深入地探究游戏,所以通常情况下你想将它用ε贪婪策略或另一种探索策略相结合。
但是评论家DQN呢?它如何去学习玩游戏?简而言之,它将试图使其预测的Q值去匹配演员通过其经验的游戏估计的Q值。具体来说,我们将让演员玩一段时间,把所有的经验保存在回放记忆中。每个内存将是一个5元组(状态、动作、下一状态、奖励、继续),其中“继续”项在游戏结束时等于0,否则为1。接下来,我们定期地从回放存储器中采样一批记忆,并且我们将估计这些存储器中的Q值。最后,我们将使用监督学习技术训练批评家DQN去预测这些Q值。每隔几个训练周期,我们会把评论家DQN复制到演员DQN。就这样!方程16-7示出了用于训练批评家DQN的成本函数:
![图E16-7](../images/chapter_16/E16-7.png)
其中:
S,A,R和S`分别为状态,行为,回报,和在下一状态在存储器中采样来的第i个记忆
m是记忆批处理的长度
Oc和Oa为评论家和演员的参数
Q是评论家DQN对第i记忆状态行为Q值的预测
Q'是演员DQN在选择动作A时的下一状态S'的期望Q值的预测
y是第i记忆的目标Q值,注意,它等同于演员实际观察到的奖励,再加上演员对如果它能发挥最佳效果(据它所知),未来的回报应该是什么的预测。
J为训练评论家DQN的损失函数。正如你所看到的,这只是由演员DQN估计的目标Q值y和评论家DQN对这些Q值的预测之间的均方误差。
回放记忆是可选的,但强烈推荐使它存在。没有它,你会训练评论家DQN使用连续的经验,这可能是相关的。这将引入大量的偏差并且减慢训练算法的收敛性。通过使用回放记忆,我们确保馈送到训练算法的存储器可以是不相关的。
让我们添加批评家DQN的训练操作。首先,我们需要能够计算其在存储器批处理中的每个状态动作的预测Q值。由于DQN为每一个可能的动作输出一个Q值,所以我们只需要保持与在该存储器中实际选择的动作相对应的Q值。为此,我们将把动作转换成一个热向量(记住这是一个满是0的向量,除了第i个索引中的1),并乘以Q值:这将删除所有与记忆动作对应的Q值外的Q值。然后只对第一轴求和,以获得每个存储器所需的Q值预测。
```python
X_action = tf.placeholder(tf.int32, shape=[None])
q_value = tf.reduce_sum(critic_q_values * tf.one_hot(X_action, n_outputs), axis=1, keep_dims=True)
```
接下来,让我们添加训练操作,假设目标Q值将通过placeholder馈入。我们还创建了一个不可训练的变量 *global_step* 。优化器的 *minimize()* 操作将负责增加它。另外,我们创建了 *init* 操作和 *Saver* 。
```python
y = tf.placeholder(tf.float32, shape=[None, 1])
cost = tf.reduce_mean(tf.square(y - q_value))
global_step = tf.Variable(0, trainable=False, name='global_step')
optimizer = tf.train.AdamOptimizer(learning_rate)
training_op = optimizer.minimize(cost, global_step=global_step)
init = tf.global_variables_initializer() saver = tf.train.Saver()
```
这就是训练阶段的情况。在我们查看执行阶段之前,我们需要一些工具。首先,让我们从回放记忆开始。我们将使用一个 *deque* 列表,因为在将数据推送到队列中并在达到最大内存大小时从列表的末尾弹出它们使是非常有效的。我们还将编写一个小函数来随机地从回放记忆中采样一批处理:
```python
from collections import deque
replay_memory_size = 10000
replay_memory = deque([], maxlen=replay_memory_size)
def sample_memories(batch_size):
indices = rnd.permutation(len(replay_memory))[:batch_size]
cols = [[], [], [], [], []] # state, action, reward, next_state, continue
for idx in indices:
memory = replay_memory[idx]
for col, value in zip(cols, memory):
col.append(value)
cols = [np.array(col) for col in cols]
return (cols[0], cols[1], cols[2].reshape(-1, 1), cols[3],cols[4].reshape(-1, 1))
``` ```
接下来,我们需要演员来探索游戏。我们使用ε-贪婪策略,并在50000个训练步骤中逐步将ε从1降低到0.05。
```python
eps_min = 0.05
eps_max = 1.0
eps_decay_steps = 50000
def epsilon_greedy(q_values, step):
epsilon = max(eps_min, eps_max - (eps_max-eps_min) * step/eps_decay_steps)
if rnd.rand() < epsilon:
return rnd.randint(n_outputs) # 随机动作
else:
return np.argmax(q_values) # 最优动作
```
就是这样!我们准备好开始训练了。执行阶段不包含太复杂的东西,但它有点长,所以深呼吸。Are you Ready?来次够!首先,让我们初始化几个变量:
```python
n_steps = 100000 # 总的训练步长
training_start = 1000 # 在游戏1000次迭代后开始训练
training_interval = 3 # 每3次迭代训练一次
save_steps = 50 # 每50训练步长保存模型
copy_steps = 25 # 每25训练步长后复制评论家Q值到演员
discount_rate = 0.95
skip_start = 90 # 跳过游戏开始(只是等待时间)
batch_size = 50
iteration = 0 # 游戏迭代
checkpoint_path = "./my_dqn.ckpt"
done = True # env 需要被重置
```
接下来,让我们打开会话并开始训练:
```python
with tf.Session() as sess:
if os.path.isfile(checkpoint_path):
saver.restore(sess, checkpoint_path)
else:
init.run()
while True:
step = global_step.eval()
if step >= n_steps:
break
iteration += 1
if done: # 游戏结束,重来
obs = env.reset()
for skip in range(skip_start): # 跳过游戏开头
obs, reward, done, info = env.step(0)
state = preprocess_observation(obs)
# 演员评估要干什么
q_values = actor_q_values.eval(feed_dict={X_state: [state]})
action = epsilon_greedy(q_values, step)
# 演员开始玩游戏
obs, reward, done, info = env.step(action)
next_state = preprocess_observation(obs)
# 让我们记下来刚才发生了啥
replay_memory.append((state, action, reward, next_state, 1.0 - done)) state = next_state
if iteration < training_start or iteration % training_interval != 0: continue
# 评论家学习
X_state_val, X_action_val, rewards, X_next_state_val, continues = ( sample_memories(batch_size))
next_q_values = actor_q_values.eval( feed_dict={X_state: X_next_state_val})
max_next_q_values = np.max(next_q_values, axis=1, keepdims=True)
y_val = rewards + continues * discount_rate * max_next_q_values
training_op.run(feed_dict={X_state: X_state_val,X_action: X_action_val, y: y_val})
# 复制评论家Q值到演员
if step % copy_steps == 0:
copy_critic_to_actor.run()
# 保存模型
if step % save_steps == 0:
saver.save(sess, checkpoint_path)
```
如果检查点文件存在,我们就开始恢复模型,否则我们只需初始化变量。然后,主循环开始,其中 *iteration* 计算从程序开始以来游戏步骤的总数,同时 *step* 计算从训练开始的训练步骤的总数(如果恢复了检查点,也恢复全局步骤)。然后代码重置游戏(跳过第一个无聊的等待游戏的步骤,这步骤啥都没有)。接下来,演员评估该做什么,并且玩游戏,并且它的经验被存储在回放记忆中。然后,每隔一段时间(热身期后),评论家开始一个训练步骤。它采样一批回放记忆,并要求演员估计下一状态的所有动作的Q值,并应用方程16-7来计算目标Q值 *y_val* .这里唯一棘手的部分是,我们必须将下一个状态的Q值乘以 *continues* 向量,以将对应于游戏结束的记忆Q值清零。接下来,我们进行训练操作,以提高评论家预测Q值的能力。最后,我们定期将评论家的Q值复制给演员,然后保存模型。
不幸的是,训练过程是非常缓慢的:如果你使用你的破笔记本电脑进行训练的话,想让Ms. Pac-Man变好一点点你得花好几天,如果你看看学习曲线,计算一下每次的平均奖励,你会发现到它是非常嘈杂的。在某些情况下,很长一段时间内可能没有明显的进展,直到智能点学会在合理的时间内生存。如前所述,一种解决方案是将尽可能多的先验知识注入到模型中(例如,通过预处理、奖励等),也可以尝试通过首先训练它来模仿基本策略来引导模型。在任何情况下,RL仍然需要相当多的耐心和调整,但最终结果是非常令人兴奋的。
##练习
1.你怎样去定义强化学习?它与传统的监督以及非监督学习有什么不同?
2.你能想到什么本章没有提到过的强化学习应用?智能体是什么?什么是可能的动作,什么是奖励?
3.什么是衰减率?如果你修改了衰减率那最优策略会变化吗?
4.你怎么去定义强化学习智能体的表现?
5.什么是信用评估问题?它怎么出现的?你怎么解决?
6.使用回放记忆的目的是什么?
7.什么是闭策略RL算法?
8.使用深度Q学习来处理OpenAI gym的 “BypedalWalker-v2.” 。QNET不需要对这个任务使用非常深的网络。
9.使用策略梯度训练智能体扮演Pong,一个著名的Atari 游戏(PANV0在OpenAI gym的 Pong-v0)。注意:个人的观察不足以说明球的方向和速度。一种解决方案是一次将两次观测传递给神经网络策略。为了减少维度和加速训练,你必须预先处理这些图像(裁剪,调整大小,并将它们转换成黑白),并可能将它们合并成单个图像(例如去叠加它们)。
10.如果你有大约100美元备用,你可以购买 Raspberry Pi 3再加上一些便宜的机器人组件,在PI上安装TensorFlow,然后让我们嗨起来~!举个例子,看看Lukas Biewald的这个有趣的帖子,或者看看GoPiGo或BrickPi。为什么不尝试通过使用策略梯度训练机器人来构建真实的cartpole ?或者造一个机器人蜘蛛,让它学会走路;当它接近某个目标时,给予奖励(你需要传感器来测量目标的距离)。唯一的限制就是你的想象力。
练习答案均在附录A
##感谢 ##感谢
在我们结束这本书的最后一章之前,我想感谢你们读到最后一段。我真心希望你能像我写这本书一样愉快地阅读这本书,这对你的项目,或多或少都是有用的。 在我们结束这本书的最后一章之前,我想感谢你们读到最后一段。我真心希望你能像我写这本书一样愉快地阅读这本书,这对你的项目,或多或少都是有用的。
...@@ -510,4 +737,6 @@ Q-学习的主要问题是,它不能很好地扩展到具有许多状态和动 ...@@ -510,4 +737,6 @@ Q-学习的主要问题是,它不能很好地扩展到具有许多状态和动
我最大的希望是,这本书将激励你建立一个美妙的ML应用程序,这将有利于我们所有人!那会是什么呢? 我最大的希望是,这本书将激励你建立一个美妙的ML应用程序,这将有利于我们所有人!那会是什么呢?
2016年11月26日,奥列伦·格伦 *2016年11月26日,奥列伦·格伦*
\ No newline at end of file
*你的支持,是我们每个开源工作者的骄傲~*
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册