Théorie + pratique, vous amène à comprendre la formation distribuée

Cet article est partagé par Huawei Cloud Community « Distributed Training of Large Model LLM », auteur : Hua Shanghua_Lancer.

Avec la croissance rapide du nombre de paramètres de modèle de langage et des données de formation requises, les ressources limitées sur une seule machine ne peuvent plus répondre aux exigences d'une formation de modèle de langage à grande échelle. Un système de formation distribuée (Distributed Training) doit être conçu pour résoudre le problème des besoins massifs en ressources informatiques et en mémoire.

Dans un environnement de système de formation distribué, il est nécessaire de diviser une tâche de formation modèle en plusieurs sous-tâches et de distribuer les sous-tâches sur plusieurs appareils informatiques pour résoudre les goulots d'étranglement des ressources. Mais comment pouvons-nous utiliser un cluster comprenant des dizaines de milliers de puces d’accélération de calcul pour former un modèle de langage à grande échelle avec des centaines de milliards, voire des milliards de paramètres de modèle ? Cela implique une série de technologies telles que l'architecture de cluster, la stratégie parallèle, l'architecture de modèle, l'optimisation de la mémoire et l'optimisation informatique.

Je présenterai en détail les concepts de base des systèmes d'apprentissage automatique distribués, de l'architecture de cluster de formation distribuée, des stratégies parallèles de formation distribuée et j'utiliserai DeepSpeed ​​​​comme exemple pour présenter comment entraîner de grands modèles de langage sur un cluster.

1. Aperçu de la formation distribuée

La formation distribuée fait référence à la décomposition des tâches de formation de modèles d'apprentissage automatique ou d'apprentissage profond en plusieurs sous-tâches et à leur formation en parallèle sur plusieurs appareils informatiques. La figure 1 donne un exemple d'un seul dispositif informatique et de plusieurs dispositifs informatiques. Le dispositif informatique peut ici être une unité centrale de traitement (CPU), une unité de traitement graphique (GPU) ou une unité de traitement tensoriel (Tensor Processing Unit (unité de traitement tenseur). TPU) peut également être un processeur de réseau neuronal (Neural network Processing Unit, NPU).

Étant donné que la mémoire ne peut pas être partagée entre plusieurs appareils informatiques au sein du même serveur, que ces appareils informatiques se trouvent sur un seul serveur ou sur plusieurs serveurs, leur architecture système relève de la catégorie des systèmes distribués. Une tâche de formation de modèle comporte souvent un grand nombre d'échantillons de formation en entrée, qui peuvent être effectués à l'aide d'un seul appareil informatique, ou l'ensemble de la tâche de formation de modèle peut être divisé en sous-tâches et distribuée à différents appareils informatiques pour réaliser un calcul parallèle.

Après cela, les résultats de chaque appareil informatique doivent être combinés pour finalement obtenir un résultat de calcul équivalent à celui d'un seul appareil informatique. Étant donné que chaque appareil informatique n'a besoin d'être responsable que de sous-tâches et que plusieurs appareils informatiques peuvent s'exécuter en parallèle, il peut effectuer le calcul global plus rapidement et, en fin de compte, accélérer l'ensemble du processus informatique.

Figure 1 Exemples d'appareil informatique unique et de plusieurs appareils informatiques

L’une des raisons les plus importantes qui incitent les gens à concevoir des systèmes de formation distribués est que la puissance de calcul d’un seul appareil informatique n’est plus suffisante pour prendre en charge la formation sur modèle. La figure 2 montre les besoins en puissance de calcul du modèle d'apprentissage automatique et la puissance de calcul qu'un seul appareil informatique peut fournir pendant la même période. Comme le montre la figure, les modèles d'apprentissage automatique se développent rapidement, depuis AlexNet en 2013 jusqu'au modèle Palm avec 540 milliards de paramètres en 2022, les modèles d'apprentissage automatique se développent à un rythme de 56 fois tous les 18 mois. À mesure que l'échelle des paramètres du modèle augmente, les exigences en matière de quantité de données d'entraînement augmentent également de façon exponentielle, ce qui intensifie la demande de puissance de calcul.

Cependant, ces dernières années, l'augmentation de la puissance de calcul du processeur a été bien inférieure à la loi de Moore. Bien que les dispositifs d'accélération informatique (tels que les GPU, TPU, etc.) fournissent une grande quantité de puissance de calcul pour les modèles d'apprentissage automatique, son taux de croissance reste élevé. n'a pas dépassé la loi de Moore du doublement tous les 18 mois. Afin de pouvoir répondre au développement des modèles d'apprentissage automatique, seul un système de formation distribué peut répondre aux besoins croissants en puissance de calcul du modèle.

Figure 2 Comparaison entre la croissance des paramètres du modèle d'apprentissage automatique et la croissance de la puissance de calcul du matériel informatique

L'objectif global de la formation distribuée est d'augmenter la vitesse globale de formation et de réduire la durée globale de la formation du modèle. La vitesse totale d'entraînement peut être brièvement estimée à l'aide de la formule suivante :

Vitesse totale de formation ∝ Vitesse de calcul d'un seul appareil × Nombre total d'appareils informatiques × Taux d'accélération multi-appareils

Parmi eux, la vitesse de calcul d'un seul appareil est principalement déterminée par la vitesse de calcul et les capacités d'E/S de données d'une seule puce d'accélération informatique. Pour optimiser l'efficacité de la formation d'un seul appareil, les principaux moyens techniques incluent une formation de précision mixte et un opérateur. fusion, accumulation de gradient, etc. ; plus le nombre d'appareils informatiques dans un système de formation distribué est élevé, plus sa vitesse de calcul théorique maximale sera élevée. Cependant, affectée par l'efficacité de la communication, une augmentation du nombre d'appareils informatiques entraînera un ralentissement rapide. diminution du taux d'accélération ; le taux d'accélération multi-périphériques est déterminé par le calcul et l'efficacité de la communication est déterminée en combinant des algorithmes et une topologie de réseau pour l'optimisation. L'objectif principal de la stratégie parallèle de formation distribuée est d'améliorer le taux d'accélération multi-périphériques. le système de formation distribué.

