Halaman ini diterjemahkan oleh Cloud Translation API.
Switch to English

Lingkungan

Lihat di TensorFlow.org Jalankan di Google Colab Lihat sumber di GitHub Unduh buku catatan

pengantar

Tujuan dari Reinforcement Learning (RL) adalah merancang agen yang belajar dengan berinteraksi dengan lingkungan. Dalam pengaturan RL standar, agen menerima pengamatan di setiap langkah waktu dan memilih tindakan. Tindakan tersebut diterapkan pada lingkungan dan lingkungan mengembalikan penghargaan dan pengamatan baru. Agen melatih kebijakan untuk memilih tindakan guna memaksimalkan jumlah hadiah, juga dikenal sebagai pengembalian.

Di TF-Agents, lingkungan dapat diimplementasikan dengan Python atau TensorFlow. Lingkungan Python biasanya lebih mudah diimplementasikan, dipahami, dan di-debug, tetapi lingkungan TensorFlow lebih efisien dan memungkinkan paralelisasi alami. Alur kerja yang paling umum adalah mengimplementasikan lingkungan dengan Python dan menggunakan salah satu pembungkus kami untuk otomatis mengubahnya menjadi TensorFlow.

Mari kita lihat lingkungan Python terlebih dahulu. Lingkungan TensorFlow mengikuti API yang sangat mirip.

Mendirikan

Jika Anda belum menginstal tf-agent atau gym, jalankan:

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

Lingkungan Python

Lingkungan Python memiliki metode step(action) -> next_time_step yang menerapkan tindakan ke lingkungan, dan mengembalikan informasi berikut tentang langkah berikutnya:

  1. observation : Ini adalah bagian dari status lingkungan yang dapat diamati oleh agen untuk memilih tindakannya pada langkah berikutnya.
  2. reward : Agen sedang belajar memaksimalkan jumlah reward ini dalam beberapa langkah.
  3. step_type : Interaksi dengan lingkungan biasanya merupakan bagian dari urutan / episode. misalnya banyak gerakan dalam permainan catur. step_type dapat berupa FIRST , MID atau LAST untuk menunjukkan apakah langkah waktu ini adalah langkah pertama, menengah, atau terakhir secara berurutan.
  4. discount : Ini adalah float yang menunjukkan seberapa banyak bobot hadiah pada langkah waktu berikutnya relatif terhadap hadiah pada langkah waktu saat ini.

Ini dikelompokkan ke dalam tuple TimeStep(step_type, reward, discount, observation) bernama TimeStep(step_type, reward, discount, observation) .

Antarmuka yang harus diterapkan oleh semua lingkungan Python ada di environments/py_environment.PyEnvironment . Metode utamanya adalah:

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

Selain metode step() , lingkungan juga menyediakan metode reset() yang memulai urutan baru dan menyediakan TimeStep awal. Tidak perlu memanggil metode reset secara eksplisit. Kami berasumsi bahwa lingkungan disetel ulang secara otomatis, baik saat lingkungan mencapai akhir episode atau saat step () dipanggil pertama kali.

Perhatikan bahwa subclass tidak mengimplementasikan step() atau reset() secara langsung. Mereka malah mengganti metode _step() dan _reset() . Langkah waktu yang dikembalikan dari metode ini akan di-cache dan diekspos melalui current_time_step() .

Metode observation_spec dan action_spec mengembalikan sarang (Bounded)ArraySpecs -masing menjelaskan nama, bentuk, tipe data, dan rentang observasi dan tindakan.

Dalam TF-Agents kami berulang kali merujuk ke sarang yang didefinisikan sebagai struktur seperti pohon apa pun yang terdiri dari daftar, tupel, tupel bernama, atau kamus. Ini dapat disusun secara sewenang-wenang untuk mempertahankan struktur pengamatan dan tindakan. Kami menemukan ini sangat berguna untuk lingkungan yang lebih kompleks di mana Anda memiliki banyak pengamatan dan tindakan.

Menggunakan Lingkungan Standar

Agen TF memiliki pembungkus bawaan untuk banyak lingkungan standar seperti OpenAI Gym, DeepMind-control dan Atari, sehingga mereka mengikuti antarmuka py_environment.PyEnvironment kami. Lingkungan yang dibungkus ini dapat dengan mudah dimuat menggunakan rangkaian lingkungan kami. Mari muat lingkungan CartPole dari gym OpenAI dan lihat aksi dan 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')

Jadi kita melihat bahwa lingkungan mengharapkan tindakan tipe int64 di [0, 1] dan mengembalikan TimeSteps mana pengamatan adalah vektor float32 panjang 4 dan faktor diskon adalah float32 di [0.0, 1.0]. Sekarang, mari kita coba mengambil tindakan tetap (1,) untuk keseluruhan episode.

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

Membuat Lingkungan Python Anda sendiri

Untuk banyak klien, kasus penggunaan yang umum adalah menerapkan salah satu agen standar (lihat agen /) di TF-Agents untuk masalah mereka. Untuk melakukan ini, mereka harus membingkai masalah mereka sebagai lingkungan. Jadi mari kita lihat bagaimana mengimplementasikan lingkungan dengan Python.

