Entrene una red Deep Q con TF-Agents

Ver en TensorFlow.org Ejecutar en Google Colab Ver fuente en GitHubDescargar cuaderno

Introducción

Este ejemplo muestra cómo entrenar a un agente DQN (Deep Q Networks) en el entorno Cartpole usando la biblioteca TF-Agents.

Entorno de cartpole

Lo guiará a través de todos los componentes de una canalización de aprendizaje reforzado (RL) para capacitación, evaluación y recopilación de datos.

Para ejecutar este código en vivo, haga clic en el enlace 'Ejecutar en Google Colab' arriba.

Configuración

Si no ha instalado las siguientes dependencias, ejecute:

sudo apt-get update
sudo apt-get install -y xvfb ffmpeg
pip install 'imageio==2.4.0'
pip install pyvirtualdisplay
pip install tf-agents
from __future__ import absolute_import, division, print_function

import base64
import imageio
import IPython
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import PIL.Image
import pyvirtualdisplay

import tensorflow as tf

from tf_agents.agents.dqn import dqn_agent
from tf_agents.environments import suite_gym
from tf_agents.environments import tf_py_environment
from tf_agents.eval import metric_utils
from tf_agents.metrics import tf_metrics
from tf_agents.networks import sequential
from tf_agents.policies import random_tf_policy
from tf_agents.replay_buffers import tf_uniform_replay_buffer
from tf_agents.trajectories import trajectory
from tf_agents.specs import tensor_spec
from tf_agents.utils import common
# Set up a virtual display for rendering OpenAI gym environments.
display = pyvirtualdisplay.Display(visible=0, size=(1400, 900)).start()
tf.version.VERSION
'2.5.0'

Hiperparámetros

num_iterations = 20000 # @param {type:"integer"}

initial_collect_steps = 100  # @param {type:"integer"} 
collect_steps_per_iteration = 1  # @param {type:"integer"}
replay_buffer_max_length = 100000  # @param {type:"integer"}

batch_size = 64  # @param {type:"integer"}
learning_rate = 1e-3  # @param {type:"number"}
log_interval = 200  # @param {type:"integer"}

num_eval_episodes = 10  # @param {type:"integer"}
eval_interval = 1000  # @param {type:"integer"}

Ambiente

En el aprendizaje por refuerzo (RL), un entorno representa la tarea o problema a resolver. Se pueden crear entornos estándar en TF-Agents utilizando tf_agents.environments suites. TF-Agents tiene suites para cargar entornos de fuentes como OpenAI Gym, Atari y DM Control.

Cargue el entorno CartPole desde la suite OpenAI Gym.

env_name = 'CartPole-v0'
env = suite_gym.load(env_name)

Puede renderizar este entorno para ver cómo se ve. Un poste de oscilación libre está sujeto a un carro. El objetivo es mover el carro hacia la derecha o hacia la izquierda para mantener el poste apuntando hacia arriba.

env.reset()
PIL.Image.fromarray(env.render())

png

El método environment.step realiza una action en el entorno y devuelve una tupla TimeStep contiene la siguiente observación del entorno y la recompensa por la acción.

El método time_step_spec() devuelve la especificación de la tupla TimeStep . Su atributo de observation muestra la forma de las observaciones, los tipos de datos y los rangos de valores permitidos. El atributo de reward muestra los mismos detalles para la recompensa.

print('Observation Spec:')
print(env.time_step_spec().observation)
Observation Spec:
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])
print('Reward Spec:')
print(env.time_step_spec().reward)
Reward Spec:
ArraySpec(shape=(), dtype=dtype('float32'), name='reward')

El método action_spec() devuelve la forma, los tipos de datos y los valores permitidos de las acciones válidas.

print('Action Spec:')
print(env.action_spec())
Action Spec:
BoundedArraySpec(shape=(), dtype=dtype('int64'), name='action', minimum=0, maximum=1)

En el entorno Cartpole:

  • observation es una matriz de 4 flotadores:
    • la posición y velocidad del carro
    • la posición angular y la velocidad del polo
  • reward es un valor flotante escalar
  • action es un entero escalar con solo dos valores posibles:
    • 0 - "mover a la izquierda"
    • 1 - "mover a la derecha"
time_step = env.reset()
print('Time step:')
print(time_step)

