Revisión de la evaluación comparativa de métodos de aprendizaje de refuerzo tabular

Publicando mi publicación anterior En los métodos de aprendizaje de refuerzo tabular (RL) de la evaluación comparativa, no pude sacudir la sensación de que algo no estaba bien. Los resultados miraron, y no estaba completamente satisfecho con cómo resultaron.

Aún así, continué con la serie Post, cambiando el enfoque a los juegos de jugadores múltiples y métodos de solución aproximados. Para apoyar esto, he estado refactorizando constantemente el marco original que construimos. La nueva versión es más limpia, más general y más fácil de usar. En el proceso, también ayudó a descubrir algunos errores y problemas de cargos en algunos de los algoritmos anteriores (más sobre eso más adelante).

En esta publicación, presentaré el marco actualizado, resaltaré los errores que cometí, compartiré resultados corregidos y reflexionaré sobre las lecciones clave aprendidas, preparando el escenario para experimentos más complejos por venir.

El código actualizado se puede encontrar en Github.

Estructura

El mayor cambio de la versión anterior del código es que los métodos de solución RL ahora se implementan como clases. Estas clases exponen métodos comunes como act() (para seleccionar acciones) y update() (para ajustar los parámetros del modelo).

Complementando esto, un guión de entrenamiento unificado administra la interacción con el entorno: genera episodios y los alimenta al método apropiado para el aprendizaje, utilizando la interfaz compartida proporcionada por esos métodos de clase.

Esta refactorización simplifica y estandariza significativamente el proceso de capacitación. Anteriormente, cada método tenía su propia lógica de entrenamiento independiente. Ahora, el entrenamiento está centralizado, y el papel de cada método está claramente definido y modular.

Antes de sumergirse en las clases de métodos en detalle, veamos primero el bucle de entrenamiento para entornos para un solo jugador:

def train_single_player(
    env: ParametrizedEnv,
    method: RLMethod,
    max_steps: int = 100,
    callback: Callable | None = None,
) -> tuple[bool, int]:
    """Trains a method on single-player environments.

    Args:
        env: env to use
        method: method to use
        max_steps: maximal number of update steps
        callback: callback to determine if method already solves the given problem

    Returns:
        tuple of success, found policy, number of update steps
    """
    for step in range(max_steps):
        observation, _ = env.env.reset()
        terminated = truncated = False

        episode = []
        cur_episode_len = 0

        while not terminated and not truncated:
            action = method.act(observation, step)

            observation_new, reward, terminated, truncated, _ = env.step(
                action, observation
            )

            episode.append(ReplayItem(observation, action, reward))
            method.update(episode, step)

            observation = observation_new

            # NOTE: this is highly dependent on environment size
            cur_episode_len += 1
            if cur_episode_len > env.get_max_num_steps():
                break

        episode.append(ReplayItem(observation_new, -1, reward, []))
        method.finalize(episode, step)

        if callback and callback(method, step):
            return True, step

    env.env.close()

    return False, step

Visualizemos cómo se ve un episodio completado, y cuando el update() y finalize() Los métodos se llaman durante el proceso:

Imagen del autor

Después de que se procese cada elemento de repetición, considerando un estado, la acción tomada y la recompensa recibida, el método update() Se llama a la función para ajustar los parámetros internos del modelo. El comportamiento específico de esta función depende del algoritmo que se utiliza.

Para darle un ejemplo concreto, echemos un vistazo rápido a cómo funciona esto Q-Learning.

Recuerde la regla de actualización de Q-Learning:

Imagen de [1]

Cuando la segunda llamada a update() ocurre, tenemos sT= S1AT = A1 y RT+1 = R2.

Usando esta información, el agente Q-Learning actualiza sus estimaciones de valor en consecuencia.

Métodos no compatibles

Programación dinámica (DP) Los métodos no se ajustan anteriormente a la estructura introducida, ya que se basan en iterar sobre todos los estados del medio ambiente. Por esa razón, dejamos su código intacto y manejamos capacitarlos de manera diferente.

Además, eliminamos por completo el soporte para Barrido priorizado. Además, aquí necesitamos iterar sobre los estados de alguna manera para encontrar estados predecesor, que, nuevamente, no encajan en nuestra estructura de entrenamiento de actualizaciones, y, lo que es más importante, no es factible para juegos de jugadores múltiples más complejos, donde el número de estados es mucho más grande y difícil de iterar.

Dado que este método de todos modos no produjo buenos resultados, nos centramos en los restantes. Nota: Se realizó un razonamiento similar para los métodos DP: estos no se pueden extender tan fácilmente a los juegos de múltiples jugadores y, por lo tanto, será de menor interés en el futuro.

Insectos

Los errores ocurren, en todas partes, y este proyecto no es una excepción. En esta sección, destacaré un error particularmente impactante que llegó a los resultados de la publicación anterior, junto con algunos cambios y mejoras menores. También explicaré cómo estos afectaron los resultados anteriores.

Cálculo de probabilidad de acción incorrecta

Algunos métodos requieren la probabilidad de una acción elegida durante el paso de actualización. En la versión de código anterior, teníamos:

def _get_action_prob(Q: np.ndarray) -> float:
        return (
            Q[observation_new, a] / sum(Q[observation_new, :])
            if sum(Q[observation_new, :])
            else 1
        )

Esto funcionó Solo para valores Q estrictamente positivospero se rompió cuando los valores Q fueron negativos, lo que hace que la normalización no sea válida.

La versión corregida maneja los valores Q positivos y negativos correctamente utilizando un enfoque Softmax:

def _get_action_prob(self, observation: int, action: int) -> float:
        probs = [self.Q[observation, a] for a in range(self.env.get_action_space_len())]
        probs = np.exp(probs - np.max(probs))
        return probs[action] / sum(probs)

Este error impactó significativamente Sarsa esperado y copia de seguridad del árbol de N-pasosya que sus actualizaciones se basaron en gran medida en las probabilidades de acción.

TIPE-Breaking en la selección de acción codiciosa

Anteriormente, al generar episodios, seleccionamos la acción codiciosa o se muestreamos al azar con ε-Greedy Logic:

def get_eps_greedy_action(q_values: np.ndarray, eps: float = 0.05) -> int:
    if random.uniform(0, 1) < eps or np.all(q_values == q_values[0]):
        return int(np.random.choice([a for a in range(len(q_values))]))
    else:
        return int(np.argmax(q_values))

Sin embargo, esto no se manejó correctamente vínculoes decir, cuando múltiples acciones compartieron el mismo valor Q máximo. El actualizado act() El método ahora incluye la ruptura de lazos justos:

def act(
        self, state: int, step: int | None = None, mask: np.ndarray | None = None
    ) -> int:
        allowed_actions = self.get_allowed_actions(mask)
        if self._train and step and random.uniform(0, 1) < self.env.eps(step):
            return random.choice(allowed_actions)
        else:
            q_values = [self.Q[state, a] for a in allowed_actions]
            max_q = max(q_values)
            max_actions = [a for a, q in zip(allowed_actions, q_values) if q == max_q]
            return random.choice(max_actions)

Un pequeño cambio, pero posiblemente bastante relevante, dado que este EG estimula una selección de acción más exploratoria al comienzo de cada entrenamiento, donde todos los valores Q son iguales.

Este pequeño cambio puede tener un impacto notable, especialmente temprano en el entrenamiento, cuando todos los valores Q se inicializan por igual. Fomenta una estrategia de exploración más diversa durante la fase temprana crítica.

Como se discutió anteriormente, y como veremos nuevamente a continuación, los métodos RL exhiben una alta variación, lo que hace que el impacto de tales cambios sea difícil de medir con precisión. Sin embargo, este ajuste pareció Mejorar ligeramente El rendimiento de varios métodos: Sarsa, Q-Learning, Doble Q-learningy Sarsa-n.

Resultados actualizados

Examinemos ahora los resultados actualizados: para completar, incluimos todos los métodos, no solo los mejorados.

Pero primero, un recordatorio rápido de la tarea que estamos resolviendo: estamos trabajando con el gimnasio Mundo de la red ambiente [2] -Esencialmente una tarea de resolución de laberinuras:

Imagen del autor

El agente debe navegar desde la parte superior izquierda hasta la parte inferior derecha de la cuadrícula mientras evita los lagos helados.

Para evaluar el rendimiento de cada método, escalamos el tamaño del mundo de la red y medimos el número de Actualizar pasos hasta la convergencia.

Métodos de Monte Carlo

Estos métodos no se vieron afectados por los cambios de implementación recientes, por lo que observamos los resultados consistentes con nuestros hallazgos anteriores:

  • Ambos son capaces de resolver entornos hasta 25 × 25 en tamaño.
  • En la política MC funciona un poco mejor que la policía.
Imagen del autor

Métodos de diferencia temporal

Para estos, medimos los siguientes resultados:

Imagen del autor

Para estos, notamos inmediatamente que el Sarsa esperado ahora le va mucho mejor, debido a la corrección de errores mencionados anteriormente sobre la calculación de las probabilidades de acción.