Katakanlah kita ingin melatih agen untuk memainkan permainan kartu (terinspirasi Black Jack) berikut:

  1. Permainan ini dimainkan menggunakan setumpuk kartu tak terbatas bernomor 1 ... 10.
  2. Di setiap kesempatan, agen dapat melakukan 2 hal: mendapatkan kartu acak baru, atau menghentikan putaran saat ini.
  3. Tujuannya adalah untuk mendapatkan jumlah kartu Anda sedekat mungkin dengan 21 di akhir ronde, tanpa melebihi.

Lingkungan yang mewakili permainan bisa terlihat seperti ini:

  1. Tindakan: Kami memiliki 2 tindakan. Tindakan 0: dapatkan kartu baru, dan Tindakan 1: hentikan putaran saat ini.
  2. Pengamatan: Jumlah kartu di babak saat ini.
  3. Imbalan: Tujuannya adalah untuk mendapatkan sebanyak mungkin 21 tanpa melampaui, jadi kita dapat mencapai ini menggunakan hadiah berikut di akhir babak: 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)

Mari kita pastikan kita melakukan semuanya dengan benar dengan mendefinisikan lingkungan di atas. Saat membuat lingkungan Anda sendiri, Anda harus memastikan observasi dan time_steps yang dihasilkan mengikuti bentuk dan jenis yang benar seperti yang ditentukan dalam spesifikasi Anda. Ini digunakan untuk menghasilkan grafik TensorFlow dan dengan demikian dapat menimbulkan masalah yang sulit di-debug jika kita salah.

Untuk memvalidasi lingkungan kami, kami akan menggunakan kebijakan acak untuk menghasilkan tindakan dan kami akan mengulang lebih dari 5 episode untuk memastikan semuanya berfungsi sebagaimana mestinya. Kesalahan muncul jika kita menerima time_step yang tidak mengikuti spesifikasi lingkungan.

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

Sekarang kita tahu bahwa lingkungan berfungsi sebagaimana mestinya, mari kita jalankan lingkungan ini menggunakan kebijakan tetap: minta 3 kartu lalu akhiri putaran.

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

Wrappers Lingkungan

Wrapper lingkungan menggunakan lingkungan Python dan mengembalikan versi lingkungan yang dimodifikasi. Baik lingkungan asli maupun lingkungan yang dimodifikasi adalah instance py_environment.PyEnvironment , dan beberapa pembungkus dapat digabungkan bersama.

Beberapa pembungkus umum dapat ditemukan di environments/wrappers.py . Sebagai contoh:

  1. ActionDiscretizeWrapper : Mengubah ruang aksi berkelanjutan menjadi ruang aksi diskrit.
  2. RunStats : RunStats statistik run dari lingkungan seperti jumlah langkah yang diambil, jumlah episode yang diselesaikan, dll.
  3. TimeLimit : Menghentikan episode setelah sejumlah langkah tetap.

Contoh 1: Action Discretize Wrapper

InvertedPendulum adalah lingkungan PyBullet yang menerima tindakan kontinu dalam rentang [-2, 2] . Jika kita ingin melatih agen aksi diskrit seperti DQN di lingkungan ini, kita harus mendiskritisasi (mengukur) ruang aksi. Inilah tepatnya yang dilakukan ActionDiscretizeWrapper . Bandingkan action_spec sebelum dan sesudah penggabungan:

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)

Dibungkus discrete_action_env adalah sebuah contoh dari py_environment.PyEnvironment dan dapat diperlakukan seperti lingkungan Python biasa.

Lingkungan TensorFlow

Antarmuka untuk lingkungan TF didefinisikan dalam environments/tf_environment.TFEnvironment dan terlihat sangat mirip dengan lingkungan Python. Lingkungan TF berbeda dari Python envs dalam beberapa cara:

  • Mereka menghasilkan objek tensor, bukan array
  • Lingkungan TF menambahkan dimensi batch ke tensor yang dihasilkan jika dibandingkan dengan spesifikasi.

Mengubah lingkungan Python menjadi TFEnvs memungkinkan tensorflow untuk memparalelkan operasi. Misalnya, seseorang dapat mendefinisikan collect_experience_op yang mengumpulkan data dari lingkungan dan menambahkan ke replay_buffer , dan train_op yang membaca dari replay_buffer dan melatih agen, dan menjalankannya secara paralel secara alami di 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`."""

Metode current_time_step() mengembalikan time_step saat ini dan menginisialisasi lingkungan jika diperlukan.

Metode reset() memaksa reset di lingkungan dan mengembalikan langkah_kini.

Jika action tidak bergantung pada time_step sebelumnya, diperlukan tf.control_dependency dalam mode Graph .

Untuk saat ini, mari kita lihat bagaimana TFEnvironments dibuat.

Membuat Lingkungan TensorFlow Anda sendiri

Ini lebih rumit daripada membuat lingkungan dengan Python, jadi kami tidak akan membahasnya di colab ini. Contohnya tersedia di sini . Kasus penggunaan yang lebih umum adalah mengimplementasikan lingkungan Anda dengan Python dan menggabungkannya dalam TensorFlow menggunakan pembungkus TFPyEnvironment kami (lihat di bawah).

Menggabungkan Lingkungan Python di TensorFlow

Kita dapat dengan mudah menggabungkan lingkungan Python apa pun ke dalam lingkungan TensorFlow menggunakan pembungkus 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))

Perhatikan bahwa spesifikasi sekarang berjenis: (Bounded)TensorSpec .

Contoh Penggunaan

Contoh Sederhana

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

Seluruh Episode

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