La quantité de paramètres de modèle de langage volumineux et la quantité de données utilisées sont très énormes, c'est pourquoi une architecture de formation distribuée est utilisée pour compléter la formation. Le document [5] présente uniquement le processus de formation de GPT-3 à l'aide des GPU NVIDIA V100. Le document [31] introduit que l'OPT utilise 992 GPU NVIDIA A100 80G et adopte le parallèle de données entièrement partagé [129] et le parallélisme tenseur Megatron-LM [130]. , la durée globale de la formation est de près de 2 mois.

Les chercheurs du modèle BLOOM[33] ont divulgué plus de détails sur l'architecture matérielle et système utilisée. La formation du modèle a duré au total 3,5 mois et a utilisé 48 nœuds de calcul. Chaque nœud contient 8 GPU NVIDIA A100 80G (384 GPU au total) et utilise 4*NVLink pour la communication entre les GPU au sein du nœud. Les nœuds communiquent entre eux à l’aide d’un réseau topologique global hypercube à 8 dimensions amélioré construit avec quatre cartes réseau Omni-Path 100 Gbit/s.

La littérature [37] ne donne pas la configuration spécifique et la topologie du réseau du cluster utilisé dans la formation du modèle LLaMA, mais donne le nombre total d'heures GPU pour différentes échelles de paramètres. La formation du modèle LLaMA utilise un GPU A100-80GB, la formation du modèle LLaMA-7B nécessite 82432 heures GPU, la formation du modèle LLaMA-13B nécessite 135168 heures GPU, la formation du modèle LLaMA-33B prend 530432 heures GPU et la formation du modèle LLaMA-65B coûte jusqu'à 1022362 GPU. Heure. Étant donné que la quantité de données de formation utilisée par LLaMA dépasse de loin celle des modèles OPT et BLOOM, bien que le nombre de paramètres du modèle soit bien inférieur à celui des deux modèles ci-dessus, la quantité de calcul requise reste très stupéfiante.

En utilisant un système de formation distribué, le cycle de formation de grands modèles de langage peut être raccourci de plusieurs décennies sur un seul appareil informatique à des dizaines de jours sur des milliers d'appareils informatiques. Cependant, les systèmes de formation distribués doivent encore surmonter divers défis tels que les murs informatiques, les murs de mémoire vidéo et les murs de communication pour garantir que toutes les ressources du cluster sont pleinement utilisées, accélérant ainsi le processus de formation et raccourcissant le cycle de formation.

• Mur informatique : il existe un énorme écart entre la puissance de calcul qu'un seul appareil informatique peut fournir et la quantité totale de calcul requise pour un grand modèle de langage. Le NVIDIA H100 SXM sorti en mars 2022 dispose d'une seule carte FP16 avec une puissance de calcul de seulement 2 000 TFLOP, tandis que GPT-3
nécessite une puissance de calcul totale de 314 ZFLOP. La différence entre les deux est de 8 ordres de grandeur.

• Mur de mémoire vidéo : un seul appareil informatique ne peut pas stocker complètement les paramètres d'un grand modèle de langage. GPT-3 contient 175 milliards de paramètres. S'il est stocké au format FP16, il nécessite 700 Go d'espace mémoire sur l'appareil informatique, et le GPU NVIDIA H100 ne dispose que de 80 Go de mémoire vidéo.

• Mur de communication : une transmission et une synchronisation fréquentes des paramètres sont nécessaires entre les appareils informatiques dans un système de formation distribué. En raison de la latence de communication et des limitations de bande passante, cela peut devenir un goulot d'étranglement dans le processus de formation. Au cours du processus de formation GPT-3, s'il existe 128 copies de modèle dans le système distribué, au moins 89,6 To de données de gradient doivent être transmises à chaque itération. Depuis août 2023, une seule liaison InfiniBand ne peut fournir qu’une bande passante maximale de 800 Gbit/s. Le mur informatique et le mur de mémoire vidéo résultent du conflit entre les capacités limitées de calcul et de stockage d'un seul appareil informatique et les énormes exigences de calcul et de stockage du modèle. Ce problème peut être résolu en utilisant une méthode de formation distribuée, mais la formation distribuée sera confrontée au défi des murs de communication. Lors de la formation de plusieurs machines et cartes, ces problèmes sont progressivement apparus. À mesure que les paramètres des grands modèles augmentent, la taille des clusters correspondants augmente également, et ces problèmes deviennent plus importants. Dans le même temps, lorsque de grands clusters sont formés pendant une longue période, une panne d'équipement peut affecter ou interrompre le processus de formation, ce qui impose également des exigences élevées au système distribué.

2. Stratégie parallèle de formation distribuée

L'objectif du système de formation distribué est de convertir la formation de modèle à nœud unique en formation de modèle parallèle distribué équivalente. Pour les grands modèles de langage, le processus de formation est le processus de mise à jour des paramètres du modèle de réseau neuronal à l'aide d'algorithmes d'optimisation basés sur des fonctions de données et de perte. La structure du système de formation de modèle à nœud unique est illustrée à la figure 3, qui se compose principalement de deux parties : les données et le modèle. Le processus de formation sera complété par plusieurs mini-lots de données (Mini-batch).

Les données de la figure représentent un petit lot de données. Le système de formation utilise de petits lots de données pour générer des gradients basés sur des fonctions de perte et des algorithmes d'optimisation pour corriger les paramètres du modèle. Le processus d'exécution d'un réseau neuronal multicouche pour un grand modèle de langage peut être représenté par un graphe informatique (Computational Graph). Ce graphique comporte plusieurs opérateurs interconnectés (Operators), chaque opérateur implémente une couche de réseau neuronal (Neural Network Layer) et les paramètres représentent les poids mis à jour par cette couche lors de l'entraînement.

Figure 3 Système de formation modèle à appareil unique

