Saltar al contenido

Aprendizaje por refuerzo con Stable Baselines 3

    stable baselines portada

    Uno de los grandes problemas que sucedía con el aprendizaje por refuerzo es que no existía una base común que reúna todos los posibles agentes que se hacían para poder ser comparados. Debido a esto cada programador hacía su propio entorno, lo que hacía que fuera muy difícil poder comparar entre un agente desarrollado por una persona y otro desarrollado por otra persona. Cada programación se hacía su propio entorno que, normalmente, solo era válido ese agente.

    Algo similar a este problema es lo que yo mismo he hecho en la anterior entrada sobre aprendizaje por refuerzo. En ella creamos un entorno para jugar a tres en línea desde cero, creando todo, desde el entorno hasta el modo de crear un poco de inteligencia.

    Para intentar resolver este problema y para, además, hacer la vida más fácil a los programadores surge stable baselines (que ahora se encuentra ya en su versión 3). Para ver realmente como funciona este framework vamos a hacer lo mismo que en la entrada anterior: Un tres en raya. Así que vayamos a ello.

    ¿Qué es stable baselines 3?

    La respuesta más sencilla es decir que esta librería sirve para ofrecernos unas implementaciones de algoritmos de aprendizaje por refuerzo utilizando pyTorch. Es decir, que nos fácilita mucho más la creación de algoritmos para nuestros agentes, lo que antes nos iba a costar programar un buen rato, utilizando stable baselines 3 nos saldrán muy pocas líneas de código.

    Otro de los placeres que nos brinda utilizar esta librería es que tiene muy buena documentación (eso sí, en perfecto inglés) lo cual hace que la curva de aprendizaje para realizar aprendizaje por refuerzo no sea tan elevada. En la documentación nos podemos encontrar desde como hacer la instalación hasta ejemplos muy complejos, que merecen la pena ser analizados para ver entender como realizan ellos mismos el código.

    Además de que su código ha sido muy probado, con lo que la fiabilidad es muy alta, está basado en la interfaz más importante que existe sobre aprendizaje reforzado OpenAI Gym. Todos los algoritmos que implementa stable baselines 3 tienen detrás código de OpenAI, lo cual es una garantía de seguridad.

    La instalación de stable baselines es tan sencilla como cualquier otra en python. Solo hace falta poner:

    pip install stable-baselines3[extra]

    Pero dejémonos de presentaciones y vayamos al grano que es lo que estamos deseando. También comentaros que todo el código fuente de esta entrada lo tenéis en mi github (o en la sección de código fuente), donde he colgado el cuaderno Jupyter con el que he hecho esta entrada (en el cual he utilizada google colab, por si queréis probar vosotros también)

    La clase principal GYM

    Antes de empezar con el código principal hay que hacer unos imports que nos van ser utilidad:

    
    import numpy as np        # Para los tipos de las variables
    import gym                # El entorno del gimnasio
    from gym import spaces    # Para la declaración del entorno
    import random             # Para hacer un jugador aleatorio sin mucha inteligencia
    
    

    La clase que vamos a generar tiene que heredar todo lo que tiene la clase gym.Env, y necesita que tenga dentro lo siguiente:

    • __ init__: El sitio donde es obligatorio crear dos variables: self.action_space y self.observation_space. Estas dos variables solo pueden ser de varios tipos pero las que más se usan son: “Discrete” para espacios de una dimensión y “Box” para espacios de más dimensiones.
    • Reset: devuelve un valor dentro del “observation_space” donde, como su palabra indica, se resetearán los valores del entorno a los iniciales. Si se usa Discrete tiene que devolver un integer, y si se usa box un numpy.array.
    • Step: Esta función tendrá un parámetro de entrada que se llamará action y que estará dentro del “action_space”. En esta variable es donde el agente lleva la acción que va a hacer y el entorno le devuelve el estado en el que se encuentra después de esa acción. El valor de retorno es una tupla cuádruple (en este orden):
      • State: es igual que la variable que devuelve Reset, un valor para action_space.
      • Reward: El numero de la recompensa que ha recibido el agente por su acción.
      • Done: Cuando se acabe la tarea devolverá True (y habría que hacerle un reset) mientras tanto será False.
      • Info: Un diccionario con información complementaria de la ejecución.
    • Render: Es la función que hará que el resultado que tenemos en “state” se muestre de una forma más entendible para los humanos. Para esto también hay que definir al inicio la variable “metadata = {‘render.modes’: [‘human’]}”.
    • Close y seed: Estas dos funciones no las vamos a necesitar, al menos por ahora, ya que “close” se utiliza para dar por finalizada la ejecución y limpiar el sistema y “seed” es para cuando utilicemos una variable aleatoria y queremos que el resultado sea reproducible.

    El código que vamos a introducir en la clase será muy similar al que introducimos en el anterior juego de 3 en raya desarrollado desde cero, pero adaptado a stable baselines. Además de las librerías obligatorias incluiremos algunas nuevas.

    Como no hemos querido complicar el código en exceso, nuestro agente siempre empezará él la partida y otro jugador simplemente pondrá fichas aleatorias sin mucho sentido. Con ello simplificamos algo más el código y así veremos resultados antes.

    Probar la clase

    Stable baselines 3 nos proporciona una función que comprueba si la clase está bien desarrollada y tiene todo lo necesario para poder llevar a cabo un aprendizaje por refuerzo (otra cosa es que la lógica y las recompensas estén bien, claro).

    El código para hacer esta comprobación es muy simple:

    
    from stable_baselines3.common.env_checker import check_env
    env = tresEnRaya()
    check_env(env)
    
    

    Si se ejecuta este código y no devuelve nada la clase estará bien hecha y preparada para hacer el aprendizaje por refuerzo. Si algo falla aquí nos devolverá un error indicándonos de forma bastante precisa donde se encuentra el fallo en la clase que hemos generado.

    No se trata de un error normal que nos devolvería Python en condiciones normales, ya que aquí comprueba que la estructura de la clase sea la correcta para hacer el aprendizaje, cosa que Python no realizaría. Es muy normal que si hacéis este proyecto en Jupyter y ejecutáis este código en una celda funcione correctamente, pero luego al probar la clase os falle, lo cual significa que el código es correcto para Python, pero no para stable baselines.

    Probar el entorno

    El siguiente paso es probar el entorno. Esto también nos lo facilita bastante stable baselines, ya que solo son necesarias 2 líneas para hacerlo (sin incluir los imports):

    
    from stable_baselines3 import PPO
    from stable_baselines3.ppo.policies import MlpPolicy
    
    env = tresEnRaya()
    model = PPO(MlpPolicy, env, verbose=0).learn(int(50000))
    
    

    Esta parte requiere un poco más de explicación ya que aquí está la parte en la que se le introduce la forma en la que queremos que haga el aprendizaje.

    En nuestro caso hemos utilizado PPO y la política MlpPolicy, pero existen muchas otras, como podéis observar en la siguiente imagen.

    Tipos de aprendizaje que se pueden hacer con stable baselines

    También como se puede observar en la última línea, hemos puesto un entrenamiento de 50.000 partidas. Lo cual ya es bastante para este caso de prueba.

    Probar el entorno

    Después de todo el entrenamiento ya estamos listos para probar como funciona nuestro agente con la nueva inteligencia artificial que ha aprendido. Para ello utilizamos el siguiente código:

    env = tresEnRaya()
    recompensas_finales = []
    num_episodes = 5
    for i in range(num_episodes):
      recompensas_episodio = []
      done = False
      obs = env.reset()
      while not done:
        action, _ = model.predict(obs)
        obs, reward, done, info = env.step(action)
        recompensas_episodio.append(reward)
      env.render()
      recompensas_finales.append(sum(recompensas_episodio))
    

    Con esto haremos que el agente con el entrenamiento realice 5 partidas, y las puntuaciones que vaya obteniendo por cada partida las vamos a ir almacenando en la variable “recompensas_finales”.

    Como se ve en cada iteración se resetea el entorno entero y luego se entra en un bucle en el cual el agente va dando predicciones y va almacenando las recompensas por episodio (por si las queremos luego comprobar una a una, que siempre viene bien para hacer debug).

    Después de que termine cada iteración hacemos un “render” para mostrar como quedó el tablero de juego en cada partida.

    Conclusiones

    Como se ha podido ver programar con stable baselines hace que la programación sea mucho más sencilla que cuando realizamos el mismo juego desde cero. La programación del algoritmo ya viene predefinida y no es necesario pensar como nuestro agente va aprender desde cero, sino que podemos ir probando los diferentes algoritmos hasta probar cual se adapta mejora a nuestro agente.

    El agente para el tres en raya que hemos desarrollado todavía es muy mejorable: solamente juega empezando él la partida, siempre con las mismas fichas; y seguro que hay más detalles que son mejorables, pero no era el fin de esta entrada hacer un juego perfecto sino hacer una pequeña introducción a stable baselines para realizar aprendizaje reforzado. Espero que os hayan quedado claras las bases de como hacerlo.

    Como siempre, cualquier duda o aclaración os podéis poner en contacto conmigo por cualquiera de los medios disponibles para ello.

    Deja una respuesta

    Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *