Maison  >  Article  >  Périphériques technologiques  >  Comment affiner des modèles très volumineux avec des ressources GPU limitées

Comment affiner des modèles très volumineux avec des ressources GPU limitées

WBOY
WBOYavant
2023-04-13 08:10:071502parcourir

Comment affiner des modèles très volumineux avec des ressources GPU limitées

Question : Que dois-je faire si la taille du modèle dépasse la capacité du GPU ?

Cet article est inspiré du cours "Efficient Deep Learning Systems" dispensé par la Yandex School of Data Analysis.

Connaissances préliminaires : On suppose que le lecteur comprend déjà le fonctionnement des passages aller et retour des réseaux de neurones, ce qui est crucial pour comprendre le contenu de cet article. Cet article utilise PyTorch comme framework.

Commençons !

Lorsque vous essayez d'utiliser un grand modèle (alias gpt-2-xl), livré avec plus de 500 millions de paramètres, et que vos ressources GPU sont limitées, vous ne pouvez pas l'installer pour l'exécuter sur le GPU ou pendant la formation du modèle. Que dois-je faire si la taille du lot définie dans le document ne peut pas être atteinte pendant cette période ? Vous pouvez peut-être abandonner et utiliser une version plus légère du modèle, ou réduire la taille du lot de formation, afin de ne pas obtenir les résultats de formation décrits dans le document.

Cependant, certaines techniques peuvent aider à résoudre les problèmes ci-dessus.

Discutons de quelques méthodes permettant d'affiner le modèle GPT-2-XL avec 1,5 milliard de paramètres.

Le cœur du problème

Tout d'abord, comprenons l'essence du problème de mémoire GPU nécessaire pour charger le modèle dans le GPU.

En supposant que le modèle ait des paramètres FP32 (virgule flottante 32 bits), ce modèle doit être entraîné sur le GPU, par exemple, en exécutant l'optimiseur Adam.

Grâce au calcul, les résultats sont choquants.

Comment affiner des modèles très volumineux avec des ressources GPU limitées

Supposons que vous possédez déjà une NVIDIA GeForce RTX 3060 avec 12 Go de mémoire. Premièrement, les paramètres 1e9 FP32 occupent environ 4 Go de mémoire GPU.

De même, la même quantité de mémoire sera réservée aux dégradés. Par conséquent, un total de 8 Go de mémoire a été réservé. Comme l'entraînement n'a pas encore commencé et que l'optimiseur n'a pas été chargé, le chargement de l'optimiseur nécessite également une certaine quantité de mémoire. L'optimiseur Adam doit stocker les première et deuxième sauvegardes pour chaque paramètre, ce qui nécessite 8 Go de mémoire supplémentaire. En calculant cela, vous devez disposer d'environ 16 Go de mémoire GPU pour charger correctement le modèle sur le GPU. Dans cet exemple, le GPU ne dispose que de 12 Go de mémoire libre. Ça a l'air mauvais, non ?

Cependant, il existe plusieurs façons d'essayer de résoudre ce problème, voici les plus pertinentes :

  • accumulation de gradient/micro-batching ;
  • Formation parallèle de modèles;
  • emplois de pipeline
  • parallélisation des tenseurs
  • Formation de précision mixte;
  • Déchargement de mémoire
  • Optimiseur de quantification 8 bits.
  • Ensuite, ces technologies seront expliquées en détail.
  • Démarrer

Question : Le modèle est plus grand que la capacité du GPU, que dois-je faire ?

  • Mode simple : impossible de s'adapter à une taille de lot de 1
  • Mode professionnel : les paramètres ne peuvent pas non plus être adaptés

Aperçu

Si modèle Supérieur à la capacité du GPU, même définir la taille du lot sur 1 n'est pas suffisant, alors que devons-nous faire ? Il existe une solution qui consiste à définir des points de contrôle de dégradé. Examinons ce concept. Pour un réseau de neurones feedforward simple contenant n couches, le schéma de calcul du gradient est le suivant :

Comment affiner des modèles très volumineux avec des ressources GPU limitées

L'activation de la couche du réseau de neurones correspond au nœud marqué de f, dans le sens direct Lors du passage, tous ces nœuds sont évalués en séquence. Les gradients de perte correspondant aux activations et paramètres de ces couches sont représentés par des nœuds étiquetés b. Lors du passage en arrière, tous ces nœuds sont évalués dans l'ordre inverse. Les résultats du calcul des nœuds f sont utilisés pour calculer les nœuds b, de sorte que tous les nœuds f sont conservés en mémoire après leur passage. Ce n’est que lorsque la rétropropagation a suffisamment progressé pour calculer toutes les dépendances du nœud f qu’elle peut être effacée de la mémoire. Cela signifie : la mémoire requise pour une simple rétropropagation augmente linéairement avec le nombre de couches de réseau neuronal n.