action = np.array(1, dtype=np.int32)

next_time_step = env.step(action)
print('Next time step:')
print(next_time_step)
Time step:
TimeStep(
{'discount': array(1., dtype=float32),
 'observation': array([-0.02291739, -0.04223812,  0.04325358,  0.03615353], dtype=float32),
 'reward': array(0., dtype=float32),
 'step_type': array(0, dtype=int32)})
Next time step:
TimeStep(
{'discount': array(1., dtype=float32),
 'observation': array([-0.02376215,  0.15223774,  0.04397665, -0.24257484], dtype=float32),
 'reward': array(1., dtype=float32),
 'step_type': array(1, dtype=int32)})

Por lo general, se crean instancias de dos entornos: uno para entrenamiento y otro para evaluación.

train_py_env = suite_gym.load(env_name)
eval_py_env = suite_gym.load(env_name)

El entorno Cartpole, como la mayoría de los entornos, está escrito en Python puro. Esto se convierte a TensorFlow mediante el contenedor TFPyEnvironment .

La API del entorno original utiliza matrices Numpy. TFPyEnvironment convierte en Tensors para que sean compatibles con los agentes y las políticas de Tensorflow.

train_env = tf_py_environment.TFPyEnvironment(train_py_env)
eval_env = tf_py_environment.TFPyEnvironment(eval_py_env)

Agente

El algoritmo utilizado para resolver un problema de RL está representado por un Agent . TF-Agents proporciona implementaciones estándar de una variedad de Agents , que incluyen:

El agente DQN se puede utilizar en cualquier entorno que tenga un espacio de acción discreto.

En el corazón de un agente DQN se encuentra un QNetwork , un modelo de red neuronal que puede aprender a predecir QValues (retornos esperados) para todas las acciones, dada una observación del entorno.

Usaremos tf_agents.networks. para crear una QNetwork . La red constará de una secuencia de tf.keras.layers.Dense Capas tf.keras.layers.Dense , donde la capa final tendrá 1 salida para cada posible acción.

fc_layer_params = (100, 50)
action_tensor_spec = tensor_spec.from_spec(env.action_spec())
num_actions = action_tensor_spec.maximum - action_tensor_spec.minimum + 1

# Define a helper function to create Dense layers configured with the right
# activation and kernel initializer.
def dense_layer(num_units):
  return tf.keras.layers.Dense(
      num_units,
      activation=tf.keras.activations.relu,
      kernel_initializer=tf.keras.initializers.VarianceScaling(
          scale=2.0, mode='fan_in', distribution='truncated_normal'))

# QNetwork consists of a sequence of Dense layers followed by a dense layer
# with `num_actions` units to generate one q_value per available action as
# it's output.
dense_layers = [dense_layer(num_units) for num_units in fc_layer_params]
q_values_layer = tf.keras.layers.Dense(
    num_actions,
    activation=None,
    kernel_initializer=tf.keras.initializers.RandomUniform(
        minval=-0.03, maxval=0.03),
    bias_initializer=tf.keras.initializers.Constant(-0.2))
q_net = sequential.Sequential(dense_layers + [q_values_layer])

Ahora use tf_agents.agents.dqn.dqn_agent para instanciar un DqnAgent . Además de time_step_spec , action_spec y QNetwork, el constructor del agente también requiere un optimizador (en este caso, AdamOptimizer ), una función de pérdida y un contador de pasos de números enteros.

optimizer = tf.keras.optimizers.Adam(learning_rate=learning_rate)

train_step_counter = tf.Variable(0)

agent = dqn_agent.DqnAgent(
    train_env.time_step_spec(),
    train_env.action_spec(),
    q_network=q_net,
    optimizer=optimizer,
    td_errors_loss_fn=common.element_wise_squared_loss,
    train_step_counter=train_step_counter)

agent.initialize()

Políticas

Una política define la forma en que actúa un agente en un entorno. Normalmente, el objetivo del aprendizaje por refuerzo es entrenar el modelo subyacente hasta que la política produzca el resultado deseado.

En este tutorial:

  • El resultado deseado es mantener el poste en equilibrio sobre el carro.
  • La política devuelve una acción (izquierda o derecha) para cada observación time_step .

