Différences
Ci-dessous, les différences entre deux révisions de la page.
Les deux révisions précédentes Révision précédente Prochaine révision | Révision précédente | ||
public:rl_tp2 [2018/12/14 09:33] – edauce | public:rl_tp2 [2019/01/07 16:07] (Version actuelle) – [Calcul d'une fonction de valeur sur les transitions] edauce | ||
---|---|---|---|
Ligne 1: | Ligne 1: | ||
+ | ===== Utilisation de la librairie OpenAI Gym ===== | ||
+ | |||
+ | Comme nous l' | ||
+ | * L' | ||
+ | * L' | ||
+ | |||
+ | On considère de plus que le temps s' | ||
+ | |||
+ | Dans le cas parfaitement observable, l' | ||
+ | |||
+ | La librairie '' | ||
+ | |||
+ | === Classe Space === | ||
+ | La classe '' | ||
+ | |||
+ | {{: | ||
+ | |||
+ | |||
+ | |||
+ | Un espace peut être de type discret, continu, ou encore être défini comme une combinaison d' | ||
+ | Tous les espaces implémentent les méthodes : | ||
+ | * '' | ||
+ | * '' | ||
+ | |||
+ | * Un espace de type '' | ||
+ | * Un espace de type '' | ||
+ | * l' | ||
+ | * l' | ||
+ | * l' | ||
+ | |||
+ | === Classe Env === | ||
+ | La classe centrale est la classe '' | ||
+ | |||
+ | La classe '' | ||
+ | * '' | ||
+ | * '' | ||
+ | |||
+ | Elle propose les méthodes: | ||
+ | * la méthode '' | ||
+ | * l' | ||
+ | * la récompense | ||
+ | * une indication pour savoir si l' | ||
+ | * la méthode '' | ||
+ | * la méthode '' | ||
+ | |||
+ | **remarque :** la méthode '' | ||
+ | |||
+ | ==== Le pendule inversé ==== | ||
+ | |||
+ | Dans ce TP, nous nous intéressons au problème du pendule inversé (voir {{https:// | ||
+ | |||
+ | {{ : | ||
+ | |||
+ | Une barre rigide est attachée par une charnière à un chariot, qui bouge le long d'un rail sans friction. | ||
+ | |||
+ | Le système est contrôlé en appliquant une force de +1 ou -1 au chariot. | ||
+ | |||
+ | Le pendule démarre en position verticale instable, et le but est de le maintenir ainsi le plus longtemps possible, en appliquant des forces de +1 ou -1 alternativement. | ||
+ | |||
+ | Une récompense de +1 est donnée tant que le pendule est maintenu en position verticale. | ||
+ | |||
+ | L' | ||
+ | * le pendule est à plus de 15 degrés de la verticale | ||
+ | * le chariot est à plus de 2.4 unités du centre | ||
+ | |||
+ | Le vecteur d' | ||
+ | |||
+ | <note tip> | ||
+ | NB : si on connaît les caractéristiques physiques du chariot, il est possible de trouver une loi de contrôle qui maintient le pendule en position verticale. Le problème d' | ||
+ | </ | ||
+ | |||
+ | ==== Premières simulations ==== | ||
+ | <note tip> | ||
+ | Pour installer gym dans Pycharm, il faut utiliser un environnement virtuel (qui donne le droit d' | ||
+ | * Pour configurer un environnement, | ||
+ | * une fois l' | ||
+ | * l' | ||
+ | </ | ||
+ | |||
+ | Chaque environnement proposé par Gym possède un nom unique. | ||
+ | |||
+ | Pour voir la liste des environnements proposés, suivre {{https:// | ||
+ | |||
+ | L' | ||
+ | |||
+ | Pour créer l' | ||
+ | <code python> | ||
+ | import gym | ||
+ | env = gym.make(' | ||
+ | </ | ||
+ | L' | ||
+ | La commande : | ||
+ | <code python> | ||
+ | obs = env.reset() | ||
+ | </ | ||
+ | initialise l' | ||
+ | |||
+ | L' | ||
+ | <code python> | ||
+ | env.render() | ||
+ | c = input(' | ||
+ | </ | ||
+ | remarque : l' | ||
+ | |||
+ | Il est possible de modifier la position du pendule à l'aide d'une action. Pour choisir une action au hasard: | ||
+ | <code python> | ||
+ | action = 0 | ||
+ | </ | ||
+ | <note tip> | ||
+ | NB : L' | ||
+ | * l' | ||
+ | * l' | ||
+ | </ | ||
+ | pour appliquer l' | ||
+ | <code python> | ||
+ | observation, | ||
+ | env.render() | ||
+ | c = input(' | ||
+ | </ | ||
+ | On peut remarquer sur la fenêtre graphique que le pendule a bougé de manière infinitésimale suite à l' | ||
+ | <note tip> | ||
+ | NB : la fonction step retourne plusieurs valeurs : | ||
+ | * une observation | ||
+ | * un reward (recompense) | ||
+ | * un indicateur '' | ||
+ | </ | ||
+ | |||
+ | Pour le faire bouger de manière plus franche, il faut appliquer plusieurs fois l' | ||
+ | <code python> | ||
+ | obs = env.reset() | ||
+ | for i in range(100): | ||
+ | env.render() | ||
+ | action = 0 | ||
+ | obs, reward, done, _ = env.step(action) | ||
+ | c = input(" | ||
+ | </ | ||
+ | Le chariot disparaît très vite à gauche de l' | ||
+ | |||
+ | Pour contrôler le pendule, il faut donc alterner les actions 0 et 1. | ||
+ | Essayons : | ||
+ | <code python> | ||
+ | obs = env.reset() | ||
+ | for i in range(1000): | ||
+ | env.render() | ||
+ | if i % 2 == 0 : | ||
+ | action = 0 | ||
+ | else: | ||
+ | action = 1 | ||
+ | obs, reward, done, _ = env.step(action) | ||
+ | c = input(" | ||
+ | </ | ||
+ | C'est un peu mieux, mais la barre ne reste pas en position verticale (le système est instable) | ||
+ | |||
+ | Essayons de prendre en compte l' | ||
+ | <code python> | ||
+ | obs = env.reset() | ||
+ | for i in range(1000): | ||
+ | env.render() | ||
+ | if obs[2] < 0: | ||
+ | action = 0 | ||
+ | else: | ||
+ | action = 1 | ||
+ | obs, reward, done, _ = env.step(action) | ||
+ | c = input(" | ||
+ | </ | ||
+ | C'est encore un peu mieux mais le pendule finit quand même par se déstabiliser. | ||
+ | |||
+ | Si nous prenons en compte l' | ||
+ | < | ||
+ | obs = env.reset() | ||
+ | total_steps = 0 | ||
+ | while True: | ||
+ | env.render() | ||
+ | if obs[2] < 0: | ||
+ | action = 0 | ||
+ | else: | ||
+ | action = 1 | ||
+ | obs, reward, done, _ = env.step(action) | ||
+ | total_steps += 1 | ||
+ | if done: | ||
+ | break | ||
+ | print(" | ||
+ | c = input(" | ||
+ | </ | ||
+ | |||
+ | Il est également possible de contrôler le pendule de manière aléatoire à l'aide le la méthode '' | ||
+ | |||
+ | <code python> | ||
+ | obs = env.reset() | ||
+ | total_steps = 0 | ||
+ | while True: | ||
+ | env.render() | ||
+ | action = env.action_space.sample() | ||
+ | obs, reward, done, _ = env.step(action) | ||
+ | total_steps += 1 | ||
+ | if done: | ||
+ | break | ||
+ | print(" | ||
+ | c = input(" | ||
+ | </ | ||
+ | |||
+ | **A faire** | ||
+ | * Modifiez le code pour que ce soit l' | ||
+ | * Définissez une loi de contrôle qui prenne en compte à la fois la position angulaire '' | ||
+ | |||
+ | ==== Discrétiser l' | ||
+ | Les algorithmes d' | ||
+ | |||
+ | Définir un critère simple : '' | ||
+ | * l' | ||
+ | * l' | ||
+ | |||
+ | On peut généraliser ce principe et définir 16 états différents à partir des 4 observables : | ||
+ | * état 0 : '' | ||
+ | * état 1 : '' | ||
+ | * ... | ||
+ | * état 15 : '' | ||
+ | |||
+ | **A faire ** : écrire une fonction qui calcule un état discret à partir des valeurs d'une observation. | ||
+ | |||
+ | |||
+ | |||
+ | ==== Calcul d'une fonction de valeur sur les transitions ==== | ||
+ | |||
+ | L' | ||
+ | |||
+ | Rappel : Si st est l' | ||
+ | V(st)=E(T∑t′=trt′) | ||
+ | est la fonction de valeur de l' | ||
+ | |||
+ | La " | ||
+ | |||
+ | Contrairement au TD1, nous ne possédons pas de modèle de transitions d' | ||
+ | Q(st,at)=E(T∑t′=trt′) | ||
+ | la fonction de valeur sur les transitions d' | ||
+ | |||
+ | NB : l' | ||
+ | |||
+ | Il est nécessaire pour cela d' | ||
+ | |||
+ | Nous reprenons ici le modèle d' | ||
+ | <code python> | ||
+ | class Agent: | ||
+ | def __init__(self): | ||
+ | self.total_reward = 0.0 | ||
+ | self.trace = [] | ||
+ | </ | ||
+ | (pas besoin de méthode '' | ||
+ | |||
+ | Cette trace doit être mise à jour à chaque itération pour obtenir en fin d' | ||
+ | |||
+ | On suppose ici que l' | ||
+ | <code python> | ||
+ | action = env.action_space.sample() | ||
+ | </ | ||
+ | |||
+ | Pour calculer (de façon simplifiée) la fonction de valeur, nous utiliserons l' | ||
+ | |||
+ | < | ||
+ | Q = {} | ||
+ | nb_visites = {} | ||
+ | total_rewards_sum | ||
+ | pour tout couple (s,a) possible: | ||
+ | | ||
+ | | ||
+ | | ||
+ | pour i de 1 à N: | ||
+ | exécuter un épisode | ||
+ | pour tout (s,a) dans agent.trace : | ||
+ | nb_visites[(s, | ||
+ | total_rewards_sum[(s, | ||
+ | pour tout (s,a) dans Q: | ||
+ | si nb_visites[(s, | ||
+ | Q[(s,a)] = total_rewards_sum[(s, | ||
+ | </ | ||
+ | |||
+ | Pour calculer la fonction de valeur, on utilisera N = 1000 | ||
+ | |||
+ | Modifier l'init : | ||
+ | <code python> | ||
+ | class Agent: | ||
+ | def __init__(self): | ||
+ | self.total_reward = 0.0 | ||
+ | self.trace = [] | ||
+ | self.Q = {} | ||
+ | self.nb_visites = {} | ||
+ | self.total_rewards_sum | ||
+ | ... | ||
+ | </ | ||
+ | <note important> | ||
+ | L' | ||
+ | pour tout couple (s,a) possible: | ||
+ | Q[(s,a)] = 0 | ||
+ | nb_visites[(s, | ||
+ | total_rewards_sum[(s, | ||
+ | </ | ||
+ | Définir les méthodes suivantes : | ||
+ | * une méthode qui exécute un épisode complet en utilisant une politique aléatoire: | ||
+ | <code python> | ||
+ | def run_episode_random(self, | ||
+ | ... | ||
+ | </ | ||
+ | <note important> | ||
+ | Attention, la '' | ||
+ | </ | ||
+ | * une méthode qui met à jour les dictionnaires '' | ||
+ | <code python> | ||
+ | def update_dicos(self): | ||
+ | ... | ||
+ | </ | ||
+ | <note important> | ||
+ | Attention penser à remettre zéro la '' | ||
+ | </ | ||
+ | * une méthode qui estime la valeur de Q après un grand nombre d' | ||
+ | <code python> | ||
+ | def update_Q(self): | ||
+ | ... | ||
+ | </ | ||
+ | |||
+ | Implémentez ensuite l' | ||
+ | |||
+ | ==== Politique guidée par la valeur ==== | ||
+ | |||
+ | Pour améliorer sa récompense moyenne, l' | ||
+ | at=argmax a∈AQ(st,a) | ||
+ | |||
+ | * Écrire une méthode '' | ||
+ | <code python> | ||
+ | def choix_action(self, | ||
+ | ... | ||
+ | </ | ||
+ | * Lancer des simulations avec cette nouvelle méthode : | ||
+ | <code python> | ||
+ | action = agent.choix_action(s) | ||
+ | </ | ||
+ | * et calculer la récompense moyenne obtenue sur 100 épisodes et comparer au résultat précédent. Conclusion? | ||
+ | |||
+ | ==== Améliorations ==== | ||
+ | |||
+ | Le calcul exact de la fonction de valeur nécessite de mémoriser l' | ||
+ | |||
+ | <code python> | ||
+ | class Agent: | ||
+ | def __init__(self): | ||
+ | self.rewards = [] | ||
+ | self.trace = [] | ||
+ | self.Q = {} | ||
+ | self.nb_visites = {} | ||
+ | self.total_rewards_sum | ||
+ | ... | ||
+ | </ | ||
+ | |||
+ | et la mise à jour se fait en ajoutant les récompenses en fin de liste : | ||
+ | self.rewards += [reward] | ||
+ | à chaque pas de temps (après chaque appel à '' | ||
+ | |||
+ | En fin de simulation, la valeur cumulée est calculée séparément pour chaque (s,a) rencontré: | ||
+ | * pour chaque couple st,at de la trace : | ||
+ | * calculer la somme des récompenses **présente et futures** | ||
+ | * ajouter la valeur dans '' | ||
+ | * incrémenter le nombre de visites | ||
+ | |||
+ | Calculer à nouveau la fonction de valeur et comparer la politique obtenue à la politique précédente. | ||
+ | |||
+ | |||