Políticas

Ver no TensorFlow.org Executar no Google Colab Ver fonte no GitHub Baixar caderno

Introdução

Na terminologia do Aprendizado por Reforço, as políticas mapeiam uma observação do ambiente para uma ação ou distribuição de ações. Em TF-agentes, observações do ambiente estão contidas em uma tupla chamado TimeStep('step_type', 'discount', 'reward', 'observation') , e as políticas de mapear Timesteps para ações ou distribuições sobre ações. A maioria das políticas usar timestep.observation , algumas políticas usar timestep.step_type (por exemplo, para redefinir o estado no início de um episódio em políticas stateful), mas timestep.discount e timestep.reward são geralmente ignoradas.

As políticas estão relacionadas a outros componentes em TF-Agents da seguinte maneira. A maioria das políticas tem uma rede neural para computar ações e / ou distribuições sobre ações de TimeSteps. Os agentes podem conter uma ou mais políticas para finalidades diferentes, por exemplo, uma política principal que está sendo treinada para implantação e uma política barulhenta para coleta de dados. As políticas podem ser salvas / restauradas e podem ser usadas independentemente do agente para coleta de dados, avaliação, etc.

Algumas políticas são mais fáceis de escrever no Tensorflow (por exemplo, aquelas com uma rede neural), enquanto outras são mais fáceis de escrever em Python (por exemplo, seguir um script de ações). Portanto, em agentes TF, permitimos políticas Python e Tensorflow. Além disso, as políticas escritas no TensorFlow podem ter que ser usadas em um ambiente Python ou vice-versa, por exemplo, uma política do TensorFlow é usada para treinamento, mas posteriormente implantada em um ambiente Python de produção. Para tornar isso mais fácil, fornecemos wrappers para conversão entre as políticas do Python e do TensorFlow.

Outra classe interessante de políticas são os wrappers de políticas, que modificam uma determinada política de uma determinada maneira, por exemplo, adicionam um tipo específico de ruído, fazem uma versão gananciosa ou gananciosa de uma política estocástica, misturam várias políticas aleatoriamente, etc.

Configurar

Se você ainda não instalou tf-agents, execute:

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

Políticas Python

A interface para políticas Python é definido em policies/py_policy.PyPolicy . Os principais métodos são:

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

O método mais importante é action(time_step) que mapeia um time_step contendo uma observação a partir do ambiente para um tuplo PolicyStep chamado contendo os seguintes atributos:

  • action : A ação a ser aplicada ao ambiente.
  • state : O estado da política (por exemplo, estado RNN) a ser alimentada para a próxima chamada a acção.
  • info : informações laterais opcionais, tais como probabilidades de registro de ação.

O time_step_spec e action_spec estão as especificações para o passo de tempo de entrada e a ação de saída. Políticas também têm uma reset função que normalmente é usado para repor o estado em políticas stateful. A update(new_policy) função atualiza self direção new_policy .

Agora, vamos dar uma olhada em alguns exemplos de políticas Python.

Exemplo 1: política aleatória de Python

Um exemplo simples de um PyPolicy é o RandomPyPolicy que gera acções aleatórios para o discreta / dado action_spec contínua. A entrada time_step é ignorado.

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)
PolicyStep(action=array([10, -4], dtype=int32), state=(), info=())
PolicyStep(action=array([7, 6], dtype=int32), state=(), info=())

Exemplo 2: política Python com script

Um script jogadas políticas trás um roteiro de acções representadas como uma lista de (num_repeats, action) tuplas. Toda vez que a action função é chamada, retorna a próxima ação na lista até que o número especificado de repetições é feito, e então se move para a próxima ação na lista. A reset método pode ser chamado para iniciar a execução a partir do início da lista.

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)
Executing scripted policy...
PolicyStep(action=array([5, 2], dtype=int32), state=[0, 1], info=())
PolicyStep(action=array([1, 2], dtype=int32), state=[2, 1], info=())
PolicyStep(action=array([1, 2], dtype=int32), state=[2, 2], info=())
Resetting my_scripted_py_policy...
PolicyStep(action=array([5, 2], dtype=int32), state=[0, 1], info=())

Políticas do TensorFlow

As políticas do TensorFlow seguem a mesma interface que as políticas do Python. Vejamos alguns exemplos.

