Saltar al contenido principal

Primeros pasos con TorchRL para el aprendizaje profundo por refuerzo

Una guía para principiantes sobre TorchRL para el aprendizaje profundo por refuerzo: aprende a construir agentes RL con PyTorch mediante ejemplos prácticos.
Actualizado 30 ene 2025  · 30 min de lectura

El aprendizaje por refuerzo (RL) se utiliza para resolver muchos problemas complejos, desde el entrenamiento de coches autoconducidos hasta el entrenamiento de grandes modelos lingüísticos (LLM) para dar respuestas similares a las humanas. 

Los agentes de RL se entrenan para responder en función de la respuesta humana, utilizando el Aprendizaje por Refuerzo a partir de la Respuesta Humana (RLHF). Aunque los marcos basados en Python, como Keras y Tensorflow, se utilizan en aplicaciones empresariales de aprendizaje profundo, la mayoría de los nuevos proyectos se basan ahora en PyTorch y PyTorch Lightning.

TorchRL es una biblioteca de código abierto para construir soluciones RL utilizando PyTorch. En este tutorial, te mostraré cómo configurar TorchRL, comprender sus componentes subyacentes y utilizarlo para construir un agente RL sencillo. También hablaremos del uso de TorchRL para implementar versiones preconstruidas de algoritmos de RL como la Optimización de Política Proximal (PPO). Por último, trataremos los principios básicos del registro y la supervisión de los agentes de RL.

¿Qué es TorchRL y por qué utilizarlo?

Los algoritmos de RL suelen ser complejos. Tienes que crear el agente o agentes, calcular los rendimientos y las pérdidas, definir los pases hacia delante y hacia atrás, y evaluar el rendimiento del agente. 

TorchRL empaqueta muchas funciones RL de uso común en módulos a los que puedes acceder directamente. Esto facilita la aplicación y la experimentación con diversos algoritmos para resolver problemas prácticos. También simplifica la construcción de nuevos algoritmos, porque los investigadores tienen acceso al resto del ecosistema de RL sin tener que construirlo ellos mismos. 

TorchRL viene con muchos módulos preconstruidos que hacen que el desarrollo de RL sea más eficiente. Por ejemplo: 

  • Entornos: TorchRL proporciona una API estandarizada para importar y utilizar entornos RL de diversas fuentes, encluyendo Gymnasium, Jumanji, RoboHive, y more. En muchos casos, es necesario personalizar la salida del entorno para adaptarla a necesidades de formación específicas. TorchRL incluye módulos para diversas transformaciones del entorno mediante una única llamada a una función con los parámetros deseados. 
  • Colectores de datos y búferes de repetición: Muchos algoritmos de entrenamiento de RL implican recoger datos sobre las interacciones del agente con el entorno: la acción del agente, la recompensa que recibió y el siguiente estado en el que terminó. TorchRL incluye paquetes con las estructuras de datos para recoger y muestrear esta información. 
  • Funciones objetivo: La metodología central de un algoritmo de RL se implementa en su función objetivo. TorchRL empaqueta algoritmos comunes de RL como DQN (Redes Q Profundas), A2C (Ventaja Actor-Crítico), PPO y muchos más en módulos preconstruidos que puedes invocar directamente y utilizar para entrenar al agente. 

Dada la funcionalidad anterior, TorchRL ha demostrado su utilidad para agilizar y simplificar la construcción de soluciones de RL para diversos casos de uso, como:

  • Robótica: Hay que entrenar al robot para que navegue con éxito por un entorno complejo. Se le penaliza por tropezar o por cualquier otro fallo en la navegación. Utilizando métodos de entrenamiento RL, aprende las acciones correctas a realizar para navegar con éxito por un entorno como una superficie natural irregular. 
  • Juego de IA: Los jugadores informáticos de videojuegos y consolas tienen que idear e improvisar sus movimientos en respuesta a las acciones del jugador humano. La RL, con recompensas y penalizaciones, se utiliza para entrenar a esos jugadores a elegir las jugadas correctas para jugar contra el humano. 
  • Sistemas autónomos: Los coches autoconducidos tienen que navegar de forma independiente por un entorno complejo (como el tráfico rodado) y completar el objetivo (llegar al destino) sin accidentes. La RL se utiliza para entrenar sistemas autónomos que imiten el comportamiento de un conductor humano en diversas condiciones del mundo real. 

Desarrollar aplicaciones de IA

Aprende a crear aplicaciones de IA utilizando la API OpenAI.
Empieza a hacer Upskilling gratis

Configurar TorchRL

En esta sección, te mostraré cómo instalar y empezar con TorchRL. 

Requisitos previos

Necesitas algunos paquetes de software dependientes antes de instalar y utilizar TorchRL:

  • PyTorch: TorchRL se basa en PyTorch, por lo que lo necesitas como requisito previo. 
  • Gimnasio: Ennecesitas el paquete Gimnasio para importar entornos RL. Desde enero de 2025, la última versión de Gymnasium no es compatible con TorchRL, como se explica en esta página de Discusiones Git. Por tanto, instala la versión anterior 0.29.1.
  • PyGame: Es un paquete Python para videojuegos.  TorchRL lo necesita para simular entornos RL similares a juegos, como CartPole. 
  • TensorDict: El uso de una estructura de datos similar a un diccionario para almacenar las entradas y salidas de las redes neuronales hace que sea cómodo trabajar con tensores en TorchRL. TensorDict proporciona un contenedor tensorial que almacena los tensores como pares clave-valor. 

Instala los paquetes de requisitos previos:

!pip install torch tensordict  gymnasium==0.29.1 pygame

