Hak Cipta 2021 The TF-Agents Authors.
Lihat di TensorFlow.org | Jalankan di Google Colab | Lihat sumber di GitHub | Unduh buku catatan |
pengantar
Tujuan Reinforcement Learning (RL) adalah untuk merancang agen yang belajar dengan berinteraksi dengan lingkungan. Dalam pengaturan RL standar, agen menerima pengamatan pada setiap langkah waktu dan memilih tindakan. Tindakan tersebut diterapkan pada lingkungan dan lingkungan mengembalikan reward dan observasi baru. Agen melatih kebijakan untuk memilih tindakan untuk memaksimalkan jumlah imbalan, 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 secara otomatis mengubahnya menjadi TensorFlow.
Mari kita lihat lingkungan Python terlebih dahulu. Lingkungan TensorFlow mengikuti API yang sangat mirip.
Mempersiapkan
Jika Anda belum menginstal tf-agents atau gym, jalankan:
pip install "gym>=0.21.0"
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 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
Lingkungan Python
Lingkungan Python memiliki step(action) -> next_time_step
metode yang berlaku tindakan untuk lingkungan, dan mengembalikan informasi berikut tentang langkah berikutnya:
-
observation
: ini adalah bagian dari negara lingkungan bahwa agen dapat mengamati untuk memilih tindakannya pada langkah berikutnya. -
reward
: Agen adalah belajar untuk memaksimalkan jumlah imbalan ini di beberapa langkah. -
step_type
: Interaksi dengan lingkungan biasanya bagian dari urutan / episode. misalnya beberapa gerakan dalam permainan catur. step_type dapat berupaFIRST
,MID
atauLAST
untuk menunjukkan apakah kali ini langkah pertama, menengah atau terakhir langkah secara berurutan. -
discount
: Ini adalah pelampung yang mewakili berapa banyak berat pahala pada waktu berikutnya langkah relatif reward saat saat langkah.
Ini dikelompokkan ke dalam tuple bernama TimeStep(step_type, reward, discount, observation)
.
Antarmuka yang semua lingkungan Python harus menerapkan dalam 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 step()
metode, lingkungan juga menyediakan reset()
metode yang dimulai urutan baru dan memberikan awal TimeStep
. Hal ini tidak perlu untuk memanggil reset
metode eksplisit. Kami berasumsi bahwa lingkungan disetel ulang secara otomatis, baik saat mereka mencapai akhir episode atau saat step() dipanggil pertama kali.
Perhatikan bahwa subclass tidak melaksanakan step()
atau reset()
secara langsung. Mereka bukannya menimpa _step()
dan _reset()
metode. Langkah-langkah waktu kembali dari metode ini akan di-cache dan terpapar melalui current_time_step()
.
The observation_spec
dan action_spec
metode kembali sarang (Bounded)ArraySpecs
yang menggambarkan nama, bentuk, tipe data dan rentang pengamatan dan tindakan masing-masing.
Dalam TF-Agents kami berulang kali merujuk ke sarang yang didefinisikan sebagai struktur seperti pohon yang terdiri dari daftar, tupel, tupel bernama, atau kamus. Ini dapat secara sewenang-wenang disusun untuk mempertahankan struktur pengamatan dan tindakan. Kami telah menemukan ini sangat berguna untuk lingkungan yang lebih kompleks di mana Anda memiliki banyak pengamatan dan tindakan.
Menggunakan Lingkungan Standar
TF Agen telah built-in pembungkus untuk banyak lingkungan standar seperti OpenAI Gym, DeepMind-kontrol dan Atari, sehingga mereka mengikuti kami py_environment.PyEnvironment
antarmuka. Lingkungan yang dibungkus ini dapat dengan mudah dimuat menggunakan suite lingkungan kami. Mari muat lingkungan CartPole dari gym OpenAI dan lihat action 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 dari jenis int64
di [0, 1] dan kembali TimeSteps
mana pengamatan adalah float32
vektor dengan panjang 4 dan faktor diskon adalah float32
di [0.0, 1.0]. Sekarang, mari kita coba untuk mengambil tindakan tetap (1,)
untuk seluruh 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( {'discount': array(1., dtype=float32), 'observation': array([ 0.0138565 , -0.03582913, 0.04861612, -0.03755046], dtype=float32), 'reward': array(0., dtype=float32), 'step_type': array(0, dtype=int32)}) TimeStep( {'discount': array(1., dtype=float32), 'observation': array([ 0.01313992, 0.15856317, 0.0478651 , -0.3145069 ], dtype=float32), 'reward': array(1., dtype=float32), 'step_type': array(1, dtype=int32)}) TimeStep( {'discount': array(1., dtype=float32), 'observation': array([ 0.01631118, 0.35297176, 0.04157497, -0.5917188 ], dtype=float32), 'reward': array(1., dtype=float32), 'step_type': array(1, dtype=int32)}) TimeStep( {'discount': array(1., dtype=float32), 'observation': array([ 0.02337062, 0.54748774, 0.02974059, -0.87102115], dtype=float32), 'reward': array(1., dtype=float32), 'step_type': array(1, dtype=int32)}) TimeStep( {'discount': array(1., dtype=float32), 'observation': array([ 0.03432037, 0.74219286, 0.01232017, -1.1542072 ], dtype=float32), 'reward': array(1., dtype=float32), 'step_type': array(1, dtype=int32)}) TimeStep( {'discount': array(1., dtype=float32), 'observation': array([ 0.04916423, 0.93715197, -0.01076398, -1.4430016 ], dtype=float32), 'reward': array(1., dtype=float32), 'step_type': array(1, dtype=int32)}) TimeStep( {'discount': array(1., dtype=float32), 'observation': array([ 0.06790727, 1.1324048 , -0.03962401, -1.7390285 ], dtype=float32), 'reward': array(1., dtype=float32), 'step_type': array(1, dtype=int32)}) TimeStep( {'discount': array(1., dtype=float32), 'observation': array([ 0.09055536, 1.327955 , -0.07440457, -2.04377 ], dtype=float32), 'reward': array(1., dtype=float32), 'step_type': array(1, dtype=int32)}) TimeStep( {'discount': array(1., dtype=float32), 'observation': array([ 0.11711447, 1.523758 , -0.11527998, -2.3585167 ], dtype=float32), 'reward': array(1., dtype=float32), 'step_type': array(1, dtype=int32)}) TimeStep( {'discount': array(1., dtype=float32), 'observation': array([ 0.14758962, 1.7197047 , -0.16245031, -2.6843033 ], dtype=float32), 'reward': array(1., dtype=float32), 'step_type': array(1, dtype=int32)}) TimeStep( {'discount': array(0., dtype=float32), 'observation': array([ 0.18198372, 1.9156038 , -0.21613638, -3.0218334 ], dtype=float32), 'reward': array(1., dtype=float32), 'step_type': array(2, dtype=int32)})
Membuat Lingkungan Python Anda sendiri
Bagi 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 seorang agen untuk memainkan permainan kartu (terinspirasi Black Jack) berikut:
- Permainan ini dimainkan menggunakan setumpuk kartu tak terbatas bernomor 1...10.
- Di setiap giliran agen dapat melakukan 2 hal: mendapatkan kartu acak baru, atau menghentikan putaran saat ini.
- Tujuannya adalah untuk mendapatkan jumlah kartu Anda sedekat mungkin dengan 21 di akhir putaran, tanpa melewatinya.
Lingkungan yang mewakili permainan dapat terlihat seperti ini:
- Tindakan: Kami memiliki 2 tindakan. Tindakan 0: dapatkan kartu baru, dan Tindakan 1: akhiri putaran saat ini.
- Pengamatan: Jumlah kartu di babak saat ini.
- Hadiah: Tujuannya adalah untuk mendapatkan sedekat mungkin dengan 21 tanpa melewatinya, jadi kita dapat mencapai ini dengan menggunakan hadiah berikut di akhir putaran: sum_of_cards - 21 jika 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 pastikan kita melakukan semuanya dengan benar mendefinisikan lingkungan di atas. Saat membuat lingkungan Anda sendiri, Anda harus memastikan pengamatan dan time_steps yang dihasilkan mengikuti bentuk dan tipe yang benar seperti yang ditentukan dalam spesifikasi Anda. Ini digunakan untuk menghasilkan grafik TensorFlow dan dengan demikian dapat membuat masalah sulit untuk di-debug jika kita salah.
Untuk memvalidasi lingkungan kami, kami akan menggunakan kebijakan acak untuk menghasilkan tindakan dan kami akan mengulangi lebih dari 5 episode untuk memastikan semuanya berfungsi sebagaimana mestinya. Kesalahan muncul jika kami menerima time_step yang tidak mengikuti spesifikasi lingkungan.
environment = CardGameEnv()
utils.validate_py_environment(environment, episodes=5)
Sekarang kita tahu lingkungan bekerja sebagaimana dimaksud, mari kita jalankan lingkungan ini menggunakan kebijakan tetap: minta 3 kartu dan kemudian 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( {'discount': array(1., dtype=float32), 'observation': array([0], dtype=int32), 'reward': array(0., dtype=float32), 'step_type': array(0, dtype=int32)}) TimeStep( {'discount': array(1., dtype=float32), 'observation': array([9], dtype=int32), 'reward': array(0., dtype=float32), 'step_type': array(1, dtype=int32)}) TimeStep( {'discount': array(1., dtype=float32), 'observation': array([12], dtype=int32), 'reward': array(0., dtype=float32), 'step_type': array(1, dtype=int32)}) TimeStep( {'discount': array(0., dtype=float32), 'observation': array([21], dtype=int32), 'reward': array(0., dtype=float32), 'step_type': array(2, dtype=int32)}) TimeStep( {'discount': array(0., dtype=float32), 'observation': array([21], dtype=int32), 'reward': array(0., dtype=float32), 'step_type': array(2, dtype=int32)}) Final Reward = 0.0
Pembungkus Lingkungan
Pembungkus lingkungan mengambil lingkungan Python dan mengembalikan versi lingkungan yang dimodifikasi. Kedua lingkungan asli dan lingkungan dimodifikasi adalah contoh dari py_environment.PyEnvironment
, dan beberapa bungkus dapat dirantai bersama-sama.
Beberapa pembungkus umum dapat ditemukan di environments/wrappers.py
. Sebagai contoh:
-
ActionDiscretizeWrapper
: Mengkonversi ruang tindakan terus-menerus untuk ruang tindakan diskrit. -
RunStats
: Captures menjalankan statistik dari lingkungan seperti jumlah langkah yang diambil, jumlah episode selesai dll -
TimeLimit
: Menghentikan episode setelah sejumlah tetap langkah.
Contoh 1: Pembungkus Tindakan Diskrit
InvertedPendulum adalah lingkungan PyBullet yang menerima tindakan terus menerus dalam rentang [-2, 2]
. Jika kita ingin melatih agen aksi diskrit seperti DQN pada lingkungan ini, kita harus mendiskritisasi (mengkuantisasi) ruang aksi. Ini adalah persis apa yang ActionDiscretizeWrapper
tidak. Bandingkan action_spec
sebelum dan sesudah pembungkus:
env = suite_gym.load('Pendulum-v1')
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 envs Python dalam beberapa cara:
- Mereka menghasilkan objek tensor alih-alih array
- Lingkungan TF menambahkan dimensi batch ke tensor yang dihasilkan jika dibandingkan dengan spesifikasi.
Mengubah lingkungan Python menjadi TFEnvs memungkinkan tensorflow memparalelkan operasi. Misalnya, orang bisa menentukan collect_experience_op
yang mengumpulkan data dari lingkungan dan menambah sebuah replay_buffer
, dan train_op
yang membaca dari replay_buffer
dan melatih agen, dan menjalankannya secara paralel secara alami dalam 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`."""
The current_time_step()
method mengembalikan time_step saat ini dan menginisialisasi lingkungan jika diperlukan.
The reset()
pasukan metode reset di lingkungan dan kembali CURRENT_STEP tersebut.
Jika action
tidak tergantung pada sebelumnya time_step
sebuah tf.control_dependency
diperlukan dalam Graph
modus.
Untuk saat ini, mari kita lihat bagaimana TFEnvironments
diciptakan.
Membuat Lingkungan TensorFlow Anda sendiri
Ini lebih rumit daripada membuat lingkungan dengan Python, jadi kami tidak akan membahasnya dalam colab ini. Contohnya adalah tersedia di sini . Kasus penggunaan yang lebih umum adalah untuk menerapkan lingkungan Anda dengan Python dan bungkus dalam TensorFlow menggunakan kami TFPyEnvironment
pembungkus (lihat di bawah).
Membungkus Lingkungan Python di TensorFlow
Kita dapat dengan mudah membungkus setiap lingkungan Python ke dalam lingkungan TensorFlow menggunakan TFPyEnvironment
pembungkus.
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( {'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)), 'reward': TensorSpec(shape=(), dtype=tf.float32, name='reward'), 'step_type': TensorSpec(shape=(), dtype=tf.int32, name='step_type')}) Action Specs: BoundedTensorSpec(shape=(), dtype=tf.int64, name='action', minimum=array(0), maximum=array(1))
Catatan spesifikasi sekarang tipe: (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( {'discount': array([1.], dtype=float32), 'observation': array([[-0.0078796 , -0.04736348, -0.04966116, 0.04563603]], dtype=float32), 'reward': array([0.], dtype=float32), 'step_type': array([0], dtype=int32)}), array([0], dtype=int32), TimeStep( {'discount': array([1.], dtype=float32), 'observation': array([[-0.00882687, -0.24173944, -0.04874843, 0.32224613]], dtype=float32), 'reward': array([1.], dtype=float32), 'step_type': array([1], dtype=int32)})] [TimeStep( {'discount': array([1.], dtype=float32), 'observation': array([[-0.00882687, -0.24173944, -0.04874843, 0.32224613]], dtype=float32), 'reward': array([1.], dtype=float32), 'step_type': array([1], dtype=int32)}), array([1], dtype=int32), TimeStep( {'discount': array([1.], dtype=float32), 'observation': array([[-0.01366166, -0.04595843, -0.04230351, 0.01459712]], dtype=float32), 'reward': array([1.], dtype=float32), 'step_type': array([1], dtype=int32)})] [TimeStep( {'discount': array([1.], dtype=float32), 'observation': array([[-0.01366166, -0.04595843, -0.04230351, 0.01459712]], dtype=float32), 'reward': array([1.], dtype=float32), 'step_type': array([1], dtype=int32)}), array([0], dtype=int32), TimeStep( {'discount': array([1.], dtype=float32), 'observation': array([[-0.01458083, -0.24044897, -0.04201157, 0.2936384 ]], dtype=float32), 'reward': array([1.], dtype=float32), 'step_type': array([1], dtype=int32)})] 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: 131 avg_length 26.2 avg_reward: 26.2