Weight Folding, flux CUDA et le bug qui a fait parler mon modèle à l'envers — Filip Makraduli

AAI Engineer
Computing/SoftwareInternet Technology

Transcript

00:00:00.
00:00:13Bonjour à tous, merci d'être venus et
00:00:18je vais commencer l'exposé. Alors
00:00:22cet exposé porte sur un article
00:00:27que j'ai rédigé, qui est très simple.
00:00:33La proposition est très claire.
00:00:36Il s'agit fondamentalement de deux lignes d'
00:00:39algèbre qui rendent la couche RMS norm
00:00:42dans les transformeurs moins coûteuse,
00:00:45plus rapide et qui l'améliorent en quelque sorte
00:00:48en tant que couche dans l'architecture
00:00:51des transformeurs. De la même manière que
00:00:54la normalisation par couche a été la norme
00:00:57avant d'être remplacée par RMS norm.
00:01:00Cela s'inscrit dans cette lignée de pensée.
00:01:04Et j'ai eu l'occasion de rencontrer
00:01:09certaines personnes du monde de l'open source
00:01:11et j'ai coécrit cet article
00:01:14avec Nils Graf qui a été
00:01:17en quelque sorte le créateur de ceci.
00:01:21Et le travail découle de là.
00:01:23C'est donc présenté sur arXiv.
00:01:26Vous pouvez y jeter un œil, le lire, l'essayer.
00:01:29Il y a également un dépôt.
00:01:31Et le concept, disons l'idée
00:01:36et la façon de penser, est plus facile
00:01:38à expliquer peut-être avec FlashAttention.
00:01:40De la même manière que FlashAttention
00:01:45attend qu'il y ait une multiplication
00:01:48et essaie de limiter ces communications
00:01:52entre la mémoire afin que l'ensemble du processus
00:01:54soit plus rapide.
00:01:55C'est un peu une réflexion similaire dans ce sens.
00:01:58Et cela apporte certaines améliorations
00:02:01qui rendent le processus RMS norm bien plus rapide
00:02:08et par conséquent améliorent l'ensemble du transformeur.
00:02:15Et une question se pose : pourquoi RMS norm ?
00:02:19Puisque cette couche ne fait presque aucun calcul.
00:02:24Et c'est vrai.
00:02:26Donc la part de la portion de calcul
00:02:29si l'on regarde est assez faible.
00:02:31Cependant, le temps d'horloge ou temps réel,
00:02:34comme on dit, est assez important.
00:02:36Et par exemple, dans une étape de décodage,
00:02:40donc juste au moment où l'inférence est exécutée,
00:02:43RMS norm peut être lancée environ 33 fois.
00:02:47Bien sûr, cela dépend du modèle et ainsi de suite.
00:02:50Dans l'article, vous trouverez les modèles spécifiques
00:02:52et la manière dont cela a été testé.
00:02:54Et la question est de savoir comment améliorer cela
00:02:59et comment ce temps d'attente pour la multiplication matricielle
00:03:03peut être en quelque sorte évité.
00:03:06Et la raison pour laquelle c'est lent
00:03:09vient du fait que les GPU ne sont pas lents ou mauvais en calcul,
00:03:15mais ils sont mauvais pour tout ce qui entoure le calcul proprement dit.
00:03:20Cela signifie lancer le travail, le travail réel.
00:03:24Donc par exemple, lancer le processus comme cela se produit
00:03:30dans certaines expériences 33 fois,
00:03:32cela prend beaucoup de temps.
00:03:35Et par exemple, fusionner chaque normalisation
00:03:40dans la multiplication matricielle peut aider à l'éviter.
00:03:44De même, effectuer un repliement de poids
00:03:46peut aider à déplacer les données entre la mémoire,
00:03:50et c'est un processus qui est également lent pour les GPU.
00:03:54Et aussi l'attente.
00:03:56Donc par exemple, différer la division
00:04:00qui est effectuée dans la couche RMS norm
00:04:02est également un moyen d'éviter cette étape d'attente.
00:04:06Donc en gros, ce que fait cet article,
00:04:09c'est qu'il améliore ces trois aspects
00:04:13grâce à quelques astuces algébriques
00:04:16dans la façon dont la norme RMS est calculée.
00:04:19Voilà.
00:04:21Et mathématiquement, voici ces astuces.
00:04:25Elles tournent principalement autour des deux premières propositions.
00:04:29La première est la normalisation sans poids.
00:04:31Vous pouvez la voir ici.
00:04:33Et la normalisation différée.
00:04:35C'est donc la deuxième.
00:04:37Et maintenant, dans les architectures plus récentes,
00:04:39il arrive que la norme RMS apparaisse deux fois.
00:04:44Par exemple, c'est le cas dans Gemma 4.
00:04:48Donc, annuler la pré-normalisation fonctionne aussi.
00:04:52Et tout cela est prouvé algébriquement dans l'article.
00:04:58Et la première proposition est celle-ci,
00:05:00où le gain et le pliage des poids
00:05:04se combinent en une seule matrice.
00:05:06W, que vous pouvez voir ici avec un astérisque.
00:05:09Et cela est calculé hors ligne,
00:05:12un peu comme dans Flash Attention
00:05:14où l'on calcule des choses en arrière-plan
00:05:16pour éviter d'avoir des communications
00:05:18constantes avec la mémoire.
00:05:20C'est donc une étape qui est réalisée, pour ainsi dire,
00:05:24ce pliage des poids.
00:05:26L'autre étape consiste à différer la division par le scalaire
00:05:32de la multiplication matricielle pour pouvoir les exécuter en parallèle.
00:05:35Normalement, il faudrait calculer une première fois, puis attendre et recalculer.
00:05:38Puis attendre et calculer à nouveau.
00:05:41Dans ce cas, l'idée est de diviser tout ça
00:05:44afin de pouvoir le paralléliser.
00:05:48Et la troisième, qui est une sorte de variante,
00:05:51c'est que s'il y en a deux,
00:05:56comme c'est invariant par changement d'échelle,
00:05:58on peut en éliminer une et cela fonctionne toujours.
00:06:01Et cela s'applique aux modèles plus récents
00:06:04qui peuvent adopter cette architecture et cette implémentation.
00:06:10Alors, pour mettre cela en pratique dans la réalité,
00:06:13surtout pour cette proposition numéro deux.
00:06:16Pour celle-ci, par exemple, c'est simple.
00:06:20Il y a un dépôt appelé transformer tricks.
00:06:22Vous pouvez simplement l'appliquer à n'importe quel modèle et ça marche.
00:06:25Mais pour y parvenir, il y a un travail sur les noyaux à faire.
00:06:29Ce n'est donc pas si simple que ça.
00:06:31Alors pour que je puisse le faire,
00:06:35j'étais en train de l'implémenter et j'ai abouti à cette expérience.
00:06:42Ça a l'air correct en général, du genre :
00:06:46« Le prompt est : l'architecture transformer
00:06:48a révolutionné le TAL car... »
00:06:51et il y a une sorte de sortie attendue.
00:06:54Mais dans la sortie que j'ai obtenue,
00:06:56j'ai vu cette répétition et un décalage d'un pas.
00:06:59Comme vous pouvez le voir ici, le mot car réapparaît.
00:07:01Il se passait quelque chose avec les flux du GPU
00:07:07et j'essayais de comprendre ce qui se passait.
00:07:10J'obtenais ce décalage d'un pas et des sortes de résultats
00:07:15qui provenaient du passé en quelque sorte.
00:07:18Et en déboguant tout cela,
00:07:21je me suis rendu compte qu'en construisant quelque chose de ce genre...
00:07:26Comme je l'ai expliqué pour la proposition 2 ou le report de ces opérations,
00:07:32en CUDA, vous pouvez faire deux choses.
00:07:35Vous pouvez utiliser des Tensor Cores qui effectuent une partie de la multiplication matricielle,
00:07:39et vous pouvez utiliser des CUDA Cores qui exécutent des opérations par élément,
00:07:44des réductions, des racines carrées, etc.
00:07:47L'idée était donc de faire cela en parallèle et de bénéficier
00:07:52de ce que j'expliquais dans l'article pour tester réellement ce concept.
00:07:58Voici à quoi cela était censé ressembler.
00:08:00Si l'on procède de manière séquentielle,
00:08:03il y a un temps d'attente inactif lorsque l'unité vectorielle calcule le RMS et la mise à l'échelle,
00:08:10puis vient la multiplication matricielle.
00:08:12L'idée était donc d'utiliser Flash Norm, la technique présentée dans l'article,
00:08:16pour exécuter ces deux opérations en parallèle.
00:08:19L'unité matricielle calcule donc le produit matriciel et l'unité vectorielle calcule le RMS.
00:08:23De cette façon, on gagne du temps.
00:08:26Cependant, on ne peut pas faire cela uniquement en Python.
00:08:28Il faut descendre un peu plus bas.
00:08:30Et je l'ai fait avec des codes CUDA comme celui-ci.
00:08:35À l'époque, cela semblait globalement correct.
00:08:42Cependant, je me suis rendu compte que j'avais commis une petite erreur.
00:08:47Cette erreur était que la jonction finale, censée réunir les deux flux, était implicite dans mon cas.
00:08:57Lorsque j'ai testé cela, le test unitaires a fonctionné.
00:09:01La qualité semblait similaire, comme pour les tests de perplexité, car la génération est comparable.
00:09:08Mais lors de longues générations, ce problème est apparu.
00:09:11Je n'avais donc aucune idée de ce que c'était.
00:09:14La raison était que, lors de cette jonction implicite, l'un des flux n'avait fondamentalement pas terminé son travail.
00:09:24J'ai donc obtenu des conditions de concurrence qui lisaient le passé de la multiplication matricielle inachevée.
00:09:31Pour corriger cela, l'idée consistait à rendre la jonction explicite.
00:09:40Et à attendre qu'une des opérations soit terminée pour m'assurer que, lors de la jonction, je ne lis pas des données du passé.
00:09:48Voilà ce que j'ai compris en explorant les flux CUDA.
00:09:54Et voici comment les choses étaient configurées.
00:09:57La jonction était implicite.
00:10:00L'étape de post-mise à l'échelle lisait donc une ancienne valeur de tampon.
00:10:06Pour corriger cela, il faut marquer la fin de la multiplication matricielle.
00:10:14Puis marquer la fin du RMS.
00:10:17Ensuite, lors du post-mise à l'échelle, attendre le premier flux.
00:10:21Puis attendre le second flux.
00:10:24Cela a corrigé le bogue, permis à l'article de fonctionner et fait en sorte que le modèle avance au lieu de régresser.
00:10:33C'était la perspective académique intéressante.
00:10:38Mais je voulais aussi tester des choses, n'est-ce pas ?
00:10:40Déployer ceci, l'essayer et voir comment le faire fonctionner dans un cadre plus proche de la production.
00:10:47Vous pouvez également lire l'article et consulter tous les tests.
00:10:50La plupart sont réalisés sur des modèles Llama, mais cela fonctionne aussi pour d'autres architectures.
00:10:56Pour cet article spécifique, vous pouvez par exemple réaliser le repliement de poids que j'ai expliqué (la proposition 1) simplement avec du code du dépôt.
00:11:10On appelle simplement flashify et c'est fait.
00:11:13Cependant, pour la deuxième chose que j'ai mentionnée, il faut un peu de travail sur les noyaux si l'on veut procéder ainsi.
00:11:19Comme je l'ai expliqué dans mon exemple.
00:11:22Voici quelques résultats basés sur les modèles Llama, et il y a différents détails que vous pouvez également examiner.
00:11:29Comme ce qui se passe si l'on fait seulement une normalisation différée, ou un noyau entièrement fusionné.
00:11:36Il y a donc beaucoup d'expériences bas niveau pour tester toutes les propositions.
00:11:41Ce sont nos résultats avec différents niveaux d'analyse et de détail.
00:11:48Mais même la méthode simple du repliement de poids montre des améliorations.
00:11:54Et cela fonctionne également avec les outils quotidiens que l'on utilise pour un modèle.
00:11:59Il n'est donc pas nécessaire de réinventer la roue ou de tout faire de zéro.
00:12:05Cela fonctionne avec torch.compile, car c'est en quelque sorte un nouveau point de contrôle et c'est tout.
00:12:13Flash Attention utilise des astuces similaires à un autre niveau.
00:12:16Et cela fonctionne aussi avec les modèles quantifiés.
00:12:18Il est donc tout à fait possible d'appliquer cela pour obtenir un modèle doté de cette nouvelle couche de normalisation.
00:12:27Pour trouver ces détails et le code afin de les exécuter, rendez-vous sur le dépôt transformer-tricks.
00:12:34Il contient différentes astuces algébriques comme je l'ai expliqué, ainsi que l'article mentionné.
00:12:41Il y a aussi le dépôt de modèles Hugging Face où j'ai implémenté cela avec quelques modèles.
00:12:50Vous pouvez accéder au lien Hugging Face de ce modèle et le tester.
00:12:54Ces modèles Hugging Face peuvent également être déployés en production.
00:13:00Quand je pensais à faire cela, je me suis dit que maintenant que la science est faite et qu'il y a un lien vers un modèle Hugging Face,
00:13:11le moteur d'inférence de Superlink était un moyen formidable de déployer n'importe quel modèle Hugging Face.
00:13:19Nous l'avons fait lors de hackathons où des gens apportaient un modèle ou un point de contrôle Hugging Face personnalisé avec leurs ajustements.
00:13:27On peut ainsi tester une version de ces astuces algébriques pour améliorer un modèle et valider ses propres idées de recherche,
00:13:37déployer une version de ce modèle sur un cluster et s'affranchir du code de liaison nécessaire au déploiement.
00:13:48C'est plutôt cool. L'idée est que si l'on dispose d'un cluster et d'une inférence open source,
00:13:58on peut tester des idées de recherche plus novatrices, par exemple de la manipulation de noyaux ou Flash Norm,
00:14:12ce qui est beaucoup plus difficile sur un point de terminaison loué où l'on ne possède pas l'inférence.
00:14:18On veut quelque chose de portable et de flexible pour pouvoir réaliser ce genre de choses,
00:14:23tout en étant suffisamment prêt pour la production pour tester à grande échelle.
00:14:27On peut par exemple utiliser Site pour combiner cela avec d'autres modèles.
00:14:33Comme vous le voyez en haut à gauche, on peut associer ces modèles optimisés à d'autres pour effectuer des tâches d'agents si l'on veut,
00:14:43et ainsi réaliser ce cas d'usage global de bout en bout.
00:14:46Le fonctionnement de Site repose sur ce cluster de production qui aide à déployer les modèles.
00:14:52Vous pouvez également consulter le dépôt de Site pour plus de détails.
00:14:57Il y a aussi un mécanisme de mise en file d'attente intelligent qui aide, surtout si l'on travaille avec de petits modèles,
00:15:03car pour le travail sur Flash Norm, j'ai utilisé de petits modèles Llama ainsi que de petits agents de Hugging Face.
00:15:11Avoir un moyen de déployer de petits modèles pouvant tourner sur le même GPU évite de gaspiller de l'argent en frais de GPU,
00:15:24tout en permettant de basculer facilement entre les modèles, ce qui s'est avéré très utile.
00:15:30On peut également contrôler les configurations des modèles via une API ainsi que via le cluster,
00:15:35ce qui est très pratique sans avoir besoin d'un spécialiste infrastructure pour vous soutenir dans votre recherche open source.
00:15:40C'est donc également un point positif.
00:15:43De plus, on possède son propre cloud, ce qui est utile pour les poids ouverts, les modèles ouverts et l'open source.
00:15:50Site propose également un catalogue de différents modèles, pas seulement ceux que j'ai mentionnés,
00:15:57que vous pouvez aller voir.
00:15:59Il y a aussi des modèles de reclassement et d'incorporation si vous développez quelque chose dans ce domaine.
00:16:03Sur ce, je conclus l'histoire de mon parcours de recherche où j'ai coécrit cet article sur la technique qui améliore le transformeur,
00:16:15tout en trouvant un moyen de l'amener en production, de le tester et de jouer avec ces modèles open source.
00:16:24N'hésitez pas à me contacter sur LinkedIn si vous avez des questions ou souhaitez contribuer.
00:16:30Bon nombre des éléments mentionnés correspondent à des demandes de fusion (PR) sur VLLM ou Hugging Face.
00:16:36Vous devriez les retrouver un peu partout.
00:16:38Vous pouvez également jeter un œil à l'article.
00:16:40Le lien arXiv est à votre disposition.
00:16:43Vous avez aussi le dépôt de Site et mon profil LinkedIn.
00:16:47Alors, merci beaucoup d'avoir assisté à cette présentation.
00:16:58Et vous pouvez me trouver pour poser vos questions.
00:16:59Nous serons ici.
00:17:00Tout près.
00:17:13vous