Instalación de TorchRL

Instala TorchRL utilizando pip. Te recomiendo encarecidamente que instales estos paquetes en un entorno Conda si trabajas en un ordenador personal o en un servidor. 

!pip install torchrl

Verificar la instalación

Tras la instalación, comprueba que TorchRL se ha instalado correctamente. Intenta importar torchrl en un intérprete de comandos (o cuaderno) de Python. Utiliza el método check_env_specs() para comprobar que un entorno estándar (como CartPole) coincide con las especificaciones de torchrl:

import torchrl 
from torchrl.envs import GymEnv 
from torchrl.envs.utils import check_env_specs

check_env_specs(GymEnv("CartPole-v1"))

La salida debería indicar que ha creado correctamente el entorno y que funciona con TorchRL. 

[torchrl][INFO] check_env_specs succeeded!

Componentes clave de TorchRL

Antes de construir tu primer agente RL, veamos los elementos básicos de TorchRL.

Entornos

TorchRL proporciona una API uniforme para interactuar con distintos entornos. Lo hace envolviendo funcionalidades individuales específicas del entorno en un conjunto estándar de clases y funciones envolventes. Pasas los parámetros apropiados a la envoltura, y ésta asigna internamente tu orden a la llamada a la función correspondiente al entorno específico. En particular:

  • TorchRL convierte los estados del entorno (observaciones), las acciones y las recompensas en objetos tensoriales PyTorch, que pueden ser utilizados directamente por los módulos que implementan algoritmos de RL.
  • Permite aplicar pasos de preprocesamiento y postprocesamiento, por ejemplo, para normalizar y escalar los tensores de entrada o para presentar los tensores de salida en un formato específico.

Por ejemplo, para crear un entorno a partir de Gimnasio, utiliza el módulo GymEnv:

env = GymEnv("CartPole-v1")

Transforma

Es habitual ampliar el entorno de stock con funciones adicionales y transformaciones que, de otro modo, tendrías que implementar tú mismo. Por ejemplo, puedes añadir un módulo contador de pasos al entorno en lugar de codificar tú mismo el contador. El contador de pasos lleva la cuenta del número de pasos de cada episodio. El módulo transformedEnv ayuda a ello:

from torchrl.envs import GymEnv, StepCounter, TransformedEnv
env = TransformedEnv(GymEnv("CartPole-v1"), StepCounter())

Del mismo modo, normalizar los tensores antes de operar con ellos es un paso común del preprocesamiento. Una transformación de normalización, mediante el módulo ObservationNorm normaliza los tensores. La documentación de la función TransformedEnv trata de varios tipos de transformaciones. 

También puedes combinar varias transformaciones utilizando el parámetro compose():

base_env = GymEnv('CartPole-v1', device=device) 

env = TransformedEnv( 
    base_env, 
    Compose(
        ObservationNorm(in_keys=["observation"]), 
        StepCounter()
    )
)

Agentes y políticas

En la RL, el agente decide sus acciones utilizando la política y basándose en el estado observado del entorno. El objetivo del agente es maximizar las recompensas acumuladas del entorno. Recibe recompensas cuando elige la acción correcta (como parar en un semáforo en rojo). 

Por ejemplo, en un coche autoconducido, el agente conduce el coche. Sus decisiones (como dirigir el coche en una dirección determinada) se basan en sus observaciones del estado del entorno (como el tráfico, la posición del coche, etc.) y de su política (como evitar a los peatones y otros obstáculos, parar en los semáforos en rojo, etc.). 

La política más sencilla consiste en elegir una acción al azar. El actor elige al azar una de las acciones del espacio de acciones posibles. A veces se utiliza una política aleatoria para generar un conjunto inicial de datos de interacciones antes de empezar a entrenar el modelo. 

Utiliza el módulo RandomPolicy para crear una política aleatoria. Esta política aleatoria acepta un parámetro action_spec que especifica el espacio de acción. En el ejemplo siguiente, el espacio de acción está formado por números (continuos) entre -1 y +1. Así, la política aleatoria elige una acción representada por un número aleatorio entre -1 y +1. 

import torchrl 
import torch
from tensordict import TensorDict
from torchrl.data.tensor_specs import Bounded

action_spec = Bounded(-torch.ones(1), torch.ones(1))
actor = torchrl.envs.utils.RandomPolicy(action_spec=action_spec) 
td = actor(TensorDict({}, batch_size=[])) 
print(td.get("action"))

La salida debe ser un tensor, como se muestra a continuación:

tensor([0.9258])

Comprueba que la política es realmente aleatoria ejecutando de nuevo el actor:

td = actor(TensorDict({}, batch_size=[])) 
print(td.get("action"))

Debería salir un tensor aleatorio diferente.

Si necesitas una introducción a los conceptos de RL, echa un vistazo al curso de Aprendizaje por Refuerzo en Python en DataCamp.

Crear tu primer agente RL con TorchRL

En esta sección, te muestro cómo implementar un agente sencillo de Aprendizaje por Refuerzo utilizando TorchRL. 

Antes de empezar, importa los paquetes de software necesarios en Python:

  • time para medir el tiempo necesario para entrenar al agente.
  • GymEnv, StepCounter, y TransformedEnv para trabajar en entornos de Gimnasio.
  • MLP para crear una sencilla red neuronal MLP (perceptrón multicapa). 
  • EGreedyModule para equilibrar la exploración del entorno y la explotación de la política más conocida.
  • QValueModule y DQNLoss para aplicar el algoritmo Deep Q-Learning.
  • SoftUpdate para actualizar la red neuronal.
  • SyncDataCollector para recoger datos de las interacciones del agente. 
  • ReplayBuffer para almacenar los datos de las interacciones del agente.
  • Adam para la retropropagación.
  • matplotlib para mostrar visualmente el progreso del entrenamiento. 
  • torchrl_logger para registrar la sesión de entrenamiento.