Voici l'ordre de calcul de ces nœuds. Le cercle ombré violet indique quel nœud doit être enregistré en mémoire à un moment donné. La rétropropagation simple telle que décrite ci-dessus est optimale sur le plan informatique : elle ne calcule chaque nœud qu'une seule fois. Cependant, si vous recalculez les nœuds, vous économiserez peut-être beaucoup de mémoire. Par exemple, chaque nœud peut simplement être recalculé. L'ordre d'exécution et la mémoire utilisée sont tels qu'indiqués dans la figure ci-dessous :

Comment affiner des modèles très volumineux avec des ressources GPU limitées

Cette stratégie est optimale en terme de mémoire. Cependant, notez que le nombre de calculs de nœuds est mis à l'échelle n² fois, par rapport au facteur d'échelle précédent de n : chaque n nœud est recalculé séquentiellement n fois. En raison de la lenteur de calcul, cette méthode n’est pas adaptée au deep learning. Pour trouver un équilibre entre mémoire et calcul, il faut proposer une stratégie permettant de recalculer les nœuds, mais pas trop fréquemment. Une stratégie utilisée ici consiste à marquer un sous-ensemble d’activations de réseaux neuronaux en tant que nœuds de point de contrôle.

Dans cet exemple, choisissez de marquer le (n)ème nœud sqrt comme point de contrôle. De cette façon, le nombre de nœuds de point de contrôle et le nombre de nœuds entre les points de contrôle sont compris entre sqrt(n), ce qui signifie : la quantité de mémoire requise évolue également dans l'ordre de n. Le calcul supplémentaire requis pour cette stratégie est équivalent au calcul requis pour un seul passage à travers le réseau. Comment affiner des modèles très volumineux avec des ressources GPU limitées

Routine :

Après avoir appris les détails du point de contrôle de dégradé, voyons comment appliquer ce concept dans PyTorch, cela ne semble pas trop difficile :

Accumulation de dégradés/micro-batching

Comment affiner des modèles très volumineux avec des ressources GPU limitées

Vue d'ensemble

Les modèles d'apprentissage en profondeur sont de plus en plus grands, et il est difficile d'intégrer des réseaux de neurones aussi grands dans la mémoire GPU. Par conséquent, vous êtes obligé de choisir une taille de lot plus petite pendant la formation, ce qui peut entraîner une convergence plus lente et une précision moindre.

Qu'est-ce que l'accumulation de dégradé ?

Lors de la formation d'un réseau neuronal, les données sont généralement divisées en lots, et le réseau neuronal prédit les étiquettes de lot, qui sont utilisées pour calculer la perte par rapport à la cible réelle. Ensuite, effectuez une passe en arrière pour calculer le gradient et mettre à jour les poids du modèle. L'accumulation de dégradé modifie la dernière étape du processus de formation : avant de passer au mini-lot suivant, enregistrez la valeur du dégradé et ajoutez le nouveau dégradé au dégradé précédemment enregistré, au lieu de mettre à jour les poids du réseau pour chaque mini-lot. Les poids sont mis à jour uniquement après que le modèle a traité plusieurs mini-lots. L'accumulation de dégradé simule une taille de lot plus grande. Si vous souhaitez utiliser 64 images dans un petit lot, si la taille du lot dépasse 8, une "erreur de mémoire CUDA..." sera signalée. Dans ce cas, vous pouvez utiliser 8 lots d'images et mettre à jour les poids une fois après que le modèle ait traité 64/8 = 8 lots. Si vous cumulez les dégradés de chacun de ces 8 lots, les résultats seront (presque) les mêmes et l'entraînement pourra être réalisé !

Routine :

Une boucle d'entraînement standard sans accumulation de gradient est généralement :

Comment affiner des modèles très volumineux avec des ressources GPU limitées

Dans PyTorch, l'accumulation de gradient peut être effectuée facilement. Une fois que le modèle a terminé le traitement par mini-lots à l'aide de accumulation_steps, l'optimisation peut être effectuée. Accumulation_steps peut également être utilisé pour diviser la perte courante selon la nature de la fonction de perte :

Comment affiner des modèles très volumineux avec des ressources GPU limitées

Vraiment magnifique, non ? Le gradient est calculé lorsque loss.backward() est appelé et accumulé par PyTorch jusqu'à ce qu'il soit arrêté lorsque l'optimizer.zero_grad() est appelé.

Important

Certaines architectures réseau utilisent des opérations par lots dédiées, telles que BatchNorm, et lorsque vous utilisez la même taille de lot, les résultats peuvent être légèrement différents. Entraînement de précision mixte tenseur) ou BF16 (octets à virgule flottante).

Principaux avantages

Les principaux avantages de l'entraînement de précision mixte sont :

Utilisation réduite de la mémoire

Performance ; accélération (vitesse de calcul plus élevée) force technique ou moins d'utilisation de communication);

