La tokenisation BPE.
Nous voici au début de la dernière partie du parcours : suivre le trajet complet d’une vraie requête, du champ de saisie jusqu’à l’écran. Et la toute première étape nous ramène à la leçon 01, la tokenisation. Depuis le début, notre laboratoire découpe le texte caractère par caractère. C’était parfait pour comprendre, mais aucun modèle en production ne fait ainsi. GPT, Llama et les autres découpent le texte en sous-mots, avec un algorithme appelé BPE. C’est lui qui décide ce que « coûte » votre prompt, et combien de texte tient dans la fenêtre de contexte.
Ni caractères, ni mots entiers
Découper par caractère donne un tout petit vocabulaire (111 tokens chez nous) et ne laisse aucun mot inconnu. Mais les séquences deviennent très longues. Or, depuis la leçon 06, nous savons que l’attention coûte n × n, et que la fenêtre de contexte se compte en tokens : chaque caractère gaspille une place précieuse.
Découper par mot donne des séquences courtes, mais un vocabulaire gigantesque. En français, « parler », « parlait », « parlerions » et « reparlons » seraient quatre tokens sans lien. Et que faire d’un mot jamais vu, d’une faute de frappe ou d’un nom propre ? Il faudrait un token « inconnu », qui efface l’information.
La solution retenue par tous les grands modèles est entre les deux : les sous-mots (subwords). Les mots fréquents deviennent un seul token, les mots rares se découpent en morceaux connus, et dans le pire des cas on retombe sur les caractères. Aucun texte n’est jamais impossible à écrire.
L’algorithme BPE : fusionner les voisins les plus fréquents
Le BPE (Byte Pair Encoding) vient à l’origine de la compression de données. Il a été adapté au traitement du langage en 2016, puis adopté par GPT-2. Son principe tient en quelques lignes :
1. partir des caractères : c’est le vocabulaire de départ
2. compter toutes les paires de tokens voisins dans le corpus
3. fusionner la paire la plus fréquente en un nouveau token
4. recommencer jusqu’à la taille de vocabulaire voulueNotre laboratoire entraîne un vrai BPE sur le texte de Jules Verne. La première fusion apprise est « espace + d » (3 046 fois dans le texte : « de », « des », « du »…), puis « espace + l », « es » et « en ». Fusion après fusion apparaissent « ␣de », « ait », « était », « tion », puis des mots entiers comme « ␣Fogg », et même presque tout « Passepartout » : « P » + « assepartout ».
Ce que l’on garde à la fin, c’est la liste ordonnée des fusions (merges) et le vocabulaire qui en découle. Ensemble, ils forment le tokenizer. Comme nous l’avons vu à la leçon 02, ce fichier de tokenizer doit toujours accompagner les poids du modèle : changer de tokenizer, c’est changer tous les identifiants numériques, ce qui rendrait la table d’embeddings inutilisable.
Encoder un texte : rejouer les fusions
Pour découper un nouveau texte, on commence par le couper en morceaux, en gardant l’espace collée au mot qui suit : c’est la pré-tokenisation. Puis, dans chaque morceau, on applique les fusions apprises, dans l’ordre où elles ont été apprises.
Pourquoi ce découpage grossier, appliqué dès l’entraînement ? Sans barrière entre les mots, les paires très fréquentes à cheval sur deux mots finiraient par fusionner : une fin de pluriel suivie de « le », par exemple « s␣le », deviendrait un token à part entière, absurde, qui empêcherait de reconnaître chacun des deux mots. Les tokenizers de la famille GPT découpent donc d’abord le texte en morceaux : chaque mot avec l’espace qui le précède, la ponctuation à part. En attachant l’espace au début du mot qui suit (« ␣jours »), chaque mot reste autonome, et les espaces sont conservées pour reconstruire la phrase exacte au décodage.
␣ j o u r s
␣ j ou r s fusion « ou »
␣ j our s fusion « our »
␣j our s fusion « ␣j »
␣j ours fusion « ours »Avec 300 fusions, la phrase « Le tour du monde en quatre-vingts jours » passe de 39 caractères à 17 tokens : plus de deux fois moins de positions à traiter, pour un vocabulaire de 411 tokens au lieu de 111. Plus on ajoute de fusions, plus les séquences raccourcissent, mais plus la table d’embeddings (leçon 03) et la tête de langage (leçon 08) grossissent : c’est le compromis de la taille du vocabulaire.
Les vrais tokenizers : octets et tiktoken
GPT-2 ne part pas des caractères mais des octets (bytes). En informatique, avec l’encodage standard UTF-8, tout caractère est représenté par une suite de 1 à 4 octets, et chaque octet est un simple nombre entre 0 et 255.
a → 1 octet : 97
é → 2 octets : 195, 169
🙂 → 4 octets : 240, 159, 153, 130En partant de ces 256 octets de base plutôt que d’une liste finie de caractères, le tokenizer peut écrire n’importe quel texte, dans n’importe quelle langue, avec des emojis ou des symboles. C’est le BPE au niveau des octets (byte-level BPE) : il n’existe plus aucun caractère inconnu, contrairement à notre laboratoire qui refuse « 🙂 ». Revers de la médaille, qui reviendra à la leçon 16 : un caractère de plusieurs octets peut se retrouver coupé entre deux tokens.
GPT-2 : 50 257 tokens (256 octets + 50 000 fusions + 1 token spécial)
cl100k_base : environ 100 000 tokens (GPT-4, GPT-3.5)
o200k_base : environ 200 000 tokens (GPT-4o)OpenAI publie sa bibliothèque de tokenisation, tiktoken, et propose une page pour voir le découpage de n’importe quel texte. Ordre de grandeur souvent cité pour l’anglais : un token vaut environ 4 caractères, soit à peu près trois quarts de mot. Le français, moins présent dans les données d’entraînement des tokenizers, demande en général un peu plus de tokens pour le même contenu. D’autres familles d’algorithmes existent, comme WordPiece (BERT) ou Unigram avec SentencePiece, mais l’idée reste la même.
Les tokens spéciaux
À côté des tokens appris, le vocabulaire contient des tokens spéciaux ajoutés à la main : le token de fin de texte de GPT-2, <|endoftext|>, ou les marqueurs de rôles du chat template de la leçon 12. Ils ne sont jamais produits par la tokenisation d’un texte ordinaire.
C’est une question de sécurité : si un utilisateur tape littéralement « <|endoftext|> » dans son message, le tokenizer doit le découper comme du texte normal, et non comme le vrai token spécial. Sinon, il pourrait faire croire au modèle qu’une conversation se termine ou qu’un autre rôle prend la parole.
Pourquoi c’est important au quotidien
Les tokens sont l’unité de compte de tout l’écosystème. Les API facturent au token, en entrée et en sortie. La fenêtre de contexte et le paramètre max_tokens se comptent en tokens, pas en mots. Et la vitesse de génération, que nous mesurerons à la leçon 15, se compte en tokens par seconde.
La tokenisation explique aussi des comportements étranges. Un modèle voit « ␣chat » et « chat » comme deux tokens différents. Il ne voit pas non plus les lettres à l’intérieur d’un token fusionné. Le modèle ne reçoit pas les 10 lettres de « strawberry », mais seulement quelques identifiants numériques, ce qui l’empêche parfois de compter ses « r ».
strawberry → st | raw | berry 3 tokens
␣strawberry → ␣strawberry 1 seul token
12345 → 123 | 45
123456 → 123 | 456Les nombres sont regroupés par trois chiffres depuis la gauche, sans tenir compte de leur valeur. Dans 12345, le dernier token « 45 » contient les dizaines et les unités ; dans 123456, le dernier token « 456 » contient les centaines, les dizaines et les unités. D’un nombre à l’autre, les unités, dizaines et centaines ne tombent jamais au même endroit, ce qui complique le calcul.
Observez par vous-même
Dans le laboratoire, tapez Le tour du monde en quatre-vingts jours. Avec 0 fusion, vous retrouvez le découpage par caractère de la leçon 01 ; avec 300, la phrase tient en 17 tokens. Cliquez sur un morceau pour rejouer ses fusions une à une.
Essayez ensuite Passepartout, cité des centaines de fois dans le roman : avec 300 fusions, il ne fait plus que deux tokens. Puis un mot rare, comme « anticonstitutionnellement », qui reste découpé en nombreux morceaux.
Les limites de notre exemple
Notre BPE part de 111 caractères au lieu de 256 octets, n’apprend que 300 fusions au lieu de dizaines de milliers, et sur un seul roman. Les leçons précédentes continuent d’utiliser le découpage par caractère : un modèle doit toujours être entraîné et utilisé avec le même tokenizer.
Le texte est maintenant découpé en tokens. Mais dans une vraie application, l’utilisateur ne tape pas un texte brut envoyé tel quel au modèle : son message part dans une requête HTTP, rejoint un historique de conversation et des instructions système, puis est mis en forme par le chat template. C’est le sujet de la prochaine leçon : du champ de saisie au modèle.
Les LLM découpent le texte en sous-mots : compromis entre caractères (séquences longues) et mots (vocabulaire énorme, mots inconnus). Le BPE part d’un vocabulaire de base (caractères, ou 256 octets pour le byte-level BPE de GPT-2), fusionne itérativement la paire voisine la plus fréquente, et conserve la liste ordonnée des fusions. L’encodage pré-tokenise le texte (espace attachée au mot suivant), puis rejoue les fusions dans l’ordre.
Vocabulaires : GPT-2 = 50 257 (256 + 50 000 fusions + <|endoftext|>), cl100k_base ≈ 100 000, o200k_base ≈ 200 000 (tiktoken). En anglais, 1 token ≈ 4 caractères. Les tokens spéciaux ne sont jamais produits à partir du texte utilisateur. Facturation, fenêtre de contexte, max_tokens et vitesse se comptent en tokens.
tiktoken · OpenAI, GitHub ↗
Byte-Pair Encoding tokenization · Hugging Face ↗