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_tp1 [2018/11/30 09:18] – edauce | public:rl_tp1 [2019/12/13 10:38] (Version actuelle) – edauce | ||
---|---|---|---|
Ligne 1: | Ligne 1: | ||
+ | ===== Apprentissage par renforcement : premiers pas ===== | ||
+ | |||
+ | Dans ce TP nous allons décrire le comportement d'un agent se déplaçant aléatoirement dans un environnement très simple. | ||
+ | |||
+ | Pour établir développer des algorithmes d’apprentissage par renforcement, | ||
+ | * Un **agent** : l' | ||
+ | * de percevoir son environnement (à l'aide d' | ||
+ | * d'agir sur son environnement à l'aide d' | ||
+ | * −−> la politique décide des choix d' | ||
+ | * Un **environnement** : | ||
+ | * Il s'agit d'un simulateur du monde extérieur à l' | ||
+ | * L' | ||
+ | * les observations | ||
+ | * les récompenses (gratification des actions) | ||
+ | |||
+ | Nous allons regarder comment programmer ces deux entités pour pouvoir réaliser des simulations simples. | ||
+ | |||
+ | ==== Structure du code ==== | ||
+ | |||
+ | Dans un projet '' | ||
+ | |||
+ | === Classe Environnement === | ||
+ | |||
+ | On considère ici que les simulations se déroulent sur un nombre limité de pas de temps (ici 10). | ||
+ | |||
+ | Voici le constructeur : | ||
+ | <code python> | ||
+ | class Environment: | ||
+ | def __init__(self): | ||
+ | self.state = 0 | ||
+ | self.steps_left = 10 | ||
+ | </ | ||
+ | Dans le cas général, l' | ||
+ | |||
+ | L' | ||
+ | <code python> | ||
+ | def get_observation(self): | ||
+ | return self.state | ||
+ | </ | ||
+ | |||
+ | La méthode '' | ||
+ | <code python> | ||
+ | def get_actions(self): | ||
+ | return [0, 1] | ||
+ | </ | ||
+ | |||
+ | La méthode '' | ||
+ | <code python> | ||
+ | def is_done(self): | ||
+ | return self.steps_left == 0 | ||
+ | </ | ||
+ | |||
+ | Enfin, la méthode '' | ||
+ | <note tip> | ||
+ | Dans cet exemple simple, | ||
+ | * l' | ||
+ | * la récompense est tirée au hasard. | ||
+ | </ | ||
+ | |||
+ | <code python> | ||
+ | def action(self, | ||
+ | if self.is_done(): | ||
+ | raise Exception(" | ||
+ | self.steps_left -= 1 | ||
+ | return random.random() | ||
+ | </ | ||
+ | |||
+ | === La classe Agent === | ||
+ | |||
+ | La classe Agent est plus simple et ne contient que deux méthodes : | ||
+ | * le constructeur | ||
+ | * la méthode de mise à jour qui effectue une itération de l' | ||
+ | |||
+ | Voici le constructeur. Il contient un compteur qui conserve le nombre total e récompenses accumulées. | ||
+ | <code python> | ||
+ | class Agent: | ||
+ | def __init__(self): | ||
+ | self.total_reward = 0.0 | ||
+ | </ | ||
+ | |||
+ | La méthode de mise à jour prend comme argument une instance d l' | ||
+ | * observer l' | ||
+ | * lire le répertoire d' | ||
+ | * choisir une action et la transmettre à l' | ||
+ | * lire la récompense et mettre à jour le compteur | ||
+ | <code python> | ||
+ | def step(self, env): | ||
+ | current_obs = env.get_observation() | ||
+ | actions = env.get_actions() | ||
+ | reward = env.action(random.choice(actions)) | ||
+ | self.total_reward += reward | ||
+ | </ | ||
+ | |||
+ | <note tip> | ||
+ | Ici bien sûr, l' | ||
+ | </ | ||
+ | |||
+ | === Programme principal === | ||
+ | |||
+ | Créez maintenant un programme principal qui crée les deux classes et exécute un épisode : | ||
+ | |||
+ | <code python> | ||
+ | env = Environment() | ||
+ | agent = Agent() | ||
+ | |||
+ | while not env.is_done(): | ||
+ | agent.step(env) | ||
+ | |||
+ | print(" | ||
+ | </ | ||
+ | |||
+ | Faites tourner cet environnement plusieurs fois et vérifiez que la somme des récompenses obtenues varie à chaque fois. | ||
+ | |||
+ | |||
+ | ==== Simulation d'un Monde-grille ==== | ||
+ | |||
+ | Les mondes-grilles (//Grid worlds//) permettent de simuler de petits labyrinthes dans lesquels les actions de l' | ||
+ | |||
+ | Nous considérons ici un labyrinthe extrêmement simple à 8 cases : | ||
+ | < | ||
+ | {{: | ||
+ | </ | ||
+ | |||
+ | * L' | ||
+ | S={1,2,3,4,5,6,7,8} | ||
+ | <note tip> | ||
+ | Sur le dessin ci-dessus, l' | ||
+ | </ | ||
+ | * Les actions correspondent aux mouvements de l' | ||
+ | A={N,S,E,W} | ||
+ | <note important> | ||
+ | Attention cependant, toutes les actions ne sont pas autorisées dans toutes les positions. Ainsi, à la position '' | ||
+ | </ | ||
+ | * Les cases 1, 2, 3, 4, et 5 n' | ||
+ | |||
+ | Nous allons modifier pas à pas le squelette d' | ||
+ | |||
+ | === L' | ||
+ | |||
+ | L' | ||
+ | |||
+ | * 1. Le changement d' | ||
+ | |||
+ | <note tip> | ||
+ | Plutôt que d' | ||
+ | <code python> | ||
+ | next = { | ||
+ | 1 : {" | ||
+ | 2 : {" | ||
+ | 3 : {" | ||
+ | 4 : {" | ||
+ | 5 : {" | ||
+ | 6 : {" | ||
+ | 7 : {" | ||
+ | 8 : {" | ||
+ | } | ||
+ | </ | ||
+ | ainsi '' | ||
+ | </ | ||
+ | |||
+ | <note tip> | ||
+ | La récompense est la valeur qui doit être retournée par la méthode '' | ||
+ | <code python> | ||
+ | reward = {1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: -10, 7: 10, 8: -10} | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | * 2. Pour l' | ||
+ | |||
+ | <code python> | ||
+ | def __init__(self): | ||
+ | self.state = random.randint(1, | ||
+ | self.steps_left = 10 | ||
+ | </ | ||
+ | |||
+ | * 3. La méthode '' | ||
+ | |||
+ | === L' | ||
+ | Pour l' | ||
+ | |||
+ | === Simulation === | ||
+ | Dans le programme principal, effectuer un total de 100 simulations et calculer la moyenne et l' | ||
+ | |||
+ | <note tip> | ||
+ | Pour bien comprendre le comportement de l' | ||
+ | </ | ||
+ | |||
+ | ==== Calcul d'une fonction de valeur ==== | ||
+ | |||
+ | Afin de rendre notre agent un peu plus " | ||
+ | |||
+ | Si st est l' | ||
+ | V(st)=E(T∑t′=trt′) | ||
+ | avec T l' | ||
+ | |||
+ | Il est nécessaire pour cela d' | ||
+ | |||
+ | Ainsi, le constructeur de l' | ||
+ | <code python> | ||
+ | class Agent: | ||
+ | def __init__(self): | ||
+ | self.total_reward = 0.0 | ||
+ | self.trace = [] | ||
+ | </ | ||
+ | |||
+ | Cette trace doit être mise à jour à chaque itération pour obtenir en fin d' | ||
+ | |||
+ | Pour calculer (de façon simplifiée) la fonction de valeur, nous utiliserons l' | ||
+ | |||
+ | < | ||
+ | V = {1 :0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0} | ||
+ | nb_visites = {1 :0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0} | ||
+ | total_reward_sum = {1 :0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0} | ||
+ | pour i de 1 à N: | ||
+ | exécuter un épisode | ||
+ | pour tout s dans agent.trace : | ||
+ | nb_visites[s] += 1 | ||
+ | total_reward_sum[s] += agent.total_reward | ||
+ | pour tout s dans V: | ||
+ | V[s] = total_reward_sum[s] / nb_visites[s] | ||
+ | </ | ||
+ | |||
+ | Pour calculer la fonction de valeur, on utilisera N = 100 | ||
+ | |||
+ | ==== Politique guidée par la valeur ==== | ||
+ | |||
+ | Pour améliorer sa récompense moyenne, l' | ||
+ | at=argmax a∈AV(next(st,a)) | ||
+ | |||
+ | * Écrire une méthode '' | ||
+ | <code python> | ||
+ | def step_valeur(self, | ||
+ | ... | ||
+ | </ | ||
+ | * Lancer des simulations avec cette nouvelle méthode de mise à jour 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 = [] | ||
+ | </ | ||
+ | |||
+ | et la mise à jour se fait en ajoutant les récompenses en fin de liste : | ||
+ | <code python> | ||
+ | def step(self, env): | ||
+ | ... | ||
+ | self.rewards += [reward] | ||
+ | </ | ||
+ | |||
+ | En fin de simulation, la valeur cumulée est calculée séparément pour chaque état rencontré: | ||
+ | * pour chaque état st 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. | ||
+ | |||
+ | |||