Pero también los otros métodos funcionan mejor: como se mencionó anteriormente, esto podría ser simplemente una posibilidad / varianza, o ser una consecuencia de las otras mejoras menores que hicimos, en particular el mejor manejo de lazos durante la selección de la acción.

TD-N

Para los métodos TD-N, nuestros resultados se ven muy diferentes:

Imagen del autor

Sarsa-N también ha mejorado, probablemente por razones similares a las que se discutió en la última sección, pero en particular la copia de seguridad del árbol N-Step ahora funciona muy bien, lo que demuestra que con la selección de acción correcta, este es un método de solución muy poderoso.

Planificación

Para la planificación, solo tenemos Dyna-Q, lo que también parece haber mejorado ligeramente:

Imagen del autor

Comparando los mejores métodos de solución en entornos más grandes

Con eso, visualicemos los métodos de mejor rendimiento de todas las categorías en un diagrama. Debido a la eliminación de algunos métodos como DP, ahora seleccioné MC en la política, Sarsa, Q-Learning, Sarsa-N, respaldo de árbol N, Dyna-Q.

Comenzamos mostrando resultados para mundos de cuadrícula hasta el tamaño 50 x 50:

Imagen del autor

Observamos en la política MC Para realizar sorprendentemente bien, de acuerdo con los hallazgos anteriores. Su fuerza probablemente surge de su Simplicidad y estimaciones imparcialesque funcionan bien para episodios de corto a mediano longitud.

Sin embargo, a diferencia de la publicación anterior, copia de seguridad del árbol de N-pasos Claramente emerge como el método de rendimiento superior. Esto se alinea con la teoría: su uso de copias de seguridad de múltiples pasos esperadas habilita Propagación de valor suave y establecombinando las fortalezas de las actualizaciones fuera de la política con la estabilidad del aprendizaje en la política.

A continuación, observamos un grupo medio: Sarsa, Q-Learning y Dyna-Q – con Sarsa superando ligeramente a los demás.
Es algo sorprendente que las actualizaciones basadas en modelos en DYNA-Q no conduzcan a un mejor rendimiento. Esto podría apuntar a las limitaciones en la precisión del modelo o el número de pasos de planificación utilizados. Q-Learning tiende a tener un rendimiento inferior debido al aumento de la varianza introducida por su naturaleza fuera de política.

El Método de peor rendimiento En este experimento es Sarsa-nconsistente con observaciones anteriores. Sospechamos que la degradación en el rendimiento proviene del aumento varianza y sesgo Debido al muestreo de N-Step sin expectativa sobre las acciones.

Todavía es algo inesperado que los métodos MC superan a TD en este entorno; tradicionalmente, se espera que los métodos de TD funcionen mejor en entornos grandes. Sin embargo, esto se mitiga en nuestra configuración por el Estrategia de configuración de recompensas: Proporcionamos una pequeña recompensa positiva en cada paso a medida que el agente se acerca a la meta. Esto alivia una de las principales debilidades de MC: un bajo rendimiento en escasos entornos de recompensas.

Conclusión y aprendizajes

En esta publicación, compartimos actualizaciones del marco RL desarrollado sobre esta serie. Junto con varias mejoras, solucionamos algunos errores, que mejoraron significativamente el rendimiento del algoritmo.

Luego aplicamos los métodos actualizados a entornos de mundo de red cada vez más más grandes, con los siguientes hallazgos:

  • copia de seguridad del árbol de N-pasos Surgió como el mejor método en general, gracias a sus actualizaciones esperadas de varios pasos que combinan los beneficios del aprendizaje de la política y fuera de la política.
  • Métodos de Monte Carlo seguido, mostrando un rendimiento sorprendentemente fuerte debido a sus estimaciones imparciales y las recompensas intermedias de recompensa para guiar el aprendizaje.
  • Un grupo de Métodos de TD -Q-Learning, Sarsa y Dyna-Q-Siguieron. A pesar de las actualizaciones de modelos de DYNA-Q, no superó significativamente a sus homólogos sin modelo.
  • Sarsa-n se desempeñó lo peor, probablemente debido al sesgo y la varianza compuestos introducidos por el muestreo de los retornos de N-Step.

¡Gracias por leer esta actualización! Estén atentos para obtener más contenido: a continuación, cubrimos Juegos y entornos de múltiples jugadores.

Otras publicaciones de esta serie

Referencias

[1] http://incompleteideas.net/book/rlbook2020.pdf

[2] https://gymnasium.farama.org/