Key Takeaway

Des astuces algébriques et l'optimisation des flux CUDA permettent de fusionner la couche RMS norm avec les multiplications matricielles, accélérant ainsi l'inférence des transformeurs.

Highlights

  • Deux lignes d'algèbre réduisent le coût et augmentent la vitesse de la couche RMS norm dans les transformeurs.

  • La couche RMS norm peut être exécutée environ 33 fois lors d'une seule étape de décodage.

  • La technique combine le pliage des poids et la normalisation différée pour éviter les temps d'attente des GPU.

  • L'utilisation de flux CUDA parallèles nécessite une jonction explicite pour éviter les conditions de concurrence et la lecture de données obsolètes.

Timeline

Optimisation algébrique de la couche RMS norm

  • Une modification algébrique rend la couche RMS norm plus rapide et moins coûteuse dans l'architecture des transformeurs.
  • La normalisation RMS est exécutée fréquemment lors du décodage, atteignant environ 33 fois par étape d'inférence.
  • Les GPU perdent du temps à lancer de multiples petites opérations plutôt qu'à effectuer les calculs eux-mêmes.

L'article coécrit avec Nils Graf propose des solutions pour contourner la lenteur inhérente aux lancements fréquents de la couche RMS norm. En fusionnant les normalisations dans les multiplications matricielles, en effectuant un repliement de poids et en différant la division scalaire, le processus gagne en efficacité.

