Google I / O kehrt vom 18. bis 20. Mai zurück! Reservieren Sie Platz und erstellen Sie Ihren Zeitplan Registrieren Sie sich jetzt
Diese Seite wurde von der Cloud Translation API übersetzt.
Switch to English

Umgebungen

Ansicht auf TensorFlow.org In Google Colab ausführen Quelle auf GitHub anzeigen Notizbuch herunterladen

Einführung

Das Ziel von Reinforcement Learning (RL) ist es, Agenten zu entwerfen, die durch Interaktion mit einer Umgebung lernen. In der Standardeinstellung RL erhält der Agent bei jedem Zeitschritt eine Beobachtung und wählt eine Aktion aus. Die Aktion wird auf die Umgebung angewendet und die Umgebung gibt eine Belohnung und eine neue Beobachtung zurück. Der Agent trainiert eine Richtlinie zur Auswahl von Aktionen, um die Summe der Belohnungen zu maximieren, die auch als Rendite bezeichnet wird.

In TF-Agents können Umgebungen entweder in Python oder TensorFlow implementiert werden. Python-Umgebungen sind normalerweise einfacher zu implementieren, zu verstehen und zu debuggen, aber TensorFlow-Umgebungen sind effizienter und ermöglichen eine natürliche Parallelisierung. Der häufigste Workflow besteht darin, eine Umgebung in Python zu implementieren und sie mithilfe eines unserer Wrapper automatisch in TensorFlow zu konvertieren.

Schauen wir uns zuerst die Python-Umgebungen an. TensorFlow-Umgebungen folgen einer sehr ähnlichen API.

Einrichten

Wenn Sie noch keine tf-Agenten oder ein Fitnessstudio installiert haben, führen Sie Folgendes aus:

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-Umgebungen

Python-Umgebungen verfügen über eine step(action) -> next_time_step Methode, die eine Aktion auf die Umgebung anwendet und die folgenden Informationen zum nächsten Schritt zurückgibt:

  1. observation : Dies ist der Teil des Umgebungszustands, den der Agent beobachten kann, um seine Aktionen im nächsten Schritt auszuwählen.
  2. reward : Der Agent lernt, die Summe dieser Belohnungen über mehrere Schritte zu maximieren.
  3. step_type : Interaktionen mit der Umgebung sind normalerweise Teil einer Sequenz / Episode. zB mehrere Züge in einer Schachpartie. step_type kann entweder FIRST , MID oder LAST um anzugeben, ob dieser Zeitschritt der erste, mittlere oder letzte Schritt in einer Sequenz ist.
  4. discount : Dies ist ein Float, der angibt, wie viel die Belohnung beim nächsten Zeitschritt im Verhältnis zur Belohnung im aktuellen Zeitschritt gewichtet werden soll.

Diese sind in einem benannten Tupel TimeStep(step_type, reward, discount, observation) .