import time
import matplotlib.pyplot as plt
from torchrl.envs import GymEnv, StepCounter, TransformedEnv
from tensordict.nn import TensorDictModule as TensorDict, TensorDictSequential as Seq
from torchrl.modules import EGreedyModule, MLP, QValueModule
from torchrl.objectives import DQNLoss, SoftUpdate
from torchrl.collectors import SyncDataCollector
from torchrl.data import LazyTensorStorage, ReplayBuffer
from torch.optim import Adam
from torchrl._utils import logger as torchrl_logger

Paso 1: Definir el entorno

En este ejemplo, resolvemos el entorno de CartPole. Importa este entorno desde Gimnasio junto con un contador de pasos para llevar la cuenta del número de pasos de entrenamiento:

env = TransformedEnv(GymEnv("CartPole-v1"), StepCounter())

Siembra los entornos Python y RL para replicar resultados similares en todas las sesiones de entrenamiento. 

torch.manual_seed(0)
env = TransformedEnv(GymEnv("CartPole-v1"), StepCounter())
env.set_seed(0)

Define los parámetros y los hiperparámetros para el entrenamiento: 

  • INIT_RAND_STEPS: El número de pasos durante los cuales el agente actúa aleatoriamente antes de utilizar la política. Estos pasos iniciales sirven para recoger los datos iniciales para empezar a entrenar la política.
  • FRAMES_PER_BATCH: El número de puntos de datos (uno por cada interacción o paso temporal) en un lote de entrenamiento. 
  • OPTIM_STEPS: El número de pasos para los que acumular las pérdidas antes de ejecutar una pasada de retroceso. 
  • EPS_0: El valor inicial de épsilon, el coeficiente de exploración. 
  • BUFFER_LEN: El tamaño del búfer de repetición.
  • ALPHA: El ritmo de aprendizaje.
  • TARGET_UPDATE_EPS: El factor de decaimiento para actualizar la red de destino using el módulo de actualización suave.
  • REPLAY_BUFFER_SAMPLE: El tamaño de la muestra aleatoria a elegir del buffer de repetición en cada iteración de entrenamiento. El búfer de repetición almacena los resultados de las interacciones del agente con el entorno en cada paso temporal. 
  • LOG_EVERY: El número de pasos tras los cuales imprimir el progreso del entrenamiento.
  • MLP_SIZE: El tamaño de la red neuronal.
INIT_RAND_STEPS = 5000 
FRAMES_PER_BATCH = 100
OPTIM_STEPS = 10
EPS_0 = 0.5
BUFFER_LEN = 100_000
ALPHA = 0.05
TARGET_UPDATE_EPS = 0.95
REPLAY_BUFFER_SAMPLE = 128
LOG_EVERY = 1000
MLP_SIZE = 64

Paso 2: Crea la política

Define una red neuronal sencilla para aplicar la política: 

  • Define la MLP (red neuronal). Dada una observación, el MLP emite el valor de la acción, haciendo efectivamente el trabajo de la función Q. 
  • Define un diccionario tensorial utilizando el módulo TensorDict en la MLP anterior. Este diccionario asigna las observaciones de estado del entorno (claves del diccionario) a los valores de probabilidad de acción (valores del diccionario) correspondientes a ese estado. 
  • Utiliza el módulo QValueModule para implementar la función Q. Dado un tensor que contiene valores de acción, devuelve la acción correspondiente al valor de acción más alto. Aplica la estrategia codiciosa (de explotación). 
  • Combina (mediante elmódulo TensorDictSequential) el diccionario tensorial de la MLP y el QValueModule para definir la política .
value_mlp = MLP(out_features=env.action_spec.shape[-1], num_cells=[MLP_SIZE, MLP_SIZE])
value_net = TensorDict(value_mlp, in_keys=["observation"], out_keys=["action_value"])
policy = Seq(value_net, QValueModule(spec=env.action_spec))
  • Define la función de exploración utilizando el módulo EGreedyModule: toma como entrada el espacio de acción del entorno, la longitud del búfer de repetición y el coeficiente de exploración, epsilon. Encadena (utilizando el módulo TensorDictSecuencial ) esta función de exploración con la política definida anteriormente para obtener la política final:
exploration_module = EGreedyModule(
    env.action_spec, annealing_num_steps=BUFFER_LEN, eps_init=EPS_0
)
policy_explore = Seq(policy, exploration_module)

Paso 3: Formar al agente

El primer paso para entrenar al agente es recoger los datos de las interacciones del agente con el entorno. Utiliza SyncDataCollector para construir un recolector que ejecute la política y recoja los resultados de las interacciones del agente: 

collector = SyncDataCollector(
    env,
    policy_explore,
    frames_per_batch=FRAMES_PER_BATCH,
    total_frames=-1,
    init_random_frames=INIT_RAND_STEPS,
)

Crea un búfer de repetición para almacenar los resultados de las interacciones:

rb = ReplayBuffer(storage=LazyTensorStorage(BUFFER_LEN))

También tienes que declarar módulos específicos del entrenamiento, como la función de pérdida (para utilizar el cálculo de pérdidas basado en DQN), el optimizador (el algoritmo Adam tradicional) y las funciones del actualizador (para actualizar la red neuronal). Todos ellos se basan en módulos predefinidos de TorchRL:

