TP 4 : Policy gradient

Ce TP peut être effectué dans google colab ici

ou téléchargé ici

Dans ce TP nous allons aborder les approches policy gradient au travers de l’algorithme REINFORCE pour bien comprendre la philosophie de ces approches. Dans une seconde partie nous prendrons également en main la bibliothèque Stable Baseline 3 qui propose de nombreuses implémentations d’algorithmes de RL.

Partie 0 : Imports et dependencies

# Paquets à installer , ouvrir un terminal sur jupyter hub et copier-coller sans les '!'
! pip install moviepy
! pip install gym[classic_control]
! pip install stable-baselines3[extra]
import os

import random
import gym
import pylab
import numpy as np
from collections import deque
import time
from matplotlib import pyplot as plt
from gym.wrappers import RecordVideo
from itertools import count
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.distributions import Categorical

Partie 1 : REINFORCE

Dans cette partie nous allons coder l’algorithme REINFORCE pour entraîner un agent à maximiser ses récompense dans l’environnement de cartpole que nous avions déjà utilisé la dernière fois.

Coding task

Complétez le code suivant pour implémenter un réseaux de neuronnes chargé d’évaluer la politique à chaque instant.

Ce réseau est composé de :

  • une couche linéaire de taille \((state\_space\times 128)\)

  • une couche de dropout (p=0.6)

  • activation ReLU

  • une couche linéaire de taille \(128\times action\_space)\)

  • softmax

NB : On compte 2 actions possibles et les états sont décrits par des vecteurs de taille 4

class Policy(nn.Module):
    def __init__(self):
        super(Policy, self).__init__()
        ####### Votre code ici ########


        #############################

        self.saved_log_probs = []
        self.rewards = []

    def forward(self, x):
        x = self.affine1(x)
        x = self.dropout(x)
        x = F.relu(x)
        action_scores = self.affine2(x)
        return F.softmax(action_scores, dim=1)


policy = Policy()
optimizer = optim.Adam(policy.parameters(), lr=1e-2)
eps = np.finfo(np.float32).eps.item()

Coding Task

Complétez le code suivant pour tirer une action selon la distribution donné par le réseau policy :

  1. estimation des probabilités des états d’après l’agent (le réseau)

  2. échantillonnage d’une action d’après ces probabilités. On pourra utiliser la fonction Categorical() pour générer une distribution et la méthode sample() pour tirer une action selon cette distribution.

def select_action(state):
    state = torch.from_numpy(state).float().unsqueeze(0)
    ############### Votre code ici ##############


    ##############################################
    policy.saved_log_probs.append(m.log_prob(action))
    return action.item()

Coding Task

Complétez la fonction suivante pour implémenter le calcul des retours pour chaque étape de la trajectoire.

  1. Stocker les retours pour chaque étape de la trajectoire dans le tableau returns

  2. Sommer les scores \(log \; \pi_\theta(a|s)\) contenu dans le paramètre saved_log_prob du réseau fois le retour. On cherche donc à calculer notre fonction objectif :

