드라이버

TensorFlow.org에서 보기 Google Colab에서 실행하기 GitHub에서 소스 보기 노트북 다운로드하기

소개

강화 학습의 일반적인 패턴은 지정된 수의 단계 또는 에피소드에 대해 환경에서 정책을 실행하는 것입니다. 이는 예를 들어 데이터 수집, 평가 및 에이전트의 비디오 생성 중에 발생합니다.

Python으로 작성하는 것이 비교적 간단하지만, tf.while 루프, tf.condtf.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