Se concentrez sur les éviers et où les cacher un parcours visuel pour la mise en œuvre du streaming LLM

Se concentrer sur les essentiels et les dissimuler un guide visuel pour la réalisation du streaming LLM

GPT-2 Générateur de texte avec cache

Un des derniers articles sur l’IA qui fait parler de lui est une technique pour les architectures de modèle Generative Pre-training Transformer (GPT) qui permettent des fenêtres de contexte de taille illimitée et efficace pour la génération de texte. Cela est rendu possible en exploitant une découverte sur les “attention sinks”, c’est-à-dire que les premiers tokens dans la prédiction du prochain token (autorégressive) sont ceux qui font la plus grande partie du travail pour l’auto-attention afin de construire une représentation du texte. C’est très pratique car cela ne nécessite pas de fine-tuning et ne requiert que des modifications minimales de l’architecture GPT. Cet article se concentre sur ces modifications en détail afin que vous puissiez vous sentir à l’aise en sachant comment le mettre en pratique.

Pour vous rappeler pourquoi c’est important, un modèle LLM basique nécessite une mémoire et un temps de traitement exponentiels lorsque la longueur du contexte augmente pour générer le prochain token. De plus, de nombreux modèles ne sont pas réellement entraînés sur des entrées très longues, donc ils souffrent lorsque les entrées deviennent plus longues. À chaque fois que le modèle génère le prochain token, la fenêtre devient plus longue. Imaginez GPT écrivant la fin d’un livre. Pour que le modèle comprenne tout ce qu’il a écrit, il a besoin de garder une très longue fenêtre de contexte, sinon la fin du livre ne rassemblerait pas tous les détails de l’intrigue.

L’article :

https://arxiv.org/pdf/2309.17453v1.pdf

Le reste de cet article se concentre sur la technique réelle, et non sur sa justification ou sur l’examen des résultats. Le texte réel dans l’article sur la technique est relativement court. Essentiellement, vous choisissez un certain nombre de “attention sinks”, puis vous avez une file d’attente d’embeddings de tokens après le sink qui a une taille fixe. À chaque itération, lorsque vous générez un nouvel embedding de token, vous conservez les embeddings du sink et vous ignorez simplement les embeddings des tokens situés à la fin de la file d’attente.

Voici donc un exemple concret pour le texte commençant par

Hmm, d’accord donc voici une entrée

Et disons que nous avons 3 attention sinks et une longueur maximale de token de seulement 7. Initialement, nous faisons passer tous les tokens à travers les couches pour produire 7 embeddings de tokens, puis nous passons à la génération du 8ème token. Disons que le prochain token produit est “texte”, et en gras se trouve le token que nous allons évacuer ensuite.

[Hmm, d’accord, donc, ce, est, une, entrée] → “texte”

Ensuite, à la prochaine itération, nous faisons défiler la file d’attente en arrière et évacuons le token qui apparaît le plus tôt possible après le sink.

[Hmm, d’accord, donc, est, une, entrée, texte] → “et”

Et nous continuerions de faire cela jusqu’à la fin.

[Hmm, d’accord, donc, une, entrée, texte, et] → “ce”

L’autre chose à garder à l’esprit est que les embeddings de position ne sont pas avancés, ils restent simplement les mêmes. Cela signifie que l’embedding de position associé au token change à chaque itération.

Détails de visualisation

Les étapes de calcul seront présentées visuellement à l’aide d’un outil de visualisation de graphe de nœuds. Chaque bloc est une opération qui prend les entrées du côté gauche et produit les données pour les variables de sortie du côté droit. Les liens indiquent le passage de données des sorties vers les entrées, et les cercles sur les entrées signifient que les données sont spécifiées sur place et sont statiques.

Les opérations sont soit des compositions contenant une icône “unbox”, qui se décompose ensuite en un sous-graphe dont les entrées sont les entrées du parent et les sorties sont les sorties du parent, soit elles sont primitives, ce qui signifie qu’elles ne peuvent pas être décomposées davantage et correspondent à des opérations de tenseur de bas niveau telles que celles de NumPy ou TensorFlow. Les couleurs indiquent le type de données et les motifs indiquent la forme des données. Le bleu signifie que le type de données est un entier, alors que le violet/rosé signifie qu’il s’agit d’un type de données décimales et le vert signifie qu’il s’agit de texte. Les liens solides indiquent que la forme des données est scalaire, tandis que les points dans le lien indiquent le nombre de dimensions du tableau (le nombre de points entre les tirets). Au bas de chaque graphe se trouve un tableau qui caractérise la forme, le type et le nom de l’opération de chaque variable qui transporte des données dans le modèle.

J’ai traité et utilisé la visualisation dans des articles précédents, comme la création d’une carte de référence pour GPT Fully Visualized et BERT Fully Visualized, et pour des visites guidées visuelles sur les Graph Attention Networks, la méthode de fine-tuning LoRA et BERTScore.

Mise en œuvre visualisée

Plongeons dans l’implémentation. Ce que nous voyons ci-dessous est le début d’une boucle. À chaque itération, nous avons le prochain jeton et les jetons concaténés jusqu’à présent. Nous ne passons que le nouveau jeton suivant à GPT car nous lisons et écrivons les embeddings mis en cache à chaque couche. C’est une astuce d’implémentation qui le rend plus efficace, de sorte qu’à chaque nouvelle itération, nous ayons seulement besoin de récupérer les embeddings pour le dernier jeton.

Avant d’aller plus loin, examinons certains hyperparamètres (variables globales) du modèle. Les constantes globales sont des valeurs statiques dans votre graphique.

Nous allons lire depuis la base de données publique contenant les poids de GPT-2 et les mettre en cache dans le répertoire spécifié “gpt_2_rolling_cache”. Ces chemins de cache sont utilisés pour stocker les paramètres de chaque poids et chaque fonction, comme les paramètres du modèle qui sont en mémoire.

Vous pouvez voir que nous avons défini le nombre de sinks d’attention à 3 jetons et le nombre maximal de jetons à 7. Cela signifie que nous limiterons le modèle à traiter plus de 7 jetons à la fois, ce qui est assez court, mais c’est juste un exemple. En général, ce nombre correspondrait aux longueurs de contexte d’origine utilisées pour l’entraînement, qui pour ce petit modèle GPT-2 est de 32. À chaque fois que nous traitons le jeton suivant, nous supprimons le jeton le plus ancien mis en cache après les sinks d’attention, et au total, nous ne regardons que les 3 sinks d’attention + les 4 derniers jetons à chaque itération.

Mais lorsque nous disons “regarder les jetons”, que signifie vraiment cela ? Plongeons dans les couches. En regardant juste la couche 0, vous pouvez suivre les indices pour voir où nous en sommes dans l’architecture. Ici, nous récupérons les couches de projection dense pour les poids Clé et Valeur.

À l’intérieur de Key Rolling Cache, nous lisons les poids depuis le cache. Notez que nous sommes dans un bloc conditionnel, donc lors de la première itération, nous écrirons simplement dans le cache sans lire. Le cache comprend les embeddings des jetons de l’itération précédente. Les embeddings ont une forme [1, 12, 7, 64].

  • La dimension 0 représente la taille du batch (1),
  • La dimension 1 représente le nombre de têtes d’attention (12),
  • La dimension 2 représente le nombre de jetons (7),
  • La dimension 3 représente la taille cachée (768) divisée par le nombre de têtes d’attention (64).
Lecture depuis le cache à chaque couche

Le lien entrant autour de la lecture depuis le fichier concerne uniquement le(s) jeton(s) entrant(s). Dans la première itération de la boucle de notre exemple, il s’agit de [1, 12, 7, 64], puis à chaque itération suivante, cela ne s’exécute que pour le jeton suivant, qui est [1, 12, 1, 64]. La première chose que nous ferons est de séparer les sinks d’attention (sur la dimension 2) et de concaténer le nouvel embedding selon l’axe de la dimension 2. Les poids des sinks d’attention sautent en avant pour être concaténés. À l’intérieur du bloc d’éviction, nous évacuerons 1 ou plusieurs jetons de la fin de la file d’attente.

Logique de cache roulant

A l’intérieur du bloc de suppression, vous pouvez voir que nous calculons combien d’encodages de jetons à découper (c’est-à-dire supprimer, ouais “supprimer” sonne mieux) du début de la dimension 2. En général, chaque nouveau jeton entraîne la suppression de 1 jeton.

Supprimer

Enfin, nous prenons le résultat et le concaténons avec les encodages d’attention du puits précédent et le transmettons vers l’avant. Nous faisons de même pour les poids de clés et de valeurs pour chaque couche lorsque nous récupérons les couches de clés de requête de valeur dans l’opération d’auto-attention.

Récupérer les encodages de clés de requête de valeur

Enfin, il ne reste plus que l’encodage de position. Dans le bloc “Créer des ID de position”, nous pouvons mettre à jour la logique des encodages de position. La logique est relativement simple. Soit nous incrémentons l’encodage de position pour le prochain jeton parce que nous n’avons pas encore atteint la longueur du jeton, soit nous les gardons les mêmes et récupérons les mêmes encodages de position.

Créer des ID de position

Par exemple, je compare côte à côte GPT-2 sans le cache roulant et avec le cache roulant pour générer 20 jetons, en commençant par l’exemple que j’ai donné plus tôt “Hmm d’accord, c’est un peu de texte d’entrée”. C’est encore court et ne nécessite pas vraiment le cache roulant, mais cela montre que ça fonctionne.

GPT2 sans cache roulant :

GPT2 avec cache roulant (max 7 jetons et 3 puits d’attention) :

Ils sont différents, ce qui est attendu, mais l’un est-il meilleur que l’autre ?

Merci d’avoir lu ! Le graphique complet est disponible en format JSON sur Github. Qu’en avez-vous pensé ? Ai-je fait une erreur quelque part ? Y a-t-il quelque chose que vous aimeriez voir ensuite ? Faites-le moi savoir dans les commentaires !

We will continue to update IPGirl; if you have any questions or suggestions, please contact us!

Share:

Was this article helpful?

93 out of 132 found this helpful

Discover more

AI

Une nouvelle chimie

Les batteries à ions d'oxygène avancent.

AI

Architecture d'intégration de LinkedIn alimentant ses capacités de recherche d'emploi

Les embeddings sont devenus l'un des composants les plus importants des applications des modèles de langage de grande...

AI

Sonner dans l'avenir NVIDIA et Amdocs apportent une IA générative personnalisée à l'industrie mondiale des télécommunications

L’industrie des télécommunications – l’épine dorsale du monde interconnecté d’aujourd’h...

AI

Do Flamingo et DALL-E se comprennent-ils ? Explorer la symbiose entre les modèles de légendage d'images et de synthèse texte-image

La recherche multimodale qui améliore la compréhension informatique des textes et des visuels a récemment fait de gra...

AI

Rencontrez l'Omnivore une start-up développe une application permettant aux utilisateurs de transformer des objets en modèles 3D avec simplement leur smartphone.

Note de l’éditeur : Cet article fait partie de notre série Rencontre avec l’Omnivore, qui met en avant de...