Le processus d'exécution du graphe de calcul peut être divisé en deux étapes : le calcul direct et le calcul inverse. Le processus de calcul direct consiste à lire les données dans le premier opérateur, à calculer la structure de sortie correspondante, puis à répéter le processus de calcul direct jusqu'à la fin du dernier opérateur. Le processus de calcul inverse est basé sur la fonction d'optimisation et la perte. Chaque opérateur calcule le gradient à son tour et utilise le gradient pour mettre à jour les paramètres locaux. Une fois le calcul inverse terminé et le calcul du mini-lot de données terminé, le système lira le mini-lot de données suivant et poursuivra la prochaine série de mises à jour des paramètres du modèle.

Selon le processus du système de formation de modèles à dispositif unique, nous pouvons voir que si une accélération parallèle est effectuée, elle peut être considérée sous deux dimensions : les données et le modèle. Premièrement, les données peuvent être partitionnées (Partition), le même modèle peut être copié sur plusieurs appareils et différentes partitions de données peuvent être exécutées en parallèle. Cette méthode est généralement appelée parallélisme des données (DP). Le modèle peut également être divisé et les opérateurs du modèle peuvent être distribués sur plusieurs appareils pour être complétés respectivement. Cette méthode est généralement appelée parallélisme de modèle (MP). Lors de la formation de modèles de langage à très grande échelle, il est souvent nécessaire de diviser les données et le modèle en même temps pour obtenir un degré de parallélisme plus élevé. Cette méthode est souvent appelée parallélisme hybride (HP).

2.1. Parallélisme des données

Dans un système parallèle de données, chaque appareil informatique dispose d'une copie complète de l'intégralité du modèle de réseau neuronal (réplique de modèle). Lors de l'itération, chaque appareil informatique se voit attribuer uniquement un sous-ensemble d'un lot d'échantillons de données, et en fonction du lot d'échantillons. un sous-ensemble de données est utilisé pour le calcul avancé du modèle de réseau. Supposons que le nombre d'échantillons d'apprentissage dans un lot est N et que M appareils informatiques sont utilisés pour le calcul parallèle, et chaque appareil informatique se verra attribuer N/M échantillons. Une fois le calcul direct terminé, chaque dispositif informatique calculera l'erreur de perte sur la base de l'échantillon local pour obtenir le gradient Gi (i est le numéro de la carte accélératrice) et diffusera le gradient local Gi. Tous les appareils informatiques doivent agréger les valeurs de gradient données par d'autres cartes d'accélération, puis utiliser le gradient moyen (ΣNi=1Gi)/N pour mettre à jour le modèle afin de terminer la formation par lots. La figure 4 montre un exemple de système de formation parallèle de données composé de deux dispositifs informatiques.

Figure 4 Exemple de système de formation parallèle de données à deux nœuds

Le système de formation parallèle de données peut améliorer efficacement le débit global de formation et la taille globale du lot par seconde (Global Batch Size Per Second) en ajoutant un équipement informatique. Par rapport à la formation sur un seul appareil informatique, la principale différence est que les gradients du calcul inverse doivent être synchronisés sur tous les appareils informatiques pour garantir que le résultat final sur chaque appareil informatique est la moyenne des gradients de tous les processus.

Les frameworks de réseaux neuronaux courants ont des implémentations spécifiques du parallélisme des données, notamment : TensorFlow DistributedStrategy, PyTorch Distributed, Horovod DistributedOptimizer, etc. Étant donné que chaque opérateur dans un grand modèle de langage basé sur l'architecture Transformer s'appuie sur des données uniques plutôt que sur des données par lots, le parallélisme des données n'affectera pas sa logique de calcul. En général, le calcul direct dans chaque appareil de formation est indépendant et n'affecte pas la logique de calcul. . Implique des problèmes de synchronisation. La formation parallèle de données présente le taux d'accélération le plus élevé, mais nécessite la sauvegarde d'une copie du modèle sur chaque appareil et consomme une quantité relativement élevée de mémoire vidéo.

Le code permettant d'utiliser PyTorch DistributedDataParallel pour implémenter plusieurs formations de cartes accélératrices sur un seul serveur est le suivant. Tout d'abord, construisez la classe DistributedSampler pour perturber de manière aléatoire les échantillons de l'ensemble de données et les distribuer à différents appareils informatiques :

classe DistributedSampler(Sampler):
  def __init__(self, dataset, num_replicas=None, Rank=None, shuffle=True, seed=0) :
    si num_replicas vaut Aucun :
        sinon dist.is_available() :
            raise RuntimeError("Nécessite que le package distribué soit disponible")
        num_replicas = dist.get_world_size()
    si le rang est Aucun :
        sinon dist.is_available() :
            raise RuntimeError("Nécessite que le package distribué soit disponible")
        rang = dist.get_rank()
    self.dataset = ensemble de données #dataset
    self.num_replicas = num_replicas #Le nombre de processus est par défaut world_size (nombre de GPU)
    self.rank = rang # À quel processus/GPU appartient actuellement
    soi.époque = 0
    self.num_samples = int(math.ceil(len(self.dataset) * 1.0 / self.num_replicas))
    #Nombre d'échantillons par processus
    self.total_size = self.num_samples * self.num_replicas #Le nombre total d'échantillons dans l'ensemble de données
    self.shuffle = shuffle # S'il faut mélanger l'ensemble de données
    self.seed = graine

def __iter__(soi) :
# 1. Traitement aléatoire : perturber l'ordre de l'ensemble de données
    si self.shuffle :
        # Obscurcir en fonction de l'époque et de la graine
        g = torche.Générateur()
        # Ici, self.seed est une valeur fixe. Changer self.epoch par set_epoch peut changer notre graine d'initialisation.
        # Cela permet à l'ordre de brassage des ensembles de données à chaque époque d'être différent, de sorte qu'à chaque époque,
        # Chaque GPU obtient des données différentes, ce qui peut faciliter une meilleure formation.
        g.manual_seed(self.seed + self.epoch)
        indices = torch.randperm(len(self.dataset), générateur=g).tolist()
    autre:
        indices = liste (range (len (self.dataset)))
    # Supplément de données
    indices += indices[:(self.total_size - len(indices))]
    assert len(indices) == self.total_size
    # allouer des données
    indices = indices[self.rank:self.total_size:self.num_replicas]
    assert len(indices) == self.num_samples
    retourner iter (indices)
