百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

详解深度强化学习展现TensorFlow 2.0新特性(代码)

toyiye 2024-06-21 12:38 12 浏览 0 评论


【新智元导读】自TensorFlow官方发布其2.0版本新性能以来,不少人可能对此会有些许困惑。因此博主Roman Ring写了一篇概述性的文章,通过实现深度强化学习算法来具体的展示了TensorFlow 2.0的特性。

正所谓实践出真知。

TensorFlow 2.0的特性公布已经有一段时间了,但很多人对此应当还是一头雾水。

在本教程中,作者通过深度强化学习(DRL)来展示即将到来的TensorFlow 2.0的特性,具体来讲就是通过实现优势actor-critic(演员-评判家,A2C)智能体来解决经典的CartPole-v0环境

虽然作者本文的目标是展示TensorFlow 2.0,但他先介绍了DRL方面的内容,包括对该领域的简要概述。

事实上,由于2.0版本的主要关注点是简化开发人员的工作,即易用性,所以现在正是使用TensorFlow进入DRL的好时机。

本文完整代码资源链接

GitHub:https://github.com/inoryy/tensorflow2-deep-reinforcement-learning

Google Colab:https://colab.research.google.com/drive/12QvW7VZSzoaF-Org-u-N6aiTdBN5ohNA

安装

由于TensorFlow 2.0仍处于试验阶段,建议将其安装在一个独立的(虚拟)环境中。我比较倾向于使用Anaconda,所以以此来做说明:

> conda create -n tf2 python=3.6
> source activate tf2
> pip install tf-nightly-2.0-preview # tf-nightly-gpu-2.0-preview for GPU version

让我们来快速验证一下,一切是否按着预测正常工作:

>>> import tensorflow as tf
>>> print(tf.__version__)
1.13.0-dev20190117
>>> print(tf.executing_eagerly())
True

不必担心1.13.x版本,这只是一个早期预览。此处需要注意的是,默认情况下我们是处于eager模式的!

>>> print(tf.reduce_sum([1, 2, 3, 4, 5]))
tf.Tensor(15, shape=(), dtype=int32)

如果读者对eager模式并不熟悉,那么简单来讲,从本质上它意味着计算是在运行时(runtime)被执行的,而不是通过预编译的图(graph)来执行。读者也可以在TensorFlow文档中对此做深入了解:

https://www.tensorflow.org/tutorials/eager/eager_basics

深度强化学习

一般来说,强化学习是解决顺序决策问题的高级框架。RL智能体通过基于某些观察采取行动来导航环境,并因此获得奖励。大多数RL算法的工作原理是最大化智能体在一个轨迹中所收集的奖励的总和。

基于RL的算法的输出通常是一个策略—一个将状态映射到操作的函数。有效的策略可以像硬编码的no-op操作一样简单。随机策略表示为给定状态下行为的条件概率分布。

Actor-Critic方法

RL算法通常根据优化的目标函数进行分组。基于值的方法(如DQN)通过减少预期状态-动作值(state-action value)的误差来工作。

策略梯度(Policy Gradient)方法通过调整其参数直接优化策略本身,通常是通过梯度下降。完全计算梯度通常是很困难的,所以通常用蒙特卡洛(monte-carlo)方法来估计梯度。

最流行的方法是二者的混合:actor- critical方法,其中智能体策略通过“策略梯度”进行优化,而基于值的方法则用作期望值估计的引导。

深度actor- critical方法

虽然很多基础的RL理论是在表格案例中开发的,但现代RL几乎完全是用函数逼近器完成的,例如人工神经网络。 具体来说,如果策略和值函数用深度神经网络近似,则RL算法被认为是“深度的”。

异步优势(asynchronous advantage) actor- critical

多年来,为了解决样本效率和学习过程的稳定性问题,已经为此做出了一些改进。

首先,梯度用回报(return)来进行加权:折现的未来奖励,这在一定程度上缓解了信用(credit)分配问题,并以无限的时间步长解决了理论问题。

其次,使用优势函数代替原始回报。收益与基线(如状态行动估计)之间的差异形成了优势,可以将其视为与某一平均值相比某一给定操作有多好的衡量标准。

第三,在目标函数中使用额外的熵最大化项,以确保智能体充分探索各种策略。本质上,熵以均匀分布最大化,来测量概率分布的随机性。

最后,并行使用多个worker来加速样品采集,同时在训练期间帮助将它们去相关(decorrelate)。

将所有这些变化与深度神经网络结合起来,我们得到了两种最流行的现代算法:异步优势actor- critical算法,或简称A3C/A2C。两者之间的区别更多的是技术上的而不是理论上的:顾名思义,它归结为并行worker如何估计其梯度并将其传播到模型中。

有了这些,我将结束我们的DRL方法之旅,因为这篇博客文章的重点是TensorFlow 2.0特性。如果您仍然不确定主题,不要担心,通过代码示例,一切都会变得更加清晰明了。

使用TensorFlow 2.0实现Advantage Actor-Critic

让我们看看实现各种现代DRL算法的基础是什么:是actor-critic agent,如前一节所述。为了简单起见,我们不会实现并行worker,尽管大多数代码都支持它。感兴趣的读者可以将这作为一个练习机会。

作为一个测试平台,我们将使用CartPole-v0环境。虽然有点简单,但它仍然是一个很好的选择。

通过Keras模型API实现的策略和价值

首先,让我们在单个模型类下创建策略和价值预估神经网络:

import numpy as np
import tensorflow as tf
import tensorflow.keras.layers as kl
class ProbabilityDistribution(tf.keras.Model):
 def call(self, logits):
 # sample a random categorical action from given logits
 return tf.squeeze(tf.random.categorical(logits, 1), axis=-1)
class Model(tf.keras.Model):
 def __init__(self, num_actions):
 super().__init__('mlp_policy')
 # no tf.get_variable(), just simple Keras API
 self.hidden1 = kl.Dense(128, activation='relu')
 self.hidden2 = kl.Dense(128, activation='relu')
 self.value = kl.Dense(1, name='value')
 # logits are unnormalized log probabilities
 self.logits = kl.Dense(num_actions, name='policy_logits')
 self.dist = ProbabilityDistribution()
 def call(self, inputs):
 # inputs is a numpy array, convert to Tensor
 x = tf.convert_to_tensor(inputs, dtype=tf.float32)
 # separate hidden layers from the same input tensor
 hidden_logs = self.hidden1(x)
 hidden_vals = self.hidden2(x)
 return self.logits(hidden_logs), self.value(hidden_vals)
 def action_value(self, obs):
 # executes call() under the hood
 logits, value = self.predict(obs)
 action = self.dist.predict(logits)
 # a simpler option, will become clear later why we don't use it
 # action = tf.random.categorical(logits, 1)
 return np.squeeze(action, axis=-1), np.squeeze(value, axis=-1)

然后验证模型是否如预期工作:

import gym
env = gym.make('CartPole-v0')
model = Model(num_actions=env.action_space.n)
obs = env.reset()
# no feed_dict or tf.Session() needed at all
action, value = model.action_value(obs[None, :])
print(action, value) # [1] [-0.00145713]

这里需要注意的是:

  • 模型层和执行路径是分别定义的
  • 没有“输入”层,模型将接受原始numpy数组
  • 通过函数API可以在一个模型中定义两个计算路径
  • 模型可以包含一些辅助方法,比如动作采样
  • 在eager模式下,一切都可以从原始numpy数组中运行

Random Agent

现在让我们转到 A2CAgent 类。首先,让我们添加一个 test 方法,该方法运行完整的episode并返回奖励的总和。

class A2CAgent:
 def __init__(self, model):
 self.model = model
 def test(self, env, render=True):
 obs, done, ep_reward = env.reset(), False, 0
 while not done:
 action, _ = self.model.action_value(obs[None, :])
 obs, reward, done, _ = env.step(action)
 ep_reward += reward
 if render:
 env.render()
 return ep_reward

让我们看看模型在随机初始化权重下的得分:

agent = A2CAgent(model)
rewards_sum = agent.test(env)
print("%d out of 200" % rewards_sum) # 18 out of 200

