Accéder au contenu principal

Optimisation des politiques proximales avec PyTorch et Gymnasium

Apprenez les premiers principes de l'optimisation de la politique proximale, y compris son implémentation dans PyTorch avec Gymnasium !
Actualisé 14 févr. 2025  · 25 min de lecture

L'optimisation de la politique proximale (PPO) est l'un des algorithmes préférés pour résoudre les problèmes d'apprentissage par renforcement (RL). Il a été développé en 2017 par John Schuman, cofondateur d'OpenAI. 

Le PPO a été largement utilisé à l'OpenAI pour entraîner des modèles à émuler des comportements humains. Il améliore les méthodes antérieures telles que l'optimisation de la politique de la région de confiance (TRPO) et est devenu populaire parce qu'il s'agit d'un algorithme robuste et efficace. 

Dans ce tutoriel, nous examinons le PPO en profondeur. Nous couvrons la théorie et démontrons comment la mettre en œuvre à l'aide de PyTorch.

Comprendre l'optimisation des politiques proximales (PPO)

Les algorithmes classiques d'apprentissage supervisé mettent à jour les paramètres en suivant la direction du gradient le plus raide. Si cette mise à jour s'avère excessive, elle est corrigée au cours des exemples de formation ultérieurs qui sont indépendants les uns des autres.

Cependant, les exemples de formation dans l'apprentissage par renforcement consistent en des actions et des retours de l'agent. Ainsi, les exemples de formation sont corrélés les uns aux autres. L'agent explore l'environnement pour trouver la politique optimale. Par conséquent, une modification importante du gradient peut entraîner un blocage de la politique dans une mauvaise région avec des récompenses sous-optimales. Étant donné que l'agent doit explorer l'environnement, les changements importants de politique rendent le processus de formation instable. 

Les méthodes basées sur les régions de confiance visent à éviter ce problème en garantissant que les mises à jour de la politique se font dans une région de confiance. Cette région de confiance est une région artificiellement restreinte de l'espace politique dans lequel les mises à jour sont autorisées. La politique mise à jour ne peut se situer que dans une région de confiance de l'ancienne politique. Veiller à ce que les mises à jour des politiques soient incrémentielles permet d'éviter l'instabilité.

Mises à jour de la politique de la région fiduciaire (TRPO)

L'algorithme TRPO (Trust Region Policy Updates) a été proposé en 2015 par John Schulman (qui a également proposé le PPO en 2017). Pour mesurer la différence entre l'ancienne politique et la politique actualisée, TRPO utilise la divergence de Kullback-Leibler (KL). La divergence KL est utilisée pour mesurer la différence entre deux distributions de probabilité. Le TRPO s'est avéré efficace dans la mise en œuvre des régions de confiance. 

Le problème de TRPO est la complexité de calcul associée à la divergence KL. L'application de la divergence KL doit être étendue au second ordre à l'aide de méthodes numériques telles que l'expansion de Taylor. Cette opération est coûteuse en termes de calcul. L'OPP a été proposé comme une alternative plus simple et plus efficace à l'OPRT. PPO clip le ratio des politiques pour approximer la région de confiance sans avoir recours à des calculs complexes impliquant la divergence KL.

C'est pourquoi l'OPP a été préféré à l'OPRT pour résoudre les problèmes de NR. Grâce à la méthode plus efficace d'estimation des régions de confiance, le PPO permet d'équilibrer efficacement la performance et la stabilité. 

Approche politique proximale (PPO)

La PPO est souvent considérée comme une sous-classe des méthodes de critique des acteurs, qui mettent à jour les gradients de politique sur la base de la fonction de valeur. Avantage Les méthodes de critique actorielle (A2C) utilisent un paramètre appelé avantage. Il s'agit de la différence entre les rendements prévus par le critique et les rendements réalisés par la mise en œuvre de la politique. 