def __len__(soi) :
    retourner self.num_samples
def set_epoch(soi, époque) :

    self.epoch = époque

Utilisez DistributedSampler pour créer un exemple de programme de formation complet main.py comme suit :

importer l'argparse
importez-nous
importer des produits
heure d'importation
avertissements d'importation
importer numpy en tant que np
avertissements.filterwarnings('ignorer')
importer une torche
importer torch.nn en tant que nn
importer torch.nn.parallel
importer torch.backends.cudnn en tant que cudnn
importer torch.distributed en tant que dist
importer torch.optim
importer torch.utils.data
importer torch.utils.data.distributed
à partir de torch.utils.data.distributed import DistributedSampler
à partir de modèles importer DeepLab
à partir de l'importation d'un ensemble de données Cityscaples
analyseur = argparse.ArgumentParser(description='DeepLab')
parser.add_argument('-j', '--workers', par défaut=4, type=int, métavar='N',
help='nombre de travailleurs chargés du chargement des données (par défaut : 4)')
parser.add_argument('--epochs', par défaut=100, type=int, metavar='N',
help='nombre total d'époques à exécuter')
parser.add_argument('--start-epoch', default=0, type=int, metavar='N',
help='numéro d'époque manuel (utile lors des redémarrages)')
parser.add_argument('-b', '--batch-size', par défaut=3, type=int,
métavar='N')
parser.add_argument('--local_rank', default=0, type=int, help='node Rank pour la formation distribuée')
args = parser.parse_args()
torch.distributed.init_process_group(backend="nccl") #Initialisation
print("Utiliser GPU : {} pour l'entraînement".format(args.local_rank))
# créer un modèle
modèle = DeepLab()
torch.cuda.set_device(args.local_rank) #Carte graphique actuelle
model = model.cuda() # Le modèle est placé sur la carte graphique
model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.local_rank],
    output_device=args.local_rank, find_unused_parameters=True) # Parallélisme des données
critère = nn.CrossEntropyLoss().cuda()
optimiseur = torch.optim.SGD(model.parameters(), args.lr,
    momentum=args.momentum, poids_decay=args.weight_decay)
train_dataset = Citycaples()
train_sampler = DistributedSampler(train_dataset) # Distribuer les données
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=args.batch_size,
    shuffle=False, num_workers=args.workers, pin_memory=True, sampler=train_sampler)

Démarrez le programme ci-dessus via la ligne de commande suivante :

CUDA_VISIBLE_DEVICES=0,1 python -m torch.distributed.launch --nproc_per_node=2 main.py

2.2 Parallélisme des modèles

Le parallélisme modèle est souvent utilisé pour résoudre le problème de mémoire insuffisante sur un seul nœud. Prenons comme exemple le modèle GPT-3 contenant 175 milliards de paramètres. Si chaque paramètre du modèle est représenté par un nombre à virgule flottante de 32 bits, le modèle doit occuper 700 Go (soit 175 Go × 4 octets) de mémoire. représenté par un nombre à virgule flottante de 16 bits, chaque copie de modèle nécessite 350 Go de mémoire. La carte accélératrice H100 lancée par NVIDIA en mars 2022 ne prend en charge que 80 Go de mémoire vidéo et l'ensemble du modèle ne peut pas y être complètement placé. Le parallélisme des modèles peut être divisé sous les deux formes suivantes du point de vue des graphes informatiques :

(1) Divisé en différents dispositifs selon les couches du modèle, c'est-à-dire le parallélisme inter-couches ou le parallélisme inter-opérateurs (Parallélisme inter-opérateurs), également appelé parallélisme pipeline (Parallélisme pipeline, PP) ;

(2) Divisez les paramètres de la couche de calcul en différents dispositifs, c'est-à-dire le parallélisme intra-couche ou le parallélisme intra-opérateur (parallélisme intra-opérateur), également appelé parallélisme tensoriel (parallélisme tenseur, TP).

Un exemple d'un système de formation parallèle de modèle à deux nœuds est présenté dans la figure 4.9. Le côté gauche est le parallélisme du pipeline, et les différentes couches du modèle sont divisées en différents dispositifs, le côté droit est le parallélisme tensoriel et différents paramètres dans la même couche. sont répartis en différents calculs dans l'appareil.

Parallélisme des pipelines

Le parallélisme de pipeline (PP) est une stratégie de calcul parallèle qui traite chaque couche du modèle en segments et distribue chaque segment sur différents appareils informatiques, afin que les étapes précédentes et suivantes puissent fonctionner dans un pipeline et par lots. Le parallélisme de pipeline est généralement appliqué dans des systèmes parallèles de modèles à grande échelle pour résoudre efficacement le problème de mémoire insuffisante sur un seul appareil informatique. La figure 4.6 montre un système parallèle de pipeline composé de quatre dispositifs informatiques, dont le calcul en avant et le calcul en arrière. Parmi eux, F1, F2, F3 et F4 représentent respectivement quatre chemins aller, qui sont situés sur des appareils différents, tandis que B4, B3, B2 et B1 représentent les chemins inverses, qui sont également situés sur quatre appareils différents ; Cependant, comme le montre la figure, le périphérique en aval (Périphérique en aval) dans le graphique de calcul doit rester inactif pendant une longue période, en attendant que le périphérique en amont (Périphérique en amont) termine ses calculs avant de pouvoir commencer à calculer le sien. Tâches.

Figure 5 Exemple de système de formation parallèle modèle à deux nœuds

Cette situation a entraîné une réduction significative de l’utilisation moyenne de l’appareil, formant une bulle de parallélisme modèle, également connue sous le nom de bulle de pipeline.

Figure 6 Exemple de pipeline parallèle

Les bulles parallèles générées par la stratégie de pipeline naïve empêchent le système d'utiliser pleinement les ressources informatiques et réduisent l'efficacité informatique globale du système. Afin de réduire les bulles parallèles, la littérature [131] a proposé la méthode GPipe, qui divise davantage le mini-lot en micro-lots plus petits et utilise le schéma parallèle de pipeline pour traiter un micro-lot à la fois.

