Aller au contenu
Kudos AI
Read in English
Construire un modèle de langue

La tokenisation et les plongements

Comment le texte devient des nombres sur lesquels un modèle peut s’entraîner : construire un vocabulaire, pourquoi le codage par paires d’octets n’a jamais besoin d’un token inconnu, la couche de plongement comme une consultation qui est prouvablement un one-hot fois une matrice, et pourquoi la position doit être réinjectée à la main.

8 min de lectureKudos AI

Prérequis : Qu’est-ce qu’un réseau de neurones ?

Un mot scindé en trois sous-mots, puis un vecteur one-hot multiplié à travers la matrice d’embeddings, chaque autre ligne visiblement annulée.

Un réseau de neurones multiplie des matrices. Le texte est une suite de caractères. Avant que la machinerie de Qu’est-ce qu’un réseau de neurones ? puisse tourner, il faut franchir cet écart - et le pont se révèle avoir trois étapes distinctes, chacune résolvant un problème créé par la précédente.

Cet article suit une chaîne de caractères jusqu’au tenseur qu’un transformeur consomme : la découper en tokens, les associer à des entiers, transformer les entiers en vecteurs, puis réparer l’information détruite en chemin.

A. Construire un vocabulaire

On commence par découper le texte en tokens, puis on rassemble chaque token unique, on les trie et on les numérote. L’illustration de Raschka utilise un corpus d’une phrase :

The quick brown fox jumps over the lazy dog

Les tokens uniques triés alphabétiquement, chacun associé à un entier unique appelé identifiant de token :

TokenID
brown0
dog1
fox2
jumps3
lazy4
over5
quick6
the7

Le vocabulaire est construit une fois à partir de tout le jeu d’entraînement, puis appliqué à n’importe quel nouveau texte. Encoder est une consultation de dictionnaire ; décoder demande le vocabulaire inverse qui ramène les identifiants à des chaînes, et c’est ainsi qu’on récupère du texte à partir de la sortie d’un modèle.

Python

S'exécute dans votre navigateur. La première exécution télécharge l'environnement Python (~10 Mo), puis il est mis en cache.

B. Le problème que cela crée

Un vocabulaire fixe ne peut encoder que les mots qu’il a vus. Donnez-lui someunknownPlace et la consultation échoue. Le rustinage évident est un token spécial <|unk|> signifiant « quelque chose que je ne connais pas », mais c’est une véritable perte d’information : tout mot inédit s’effondre sur le même symbole, et le modèle ne pourra jamais rien dire de spécifique sur aucun d’eux.

C. Le codage par paires d’octets

Le codage par paires d’octets (BPE) résout cela sans aucun token inconnu. Raschka note qu’il fut le tokeniseur employé pour entraîner GPT-2, GPT-3 et le modèle original derrière ChatGPT, et que son vocabulaire compte 50 257 entrées, <|endoftext|> recevant le plus grand identifiant, 50256.

Le mécanisme s’énonce simplement : quand le BPE rencontre un mot absent de son vocabulaire prédéfini, il décompose le mot en unités sous-lexicales plus petites, voire en caractères individuels. Comme chaque caractère individuel est dans le vocabulaire, il existe toujours un repli, et n’importe quelle chaîne peut être encodée. Raschka est explicite : c’est ainsi que le tokeniseur traite correctement un mot comme someunknownPlace sans jamais avoir besoin de <|unk|>.

C’est pourquoi les comptes de tokens et de mots diffèrent. Un mot courant est en général un token ; un mot rare ou inventé en fait plusieurs. Cela explique aussi une famille de comportements qui déroutent - la prise fragile d’un modèle sur l’orthographe ou le comptage de caractères tient en partie à ce qu’il ne voit jamais de caractères, seulement ces morceaux.

<|endoftext|> fait double emploi. Raschka note qu’il sert aussi de token de remplissage lors de la mise en lots d’entrées de longueurs différentes. Cela semble dangereux - le remplissage est un bourrage dénué de sens - mais c’est sans conséquence, car l’entraînement utilise un masque de sorte que les positions remplies ne sont pas prises en compte par l’attention. Le token précis choisi pour le remplissage est donc indifférent.

D. Des entiers aux vecteurs

Les identifiants de tokens restent inutilisables comme entrée d’un modèle. L’ID 7 n’est pas sept fois l’ID 1 ; les entiers sont des étiquettes, et toute arithmétique sur eux est dénuée de sens.

Le remède est une couche de plongement : une matrice de poids comportant une ligne par entrée du vocabulaire, la ligne ii étant le vecteur représentant le token ii. Raschka le décrit clairement - la couche de plongement est essentiellement une opération de consultation qui récupère des lignes de la matrice de poids de la couche de plongement au moyen d’un identifiant de token.

