Tu ouvres Python, tu tapes "Bonjour le monde", et tu te dis : « Mon modèle va comprendre ça, non ? » Sauf que non. Un réseau de neurones ne voit pas de lettres, pas de mots, pas de phrases. Il voit des nombres. Des tenseurs de flottants. Alors comment passer d'une chaîne de caractères — un truc fondamentalement humain — à une séquence de nombres qu'un GPU peut multiplier ? C'est exactement le problème que la tokenization résout. Et si tu te trompes à cette étape, tout le reste de ton LLM s'effondre.
Key Idea
La tokenization est le pont entre le texte humain et les nombres que manipule un réseau de neurones. Le choix de la granularité (caractère, mot, sous-mot) détermine la taille du vocabulaire, la capacité de généralisation, et la performance du modèle.
Key Idea
Il n'existe pas de tokenization « parfaite » — chaque approche est un compromis entre taille du vocabulaire, longueur des séquences, et couverture des langues.
Pourquoi un ordinateur ne peut pas « lire »
Situation
Imagine que tu donnes la phrase « Le chat dort » à un programme C. Ce programme voit : 4C 65 20 63 68 61 74 20 64 6F 72 74 — des octets hexadécimaux. Pas de sémantique, pas de grammaire. Juste des nombres bruts. Comment un réseau de neurones peut-il apprendre que « chat » et « chaton » sont liés si tout ce qu'il voit, ce sont des octets arbitraires ?
Un ordinateur stocke du texte sous forme de séquences d'octets. Chaque caractère est encodé selon un standard (ASCII, UTF-8, UTF-16…). Mais ces octets n'ont aucune structure sémantique.
Tokenization
Processus de découpage d'un texte brut en unités discrètes appelées tokens, qui sont ensuite converties en identifiants numériques (token IDs) pour être traitées par un modèle.
Sans tokenization, tu ne peux pas construire de table d'embedding, tu ne peux pas calculer d'attention, tu ne peux rien faire.
L'approche naïve : tokenization par caractère
Situation
Tu te dis : « Simple ! Je découpe lettre par lettre. » Avec un vocabulaire de ~256 caractères (ASCII étendu), c'est compact. Mais la phrase « Le chat dort sur le tapis » devient une séquence de 26 tokens. Et chaque token ne porte presque aucune information sémantique. Le modèle doit apprendre que c, h, a, t ensemble forment un concept.
Tokenization par caractère (Character-level)
Chaque caractère individuel (lettre, chiffre, ponctuation, espace) constitue un token. Le vocabulaire est très petit (~256 pour UTF-8 bytes), mais les séquences sont très longues.
Avantages :
Vocabulaire minuscule (~256 tokens)
Zéro token inconnu ([UNK]) — tout caractère est représentable
Fonctionne pour n'importe quelle langue
Inconvénients :
Séquences extrêmement longues (un mot de 5 lettres = 5 tokens)
Le modèle doit apprendre la composition des mots lui-même
Coût computationnel élevé (l'attention est O(n²) sur la longueur)
Pour un Transformer avec une fenêtre de contexte de 1024 tokens, tu ne pourrais traiter que ~1024 caractères — soit environ 200 mots. C'est très peu.
L'approche classique : tokenization par mot
Situation
OK, tu passes à l'autre extrême : tu découpes par mot (split sur les espaces et la ponctuation). « Le chat dort. » → ["Le", "chat", "dort", "."]. Chaque token porte du sens. Mais que se passe-t-il quand le modèle rencontre « chatouiller » ? Ce mot n'est pas dans ton vocabulaire de 50 000 mots. Token inconnu. Information perdue.
Tokenization par mot (Word-level)
Chaque mot (délimité par des espaces ou de la ponctuation) constitue un token. Le vocabulaire est grand (50K–200K+), et les mots hors vocabulaire deviennent [UNK].
import re
text = "Le chat dort sur le tapis."
tokens = re.findall(r"\w+|[^\w\s]", text)
print(tokens)
# ['Le', 'chat', 'dort', 'sur', 'le', 'tapis', '.']
Avantages :
Chaque token est sémantiquement riche
Séquences courtes
Inconvénients :
Vocabulaire énorme (la langue anglaise a ~170 000 mots courants)
Problème des mots hors vocabulaire (OOV — Out Of Vocabulary)
Les formes fléchies (mange, manges, mangeons, mangeaient) sont des tokens séparés
Catastrophique pour les langues agglutinantes (turc, finnois, allemand)
Le compromis : tokenization par sous-mot (subword)
Situation
Tu veux le meilleur des deux mondes : des tokens assez longs pour porter du sens, mais assez flexibles pour décomposer les mots rares. Le mot « incroyablement » pourrait devenir ["in", "croy", "able", "ment"]. Chaque morceau est fréquent, et le modèle peut recombiner ces morceaux pour comprendre des mots jamais vus.
Tokenization par sous-mot (Subword tokenization)
Approche qui découpe le texte en unités intermédiaires entre le caractère et le mot. Les mots fréquents restent entiers, les mots rares sont décomposés en sous-unités fréquentes. C'est l'approche dominante dans tous les LLM modernes.
Granularité :
Caractère ◄──────────────────────────────────────────► Mot
| |
| Vocabulaire petit Sous-mot Vocabulaire grand
| Séquences longues ◄── SWEET SPOT ──► Séquences courtes
| Pas d'OOV Beaucoup d'OOV
Les trois grandes familles d'algorithmes de sous-mots :
Algorithme
Utilisé par
Approche
BPE (Byte Pair Encoding)
GPT-2, GPT-3, GPT-4, LLaMA
Bottom-up : fusionne les paires les plus fréquentes
WordPiece
BERT, DistilBERT
Similaire à BPE mais maximise la vraisemblance
Unigram
T5, ALBERT, XLNet (via SentencePiece)
Top-down : part d'un grand vocabulaire et élague
Le compromis de la taille du vocabulaire
Situation
Tu dois choisir la taille de ton vocabulaire. 256 tokens ? 32 000 ? 100 000 ? 256 000 ? Ce choix a des conséquences massives sur la mémoire, la vitesse, et la qualité du modèle. GPT-2 utilise 50 257 tokens. LLaMA-2 utilise 32 000. GPT-4 utilise ~100 000. Pourquoi ces différences ?
Taille du vocabulaire (Vocabulary size)
Le nombre total de tokens uniques que le tokenizer peut produire. Détermine la taille de la matrice d'embedding (vocab_size × embedding_dim) et de la couche de sortie (head).
Le compromis fondamental :
Petit vocabulaire (ex: 8K) Grand vocabulaire (ex: 100K)
───────────────────────── ──────────────────────────────
✓ Matrice d'embedding petite ✗ Matrice d'embedding énorme
✓ Moins de paramètres ✗ Plus de paramètres
✗ Séquences longues (plus de tokens)✓ Séquences courtes (moins de tokens)
✗ Tokens peu informatifs ✓ Tokens riches en sens
✗ Lent à l'inférence (plus d'étapes)✓ Rapide à l'inférence
Voici les tailles de vocabulaire des modèles populaires :
Modèle
Vocab size
Algorithme
GPT-2
50 257
BPE (byte-level)
GPT-3/3.5
50 257
BPE (byte-level)
GPT-4
~100 000
BPE (cl100k_base)
BERT
30 522
WordPiece
LLaMA 1/2
32 000
BPE (SentencePiece)
LLaMA 3
128 256
BPE (tiktoken)
Mistral 7B
32 000
BPE (SentencePiece)
T5
32 100
Unigram (SentencePiece)
Unicode et UTF-8 : le socle invisible
Situation
Tu entraînes ton modèle sur du texte anglais, tout va bien. Puis tu lui donnes du chinois : « 你好世界 ». Ou de l'arabe : « مرحبا بالعالم ». Ou des emojis : « 🤖💬 ». Soudain, ton tokenizer produit des résultats bizarres. Pourquoi ? Parce que tu n'as pas compris comment le texte est réellement stocké en mémoire.
Unicode
Standard universel qui attribue un code point unique à chaque caractère de toutes les écritures humaines. Actuellement ~150 000 caractères définis. Exemple : « A » = U+0041, « é » = U+00E9, « 你 » = U+4F60, « 🤖 » = U+1F916.
UTF-8
Encodage de Unicode en octets. Utilise 1 à 4 octets par caractère. Compatible ASCII (les 128 premiers caractères = 1 octet). C'est l'encodage dominant sur le web (~98% des pages).
Caractère Code Point UTF-8 (octets) Nb octets
───────── ────────── ────────────────── ─────────
A U+0041 41 1
é U+00E9 C3 A9 2
中 U+4E2D E4 B8 AD 3
🤖 U+1F916 F0 9F A4 96 4
Le BPE de GPT-2 travaille au niveau des octets (byte-level BPE). Le vocabulaire de base contient les 256 valeurs d'octets possibles (0x00 à 0xFF). Tout texte, dans n'importe quelle langue, peut être représenté — il n'y a jamais de token [UNK].
Tu veux comprendre concrètement ce qui se passe quand tu appelles tokenizer.encode("Hello world") avec le tokenizer de GPT-2. Quelles sont les étapes internes ? Comment le texte est-il découpé ?
Le tokenizer de GPT-2 suit trois étapes :
Étape 1 : Pré-tokenization par regex
Avant d'appliquer BPE, GPT-2 découpe le texte en « mots » grossiers avec une expression régulière :
Note que les espaces sont attachés au début du mot suivant ( world, I, fine). C'est un choix de design important.
Étape 2 : Conversion en octets
Chaque pré-token est converti en sa séquence d'octets UTF-8, puis chaque octet est mappé vers un caractère Unicode imprimable (pour éviter les caractères de contrôle).
Étape 3 : Application des fusions BPE
Les paires d'octets/tokens sont fusionnées selon une liste ordonnée de ~50 000 règles de fusion, apprises lors de l'entraînement du tokenizer.
Utiliser le tokenizer GPT-2 avec tiktoken
import tiktoken
enc = tiktoken.get_encoding("gpt2")
text = "Bonjour le monde! 🤖"
token_ids = enc.encode(text)
print(f"Token IDs : {token_ids}")
for tid in token_ids:
token_bytes = enc.decode_single_token_raw(tid)
print(f" ID {tid:>6} → {token_bytes}")
print(f"Nombre de tokens : {len(token_ids)}")
decoded = enc.decode(token_ids)
assert decoded == text
Visualiser la tokenization
Situation
Tu veux voir concrètement comment différents textes sont découpés. Est-ce que « anticonstitutionnellement » est un seul token ou plusieurs ? Et « 🇫🇷 » ? Et du code Python ?
Comparer la tokenization de différents textes
import tiktoken
enc = tiktoken.get_encoding("cl100k_base") # GPT-4
examples = [
"Hello world",
"Bonjour le monde",
"anticonstitutionnellement",
"def fibonacci(n):\n return n if n <= 1 else fibonacci(n-1) + fibonacci(n-2)",
"🇫🇷 Liberté, égalité, fraternité",
"SELECT * FROM users WHERE id = 1;",
"2024年は素晴らしい年です",
]
for text in examples:
tokens = enc.encode(text)
token_strs = [enc.decode([t]) for t in tokens]
print(f"\n'{text}'")
print(f" Tokens ({len(tokens)}): {token_strs}")
Quelques observations importantes :
Les mots anglais courants sont souvent un seul token : "Hello" → 1 token
Les mots français longs sont découpés : "anticonstitutionnellement" → ~5-7 tokens
Le code est tokenisé efficacement car les corpus d'entraînement contiennent beaucoup de code
Les emojis composés (drapeaux) peuvent prendre 4-7 tokens
Le japonais/chinois utilise plus de tokens par « mot » car ces langues sont sous-représentées dans les corpus d'entraînement
Outils en ligne pour explorer la tokenization
Situation
Tu veux rapidement tester comment un texte est tokenisé sans écrire de code. Plusieurs outils en ligne existent.
Les outils les plus utiles :
OpenAI Tokenizer (platform.openai.com/tokenizer) : visualise la tokenization avec les encodages GPT-3.5/4. Chaque token est coloré différemment.
Tiktokenizer (tiktokenizer.vercel.app) : interface web pour tiktoken avec tous les encodages disponibles.
HuggingFace Tokenizer Playground : teste n'importe quel tokenizer de la Hub HuggingFace.
Le problème de la fertilité et des nombres
Situation
Tu construis un chatbot multilingue. Tes utilisateurs français se plaignent que les réponses sont tronquées — le modèle atteint sa limite de tokens plus vite qu'en anglais. Et quand tu demandes « Combien font 1234 + 5678 ? », le modèle se trompe. Deux problèmes, une même cause : la tokenization.
Fertilité (Fertility)
Le ratio entre le nombre de tokens produits et le nombre de mots dans le texte original. Une fertilité de 1.5 signifie que chaque mot produit en moyenne 1.5 tokens.
Mesurer la fertilité par langue
import tiktoken
enc = tiktoken.get_encoding("cl100k_base")
texts = {
"Anglais": "The quick brown fox jumps over the lazy dog",
"Français": "Le rapide renard brun saute par-dessus le chien paresseux",
"Japonais": "素早い茶色の狐が怠惰な犬を飛び越える",
}
for lang, text in texts.items():
tokens = enc.encode(text)
words = text.split()
fertility = len(tokens) / max(len(words), 1)
print(f"{lang:>10}: {len(words):>3} mots → {len(tokens):>3} tokens "
f"(fertilité: {fertility:.2f})")
Pour les nombres, la tokenization détruit la structure numérique :
import tiktoken
enc = tiktoken.get_encoding("cl100k_base")
for num in ["42", "123", "1234", "12345", "123456789"]:
tokens = enc.encode(num)
token_strs = [enc.decode([t]) for t in tokens]
print(f"{num:>12} → {token_strs}")
1234 peut devenir ["123", "4"] — des morceaux arbitraires sans rapport avec la valeur numérique. C'est pourquoi les LLM utilisent des outils externes pour les calculs.
Let's build the GPT Tokenizer — Andrej Karpathy
Cette vidéo de 2h d'Andrej Karpathy est la référence pour comprendre la tokenization de A à Z. Il construit un tokenizer BPE from scratch en Python et explique tous les choix de design de GPT-2/GPT-4.
Summary
Concept
Description
Tokenization
Découpage du texte en unités discrètes (tokens) pour un modèle
Character-level
1 caractère = 1 token. Petit vocab, longues séquences
Word-level
1 mot = 1 token. Grand vocab, problème OOV
Subword (BPE, WordPiece, Unigram)
Compromis optimal. Mots fréquents entiers, mots rares décomposés
Byte-level BPE
Travaille sur les octets UTF-8. Zéro [UNK]. Utilisé par GPT-2/3/4
Taille du vocabulaire
Compromis mémoire vs longueur de séquence. 32K–128K typique
Fertilité
Ratio tokens/mots. Varie selon la langue
UTF-8
Encodage Unicode en 1-4 octets. Base du byte-level BPE
Références
Sennrich, R., Haddow, B., & Birch, A. (2016). Neural Machine Translation of Rare Words with Subword Units. ACL 2016. arXiv:1508.07909
Kudo, T., & Richardson, J. (2018). SentencePiece: A simple and language independent subword tokenizer and detokenizer for Neural Text Processing. EMNLP 2018. arXiv:1808.06226
Radford, A., Wu, J., Child, R., Luan, D., Amodei, D., & Sutskever, I. (2019). Language Models are Unsupervised Multitask Learners. OpenAI Technical Report.
Karpathy, A. (2024). Let's build the GPT Tokenizer. YouTube
Raschka, S. (2024). Build a Large Language Model (From Scratch). Manning Publications. Chapitre 2.