Los agentes contienen dos políticas:

  • agent.policy : la política principal que se utiliza para la evaluación y la implementación.
  • agent.collect_policy : una segunda política que se utiliza para la recopilación de datos.
eval_policy = agent.policy
collect_policy = agent.collect_policy

Las políticas se pueden crear independientemente de los agentes. Por ejemplo, use tf_agents.policies.random_tf_policy para crear una política que seleccionará aleatoriamente una acción para cada time_step .

random_policy = random_tf_policy.RandomTFPolicy(train_env.time_step_spec(),
                                                train_env.action_spec())

Para obtener una acción de una política, llame al policy.action(time_step) . time_step contiene la observación del entorno. Este método devuelve un PolicyStep , que es una tupla con nombre con tres componentes:

  • action : la acción que se va a realizar (en este caso, 0 o 1 )
  • state : se utiliza para políticas con estado (es decir, basadas en RNN)
  • info : datos auxiliares, como el registro de probabilidades de acciones
example_environment = tf_py_environment.TFPyEnvironment(
    suite_gym.load('CartPole-v0'))
time_step = example_environment.reset()
random_policy.action(time_step)
PolicyStep(action=<tf.Tensor: shape=(1,), dtype=int64, numpy=array([1])>, state=(), info=())

Métricas y evaluación

La métrica más común utilizada para evaluar una póliza es el rendimiento promedio. El retorno es la suma de las recompensas obtenidas al ejecutar una política en un entorno para un episodio. Se ejecutan varios episodios, lo que genera un rendimiento promedio.

La siguiente función calcula el rendimiento promedio de una política, dados la política, el entorno y una serie de episodios.

def compute_avg_return(environment, policy, num_episodes=10):

  total_return = 0.0
  for _ in range(num_episodes):

    time_step = environment.reset()
    episode_return = 0.0

    while not time_step.is_last():
      action_step = policy.action(time_step)
      time_step = environment.step(action_step.action)
      episode_return += time_step.reward
    total_return += episode_return

  avg_return = total_return / num_episodes
  return avg_return.numpy()[0]


# See also the metrics module for standard implementations of different metrics.
# https://github.com/tensorflow/agents/tree/master/tf_agents/metrics

La ejecución de este cálculo en la random_policy muestra un rendimiento de referencia en el entorno.

compute_avg_return(eval_env, random_policy, num_eval_episodes)
28.4

Búfer de reproducción

El búfer de reproducción realiza un seguimiento de los datos recopilados del entorno. Este tutorial usa tf_agents.replay_buffers.tf_uniform_replay_buffer.TFUniformReplayBuffer , ya que es el más común.

El constructor requiere las especificaciones de los datos que recopilará. Está disponible en el agente mediante el método collect_data_spec . También se requieren el tamaño del lote y la longitud máxima del búfer.

replay_buffer = tf_uniform_replay_buffer.TFUniformReplayBuffer(
    data_spec=agent.collect_data_spec,
    batch_size=train_env.batch_size,
    max_length=replay_buffer_max_length)

Para la mayoría de los agentes, collect_data_spec es una tupla con nombre llamada Trajectory , que contiene las especificaciones de observaciones, acciones, recompensas y otros elementos.

agent.collect_data_spec
Trajectory(
{'action': BoundedTensorSpec(shape=(), dtype=tf.int64, name='action', minimum=array(0), maximum=array(1)),
 'discount': BoundedTensorSpec(shape=(), dtype=tf.float32, name='discount', minimum=array(0., dtype=float32), maximum=array(1., dtype=float32)),
 'next_step_type': TensorSpec(shape=(), dtype=tf.int32, name='step_type'),
 '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)),
 'policy_info': (),
 'reward': TensorSpec(shape=(), dtype=tf.float32, name='reward'),
 'step_type': TensorSpec(shape=(), dtype=tf.int32, name='step_type')})
agent.collect_data_spec._fields
('step_type',
 'observation',
 'action',
 'policy_info',
 'next_step_type',
 'reward',
 'discount')

Recopilación de datos

Ahora ejecute la política aleatoria en el entorno durante algunos pasos, registrando los datos en el búfer de reproducción.