Une fois le calcul de l'étape en cours terminé et les résultats obtenus, les résultats du micro-lot sont envoyés au périphérique en aval et les données du micro-lot suivant commencent à être traitées en même temps, ce qui peut réduire bulles parallèles dans une certaine mesure. Figure 7 Exemple parallèle de pipeline de stratégie GPipe. Comme le montre la figure, le calcul direct de F1 est décomposé en F11, F12, F13 et F14. Une fois le calcul de F11 terminé dans le dispositif informatique 1, le calcul de F21 sera lancé dans le dispositif informatique 2 et au niveau du dispositif informatique 2. en même temps, F12 sera démarré en parallèle dans le dispositif informatique 1. calcul. Par rapport à la méthode parallèle de pipeline originale, la méthode de pipeline GPipe peut réduire efficacement les bulles parallèles.

Figure 7 Exemple parallèle de pipeline de stratégie GPipe

Bien que la stratégie GPipe puisse réduire certaines bulles parallèles, le calcul en arrière ne peut démarrer qu'une fois tous les calculs en avant d'un mini-batch terminés. Par conséquent, de nombreuses bulles parallèles seront toujours générées, réduisant ainsi l’efficacité parallèle du système. Megatron-LM[132] a proposé une stratégie de pipeline 1F1B, qui consiste en un canal aller et un canal retour. La stratégie de pipeline 1F1B introduit un mécanisme de planification des tâches, permettant aux appareils en aval d'exécuter d'autres tâches parallèles en attendant les calculs en amont, améliorant ainsi l'utilisation des appareils. 1F1B propose deux méthodes de planification, non entrelacée et entrelacée, comme le montre la figure 8.

Le mode de planification non entrelacé 1F1B peut être divisé en trois étapes. La première est une phase de préchauffage au cours de laquelle un nombre variable de calculs directs sont effectués dans le dispositif informatique. La phase suivante est la phase avant-arrière, au cours de laquelle le dispositif informatique effectue séquentiellement un calcul en avant, puis un calcul en arrière. La dernière phase est la phase inverse, au cours de laquelle le dispositif informatique effectue le dernier calcul inverse. Par rapport à la stratégie GPipe, le mode de planification non entrelacé est plus performant en termes d'économie de mémoire. Cependant, cela nécessite le même temps que la stratégie GPipe pour effectuer une série de calculs.

Le mode de planification entrelacé 1F1B nécessite que le nombre de micro-lots soit un multiple entier des étapes du pipeline. Chaque appareil n'est plus seul responsable du calcul de plusieurs couches consécutives, mais peut traiter des sous-ensembles de plusieurs couches, appelés nuggets de modèle. Plus précisément, dans le modèle précédent, le périphérique 1 pouvait être responsable des couches 1 à 4, le périphérique 2 des couches 5 à 8, etc. Cependant, dans le nouveau mode, le périphérique 1 peut gérer les couches 1, 2, 9, 10, le périphérique 2 peut gérer les couches 3, 4, 11, 12, etc. Dans ce mode, chaque appareil est affecté à plusieurs étapes du pipeline. Par exemple, le dispositif 1 peut être impliqué dans certains sous-ensembles de tâches dans la phase d'échauffement, la phase de calcul avant et la phase de calcul arrière. Chaque appareil peut effectuer des tâches informatiques à différentes étapes en parallèle, tirant ainsi mieux parti du parallélisme des pipelines. Ce mode est non seulement performant en termes de consommation de mémoire, mais améliore également l'efficacité des calculs, permettant aux systèmes parallèles pour les grands modèles d'accomplir les tâches informatiques plus efficacement.


Figure 8 : Exemple de stratégie parallèle de pipeline 1F1B  

PyTorch inclut également la fonction API Pipe pour implémenter le pipeline. Pour une implémentation spécifique, veuillez vous référer à la classe « torch.distributed.pipeline.sync.Pipe ». Vous pouvez utiliser cette API pour construire un échantillon contenant deux couches linéaires, placées dans deux appareils informatiques différents, comme suit :

{#
Étape 0. Vous devez d’abord initialiser le framework RPC.
os.environ['MASTER_ADDR'] = 'localhost'
os.environ['MASTER_PORT'] = '29500'
torch.distributed.rpc.init_rpc('worker', rang=0, world_size=1)
# Étape 1 : construire un modèle comprenant deux couches linéaires
fc1 = nn.Linear(16, 8).cuda(0)
fc2 = nn.Linear(8, 4).cuda(1)
# Étape 2 : envelopper les deux calques avec nn.Sequential
modèle = nn.Séquentiel(fc1, fc2)
# Étape 3 : construire Pipe (torch.distributed.pipeline.sync.Pipe)
modèle = Tuyau (modèle, morceaux = 8)
# faire de la formation/inférence
entrée = torch.rand(16, 16).cuda(0)
sortie_rref = modèle (entrée)
}

parallélisme tensoriel

Le parallélisme tensoriel (TP) doit résoudre deux problèmes : comment diviser les paramètres en différents appareils en fonction de la structure spécifique et du type d'opérateur du modèle, et comment assurer la cohérence mathématique après la division. Les grands modèles de langage sont basés sur la structure Transformer. La structure Transformer est principalement composée des trois opérateurs suivants : représentation embarquée (Embedding), multiplication matricielle (MatMul) et calcul de perte d'entropie croisée (Cross Entropy Loss).

Ces trois types d'opérateurs sont assez différents, et les stratégies tensorielles parallèles correspondantes [130] doivent être conçues pour diviser les paramètres en différents dispositifs. Pour l'opérateur Embedding, si le nombre total de vocabulaires est très important, la mémoire vidéo d'un seul appareil informatique ne pourra pas accueillir les paramètres de la couche Embedding. Par exemple, si le nombre de vocabulaires est de 64 000, la dimension de représentation d'intégration est de 5 120 et le type utilise des nombres à virgule flottante de précision de 32 bits, alors la mémoire vidéo requise pour l'ensemble de la couche de paramètres est d'environ 64 000 × 5 120 × 4/1 024. /1024 = 1250 Mo, et le gradient inverse est le même. Nécessite 1250 Mo, soit près de 2,5 Go pour le stockage seul.

