Les grands réseaux de neurones sont au cœur de nombreuses avancées récentes de l'IA, mais leur formation est un défi d'ingénierie et de recherche difficile qui nécessite d'orchestrer un cluster de GPU pour effectuer un seul calcul synchronisé. À mesure que la taille des clusters et des modèles a augmenté, les praticiens de l'apprentissage automatique ont développé une variété croissante de techniques pour paralléliser la formation de modèles sur de nombreux GPU. À première vue, comprendre ces techniques de parallélisme peut sembler intimidant, mais avec seulement quelques hypothèses sur la structure du calcul, ces techniques deviennent beaucoup plus claires - à ce stade, vous ne faites que faire la navette entre des bits opaques de A à B comme un réseau changer les navettes autour des paquets.
Parallélisme des données
Parallélisme de pipeline
Parallélisme tenseur
Parallélisme expert
Parallélisme des données
Parallélisme de pipeline
Parallélisme tenseur
Parallélisme expert
Une illustration de diverses stratégies de parallélisme sur un modèle à trois couches. Chaque couleur fait référence à une couche et les lignes pointillées séparent les différents GPU.
Pas de parallélisme
La formation d'un réseau de neurones est un processus itératif. À chaque itération, nous effectuons une passe en avant à travers les modèles d'un modèle couches pour calculer une sortie pour chaque exemple d'apprentissage dans un lot de données. Puis une autre passe se déroule en arrière à travers les couches, en propageant à quel point chaque paramètre affecte la sortie finale en calculant un pente par rapport à chaque paramètre. Le gradient moyen pour le lot, les paramètres et certains états d'optimisation par paramètre sont transmis à un algorithme d'optimisation, tel que Adam, qui calcule les paramètres de la prochaine itération (qui devraient avoir des performances légèrement meilleures sur vos données) et un nouvel état d'optimisation par paramètre. Au fur et à mesure que la formation itère sur des lots de données, le modèle évolue pour produire des sorties de plus en plus précises.
Diverses techniques de parallélisme découpent ce processus de formation dans différentes dimensions, notamment :
- Parallélisme des données : exécutez différents sous-ensembles du lot sur différents GPU ;
- Parallélisme de pipeline : exécutez différentes couches du modèle sur différents GPU ;
- Parallélisme des tenseurs : décomposez les calculs pour une seule opération telle qu'une multiplication matricielle à répartir sur les GPU ;
- Mixture-of-Experts - traiter chaque exemple par seulement une fraction de chaque couche.
(Dans cet article, nous supposerons que vous utilisez des GPU pour entraîner vos réseaux de neurones, mais les mêmes idées s'appliquent à ceux qui utilisent d'autres accélérateur de réseau de neurones.)
Parallélisme des données
Données parallèles la formation consiste à copier les mêmes paramètres sur plusieurs GPU (souvent appelés "travailleurs") et à attribuer différents exemples à chacun pour qu'ils soient traités simultanément. Le parallélisme des données à lui seul nécessite toujours que votre modèle s'intègre dans la mémoire d'un seul GPU, mais vous permet d'utiliser le calcul de nombreux GPU au prix du stockage de nombreuses copies en double de vos paramètres. Cela étant dit, il existe des stratégies pour augmenter la RAM effective disponible pour votre GPU, comme le déchargement temporaire des paramètres vers la mémoire CPU entre les utilisations.
Au fur et à mesure que chaque travailleur parallèle de données met à jour sa copie des paramètres, ils doivent se coordonner pour s'assurer que chaque travailleur continue d'avoir des paramètres similaires. L'approche la plus simple consiste à introduire une communication bloquante entre les travailleurs : (1) calculer indépendamment le gradient sur chaque travailleur ; (2) faire la moyenne des gradients entre les travailleurs; et (3) calculer indépendamment les mêmes nouveaux paramètres sur chaque travailleur. L'étape (2) est une moyenne de blocage qui nécessite le transfert d'un grand nombre de données (proportionnelles au nombre de travailleurs multiplié par la taille de vos paramètres), ce qui peut nuire à votre débit d'entraînement. Il y a plusieurs schémas de synchronisation asynchrone pour supprimer cette surcharge, mais ils nuisent à l'efficacité de l'apprentissage ; en pratique, les gens s'en tiennent généralement à l'approche synchrone.
Parallélisme de pipeline
Avec Pipeline Parallèle formation, nous partitionnons des morceaux séquentiels du modèle sur les GPU. Chaque GPU ne contient qu'une fraction des paramètres, et donc le même modèle consomme proportionnellement moins de mémoire par GPU.
Il est simple de diviser un grand modèle en morceaux de couches consécutives. Cependant, il existe une dépendance séquentielle entre les entrées et les sorties des couches, de sorte qu'une implémentation naïve peut entraîner une grande quantité de temps d'inactivité pendant qu'un travailleur attend que les sorties de la machine précédente soient utilisées comme entrées. Ces blocs de temps d'attente sont connus sous le nom de "bulles", ce qui gaspille le calcul qui pourrait être effectué par les machines au ralenti.
Illustration d'une configuration de parallélisme de pipeline naïf où le modèle est divisé verticalement en 4 partitions par couche. Le travailleur 1 héberge les paramètres du modèle de la première couche du réseau (la plus proche de l'entrée), tandis que le travailleur 4 héberge la couche 4 (la plus proche de la sortie). "F", "B" et "U" représentent respectivement les opérations avant, arrière et de mise à jour. Les indices indiquent sur quel worker une opération s'exécute. Les données sont traitées par un travailleur à la fois en raison de la dépendance séquentielle, ce qui entraîne de grandes « bulles » de temps d'inactivité.
Nous pouvons réutiliser les idées du parallélisme des données pour réduire le coût de la bulle en demandant à chaque travailleur de ne traiter qu'un sous-ensemble d'éléments de données à la fois, ce qui nous permet de superposer intelligemment les nouveaux calculs avec le temps d'attente. L'idée centrale est de diviser un lot en plusieurs microlots ; chaque micro-lot doit être proportionnellement plus rapide à traiter et chaque travailleur commence à travailler sur le micro-lot suivant dès qu'il est disponible, accélérant ainsi l'exécution du pipeline. Avec suffisamment de microlots, les travailleurs peuvent être utilisés la plupart du temps avec une bulle minimale au début et à la fin de l'étape. Les gradients sont moyennés sur les microlots et les mises à jour des paramètres ne se produisent qu'une fois tous les microlots terminés.
Le nombre de travailleurs sur lesquels le modèle est divisé est communément appelé profondeur de canalisation.
Pendant le passage vers l'avant, les travailleurs n'ont qu'à envoyer la sortie (appelée activations) de son bloc de couches au prochain travailleur ; lors de la passe arrière, il n'envoie que les gradients de ces activations au travailleur précédent. Il existe un grand espace de conception sur la façon de planifier ces passes et d'agréger les gradients sur les microlots. GPipe fait passer chaque processus de travail en avant et en arrière consécutivement, puis agrège les gradients de plusieurs microlots de manière synchrone à la fin. PipeDream planifie à la place chaque travailleur pour qu'il traite alternativement les passes avant et arrière.
GPipe
PipeDream
Comparaison des schémas de pipelining GPipe et PipeDream, en utilisant 4 microlots par lot. Les microlots 1 à 8 correspondent à deux lots de données consécutifs. Dans l'image, "(nombre)" indique sur quel microlot une opération est effectuée et l'indice marque l'ID du travailleur. Notez que PipeDream gagne en efficacité en effectuant certains calculs avec des paramètres obsolètes.
Parallélisme tenseur
Le parallélisme de pipeline divise un modèle "verticalement" par couche. Il est également possible de diviser « horizontalement » certaines opérations au sein d'une couche, ce qui est généralement appelé Tenseur Parallèle entraînement. Pour de nombreux modèles modernes (tels que le Transformateur), le goulot d'étranglement du calcul est la multiplication d'une matrice de lot d'activation par une grande matrice de poids. Multiplication matricielle peuvent être considérés comme des produits scalaires entre des paires de lignes et de colonnes ; il est possible de calculer des produits scalaires indépendants sur différents GPU, ou de calculer des parties de chaque produit scalaire sur différents GPU et de résumer les résultats. Avec l'une ou l'autre stratégie, nous pouvons découper la matrice de poids en "fragments" de taille égale, héberger chaque fragment sur un GPU différent et utiliser ce fragment pour calculer la partie pertinente du produit matriciel global avant de communiquer ultérieurement pour combiner les résultats.
Un exemple est Mégatron-LMqui parallélise les multiplications matricielles dans les couches auto-attention et MLP du transformateur. PTD-P utilise le parallélisme des tenseurs, des données et des pipelines ; son calendrier de pipeline attribue plusieurs couches non consécutives à chaque appareil, réduisant ainsi la surcharge de bulles au prix d'une communication réseau accrue.
Parfois, l'entrée du réseau peut être parallélisée sur une dimension avec un degré élevé de calcul parallèle par rapport à la communication croisée. Parallélisme de séquence est une de ces idées, où une séquence d'entrée est divisée dans le temps en plusieurs sous-exemples, diminuant proportionnellement la consommation maximale de mémoire en permettant au calcul de se poursuivre avec des exemples de taille plus granulaire.
Mélange d'experts (MoE)
Avec le Mélange d'experts (MoE) approche, seule une fraction du réseau est utilisée pour calculer la sortie pour une entrée. Un exemple d'approche consiste à avoir de nombreux ensembles de pondérations et le réseau peut choisir l'ensemble à utiliser via un mécanisme de déclenchement au moment de l'inférence. Cela permet d'avoir beaucoup plus de paramètres sans augmenter le coût de calcul. Chaque ensemble de pondérations est appelé « experts », dans l'espoir que le réseau apprendra à attribuer des calculs et des compétences spécialisés à chaque expert. Différents experts peuvent être hébergés sur différents GPU, offrant un moyen clair d'augmenter le nombre de GPU utilisés pour un modèle.
Illustration d'une couche de mélange d'experts (MoE). Seulement 2 sur n les experts sont sélectionnés par le réseau de blocage. (Image adaptée de : Shazeer et al., 2017)
GShard met à l'échelle un transformateur MoE jusqu'à 600 milliards de paramètres avec un schéma dans lequel seules les couches MoE sont réparties sur plusieurs appareils TPU et les autres couches sont entièrement dupliquées. Commutateur Transformateur adapte la taille du modèle à des billions de paramètres avec une parcimonie encore plus élevée en acheminant une entrée vers un seul expert.
Autres conceptions d'économie de mémoire
Il existe de nombreuses autres stratégies de calcul pour rendre la formation de réseaux de neurones de plus en plus grands plus faciles à gérer. Par exemple:
-
Pour calculer le gradient, vous devez avoir enregistré les activations d'origine, ce qui peut consommer beaucoup de RAM de l'appareil. Point de contrôle (également connu sous le nom de recalcul d'activation) stocke n'importe quel sous-ensemble d'activations et recalcule les activations intermédiaires juste à temps lors de la passe en arrière. Cela économise beaucoup de mémoire au coût de calcul d'au plus une passe avant complète supplémentaire. On peut aussi continuellement arbitrer entre le coût de calcul et le coût de la mémoire en recalcul d'activation sélectivequi contrôle des sous-ensembles d'activations qui sont relativement plus coûteux à stocker mais moins chers à calculer.
-
Entraînement de précision mixte est de former des modèles en utilisant des nombres de précision inférieure (le plus souvent FP16). Les accélérateurs modernes peuvent atteindre des nombres de FLOP beaucoup plus élevés avec des nombres de précision inférieure, et vous économisez également sur la RAM de l'appareil. Avec des soins appropriés, le modèle résultant ne peut perdre pratiquement aucune précision.
-
Déchargement est de décharger temporairement les données inutilisées vers le CPU ou entre différents appareils et de les relire ultérieurement si nécessaire. Les implémentations naïves ralentiront beaucoup la formation, mais les implémentations sophistiquées pré-récupéreront les données afin que l'appareil n'ait jamais besoin de les attendre. Une mise en œuvre de cette idée est Zéro qui divise les paramètres, les gradients et les états de l'optimiseur sur tout le matériel disponible et les matérialise selon les besoins.
-
Optimiseurs efficaces en mémoire ont été proposés pour réduire l'empreinte mémoire de l'état d'exécution maintenu par l'optimiseurtel que Adafactor.
-
Compression peut également être utilisé pour stocker des résultats intermédiaires dans le réseau. Par exemple, Essentiel compresse les activations enregistrées pour la passe arrière ; DALL·E compresse les dégradés avant de les synchroniser.
Chez OpenAI, nous formons et améliorons de grands modèles depuis l'infrastructure sous-jacente jusqu'à leur déploiement pour des problèmes du monde réel. Si vous souhaitez mettre en pratique les idées de cet article, particulièrement pertinentes pour nos équipes de mise à l'échelle et de recherche appliquée, nous sommes embauche!
Source