政策

在 TensorFlow.org 上檢視 在 Google Colab 中執行 在 GitHub 上檢視原始碼 下載筆記本

簡介

在強化學習術語中,「政策」會將環境的觀察結果對應到動作或動作分配。在 TF-Agents 中,環境的觀察結果包含在名為 TimeStep('step_type', 'discount', 'reward', 'observation') 的具名元組中,而政策會將時間步對應到動作或動作分配。大多數政策會使用 timestep.observation,有些政策會使用 timestep.step_type (例如,在狀態政策中,於劇集開始時重設狀態),但通常會忽略 timestep.discounttimestep.reward

政策與 TF-Agents 中的其他元件有以下關聯。大多數政策都有一個神經網路,可從 TimeStep 計算動作和/或動作分配。代理程式可以包含一或多個用於不同用途的政策,例如,用於部署的訓練主要政策,以及用於資料收集的雜訊政策。政策可以儲存/還原,而且可以獨立於代理程式用於資料收集、評估等。

有些政策比較容易以 Tensorflow 撰寫 (例如,具有神經網路的政策),而其他政策則比較容易以 Python 撰寫 (例如,遵循動作指令碼)。因此,在 TF agents 中,我們允許 Python 和 Tensorflow 政策。此外,以 TensorFlow 撰寫的政策可能必須在 Python 環境中使用,反之亦然,例如,TensorFlow 政策用於訓練,但稍後會部署在生產 Python 環境中。為了簡化此流程,我們提供了用於在 Python 和 TensorFlow 政策之間轉換的包裝函式。

另一種有趣的政策類別是政策包裝函式,可用於以特定方式修改給定的政策,例如新增特定類型的雜訊、建立隨機政策的貪婪或 epsilon-貪婪版本、隨機混合多個政策等。

設定

如果您尚未安裝 tf-agents,請執行

pip install tf-agents
pip install tf-keras
import os
# Keep using keras-2 (tf-keras) rather than keras-3 (keras).
os.environ['TF_USE_LEGACY_KERAS'] = '1'
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 政策

PyPolicy 的簡單範例是 RandomPyPolicy,此政策會為指定的離散/連續 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)
PolicyStep(action=array([5, 3], dtype=int32), state=(), info=())
PolicyStep(action=array([-4,  3], dtype=int32), state=(), info=())

範例 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)
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=())

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)
Action:
tf.Tensor([9.8276138e-04 2.8761353e+00], shape=(2,), dtype=float32)

範例 2:Actor 政策

Actor 政策可以使用網路建立,此網路可將 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_steps 會進行批次處理,而且網路的輸出也會進行批次處理。此外,網路也負責在指定 action_spec 的正確範圍內產生動作。這通常是透過以下方式完成:例如,對最後一層使用 tanh 啟動函式,以產生 [-1, 1] 範圍內的動作,然後縮放並將其位移到輸入 action_spec 的正確範圍 (例如,請參閱 tf_agents/agents/ddpg/networks.actor_network())。

現在,我們可以利用上述網路建立 actor 政策。

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 的時間步批次

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.85880756 -0.74206954 -0.7772715 ]
 [ 0.85880756 -0.74206954 -0.7772715 ]], shape=(2, 3), dtype=float32)
Action distribution:
tfp.distributions.Deterministic("Deterministic", batch_shape=[2, 3], event_shape=[], dtype=float32)

在上述範例中,我們使用產生動作張量的動作網路建立了政策。在此情況下,policy.distribution(time_step)policy.action(time_step) 輸出周圍的決定性 (delta) 分配。產生隨機政策的一種方式是將 actor 政策包裝在政策包裝函式中,以將雜訊新增至動作。另一種方式是使用動作分配網路 (而非動作網路) 建立 actor 政策,如下所示。

使用動作分配網路

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(
[[-1.          1.          1.        ]
 [-0.7074561   0.1602813   0.34091526]], shape=(2, 3), dtype=float32)
Action distribution:
tfp.distributions.MultivariateNormalDiag("MultivariateNormalDiag", batch_shape=[2], event_shape=[3], dtype=float32)

請注意,在上述範例中,動作會裁剪到指定 action spec [-1, 1] 的範圍。這是因為 ActorPolicy 的建構函式引數預設為 clip=True。將其設為 false 將傳回網路產生的未裁剪動作。

隨機政策可以轉換為決定性政策,例如,使用 GreedyPolicy 包裝函式,此函式會選擇 stochastic_policy.distribution().mode() 作為其動作,並以這個貪婪動作為中心建立決定性/delta 分配,作為其 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)
Action:
tf.Tensor([2 0], shape=(2,), dtype=int32)
Action distribution:
tfp.distributions.Categorical("Categorical", batch_shape=[2], event_shape=[], dtype=int32)

政策包裝函式

政策包裝函式可用於包裝和修改給定的政策,例如新增雜訊。政策包裝函式是 Policy (Python/TensorFlow) 的子類別,因此可以像任何其他政策一樣使用。

範例:貪婪政策

貪婪包裝函式可用於包裝任何實作 distribution() 的 TensorFlow 政策。GreedyPolicy.action() 將傳回 wrapped_policy.distribution().mode(),而 GreedyPolicy.distribution() 是以 GreedyPolicy.action() 為中心建立的決定性/delta 分配

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([0 0], shape=(2,), dtype=int32)
Action distribution:
tfp.distributions.Deterministic("Deterministic", batch_shape=[2], event_shape=[], dtype=int32)