def collect_step(environment, policy, buffer):
  time_step = environment.current_time_step()
  action_step = policy.action(time_step)
  next_time_step = environment.step(action_step.action)
  traj = trajectory.from_transition(time_step, action_step, next_time_step)

  # Add trajectory to the replay buffer
  buffer.add_batch(traj)

def collect_data(env, policy, buffer, steps):
  for _ in range(steps):
    collect_step(env, policy, buffer)

collect_data(train_env, random_policy, replay_buffer, initial_collect_steps)

# This loop is so common in RL, that we provide standard implementations. 
# For more details see tutorial 4 or the drivers module.
# https://github.com/tensorflow/agents/blob/master/docs/tutorials/4_drivers_tutorial.ipynb 
# https://www.tensorflow.org/agents/api_docs/python/tf_agents/drivers

El búfer de reproducción ahora es una colección de trayectorias.

# For the curious:
# Uncomment to peel one of these off and inspect it.
# iter(replay_buffer.as_dataset()).next()

El agente necesita acceso al búfer de reproducción. Esto se proporciona mediante la creación de una canalizacióntf.data.Dataset iterable que proporcionará datos al agente.

Cada fila del búfer de reproducción solo almacena un único paso de observación. Pero dado que el agente DQN necesita tanto la observación actual como la siguiente para calcular la pérdida, la canalización del conjunto de datos muestreará dos filas adyacentes para cada elemento del lote ( num_steps=2 ).

Este conjunto de datos también se optimiza mediante la ejecución de llamadas paralelas y la obtención previa de datos.

# Dataset generates trajectories with shape [Bx2x...]
dataset = replay_buffer.as_dataset(
    num_parallel_calls=3, 
    sample_batch_size=batch_size, 
    num_steps=2).prefetch(3)


