web-dev-qa-db-fra.com

Stratégies de parallélisation pour l'apprentissage en profondeur

Quelles stratégies et formes de parallélisation sont réalisables et disponibles pour formation et service un réseau neuronal?:

  • à l'intérieur une machine à travers cœurs (par exemple GPU/TPU/CPU)
  • à travers machines sur un réseau ou un rack

Je recherche également des preuves de la manière dont ils peuvent également être utilisés par ex. TensorFlow, PyTorch ou MXNet.

Formation

À ma connaissance, lors de la formation de grands réseaux de neurones sur de grands ensembles de données, on pourrait au moins avoir:

  1. Différents noyaux ou machines fonctionnent sur différentes parties du graphique ("graphique fractionnement "). Par exemple. la rétropropagation à travers le graphique lui-même peut être parallélisée, par ex. en ayant différentes couches hébergées sur différentes machines puisque (je pense?) le graphe autodiff est toujours un DAG .
  2. Différents noyaux ou machines fonctionnent sur différents échantillons de données ( "données fractionnement "). Dans SGD, le calcul des gradients entre les lots ou les échantillons peut également être parallélisé (par exemple, les gradients peuvent être combinés après les avoir calculés indépendamment sur différents lots). Je crois que c'est aussi appelé accumulation de gradient (?).

Quand chaque stratégie est-elle meilleure pour quel type de problème ou de réseau neuronal? Quels modes sont pris en charge par les bibliothèques modernes? et peut-on combiner les quatre stratégies (2x2)?

En plus de cela, j'ai lu sur:

  • Asynchrone entraînement
  • Synchrone entraînement

mais je ne sais pas à quoi cela se réfère exactement, par exemple est-ce le calcul de gradients sur différents lots de données ou le calcul de gradients sur différents sous-graphes? Ou peut-être fait-il référence à autre chose?

Portion

Si le réseau est énorme, la prédiction/inférence peut également être lente et le modèle peut ne pas tenir sur une seule machine en mémoire au moment de la diffusion. Existe-t-il des solutions de prédiction multicœur et multi-nœuds connues qui fonctionnent et qui peuvent gérer de tels modèles?

15
Josh

Comme la question est assez large, je vais essayer d'apporter un éclairage un peu différent et de toucher des sujets différents de ce qui a été montré dans @ Daniel's réponse approfondie.

Formation

Parallélisation des données vs parallélisation des modèles

Comme mentionné par @ Daniel le parallélisme des données est utilisé beaucoup plus souvent et est plus facile à faire correctement. La principale mise en garde du parallélisme des modèles est la nécessité d'attendre une partie du réseau neuronal et la synchronisation entre eux.

Supposons que vous ayez un simple feedforward 5 réseau neuronal de couche réparti sur 5 différents GPU, chaque couche pour un appareil. Dans ce cas, lors de chaque passage avant, chaque appareil doit attendre les calculs des couches précédentes. Dans ce cas simpliste, la copie de données entre les appareils et la synchronisation prendrait beaucoup plus de temps et n'apporterait aucun avantage.

D'autre part, il existe des modèles mieux adaptés à la parallélisation de modèles comme Inception networks , voir l'image ci-dessous:

inception block

Içi vous pouvez voir 4 chemins indépendants de la couche précédente qui pourraient aller en parallèle et seulement 2 points de synchronisation (Filter concatenation et Previous Layer).

Des questions

Par exemple. la rétropropagation à travers le graphique lui-même peut être parallélisée, par ex. en ayant différentes couches hébergées sur différentes machines puisque (je pense?) le graphe autodiff est toujours un DAG.

Ce n'est pas si facile. Les gradients sont calculés en fonction de la valeur de la perte (généralement) et vous devez connaître les gradients des couches plus profondes pour calculer les gradients pour les plus superficiels. Comme ci-dessus, si vous avez des chemins indépendants, c'est plus facile et peut aider, mais c'est beaucoup plus facile sur un seul appareil.

Je crois que c'est aussi appelé accumulation de gradient (?)

Non, c'est en fait une réduction sur plusieurs appareils. Vous pouvez voir une partie de cela dans tutoriel PyTorch . L'accumulation de gradient se produit lorsque vous exécutez votre passe avant (sur un ou plusieurs appareils) N fois et rétropropagé (le dégradé est conservé dans le graphique et les valeurs sont ajoutées à chaque passe) et l'optimiseur ne fait qu'une seule étape pour changer les poids du réseau neuronal (et efface le gradient). Dans ce cas, la perte est généralement divisée par le nombre d'étapes sans optimiseur. Ceci est utilisé pour une estimation de gradient plus fiable, généralement lorsque vous ne pouvez pas utiliser de grands lots.

La réduction sur tous les appareils ressemble à ceci:

reduction

Il s'agit d'une réduction totale de la parallélisation des données, chaque périphérique calcule les valeurs qui sont envoyées à tous les autres périphériques et y sont rétropropagées.