Les paramètres intégrés dans la couche de présentation peuvent être divisés en fonction de la dimension du mot. Chaque dispositif informatique ne stocke qu'une partie du vecteur de mot, puis le vecteur de mot complet est obtenu en résumant les vecteurs de mots partiels sur chaque appareil. La figure 4.9 montre un diagramme schématique de l'intégration à un seul nœud et du parallélisme tensoriel à deux nœuds.

Sur un seul nœud, effectuez l'opération d'intégration, bz est la taille du lot (taille du lot), la taille du paramètre d'intégration est [word_size, Hidden_size] et le tenseur [bz, Hidden_size] est calculé. L'exemple parallèle du tenseur d'intégration de la figure 4.9 divise les paramètres d'intégration en deux blocs le long de la dimension word_size. Chaque bloc a une taille de [word_size/2, Hidden_size] et est stocké respectivement sur deux appareils. Lorsque chaque nœud interroge sa propre liste de mots, s'il est introuvable, la représentation du mot est 0. Après avoir interrogé l'appareil respectif, le tenseur de résultat [bz, Hidden_size] est obtenu Enfin, via la communication AllReduce_Sum ¬, en additionnant tous les appareils. , nous obtenons À partir des résultats complets, on peut voir que les résultats de sortie ici sont cohérents avec les résultats exécutés par un seul appareil informatique.

Figure 9 Exemple de tenseur parallèle d'opérateur d'intégration à deux nœuds

Le parallélisme tensoriel de la multiplication matricielle (MatMul) devrait exploiter pleinement le principe de multiplication par blocs matriciels. Par exemple, pour implémenter la multiplication matricielle suivante Y = X ×A, où X est la matrice d'entrée de dimension M × N, A est la matrice de paramètres de dimension N ×K et Y est la matrice de résultat de dimension M ×K. Si la matrice de paramètres A est très grande, ou dépasse même la capacité de mémoire vidéo d'une seule carte, alors la matrice de paramètres A peut être divisée en plusieurs cartes et les résultats peuvent être rassemblés par communication collective pour garantir que le résultat final est mathématiquement équivalent à un seul appareil informatique Résultats du calcul. Il existe deux manières de segmenter la matrice de paramètres A :

(1) La matrice des paramètres A est découpée en colonnes, et la matrice A est découpée en colonnes : A = [A1,A2]

(2) La matrice de paramètres A est découpée en lignes, et la matrice A est découpée en lignes :

La figure 10 montre un exemple de division de la matrice de paramètres par colonnes. La matrice de paramètres A place respectivement A1 et A2 sur deux appareils informatiques. Deux appareils informatiques calculent respectivement Y1 = X ×A1 et Y2 = X ×A2. Une fois le calcul terminé, plusieurs appareils informatiques communiquent entre eux pour obtenir les résultats de calcul sur d'autres appareils informatiques et les assemblent pour obtenir la matrice de résultat final Y. Ce résultat est mathématiquement équivalent au résultat de calcul d'un seul appareil informatique.

Figure 10 Exemple de découpage parallèle de tenseurs d'opérateurs de multiplication matricielle à deux nœuds par colonnes

La figure 11 montre un exemple de division de la matrice de paramètres par colonnes et lignes Afin de satisfaire aux règles de multiplication matricielle, la matrice d'entrée X doit être divisée par colonnes X = [X1|X2]. Dans le même temps, la matrice est divisée en blocs et placée sur deux appareils informatiques, chaque appareil informatique calcule respectivement Y1 = X1 × A1 et Y2 = X2 × A2. Une fois le calcul terminé, plusieurs dispositifs informatiques communiquent pour obtenir et réduire les résultats de calcul sur d'autres cartes, et la matrice de résultat final Y peut être obtenue. De même, cette méthode de fractionnement peut non seulement garantir l'équivalence des calculs mathématiques, mais également résoudre le problème selon lequel un seul dispositif informatique ne peut pas accueillir la mémoire vidéo, et peut également garantir qu'un seul dispositif informatique peut prendre en charge le paramètre A via le fractionnement.

La structure FFN dans Transformer contient deux couches entièrement connectées (FC), c'est-à-dire qu'il existe deux multiplications matricielles, et ces deux multiplications matricielles adoptent les deux méthodes de segmentation ci-dessus, comme le montre la figure 4.12. La matrice de paramètres de la première couche FC est découpée en blocs par colonnes, et la matrice de paramètres de la deuxième couche FC est découpée en blocs par lignes. De cette manière, la sortie de la première couche FC répond exactement aux exigences d'entrée de données de la deuxième couche FC (divisées par colonnes), de sorte que l'opération de communication récapitulative après la première couche FC peut être omise. Le parallélisme tensoriel du mécanisme d'auto-attention multi-têtes est similaire à celui de FFN. Parce qu'il comporte plusieurs têtes indépendantes, il est plus facile d'obtenir le parallélisme que FFN. Sa méthode de segmentation matricielle est illustrée dans la figure 4.13. Pour plus de détails, veuillez vous référer à [130].

La dernière couche du réseau de classification utilise généralement les opérateurs Softmax et Cross_entropy pour calculer la perte d'entropie croisée (Cross Entropy Loss). Si le nombre de catégories est très grand, la mémoire d'un seul appareil informatique ne pourra pas stocker et calculer la matrice logit. Pour ce type d'opérateur, elle peut être divisée en fonction de la dimension de la catégorie, et en même temps, la perte d'entropie croisée globale finale peut être obtenue grâce à la communication des résultats intermédiaires.

Figure 11 Exemple de division parallèle du tenseur de l'opérateur de multiplication matricielle à deux nœuds par lignes

Figure 12 Diagramme parallèle du tenseur de structure FNN

La première chose à calculer est la valeur softmax, la formule est la suivante :