dataset
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/autograph/impl/api.py:382: ReplayBuffer.get_next (from tf_agents.replay_buffers.replay_buffer) is deprecated and will be removed in a future version.
Instructions for updating:
Use `as_dataset(..., single_deterministic_pass=False) instead.
<PrefetchDataset shapes: (Trajectory(
{action: (64, 2),
 discount: (64, 2),
 next_step_type: (64, 2),
 observation: (64, 2, 4),
 policy_info: (),
 reward: (64, 2),
 step_type: (64, 2)}), BufferInfo(ids=(64, 2), probabilities=(64,))), types: (Trajectory(
{action: tf.int64,
 discount: tf.float32,
 next_step_type: tf.int32,
 observation: tf.float32,
 policy_info: (),
 reward: tf.float32,
 step_type: tf.int32}), BufferInfo(ids=tf.int64, probabilities=tf.float32))>
iterator = iter(dataset)
print(iterator)
<tensorflow.python.data.ops.iterator_ops.OwnedIterator object at 0x7f480466f5d0>
# For the curious:
# Uncomment to see what the dataset iterator is feeding to the agent.
# Compare this representation of replay data 
# to the collection of individual trajectories shown earlier.

# iterator.next()

Entrenando al agente

Deben suceder dos cosas durante el ciclo de entrenamiento:

  • recopilar datos del medio ambiente
  • usar esos datos para entrenar las redes neuronales del agente

Este ejemplo también evalúa periódicamente la política e imprime la puntuación actual.

Lo siguiente tardará ~ 5 minutos en ejecutarse.

try:
  %%time
except:
  pass

# (Optional) Optimize by wrapping some of the code in a graph using TF function.
agent.train = common.function(agent.train)

# Reset the train step
agent.train_step_counter.assign(0)

# Evaluate the agent's policy once before training.
avg_return = compute_avg_return(eval_env, agent.policy, num_eval_episodes)
returns = [avg_return]

for _ in range(num_iterations):

  # Collect a few steps using collect_policy and save to the replay buffer.
  collect_data(train_env, agent.collect_policy, replay_buffer, collect_steps_per_iteration)

  # Sample a batch of data from the buffer and update the agent's network.
  experience, unused_info = next(iterator)
  train_loss = agent.train(experience).loss

  step = agent.train_step_counter.numpy()

  if step % log_interval == 0:
    print('step = {0}: loss = {1}'.format(step, train_loss))

  if step % eval_interval == 0:
    avg_return = compute_avg_return(eval_env, agent.policy, num_eval_episodes)
    print('step = {0}: Average Return = {1}'.format(step, avg_return))
    returns.append(avg_return)
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/util/dispatch.py:206: calling foldr_v2 (from tensorflow.python.ops.functional_ops) with back_prop=False is deprecated and will be removed in a future version.
Instructions for updating:
back_prop=False is deprecated. Consider using tf.stop_gradient instead.
Instead of:
results = tf.foldr(fn, elems, back_prop=False)
Use:
results = tf.nest.map_structure(tf.stop_gradient, tf.foldr(fn, elems))
step = 200: loss = 18.56928825378418
step = 400: loss = 5.047736167907715
step = 600: loss = 14.25174331665039
step = 800: loss = 24.014192581176758
step = 1000: loss = 18.762466430664062
step = 1000: Average Return = 154.1999969482422
step = 1200: loss = 48.24091339111328
step = 1400: loss = 32.6214599609375
step = 1600: loss = 47.680694580078125
step = 1800: loss = 242.03060913085938
step = 2000: loss = 73.53008270263672
step = 2000: Average Return = 200.0
step = 2200: loss = 53.16136169433594
step = 2400: loss = 373.9162902832031
step = 2600: loss = 39.21461486816406
step = 2800: loss = 105.97955322265625
step = 3000: loss = 119.51268768310547
step = 3000: Average Return = 200.0
step = 3200: loss = 226.07618713378906
step = 3400: loss = 442.111572265625
step = 3600: loss = 437.02545166015625
step = 3800: loss = 662.8380737304688
step = 4000: loss = 782.982421875
step = 4000: Average Return = 200.0
step = 4200: loss = 121945.578125
step = 4400: loss = 2674.33154296875
step = 4600: loss = 2334.872802734375
step = 4800: loss = 2527.200927734375
step = 5000: loss = 8297.3955078125
step = 5000: Average Return = 200.0
step = 5200: loss = 8597.1923828125
step = 5400: loss = 7340.04443359375
step = 5600: loss = 3453.21728515625
step = 5800: loss = 27120.724609375
step = 6000: loss = 48760.671875
step = 6000: Average Return = 200.0
step = 6200: loss = 38986.421875
step = 6400: loss = 48238.234375
step = 6600: loss = 57711.22265625
step = 6800: loss = 274270.9375
step = 7000: loss = 63824.61328125
step = 7000: Average Return = 200.0
step = 7200: loss = 119148.859375
step = 7400: loss = 159678.6875
step = 7600: loss = 141013.96875
step = 7800: loss = 596496.9375
step = 8000: loss = 563607.125
step = 8000: Average Return = 200.0
step = 8200: loss = 7630604.0
step = 8400: loss = 13285580.0
step = 8600: loss = 333170048.0
step = 8800: loss = 23120384.0
step = 9000: loss = 369857056.0
step = 9000: Average Return = 200.0
step = 9200: loss = 17108284.0
step = 9400: loss = 49773056.0
step = 9600: loss = 21419348.0
step = 9800: loss = 148333248.0
step = 10000: loss = 67406928.0
step = 10000: Average Return = 77.0999984741211
step = 10200: loss = 16730348.0
step = 10400: loss = 19317756.0
step = 10600: loss = 12585502.0
step = 10800: loss = 15987130.0
step = 11000: loss = 32655918.0
step = 11000: Average Return = 200.0
step = 11200: loss = 11686570.0
step = 11400: loss = 15297463.0
step = 11600: loss = 15890760.0
step = 11800: loss = 17948140.0
step = 12000: loss = 7198852.5
step = 12000: Average Return = 200.0
step = 12200: loss = 12537673.0
step = 12400: loss = 11662791.0
step = 12600: loss = 157308304.0
step = 12800: loss = 9338672.0
step = 13000: loss = 7486102.0
step = 13000: Average Return = 200.0
step = 13200: loss = 7827698.5
step = 13400: loss = 7565884.0
step = 13600: loss = 12146330.0
step = 13800: loss = 8902687.0
step = 14000: loss = 12659248.0
step = 14000: Average Return = 200.0
step = 14200: loss = 9578409.0
step = 14400: loss = 13338937.0
step = 14600: loss = 12442834.0
step = 14800: loss = 8154804.5
step = 15000: loss = 14968015.0
step = 15000: Average Return = 200.0
step = 15200: loss = 6897091.0
step = 15400: loss = 11543962.0
step = 15600: loss = 8033916.5
step = 15800: loss = 16707812.0
step = 16000: loss = 3942055680.0
step = 16000: Average Return = 200.0
step = 16200: loss = 9505360.0
step = 16400: loss = 5509431.5
step = 16600: loss = 9405398.0
step = 16800: loss = 8809309.0
step = 17000: loss = 10677678.0
step = 17000: Average Return = 200.0
step = 17200: loss = 10017460.0
step = 17400: loss = 8878522.0
step = 17600: loss = 9307826.0
step = 17800: loss = 8975850.0
step = 18000: loss = 11676451.0
step = 18000: Average Return = 200.0
step = 18200: loss = 355074080.0
step = 18400: loss = 22255044.0
step = 18600: loss = 16955560.0
step = 18800: loss = 12466311.0
step = 19000: loss = 17620040.0
step = 19000: Average Return = 200.0
step = 19200: loss = 6484491.5
step = 19400: loss = 14639623.0
step = 19600: loss = 11706258.0
step = 19800: loss = 19145572.0
step = 20000: loss = 13256708.0
step = 20000: Average Return = 200.0

Visualización

Parcelas

Utilice matplotlib.pyplot para trazar cómo mejoró la política durante el entrenamiento.

Una iteración de Cartpole-v0 consta de 200 pasos de tiempo. El entorno da una recompensa de +1 por cada paso que se mantiene en el poste, por lo que el rendimiento máximo para un episodio es 200. Los gráficos muestran el rendimiento aumentando hacia ese máximo cada vez que se evalúa durante el entrenamiento. (Puede ser un poco inestable y no aumentar monótonamente cada vez).

iterations = range(0, num_iterations + 1, eval_interval)
plt.plot(iterations, returns)
plt.ylabel('Average Return')
plt.xlabel('Iterations')
plt.ylim(top=250)
(-0.1300004005432136, 250.0)

png

Videos

Los gráficos son agradables. Pero lo más emocionante es ver a un agente realizando una tarea en un entorno.

Primero, cree una función para incrustar videos en el cuaderno.

def embed_mp4(filename):
  """Embeds an mp4 file in the notebook."""
  video = open(filename,'rb').read()
  b64 = base64.b64encode(video)
  tag = '''
  <video width="640" height="480" controls>
    <source src="data:video/mp4;base64,{0}" type="video/mp4">
  Your browser does not support the video tag.
  </video>'''.format(b64.decode())

  return IPython.display.HTML(tag)

Ahora repite algunos episodios del juego Cartpole con el agente. El entorno de Python subyacente (el que está "dentro" del contenedor de entorno de TensorFlow) proporciona un método render() , que genera una imagen del estado del entorno. Estos se pueden recopilar en un video.

def create_policy_eval_video(policy, filename, num_episodes=5, fps=30):
  filename = filename + ".mp4"
  with imageio.get_writer(filename, fps=fps) as video:
    for _ in range(num_episodes):
      time_step = eval_env.reset()
      video.append_data(eval_py_env.render())
      while not time_step.is_last():
        action_step = policy.action(time_step)
        time_step = eval_env.step(action_step.action)
        video.append_data(eval_py_env.render())
  return embed_mp4(filename)




create_policy_eval_video(agent.policy, "trained-agent")
WARNING:root:IMAGEIO FFMPEG_WRITER WARNING: input image is not divisible by macro_block_size=16, resizing from (400, 600) to (400, 608) to ensure video compatibility with most codecs and players. To prevent resizing, make your input image divisible by the macro_block_size or set the macro_block_size to None (risking incompatibility). You may also see a FFMPEG warning concerning speedloss due to data not being aligned.

Para divertirse, compare al agente entrenado (arriba) con un agente que se mueve al azar. (No funciona tan bien).

create_policy_eval_video(random_policy, "random-agent")
WARNING:root:IMAGEIO FFMPEG_WRITER WARNING: input image is not divisible by macro_block_size=16, resizing from (400, 600) to (400, 608) to ensure video compatibility with most codecs and players. To prevent resizing, make your input image divisible by the macro_block_size or set the macro_block_size to None (risking incompatibility). You may also see a FFMPEG warning concerning speedloss due to data not being aligned.