Google I / O возвращается 18-20 мая! Зарезервируйте место и составьте свое расписание Зарегистрируйтесь сейчас
Эта страница переведена с помощью Cloud Translation API.
Switch to English

Среды

Посмотреть на TensorFlow.org Запустить в Google Colab Посмотреть исходный код на GitHub Скачать блокнот

Вступление

Цель обучения с подкреплением (RL) - разработать агентов, которые обучаются, взаимодействуя с окружающей средой. В стандартной настройке RL агент получает наблюдение на каждом временном шаге и выбирает действие. Действие применяется к окружению, и оно возвращает награду и новое наблюдение. Агент обучает политику выбирать действия, чтобы максимизировать сумму вознаграждений, также известную как возврат.

В TF-Agents среды могут быть реализованы либо на Python, либо на TensorFlow. Среды Python обычно проще реализовать, понять и отладить, но среды TensorFlow более эффективны и допускают естественное распараллеливание. Самый распространенный рабочий процесс - реализовать среду на Python и использовать одну из наших оболочек для ее автоматического преобразования в TensorFlow.

Давайте сначала посмотрим на среды Python. Среды TensorFlow следуют очень похожему API.

Настраивать

Если вы еще не установили tf-agent или тренажерный зал, запустите:

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

import abc
import tensorflow as tf
import numpy as np

from tf_agents.environments import py_environment
from tf_agents.environments import tf_environment
from tf_agents.environments import tf_py_environment
from tf_agents.environments import utils
from tf_agents.specs import array_spec
from tf_agents.environments import wrappers
from tf_agents.environments import suite_gym
from tf_agents.trajectories import time_step as ts

tf.compat.v1.enable_v2_behavior()

Среды Python

В средах Python есть метод step(action) -> next_time_step который применяет действие к среде и возвращает следующую информацию о следующем шаге:

  1. observation : это часть состояния среды, которую агент может наблюдать, чтобы выбрать свои действия на следующем шаге.
  2. reward : агент учится максимизировать сумму этих вознаграждений на нескольких этапах.
  3. step_type : Взаимодействие с окружающей средой обычно является частью эпизода / эпизода. например, несколько ходов в шахматной игре. step_type может быть FIRST , MID или LAST чтобы указать, является ли этот временной шаг первым, промежуточным или последним шагом в последовательности.
  4. discount : это число с плавающей запятой, показывающее, насколько взвесить вознаграждение на следующем временном шаге относительно вознаграждения на текущем временном шаге.

Они сгруппированы в именованный кортеж TimeStep(step_type, reward, discount, observation) .

Интерфейс, который должны реализовывать все среды Python, находится в environments/py_environment.PyEnvironment . Основные методы:

class PyEnvironment(object):

  def reset(self):
    """Return initial_time_step."""
    self._current_time_step = self._reset()
    return self._current_time_step

  def step(self, action):
    """Apply action and return new time_step."""
    if self._current_time_step is None:
        return self.reset()
    self._current_time_step = self._step(action)
    return self._current_time_step

  def current_time_step(self):
    return self._current_time_step

  def time_step_spec(self):
    """Return time_step_spec."""

  @abc.abstractmethod
  def observation_spec(self):
    """Return observation_spec."""

  @abc.abstractmethod
  def action_spec(self):
    """Return action_spec."""

  @abc.abstractmethod
  def _reset(self):
    """Return initial_time_step."""

  @abc.abstractmethod
  def _step(self, action):
    """Apply action and return new time_step."""

В дополнение к методу step() среды также предоставляют метод reset() который запускает новую последовательность и предоставляет начальный TimeStep . Нет необходимости явно вызывать метод reset . Мы предполагаем, что среды сбрасываются автоматически, либо когда они доходят до конца эпизода, либо когда step () вызывается в первый раз.

Обратите внимание, что подклассы не реализуют step() или reset() напрямую. Вместо этого они переопределяют _step() и _reset() . Временные шаги, возвращаемые этими методами, будут кэшироваться и отображаться через current_time_step() .

Методы observation_spec и action_spec возвращают набор (Bounded)ArraySpecs которые описывают имя, форму, тип данных и диапазоны наблюдений и действий соответственно.

В TF-Agents мы неоднократно ссылаемся на гнезда, которые определяются как любая древовидная структура, состоящая из списков, кортежей, именованных кортежей или словарей. Они могут быть составлены произвольно, чтобы поддерживать структуру наблюдений и действий. Мы обнаружили, что это очень полезно для более сложных сред, где у вас есть много наблюдений и действий.

