Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Réalisation d'une IA jouant au jeu Acrobot avec MCTS

Présentation du jeu Acrobot

Acrobot

Le jeu Acrobot est un environnement de type contrôle classique disponible dans Gymnasium. Le but du jeu est simple :

Un système à deux segments articulés (un pendule double) doit amener l’extrémité du second segment au dessus d’une certaine hauteur. L’agent peut appliquer un couple moteur limité sur l’articulation centrale, ce qui influence le mouvement des deux segments. L’agent reçoit des récompenses négatives à chaque étape, ce qui a pour but d'encourager l'agent à trouver la solution le plus rapidement possible. La réussite est atteinte lorsque l’extrémité du pendule dépasse la hauteur cible (la ligne noire).

Cet environnement est idéal pour expérimenter avec des algorithmes de planification comme le MCTS, car il combine une dynamique continue avec des actions discrètes simples, tout en offrant un défi non trivial de contrôle et de planification à court et long terme.

Monte Carlo Tree Search (MCTS)

Le Monte Carlo Tree Search (MCTS) est un algorithme de planification et d’apprentissage par simulation qui permet à un agent de choisir des actions en explorant un arbre de décisions. L’idée est d’estimer la valeur des actions en simulant des parties complètes depuis l’état courant et en utilisant ces simulations pour guider le choix :

  • Chaque nœud de l’arbre représente un état de l’environnement, et chaque branche correspond à une action possible.
  • L’agent sélectionne les actions à explorer en équilibrant exploitation (choisir les actions avec de bonnes valeurs estimées) et exploration (essayer des actions moins connues) via une formule comme UCB1 :

où :

  • (Vi) : valeur cumulée du nœud (i)
  • (Ni) : nombre de visites du nœud (i)
  • (Np) : nombre de visites du parent
  • (c) : coefficient d’exploration

(Algorithme UCB1, réalisé en python, permettant de choisir le meilleur noeud parmis plusieurs)

def ucb1(children, c=1.0):
    parent_visits = sum(child.visits for child in children.values()) + 1e-5
    best_score = -float('inf')
    best_child = None
    for action, child in children.items():
        if child.visits == 0:
            score = float('inf')
        else:
            exploit = child.value / child.visits
            explore = math.sqrt(math.log(parent_visits) / child.visits)
            score = exploit + c * explore
        if score > best_score:
            best_score = score
            best_child = child
    return best_child
  • Après chaque simulation, la récompense finale est propagée vers le haut de l’arbre (backpropagation) pour mettre à jour les valeurs des nœuds traversés.
  • À la fin des itérations, l’action choisie est celle qui maximise la valeur moyenne estimée au nœud racine.

Ainsi, MCTS permet à l’agent de planifier plusieurs coups à l’avance sans connaître explicitement la dynamique de l’environnement, en s’appuyant sur des simulations pour guider ses décisions.

Étapes pour réaliser l’IA

Réalisation des fonctions nécessaires :

Importation des bibliothèques nécessaires et création de l'objet "Node" (noeud)

L'objet Node (noeud) possède :

  • Un état
  • un parent
  • un enfant
  • le nombre de visites
  • une valeur
import gymnasium as gym
import math
import random
import copy

env = gym.make("Acrobot-v1")
obs, info = env.reset()

# Crtion d'un objet représentant le noeud d'un arbre
class Node:
    def __init__(self, state, parent):
        self.parent = parent
        self.state = state
        self.visits = 0
        self.value = 0.0
        self.children = {}

La fonction de simulation

Cette fonction permet, à partir d'un noeud, de faire X simulations à partir du noeud de la fin du jeu.

def simulate(Inode: Node, max_steps):
    total_reward = 0
    env_simu = copy.deepcopy(env.unwrapped)
    env_simu.state = Inode.state
    steps = 0
    done = False 
    
    #Tant que le programme n'a pas réussi, ou que le max_steps n'a pas été atteint, on réalise des simulations
    while not done and steps < max_steps:
        steps += 1
        action = env_simu.action_space.sample()
        obs, reward, terminated, truncated, _ = env_simu.step(action)
        total_reward += reward

        if terminated:
            done = True

    return env_simu.state, done, total_reward

La fonction "Backpropagate"

La méthode backpropagate joue un rôle central dans le MCTS en permettant de remonter la récompense obtenue après une simulation à travers l’arbre de décision

def backpropagate(node: Node, reward):
    while node is not None:
        node.visits += 1
        node.value += reward
        node = node.parent

La fonction de réalisation de cycle

La fonction cycle représente une itération complète de MCTS à partir d’un nœud donné. Elle commence par créer les enfants du nœud si nécessaire, puis sélectionne un noeud au hasard parmi les enfants nouvellement créés. À partir de ce nœud, plusieurs simulations sont effectuées, chaque simulation produisant une récompense cumulée qui est ensuite propagée vers le haut de l’arbre grâce à backpropagate

def cycle(noeud, env):
    simulate_node = noeud
    all_actions = list(range(env.action_space.n))
    if noeud.children == {}:
        for action in all_actions:
            noeud.children[action] = Node(state=copy.deepcopy(noeud.state), parent=noeud)
        simulate_node = random.choice(list(noeud.children.values()))
    for _ in range(random.randint(50, 100)):
        final_state, done, epdone, total_reward = simulate(simulate_node, max_steps=300)
        simulate_node.state = final_state
        backpropagate(simulate_node, reward=total_reward)
    return final_state, done, epdone, total_reward

La fonction "MCTS"

Dans cette fonction, on rassemble tout ce que l'on a codé auparavant. On commence par initialisé le noeud racine ainsi que certaines variables. Ensuite, dans la boucle "while", tant que le jeu n'est pas terminé, ou que les episodes ne se sont pas écoulés et que le compteur est inférieur aux nombres d'itérations choisis, on effectue des cycles.

def mcts(env, iterations=100):
    racine = Node(state=copy.deepcopy(env.unwrapped.state), parent=None)
    done = False
    compteur = 0
    while not done and compteur < iterations:
        final_state, done, epdone, total_reward = cycle(noeud=racine, env=env)
        compteur += 1
        print('episode :', compteur)
        print('recompense actuel :', total_reward)

    if done:
        print('Agent a reussi')
    else:
        print('Agent a pas réussi')

    print("Nb d'itérations :", compteur)
    print("Nombre de visites racine :", racine.visits)
    print("Nombre d’enfants :", len(racine.children))
    return racine

Observations

  • Après réflexion, la réalisation d’un MCTS sur un jeu tel que Acrobot n’est pas très idéale car l’environnement possède un espace d’état continu et infini avec une dynamique physique complexe, or, le MCTS est efficace pour des espaces discret et limitées, comme le jeu de plateau Taxi. Pour ce type d’environnement, le Q-Learning est plus adapté pour apprendre une politique optimale.