Quand chaque stratégie est-elle meilleure pour quel type de problème ou de réseau neuronal?

Décrit ci-dessus, le parallèle de données est presque toujours correct si vous avez suffisamment de données et que les échantillons sont volumineux (jusqu'à 8k des échantillons ou plus peuvent être faits à la fois sans très grande lutte).

Quels modes sont pris en charge par les bibliothèques modernes?

tensorflow et pytorch les deux supportent l'un ou l'autre, la plupart des bibliothèques modernes et maintenues ont ces fonctionnalités implémentées d'une manière ou d'une autre

peut-on combiner les quatre stratégies (2x2)

Oui, vous pouvez paralléliser le modèle et les données entre et au sein des machines.

synchrone vs asynchrone

asynchrone

Décrit par @ Daniel en bref, mais il convient de mentionner que les mises à jour ne sont pas totalement séparées. Cela n'aurait pas de sens, car nous formerions essentiellement N différents modèles en fonction de leurs lots.

Au lieu de cela, il existe un espace de paramètres global, où chaque réplica est censé partager les mises à jour calculées de manière asynchrone (donc passer en avant, en arrière, calculer la mise à jour avec l'optimiseur et partager cette mise à jour avec les paramètres globaux).

Cette approche a cependant un problème: il n'y a aucune garantie que lorsqu'un travailleur a calculé le passage avant, un autre travailleur a mis à jour les paramètres, donc la mise à jour est calculée par rapport à l'ancien ensemble de paramètres et cela s'appelle gradients périmés . Pour cette raison, la convergence pourrait être affectée.

Une autre approche consiste à calculer les étapes et les mises à jour N pour chaque worker et à les synchroniser par la suite, bien que cela ne soit pas utilisé aussi souvent.

Cette partie était basée sur great blogpost et vous devriez certainement la lire si vous êtes intéressé (il y a plus sur l'obsolescence et quelques solutions).

synchrone

Principalement décrit précédemment, il existe différentes approches, mais PyTorch collecte la sortie du réseau et rétropropage sur eux (torch.nn.parallel.DistributedDataParallel) [https://pytorch.org/docs/stable/nn.html#torch.nn.parallel.DistributedDataParallel]. BTW. Vous devez uniquement cela (non torch.nn.DataParallel) car il surmonte le problème GIL de Python.

À emporter

  • La parallélisation des données est presque toujours utilisée pour accélérer car il vous suffit de répliquer le réseau neuronal sur chaque appareil (soit sur le réseau, soit sur une seule machine), d'exécuter une partie du lot sur chacun pendant le passage avant, de les concaténer dans un lot unique (synchronisation) sur un appareil et rétropropagation sur ledit.
  • Il existe plusieurs façons de faire la parallélisation des données, déjà introduites par @ Daniel
  • La parallélisation du modèle est effectuée lorsque le modèle est trop grand pour tenir sur une seule machine ( GPT-3 d'OpenAI serait un cas extrême) ou lorsque l'architecture est adaptée à cette tâche, mais les deux sont rarement le cas AFAIK .
  • Plus le modèle a de chemins parallèles et plus longs (points de synchronisation), mieux il peut être adapté à la parallélisation du modèle
  • Il est important de démarrer les travailleurs à des moments similaires avec des charges similaires afin de ne pas passer par des processus de synchronisation en approche synchrone ou de ne pas obtenir des gradients périmés en asynchrone (bien que dans ce dernier cas, ce ne soit pas suffisant).

Portion

Petits modèles

Comme vous recherchez de grands modèles, je ne vais pas explorer les options pour les plus petits, juste une brève mention.

Si vous souhaitez servir plusieurs utilisateurs sur le réseau, vous avez besoin d'un moyen de faire évoluer votre architecture (généralement dans le cloud comme GCP ou AWS). Vous pouvez le faire en utilisant Kubernetes et ses POD ou pré-allouer certains serveurs pour gérer les demandes, mais cette approche serait inefficace (un petit nombre d'utilisateurs et de serveurs en cours d'exécution générerait des coûts inutiles, tandis qu'un grand nombre d'utilisateurs peuvent interrompre l’infrastructure et prendre trop de temps pour traiter les résultats).

Une autre façon consiste à utiliser l'autoscaling basé sur une approche sans serveur. Les ressources seront fournies en fonction de chaque demande, de sorte qu'il a de grandes capacités de mise à l'échelle + vous ne payez pas lorsque le trafic est faible. Vous pouvez voir Azure Functions car ils sont sur le chemin pour l'améliorer pour les tâches ML/DL, ou torchlambda pour PyTorch (clause de non-responsabilité, je suis le author) pour les modèles plus petits.

Grands modèles

Comme mentionné précédemment, vous pouvez utiliser Kubernetes avec votre code personnalisé ou des outils prêts à l'emploi.

Dans le premier cas, vous pouvez diffuser le modèle de la même manière que pour l'entraînement, mais seulement forward passe. De cette façon, même des modèles géants peuvent être installés sur le réseau (encore une fois, GPT- avec des paramètres 175B), mais cela demande beaucoup de travail.

Dans le second, @ Daniel a fourni deux possibilités. D'autres méritent d'être mentionnés (lisez les documents respectifs car ceux-ci ont beaucoup de fonctionnalités):

  • KubeFlow - plusieurs frameworks, basés sur Kubernetes (donc auto-scaling, multi-nœuds), formation, service et autres, se connecte avec d'autres choses comme MLFlow ci-dessous
  • AWS SageMaker - formation et service avec Python API, pris en charge par Amazon
  • MLFlow - plusieurs frameworks, pour la gestion et la diffusion des expériences
  • BentoML - plusieurs frameworks, formation et service

Pour PyTorch, vous pouvez en savoir plus ici , tandis que tensorflow a beaucoup de fonctionnalités de service prêtes à l'emploi via Tensorflow EXtended (TFX) .

Questions du commentaire d'OP

Existe-t-il des formes de parallélisme meilleures au sein d'une machine que sur plusieurs machines?

Le meilleur pour le parallélisme serait probablement dans un ordinateur géant afin de minimiser le transfert entre les périphériques.

De plus, il existe différents backends (au moins dans PyTorch) parmi lesquels choisir (mpi, gloo, nccl) et tous ne prennent pas en charge l'envoi direct, la réception, la réduction etc. données entre appareils (certains peuvent prendre en charge CPU à CPU, d'autres GPU à GPU). S'il n'y a pas de lien direct entre les appareils, ceux-ci doivent d'abord être copiés sur un autre appareil et copiés à nouveau sur l'appareil cible (par exemple, GPU sur une autre machine -> CPU sur l'hôte -> GPU sur l'hôte). Voir pytorch info .

Plus il y a de données et plus le réseau est grand, plus il devrait être rentable de paralléliser les calculs. Si l'ensemble de données peut être ajusté sur un seul appareil, la parallélisation n'est pas nécessaire. De plus, il convient de prendre en compte des éléments tels que la vitesse de transfert Internet, la fiabilité du réseau, etc. Ces coûts peuvent l'emporter sur les avantages.

En général, optez pour la parallélisation des données si vous avez beaucoup de données (disons ImageNet avec 1.000.000 images) ou de gros échantillons (disons des images 2000x2000). Si possible, au sein d'une seule machine afin de minimiser le transfert entre les machines. Ne distribuez le modèle que s'il n'y a aucun moyen de le contourner (par exemple, il ne convient pas au GPU). Ne faites pas autrement (il n'y a pas grand-chose à faire de parallélisme lors de l'entraînement de MNIST car l'ensemble de données s'intégrera facilement dans RAM et la lecture en sera la plus rapide).

pourquoi se donner la peine de construire du matériel spécifique au ML personnalisé tel que des TPU?

Les processeurs ne sont pas les mieux adaptés aux calculs hautement parallèles (par exemple, la multiplication des matrices) + le processeur peut être occupé par de nombreuses autres tâches (comme le chargement de données), il est donc logique d'utiliser le GPU.

Comme le GPU a été créé avec les graphiques à l'esprit (donc la transformation algébrique), il peut prendre certaines tâches du processeur et peut être spécialisé (beaucoup plus de cœurs par rapport au processeur mais plus simples, voir V1 par exemple).

Désormais, les TPU sont spécialement conçus pour les calculs tensoriels (donc l'apprentissage en profondeur principalement) et proviennent de Google, toujours WIP par rapport aux GPU. Ceux-ci sont adaptés à certains types de modèles (principalement les réseaux de neurones convolutifs) et peuvent apporter des accélérations dans ce cas. De plus, il faut utiliser les plus gros lots avec cet appareil (voir ici ), mieux vaut être divisible par 128. Vous pouvez comparer cela à la technologie Tensor Cores (GPU) de NVidia où vous êtes d'accord avec les lots (ou tailles de couche) divisibles par 16 ou 8 (float16 précision et int8 respectivement) pour une bonne utilisation (bien que plus il y en a, mieux c'est et dépend du nombre de cœurs, de la carte graphique exacte et de bien d'autres choses, voir quelques lignes directrices ici ).

D'un autre côté, le support des TPU n'est toujours pas le meilleur, bien que deux frameworks majeurs le prennent en charge (tensorflow officiellement, tandis que PyTorch avec torch_xla package).

En général, le GPU est actuellement un bon choix par défaut dans l'apprentissage en profondeur, les TPU pour les architectures lourdes à convolution, bien que cela puisse donner des maux de tête. De plus (encore une fois merci @Daniel), les TPU sont plus efficaces en énergie et devraient donc être moins chers lorsque l'on compare le coût de fonctionnement à virgule flottante unique.

2
Szymon Maszke