Débogage et gestion des flux CUDA

  • L'implémentation utilise des Tensor Cores pour les multiplications et des CUDA Cores pour les réductions en parallèle.
  • Une jonction implicite entre les flux GPU provoque des conditions de concurrence et génère des répétitions dans le texte généré.
  • Rendre la jonction explicite et attendre la fin de chaque flux résout le bogue de génération.

L'expérimentation de la normalisation différée à l'aide de code CUDA brut a mis en évidence un problème de décalage textuel. La cause provient d'une lecture anticipée de tampons non finalisés. L'ajout d'une synchronisation explicite des flux garantit la validité des données manipulées.

Déploiement et intégration dans l'écosystème open source

  • Le repliement de poids s'applique simplement via le dépôt transformer-tricks et est compatible avec torch.compile.
  • Le moteur d'inférence de Site permet de déployer des modèles personnalisés et de tester des idées de recherche sur un cluster.
  • La mise en file d'attente intelligente de Site optimise l'utilisation des GPU pour les petits modèles.

Les améliorations développées s'intègrent dans les outils existants comme Hugging Face ou torch.compile sans nécessiter de réécriture complète. L'utilisation de plateformes de déploiement open source facilite la validation des performances et l'expérimentation à grande échelle sur des clusters de production.

Community Posts

No posts yet. Be the first to write about this video!

Write about this video