离最佳状态还很远,接下来是训练部分!

损失/目标函数

正如我在DRL概述部分中所描述的,agent通过基于某些损失(目标)函数的梯度下降来改进其策略。在 actor-critic 中,我们针对三个目标进行训练:利用优势加权梯度加上熵最大化来改进策略,以及最小化价值估计误差。

import tensorflow.keras.losses as kls
import tensorflow.keras.optimizers as ko
class A2CAgent:
 def __init__(self, model):
 # hyperparameters for loss terms
 self.params = {'value': 0.5, 'entropy': 0.0001}
 self.model = model
 self.model.compile(
 optimizer=ko.RMSprop(lr=0.0007),
 # define separate losses for policy logits and value estimate
 loss=[self._logits_loss, self._value_loss]
 )
 def test(self, env, render=True):
 # unchanged from previous section
 ...
 def _value_loss(self, returns, value):
 # value loss is typically MSE between value estimates and returns
 return self.params['value']*kls.mean_squared_error(returns, value)
 def _logits_loss(self, acts_and_advs, logits):
 # a trick to input actions and advantages through same API
 actions, advantages = tf.split(acts_and_advs, 2, axis=-1)
 # polymorphic CE loss function that supports sparse and weighted options
 # from_logits argument ensures transformation into normalized probabilities
 cross_entropy = kls.CategoricalCrossentropy(from_logits=True)
 # policy loss is defined by policy gradients, weighted by advantages
 # note: we only calculate the loss on the actions we've actually taken
 # thus under the hood a sparse version of CE loss will be executed
 actions = tf.cast(actions, tf.int32)
 policy_loss = cross_entropy(actions, logits, sample_weight=advantages)
 # entropy loss can be calculated via CE over itself
 entropy_loss = cross_entropy(logits, logits)
 # here signs are flipped because optimizer minimizes
 return policy_loss - self.params['entropy']*entropy_loss

我们完成了目标函数!注意代码非常紧凑:注释行几乎比代码本身还多。

Agent Training Loop

最后,还有训练环路。它有点长,但相当简单:收集样本,计算回报和优势,并在其上训练模型。

class A2CAgent:
 def __init__(self, model):
 # hyperparameters for loss terms
 self.params = {'value': 0.5, 'entropy': 0.0001, 'gamma': 0.99}
 # unchanged from previous section
 ...
 def train(self, env, batch_sz=32, updates=1000):
 # storage helpers for a single batch of data
 actions = np.empty((batch_sz,), dtype=np.int32)
 rewards, dones, values = np.empty((3, batch_sz))
 observations = np.empty((batch_sz,) + env.observation_space.shape)
 # training loop: collect samples, send to optimizer, repeat updates times
 ep_rews = [0.0]
 next_obs = env.reset()
 for update in range(updates):
 for step in range(batch_sz):
 observations[step] = next_obs.copy()
 actions[step], values[step] = self.model.action_value(next_obs[None, :])
 next_obs, rewards[step], dones[step], _ = env.step(actions[step])
 ep_rews[-1] += rewards[step]
 if dones[step]:
 ep_rews.append(0.0)
 next_obs = env.reset()
 _, next_value = self.model.action_value(next_obs[None, :])
 returns, advs = self._returns_advantages(rewards, dones, values, next_value)
 # a trick to input actions and advantages through same API
 acts_and_advs = np.concatenate([actions[:, None], advs[:, None]], axis=-1)
 # performs a full training step on the collected batch
 # note: no need to mess around with gradients, Keras API handles it
 losses = self.model.train_on_batch(observations, [acts_and_advs, returns])
 return ep_rews
 def _returns_advantages(self, rewards, dones, values, next_value):
 # next_value is the bootstrap value estimate of a future state (the critic)
 returns = np.append(np.zeros_like(rewards), next_value, axis=-1)
 # returns are calculated as discounted sum of future rewards
 for t in reversed(range(rewards.shape[0])):
 returns[t] = rewards[t] + self.params['gamma'] * returns[t+1] * (1-dones[t])
 returns = returns[:-1]
 # advantages are returns - baseline, value estimates in our case
 advantages = returns - values
 return returns, advantages
 def test(self, env, render=True):
 # unchanged from previous section
 ...
 def _value_loss(self, returns, value):
 # unchanged from previous section
 ...
 def _logits_loss(self, acts_and_advs, logits):
 # unchanged from previous section
 ...