Parmi eux, p représente le numéro de périphérique du parallélisme tensoriel. Après avoir obtenu le résultat du calcul Softmax, l'étiquette Target est divisée par catégorie en même temps, et chaque appareil reçoit une partie de la perte. Enfin, une autre communication est effectuée pour obtenir la perte de toutes les catégories. L'ensemble du processus ne nécessite que trois petites quantités de communication pour terminer le calcul de la perte d'entropie croisée. PyTorch fournit une API parallèle au niveau du tenseur à granularité fine, DistributedTensor. Il fournit également une API au niveau du modèle à gros grain pour effectuer le parallélisme tensoriel sur "nn.Module". Vous pouvez partitionner un grand tenseur avec les lignes de code suivantes :

importer une torche
à partir de torch.distributed._tensor importer DTensor, DeviceMesh, Shard, distribuer_tensor
# construire un maillage de périphériques avec les périphériques disponibles (multi-hôtes ou hôte unique)
périphérique_mesh = DeviceMesh("cuda", [0, 1, 2, 3])
# si nous voulons effectuer un partitionnement par ligne
rowwise_placement=[Partage(0)]
# si nous voulons faire du partitionnement au niveau des couleurs
colwise_placement=[Partage(1)]
big_tensor = torch.randn(888, 12)
# tenseur distribué renvoyé sera partagé sur la dimension spécifiée dans les emplacements
rowwise_tensor = distribuer_tensor (big_tensor, device_mesh=device_mesh, placements=rowwise_placement)

Pour les modules comme "nn.Linear" qui ont déjà "torch.Tensor" comme paramètre, l'API au niveau du module "distribute_module" est également fournie pour effectuer le parallélisme tensoriel au niveau du modèle. Le code de référence est le suivant :

importer une torche
à partir de torch.distributed._tensor importer DeviceMesh, Shard, distribuer_tensor,distribute_module
classe MonModule(nn.Module) :
    def __init__(soi) :
        super().__init__()
        self.fc1 = nn.Linear(8, 8)
        self.fc2 = nn.Linear(8, 8)
        self.relu = nn.ReLU()
        def forward (soi, entrée):
            retourner self.relu(self.fc1(input) + self.fc2(input))
    mesh = DeviceMesh(device_type="cuda", mesh=[[0, 1], [2, 3]])
    def shard_params (nom_mod, mod, mesh) :
        rowwise_placement = [Partage (0)]
        def to_dist_tensor(t) : return distribuer_tensor(t, mesh, rowwise_placement)
        mod._apply(to_dist_tensor)
    sharded_module = distribuer_module(MonModule(), mesh, partition_fn=shard_params)
    def shard_fc (nom_mod, mod, maille) :
        rowwise_placement = [Partage (0)]
        si nom_mod == "fc1":
            mod.weight = torch.nn.Parameter(distribute_tensor(mod.weight, mesh, rowwise_placement))
    sharded_module = distribuer_module (MonModule(), maillage, partition_fn=shard_fc)

2.3 Parallélisme hybride

Le parallélisme hybride (HP) est un mélange de plusieurs stratégies parallèles telles que le parallélisme des données, le parallélisme des pipelines et le parallélisme des tenseurs. En combinant différentes stratégies parallèles, le parallélisme hybride peut tirer pleinement parti de diverses stratégies parallèles pour maximiser les performances et l'efficacité du calcul.

Pour les grands modèles de langage à l'échelle de centaines de milliards, une stratégie tensorielle parallèle est généralement utilisée au sein de chaque serveur. Étant donné que cette stratégie implique une grande quantité de communication réseau, il est nécessaire d'utiliser une bande passante de communication à haut débit entre différents appareils informatiques au sein du serveur. serveur. Grâce au parallélisme des pipelines, différentes couches du modèle sont divisées en plusieurs étapes, et chaque étape est calculée par une machine différente. De cette manière, la puissance de calcul de plusieurs machines peut être pleinement utilisée, et les résultats de calcul et les données intermédiaires peuvent être transférés via une communication à haut débit entre les machines pour améliorer la vitesse et l'efficacité globales du calcul.

Enfin, la stratégie de parallèle de données est superposée à la couche externe pour augmenter le nombre de simultanéités et améliorer la vitesse globale de formation. Grâce au parallélisme des données, les données de formation sont distribuées à plusieurs groupes de serveurs pour un traitement parallèle, et chaque groupe de serveurs traite différents lots de données. Cela peut utiliser pleinement les ressources informatiques de plusieurs serveurs et augmenter la simultanéité de la formation, accélérant ainsi la vitesse globale de formation.

BLOOM utilise le framework Megatron-DeepSpeed[104] pour la formation, qui se compose principalement de deux parties : Megatron-LM fournit des capacités tensorielles parallèles et des primitives de chargement de données ; DeepSpeed ​​​​fournit l'optimiseur ZeRO, le pipeline de modèles et les composants de formation distribués conventionnels. De cette manière, un parallélisme tridimensionnel des données, des tenseurs et des pipelines peut être obtenu. La structure de calcul parallèle utilisée dans la formation du modèle BLOOM est illustrée à la figure 14.

La formation du modèle BLOOM utilise un cluster de 48 serveurs NVIDIA DGX-A100. Chaque serveur DGX-A100 contient 8 GPU NVIDIA A100 de 80 Go, soit un total de 384. La stratégie adoptée dans la formation BLOOM consiste à diviser d'abord le cluster en groupes de 48 pour la parallélisation des données.

Ensuite, l'ensemble du modèle est divisé en 12 étapes pour la parallélisation du pipeline. Le modèle de chaque étage est divisé en 4 GPU pour le parallélisme tensoriel. Dans le même temps, BLOOM utilise également ZeRO (Zero Redundancy Optimizer) [134] pour réduire davantage l'occupation de la mémoire vidéo du modèle. Grâce aux quatre étapes ci-dessus, un calcul parallèle efficace de centaines de GPU peut être réalisé.

Figure 14 Structure de calcul parallèle utilisée dans la formation du modèle BLOOM

2.4 Optimisation de la mémoire des appareils informatiques