loss = DQNLoss(value_network=policy, action_space=env.action_spec, delay_value=True)
optim = Adam(loss.parameters(), lr=ALPHA)
updater = SoftUpdate(loss, eps=TARGET_UPDATE_EPS)

Inicializa los contadores para llevar la cuenta del número total de pasos y episodios, los pasos realizados con éxito por episodio y el tiempo de ejecución:

total_count = 0
total_episodes = 0
t0 = time.time()
success_steps = []

La función de entrenamiento consta de 2 bucles for:

  • El bucle superior ejecuta la política y añade los resultados de las interacciones del agente al búfer de reproducción.
  • El bucle interno divide las muestras de entrenamiento en lotes. Procesa cada lote del siguiente modo: 
    • Elige una muestra aleatoria del búfer de repetición.
    • Calcula las pérdidas utilizando la función de pérdidas.
    • Ejecuta el optimizador y el actualizador.
    • Actualiza los contadores.

Cada paso consiste en llamar a módulos predefinidos de TorchRL sin codificar nada desde cero. El código siguiente muestra cómo realizar estos pasos:

for i, data in enumerate(collector):
    rb.extend(data)
    max_length = rb[:]["next", "step_count"].max()
    if len(rb) > INIT_RAND_STEPS:
        for _ in range(OPTIM_STEPS):
            sample = rb.sample(REPLAY_BUFFER_SAMPLE)
            loss_vals = loss(sample)
            loss_vals["loss"].backward()
            optim.step()

            optim.zero_grad()
            # Update exploration factor
            exploration_module.step(data.numel())

            # Update target params
            updater.step()
            total_count += data.numel()
            total_episodes += data["next", "done"].sum()
    success_steps.append(max_length)

Paso 4: Evalúa al agente

El bucle de la sección anterior entrena continuamente la política. Tenemos que establecer los criterios para evaluar el rendimiento y decidir cuándo considerar que la formación ha tenido éxito. También queremos dar salida al progreso de la formación. 

Imprimimos el progreso del entrenamiento a intervalos periódicos utilizando el registrador TorchRL:

if total_count > 0 and total_count % LOG_EVERY == 0:
    torchrl_logger.info(f"Successful steps in the last episode: {max_length}, rb length {len(rb)}, Number of episodes: {total_episodes}")

Utilizamos el número máximo de pasos que el agente consiguió en el último episodio para determinar si el entrenamiento ha tenido éxito. El entorno CartPole-v1 limita el número máximo de pasos y la recompensa total por episodio a 500. Es convencional considerar que una política tiene éxito si consigue más de 475 pasos:

if max_length > 475:
    print("TRAINING COMPLETE")
    break

Los dos fragmentos de código anteriores deben añadirse al final del bucle de entrenamiento (mostrado anteriormente). El siguiente fragmento muestra el bucle de entrenamiento con el código para evaluar al agente: 

for i, data in enumerate(collector):
    # Write data in replay buffer
    rb.extend(data)
    max_length = rb[:]["next", "step_count"].max()
    if len(rb) > INIT_RAND_STEPS:
        for _ in range(OPTIM_STEPS):
            sample = rb.sample(REPLAY_BUFFER_SAMPLE)
            loss_vals = loss(sample)
            loss_vals["loss"].backward()
            optim.step()

            optim.zero_grad()
            # Update exploration factor
            exploration_module.step(data.numel())

            # Update target params
            updater.step()
            total_count += data.numel()
            total_episodes += data["next", "done"].sum()
    success_steps.append(max_length)

    if total_count > 0 and total_count % LOG_EVERY == 0:
        torchrl_logger.info(f"Successful steps in the last episode: {max_length}, rb length {len(rb)}, Number of episodes: {total_episodes}")

    if max_length > 475:
        print("TRAINING COMPLETE")
        break

Por último, tras el entrenamiento y la evaluación, imprime el tiempo total de entrenamiento y traza el progreso del entrenamiento:

t1 = time.time()

torchrl_logger.info(
    f"solved after {total_count} steps, {total_episodes} episodes and in {t1-t0}s."
)

def plot_steps():
    plt.plot(success_steps)
    plt.title('Successful steps over training episodes')
    plt.xlabel('Training episodes')
    plt.ylabel('Steps')
    plt.show()

plot_steps()

Puedes encontrar y ejecutar el código completo para implementar DQN utilizando TorchRL en este libro de trabajo de DataLab

En esta sección, construimos y entrenamos un agente RL utilizando el sencillo algoritmo DQN. En la siguiente sección, exploramos cómo utilizar módulos TorchRL preconstruidos para implementar un algoritmo más complejo como el PPO. 

Explorando Algoritmos Preconstruidos en TorchRL

Como se ha mencionado en secciones anteriores, TorchRL viene con unos cuantos algoritmos preconstruidos. Veámoslos y cómo funcionan.

Algoritmos admitidos

TorchRL incluye módulos preconstruidos para muchos algoritmos comunes de Aprendizaje Profundo por Refuerzo, como: 

  • Redes Q profundas (DQN)
  • Gradiente político determinista profundo (DDPG)
  • Actor-crítico blando (SAC)
  • Aprendizaje Q doble ensamblado aleatorio (REDQ)
  • CrossQ
  • Aprendizaje Q implícito (QI)
  • Aprendizaje Q continuo (CQL)
  • Aprendizaje Generativo Adversarial por Imitación (GAIL)
  • Transformador de decisión (DT)
  • DDPG con doble retardo (TD3) 
  • Crítico actor-ventaja (A2C)
  • Optimización de la política proximal (OPP)
  • REFUERZA
  • y más 

Esto hace que sea eficaz experimentar con distintos tipos de algoritmos y estudiar el rendimiento de cada uno para resolver un problema determinado. 