Использование стандартных сред

TF Agents имеет встроенные оболочки для многих стандартных сред, таких как OpenAI Gym, DeepMind-control и Atari, поэтому они следуют нашему интерфейсу py_environment.PyEnvironment . Эти обернутые среды можно легко загрузить с помощью наших наборов сред. Давайте загрузим среду CartPole из тренажерного зала OpenAI и посмотрим на действие и time_step_spec.

environment = suite_gym.load('CartPole-v0')
print('action_spec:', environment.action_spec())
print('time_step_spec.observation:', environment.time_step_spec().observation)
print('time_step_spec.step_type:', environment.time_step_spec().step_type)
print('time_step_spec.discount:', environment.time_step_spec().discount)
print('time_step_spec.reward:', environment.time_step_spec().reward)
action_spec: BoundedArraySpec(shape=(), dtype=dtype('int64'), name='action', minimum=0, maximum=1)
time_step_spec.observation: BoundedArraySpec(shape=(4,), dtype=dtype('float32'), name='observation', minimum=[-4.8000002e+00 -3.4028235e+38 -4.1887903e-01 -3.4028235e+38], maximum=[4.8000002e+00 3.4028235e+38 4.1887903e-01 3.4028235e+38])
time_step_spec.step_type: ArraySpec(shape=(), dtype=dtype('int32'), name='step_type')
time_step_spec.discount: BoundedArraySpec(shape=(), dtype=dtype('float32'), name='discount', minimum=0.0, maximum=1.0)
time_step_spec.reward: ArraySpec(shape=(), dtype=dtype('float32'), name='reward')

Итак, мы видим, что среда ожидает действий типа int64 в [0, 1] и возвращает TimeSteps где наблюдения представляют собой вектор float32 длины 4, а коэффициент дисконтирования - float32 в [0.0, 1.0]. Теперь давайте попробуем выполнить фиксированное действие (1,) для всего эпизода.

action = np.array(1, dtype=np.int32)
time_step = environment.reset()
print(time_step)
while not time_step.is_last():
  time_step = environment.step(action)
  print(time_step)
TimeStep(step_type=array(0, dtype=int32), reward=array(0., dtype=float32), discount=array(1., dtype=float32), observation=array([ 0.01285449,  0.04769544,  0.01983412, -0.00245379], dtype=float32))
TimeStep(step_type=array(1, dtype=int32), reward=array(1., dtype=float32), discount=array(1., dtype=float32), observation=array([ 0.0138084 ,  0.24252741,  0.01978504, -0.2888134 ], dtype=float32))
TimeStep(step_type=array(1, dtype=int32), reward=array(1., dtype=float32), discount=array(1., dtype=float32), observation=array([ 0.01865895,  0.43736172,  0.01400878, -0.57519126], dtype=float32))
TimeStep(step_type=array(1, dtype=int32), reward=array(1., dtype=float32), discount=array(1., dtype=float32), observation=array([ 0.02740618,  0.6322845 ,  0.00250495, -0.8634283 ], dtype=float32))
TimeStep(step_type=array(1, dtype=int32), reward=array(1., dtype=float32), discount=array(1., dtype=float32), observation=array([ 0.04005187,  0.82737225, -0.01476362, -1.1553226 ], dtype=float32))
TimeStep(step_type=array(1, dtype=int32), reward=array(1., dtype=float32), discount=array(1., dtype=float32), observation=array([ 0.05659932,  1.0226836 , -0.03787007, -1.452598  ], dtype=float32))
TimeStep(step_type=array(1, dtype=int32), reward=array(1., dtype=float32), discount=array(1., dtype=float32), observation=array([ 0.07705299,  1.2182497 , -0.06692202, -1.7568679 ], dtype=float32))
TimeStep(step_type=array(1, dtype=int32), reward=array(1., dtype=float32), discount=array(1., dtype=float32), observation=array([ 0.10141798,  1.4140631 , -0.10205939, -2.069591  ], dtype=float32))
TimeStep(step_type=array(1, dtype=int32), reward=array(1., dtype=float32), discount=array(1., dtype=float32), observation=array([ 0.12969925,  1.6100639 , -0.1434512 , -2.3920157 ], dtype=float32))
TimeStep(step_type=array(1, dtype=int32), reward=array(1., dtype=float32), discount=array(1., dtype=float32), observation=array([ 0.16190052,  1.8061239 , -0.19129153, -2.725115  ], dtype=float32))
TimeStep(step_type=array(2, dtype=int32), reward=array(1., dtype=float32), discount=array(0., dtype=float32), observation=array([ 0.198023  ,  2.002027  , -0.24579382, -3.0695074 ], dtype=float32))