Die Schnittstelle, die alle Python-Umgebungen implementieren müssen, befindet sich in den environments/py_environment.PyEnvironment . Die Hauptmethoden sind:

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."""

Zusätzlich zur step() -Methode bieten Umgebungen auch eine reset() -Methode, die eine neue Sequenz startet und einen anfänglichen TimeStep . Es ist nicht erforderlich, die reset explizit aufzurufen. Wir gehen davon aus, dass Umgebungen automatisch zurückgesetzt werden, entweder wenn sie das Ende einer Episode erreichen oder wenn step () zum ersten Mal aufgerufen wird.

Beachten Sie, dass Unterklassen step() oder reset() direkt implementieren. Sie überschreiben stattdessen die _step() und _reset() . Die von diesen Methoden zurückgegebenen Zeitschritte werden zwischen current_time_step() zwischengespeichert und current_time_step() .

Die observation_spec und die action_spec Methode geben ein Nest von (Bounded)ArraySpecs , die den Namen, die Form, den Datentyp und die Bereiche der Beobachtungen bzw. Aktionen beschreiben.

In TF-Agenten beziehen wir uns wiederholt auf Nester, die als baumartige Struktur definiert sind, die aus Listen, Tupeln, benannten Tupeln oder Wörterbüchern besteht. Diese können beliebig zusammengesetzt werden, um die Struktur der Beobachtungen und Aktionen aufrechtzuerhalten. Wir haben festgestellt, dass dies für komplexere Umgebungen mit vielen Beobachtungen und Aktionen sehr nützlich ist.

Verwenden von Standardumgebungen

TF Agents verfügt über integrierte Wrapper für viele Standardumgebungen wie OpenAI Gym, DeepMind-Control und Atari, sodass sie unserer Schnittstelle py_environment.PyEnvironment folgen. Diese verpackten Umgebungen können einfach mit unseren Umgebungssuiten geladen werden. Laden Sie die CartPole-Umgebung aus dem OpenAI-Fitnessstudio und sehen Sie sich die Aktion und time_step_spec an.

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')

Wir sehen also, dass die Umgebung in [0, 1] Aktionen vom Typ int64 erwartet und TimeSteps wobei die Beobachtungen ein float32 Vektor der Länge 4 und der Abzinsungsfaktor ein float32 in [0.0, 1.0] sind. Versuchen wir nun, eine feste Aktion (1,) für eine ganze Episode auszuführen.

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))

Erstellen Sie Ihre eigene Python-Umgebung

Für viele Kunden besteht ein häufiger Anwendungsfall darin, einen der Standardagenten (siehe Agenten /) in TF-Agenten auf ihr Problem anzuwenden. Dazu müssen sie ihr Problem als Umgebung gestalten. Schauen wir uns also an, wie eine Umgebung in Python implementiert wird.

Angenommen, wir möchten einen Agenten darin schulen, das folgende (von Black Jack inspirierte) Kartenspiel zu spielen:

  1. Das Spiel wird mit einem unendlichen Kartenspiel von 1 ... 10 gespielt.
  2. In jeder Runde kann der Agent zwei Dinge tun: eine neue zufällige Karte erhalten oder die aktuelle Runde stoppen.
  3. Das Ziel ist es, die Summe Ihrer Karten am Ende der Runde so nahe wie möglich an 21 zu bringen, ohne darüber hinwegzugehen.

Eine Umgebung, die das Spiel darstellt, könnte folgendermaßen aussehen:

  1. Aktionen: Wir haben 2 Aktionen. Aktion 0: Holen Sie sich eine neue Karte und Aktion 1: Beenden Sie die aktuelle Runde.
  2. Beobachtungen: Summe der Karten in der aktuellen Runde.
  3. Belohnung: Das Ziel ist es, so nahe wie möglich an 21 heranzukommen, ohne darüber hinwegzugehen. Dies können wir am Ende der Runde mit der folgenden Belohnung erreichen: sum_of_cards - 21 if 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)

Stellen wir sicher, dass wir alles richtig gemacht haben, um die obige Umgebung zu definieren. Wenn Sie Ihre eigene Umgebung erstellen, müssen Sie sicherstellen, dass die generierten Beobachtungen und Zeitschritte den richtigen Formen und Typen entsprechen, wie sie in Ihren Spezifikationen definiert sind. Diese werden zum Generieren des TensorFlow-Diagramms verwendet und können daher zu schwer zu debuggenden Problemen führen, wenn wir sie falsch verstehen.

Um unsere Umgebung zu validieren, verwenden wir eine zufällige Richtlinie, um Aktionen zu generieren, und wir werden über 5 Episoden iterieren, um sicherzustellen, dass die Dinge wie beabsichtigt funktionieren. Ein Fehler wird ausgelöst, wenn wir einen time_step erhalten, der nicht den Umgebungsspezifikationen entspricht.

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

Nachdem wir nun wissen, dass die Umgebung wie beabsichtigt funktioniert, führen wir diese Umgebung mit einer festen Richtlinie aus: Bitten Sie um 3 Karten und beenden Sie die Runde.

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

Umweltverpackungen

Ein Umgebungs-Wrapper verwendet eine Python-Umgebung und gibt eine geänderte Version der Umgebung zurück. Sowohl die ursprüngliche Umgebung als auch die geänderte Umgebung sind Instanzen von py_environment.PyEnvironment , und mehrere Wrapper können miteinander verkettet werden.

Einige gängige Wrapper finden Sie in environments/wrappers.py . Beispielsweise:

  1. ActionDiscretizeWrapper : Konvertiert einen kontinuierlichen Aktionsraum in einen diskreten Aktionsraum.
  2. RunStats : RunStats der Umgebung, z. B. Anzahl der durchgeführten Schritte, Anzahl der abgeschlossenen Episoden usw.
  3. TimeLimit : Beendet die Episode nach einer festgelegten Anzahl von Schritten.

Beispiel 1: Aktion Diskretisieren Wrapper

InvertedPendulum ist eine PyBullet-Umgebung, die kontinuierliche Aktionen im Bereich [-2, 2] akzeptiert. Wenn wir einen diskreten Aktionsagenten wie DQN in dieser Umgebung trainieren möchten, müssen wir den Aktionsraum diskretisieren (quantisieren). Genau das macht der ActionDiscretizeWrapper . Vergleichen Sie die action_spec vor und nach dem 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)

Das py_environment.PyEnvironment discrete_action_env ist eine Instanz von py_environment.PyEnvironment und kann wie eine normale Python-Umgebung behandelt werden.

TensorFlow-Umgebungen

Die Schnittstelle für TF-Umgebungen ist in den environments/tf_environment.TFEnvironment und sieht den Python-Umgebungen sehr ähnlich. TF-Umgebungen unterscheiden sich in mehreren Punkten von Python-Umgebungen:

  • Sie erzeugen Tensorobjekte anstelle von Arrays
  • TF-Umgebungen fügen den generierten Tensoren im Vergleich zu den Spezifikationen eine Batch-Dimension hinzu.

Durch die Konvertierung der Python-Umgebungen in TFEnvs kann Tensorflow Operationen parallelisieren. Beispielsweise könnte man ein collect_experience_op definieren, das Daten aus der Umgebung sammelt und zu einem replay_buffer , und ein train_op , das aus dem replay_buffer liest und den Agenten trainiert und diese natürlich parallel in TensorFlow ausführt.

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`."""