训练&结果

我们现在已经准备好在CartPole-v0上训练这个single-worker A2C agent!训练过程应该只用几分钟。训练结束后,你应该看到一个智能体成功地实现了200分的目标。

rewards_history = agent.train(env)
print("Finished training, testing...")
print("%d out of 200" % agent.test(env)) # 200 out of 200

在源代码中,我包含了一些额外的帮助程序,可以打印出正在运行的episode的奖励和损失,以及rewards_history。

静态计算图

eager mode效果这么好,你可能会想知道静态图执行是否也可以。当然是可以!而且,只需要多加一行代码就可以启用静态图执行。

with tf.Graph().as_default():
 print(tf.executing_eagerly()) # False
 model = Model(num_actions=env.action_space.n)
 agent = A2CAgent(model)
 rewards_history = agent.train(env)
 print("Finished training, testing...")
 print("%d out of 200" % agent.test(env)) # 200 out of 200

有一点需要注意的是,在静态图执行期间,我们不能只使用 Tensors,这就是为什么我们需要在模型定义期间使用CategoricalDistribution的技巧。

One More Thing…

还记得我说过TensorFlow在默认情况下以eager 模式运行,甚至用一个代码片段来证明它吗?好吧,我骗了你。

如果你使用Keras API来构建和管理模型,那么它将尝试在底层将它们编译为静态图。所以你最终得到的是静态计算图的性能,它具有eager execution的灵活性。

你可以通过model.run_eager标志检查模型的状态,还可以通过将此标志设置为True来强制使用eager mode,尽管大多数情况下可能不需要这样做——如果Keras检测到没有办法绕过eager mode,它将自动退出。

为了说明它确实是作为静态图运行的,这里有一个简单的基准测试:

# create a 100000 samples batch
env = gym.make('CartPole-v0')
obs = np.repeat(env.reset()[None, :], 100000, axis=0)

Eager Benchmark

%%time
model = Model(env.action_space.n)
model.run_eagerly = True
print("Eager Execution: ", tf.executing_eagerly())
print("Eager Keras Model:", model.run_eagerly)
_ = model(obs)
######## Results #######
Eager Execution: True
Eager Keras Model: True
CPU times: user 639 ms, sys: 736 ms, total: 1.38 s

Static Benchmark

%%time
with tf.Graph().as_default():
 model = Model(env.action_space.n)
 print("Eager Execution: ", tf.executing_eagerly())
 print("Eager Keras Model:", model.run_eagerly)
 _ = model.predict(obs)
######## Results #######
Eager Execution: False
Eager Keras Model: False
CPU times: user 793 ms, sys: 79.7 ms, total: 873 ms

Default Benchmark

%%time
model = Model(env.action_space.n)
print("Eager Execution: ", tf.executing_eagerly())
print("Eager Keras Model:", model.run_eagerly)
_ = model.predict(obs)
######## Results #######
Eager Execution: True
Eager Keras Model: False
CPU times: user 994 ms, sys: 23.1 ms, total: 1.02 s

正如你所看到的,eager模式位于静态模式之后,默认情况下,模型确实是静态执行的。

结论

希望本文对理解DRL和即将到来的TensorFlow 2.0有所帮助。请注意,TensorFlow 2.0仍然只是预览版的,一切都有可能发生变化,如果你对TensorFlow有什么特别不喜欢(或喜欢:))的地方,请反馈给开发者。

一个总被提起的问题是,TensorFlow是否比PyTorch更好?也许是,也许不是。两者都是很好的库,所以很难说是哪一个更好。如果你熟悉PyTorch,你可能会注意到TensorFlow 2.0不仅赶上了它,而且还避免了PyTorch API的一些缺陷。

无论最后谁胜出,对于开发者来说,这场竞争给双方都带来了净积极的结果,我很期待看到这些框架未来会变成什么样子。

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码