Создание собственной среды Python

Для многих клиентов обычным вариантом использования является применение одного из стандартных агентов (см. Agent /) в TF-Agents для решения их проблемы. Для этого они должны представить свою проблему как среду. Итак, давайте посмотрим, как реализовать среду на Python.

Допустим, мы хотим научить агента играть в следующую карточную игру (вдохновленную Блэк Джеком):

  1. В игре используется бесконечная колода карт с номерами от 1 до 10.
  2. На каждом ходу агент может сделать 2 вещи: получить новую случайную карту или остановить текущий раунд.
  3. Цель состоит в том, чтобы получить сумму ваших карт как можно ближе к 21 в конце раунда, не переходя больше.

Окружение, представляющее игру, может выглядеть так:

  1. Действия: У нас есть 2 действия. Действие 0: получить новую карту, Действие 1: завершить текущий раунд.
  2. Наблюдения: сумма карт в текущем раунде.
  3. Награда: цель состоит в том, чтобы максимально приблизиться к 21, не переходя дальше, поэтому мы можем достичь этого, используя следующую награду в конце раунда: sum_of_cards - 21, если sum_of_cards <= 21, else -21
class CardGameEnv(py_environment.PyEnvironment):

  def __init__(self):
    self._action_spec = array_spec.BoundedArraySpec(
        shape=(), dtype=np.int32, minimum=0, maximum=1, name='action')
    self._observation_spec = array_spec.BoundedArraySpec(
        shape=(1,), dtype=np.int32, minimum=0, name='observation')
    self._state = 0
    self._episode_ended = False

  def action_spec(self):
    return self._action_spec

  def observation_spec(self):
    return self._observation_spec

  def _reset(self):
    self._state = 0
    self._episode_ended = False
    return ts.restart(np.array([self._state], dtype=np.int32))

  def _step(self, action):

    if self._episode_ended:
      # The last action ended the episode. Ignore the current action and start
      # a new episode.
      return self.reset()

    # Make sure episodes don't go on forever.
    if action == 1:
      self._episode_ended = True
    elif action == 0:
      new_card = np.random.randint(1, 11)
      self._state += new_card
    else:
      raise ValueError('`action` should be 0 or 1.')

    if self._episode_ended or self._state >= 21:
      reward = self._state - 21 if self._state <= 21 else -21
      return ts.termination(np.array([self._state], dtype=np.int32), reward)
    else:
      return ts.transition(
          np.array([self._state], dtype=np.int32), reward=0.0, discount=1.0)

Давайте удостоверимся, что мы сделали все правильно, определив вышеуказанное окружение. При создании собственной среды вы должны убедиться, что сгенерированные наблюдения и time_steps соответствуют правильным формам и типам, определенным в ваших спецификациях. Они используются для создания графа TensorFlow и, как таковые, могут создавать проблемы, которые трудно отлаживать, если мы ошибаемся.

Чтобы проверить нашу среду, мы будем использовать случайную политику для генерации действий, и мы будем повторять более 5 эпизодов, чтобы убедиться, что все работает так, как задумано. Ошибка возникает, если мы получаем time_step, который не соответствует спецификациям среды.

environment = CardGameEnv()
utils.validate_py_environment(environment, episodes=5)

Теперь, когда мы знаем, что среда работает так, как задумано, давайте запустим эту среду, используя фиксированную политику: запросите 3 карты и завершите раунд.

get_new_card_action = np.array(0, dtype=np.int32)
end_round_action = np.array(1, dtype=np.int32)

environment = CardGameEnv()
time_step = environment.reset()
print(time_step)
cumulative_reward = time_step.reward

for _ in range(3):
  time_step = environment.step(get_new_card_action)
  print(time_step)
  cumulative_reward += time_step.reward

time_step = environment.step(end_round_action)
print(time_step)
cumulative_reward += time_step.reward
print('Final Reward = ', cumulative_reward)
TimeStep(step_type=array(0, dtype=int32), reward=array(0., dtype=float32), discount=array(1., dtype=float32), observation=array([0], dtype=int32))
TimeStep(step_type=array(1, dtype=int32), reward=array(0., dtype=float32), discount=array(1., dtype=float32), observation=array([2], dtype=int32))
TimeStep(step_type=array(1, dtype=int32), reward=array(0., dtype=float32), discount=array(1., dtype=float32), observation=array([7], dtype=int32))
TimeStep(step_type=array(1, dtype=int32), reward=array(0., dtype=float32), discount=array(1., dtype=float32), observation=array([8], dtype=int32))
TimeStep(step_type=array(2, dtype=int32), reward=array(-13., dtype=float32), discount=array(0., dtype=float32), observation=array([8], dtype=int32))
Final Reward =  -13.0