Die Methode current_time_step current_time_step() gibt den aktuellen time_step zurück und initialisiert die Umgebung bei Bedarf.

Die Methode reset() erzwingt ein Zurücksetzen in der Umgebung und gibt den aktuellen Schritt zurück.

Wenn die action nicht vom vorherigen time_step tf.control_dependency wird im Graph eine tf.control_dependency benötigt.

Lassen Sie uns zunächst untersuchen, wie TFEnvironments erstellt werden.

Erstellen Sie Ihre eigene TensorFlow-Umgebung

Dies ist komplizierter als das Erstellen von Umgebungen in Python, daher werden wir es in dieser Spalte nicht behandeln. Ein Beispiel finden Sie hier . Der häufigste Anwendungsfall besteht darin, Ihre Umgebung in Python zu implementieren und sie mit unserem TFPyEnvironment Wrapper in TensorFlow zu TFPyEnvironment (siehe unten).

Umschließen einer Python-Umgebung in TensorFlow

Mit dem TFPyEnvironment Wrapper können wir jede Python-Umgebung problemlos in eine TensorFlow-Umgebung 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))

Beachten Sie, dass die Spezifikationen jetzt vom Typ sind: (Bounded)TensorSpec .

Anwendungsbeispiele

Einfaches Beispiel

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.]

Ganze Episoden

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