策略#

在 TensorFlow.org 上查看 在 Google Colab 运行 在 Github 上查看源代码 下载笔记本

简介#

在强化学习中,“策略”一词是指将环境的观测值映射到操作或操作分布。在 TF-Agents 中,环境的观测值包含在命名元组 TimeStep('step_type', 'discount', 'reward', 'observation') 内,而策略会将时间步骤映射到操作或操作分布。大多数策略使用 timestep.observation,某些策略会使用 timestep.step_type(例如,在有状态策略中,在片段开始时重置状态),但 timestep.discounttimestep.reward 通常会被忽略。

策略与 TF-Agents 中其他组成部分的相关性如下。大多数策略都通过神经网络来计算时间步骤的操作和/或操作分布。代理可以包含一个或多个用于不同目的的策略,例如采用一个主要策略进行训练用以部署,并采用一个噪声策略进行数据收集。策略可以保存/恢复,并且可以在不依赖于代理的状态下用于数据收集、评估等工作。

一些策略在 Tensorflow 中更容易编写(例如神经网络策略),而其他策略在 Python 中更容易编写(例如基于操作脚本的策略)。 因此,在 TF-Agent 中,Python 和 Tensorflow 策略均支持使用。此外,有时可能需要在 Python 环境中使用通过 TensorFlow 编写的策略,反之亦然(例如使用 TensorFlow 策略进行训练,但之后需要在生产 python 环境中进行部署)。为了简化这一步骤,我们提供了用于在 python 和 TensorFlow 策略之间进行转换的包装器。

与策略有关的另一项有趣内容是策略包装器,它们能够以某种方式修改给定策略,例如添加特定类型的噪声、制作随机策略的贪心或 ϵ 贪心版本、随机混合多个策略等。

设置#

如果尚未安装 TF-Agents,请运行以下命令:

!pip install tf-agents
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import abc
import tensorflow as tf
import tensorflow_probability as tfp
import numpy as np

from tf_agents.specs import array_spec
from tf_agents.specs import tensor_spec
from tf_agents.networks import network

from tf_agents.policies import py_policy
from tf_agents.policies import random_py_policy
from tf_agents.policies import scripted_py_policy

from tf_agents.policies import tf_policy
from tf_agents.policies import random_tf_policy
from tf_agents.policies import actor_policy
from tf_agents.policies import q_policy
from tf_agents.policies import greedy_policy

from tf_agents.trajectories import time_step as ts

Python 策略#

Python 策略的接口在 policies/py_policy.PyPolicy 中进行定义。主要方法为:

class Base(object):

  @abc.abstractmethod
  def __init__(self, time_step_spec, action_spec, policy_state_spec=()):
    self._time_step_spec = time_step_spec
    self._action_spec = action_spec
    self._policy_state_spec = policy_state_spec

  @abc.abstractmethod
  def reset(self, policy_state=()):
    # return initial_policy_state.
    pass

  @abc.abstractmethod
  def action(self, time_step, policy_state=()):
    # return a PolicyStep(action, state, info) named tuple.
    pass

  @abc.abstractmethod
  def distribution(self, time_step, policy_state=()):
    # Not implemented in python, only for TF policies.
    pass

  @abc.abstractmethod
  def update(self, policy):
    # update self to be similar to the input `policy`.
    pass

  @property
  def time_step_spec(self):
    return self._time_step_spec

  @property
  def action_spec(self):
    return self._action_spec

  @property
  def policy_state_spec(self):
    return self._policy_state_spec

最重要的方法为 action(time_step),该方法可将包含环境观测值的 time_step 映射到包含以下特性的 PolicyStep 命名元组:

  • action:应用于环境的操作。

  • state:将被馈入下一个操作调用的策略状态(例如 RNN 状态)。

  • info:可选辅助信息,例如操作日志概率。

time_step_specaction_spec 分别为输入时间步骤规范和输出操作规范。策略还具有 reset 函数,通常用于重置有状态策略中的状态。update(new_policy) 函数可将 self 朝着 new_policy 更新。

现在,让我们看看 Python 策略的两个示例。

示例 1:随机 Python 策略#

RandomPyPolicyPyPolicy 的一个简单示例, 它可以为给定的离散/连续 action_spec 生成随机操作。将忽略输入 time_step

action_spec = array_spec.BoundedArraySpec((2,), np.int32, -10, 10)
my_random_py_policy = random_py_policy.RandomPyPolicy(time_step_spec=None,
    action_spec=action_spec)
