Proximal Policy Optimization (PPO)

Proximal Policy Optimization (PPO) est un algorithme d’apprentissage par renforcement qui optimise la politique de manière stable et efficace. Il fait partie des méthodes basées sur la politique et appartient à la famille des algorithmes à optimisations de politique. L’algorithme PPO vise à mettre à jour la politique de manière régulière tout en garantissant que chaque mise à jour reste « proximale », c’est-à-dire suffisamment petite pour éviter des changements trop drastiques qui pourraient dégrader la performance.

L’idée principale de PPO est d’utiliser un objectif de clipping qui empêche la politique d’être modifiée de manière trop importante, ce qui est une cause fréquente d’instabilité dans les algorithmes d’apprentissage par renforcement.

L’optimisation de PPO repose sur une version simplifiée de la méthode TRPO (Trust Region Policy Optimization), mais avec une complexité calculatoire réduite, ce qui le rend plus adapté aux problèmes complexes.

Formule de mise à jour :

L’objectif de PPO est de maximiser une fonction d’objectif de type « surrogate » :

 

LCLIP(θ)=E^t[min(rt(θ)A^t,clip(rt(θ),1ϵ,1+ϵ)A^t)]L^{CLIP}(\theta) = \hat{\mathbb{E}}_t \left[ \min \left( r_t(\theta) \hat{A}_t, \text{clip}(r_t(\theta), 1-\epsilon, 1+\epsilon) \hat{A}_t \right) \right]

 

Où :

Fonctions :

  • Proximal Policy Optimization (exemple)

    Voici un exemple d'implémentation de PPO pour l'environnement CartPole-v1 de OpenAI Gym en utilisant TensorFlow.

    Exemple de code :

    import numpy as np
    import tensorflow as tf
    from tensorflow.keras import layers
    import gymnasium as gym
    
    # Créer l'environnement
    env = gym.make("CartPole-v1", render_mode=None)
    
    num_actions = env.action_space.n
    state_shape = env.observation_space.shape
    
    # Actor-Critic model: une sortie "policy" (actions probs) + une sortie "value"
    class ActorCritic(tf.keras.Model):
        def __init__(self, num_actions):
            super().__init__()
            self.common = layers.Dense(128, activation="relu")
            self.actor = layers.Dense(num_actions)
            self.critic = layers.Dense(1)
    
        def call(self, inputs):
            x = self.common(inputs)
            return self.actor(x), self.critic(x)
    
    model = ActorCritic(num_actions)
    optimizer = tf.keras.optimizers.Adam(learning_rate=0.01)
    
    def choose_action(logits):
        probs = tf.nn.softmax(logits)
        return np.random.choice(num_actions, p=probs.numpy()[0])
    
    def compute_loss(logits, values, rewards, actions, next_values, dones, gamma=0.99):
        # Calcul des cibles
        targets = rewards + gamma * next_values * (1 - dones)
        advantages = targets - values
    
        # Loss actor : log prob * advantage
        action_masks = tf.one_hot(actions, num_actions)
        log_probs = tf.nn.log_softmax(logits)
        log_probs_actions = tf.reduce_sum(log_probs * action_masks, axis=1)
        actor_loss = -tf.reduce_mean(log_probs_actions * tf.stop_gradient(advantages))
    
        # Loss critic : MSE entre valeurs prédites et targets
        critic_loss = tf.reduce_mean(tf.square(targets - tf.squeeze(values)))
    
        return actor_loss + critic_loss
    
    # Hyperparamètres
    episodes = 10
    gamma = 0.99
    max_steps = 500
    
    for episode in range(episodes):
        state, info = env.reset()
        state = np.expand_dims(state, axis=0).astype(np.float32)
    
        total_reward = 0
        done = False
    
        for step in range(max_steps):
            with tf.GradientTape() as tape:
                logits, value = model(state)
                action = choose_action(logits)
    
                next_state, reward, terminated, truncated, info = env.step(action)
                done = terminated or truncated
                total_reward += reward
    
                next_state = np.expand_dims(next_state, axis=0).astype(np.float32)
                _, next_value = model(next_state)
    
                loss = compute_loss(
                    logits,
                    value,
                    rewards=tf.constant([reward], dtype=tf.float32),
                    actions=tf.constant([action], dtype=tf.int32),
                    next_values=next_value,
                    dones=tf.constant([float(done)], dtype=tf.float32),
                    gamma=gamma,
                )
    
            grads = tape.gradient(loss, model.trainable_variables)
            optimizer.apply_gradients(zip(grads, model.trainable_variables))
    
            state = next_state
    
            if done:
                break
    
        print(f"Episode {episode+1}: Total Reward = {total_reward}")
    
    env.close()

    Explication du code :

    1. Initialisation de l’environnement CartPole
    - Environnement : `CartPole-v1` de Gymnasium, sans rendu (`render_mode=None`). - Taille de l’état (`state_shape`) : 4 (vecteur d’observation). - Nombre d’actions (`num_actions`) : 2.
    2. Modèle Actor-Critic personnalisé avec TensorFlow
    - Modèle hérité de `tf.keras.Model` avec : - Couche commune dense : 128 neurones, activation `relu`. - Sortie actor (logits) : couche dense avec `num_actions` neurones, sans activation (logits bruts). - Sortie critic : couche dense avec 1 neurone, estimation de la valeur d’état. - Optimiseur : Adam avec un taux d’apprentissage `learning_rate = 0.01`.
    3. Politique stochastique d’action
    - Les logits de l’acteur sont transformés en probabilités via `softmax`. - L’action est échantillonnée selon cette distribution de probabilités.
    4. Boucle d’interaction avec l’environnement
    - Pour chaque épisode, reset de l’environnement. - À chaque étape : - Calcul des logits et valeur d’état avec le modèle. - Choix d’une action stochastique. - Exécution de l’action dans l’environnement. - Récupération du nouvel état, récompense, et signal `done` (terminaison ou truncation). - Calcul de la perte Actor-Critic (cf. point 5). - Calcul et application des gradients pour mise à jour des poids du modèle. - Fin de l’épisode quand `done` est vrai ou nombre maximal d’étapes atteint.
    5. Calcul de la fonction de perte Actor-Critic
    - Cibles critic : \[ \text{targets} = r + \gamma \cdot V(s') \cdot (1 - \text{done}) \] - Avantages : \[ A = \text{targets} - V(s) \] - Loss actor : loss de policy gradient pondérée par les avantages (avec stop_gradient pour ne pas rétropropager dans avantages). - Loss critic : erreur quadratique moyenne entre targets et valeurs estimées par le critic. - Loss totale : somme des losses actor et critic.
    6. Suivi de l’apprentissage
    - Affichage de la récompense totale obtenue à chaque épisode.