La formation actuelle sur les grands modèles de langage utilise généralement l'algorithme d'optimisation d'Adam, qui nécessite une impulsion de premier ordre (Momentum) et une impulsion de second ordre (Variance) en plus du gradient de chaque paramètre. Bien que l’algorithme d’optimisation Adam soit généralement meilleur et plus stable que l’algorithme SGD, il consomme beaucoup plus de mémoire sur le périphérique informatique.

Afin de réduire l'utilisation de la mémoire, la plupart des systèmes ont adopté la méthode Mixed Precision Training, c'est-à-dire qu'il existe des valeurs aux formats FP16 (virgule flottante 16 bits) ou BF16 (Bfloat16) et FP32 (virgule flottante 32 bits). . FP32, FP16 et BF16 sont représentés comme le montre la figure 4.15. Dans FP32, le bit 31 est le bit de signe, les bits 30 à 23 sont utilisés pour représenter l'exposant et les bits 22 à 0 sont utilisés pour représenter la mantisse. Dans FP16, le bit 15 est le bit de signe, les bits 14 à 10 sont utilisés pour représenter l'exposant et les bits 9 à 9 sont utilisés pour représenter la mantisse. Dans BF16, le bit 15 est le bit de signe, les bits 14 à 7 sont utilisés pour représenter l'exposant et les bits 6 à 0 sont utilisés pour représenter la mantisse. Étant donné que la plage de valeurs du FP16 est beaucoup plus petite que celle du FP32, des débordements et des dépassements inférieurs peuvent facilement se produire pendant le processus de calcul. Par rapport au FP16, le BF16 échange la précision contre une plage de valeurs plus large. Cependant, en raison de la précision moindre de FP16 et BF16 par rapport à FP32, des problèmes de disparition de gradient et d'instabilité du modèle peuvent survenir pendant le processus de formation.

Par conséquent, certaines technologies doivent être utilisées pour résoudre ces problèmes, telles que la mise à l'échelle dynamique des pertes (Dynamic Loss Scaling) et l'optimiseur de précision mixte (Mixed Precision Optimizer). Le processus d’optimisation de précision mixte est illustré à la figure 4.16. L'état de l'optimiseur Adam inclut la sauvegarde des paramètres du modèle enregistrés dans FP32, et l'élan de premier ordre et l'élan de second ordre sont également stockés au format FP32. En supposant que le nombre de paramètres du modèle est Φ et que les paramètres et gradients du modèle sont stockés au format FP16, un total de 2Φ + 2Φ + (4Φ + 4Φ + 4Φ) = 16Φ octets de stockage sont requis.

Parmi eux, le statut Adam représente 75 %. Avant la rétropropagation de la mise à l'échelle dynamique des pertes, le changement de perte (dLoss) est augmenté manuellement de 2 000 fois, de sorte que le gradient de fonction d'activation obtenu lors de la rétropropagation ne débordera pas, le gradient de poids est réduit de 2 000 fois et restauré à ses valeurs normales. Par exemple, pour un modèle contenant 7,5 milliards de paramètres, si vous utilisez le format FP16, seulement 15 Go de mémoire de l'appareil informatique sont requis, mais l'état du modèle consomme en réalité 120 Go pendant la phase de formation.

En plus de l'état du modèle, la mémoire occupée par la carte informatique présente également des états résiduels (États résiduels), notamment des valeurs d'activation (Activation), divers tampons temporaires (Buffers) et des fragments de mémoire vidéo inutilisables (Fragmentation), etc. Étant donné que la valeur d'activation peut utiliser des points de contrôle (Activation Checkpointing) pour réduire considérablement l'empreinte mémoire de la valeur d'activation, la façon de réduire l'état du modèle, en particulier l'état de l'optimiseur Adam, est la clé pour résoudre le problème de l'empreinte mémoire.

Figure 16 Processus d'optimisation de précision mixte

Ce qui précède est ma brève introduction aux concepts de base des systèmes d'apprentissage automatique distribués, de l'architecture de cluster de formation distribuée et des stratégies parallèles de formation distribuée. DeepSpeed ​​​​est un exemple de la façon de former un grand modèle de langage sur un cluster. à vous dans le prochain article. Bienvenue à l'aimer. Faites attention et soutenez-le, votre soutien est le moteur de ma création.

Contenu de référence :

(1) Collection丨Partage de 30 grands ensembles de données liés à la formation de modèles de langage - Zhihu https://zhuanlan.zhihu.com/p/612243919.

(2) Quatre méthodes de traitement courantes pour les données de formation de modèles de langage volumineux - Zhihu https://zhuanlan.zhihu.com/p/673045395.

(3) « Modèle linguistique à grande échelle : de la théorie à la pratique » Zhang Qi et al — Pékin : Electronic Industry Press.

(4) Examen des grands modèles linguistiques - Université Renmin de Chine http://ai.ruc.edu.cn/research/science/20230605100.html.

Cliquez pour suivre et découvrir les nouvelles technologies de Huawei Cloud dès que possible~

 

Un programmeur né dans les années 1990 a développé un logiciel de portage vidéo et en a réalisé plus de 7 millions en moins d'un an. La fin a été très éprouvante ! Des lycéens créent leur propre langage de programmation open source en guise de cérémonie de passage à l'âge adulte - commentaires acerbes des internautes : s'appuyant sur RustDesk en raison d'une fraude généralisée, le service domestique Taobao (taobao.com) a suspendu ses services domestiques et repris le travail d'optimisation de la version Web Java 17 est la version Java LTS la plus utilisée Part de marché de Windows 10 Atteignant 70 %, Windows 11 continue de décliner Open Source Daily | Google soutient Hongmeng pour prendre le relais des téléphones Android open source pris en charge par Docker ; Electric ferme la plate-forme ouverte Apple lance la puce M4 Google supprime le noyau universel Android (ACK) Prise en charge de l'architecture RISC-V Yunfeng a démissionné d'Alibaba et prévoit de produire des jeux indépendants pour les plates-formes Windows à l'avenir
{{o.name}}
{{m.nom}}

Je suppose que tu aimes

Origine my.oschina.net/u/4526289/blog/11104017
conseillé
Classement