Pour comprendre l'OPP, vous devez en connaître les composantes :

  1. L'acteur exécute la politique. Il est mis en œuvre sous la forme d'un réseau neuronal. Si un état est donné en entrée, il produit l'action à entreprendre. 
  2. Le critique est un autre réseau neuronal. Il prend l'état en entrée et produit la valeur attendue de cet état. Le critique exprime donc la fonction état-valeur. 
  3. Les méthodes basées sur la politique de gradient peuvent choisir d'utiliser différentes fonctions objectives. En particulier, l'OPP utilise la fonction d'avantage. La fonction d'avantage mesure le montant par lequel la récompense cumulée (basée sur la politique mise en œuvre par l'acteur) dépasse la récompense de base attendue (telle que prédite par le critique). L'objectif de l'OPP est d'augmenter la probabilité de choisir des actions présentant un avantage élevé. L'objectif d'optimisation du PPO utilise des fonctions de perte basées sur cette fonction d'avantage. 
  4. La fonction objectif écrêtée est la principale innovation du PPO. Il permet d'éviter les mises à jour importantes de la politique en une seule itération de formation. Il limite le nombre de mises à jour de la politique au cours d'une seule itération. Pour mesurer les mises à jour incrémentales des politiques, les méthodes basées sur les politiques utilisent le ratio de probabilité de la nouvelle politique par rapport à l'ancienne. 
  5. La perte de substitution est la fonction objective de l'OPP et prend en compte les innovations mentionnées précédemment. Il est calculé comme suit :
    1. Calculez le ratio réel (comme expliqué précédemment) et multipliez-le par l'avantage. 
    2. Clipsez le ratio pour qu'il se situe dans une fourchette souhaitée. Multipliez le rapport d'écrêtage par l'avantage. 
    3. Prenez la valeur minimale des deux quantités ci-dessus. 
  6. Dans la pratique, un terme d'entropie est également ajouté à la perte de substitution. C'est ce qu'on appelle la prime d'entropie. Il est basé sur la distribution mathématique des probabilités d'action. L'idée derrière le bonus d'entropie est d'introduire un peu plus d'aléatoire de manière contrôlée. Cela encourage le processus d'optimisation à explorer l'espace d'action. Une prime d'entropie élevée favorise l'exploration plutôt que l'exploitation. 

Comprendre le mécanisme d'écrêtage

Supposons qu'avec l'ancienne politique πanciennela probabilité d'entreprendre une action a dans l'état s est πancienne(a|s). Dans le cadre de la nouvelle politique, la probabilité d'entreprendre la même action a à partir du même état s est actualisée à πnouveau(a|s). Le rapport de ces probabilités, en fonction des paramètres de politique θ, est r(θ). Lorsque la nouvelle politique rend l'action plus probable (dans le même état), le ratio est supérieur à 1 et vice versa. 

Le mécanisme d'écrêtage limite ce rapport de probabilité de telle sorte que les probabilités des nouvelles actions doivent se situer à l'intérieur d'un certain pourcentage des probabilités des anciennes actions. Par exemple, r(θ) peut être contraint de se situer entre 0,8 et 1,2. Cela permet d'éviter les sauts importants, ce qui garantit un processus de formation stable.

Dans la suite de cet article, vous apprendrez à assembler les composants d'une implémentation simple de PPO à l'aide de PyTorch.

Devenez un scientifique ML

Améliorez vos connaissances en Python pour devenir un scientifique spécialisé dans l'apprentissage automatique.
Commencez à apprendre gratuitement

Avant de mettre en œuvre l'OPP, nous devons installer les bibliothèques logicielles prérequises et choisir un environnement approprié pour appliquer la politique. 

Installation de PyTorch et des bibliothèques nécessaires 

Nous devons installer les logiciels suivants : 

  • PyTorch et d'autres bibliothèques logicielles, telles que numpy (pour les fonctions mathématiques et statistiques) et matplotlib (pour le traçage de graphiques).
  • Le logiciel open-source Gym d'OpenAI, une bibliothèque Python qui simule différents environnements et jeux, qui peuvent être résolus grâce à l'apprentissage par renforcement. Vous pouvez utiliser l'API Gym pour faire interagir votre algorithme avec l'environnement. Étant donné que la fonctionnalité de gym change parfois au cours du processus de mise à niveau, dans cet exemple, nous figeons sa version à 0.25.2.

Pour l'installer sur un serveur ou une machine locale, exécutez : 

$ pip install torch numpy matplotlib gym==0.25.2

Pour l'installer en utilisant un Notebook comme Google Colabou DataLab, utilisez : 

!pip install torch numpy matplotlib gym==0.25.2

Créer le(s) environnement(s) CartPole

Utilisez OpenAI Gym pour créer deux instances (une pour la formation et une autre pour les tests) de l'environnement CartPole :

env_train = gym.make('CartPole-v1')
env_test = gym.make('CartPole-v1')

Mettons maintenant en œuvre le PPO à l'aide de PyTorch.

Définir le réseau politique

Comme nous l'avons expliqué précédemment, le PPO est mis en œuvre sous la forme d'un modèle de critique d'acteur. L'acteur met en œuvre la politique et le critique prédit sa valeur estimée. Les réseaux neuronaux d'acteurs et de critiques utilisent les mêmes données d'entrée, à savoir l'état à chaque pas de temps. Ainsi, les modèles de l'acteur et du critique peuvent partager un réseau neuronal commun, que l'on appelle l'architecture dorsale. L'acteur et le critique peuvent étendre l'architecture dorsale avec des couches supplémentaires. 

Définir le réseau de base

Les étapes suivantes décrivent le réseau de base :

  • Mettez en place un réseau à trois couches : une couche d'entrée, une couche cachée et une couche de sortie. 
  • Après les couches d'entrée et cachées, nous utilisons une fonction d'activation. Dans ce tutoriel, nous avons choisi ReLU parce qu'il est efficace sur le plan des calculs. 
  • Nous imposons également une fonction d'abandon après les couches d'entrée et cachées afin d'obtenir un réseau robuste. La fonction d'exclusion met aléatoirement certains neurones à zéro. Cela permet de réduire la dépendance à l'égard de neurones spécifiques et d'éviter l'ajustement excessif, ce qui rend le réseau plus robuste. 

Le code ci-dessous met en œuvre l'épine dorsale :

class BackboneNetwork(nn.Module):
    def __init__(self, in_features, hidden_dimensions, out_features, dropout):
        super().__init__()
        self.layer1 = nn.Linear(in_features, hidden_dimensions)
        self.layer2 = nn.Linear(hidden_dimensions, hidden_dimensions)
        self.layer3 = nn.Linear(hidden_dimensions, out_features)
        self.dropout = nn.Dropout(dropout)
    def forward(self, x):
        x = self.layer1(x)
        x = f.relu(x)
        x = self.dropout(x)
        x = self.layer2(x)
        x = f.relu(x)
        x = self.dropout(x)
        x = self.layer3(x)
        return x

Définir le réseau d'acteurs et de critiques

Nous pouvons maintenant utiliser ce réseau pour définir la classe d'acteurs-critiques, ActorCritic. L'acteur modélise la politique et prédit l'action. Le critique modélise la fonction de valeur et prédit la valeur. Ils prennent tous deux l'état en entrée. 

class ActorCritic(nn.Module):
    def __init__(self, actor, critic):
        super().__init__()
        self.actor = actor
        self.critic = critic
    def forward(self, state):
        action_pred = self.actor(state)
        value_pred = self.critic(state)
        return action_pred, value_pred

Instanciation des réseaux d'acteurs et de critiques

Nous utiliserons les réseaux définis ci-dessus pour créer un acteur et un critique. Ensuite, nous créerons un agent, comprenant l'acteur et le critique. 

Avant de créer l'agent, initialisez les paramètres du réseau :

  • Les dimensions de la couche cachée, H, qui est un paramètre configurable. La taille et le nombre de couches cachées dépendent de la complexité du problème. Nous utiliserons une couche cachée de 64 x 64 dimensions. 
  • Caractéristiques d'entrée, N, où N est la taille du tableau d'états. La couche d'entrée a N X H dimensions. Dans l'environnement CartPole, l'état est un tableau à 4 éléments. So N is 4. 
  • Caractéristiques de sortie du réseau d'acteurs, O, où O est le nombre d'actions dans l'environnement. La couche de sortie de l'acteur a des dimensions H x O. L'environnement CartPole a 2 actions. 
  • Caractéristiques de sortie du réseau critique. Étant donné que le réseau critique ne prédit que la valeur attendue (compte tenu d'un état d'entrée), le nombre de caractéristiques de sortie est de 1. 
  • L'abandon scolaire en tant que fraction.

Le code suivant montre comment déclarer les réseaux d'acteurs et de critiques basés sur le réseau dorsal :

def create_agent(hidden_dimensions, dropout):
    INPUT_FEATURES = env_train.observation_space.shape[0]
    HIDDEN_DIMENSIONS = hidden_dimensions
    ACTOR_OUTPUT_FEATURES = env_train.action_space.n
    CRITIC_OUTPUT_FEATURES = 1
    DROPOUT = dropout
    actor = BackboneNetwork(
            INPUT_FEATURES, HIDDEN_DIMENSIONS, ACTOR_OUTPUT_FEATURES, DROPOUT)
    critic = BackboneNetwork(
            INPUT_FEATURES, HIDDEN_DIMENSIONS, CRITIC_OUTPUT_FEATURES, DROPOUT)
    agent = ActorCritic(actor, critic)
    return agent

Calcul des rendements

L'environnement donne une récompense qui va de chaque étape à la suivante, en fonction de l'action de l'agent. La récompense, R, est exprimée comme suit :

Le rendement est défini comme la valeur cumulée des bénéfices futurs attendus. Les récompenses obtenues à des moments plus éloignés dans le futur ont moins de valeur que les récompenses immédiates. Ainsi, le rendement est généralement calculé comme le rendement actualisé, G, défini comme suit :

Dans ce tutoriel (et dans de nombreuses autres références), le rendement fait référence au rendement actualisé. 

Pour calculer le rendement :

  • Commencez par les récompenses attendues de tous les états futurs.
  • Multipliez chaque récompense future par un exposant du facteur d'actualisation, . Par exemple, la récompense attendue après 2 pas de temps (à partir du présent) est multipliée par 2. 
  • Additionnez toutes les récompenses futures actualisées pour calculer le rendement. 
  • Normaliser la valeur du retour. 

La fonction calculate_returns() effectue ces calculs, comme indiqué ci-dessous :

def calculate_returns(rewards, discount_factor):
    returns = []
    cumulative_reward = 0
    for r in reversed(rewards):
        cumulative_reward = r + cumulative_reward * discount_factor
        returns.insert(0, cumulative_reward)
    returns = torch.tensor(returns)
    # normalize the return
    returns = (returns - returns.mean()) / returns.std()
    return returns

Mise en œuvre de la fonction d'avantage

L'avantage est calculé comme la différence entre la valeur prédite par le critique et le rendement attendu des actions choisies par l'acteur conformément à la politique. Pour une action donnée, l'avantage exprime le bénéfice qu'il y a à prendre cette action spécifique par rapport à une action arbitraire (moyenne). 

Dans le document original de l'OPP (équation 10), l'avantage, en regardant vers l'avant jusqu'au pas de temps T, est exprimé comme suit :

Lors du codage de l'algorithme, la contrainte d'anticipation jusqu'à un nombre déterminé de pas de temps est appliquée par le biais de la taille du lot. L'équation ci-dessus peut donc être simplifiée comme étant la différence entre la valeur et les rendements attendus. Les rendements attendus sont quantifiés dans la fonction de valeur de l'action d'état, Q. 

Ainsi, la formule simplifiée ci-dessous exprime l'avantage de choisir : 

  • une action particulière 
  • dans un état donné 
  • dans le cadre d'une police particulière 
  • à un moment donné 

Cela s'exprime comme suit : 

OpenAI utilise également cette formule pour mettre en œuvre RL. Lafonction calculate_advantages() présentée ci-dessous calcule l'avantage :

def calculate_advantages(returns, values):
    advantages = returns - values
    # Normalize the advantage
    advantages = (advantages - advantages.mean()) / advantages.std()
    return advantages

Perte de substitution et mécanisme d'écrêtage

La perte de police serait la perte de gradient de police standard sans techniques spéciales telles que l'OPP. La perte de pente de la politique standard est calculée comme le produit de :

  • Probabilités d'action politique 
  • La fonction d'avantage, qui est calculée comme la différence entre :
    • La politique de retour 
    • La valeur attendue

La perte de gradient de politique standard ne permet pas de corriger les changements brusques de politique. La perte de substitution modifie la perte standard afin de limiter le montant que la police peut modifier à chaque itération. C'est le minimum de deux quantités :

  • Le produit de :
    • Le ratio de politique générale. Ce ratio exprime la différence entre les anciennes et les nouvelles probabilités d'action.  
    • La fonction d'avantage 
  • Le produit de :
    • La valeur fixée du ratio de politique générale. Ce ratio est réduit de manière à ce que la politique actualisée se situe dans un certain pourcentage de l'ancienne politique. 
    • La fonction d'avantage 

Pour le processus d'optimisation, la perte de substitution est utilisée comme une approximation de la perte réelle. 

Le mécanisme d'écrêtage

Le ratio de politique générale, Rest la différence entre la nouvelle et l'ancienne politique et correspond au rapport entre les logarithmes des probabilités de la politique en fonction des nouveaux et des anciens paramètres :

Le ratio de la police d'assurance écrêtée, R'est contraint de telle sorte que :

Compte tenu de l'avantage, Atcomme indiqué dans la section précédente, et le ratio de politique, comme indiqué ci-dessus, la perte de substitution est calculée comme suit :

Le code ci-dessous montre comment mettre en œuvre le mécanisme d'écrêtage et la perte de substitution. 

def calculate_surrogate_loss(
        actions_log_probability_old,
        actions_log_probability_new,
        epsilon,
        advantages):
    advantages = advantages.detach()
    policy_ratio = (
            actions_log_probability_new - actions_log_probability_old
            ).exp()
    surrogate_loss_1 = policy_ratio * advantages
    surrogate_loss_2 = torch.clamp(
            policy_ratio, min=1.0-epsilon, max=1.0+epsilon
            ) * advantages
    surrogate_loss = torch.min(surrogate_loss_1, surrogate_loss_2)
    return surrogate_loss

Maintenant, formons l'agent.

Calcul de la perte de police et de valeur 

Nous sommes maintenant prêts à calculer les pertes de police et de valeur : 

  • La perte de la police est la somme de la perte de substitution et du bonus d'entropie. 
  • La perte de valeurest basée sur la différence entre la valeur prédite par le critique et le rendement (récompense cumulée) généré par la police. Le calcul de la perte de valeur utilise la fonction de perte L1 lissée. Cela permet de lisser la fonction de perte et de la rendre moins sensible aux valeurs aberrantes.

Les deux pertes, telles que calculées ci-dessus, sont des tenseurs. La descente de gradient est basée sur des valeurs scalaires. Pour obtenir une valeur scalaire unique représentant la perte, utilisez la fonction .sum() pour additionner les éléments du tenseur. La fonction ci-dessous montre comment procéder : 

def calculate_losses(
        surrogate_loss, entropy, entropy_coefficient, returns, value_pred):
    entropy_bonus = entropy_coefficient * entropy
    policy_loss = -(surrogate_loss + entropy_bonus).sum()
    value_loss = f.smooth_l1_loss(returns, value_pred).sum()
    return policy_loss, value_loss

Définir la boucle de formation

Avant de commencer le processus de formation, créez un ensemble de tampons sous forme de tableaux vides. L'algorithme d'apprentissage utilisera ces tampons pour stocker des informations sur les actions de l'agent, les états de l'environnement et les récompenses à chaque pas de temps. La fonction ci-dessous initialise ces tampons : 

def init_training():
    states = []
    actions = []
    actions_log_probability = []
    values = []
    rewards = []
    done = False
    episode_reward = 0
    return states, actions, actions_log_probability, values, rewards, done, episode_reward

Chaque itération de formation fait fonctionner l'agent avec les paramètres de politique de cette itération. L'agent interagit avec l'environnement par étapes temporelles dans une boucle jusqu'à ce qu'il atteigne une condition terminale. 

Après chaque étape, l'action, la récompense et la valeur de l'agent sont ajoutées aux tampons respectifs. À la fin de l'épisode, la fonction renvoie l'ensemble des tampons mis à jour, qui résument les résultats de l'épisode. 

Avant d'exécuter la boucle d'entraînement :

  • Mettez le modèle en mode apprentissage à l'aide de agent.train()
  • Réinitialisez l'environnement à un état aléatoire en utilisant env.reset(). Il s'agit de l'état initial pour cette itération de formation. 

Les étapes suivantes expliquent ce qui se passe à chaque étape de la boucle d'apprentissage : 

  • Transmettre l'état à l'agent. 
  • L'agent revient :
    • Action prévue en fonction de l'état, sur la base de la politique (acteur). Faites passer ce tenseur d'actions prédites par la fonction softmax pour obtenir l'ensemble des probabilités d'action.  
    • La valeur prédite de l'état, basée sur la critique.
  • L'agent choisit l'action à entreprendre :
    • Utilisez les probabilités d'action pour estimer la distribution de probabilité. 
    • Sélectionnez aléatoirement une action en prélevant un échantillon de cette distribution. C'est ce que fait la fonction dist.sample()
  • Utilisez la fonction env.step() pour transmettre cette action à l'environnement afin de simuler la réponse de l'environnement pour ce pas de temps. En fonction de l'action de l'agent, l'environnement génère :
    • Le nouvel État
    • La récompense
    • La valeur booléenne de retour done (indique si l'environnement a atteint un état terminal)
  • Ajoutez aux tampons respectifs les valeurs de l'action de l'agent, les récompenses, les valeurs prédites et le nouvel état. 

L'épisode de formation se termine lorsque la fonction env.step() renvoie true pour la valeur de retour booléenne de done

Une fois l'épisode terminé, utilisez les valeurs accumulées à chaque étape pour calculer les rendements cumulés de cet épisode en additionnant les récompenses de chaque étape. Pour ce faire, nous utilisons la fonction calculate_returns() décrite précédemment. Les entrées de cette fonction sont le facteur d'actualisation et la mémoire tampon contenant les récompenses de chaque pas de temps. Nous utilisons ces rendements et les valeurs accumulées à chaque étape pour calculer les avantages à l'aide de la fonction calculate_advantages()

La fonction Python suivante montre comment mettre en œuvre ces étapes :

def forward_pass(env, agent, optimizer, discount_factor):
    states, actions, actions_log_probability, values, rewards, done, episode_reward = init_training()
    state = env.reset()
    agent.train()
    while not done:
        state = torch.FloatTensor(state).unsqueeze(0)
        states.append(state)
        action_pred, value_pred = agent(state)
        action_prob = f.softmax(action_pred, dim=-1)
        dist = distributions.Categorical(action_prob)
        action = dist.sample()
        log_prob_action = dist.log_prob(action)
        state, reward, done, _ = env.step(action.item())
        actions.append(action)
        actions_log_probability.append(log_prob_action)
        values.append(value_pred)
        rewards.append(reward)
        episode_reward += reward
    states = torch.cat(states)
    actions = torch.cat(actions)
    actions_log_probability = torch.cat(actions_log_probability)
    values = torch.cat(values).squeeze(-1)
    returns = calculate_returns(rewards, discount_factor)
    advantages = calculate_advantages(returns, values)
    return episode_reward, states, actions, actions_log_probability, advantages, returns

Mise à jour des paramètres du modèle

Chaque itération de formation fait passer le modèle par un épisode complet composé de nombreux pas de temps (jusqu'à ce qu'il atteigne une condition finale). À chaque étape, nous stockons les paramètres de la politique, l'action de l'agent, les rendements et les avantages. Après chaque itération, nous mettons à jour le modèle sur la base des performances de la politique pour tous les pas de temps de cette itération. 

Le nombre maximum de pas de temps dans l'environnement CartPole est de 500. Dans des environnements plus complexes, il y a plus de pas de temps, voire des millions. Dans ce cas, l'ensemble des résultats de la formation doit être divisé en lots. Le nombre de pas de temps dans chaque lot est appelé la taille du lot d'optimisation. 

Les étapes de la mise à jour des paramètres du modèle sont donc les suivantes :

  • Divisez l'ensemble de données des résultats de la formation en lots. 
  • Pour chaque lot :
    • Obtenez l'action de l'agent et la valeur prédite pour chaque état.
    • Utilisez ces actions prédites pour estimer la nouvelle distribution de probabilité d'action. 
    • Utilisez cette distribution pour calculer l'entropie. 
    • Utilisez cette distribution pour obtenir la probabilité logarithmique des actions dans l'ensemble de données des résultats de la formation. Il s'agit du nouvel ensemble de probabilités logarithmiques des actions dans l'ensemble de données des résultats de la formation. L'ancien ensemble de probabilités logarithmiques de ces mêmes actions a été calculé dans la boucle d'apprentissage expliquée dans la section précédente. 
    • Calculez la perte de substitution en utilisant les anciennes et les nouvelles distributions de probabilité des actions. 
    • Calculez la perte de police et la perte de valeur en utilisant la perte de substitution, l'entropie et les avantages. 
    • Lancez .backward() séparément sur la police et évaluez les pertes. Les gradients des fonctions de perte sont ainsi mis à jour. 
    • Exécutez .step() sur l'optimiseur pour mettre à jour les paramètres de la politique. Dans ce cas, nous utilisons l'optimiseur Adam pour équilibrer la vitesse et la robustesse. 
    • Accumuler la police et évaluer les pertes. 
  • Répétez la passe arrière (les opérations ci-dessus) sur chaque lot plusieurs fois, en fonction de la valeur du paramètre PPO_STEPS. La répétition de la passe arrière sur chaque lot est efficace en termes de calcul, car elle augmente effectivement la taille de l'ensemble de données d'apprentissage sans avoir à exécuter des passes avant supplémentaires. Le nombre d'étapes de l'environnement dans chaque alternance entre l'échantillonnage et l'optimisation est appelé la taille du lot d'itération. 
  • Indiquez la perte moyenne de la police et la perte de valeur.

Le code ci-dessous met en œuvre ces étapes :

def update_policy(
        agent,
        states,
        actions,
        actions_log_probability_old,
        advantages,
        returns,
        optimizer,
        ppo_steps,
        epsilon,
        entropy_coefficient):
    BATCH_SIZE = 128
    total_policy_loss = 0
    total_value_loss = 0
    actions_log_probability_old = actions_log_probability_old.detach()
    actions = actions.detach()
    training_results_dataset = TensorDataset(
            states,
            actions,
            actions_log_probability_old,
            advantages,
            returns)
    batch_dataset = DataLoader(
            training_results_dataset,
            batch_size=BATCH_SIZE,
            shuffle=False)
    for _ in range(ppo_steps):
        for batch_idx, (states, actions, actions_log_probability_old, advantages, returns) in enumerate(batch_dataset):
            # get new log prob of actions for all input states
            action_pred, value_pred = agent(states)
            value_pred = value_pred.squeeze(-1)
            action_prob = f.softmax(action_pred, dim=-1)
            probability_distribution_new = distributions.Categorical(
                    action_prob)
            entropy = probability_distribution_new.entropy()
            # estimate new log probabilities using old actions
            actions_log_probability_new = probability_distribution_new.log_prob(actions)
            surrogate_loss = calculate_surrogate_loss(
                    actions_log_probability_old,
                    actions_log_probability_new,
                    epsilon,
                    advantages)
            policy_loss, value_loss = calculate_losses(
                    surrogate_loss,
                    entropy,
                    entropy_coefficient,
                    returns,
                    value_pred)
            optimizer.zero_grad()
            policy_loss.backward()
            value_loss.backward()
            optimizer.step()
            total_policy_loss += policy_loss.item()
            total_value_loss += value_loss.item()
    return total_policy_loss / ppo_steps, total_value_loss / ppo_steps

Renforcer les compétences en matière d'apprentissage automatique

Améliorez vos compétences en matière d'apprentissage automatique au niveau de la production.

Enfin, exécutons l'agent de l'OPP.

Évaluation de la performance 

Pour évaluer les performances de l'agent, créez un nouvel environnement et calculez les récompenses cumulées lors de l'exécution de l'agent dans ce nouvel environnement. Vous devez mettre l'agent en mode évaluation à l'aide de la fonction .eval(). Les étapes sont les mêmes que pour la boucle d'entraînement. L'extrait de code ci-dessous met en œuvre la fonction d'évaluation : 

def evaluate(env, agent):
    agent.eval()
    rewards = []
    done = False
    episode_reward = 0
    state = env.reset()
    while not done:
        state = torch.FloatTensor(state).unsqueeze(0)
        with torch.no_grad():
            action_pred, _ = agent(state)
            action_prob = f.softmax(action_pred, dim=-1)
        action = torch.argmax(action_prob, dim=-1)
        state, reward, done, _ = env.step(action.item())
        episode_reward += reward
    return episode_reward

Visualiser les résultats de la formation

Nous utiliserons la bibliothèque Matplotlib pour visualiser la progression du processus de formation. La fonction ci-dessous montre comment tracer les récompenses des boucles d'apprentissage et de test :

def plot_train_rewards(train_rewards, reward_threshold):
    plt.figure(figsize=(12, 8))
    plt.plot(train_rewards, label='Training Reward')
    plt.xlabel('Episode', fontsize=20)
    plt.ylabel('Training Reward', fontsize=20)
    plt.hlines(reward_threshold, 0, len(train_rewards), color='y')
    plt.legend(loc='lower right')
    plt.grid()
    plt.show()
def plot_test_rewards(test_rewards, reward_threshold):
    plt.figure(figsize=(12, 8))
    plt.plot(test_rewards, label='Testing Reward')
    plt.xlabel('Episode', fontsize=20)
    plt.ylabel('Testing Reward', fontsize=20)
    plt.hlines(reward_threshold, 0, len(test_rewards), color='y')
    plt.legend(loc='lower right')
    plt.grid()
    plt.show()

Dans les exemples de graphiques ci-dessous, nous montrons les récompenses de formation et de test, obtenues en appliquant la politique dans les environnements de formation et de test respectivement. Notez que la forme de ces tracés sera différente à chaque fois que vous exécuterez le code. Cela est dû au caractère aléatoire inhérent au processus de formation. 

Tracé des récompenses de l'entraînement dans chaque épisode

Récompenses de formation (obtenues par l'application de la politique dans l'environnement de formation). Image par l'auteur.

Récompenses de test (obtenues en appliquant la politique dans l'environnement de test). Image par l'auteur. 

Dans les graphiques de sortie présentés ci-dessus, observez la progression du processus de formation :

  • La récompense commence à partir de faibles valeurs. Au fur et à mesure que la formation progresse, les récompenses augmentent. 
  • Les récompenses fluctuent de manière aléatoire tout en augmentant. Cela est dû au fait que l'agent explore l'espace politique. 
  • L'apprentissage se termine et les récompenses des tests se sont stabilisées autour du seuil (475) pendant de nombreuses itérations. 
  • Les récompenses sont plafonnées à 500. Il s'agit de contraintes imposées par l'environnement (Gym CartPole v1). 

De même, vous pouvez tracer la valeur et les pertes de police au fil des itérations :

def plot_losses(policy_losses, value_losses):
    plt.figure(figsize=(12, 8))
    plt.plot(value_losses, label='Value Losses')
    plt.plot(policy_losses, label='Policy Losses')
    plt.xlabel('Episode', fontsize=20)
    plt.ylabel('Loss', fontsize=20)
    plt.legend(loc='lower right')
    plt.grid()
    plt.show()

L'exemple de graphique ci-dessous montre les pertes suivies au cours des épisodes de formation :

Les pertes de valeur et de politique à travers le processus de formation. Image par l'auteur

Observez l'intrigue et remarquez :

  • Les pertes semblent être réparties de manière aléatoire et ne suivent aucun schéma. 
  • C'est typique de l'entraînement RL, où l'objectif n'est pas de minimiser les pertes mais de maximiser les récompenses. 

Exécutez l'algorithme PPO

Vous disposez maintenant de tous les éléments pour former l'agent à l'utilisation de l'OPP. Pour mettre tout cela en place, vous devez.. :

  • Déclarez les hyperparamètres tels que le facteur d'actualisation, la taille du lot, le taux d'apprentissage, etc. 
  • Instanciez des tampons en tant que tableaux nuls pour stocker les récompenses et les pertes de chaque itération. 
  • Créez une instance d'agent à l'aide de la fonction create_agent().
  • Effectuez des passages itératifs vers l'avant et vers l'arrière à l'aide des fonctions forward_pass() et update_policy()
  • Testez les performances de la politique à l'aide de la fonction evaluate()
  • Ajoutez la politique, les pertes de valeur et les récompenses des fonctions de formation et d'évaluation aux tampons respectifs. 
  • Calculez la moyenne des récompenses et des pertes sur les derniers pas de temps. L'exemple ci-dessous calcule la moyenne des récompenses et des pertes sur les 40 derniers pas de temps. 
  • Imprimez les résultats de l'évaluation à intervalles réguliers. L'exemple ci-dessous imprime toutes les 10 étapes. 
  • Mettez fin au processus lorsque la récompense moyenne dépasse un certain seuil. 

Le code ci-dessous montre comment déclarer une fonction qui fait cela en Python :

def run_ppo():
    MAX_EPISODES = 500
    DISCOUNT_FACTOR = 0.99
    REWARD_THRESHOLD = 475
    PRINT_INTERVAL = 10
    PPO_STEPS = 8
    N_TRIALS = 100
    EPSILON = 0.2
    ENTROPY_COEFFICIENT = 0.01
    HIDDEN_DIMENSIONS = 64
    DROPOUT = 0.2
    LEARNING_RATE = 0.001
    train_rewards = []
    test_rewards = []
    policy_losses = []
    value_losses = []
    agent = create_agent(HIDDEN_DIMENSIONS, DROPOUT)
    optimizer = optim.Adam(agent.parameters(), lr=LEARNING_RATE)
    for episode in range(1, MAX_EPISODES+1):
        train_reward, states, actions, actions_log_probability, advantages, returns = forward_pass(
                env_train,
                agent,
                optimizer,
                DISCOUNT_FACTOR)
        policy_loss, value_loss = update_policy(
                agent,
                states,
                actions,
                actions_log_probability,
                advantages,
                returns,
                optimizer,
                PPO_STEPS,
                EPSILON,
                ENTROPY_COEFFICIENT)
        test_reward = evaluate(env_test, agent)
        policy_losses.append(policy_loss)
        value_losses.append(value_loss)
        train_rewards.append(train_reward)
        test_rewards.append(test_reward)
        mean_train_rewards = np.mean(train_rewards[-N_TRIALS:])
        mean_test_rewards = np.mean(test_rewards[-N_TRIALS:])
        mean_abs_policy_loss = np.mean(np.abs(policy_losses[-N_TRIALS:]))
        mean_abs_value_loss = np.mean(np.abs(value_losses[-N_TRIALS:]))
        if episode % PRINT_INTERVAL == 0:
            print(f'Episode: {episode:3} | \
                  Mean Train Rewards: {mean_train_rewards:3.1f} \
                  | Mean Test Rewards: {mean_test_rewards:3.1f} \
                  | Mean Abs Policy Loss: {mean_abs_policy_loss:2.2f} \
                  | Mean Abs Value Loss: {mean_abs_value_loss:2.2f}')
        if mean_test_rewards >= REWARD_THRESHOLD:
            print(f'Reached reward threshold in {episode} episodes')
            break
    plot_train_rewards(train_rewards, REWARD_THRESHOLD)
    plot_test_rewards(test_rewards, REWARD_THRESHOLD)
    plot_losses(policy_losses, value_losses)

Exécutez le programme : 

run_ppo()

Le résultat devrait ressembler à l'exemple ci-dessous : 

Episode:  10 | Mean Train Rewards: 22.3 | Mean Test Rewards: 30.4 | Mean Abs Policy Loss: 0.37 | Mean Abs Value Loss: 0.39 
Episode:  20 | Mean Train Rewards: 38.6 | Mean Test Rewards: 69.8 | Mean Abs Policy Loss: 0.46 | Mean Abs Value Loss: 0.37 
.
.
.
Episode: 100 | Mean Train Rewards: 289.5 | Mean Test Rewards: 427.3 | Mean Abs Policy Loss: 1.73 | Mean Abs Value Loss: 0.21 
Episode: 110 | Mean Train Rewards: 357.7 | Mean Test Rewards: 461.4 | Mean Abs Policy Loss: 1.86 | Mean Abs Value Loss: 0.22 
Reached reward threshold in 116 episodes

Vous pouvez visualiser et exécuter leprogramme de travailsur cet ordinateur portable DataLab!

Dans l'apprentissage automatique, les hyperparamètres contrôlent le processus de formation. Ci-dessous, j'explique certains des hyperparamètres importants utilisés dans l'OPP : 

  • Taux d'apprentissage : Le taux d'apprentissage détermine le degré de variation des paramètres de la politique à chaque itération. Dans la descente stochastique du gradient, le montant de la mise à jour des paramètres de la politique à chaque itération est déterminé par le produit du taux d'apprentissage et du gradient. 
  • Paramètre d'écrêtage : On parle également d'epsilon, ε. Il décide de l'ampleur de l'écrêtage du ratio de politique générale. Le rapport entre la nouvelle et l'ancienne politique peut varier dans la fourchette suivante [1-ε, 1+ε]. Lorsqu'elle se situe au-delà de cette fourchette, elle est artificiellement coupée pour se situer à l'intérieur de la fourchette. 
  • Taille du lot : Il s'agit du nombre d'étapes à prendre en compte pour chaque mise à jour du gradient. Dans l'OPP, la taille du lot est le nombre de pas de temps nécessaires pour appliquer la politique et calculer la perte de substitution afin de mettre à jour les paramètres de la politique. Dans cet article, nous avons utilisé une taille de lot de 64. 
  • Les étapes de l'itération : Il s'agit du nombre de fois que chaque lot est réutilisé pour exécuter la passe en amont. Le code de cet article se réfère à cela en tant que PPO_STEPS. Dans les environnements complexes, l'exécution de la passe avant à de nombreuses reprises est coûteuse en termes de calcul. Une alternative plus efficace consiste à réexécuter chaque lot plusieurs fois. Il est généralement recommandé d'utiliser une valeur comprise entre 5 et 10. 
  • Facteur d'actualisation : On parle également de gamma, γ. Il exprime la mesure dans laquelle les récompenses immédiates ont plus de valeur que les récompenses futures. Ce concept est similaire à celui des taux d'intérêt dans le calcul de la valeur temporelle de l'argent. Lorsque est plus proche de 0, cela signifie que les récompenses futures ont moins de valeur et que l'agent doit donner la priorité aux récompenses immédiates. Lorsque est proche de 1, cela signifie que les récompenses futures sont importantes .
  • Coefficient d'entropie : Le coefficient d'entropie détermine la prime d'entropie, qui est calculée comme le produit du coefficient d'entropie et de l'entropie de la distribution. Le rôle de la prime d'entropie est d'introduire plus d'aléa dans la politique. Cela encourage l'agent à explorer l'espace politique. Cependant, la formation ne converge pas vers une politique optimale lorsque ce caractère aléatoire est trop élevé. 
  • Critères de réussite de la formation : Vous devez définir les critères permettant de déterminer si la formation est réussie. Pour ce faire, il est courant de poser comme condition que la moyenne des récompenses sur les derniers N essais (épisodes) soit supérieure à un certain seuil. Dans l'exemple de code ci-dessus, ceci est exprimé par la variable N_TRIALS. Lorsque cette valeur est plus élevée, l'apprentissage est plus long car la politique doit atteindre le seuil de récompense sur un plus grand nombre d'épisodes. Elle permet également d'obtenir une politique plus robuste tout en étant plus coûteuse sur le plan informatique. Notez que la PPO est une politique stochastique et qu'il y aura des épisodes où l'agent ne franchira pas le seuil. Par conséquent, si la valeur de N_TRIALS est trop élevée, votre formation risque de ne pas s'achever.

Stratégies d'optimisation des performances des OPP

L'optimisation des performances des algorithmes d'apprentissage des OPP passe par des essais et des erreurs et par l'expérimentation de différentes valeurs d'hyperparamètres. Il existe cependant quelques grandes lignes directrices : 

  • Facteur d'actualisation : Lorsque les récompenses à long terme sont importantes, comme dans l'environnement CartPole, où le poteau doit rester stable dans le temps, commencez par une valeur gamma modérée, telle que 0,99. 
  • Prime à l'entropie : Dans des environnements complexes, l'agent doit explorer l'espace d'action pour trouver la politique optimale. Le bonus d'entropie favorise l'exploration. La prime d'entropie est ajoutée à la perte de substitution. Vérifiez l'ampleur de la perte de substitution et l'entropie de la distribution avant de décider du coefficient d'entropie. Dans cet article, nous avons utilisé un coefficient d'entropie de 0,01.
  • Paramètre d'écrêtage : Le paramètre d'écrêtage détermine dans quelle mesure la politique actualisée peut être différente de la politique actuelle. Une grande valeur du paramètre d'écrêtage favorise une meilleure exploration de l'environnement, mais risque de déstabiliser l'apprentissage. Vous souhaitez un paramètre d'écrêtage qui permette une exploration progressive tout en empêchant les mises à jour déstabilisantes. Dans cet article, nous avons utilisé un paramètre d'écrêtage de 0,2.
  • Taux d'apprentissage : Lorsque le taux d'apprentissage est trop élevé, la politique est mise à jour par grandes étapes, et chaque itération et le processus de formation peuvent devenir instables. Lorsqu'il est trop bas, la formation prend trop de temps.  Ce tutoriel a utilisé un taux d'apprentissage de 0,001, qui convient bien à l'environnement. Dans de nombreux cas, il est recommandé d'utiliser un taux d'apprentissage de 1e-5. 

Défis et bonnes pratiques en matière d'OPP

Après avoir expliqué les concepts de l'OPP et les détails de sa mise en œuvre, examinons les défis et les meilleures pratiques. 

Défis communs à la formation des OPP

Même si l'OPP est largement utilisé, vous devez être conscient des difficultés potentielles pour résoudre des problèmes réels en utilisant cette technique avec succès. Voici quelques-uns de ces défis :

  • Lenteur de la convergence : Dans des environnements complexes, l'OPP peut être inefficace en termes d'échantillon et nécessite de nombreuses interactions avec l'environnement pour converger vers la politique optimale. Cela rend la formation lente et coûteuse. 
  • Sensibilité aux hyperparamètres : L'OPP repose sur l'exploration efficace de l'espace politique. La stabilité du processus d'apprentissage et la vitesse de convergence sont sensibles aux valeurs des hyperparamètres. Les valeurs optimales de ces hyperparamètres ne peuvent souvent être déterminées que par essais et erreurs. 
  • Surajustement :  Les environnements RL sont généralement initialisés avec des paramètres aléatoires. La formation PPO est basée sur la recherche de la politique optimale en fonction de l'environnement de l'agent. Parfois, le processus de formation converge vers un ensemble de paramètres optimaux pour un environnement spécifique, mais pas pour un environnement aléatoire. Ce problème est généralement résolu en procédant à de nombreuses itérations, chacune avec un environnement d'entraînement aléatoire différent. 
  • Environnements dynamiques : Les environnements RL simples, tels que l'environnement CartPole, sont statiques - les règles sont les mêmes à travers le temps. De nombreux autres environnements, tels qu'un robot apprenant à marcher sur une surface instable, sont dynamiques - les règles de l'environnement changent avec le temps. Pour bien fonctionner dans de tels environnements, l'OPP a souvent besoin d'un réglage plus précis. 
  • Exploration ou exploitation : Le mécanisme d'écrêtage de l'OPP garantit que les mises à jour des politiques se font dans une zone de confiance. Cependant, elle empêche également l'agent d'explorer l'espace d'action. Cela peut conduire à une convergence vers des optima locaux, en particulier dans des environnements complexes. D'autre part, si l'on laisse l'agent explorer trop de choses, il risque de ne pas converger vers une politique optimale. 

Meilleures pratiques pour la formation aux modèles PPO

Pour obtenir de bons résultats avec l'OPP, je vous recommande quelques bonnes pratiques, telles que

  • Normaliser les caractéristiques d'entrée : La normalisation des valeurs des rendements et des avantages réduit la variabilité des données et conduit à des mises à jour stables du gradient. La normalisation des données permet de ramener toutes les valeurs dans une fourchette numérique cohérente. Elle permet de réduire l'effet des valeurs aberrantes et extrêmes, qui pourraient autrement fausser les mises à jour du gradient et ralentir la convergence. 
  • Utilisez des lots de taille appropriée : Les petits lots permettent des mises à jour et un apprentissage plus rapides, mais peuvent entraîner une convergence vers des optima locaux et une instabilité dans le processus d'apprentissage. Des lots plus importants permettent à l'agent d'apprendre des politiques robustes, ce qui conduit à un processus de formation stable. Cependant, des lots trop importants ne sont pas non plus optimaux. Outre l'augmentation des coûts de calcul, ils rendent les mises à jour de la politique moins sensibles à la fonction de valeur, car les mises à jour du gradient sont basées sur des moyennes estimées sur des lots importants. En outre, cela peut conduire à une adaptation excessive des mises à jour à ce lot spécifique. 
  • Les étapes de l'itération : Il est généralement conseillé de réutiliser chaque lot pour 5 à 10 itérations. Cela rend le processus de formation plus efficace. La réutilisation trop fréquente du même lot entraîne un surajustement. Le code fait référence à cet hyperparamètre en tant que PPO_STEPS
  • Procédez à une évaluation régulière : Pour détecter un surajustement, il est essentiel de contrôler périodiquement l'efficacité de la politique. Si la politique s'avère inefficace dans certains scénarios, une formation complémentaire ou une mise au point pourrait s'avérer nécessaire. 
  • Ajustez les hyperparamètres: Comme nous l'avons expliqué précédemment, la formation PPO est sensible aux valeurs des hyperparamètres. Expérimentez différentes valeurs d'hyperparamètres afin de déterminer l'ensemble de valeurs adapté à votre problème spécifique. 
  • Réseau fédérateur partagé : Comme l'illustre cet article, l'utilisation d'un réseau fédérateur partagé permet d'éviter les déséquilibres entre les réseaux d'acteurs et de critiques. Le partage d'un réseau dorsal entre l'acteur et le critique facilite l'extraction de caractéristiques partagées et une compréhension commune de l'environnement. Cela rend le processus d'apprentissage plus efficace et plus stable. Il permet également de réduire l'espace de calcul et la complexité temporelle de l'algorithme. 
  • Nombre et taille des couches cachées : Augmentez le nombre de couches cachées et de dimensions pour des environnements plus complexes. Des problèmes plus simples comme CartPole peuvent être résolus avec une seule couche cachée. La couche cachée utilisée dans cet article a 64 dimensions. Le fait de rendre le réseau beaucoup plus grand que nécessaire est un gaspillage de ressources informatiques et peut le rendre instable. 
  • Arrêt précoce : L'arrêt de la formation lorsque les paramètres d'évaluation sont atteints permet d'éviter le surentraînement et le gaspillage des ressources. Une mesure d'évaluation courante est le moment où l'agent dépasse le seuil de récompenses sur les N derniers événements. 

Conclusion

Dans cet article, nous avons abordé la question de l'OPP comme moyen de résoudre les problèmes liés à la LR. Nous avons ensuite détaillé les étapes de la mise en œuvre de PPO à l'aide de PyTorch. Enfin, nous avons présenté quelques conseils et bonnes pratiques en matière de performance pour les OPP. 

La meilleure façon d'apprendre est de mettre en œuvre le code vous-même. Vous pouvez également modifier le code pour qu'il fonctionne avec d'autres environnements de contrôle classiques dans Gym. Pour apprendre à mettre en œuvre des agents RL à l'aide de Python et de Gymnasium d'OpenAI, suivez le cours Reinforcement Learning with Gymnasium in Python!

Projets d'apprentissage automatique

Approfondissez vos compétences en apprentissage automatique grâce à des projets concrets.

Arun Nanda's photo
Author
Arun Nanda
LinkedIn

Arun est un ancien fondateur de startup qui aime construire de nouvelles choses. Il étudie actuellement les fondements techniques et mathématiques de l'intelligence artificielle. Il aime partager ce qu'il a appris, alors il écrit à ce sujet.

En plus de DataCamp, vous pouvez lire ses publications sur Medium, Airbyte et Vultr.

Sujets

Apprenez-en plus sur l'apprentissage automatique grâce à ces cours !

Certification disponible

cours

Apprentissage automatique avec des modèles arborescents en Python

5 hr
98.3K
Dans ce cours, vous apprendrez à utiliser des modèles basés sur des arbres et des ensembles pour la régression et la classification en utilisant scikit-learn.
Afficher les détailsRight Arrow
Commencer le cours
Voir plusRight Arrow