Copyright 2021 The TF-Agents Authors.
TensorFlow.org에서 보기 | Google Colab에서 실행하기 | GitHub에서 소스 보기 | 노트북 다운로드하기 |
소개
강화 학습의 일반적인 패턴은 지정된 수의 단계 또는 에피소드에 대해 환경에서 정책을 실행하는 것입니다. 이는 예를 들어 데이터 수집, 평가 및 에이전트의 비디오 생성 중에 발생합니다.
Python으로 작성하는 것이 비교적 간단하지만, tf.while
루프, tf.cond
및 tf.control_dependencies
가 포함되므로 TensorFlow에서 작성하고 디버깅하는 것이 훨씬 더 복잡합니다. 따라서 run 루프라는 개념을 driver
라는 클래스로 추상화하고 Python 및 TensorFlow에서 잘 테스트된 구현을 제공합니다.
또한, 각 단계에서 드라이버가 발견한 데이터는 Trajectory라는 명명된 튜플에 저장되고 재현 버퍼 및 메트릭과 같은 observer 세트로 브로드캐스팅됩니다. 이 데이터에는 환경의 관찰 값, 정책에서 권장하는 행동, 획득한 보상, 현재 유형 및 다음 단계 등이 포함됩니다.
설정
tf-agents 또는 gym을 아직 설치하지 않은 경우, 다음을 실행합니다.
pip install tf-agents
pip install gym
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import tensorflow as tf
from tf_agents.environments import suite_gym
from tf_agents.environments import tf_py_environment
from tf_agents.policies import random_py_policy
from tf_agents.policies import random_tf_policy
from tf_agents.metrics import py_metrics
from tf_agents.metrics import tf_metrics
from tf_agents.drivers import py_driver
from tf_agents.drivers import dynamic_episode_driver
tf.compat.v1.enable_v2_behavior()
Python 드라이버
PyDriver
클래스는 Python environment, Python policy 및 각 단계에서 업데이트할 observer의 목록을 사용합니다. 기본 메서드는 run()
이며, 다음 종료 기준 중 하나 이상이 충족될 때까지 정책의 행동을 사용하여 환경을 단계화합니다. 종료 기준은 단계 수가 max_steps
에 도달하거나 에피소드 수가 max_episodes
에 도달하는 것입니다.
구현은 대략 다음과 같습니다.
class PyDriver(object):
def __init__(self, env, policy, observers, max_steps=1, max_episodes=1):
self._env = env
self._policy = policy
self._observers = observers or []
self._max_steps = max_steps or np.inf
self._max_episodes = max_episodes or np.inf
def run(self, time_step, policy_state=()):
num_steps = 0
num_episodes = 0
while num_steps < self._max_steps and num_episodes < self._max_episodes:
# Compute an action using the policy for the given time_step
action_step = self._policy.action(time_step, policy_state)
# Apply the action to the environment and get the next step
next_time_step = self._env.step(action_step.action)
# Package information into a trajectory
traj = trajectory.Trajectory(
time_step.step_type,
time_step.observation,
action_step.action,
action_step.info,
next_time_step.step_type,
next_time_step.reward,
next_time_step.discount)
for observer in self._observers:
observer(traj)
# Update statistics to check termination
num_episodes += np.sum(traj.is_last())
num_steps += np.sum(~traj.is_boundary())
time_step = next_time_step
policy_state = action_step.state
return time_step, policy_state
이제 CartPole 환경에서 임의의 정책을 실행하여 결과를 재현 버퍼에 저장하고 일부 메트릭을 계산하는 예를 살펴보겠습니다.
env = suite_gym.load('CartPole-v0')
policy = random_py_policy.RandomPyPolicy(time_step_spec=env.time_step_spec(),
action_spec=env.action_spec())
replay_buffer = []
metric = py_metrics.AverageReturnMetric()
observers = [replay_buffer.append, metric]
driver = py_driver.PyDriver(
env, policy, observers, max_steps=20, max_episodes=1)
initial_time_step = env.reset()
final_time_step, _ = driver.run(initial_time_step)
print('Replay Buffer:')
for traj in replay_buffer:
print(traj)
print('Average Return: ', metric.result())
Replay Buffer: Trajectory( {'action': array(1), 'discount': array(1., dtype=float32), 'next_step_type': array(1, dtype=int32), 'observation': array([ 0.03515758, -0.00069493, -0.03442739, -0.04551223], dtype=float32), 'policy_info': (), 'reward': array(1., dtype=float32), 'step_type': array(0, dtype=int32)}) Trajectory( {'action': array(1), 'discount': array(1., dtype=float32), 'next_step_type': array(1, dtype=int32), 'observation': array([ 0.03514368, 0.19490334, -0.03533763, -0.34885544], dtype=float32), 'policy_info': (), 'reward': array(1., dtype=float32), 'step_type': array(1, dtype=int32)}) Trajectory( {'action': array(1), 'discount': array(1., dtype=float32), 'next_step_type': array(1, dtype=int32), 'observation': array([ 0.03904175, 0.3905096 , -0.04231474, -0.65246874], dtype=float32), 'policy_info': (), 'reward': array(1., dtype=float32), 'step_type': array(1, dtype=int32)}) Trajectory( {'action': array(0), 'discount': array(1., dtype=float32), 'next_step_type': array(1, dtype=int32), 'observation': array([ 0.04685194, 0.5861945 , -0.05536411, -0.9581702 ], dtype=float32), 'policy_info': (), 'reward': array(1., dtype=float32), 'step_type': array(1, dtype=int32)}) Trajectory( {'action': array(1), 'discount': array(1., dtype=float32), 'next_step_type': array(1, dtype=int32), 'observation': array([ 0.05857584, 0.3918589 , -0.07452752, -0.68338215], dtype=float32), 'policy_info': (), 'reward': array(1., dtype=float32), 'step_type': array(1, dtype=int32)}) Trajectory( {'action': array(0), 'discount': array(1., dtype=float32), 'next_step_type': array(1, dtype=int32), 'observation': array([ 0.06641302, 0.5879323 , -0.08819516, -0.99856657], dtype=float32), 'policy_info': (), 'reward': array(1., dtype=float32), 'step_type': array(1, dtype=int32)}) Trajectory( {'action': array(0), 'discount': array(1., dtype=float32), 'next_step_type': array(1, dtype=int32), 'observation': array([ 0.07817166, 0.39409298, -0.10816649, -0.7348335 ], dtype=float32), 'policy_info': (), 'reward': array(1., dtype=float32), 'step_type': array(1, dtype=int32)}) Trajectory( {'action': array(0), 'discount': array(1., dtype=float32), 'next_step_type': array(1, dtype=int32), 'observation': array([ 0.08605352, 0.20061833, -0.12286316, -0.4780566 ], dtype=float32), 'policy_info': (), 'reward': array(1., dtype=float32), 'step_type': array(1, dtype=int32)}) Trajectory( {'action': array(0), 'discount': array(1., dtype=float32), 'next_step_type': array(1, dtype=int32), 'observation': array([ 0.09006588, 0.00742573, -0.1324243 , -0.22648314], dtype=float32), 'policy_info': (), 'reward': array(1., dtype=float32), 'step_type': array(1, dtype=int32)}) Trajectory( {'action': array(0), 'discount': array(1., dtype=float32), 'next_step_type': array(1, dtype=int32), 'observation': array([ 0.0902144 , -0.18557958, -0.13695395, 0.02167106], dtype=float32), 'policy_info': (), 'reward': array(1., dtype=float32), 'step_type': array(1, dtype=int32)}) Trajectory( {'action': array(1), 'discount': array(1., dtype=float32), 'next_step_type': array(1, dtype=int32), 'observation': array([ 0.08650281, -0.37849882, -0.13652053, 0.2682016 ], dtype=float32), 'policy_info': (), 'reward': array(1., dtype=float32), 'step_type': array(1, dtype=int32)}) Trajectory( {'action': array(1), 'discount': array(1., dtype=float32), 'next_step_type': array(1, dtype=int32), 'observation': array([ 0.07893283, -0.18171947, -0.1311565 , -0.06423354], dtype=float32), 'policy_info': (), 'reward': array(1., dtype=float32), 'step_type': array(1, dtype=int32)}) Trajectory( {'action': array(0), 'discount': array(1., dtype=float32), 'next_step_type': array(1, dtype=int32), 'observation': array([ 0.07529844, 0.01501523, -0.13244118, -0.3952506 ], dtype=float32), 'policy_info': (), 'reward': array(1., dtype=float32), 'step_type': array(1, dtype=int32)}) Trajectory( {'action': array(0), 'discount': array(1., dtype=float32), 'next_step_type': array(1, dtype=int32), 'observation': array([ 0.07559875, -0.1780033 , -0.14034618, -0.14708233], dtype=float32), 'policy_info': (), 'reward': array(1., dtype=float32), 'step_type': array(1, dtype=int32)}) Trajectory( {'action': array(1), 'discount': array(1., dtype=float32), 'next_step_type': array(1, dtype=int32), 'observation': array([ 0.07203868, -0.3708656 , -0.14328784, 0.09824025], dtype=float32), 'policy_info': (), 'reward': array(1., dtype=float32), 'step_type': array(1, dtype=int32)}) Trajectory( {'action': array(1), 'discount': array(1., dtype=float32), 'next_step_type': array(1, dtype=int32), 'observation': array([ 0.06462137, -0.17401177, -0.14132303, -0.23599704], dtype=float32), 'policy_info': (), 'reward': array(1., dtype=float32), 'step_type': array(1, dtype=int32)}) Trajectory( {'action': array(1), 'discount': array(1., dtype=float32), 'next_step_type': array(1, dtype=int32), 'observation': array([ 0.06114113, 0.02281669, -0.14604297, -0.56970716], dtype=float32), 'policy_info': (), 'reward': array(1., dtype=float32), 'step_type': array(1, dtype=int32)}) Trajectory( {'action': array(1), 'discount': array(1., dtype=float32), 'next_step_type': array(1, dtype=int32), 'observation': array([ 0.06159747, 0.21965237, -0.15743712, -0.90460175], dtype=float32), 'policy_info': (), 'reward': array(1., dtype=float32), 'step_type': array(1, dtype=int32)}) Trajectory( {'action': array(0), 'discount': array(1., dtype=float32), 'next_step_type': array(1, dtype=int32), 'observation': array([ 0.06599052, 0.4165158 , -0.17552915, -1.2423403 ], dtype=float32), 'policy_info': (), 'reward': array(1., dtype=float32), 'step_type': array(1, dtype=int32)}) Trajectory( {'action': array(1), 'discount': array(0., dtype=float32), 'next_step_type': array(2, dtype=int32), 'observation': array([ 0.07432083, 0.22402637, -0.20037594, -1.0093838 ], dtype=float32), 'policy_info': (), 'reward': array(1., dtype=float32), 'step_type': array(1, dtype=int32)}) Average Return: 20.0
TensorFlow 드라이버
또한, TensorFlow에는 기능적으로 Python 드라이버와 유사한 드라이버가 있지만, TF environments, TF policies, TF observers 등을 사용합니다. 현재 두 개의 TensorFlow 드라이버, 주어진 수의 (유효한) 환경 단계 후 종료하는 DynamicStepDriver
및 주어진 수의 에피소드 후에 종료하는 DynamicEpisodeDriver
가 있습니다. 동작 중인 DynamicEpisode의 예를 살펴보겠습니다.
env = suite_gym.load('CartPole-v0')
tf_env = tf_py_environment.TFPyEnvironment(env)
tf_policy = random_tf_policy.RandomTFPolicy(action_spec=tf_env.action_spec(),
time_step_spec=tf_env.time_step_spec())
num_episodes = tf_metrics.NumberOfEpisodes()
env_steps = tf_metrics.EnvironmentSteps()
observers = [num_episodes, env_steps]
driver = dynamic_episode_driver.DynamicEpisodeDriver(
tf_env, tf_policy, observers, num_episodes=2)
# Initial driver.run will reset the environment and initialize the policy.
final_time_step, policy_state = driver.run()
print('final_time_step', final_time_step)
print('Number of Steps: ', env_steps.result().numpy())
print('Number of Episodes: ', num_episodes.result().numpy())
final_time_step TimeStep( {'discount': <tf.Tensor: shape=(1,), dtype=float32, numpy=array([1.], dtype=float32)>, 'observation': <tf.Tensor: shape=(1, 4), dtype=float32, numpy= array([[-0.01061224, -0.03314709, -0.03278693, -0.04203142]], dtype=float32)>, 'reward': <tf.Tensor: shape=(1,), dtype=float32, numpy=array([0.], dtype=float32)>, 'step_type': <tf.Tensor: shape=(1,), dtype=int32, numpy=array([0], dtype=int32)>}) Number of Steps: 32 Number of Episodes: 2
# Continue running from previous state
final_time_step, _ = driver.run(final_time_step, policy_state)
print('final_time_step', final_time_step)
print('Number of Steps: ', env_steps.result().numpy())
print('Number of Episodes: ', num_episodes.result().numpy())
final_time_step TimeStep( {'discount': <tf.Tensor: shape=(1,), dtype=float32, numpy=array([1.], dtype=float32)>, 'observation': <tf.Tensor: shape=(1, 4), dtype=float32, numpy= array([[-0.01437509, 0.04432349, 0.04460504, -0.01052537]], dtype=float32)>, 'reward': <tf.Tensor: shape=(1,), dtype=float32, numpy=array([0.], dtype=float32)>, 'step_type': <tf.Tensor: shape=(1,), dtype=int32, numpy=array([0], dtype=int32)>}) Number of Steps: 120 Number of Episodes: 4