Окружающие оболочки

Оболочка среды принимает среду Python и возвращает измененную версию среды. И исходная среда, и измененная среда являются экземплярами py_environment.PyEnvironment , и несколько оболочек могут быть объединены в цепочку.

Некоторые распространенные обертки можно найти в environments/wrappers.py . Например:

  1. ActionDiscretizeWrapper : преобразует непрерывное пространство действий в дискретное пространство действий.
  2. RunStats : RunStats статистику запуска среды, такую ​​как количество сделанных шагов, количество завершенных эпизодов и т. Д.
  3. TimeLimit : завершает эпизод после фиксированного количества шагов.

Пример 1: оболочка Action Discretize

InvertedPendulum - это среда PyBullet, которая принимает непрерывные действия в диапазоне [-2, 2] . Если мы хотим обучить дискретного агента действия, такого как DQN, в этой среде, мы должны дискретизировать (квантовать) пространство действий. Именно это и делает ActionDiscretizeWrapper . Сравните action_spec до и после упаковки:

env = suite_gym.load('Pendulum-v0')
print('Action Spec:', env.action_spec())

discrete_action_env = wrappers.ActionDiscretizeWrapper(env, num_actions=5)
print('Discretized Action Spec:', discrete_action_env.action_spec())
Action Spec: BoundedArraySpec(shape=(1,), dtype=dtype('float32'), name='action', minimum=-2.0, maximum=2.0)
Discretized Action Spec: BoundedArraySpec(shape=(), dtype=dtype('int32'), name='action', minimum=0, maximum=4)

Завернутый discrete_action_env является экземпляром py_environment.PyEnvironment и может рассматриваться как обычная среда Python.

Среды TensorFlow

Интерфейс для сред TF определен в environments/tf_environment.TFEnvironment и очень похож на среды Python. Среды TF отличаются от окружений Python несколькими способами:

  • Они генерируют тензорные объекты вместо массивов
  • Среды TF добавляют размерность пакета к тензорам, сгенерированным по сравнению со спецификациями.

Преобразование сред Python в TFEnvs позволяет тензорному потоку распараллеливать операции. Например, можно определить collect_experience_op который собирает данные из среды и добавляет в replay_buffer , и train_op который читает из replay_buffer и обучает агента, и естественным образом запускает их параллельно в TensorFlow.

class TFEnvironment(object):

  def time_step_spec(self):
    """Describes the `TimeStep` tensors returned by `step()`."""

  def observation_spec(self):
    """Defines the `TensorSpec` of observations provided by the environment."""

  def action_spec(self):
    """Describes the TensorSpecs of the action expected by `step(action)`."""

  def reset(self):
    """Returns the current `TimeStep` after resetting the Environment."""
    return self._reset()

  def current_time_step(self):
    """Returns the current `TimeStep`."""
    return self._current_time_step()

  def step(self, action):
    """Applies the action and returns the new `TimeStep`."""
    return self._step(action)

  @abc.abstractmethod
  def _reset(self):
    """Returns the current `TimeStep` after resetting the Environment."""

  @abc.abstractmethod
  def _current_time_step(self):
    """Returns the current `TimeStep`."""

  @abc.abstractmethod
  def _step(self, action):
    """Applies the action and returns the new `TimeStep`."""

Метод current_time_step() возвращает текущий time_step и при необходимости инициализирует среду.

Метод reset() вызывает сброс в среде и возвращает current_step.

Если action не зависит от предыдущего time_step tf.control_dependency необходимо в Graph режиме.

А пока давайте посмотрим, как TFEnvironments .

Создание собственной среды TensorFlow

Это сложнее, чем создание сред в Python, поэтому мы не будем рассматривать это в этой статье. Пример доступен здесь . Более распространенный вариант использования - реализовать вашу среду на Python и обернуть ее в TensorFlow с помощью нашей оболочки TFPyEnvironment (см. Ниже).

Обертывание среды Python в TensorFlow

Мы можем легко превратить любую среду Python в среду TFPyEnvironment оболочку TFPyEnvironment .