time_step = None
action_step = my_random_py_policy.action(time_step)
print(action_step)
action_step = my_random_py_policy.action(time_step)
print(action_step)

示例 2:脚本化 Python 策略#

脚本化策略可回放以 (num_repeats, action) 元组列表形式呈现的操作脚本。每次调用 action 函数时,它都会返回列表内的下一个操作并使其完成指定的重复次数,然后再继续执行列表中的下一个操作。调用 reset 方法即可从列表开头重新开始执行。

action_spec = array_spec.BoundedArraySpec((2,), np.int32, -10, 10)
action_script = [(1, np.array([5, 2], dtype=np.int32)), 
                 (0, np.array([0, 0], dtype=np.int32)), # Setting `num_repeats` to 0 will skip this action.
                 (2, np.array([1, 2], dtype=np.int32)), 
                 (1, np.array([3, 4], dtype=np.int32))]

my_scripted_py_policy = scripted_py_policy.ScriptedPyPolicy(
    time_step_spec=None, action_spec=action_spec, action_script=action_script)

policy_state = my_scripted_py_policy.get_initial_state()
time_step = None
print('Executing scripted policy...')
action_step = my_scripted_py_policy.action(time_step, policy_state)
print(action_step)
action_step= my_scripted_py_policy.action(time_step, action_step.state)
print(action_step)
action_step = my_scripted_py_policy.action(time_step, action_step.state)
print(action_step)

print('Resetting my_scripted_py_policy...')
policy_state = my_scripted_py_policy.get_initial_state()
action_step = my_scripted_py_policy.action(time_step, policy_state)
print(action_step)

TensorFlow 策略#

TensorFlow 策略采用与 Python 策略相同的接口。让我们看几个例子。

示例 1:随机 TF 策略#

使用 RandomTFPolicy 可根据给定的离散/连续 action_spec 生成随机操作。将忽略输入 time_step

action_spec = tensor_spec.BoundedTensorSpec(
    (2,), tf.float32, minimum=-1, maximum=3)
input_tensor_spec = tensor_spec.TensorSpec((2,), tf.float32)
time_step_spec = ts.time_step_spec(input_tensor_spec)

my_random_tf_policy = random_tf_policy.RandomTFPolicy(
    action_spec=action_spec, time_step_spec=time_step_spec)
observation = tf.ones(time_step_spec.observation.shape)
time_step = ts.restart(observation)
action_step = my_random_tf_policy.action(time_step)

print('Action:')
print(action_step.action)

示例 2:参与者策略#

可以使用将 time_steps 映射到操作的网络或将 time_steps 映射到操作分布的网络创建参与者策略。

使用操作网络#

让我们按照以下方式定义网络:

class ActionNet(network.Network):

  def __init__(self, input_tensor_spec, output_tensor_spec):
    super(ActionNet, self).__init__(
        input_tensor_spec=input_tensor_spec,
        state_spec=(),
        name='ActionNet')
    self._output_tensor_spec = output_tensor_spec
    self._sub_layers = [
        tf.keras.layers.Dense(
            action_spec.shape.num_elements(), activation=tf.nn.tanh),
    ]

  def call(self, observations, step_type, network_state):
    del step_type

    output = tf.cast(observations, dtype=tf.float32)
    for layer in self._sub_layers:
      output = layer(output)
    actions = tf.reshape(output, [-1] + self._output_tensor_spec.shape.as_list())

    # Scale and shift actions to the correct range if necessary.
    return actions, network_state

在 TensorFlow 中,大多数网络层都是针对批量运算而设计的,因此我们希望输入 time_step 得到批处理,网络的输出也得到批处理。另外,网络还负责在给定 action_spec 的正确范围内生成操作。常用方法是对最后一层使用 tanh 激活函数以在 [-1, 1] 区间内生成操作,然后将其缩放并移动到正确的范围作为输入 action_spec(例如,请参阅 tf_agents/agents/ddpg/networks.actor_network())。

现在,我们可以使用以上网络创建参与者策略。

input_tensor_spec = tensor_spec.TensorSpec((4,), tf.float32)
time_step_spec = ts.time_step_spec(input_tensor_spec)
action_spec = tensor_spec.BoundedTensorSpec((3,),
                                            tf.float32,
                                            minimum=-1,
                                            maximum=1)

action_net = ActionNet(input_tensor_spec, action_spec)

my_actor_policy = actor_policy.ActorPolicy(
    time_step_spec=time_step_spec,
    action_spec=action_spec,
    actor_network=action_net)

我们可以将其应用于遵循 time_step_spec 的任何 time_step 批次:

batch_size = 2
observations = tf.ones([2] + time_step_spec.observation.shape.as_list())

time_step = ts.restart(observations, batch_size)

action_step = my_actor_policy.action(time_step)
print('Action:')
print(action_step.action)

distribution_step = my_actor_policy.distribution(time_step)
print('Action distribution:')
print(distribution_step.action)

在以上示例中,我们是使用生成操作张量的操作网络来创建的策略。在这种情况下,policy.distribution(time_step) 是围绕 policy.action(time_step) 输出的确定性(增量)分布。生成随机策略的一种方法是在策略包装器内包装参与者策略,为操作增加噪声。另一种方法是使用操作分布网络而非操作网络来创建参与者策略,如下所示。

使用操作分布网络#

class ActionDistributionNet(ActionNet):

  def call(self, observations, step_type, network_state):
    action_means, network_state = super(ActionDistributionNet, self).call(
        observations, step_type, network_state)

    action_std = tf.ones_like(action_means)
    return tfp.distributions.MultivariateNormalDiag(action_means, action_std), network_state


action_distribution_net = ActionDistributionNet(input_tensor_spec, action_spec)

my_actor_policy = actor_policy.ActorPolicy(
    time_step_spec=time_step_spec,
    action_spec=action_spec,
    actor_network=action_distribution_net)

action_step = my_actor_policy.action(time_step)
print('Action:')
print(action_step.action)
distribution_step = my_actor_policy.distribution(time_step)
print('Action distribution:')
print(distribution_step.action)

请注意,上例中的操作被裁剪到给定操作规范 [-1, 1] 区间内。这是因为 ActorPolicy clip 的构造函数参数的默认值为 True。将其设置为 False 将返回网络生成的未裁剪操作。

可以使用 GreedyPolicy 包装器将随机策略转换为确定性策略,该包装器选择 stochastic_policy.distribution().mode() 作为其操作,并选择围绕此贪心操作的确定性/增量分布作为其 distribution()

示例 3:Q 策略#

Q 策略适用于 DQN 一类的代理,并基于预测每个离散操作 Q 值的 Q 网络。对于给定的时间步骤,Q 策略中的操作分布是使用 Q 值作为 logits 创建的分类分布。

input_tensor_spec = tensor_spec.TensorSpec((4,), tf.float32)
time_step_spec = ts.time_step_spec(input_tensor_spec)
action_spec = tensor_spec.BoundedTensorSpec((),
                                            tf.int32,
                                            minimum=0,
                                            maximum=2)
num_actions = action_spec.maximum - action_spec.minimum + 1


class QNetwork(network.Network):

  def __init__(self, input_tensor_spec, action_spec, num_actions=num_actions, name=None):
    super(QNetwork, self).__init__(
        input_tensor_spec=input_tensor_spec,
        state_spec=(),
        name=name)
    self._sub_layers = [
        tf.keras.layers.Dense(num_actions),
    ]

  def call(self, inputs, step_type=None, network_state=()):
    del step_type
    inputs = tf.cast(inputs, tf.float32)
    for layer in self._sub_layers:
      inputs = layer(inputs)
    return inputs, network_state


batch_size = 2
observation = tf.ones([batch_size] + time_step_spec.observation.shape.as_list())
time_steps = ts.restart(observation, batch_size=batch_size)

my_q_network = QNetwork(
    input_tensor_spec=input_tensor_spec,
    action_spec=action_spec)
my_q_policy = q_policy.QPolicy(
    time_step_spec, action_spec, q_network=my_q_network)
action_step = my_q_policy.action(time_steps)
distribution_step = my_q_policy.distribution(time_steps)

print('Action:')
print(action_step.action)

print('Action distribution:')
print(distribution_step.action)

策略包装器#

策略包装器可用于包装和修改给定策略,例如增加噪声。策略包装器是策略 (Python/TensorFlow) 的子类,因此可以像其他任何策略一样使用。

示例:贪心策略#

贪心包装器可用于包装实现 distribution() 的任何 TensorFlow 策略。GreedyPolicy.action() 将返回 wrapped_policy.distribution().mode(),而 GreedyPolicy.distribution() 是围绕 GreedyPolicy.action() 的确定性/增量分布:

my_greedy_policy = greedy_policy.GreedyPolicy(my_q_policy)

action_step = my_greedy_policy.action(time_steps)
print('Action:')
print(action_step.action)

distribution_step = my_greedy_policy.distribution(time_steps)
print('Action distribution:')
print(distribution_step.action)