Utilisez du matériel dédié pour des calculs plus rapides.

  • Actuellement, je ne m'intéresse qu'au premier avantage : réduire l'utilisation de la mémoire, voyons comment utiliser le modèle PyTorch pour y parvenir.
  • Routine :
  • En conséquence, après avoir terminé l'opération .half(), le modèle est devenu 2 fois plus petit. La perte de mise à l'échelle après la conversion du modèle vers différents formats (c'est-à-dire BF16, TF16) sera discutée dans un article ultérieur. Certaines opérations ne peuvent pas être réalisées dans FP16, comme Softmax. PyTorch peut utiliser torch.autocast pour gérer ces cas particuliers.

    Optimiseur 8 bits

    Augmenter la taille du modèle est un moyen efficace d'obtenir de meilleures performances. Cependant, la formation de grands modèles nécessite de stocker le modèle, les gradients et l'état de l'optimiseur (par exemple, la somme lisse exponentielle d'Adam et la somme des carrés des gradients précédents), qui sont tous stockés dans une quantité limitée de mémoire disponible.

    La rétrogradation de l'optimiseur 32 bits vers l'optimiseur 8 bits, réduisant la plage de valeurs de 2³² à seulement 2⁸=256, aura un impact énorme sur la quantité de mémoire réservée par l'optimiseur.

    Les chercheurs ont proposé un nouvel optimiseur Adam 8 bits. L'auteur de l'article a déclaré dans l'article : "Il maintient les performances 32 bits dans une partie de la mémoire d'origine."

    L'optimiseur 8 bits comporte trois composants : (1) Quantification au niveau du bloc, qui isole les valeurs aberrantes et répartit les erreurs uniformément sur chaque bit ; (2) Quantification dynamique, qui quantifie les petites et grandes valeurs avec des valeurs élevées ; valeur de précision ; (3) Couche d'intégration stable pour améliorer la stabilité du modèle d'optimisation d'intégration de mots.

    Avec ces composants, les optimisations peuvent être effectuées directement à l'aide de l'état 8 bits. Quantifiez l'état de l'optimiseur 8 bits sur 32 bits, effectuez la mise à jour, puis quantifiez l'état sur 8 bits pour le stockage. La conversion de 8 bits en 32 bits est effectuée élément par élément dans les registres sans qu'il soit nécessaire de procéder à des copies lentes vers la mémoire GPU ou une mémoire temporaire supplémentaire pour effectuer la quantification et la déquantification. Pour les GPU, cela signifie que l'optimiseur 8 bits est plus rapide que l'optimiseur 32 bits standard.

    Jetons un coup d'œil aux résultats encourageants après avoir utilisé Adam 8 bits :

    Comment affiner des modèles très volumineux avec des ressources GPU limitées

    On peut voir que l'utilisation d'Adam quantifié peut économiser environ 8,5 Go de mémoire GPU , Ça a l'air plutôt bien !

    Après avoir compris sa convivialité, voyons comment l'implémenter avec python.

    Le package Bitsandbytes fourni par Facebook est un wrapper léger autour des fonctions personnalisées CUDA, encapsulant l'optimiseur 8 bits et la fonction de quantification, qui peut être utilisé pour implémenter l'utilisation d'Adam 8 bits.

    Routine :

    Comment affiner des modèles très volumineux avec des ressources GPU limitées

    Comme mentionné ci-dessus, l'utilisation de l'optimiseur quantifié est très simple et les résultats sont bons.

    Combinez toutes les méthodes ci-dessus pour affiner GPT-2-XL sur le GPU.

    Enfin, après avoir maîtrisé les méthodes ci-dessus, utilisez ces méthodes pour résoudre des problèmes pratiques et affiner le modèle GPT-2-XL avec 1,5 milliard de paramètres. Apparemment, il n'y a aucun moyen de le charger sur un GPU NVIDIA GeForce RTX 3060 avec 12 Go de RAM. Listez toutes les méthodes que vous pouvez utiliser :

    • Gradient checkpoint ;
    • Entraînement de précision mixte (J'ai mis en place une astuce : utiliser deux échantillons du même modèle. Tout d'abord, chargez-le sur le GPU avec .half et nommez-le gpu_model ; Deuxièmement, sur le CPU, nommez-le cpu_model. Après avoir évalué le modèle GPU, chargez le dégradé de gpu_model dans cpu_model, exécutez optimiseur.step() et chargez les paramètres mis à jour dans gpu_model ); accumulation de gradient, la perte doit être mise à l'échelle par accumulation_steps ;
    • optimiseur Adam 8 bits.
    • Utilisez toutes les méthodes ci-dessus et consultez le code :

    Après avoir utilisé toutes les méthodes ci-dessus, le réglage fin du modèle GPT-2-XL de 16 Go a été mis en œuvre sur le GPU. Compris !

    Comment affiner des modèles très volumineux avec des ressources GPU limitées

    Conclusion

    Dans ce blog, les concepts clés d'une utilisation efficace de la mémoire ont été présentés, qui conviennent à une variété de tâches difficiles, comme mentionné ci-dessus. D’autres concepts seront abordés dans des articles ultérieurs. Merci beaucoup d'avoir pris le temps de lire cet article !

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer