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
Community Posts
No posts yet. Be the first to write about this video!
Write about this video