env = suite_gym.load('CartPole-v0')
tf_env = tf_py_environment.TFPyEnvironment(env)

print(isinstance(tf_env, tf_environment.TFEnvironment))
print("TimeStep Specs:", tf_env.time_step_spec())
print("Action Specs:", tf_env.action_spec())
True
TimeStep Specs: TimeStep(step_type=TensorSpec(shape=(), dtype=tf.int32, name='step_type'), reward=TensorSpec(shape=(), dtype=tf.float32, name='reward'), discount=BoundedTensorSpec(shape=(), dtype=tf.float32, name='discount', minimum=array(0., dtype=float32), maximum=array(1., dtype=float32)), observation=BoundedTensorSpec(shape=(4,), dtype=tf.float32, name='observation', minimum=array([-4.8000002e+00, -3.4028235e+38, -4.1887903e-01, -3.4028235e+38],
      dtype=float32), maximum=array([4.8000002e+00, 3.4028235e+38, 4.1887903e-01, 3.4028235e+38],
      dtype=float32)))
Action Specs: BoundedTensorSpec(shape=(), dtype=tf.int64, name='action', minimum=array(0), maximum=array(1))

Обратите внимание, что теперь спецификации имеют тип: (Bounded)TensorSpec .

Примеры использования

Простой пример

env = suite_gym.load('CartPole-v0')

tf_env = tf_py_environment.TFPyEnvironment(env)
# reset() creates the initial time_step after resetting the environment.
time_step = tf_env.reset()
num_steps = 3
transitions = []
reward = 0
for i in range(num_steps):
  action = tf.constant([i % 2])
  # applies the action and returns the new TimeStep.
  next_time_step = tf_env.step(action)
  transitions.append([time_step, action, next_time_step])
  reward += next_time_step.reward
  time_step = next_time_step

np_transitions = tf.nest.map_structure(lambda x: x.numpy(), transitions)
print('\n'.join(map(str, np_transitions)))
print('Total reward:', reward.numpy())
[TimeStep(step_type=array([0], dtype=int32), reward=array([0.], dtype=float32), discount=array([1.], dtype=float32), observation=array([[-0.03501577, -0.04957427,  0.00623939,  0.03762257]],
      dtype=float32)), array([0], dtype=int32), TimeStep(step_type=array([1], dtype=int32), reward=array([1.], dtype=float32), discount=array([1.], dtype=float32), observation=array([[-0.03600726, -0.24478514,  0.00699184,  0.33226755]],
      dtype=float32))]
[TimeStep(step_type=array([1], dtype=int32), reward=array([1.], dtype=float32), discount=array([1.], dtype=float32), observation=array([[-0.03600726, -0.24478514,  0.00699184,  0.33226755]],
      dtype=float32)), array([1], dtype=int32), TimeStep(step_type=array([1], dtype=int32), reward=array([1.], dtype=float32), discount=array([1.], dtype=float32), observation=array([[-0.04090296, -0.0497634 ,  0.01363719,  0.04179767]],
      dtype=float32))]
[TimeStep(step_type=array([1], dtype=int32), reward=array([1.], dtype=float32), discount=array([1.], dtype=float32), observation=array([[-0.04090296, -0.0497634 ,  0.01363719,  0.04179767]],
      dtype=float32)), array([0], dtype=int32), TimeStep(step_type=array([1], dtype=int32), reward=array([1.], dtype=float32), discount=array([1.], dtype=float32), observation=array([[-0.04189822, -0.24507822,  0.01447314,  0.33875188]],
      dtype=float32))]
Total reward: [3.]

Целые серии

env = suite_gym.load('CartPole-v0')
tf_env = tf_py_environment.TFPyEnvironment(env)

time_step = tf_env.reset()
rewards = []
steps = []
num_episodes = 5

for _ in range(num_episodes):
  episode_reward = 0
  episode_steps = 0
  while not time_step.is_last():
    action = tf.random.uniform([1], 0, 2, dtype=tf.int32)
    time_step = tf_env.step(action)
    episode_steps += 1
    episode_reward += time_step.reward.numpy()
  rewards.append(episode_reward)
  steps.append(episode_steps)
  time_step = tf_env.reset()

num_steps = np.sum(steps)
avg_length = np.mean(steps)
avg_reward = np.mean(rewards)

print('num_episodes:', num_episodes, 'num_steps:', num_steps)
print('avg_length', avg_length, 'avg_reward:', avg_reward)
num_episodes: 5 num_steps: 138
avg_length 27.6 avg_reward: 27.6