Avec un vocabulaire de 4 et une dimension de plongement de 3 :

E=[0.30−0.100.90−0.200.500.101.100.40−0.700.05−0.600.25]E = \begin{bmatrix} 0.30 & -0.10 & 0.90\\ -0.20 & 0.50 & 0.10\\ 1.10 & 0.40 & -0.70\\ 0.05 & -0.60 & 0.25 \end{bmatrix}

Le token d’ID 2 se plonge en la ligne d’indice 2, soit [1.100.40−0.70]\begin{bmatrix}1.10 & 0.40 & -0.70\end{bmatrix} - la troisième ligne, puisque l’indexation commence à zéro.

Pourquoi une consultation est une multiplication matricielle. Raschka souligne que la couche de plongement n’est qu’une implémentation plus efficace d’un codage one-hot suivi d’une multiplication dans une couche entièrement connectée. Vérifions :

[0010]E=[1.100.40−0.70].\begin{bmatrix}0 & 0 & 1 & 0\end{bmatrix} E = \begin{bmatrix}1.10 & 0.40 & -0.70\end{bmatrix} .

Le vecteur one-hot sélectionne exactement une ligne : le produit est la consultation. La conséquence est l’essentiel : puisque c’est une multiplication matricielle, la matrice de plongement est une couche ordinaire, optimisée par rétropropagation comme toute autre. Les vecteurs sont appris, non attribués.

Python

S'exécute dans votre navigateur. La première exécution télécharge l'environnement Python (~10 Mo), puis il est mis en cache.

Dans la figure, la matrice contient des entiers consécutifs plutôt que le EE ci-dessus, la ligne ii commençant à ii fois la dimension, ce qui rend la ligne renvoyée facile à vérifier d’un coup d’œil.

Interactif : une ligne, deux chemins

Toutes les autres lignes sont multipliées par zéro et jetées.

00123045670891011112131415016171819
Ligne renvoyée
[12, 13, 14, 15]
Multiplications, le produit
20
Multiplications, la recherche
0
Nombres conservés
4
Vecteurs modifiés par le mélange
0

Les deux chemins renvoient la même ligne, et c’est pourquoi une recherche est une opération différentiable légitime et non un raccourci autour d’une. Ce qui diffère, c’est la facture : le produit effectue 20 multiplications pour conserver 4 nombres, soit 5 fois le travail pour la même réponse. Appuyez sur la forme de GPT-2 et cela fait 38,597,376 multiplications pour 768 nombres. La recherche n’en fait aucune, et le gradient n’atteint de toute façon que la ligne sélectionnée. Le dernier affichage est l’autre moitié de la leçon : mélanger la séquence modifie 0 vecteurs. Les lignes voyagent avec leurs jetons, donc l’arithmétique en aval voit le même multiensemble de vecteurs quel que soit l’ordre, et la position doit être ajoutée délibérément plutôt qu’espérée.

À l’échelle de GPT-2 la matrice est 50,257×76850{,}257 \times 768 - 38 597 376 paramètres dans la seule couche de plongement, avant même qu’un bloc de transformeur n’existe. Raschka note également que l’architecture GPT-2 originale réutilise ces poids dans sa couche de sortie, une astuce dite de liage des poids ; les deux tenseurs ont la forme [50257, 768].

E. L’information que nous venons de détruire

Le pipeline construit jusqu’ici associe chaque token à un vecteur qui ne dépend que de quel token il est. Raschka qualifie ce plongement de déterministe et indépendant de la position, ce qui est bon pour la reproductibilité - mais cela signifie que « le chien mord l’homme » et « l’homme mord le chien » produisent le même ensemble de vecteurs.

D’ordinaire l’architecture rattraperait l’ordre. Pas ici : comme le note Raschka, le mécanisme d’auto-attention est lui-même indifférent à la position. L’attention calcule une moyenne pondérée sur les positions, et une moyenne se moque de l’ordre. Rien en aval ne remarquera la perte, la position doit donc être injectée délibérément.

Le remède est un second plongement, ajouté au premier :

inputi=token_embedding(ti)+positional_embedding(i).\text{input}_i = \text{token\_embedding}(t_i) + \text{positional\_embedding}(i) .

Raschka décrit deux grandes catégories :

  • Les plongements positionnels absolus, liés à des positions précises - chaque position de la séquence reçoit son propre plongement unique, ajouté au plongement de ce token pour transmettre sa localisation exacte.
  • Les plongements positionnels relatifs, qui encodent à quelle distance les tokens se trouvent les uns des autres plutôt que où chacun siège.

Il note que le choix dépend de l’application et des données, et consigne un fait précis à garder au clair : les modèles GPT d’OpenAI utilisent des plongements positionnels absolus optimisés pendant l’entraînement, plutôt que fixés ou prédéfinis comme les encodages positionnels de l’article originel sur le transformeur. Les vecteurs de position sont des paramètres appris, non une formule.

Addition, non concaténation. Le vecteur de position est ajouté au vecteur de token : les deux occupent donc les mêmes dimensions plutôt que d’être empilés en un vecteur plus long. La couche qui les lit doit par conséquent démêler « quel token » de « quelle position » à partir d’un unique vecteur sommé - ce qu’elle peut faire, car les deux plongements sont appris conjointement et peuvent s’organiser pour rendre la somme séparable.

F. Le pipeline complet

text  →  tokens  →  token IDs  →  token embeddings  →  +  positional  input embeddings\text{text} \;\rightarrow\; \text{tokens} \;\rightarrow\; \text{token IDs} \;\rightarrow\; \text{token embeddings} \;\xrightarrow{\;+\;\text{positional}\;} \text{input embeddings}

Le tenseur final a pour forme (lot, longueur de séquence, dimension de plongement), et c’est ce qu’un bloc de transformeur consomme. Deux des quatre flèches - la matrice de plongement et les plongements positionnels - sont apprises, ce qui signifie que la représentation du langage avec laquelle un modèle travaille n’est pas conçue à l’avance. Elle est découverte pendant l’entraînement, en même temps que tout le reste.

À retenir

  • Un vocabulaire associe des tokens uniques à des identifiants entiers ; la carte inverse ramène la sortie du modèle à du texte.
  • Le BPE n’a jamais besoin d’un token <|unk|> : les mots inconnus se décomposent en unités sous-lexicales ou en caractères, donc toute chaîne est encodable. GPT-2/3 utilisent un vocabulaire de 50 257 entrées avec <|endoftext|> à l’ID 50256.
  • Les identifiants sont des étiquettes, non des quantités - une couche de plongement transforme chacun en un vecteur appris en récupérant une ligne d’une matrice de poids.
  • Cette consultation est prouvablement one-hot × matrice, ce qui explique que la matrice de plongement s’entraîne par rétropropagation comme toute autre couche.
  • Le seul plongement de tokens de GPT-2 fait 38,6 millions de paramètres (50,257×76850{,}257 \times 768), et l’architecture originale les réutilise dans la couche de sortie.
  • Les plongements sont indépendants de la position et l’auto-attention est indifférente à la position, si bien que des plongements positionnels sont ajoutés pour restaurer l’ordre - absolus dans le cas de GPT, et appris pendant l’entraînement plutôt que fixés.

La suite

Nous disposons désormais d’un tenseur portant à la fois le contenu et la position. Ce qui le consomme est une pile de blocs de transformeur, dont le mécanisme central est développé dans L’attention et l’auto-attention et assemblé en architecture complète dans L’architecture du transformeur.

Références et lectures complémentaires

  • Sebastian Raschka, Build a Large Language Model (From Scratch), Manning, 2025· Bibliothèque de référence Kudos AI

Les œuvres protégées par le droit d’auteur sont citées à titre de référence uniquement et ne sont pas hébergées ici ; veuillez consulter l’éditeur pour y accéder.

Lecture associée

10 min de lectureConstruire un modèle de langue

L’architecture du transformeur

Assembler un GPT à partir de l’attention : projections multi-têtes, normalisation de couche déroulée à la main, pourquoi les connexions de raccourci sauvent le gradient, l’expansion x4 du réseau à propagation avant, et un décompte de paramètres qui reproduit exactement les 124 millions de GPT-2 small.

IA générativeApprentissage profondTraitement du langage naturel
8 min de lectureConstruire un modèle de langue

L’attention et l’auto-attention

Requêtes, clés et valeurs construites depuis la base : pourquoi l’attention existe, comment se calcule l’attention par produit scalaire mis à l’échelle, pourquoi elle est divisée par la racine carrée de la dimension, et comment fonctionne le masquage causal, avec chaque matrice calculée et vérifiée.

IA générativeApprentissage profondTraitement du langage naturel
8 min de lectureConstruire un modèle de langue

Le préentraînement et l’affinage

Comment la prédiction du mot suivant transforme du texte non annoté en supervision, pourquoi l’entropie croisée n’est que l’opposé de la log-probabilité moyenne, ce que mesure vraiment la perplexité, et pourquoi un modèle qui complète le texte avec fluidité ne sait toujours pas suivre une instruction.

IA générativeApprentissage profond
← Retour à tous les articles