\[V(\theta) = \sum_{t=0}^{T-1} \log \; \pi_\theta(a|s) G_t\]
  1. Calculer le gradient de l’objectif grâce à la fonction backward (ne pas oublier optimizer.zero_grad() et optimizer.step

def update_policy(gamma):
    R = 0
    policy_loss = []
    returns = []
   ###### Votre Code ici ######
    # point 1)

    #######################
    returns = torch.tensor(returns)
    returns = (returns - returns.mean()) / (returns.std() + eps)
    ############# Votre code ici ############
    # points 2) et 3)

    ###################################
    del policy.rewards[:]
    del policy.saved_log_probs[:]

On peut enfin entrâner notre agent. Les vidéos de l’entraînement sont disponibles dans le dossier video

running_reward = 10
gamma = 0.99
print_every = 10
seed = np.random.randint(0,600)
env = RecordVideo(gym.make('CartPole-v1', render_mode="rgb_array"),
                                video_folder = './video',
                                episode_trigger=lambda episode_id: episode_id%10==0)
torch.manual_seed(seed)

nb_epoch = int(1e3)
nb_iter = int(1e4)

for i_episode in range(nb_epoch):
    state, ep_reward = env.reset(seed=seed)[0], 0
    for t in range(1, nb_iter):  # Don't infinite loop while learning
        action = select_action(state)
        state, reward, done, _ , _ = env.step(action)
        policy.rewards.append(reward)
        ep_reward += reward
        if done:
            break

    running_reward = 0.05 * ep_reward + (1 - 0.05) * running_reward
    update_policy(gamma)
    if i_episode % print_every == 0:
        print('Episode {}\tLast reward: {:.2f}\tAverage reward: {:.2f}'.format(
              i_episode, ep_reward, running_reward))
    if running_reward > env.spec.reward_threshold:
        print("Solved! Running reward is now {} and "
              "the last episode runs to {} time steps!".format(running_reward, t))
        break

Dans jupyter hub : 1. Cliquez sur le dossier video puis clique droit sur la video mp4 que vous souhaitez visualiser 2. Cliquez sur Copy Download Link 3. Ouvrez un nouvel onglet et collez le lien.

Partie 2 : RL avec Stable Baseline

Dans cette partie nous allons entraîner un Deep Q-Network (DQN) sur une tâche classique en RL : le cartpole. Il s’agit d’apprendre à un agent à tenir en équilibre un mat sur un véhicule 2D en mouvement. Les actions sont \({gauche,droite}\) et les états sont décrits par la position du véhicule, l’angle du mât et sa vélocité. L’environnement est présenté dans l’interface gym cf. ici. Pour cette tâche nous allons utiliser la bibliothèque Stable Baseline 3 site proposant un large panel d’algorithme de RL implémenté et le biding avec l’environnement gym. Nous pouvons donc prendre une approche relativement au niveau dans le sens où nous nous contenterons de choisir l’environnement (ici cartpole), la durée de l’entraînement donnée par le paramètre total_timesteps dans la méthode learn().

Commençons par installer les dépendences et définir quelques fonctins utilitaires.

#@title Utilitaires

import gym
from gym.wrappers import RecordVideo


import time
from matplotlib import pyplot as plt

from stable_baselines3 import PPO
from stable_baselines3 import DQN
from stable_baselines3.common.vec_env import SubprocVecEnv
from stable_baselines3.common.env_util import make_vec_env
from stable_baselines3.common.utils import set_random_seed
from stable_baselines3.common.vec_env import VecVideoRecorder, DummyVecEnv

def make_env(env_id, rank, seed=0):
    """
    Utility function for multiprocessed env.

    :param env_id: (str) the environment ID
    :param num_env: (int) the number of environments you wish to have in subprocesses
    :param seed: (int) the inital seed for RNG
    :param rank: (int) index of the subprocess
    """
    def _init():
        env = gym.make(env_id, render_mode="rgb_array")
        return env
    set_random_seed(seed)
    return _init



def record(model,path,length):

  video_folder = path
  video_length = length

  env = DummyVecEnv([lambda: gym.make(env_id, render_mode="rgb_array")])


  # Record the video starting at the first step
  env = VecVideoRecorder(env, video_folder,
                        record_video_trigger=lambda x: x == 0, video_length=video_length,
                        name_prefix="random-agent-{}".format(env_id))

  obs = env.reset()
  for _ in range(video_length + 1):
    action, _states = model.predict(obs, deterministic=True)
    obs, reward, done, trunc = env.step(action)
  # Save the video
  env.close()

À présent, nous pouvons entraîner un DQN dans l’environnement cartpole. Nous choisissons ici un Multi Layer Perceptron et un entraîenement de \(10^5\) steps.

env_id = "CartPole-v1"
num_cpu = 1  # Number of processes to use
# Create the vectorized environment
env = SubprocVecEnv([make_env(env_id, i) for i in range(num_cpu)])

model = DQN('MlpPolicy', env, verbose=1)
model.learn(total_timesteps=1e5)

model.save("CartPole-v1")

Nous pouvons enregistrer une vidéo des performances de l’agent de la durée length. Cette vidéo sera stockée dans le répertoire donné en chemin.

record(model,'logs/videos',length=500)

Coding Task

Reprenez les fonctions précédentes pour entrainer un agent avec l’algorithme Avantage Actor Critic (A2C)

Bonus

Vous pouvez tester cette approche sur des jeux Atari (pour cela vous devez télécharger quelques fichiers de configuration cf. cellule suivante). Attention, ces environnemnets sont plus complexes et nécessitent plus temps d’entraînement voire une approche différente de DQN.

#@title Atari data

!wget http://www.atarimania.com/roms/Roms.rar
!unrar e Roms.rar
!unzip HC\ ROMS.zip
!unzip ROMS.zip
!python -m atari_py.import_roms ROMS