Ejemplo: PPO en TorchRL

La Optimización de la Política Proximal utiliza una función objetivo sustitutiva recortada para conseguir un proceso de entrenamiento suave que equilibre la exploración y la explotación. 

En esta sección, mostraré cómo utilizar el módulo PPO preconstruido en TorchRL. De forma similar a la implementación anterior, importa los módulos necesarios. Además de los módulos (como ReplayBuffer, SyncDataCollector, etc.) necesarios para la implementación anterior, necesitas algunos paquetes adicionales para PPO:

  • ActorProbabilístico, para elegir una acción estocásticamente. Elegir la acción estocásticamente (en lugar de seguir siempre la política y maximizar la recompensa) facilita la exploración del entorno y el descubrimiento de más caminos óptimos durante el entrenamiento.
  • OneHotCategorical, para generar una codificación one-hot del tensor que denota las probabilidades logarítmicas de las acciones. 
  • ValueOperator, para construir un módulo TorchRL basado en la red neuronal que implementa la función valor.
  • GAE, para aplicar la estimación de ventaja generalizada. La OPP utiliza una función de ventaja como sustituto de la función de valor. El módulo de valores (arriba) es una entrada para el GAE. 
  • ClipPPOLoss, para construir la función objetivo recortada para aplicar la OPP. 
import torch
from torch import nn

from torchrl.envs import Compose, ObservationNorm, DoubleToFloat, StepCounter, TransformedEnv
from torchrl.envs.libs.gym import GymEnv
from torchrl.envs.utils import check_env_specs
from torchrl.modules import ProbabilisticActor, OneHotCategorical, ValueOperator
from torchrl.collectors import SyncDataCollector
from torchrl.data.replay_buffers import ReplayBuffer
from torchrl.data.replay_buffers.storages import LazyTensorStorage
from torchrl.data.replay_buffers.samplers import SamplerWithoutReplacement
from torchrl.objectives.value import GAE
from torchrl.objectives import ClipPPOLoss
from tensordict.nn import TensorDictModule

Declara los parámetros y los hiperparámetros para el entrenamiento:

  • FRAMES_PER_BATCH: El número de fotogramas (pasos de tiempo) de cada lote de datos resultantes de las interacciones del agente con el entorno. 
  • SUB_BATCH_SIZE: El número de pasos de tiempo en cada sublote. En el entrenamiento PPO, cada lote se divide en sublotes. 
  • TOTAL_FRAMES: El número total de pasos de tiempo para los que ejecutar el entrenamiento. 
  • GAMMA: El factor de descuento para descontar el valor de la recompensa de futuros pasos temporales.
  • CLIP_EPSILON: Esto define la región de confianza dentro de la cual la política puede cambiar en cada iteración de entrenamiento posterior. Se aplica recortando la política actualizada de modo que la relación entre las probabilidades nuevas y las antiguas caiga dentro de un cierto límite. 
  • ALPHA: El ritmo de aprendizaje.
  • ENTROPY_EPS: Este hiperparámetro controla la relación entre exploración y explotación. 
  • OPTIM_STEPS: El número de veces que debe ejecutarse el optimizador en cada sublote de datos. 
  • LOG_EVERY: El número de pasos tras los cuales evaluar la política e imprimir las recompensas. 
FRAMES_PER_BATCH = 1024
TOTAL_FRAMES = 1048576
GAMMA = 0.99
LAMBDA = 0.95
CLIP_EPSILON = 0.2
ALPHA = 1e-4
ENTROPY_EPS = 5e-4 
SUB_BATCH_SIZE = 64
OPTIM_STEPS = 8
LOG_EVERY = 16

Importa el entorno base de CartPole desde Gymnasium:

device="cpu"
base_env = GymEnv('CartPole-v1', device=device) 

Declara el entorno TorchRL importando el entorno base y añadiendo módulos para normalizar los tensores de observación y contar el número de pasos:

env = TransformedEnv( 
    base_env, 
    Compose(
        ObservationNorm(in_keys=["observation"]), 
        DoubleToFloat(), 
        StepCounter()
    )
)

Inicializa el entorno y siembra los generadores de números aleatorios:

env.transform[0].init_stats(1024) 
torch.manual_seed(0)
env.set_seed(0)
check_env_specs(env) 

Declara que el actor es una red neuronal con 1 capa oculta con 16 pesos. Toma como entrada la observación del entorno y da como salida la probabilidad de cada acción en el espacio de acción. 

actor_net = nn.Sequential(
    nn.Linear(env.observation_spec["observation"].shape[-1], 32, device=device),
    nn.ReLU(),
    nn.Linear(32, 32, device=device),
    nn.ReLU(),
    nn.Linear(32, env.action_spec.shape[-1], device=device),
    nn.ReLU()
)

Crea un diccionario tensorial (basado en la red neuronal anterior) con claves y valores que asignen estados a probabilidades de acción. 

actor_module = TensorDictModule(actor_net, in_keys=["observation"], out_keys=["logits"])

Para entrenar PPO (y muchos otros algoritmos de RL), no siempre eliges la acción que conduce a la mayor recompensa en el siguiente paso. Tener cierto grado de aleatoriedad en la elección de la acción permite al agente explorar el entorno y descubrir mejores caminos que pueden conducir a mayores rendimientos a largo plazo. Crea un actor probabilístico para ello:

actor = ProbabilisticActor(
    module = actor_module,
    spec = env.action_spec,
    in_keys = ["logits"],
    distribution_class = OneHotCategorical, 
    return_log_prob = True
)

Crea la red para aplicar la función de valor. Esta red tiene 16 pesos. Toma como entrada el estado del entorno (observación) y da como salida el valor esperado de ese estado. 

value_net = nn.Sequential(
    nn.Linear(env.observation_spec["observation"].shape[-1], 16, device=device),
    nn.ReLU(),
    nn.Linear(16, 1, device=device),
    nn.ReLU()
)

Crea un módulo TorchRL que envuelva la función de valor (implementada anteriormente) utilizando la página ValueOperator(). Este módulo interactúa con otros componentes de TorchRL. 

value_module = ValueOperator(
    module = value_net,
    in_keys = ["observation"]
)

Crea el búfer de repetición para almacenar los resultados de las interacciones del agente con el entorno: 

replay_buffer = ReplayBuffer(
    storage = LazyTensorStorage(max_size=FRAMES_PER_BATCH),
    sampler = SamplerWithoutReplacement()
)

Crea un recopilador de datos para ejecutar la política en el entorno y recoger los resultados de las interacciones del agente con el entorno en cada paso de tiempo (fotograma):

collector = SyncDataCollector(
    env,
    actor,
    frames_per_batch = FRAMES_PER_BATCH,
    total_frames = TOTAL_FRAMES,
    split_trajs = True,
    reset_at_each_iter = True,
    device=device
)

Utiliza el módulo GAE para implementar la función de ventaja para PPO. La función de ventaja se basa en la función de valor. 

advantage_module = GAE(
    gamma = GAMMA, 
    lmbda = GAMMA, 
    value_network = value_module,
    average_gae = True
) 

Utiliza el módulo incorporado ClipPPOLoss para aplicar la función de pérdida según el algoritmo PPO:

loss_module = ClipPPOLoss(
    actor_network = actor,
    critic_network = value_module,
    clip_epsilon = CLIP_EPSILON,
    entropy_bonus = bool(ENTROPY_EPS),
    entropy_coef = ENTROPY_EPS
)

Declara el optimizador Adam: 

optim = torch.optim.Adam(loss_module.parameters(), lr=ALPHA)

Crea un programador para reducir gradualmente el ritmo de aprendizaje a medida que avanza el entrenamiento: 

scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optim, TOTAL_FRAMES // FRAMES_PER_BATCH)

Construye el bucle de entrenamiento utilizando los componentes creados anteriormente. La función de entrenamiento consta de tres bucles for:

  • El bucle más externo recorre los datos recogidos de las interacciones del agente con el entorno. El parámetro TOTAL_FRAMES decide cuántos pasos de tiempo debe ejecutar el agente .
  • El bucle central hace lo siguiente: 
    • Ejecuta el bucle más interno durante un número fijo de iteraciones (normalmente entre 5 y 10), definido por el hiperparámetro OPTIM_STEPS .
    • Actualiza el ritmo de aprendizaje después de cada iteración de entrenamiento. 
    • También calcula el valor de la función de ventaja para cada paso de tiempo. 
    • Evalúa la política periódicamente e imprime las recompensas. 
  • El bucle más interno ejecuta el bucle de entrenamiento:
    • Muestrea un lote de datos de entrenamiento del buffer de repetición.
    • Calcula la pérdida utilizando el módulo de pérdidas. 
    • Ejecuta la retropropagación sobre la pérdida.
    • Utiliza el optimizador para realizar el descenso gradiente. 

El código siguiente implementa el bucle de entrenamiento:

for i, tensordict_data in enumerate(collector): 
    for _ in range(OPTIM_STEPS): 
        advantage_module(tensordict_data)
        replay_buffer.extend(tensordict_data.reshape(-1).cpu())
        for _ in range(FRAMES_PER_BATCH // SUB_BATCH_SIZE): 
            data = replay_buffer.sample(SUB_BATCH_SIZE)
            loss = loss_module(data.to(device))
            loss_value = loss["loss_objective"] + loss["loss_critic"] + loss["loss_entropy"]
            loss_value.backward()
            optim.step()
            optim.zero_grad()
    scheduler.step()

    if i % LOG_EVERY == 0:
        with torch.no_grad():
            rollout = env.rollout(FRAMES_PER_BATCH, actor)
            reward_eval = rollout["next","reward"].sum()
            #print(rollout["next","reward"].sum())
            print(reward_eval)
            rewards.append(reward_eval)
            del rollout

Este libro de trabajo de DataLab tiene el código (como se muestra arriba) para entrenar a un agente RL utilizando el algoritmo PPO de TorchRL. Utilízalo como punto de partida para afinar los parámetros y mejorar el rendimiento del agente. 

Personalización de algoritmos

TorchRL tiene un diseño modular que permite personalizar las soluciones RL. El marco se encarga de la interconexión entre los distintos componentes. Para probar y comparar su rendimiento, puedes conectar y reproducir entornos, políticas diferentes, búferes de repetición y algoritmos RL. Debido a esta modularidad:

  • Puedes intercambiar distintos componentes sin tener que reescribir grandes partes del programa. 
  • Los componentes individuales también pueden modificarse independientemente para crear soluciones personalizadas. 
  • Puedes experimentar con distintos módulos y construir una solución optimizada. 

Algunos ejemplos de personalización en TorchRL son:

  • Los bucles de entrenamiento pueden personalizarse con programadores de ritmo de aprendizaje, registradores y métricas personalizadas. 
  • Para ajustar la duración del entrenamiento se pueden utilizar memorias intermedias de repetición de distinta longitud.
  • El entorno puede personalizarse para incluir funciones de escala y normalización, contadores de pasos, etc. 

Visualizar y depurar la formación en RL

Las secciones anteriores mostraron cómo utilizar los módulos TorchRL para entrenar a los agentes RL. En esta sección, analizamos las formas de controlar y visualizar el progreso del entrenamiento. 

Seguimiento del progreso de la formación

Es útil registrar varias métricas durante el proceso de entrenamiento para controlar su progreso. Paquetes como TensorBoard facilitan el registro directo de los resultados durante el proceso de entrenamiento. Por ejemplo, el SummaryWriter de TensorBoard te permite llamarlo dentro del bucle de entrenamiento para registrar las métricas. El siguiente pseudocódigo muestra cómo hacerlo:

from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter(log_dir="training_logs")

for _ in training_data (): # training loop
    loss = … 
    loss.backprop() 
    writer.add_scalar(“Loss - “, loss.item(), step)
   … 

writer.close() 

Tras finalizar el entrenamiento y el registro, puedes trazar y visualizar los resultados. Por ejemplo, utiliza el comando tensorboard para visualizar el progreso del entrenamiento .

tensorboard --logdir=”training_logs”

Depurar con TorchRL

La depuración es esencial para verificar la interacción del agente con el entorno. Como primer paso, debes comprobar el espacio de acción y el espacio de observación. La documentación del entorno debe incluir las especificaciones del mismo. Por ejemplo, la documentación de CartPole muestra que: 

  • El espacio de acción incluye dos valores discretos: 0 (para empujar el carro hacia la izquierda) y 1 (para empujar el carro hacia la derecha) 
  • El espacio de observación tiene 4 valores: 
    • Posición del carro, con un valor entre 
    • -4,8 y +4,8. 
    • Velocidad del carro, con cualquier valor numérico real
    • Ángulo del polo, con un valor entre -24 ° y +24 °
    • Velocidad angular del polo, con cualquier valor numérico real

Inspecciona los espacios de observación y acción para comprobar que corresponden a los valores esperados. Por ejemplo:

print("Observation space: ", env.observation_spec)
print("Action space: ", env.action_spec)

Además, puedes extraer muestras aleatorias de los espacios de observación y acción y comprobar sus valores: 

print("Sample Observation:", base_env.observation_spec.sample().get("observation"))
print("Sample Action:", base_env.action_spec.sample())

El resultado se parece al siguiente ejemplo:

Sample Observation: tensor([-4.5960e+00,  3.4028e+38,  2.2261e-02,  3.4028e+38])
Sample Action: tensor([1, 0])

Observa que los 4 valores de estado están dentro de los rangos especificados anteriormente. Repite los comandos anteriores unas cuantas veces y observa que los distintos valores de salida (muestreados aleatoriamente) entran dentro de los rangos especificados. 

Los dos comandos anteriores se basan en el entorno base, importado directamente de Gimnasio. En la práctica, utilizamos el entorno transformado, que aplica transformaciones, como la normalización, sobre los valores originales del tensor. Así, cuando utilices el entorno transformado de TorchRL para extraer una muestra de observaciones, es posible que sus valores ya no estén dentro del mismo intervalo que en el entorno original del Gimnasio. 

Por ejemplo, extrae una muestra del entorno transformado:  

print("Sample Observation:", env.observation_spec.sample().get("observation"))

La salida se parece a: 

Sample Observation: tensor([-57.5869,      nan,   4.5276,      nan])

Observa que los valores de posición del carro y ángulo del poste están fuera de los rangos. Esto se debe a que los valores del tensor se han normalizado. Además, la velocidad del carro y la velocidad angular del poste tienen valores nan porque el agente aún no ha empezado a interactuar con el entorno .

Visualizar el rendimiento de los agentes

Además de trazar el progreso del entrenamiento, puede ser útil renderizar el entorno y observar visualmente las interacciones del agente. Esto puede dar una idea de cómo está actuando el agente. 

La forma más pragmática de visualizar el entorno es renderizando un vídeo. Necesitas algunos paquetes adicionales: 

  • torchvisiontrabajar con archivos multimedia
  • avpara envolver el paquete ffmpeg de modo que pueda interactuar con PyTorch .
pip install torchvision
pip install av==12.0.0

Después de terminar la formación, sigue estos pasos preparatorios para renderizar el vídeo:

  • Declara la ruta (relativa) para almacenar el vídeo.
  • Inicializa el registrador para almacenar los resultados de las interacciones del agente en formato CSV.
  • Inicializa la grabadora de vídeo para utilizar los registros CSV para generar un vídeo.
  • Transforma el entorno TorchRL para incluir el grabador de vídeo.
…
# training loop
# for _ in …
…
…

from torchrl.record import CSVLogger, VideoRecorder 

path = "./training_loop"
logger = CSVLogger(exp_name="dqn", log_dir=path, video_format="mp4")
video_recorder = VideoRecorder(logger, tag="video")
record_env = TransformedEnv(
    GymEnv("CartPole-v1", from_pixels=True, pixels_only=False), video_recorder
)

Ejecuta la política (entrenada) en el entorno y vuelca las representaciones en el archivo de vídeo:

record_env.rollout(max_steps=1000, policy=policy)
video_recorder.dump()

Tras ejecutar el programa, encontrarás el vídeo en la ruta del directorio (./training_loop en el fragmento anterior) que habías establecido anteriormente. Ten en cuenta tque el cuaderno de trabajo DataLab para implementar DQN utilizando TorchRL no incluye el código para crear el vídeo porque exportar archivos de vídeo con un cuaderno online es difícil. Ejecuta el programa (y añade los pasos anteriores) en un ordenador local o servidor para grabar vídeos.

Buenas prácticas para utilizar TorchRL

Por último, abordemos algunas buenas prácticas. Estas son mis recomendaciones.

Empieza con entornos sencillos

A pesar de la facilidad de desarrollo debida a TorchRL, entrenar a los agentes RL para que funcionen bien en entornos complejos sigue siendo un reto. Por tanto, antes de intentar resolver problemas difíciles, es esencial resolver entornos sencillos como CartPole. 

La modularidad de TorchRL permite experimentar con distintos algoritmos y parámetros. Antes de utilizar en la práctica algoritmos alternativos y opciones de personalización, es necesario conocer un poco su funcionamiento. Esto se hace mejor en un entorno sencillo, donde los efectos de los cambios individuales sean fáciles de observar visualmente. 

Experimenta con los hiperparámetros

El rendimiento del entrenamiento de los modelos de RL es sensible a varios hiperparámetros, como la tasa de aprendizaje, la tasa de exploración, la tasa de descuento y otros hiperparámetros específicos del algoritmo, como el ratio de recorte para la PPO. Por ejemplo, una tasa de aprendizaje demasiado alta hace que el entrenamiento sea inestable, mientras que una tasa de aprendizaje demasiado baja tarda demasiado en converger. 

Del mismo modo, una tasa de exploración muy alta impide que el entrenamiento converja, mientras que una tasa de exploración demasiado baja puede impedir que el agente descubra el camino óptimo. 

No existe ninguna fórmula para determinar los valores correctos de los hiperparámetros. Existen directrices y valores recomendados para entornos estándar, pero pueden no aplicarse a todos los problemas. Por tanto, es necesario experimentar con diversos métodos, como la búsqueda en cuadrícula o la búsqueda aleatoria, para determinar los mejores valores de los hiperparámetros. 

También es posible utilizar bibliotecas automatizadas como Weights & Biases Sweeps u Optuna para comprobarel rendimiento del entrenamiento en varias combinaciones de hiperparámetros .

Aprovecha los algoritmos preconstruidos

Como has visto en los ejemplos anteriores, utilizar los módulos preconstruidos de TorchRL ahorra un considerable esfuerzo de desarrollo. La alternativa es construir toda la funcionalidad desde cero, lo que consumiría mucho más tiempo y costes de desarrollo y prueba. 

Como muchos desarrolladores utilizan los módulos de TorchRL, también se benefician de haber sido probados en varios escenarios. Por tanto, puedes esperar que estén más libres de errores que un módulo hecho a medida. Por tanto, para la mayoría de los casos de uso estándar, es muy recomendable utilizar módulos preconstruidos. 

Conclusión

En este artículo, cubrimos los conceptos básicos de TorchRL, un marco basado en PyTorch para implementar algoritmos de RL. También vimos ejemplos prácticos del uso de TorchRL para aplicar soluciones más sencillas, como el Aprendizaje Q Profundo, y algoritmos más complejos, como el PPO. 

Como siguiente paso, puedes utilizar estos programas como base para experimentar con otros entornos y algoritmos. 

Si quieres profundizar en los fundamentos del RL y sus aplicaciones prácticas, consulta Aprendizaje por Refuerzo con Gymnasium en Python para obtener más experiencia práctica con entornos basados en Gymnasium.

Conceptos de Inteligencia Artificial (IA) en Python

Empieza con la IA

Arun Nanda's photo
Author
Arun Nanda
LinkedIn

Arun es un antiguo fundador de startups que disfruta construyendo cosas nuevas. Actualmente explora los fundamentos técnicos y matemáticos de la Inteligencia Artificial. Le encanta compartir lo que ha aprendido, así que escribe sobre ello.

Además de en DataCamp, puedes leer sus publicaciones en Medium, Airbyte y Vultr.

Temas

¡Aprende más sobre el aprendizaje por refuerzo con estos cursos!

curso

Deep Reinforcement Learning in Python

4 hr
1.5K
Learn and use powerful Deep Reinforcement Learning algorithms, including refinement and optimization techniques.
Ver detallesRight Arrow
Comienza el curso
Ver másRight Arrow
Relacionado

tutorial

Introducción al Q-Learning: Tutorial para principiantes

Conozca el algoritmo de aprendizaje por refuerzo sin modelos más popular con un tutorial de Python.
Abid Ali Awan's photo

Abid Ali Awan

16 min

tutorial

Guía de torchchat de PyTorch: Configuración local con Python

Aprende a configurar el torchat de PyTorch localmente con Python en este tutorial práctico, que proporciona orientación paso a paso y ejemplos.
François Aubry's photo

François Aubry

tutorial

Construir un transformador con PyTorch

Aprende a construir un modelo Transformer utilizando PyTorch, una potente herramienta del aprendizaje automático moderno.
Arjun Sarkar's photo

Arjun Sarkar

26 min

tutorial

Construir agentes LangChain para automatizar tareas en Python

Un tutorial completo sobre la construcción de agentes LangChain multiherramienta para automatizar tareas en Python utilizando LLMs y modelos de chat utilizando OpenAI.
Bex Tuychiev's photo

Bex Tuychiev

14 min

tutorial

Cómo formar a un LLM con PyTorch

Domine el proceso de entrenamiento de grandes modelos lingüísticos con PyTorch, desde la configuración inicial hasta la implementación final.
Zoumana Keita 's photo

Zoumana Keita

8 min

tutorial

Tutorial del Optimizador Adam: Intuición e implementación en Python

Comprender y aplicar el optimizador Adam en Python. Aprende la intuición, las matemáticas y las aplicaciones prácticas del aprendizaje automático con PyTorch
Bex Tuychiev's photo

Bex Tuychiev

14 min

Ver másVer más