The Nuts and Bolts of Deep RL Research
5 octobre 2017
Intelligence Artificielle
Introduction et contexte
L'année dernière à NIPS, je devais donner une conférence à l'atelier Deep RL. Je n'étais pas sûr de ce dont j'allais parler parce que j'avais déjà parlé de tout ce que j'avais préparé tant de fois que je ne voulais tout simplement pas faire une autre conférence à ce sujet.
J'ai demandé conseil à Pieter sur ce dont je devrais parler. Il a dit qu'Andrew Ng avait donné une conférence plus tôt dans la conférence intitulée "The Nuts and Bolts of Deep Learning", où il passait en revue l'organigramme de ce qu'il faut faire face à un nouveau problème.
Si vous faites du surapprentissage, vous régularisez ; si vous faites du sous-apprentissage, vous utilisez un modèle plus grand, et ainsi de suite. Pieter a suggéré d'écrire une conférence intitulée "The Nuts and Bolts of Deep RL Research", où je parlerais de leçons similaires et de trucs et astuces pour le cadre du RL.
J'ai préparé une conférence à ce sujet, et les gens ont semblé l'apprécier. Je voyais en donner une version légèrement mise à jour dès maintenant.
Je vais parler de quelques points différents, dont certains sont généraux et s'appliquent au RL en général, et d'autres concernent des classes particulières de méthodes comme les méthodes de gradient de politique.
Ce sont juste des petits trucs et astuces sur la façon de faire fonctionner votre algorithme et sur ce que vous faites au quotidien.
Approche initiale d'un nouveau problème
Supposons que vous ayez un tout nouveau problème à résoudre. Vous avez une nouvelle tâche, vous avez trouvé comment définir une observation et un espace d'action, vous avez votre politique de réseau neuronal ou votre fonction Q, et vous voulez commencer à apprendre à le résoudre, mais vous n'avez jamais essayé auparavant.
Si vous avez un nouvel algorithme que vous n'avez jamais utilisé auparavant et que vous essayez de faire fonctionner, quelle est la première chose que vous faites ?
Mon premier conseil serait d'utiliser un petit problème afin de pouvoir lancer beaucoup d'expériences très rapidement et effectuer une recherche d'hyperparamètres. Il est vraiment utile de pouvoir visualiser le processus d'apprentissage d'autant de manières que possible.
Regardez la fréquentation des états, comment elle évolue au fil du temps, et regardez à quel point votre fonction de valeur s'ajuste bien. J'ai passé beaucoup de temps à étudier le problème du pendule où l'on essaie de faire remonter un pendule parce que ce problème a un espace d'états 2D avec l'angle et la vitesse angulaire du pendule.
Je visualisais exactement à quoi ressemble la fonction de valeur, à quoi ressemble exactement la distribution des états et comment ils évoluent au fil du temps. Je pouvais ainsi voir si mon algorithme ne fonctionnait pas, que ce soit parce qu'il oscillait d'une drôle de manière, ou parce qu'il donnait un mauvais ajustement, ou peut-être parce que la fonction de valeur qu'il apprenait n'était pas assez lisse.
Essayez de tout visualiser et utilisez de petits problèmes où vous pouvez tout visualiser. Il est également utile de construire des problèmes fictifs où votre idée sera la plus forte, là où vous pensez que si cette idée a la moindre chance de fonctionner, elle fonctionnera à coup sûr.
Par exemple, si vous essayez de faire quelque chose avec l'apprentissage par renforcement hiérarchique, construisez alors un problème présentant une hiérarchie évidente qu'il devrait apprendre, et vous pourrez dire s'il fait ce qu'il faut.
Construisez également les problèmes où il sera le plus faible. En contrepoint, ne surapprenez pas votre méthode sur un problème artificiel. Si vous avez conçu un problème fictif où votre méthode est vraiment bonne, réalisez qu'il s'agit d'un problème fictif et ne modifiez pas tout juste pour qu'il fonctionne parfaitement sur ce problème particulier.
Il est également très utile d'avoir des problèmes de taille moyenne avec lesquels vous êtes très familier et pour lesquels vous savez exactement à quelle vitesse l'apprentissage doit se faire et quelle doit être la récompense à chaque itération. Il y a quelques problèmes que j'utilise beaucoup, comme l'entraînement sur Pong Atari et le problème Hopper MuJoCo, qui est un problème de robot simulé avec un robot sauteur.
Je sais exactement à quelle vitesse un algorithme fonctionnel doit apprendre sur ces problèmes, ce qui facilite les réglages.
Simplification de la tâche et visualisation
Si vous avez une nouvelle tâche, je vous recommanderais de simplement rendre la tâche plus facile jusqu'à ce que vous commenciez à voir des signes de vie, comme un début d'apprentissage.
Il existe différentes façons de rendre cela plus facile. Vous pouvez essayer de faire de l'ingénierie de caractéristiques pour que la politique soit une fonction simple de vos caractéristiques d'entrée. Si vous essayez de faire fonctionner Pong avec des images en entrée et que vous n'apprenez rien, vous pouvez configurer le problème en passant les coordonnées X, Y en entrée puis lancer votre algorithme.
C'est une fonction beaucoup plus simple que vous essayez d'apprendre, donc c'est beaucoup plus susceptible de fonctionner. Ensuite, vous pouvez essayer de le rendre de plus en plus difficile jusqu'à résoudre le problème complet. Une autre façon de faciliter la tâche consiste à façonner la fonction de récompense. Cela signifie concevoir une fonction de récompense qui vous donne un retour rapide sur le fait que vous faites la bonne chose.
Supposons que nous définissions une tâche où un robot doit atteindre une cible et que nous lui donnions une récompense s'il l'atteint. Il reçoit une récompense de un s'il touche la cible et zéro sinon. Cela pourrait être difficile à apprendre parce que vous ne recevez aucun retour pendant que vous tâtonnez.
Mais nous pourrions définir une fonction de récompense mieux façonnée, comme la simple distance à la cible. L'apprentissage sera beaucoup plus rapide sur ce problème.
Il y a aussi le problème de savoir exactement comment transformer votre problème en un POMDP au départ. Souvent, on ne sait pas quelles devraient être les caractéristiques d'observation ni même quelle devrait être la fonction de récompense, ou si le problème que vous essayez de résoudre est réalisable tout court.
Supposons que vous ayez un jeu ou une tâche robotique et que vous vouliez en faire un problème d'apprentissage par renforcement, mais que vous ne soyez pas sûr que ce soit réalisable. La première chose à faire est de simplement visualiser une politique aléatoire agissant sur ce problème et de voir ce qui se passe.
Si la politique aléatoire fait occasionnellement la bonne chose, alors il y a de fortes chances que l'apprentissage par renforcement fonctionne parce qu'une méthode de gradient de politique va simplement prendre ce comportement aléatoire et rendre les bons comportements plus probables. Elle va progressivement s'affiner sur les bons comportements.
Alors que si vous ne faites jamais ce qu'il faut, le RL ne recevra aucun signal lui indiquant de faire la bonne chose. Parfois, le RL est capable d'apprendre même quand cela semble impossible, comme apprendre à marcher. On pourrait penser qu'il faut que toute la politique soit en place avant de faire quoi que ce soit d'utile.
En réalité, on apprend à faire un pas puis on tombe, puis on fait deux pas et on tombe, jusqu'à obtenir une démarche correcte.
Une autre chose à faire est de s'assurer que vos observations sont utilisables. Essayez de les regarder en tant qu'humain et voyez si vous pouvez contrôler le système en utilisant les mêmes observations que vous donnez à l'agent. Si vous effectuez un prétraitement sur vos images, regardez-les vous-même et assurez-vous de ne pas perdre trop de détails lors du sous-échantillonnage ou des transformations de couleur.
Vous voulez vous assurer que tout est mis à l'échelle de manière raisonnable. En règle générale, on veut généralement que tout ait une moyenne de zéro et un écart-type de un pour les observations. Pour les récompenses, c'est une heuristique raisonnable.
Vous pourriez vouloir les mettre à l'échelle avec un filtre. Si vous ne voulez pas manipuler de filtres, vous pouvez simplement mettre à l'échelle vos observations et récompenses vous-même si vous pouvez les définir.
Je recommanderais de tracer des histogrammes de toutes vos observations et récompenses pour vous assurer que chaque composant est correctement mis à l'échelle avec la bonne moyenne et le bon écart-type, sans valeurs aberrantes extrêmes.
Références et reproductibilité
Vous devriez avoir de bonnes références à utiliser face à un nouveau problème. Comme on ne sait pas d'avance quel algorithme fonctionnera, assurez-vous d'avoir plusieurs outils bien réglés à tester sur chaque problème.
Faites-vous cela sur l'ensemble de votre régime d'entraînement ou par mise à jour ?
La question était : si l'on fait une normalisation des récompenses, faut-il le faire sur tout l'entraînement ou juste sur les données récentes ? C'est subtil, je dirais d'utiliser toutes les données jusqu'ici car filtrer rend tout non stationnaire. J'en reparlerai plus tard.
Bref, je recommanderais comme références une méthode de cross-entropie, des méthodes de gradient de politique et une méthode de type Q-learning ou SARSA. Il existe maintenant beaucoup de code en ligne utilisable, comme notre dépôt OpenAI Baselines et RL Lab.
Une autre chose qui bloque souvent les gens, surtout en essayant de reproduire des travaux publiés, est d'implémenter l'algorithme selon l'article et de voir qu'il n'apprend rien. On se demande : "Mon code est-il faux ou que s'est-il passé ?"
Au début, vous pourriez avoir besoin de plus d'échantillons que prévu. Un hyperparamètre ajustable est la taille du lot ou le nombre d'échantillons. Je dirais qu'il faut parfois en utiliser plus que prévu car les choses fonctionnent presque toujours mieux avec plus d'échantillons.
Souvent, en reproduisant un article, on a presque tout bon mais pas tout ; peut-être un mauvais réglage d'échelle ou un hyperparamètre obscur, et le code n'apprend rien.
Dans ce cas, essayez d'abord d'obtenir un petit résultat puis peaufinez tout pour atteindre les performances publiées. Pour faire fonctionner quoi que ce soit, il faut souvent des tailles de lots plus grandes qu'imaginé.
Si votre lot est trop petit, le bruit masquera le signal et vous n'apprendrez rien. Pour TRPO, je ne voyais rien au début parce que mon lot était trop petit ; j'ai dû passer à 100 000 pas de temps.
Pour Atari avec DQN, les meilleurs réglages consistaient à mettre à jour la fonction Q tous les 10 000 pas de temps avec 1 million de pas dans le tampon de relecture, ce qui est énorme.
Développement et réglage continu
Je vais maintenant parler de directives pour le développement et le réglage continu, par opposition à la recherche initiale de signes de vie sur un nouveau problème.
Une une fois que ça fonctionne, regardez la sensibilité de l'algorithme à chaque hyperparamètre. S'il est trop sensible, il n'est pas robuste et vous ne devriez pas vous en satisfaire. Vous avez probablement juste eu de la chance sur ce problème.
Il est possible qu'une méthode réussisse par pur hasard sur un problème à cause d'une dynamique particulière sans fonctionner en général ; elle nécessite alors de sérieuses améliorations.
Certains indicateurs vous diront si votre processus d'optimisation est sain.
Cela varie selon l'algorithme, mais vous pouvez vérifier si votre fonction de valeur est précise et prédit bien les retours. Regardez aussi l'importance des mises à jour et les diagnostics standards comme les normes de gradients.
Il est utile d'avoir un système pour comparer continuellement tout votre code par rapport à des références, car on peut facilement régler un algorithme pour un problème et dégrader ses performances ailleurs.
On surapprenez facilement sur un seul problème en ajustant les hyperparamètres. Je recommande d'avoir un benchmark fréquent et une batterie de tests occasionnels.
De même, il est facile de croire qu'on améliore ou dégrade l'algorithme alors qu'on ne voit que du bruit aléatoire.
Gérer le bruit et la complexité
Voici sept tâches Gym MuJoCo comme HalfCheetah et Hopper avec trois algorithmes : rouge, vert et bleu. Les performances diffèrent, mais lequel semble le meilleur ?
Ça varie selon le problème : le bleu semble meilleur ici, le rouge moins bon là. En réalité, c'est le même algorithme avec des graines aléatoires différentes.
On peut croire avoir trouvé une amélioration majeure avec la courbe bleue alors que c'est juste une graine chanceuse.
Il faut lancer l'algorithme plusieurs fois et faire la moyenne. Même avec 20 graines, la barre d'erreur reste grande. C'est difficile, il faut multiplier les tâches et les graines, sinon vous surapprenez, sauf amélioration radicale.
On a aussi tendance à accumuler les petites modifications jusqu'à avoir un algorithme complexe qu'on croit parfait, alors que la plupart des ajouts sont inutiles car les astuces se substituent entre elles.
Beaucoup d'astuces ont des effets similaires sur la normalisation ou l'optimisation. On peut souvent simplifier l'algorithme sans perte.
C'est important. Les changements liés au blanchiment se substituent entre eux et aux changements d'optimisation. Simplifiez pour que vos idées se généralisent mieux.
Enfin, automatisez vos expériences pour ne pas passer la journée à regarder des chiffres. C'est tentant, mais avec plusieurs graines, il faut automatiser et lancer plusieurs tests simultanément.
Utilisez des services de cloud pour lancer des expériences à distance et récupérer les résultats. Une question ?
Vous parliez de lancer beaucoup d'expériences. Recommandez-vous un framework pour suivre les résultats ?
Personnellement, je n'utilise aucun framework, juste des notebooks IPython et des scripts qui lisent mes fichiers journaux pour les tracer. Je ne trouve pas les bases de données d'hyperparamètres nécessaires.
Prétraitement et normalisation des données
Je vais maintenant parler des stratégies de réglage générales du RL, puis des stratégies spécifiques par classe d'algorithme.
Il faut blanchir ou standardiser vos données. Si la plage des observations est inconnue, standardisez-les avec une estimation glissante de la moyenne et de l'écart-type (Z-transform).
Calculez-les sur toutes les données vues car sinon vous changez les données d'une façon que l'algorithme de gradient de politique ignore.
Si vous changez le problème en cours de route, vous risquez d'effondrer les performances car l'optimiseur n'en est pas informé.
En utilisant toutes les données depuis le début, la vitesse de changement des échelles ralentira avec le temps. C'est ce que je préconise pour les observations.
Pour les récompenses, redimensionnez-les sans les décaler, car décaler la moyenne change la "volonté de vivre" de l'agent et donc le problème lui-même.
On peut aussi standardiser les cibles de prédiction, bien que ce soit plus complexe.
Que pensez-vous du blanchiment par ACP ?
Le blanchiment par ACP pourrait aider, je n'ai pas testé. C'est dur à prédire avec les réseaux neuronaux car ils savent bien démêler les choses.
Cependant, si les échelles sont terribles (ex: de -1000 à 1000 versus -0,1 à 0,1), l'apprentissage sera lent. Cette mise à l'échelle aide beaucoup.
Paramètres critiques et discrétisation
Certains paramètres sont cruciaux, comme le facteur de remise (gamma). Il détermine l'attribution de crédit et l'importance des effets retardés.
Avec gamma = 0,99, vous ignorez les effets au-delà de 100 pas. Gamma contrôle votre myopie. Regardez à quoi cela correspond en temps réel.
On discrétise souvent le temps en RL ; vérifiez si ces 100 pas représentent trois secondes réelles et ce qui s'y passe.
Avec des méthodes TD-lambda, on peut utiliser un gamma proche de 1 (0,999) sans instabilité si lambda est plus faible (0,9).
Vérifiez si le problème est soluble au niveau de discrétisation choisi.
En frame skip, un humain peut-il encore contrôler le jeu ? Si l'action est répétée trop longtemps, le contrôle devient impossible.
Observez l'exploration aléatoire. La discrétisation détermine la portée du mouvement brownien. Choisissez-la pour qu'elle produise des comportements intéressants. Question ?
Pour un DQN simple, comment choisissez-vous les hyperparamètres pour démarrer sur un nouveau problème ?
J'aborderai le réglage du DQN très bientôt.
Indicateurs de performance et diagnostics
Analysez les retours par épisode : moyenne, min et max. Dans un système déterministe, la politique peut se focaliser sur un retour max découvert pour faire monter la moyenne.
Vérifiez si la politique découvre parfois la stratégie optimale ou si elle stagne. La longueur de l'épisode est aussi très informative.
Si vous perdez tout le temps, la récompense ne bouge pas, mais une longueur d'épisode accrue montre que vous perdez moins vite.
Pour le gradient de politique, surveillez l'entropie. Si elle chute trop vite, la politique devient déterministe et n'explore plus rien.
À l'inverse, si elle ne descend pas, la politique reste aléatoire. Utilisez un bonus d'entropie ou une pénalité KL pour stabiliser cela.
Vérifiez la divergence KL des mises à jour : 0,01 est faible, 10 est énorme. Question ?
Comment mesurez-vous l'entropie de la politique ?
Elle se calcule souvent analytiquement pour un espace d'actions discret ou une gaussienne en continu.
On parle ici de l'entropie dans l'espace des actions moyennée sur l'espace des états. L'entropie de l'espace des états est plus dure à estimer.
Regardez la variance expliquée : votre fonction de valeur prédit-elle mieux que le hasard ? Si vous prédisez zéro partout, elle est nulle.
Si elle est négative, c'est du surapprentissage ou du bruit. Réglez vos hyperparamètres pour que la prédiction soit utile.
Pourquoi un pic de KL entraîne-t-il une perte de performance ?
C'est souvent une perte car on sort de la zone où l'approximation locale est fiable. On dépasse la cible.
Un pas trop grand dans n'importe quelle direction dégrade généralement une fonction complexe.
Initialisez la dernière couche à zéro ou très bas pour maximiser l'entropie et l'exploration aléatoire initiale, plutôt que d'avoir une opinion arbitraire dès le départ.
Conseils spécifiques : Q-Learning et DQN
Pour le Q-learning : un grand tampon de relecture aide souvent, mais attention à la mémoire.
Les programmes de taux d'apprentissage et d'exploration (epsilon-greedy) sont très utiles à ajuster.
Le DQN converge lentement avec une phase de rodage mystérieuse. Il faut être patient et courageux pour laisser le code tourner.
Conseil personnel : lisez les vieux manuels et thèses, ils sont plus denses en informations fondamentales que les articles récents.
Philosophie de recherche et limites
Ne bloquez pas sur un seul problème ; même un bon algorithme peut échouer sur un test simple (ex: cart-pole swing-up) sans réglage fin.
L'algorithme ultime n'existe pas encore. Contentez-vous d'améliorer les performances sur un large éventail de tâches.
Le DQN est médiocre en contrôle continu mais excellent ailleurs. On ne peut pas tout résoudre avec la même méthode sans ajustement.
L'apprentissage supervisé et le RL sont différents. Ne soyez pas surpris si les techniques ne se transfèrent pas.
Certains s'étonnent de l'absence de batch norm ou dropout en RL. Ce n'est pas faute d'avoir essayé.
Ces méthodes n'aident pas forcément ici. Faire fonctionner le dropout en RL serait une avancée majeure, mais ce n'est pas simple.
Voilà, c'est tout. Merci.
Session de questions-réponses
J'ai quelques minutes pour vos questions.
Quand faut-il arrêter de s'acharner sur un algorithme qui ne semble pas fonctionner après une semaine ?
Difficile à dire. En gradient de politique, l'apprentissage démarre souvent vite. Si rien ne se passe, il y a peut-être un souci.
Mais il peut y avoir un temps de rodage numérique. Revenez aux problèmes simples pour forger votre intuition.
D'autres questions au fond ? Non ?
Faites-vous des tests unitaires ou du réglage d'hyperparamètres ?
J'utilise des tests unitaires pour des fonctions mathématiques précises comme la divergence KL, où l'on peut définir une réponse correcte.
C'est plus dur pour un algorithme complet à cause du hasard et du rythme d'apprentissage variable.
Un test de performance peut échouer par simple bruit. Mais les tests unitaires restent une bonne idée.
Comment choisir entre gradient de politique, itération de valeur ou acteur-critique selon la tâche ?
Il n'y a pas de règle absolue, c'est parfois lié à des accidents historiques.
Si la complexité des échantillons importe peu, le gradient de politique est plus compréhensible et sûr car c'est de la descente de gradient directe.
Le Q-learning est plus efficace pour les simulateurs coûteux ou les données hors politique, mais il est plus capricieux.
DQN brille sur les jeux avec images, tandis que le gradient de politique semble meilleur pour la locomotion robotique continue.
Quels vieux manuels recommandez-vous ?
Bertsekas sur le contrôle optimal, Sutton et Barto, Puterman sur les processus de décision de Markov, ainsi que des ouvrages d'optimisation numérique.
Que pensez-vous des stratégies d'évolution chez OpenAI ?
Avez-vous une question spécifique sur leur comparaison avec les autres méthodes ?
Si les stratégies d'évolution (ES) sont aussi performantes et plus simples, pourquoi utiliser le gradient de politique ?
L'article "Evolutionary Strategies as a Scalable Alternative to RL" montrait que l'ES est compétitif. En pratique, l'ES est souvent plus lent d'un facteur constant. Le gradient de politique (PPO, Actor) reste généralement supérieur là où il fonctionne. L'ES peut toutefois être utile pour gérer des dépendances temporelles très longues.
Dernière question.
Quel est votre framework d'optimisation d'hyperparamètres préféré ?
Je préfère l'échantillonnage aléatoire uniforme combiné à une analyse humaine des résultats pour comprendre quels paramètres comptent vraiment. Cela évite de gaspiller du calcul et l'intuition acquise se transfère d'un problème à l'autre.
Parfait. Merci John.