Exemplo 1: Política de TF aleatória

Um RandomTFPolicy pode ser usado para gerar acções aleatórios de acordo com uma dada discreta / contínua action_spec . A entrada time_step é ignorado.

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)
Action:
tf.Tensor([-0.9448042  1.9039011], shape=(2,), dtype=float32)

Exemplo 2: Política de Ator

Uma política ator pode ser criado usando uma rede que mapeia time_steps a ações ou uma rede que mapeia time_steps a distribuições sobre ações.

Usando uma rede de ação

Vamos definir uma rede da seguinte maneira:

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

No TensorFlow, a maioria das camadas de rede são projetadas para operações em lote, portanto, esperamos que os time_steps de entrada sejam agrupados e que a saída da rede também seja agrupada. Além disso, a rede é responsável por produzir ações no intervalo correto de determinado action_spec. Isto é convencionalmente feito utilizando, por exemplo uma activação tanh para a camada final de produto em acções [-1, 1] e, em seguida, escamação e deslocando este para o intervalo correcto como o action_spec de entrada (por exemplo, ver tf_agents/agents/ddpg/networks.actor_network() ).

Agora, podemos criar uma política de ator usando a rede acima.

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)

Podemos aplicá-lo a qualquer lote de time_steps após time_step_spec:

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)
Action:
tf.Tensor(
[[0.9318627 0.7770741 0.8645338]
 [0.9318627 0.7770741 0.8645338]], shape=(2, 3), dtype=float32)
Action distribution:
tfp.distributions.Deterministic("Deterministic", batch_shape=[2, 3], event_shape=[], dtype=float32)

No exemplo acima, criamos a política usando uma rede de ação que produz um tensor de ação. Neste caso, policy.distribution(time_step) é um determinista (delta) de distribuição em torno da saída de policy.action(time_step) . Uma maneira de produzir uma política estocástica é envolver a política do ator em um wrapper de política que adiciona ruído às ações. Outra forma é criar a política de ator usando uma rede de distribuição de ação em vez de uma rede de ação, conforme mostrado abaixo.

Usando uma rede de distribuição de ação

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)
Action:
tf.Tensor(
[[ 0.96731853  1.          1.        ]
 [ 0.94488937 -0.29294527  1.        ]], shape=(2, 3), dtype=float32)
Action distribution:
tfp.distributions.MultivariateNormalDiag("ActionNet_MultivariateNormalDiag", batch_shape=[2], event_shape=[3], dtype=float32)

Observe que no exemplo acima, as ações são cortadas no intervalo da especificação de ação dada [-1, 1]. Isso ocorre porque um argumento do construtor de ActorPolicy clip = True por padrão. Definir como falso retornará ações não cortadas produzidas pela rede.

Políticas estocásticos podem ser convertidos para as políticas determinísticos utilizando, por exemplo, um invólucro GreedyPolicy que escolhe stochastic_policy.distribution().mode() como a sua acção, e uma distribuição / delta determinista em torno desta acção ávido como a sua distribution() .

Exemplo 3: Política Q

A política AQ é usada em agentes como DQN e é baseada em uma rede Q que prevê um valor Q para cada ação discreta. Para um determinado intervalo de tempo, a distribuição de ação na Política Q é uma distribuição categórica criada usando os valores q como 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)
Action:
tf.Tensor([2 2], shape=(2,), dtype=int32)
Action distribution:
tfp.distributions.Categorical("Categorical", batch_shape=[2], event_shape=[], dtype=int32)

Wrappers de política

Um wrapper de política pode ser usado para embrulhar e modificar uma determinada política, por exemplo, adicionar ruído. Wrappers de política são uma subclasse de Política (Python / TensorFlow) e podem, portanto, ser usados ​​como qualquer outra política.

Exemplo: Política gananciosa

Um wrapper ganancioso pode ser usado para embrulhar qualquer política TensorFlow que implementa distribution() . GreedyPolicy.action() retornará wrapped_policy.distribution().mode() E GreedyPolicy.distribution() é uma distribuição / delta determinista torno 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)
Action:
tf.Tensor([1 1], shape=(2,), dtype=int32)
Action distribution:
tfp.distributions.DeterministicWithLogProb("Deterministic", batch_shape=[2], event_shape=[], dtype=int32)