Avant de lire un texte, un LLM le découpe en tokens : des mots entiers pour les plus fréquents, des morceaux de mots pour les autres. Le BPE (Byte Pair Encoding) construit ce vocabulaire en fusionnant pas à pas les paires de symboles les plus fréquentes. Ce découpage explique la facturation au token et la longueur maximale des documents.
Un sténographe qui invente une abréviation pour chaque groupe de lettres qu'il écrit souvent. Après quelques centaines d'abréviations, les mots courants s'écrivent en un ou deux signes.
Chaque mot du corpus est découpé en caractères, avec un marqueur de fin de mot. Le vocabulaire de départ ne contient que ces caractères.
On compte toutes les paires de symboles voisins et on fusionne la plus fréquente en un nouveau token. Sur les avis, les premières fusions sont le point en fin de mot, puis « e » et « t » en fin de mot, puis « on ».
Chaque fusion ajoute un token au vocabulaire. Les LLM actuels en comptent de quelques dizaines de milliers à plus de 200 000. Un nouveau texte est découpé en rejouant les fusions dans le même ordre.
Le corpus qu'on veut faire analyser par un LLM. Chaque avis sera envoyé au modèle, et l'API facture chaque token lu et produit.
Le code Python apprend 200 fusions sur les avis et compte les symboles avant et après : leur nombre est divisé par plus de trois. Un mot fréquent du corpus tient en quelques tokens, un mot jamais vu en beaucoup plus.
Le budget d'un traitement LLM se calcule en tokens : consigne, texte et réponse, multipliés par le nombre de documents. On mesure sur un échantillon avec le tokeniseur du modèle retenu, car chaque modèle a le sien.
Le BPE n'a presque pas d'hyperparamètres. Noms donnés pour R (tokenizers.bpe) et pour le code Python de la fiche.
Plus le vocabulaire est grand, moins il faut de tokens par texte, mais plus le modèle a de vecteurs à apprendre. C'est le compromis central.
On découpe d'abord en mots, sur les espaces et la ponctuation, et les fusions ne franchissent jamais cette frontière. Le code Python marque la fin de mot par « _ ».
Le BPE sur octets, utilisé depuis GPT-2, part des 256 valeurs d'octet : aucun caractère n'est jamais inconnu, emojis compris. Un BPE sur caractères, comme celui du code, bute sur un caractère absent du corpus.
# Coût des requêtes LLM : tokenisation BPE en R
library(tokenizers.bpe)
avis <- read.csv("avis_clients.csv")
textes <- tolower(avis$texte)
# Apprentissage d'un vocabulaire de 300 tokens sur les avis
set.seed(42)
modele <- bpe(textes, vocab_size = 300,
model_path = file.path(tempdir(), "bpe_avis.model"))
# Découpage de deux avis en tokens
print(bpe_encode(modele, x = textes[1:2], type = "subwords"))
# Nombre de tokens pour tout le corpus
ids <- bpe_encode(modele, x = textes, type = "ids")
nb_tokens <- sum(lengths(ids))
cat("Caractères :", sum(nchar(textes)), "| tokens :", nb_tokens, "\n")
cat("Caractères par token :", round(sum(nchar(textes)) / nb_tokens, 2), "\n")
# Un mot absent du corpus se découpe en plus petits morceaux
print(bpe_encode(modele, x = "réclamation", type = "subwords"))
# Coût des requêtes LLM : tokenisation BPE en Python
import re
import pandas as pd
from collections import Counter
avis = pd.read_csv("avis_clients.csv")
# Chaque mot devient une suite de caractères séparés, "_" marque la fin du mot
mots = Counter(avis["texte"].str.lower().str.split().explode())
corpus = {" ".join(m) + " _": n for m, n in mots.items()}
avant = sum(len(s.split()) * n for s, n in corpus.items())
def fusionner(s, a, b): # remplace chaque paire "a b" par le token "ab"
return re.sub(r"(?<!\S)" + re.escape(a + " " + b) + r"(?!\S)", a + b, s)
fusions = []
for etape in range(200): # 200 fusions = 200 nouveaux tokens dans le vocabulaire
paires = Counter()
for s, n in corpus.items():
for a, b in zip(s.split(), s.split()[1:]):
paires[a, b] += n
a, b = max(paires, key=paires.get) # la paire la plus fréquente
fusions.append((a, b))
corpus = {fusionner(s, a, b): n for s, n in corpus.items()}
print("10 premières fusions :", [a + b for a, b in fusions[:10]])
apres = sum(len(s.split()) * n for s, n in corpus.items())
print("Tokens pour les 1 500 avis : avant", avant, "| après", apres)
mot, nouveau = " ".join("remboursement") + " _", " ".join("réclamation") + " _"
for a, b in fusions: # un texte neuf se découpe en rejouant les fusions dans l'ordre
mot, nouveau = fusionner(mot, a, b), fusionner(nouveau, a, b)
print("Mot du corpus :", mot.split(), "| mot jamais vu :", nouveau.split())
Un token est souvent un mot court ou un morceau de mot. Pour l'anglais, OpenAI indique environ 4 caractères par token, soit à peu près trois quarts de mot. Le français demande en général un peu plus de tokens pour le même contenu, car les tokeniseurs sont construits sur des corpus majoritairement anglais.
Parce qu'ils ne voient pas les lettres. Un mot arrive au modèle sous forme d'un ou deux tokens, sans accès direct aux caractères qui les composent. Compter les lettres oblige le modèle à reconstituer une information que le découpage a masquée.
Trois façons voisines de construire un vocabulaire de sous-mots. BPE fusionne la paire la plus fréquente ; WordPiece, utilisé par BERT, retient la fusion qui augmente le plus la vraisemblance du corpus ; SentencePiece est une bibliothèque qui applique BPE ou un modèle unigramme directement au texte brut, espaces compris.
Le modèle de langage reçoit une suite de tokens et prédit le suivant. Son tokeniseur fait partie du modèle.
Voir la fiche → l'étape d'aprèsChaque token, puis chaque texte, devient un vecteur de nombres qui porte le sens.
Voir la fiche → le découpage classiqueCompte des mots entiers, sans sous-mots. Suffit pour la recherche et la classification simples.
Voir la fiche →Dataistudio forme les équipes au machine learning et à l'IA, sur des cas concrets.
Nous utilisons des cookies de mesure d'audience et de suivi publicitaire pour comprendre la fréquentation du site et l'efficacité de nos annonces. Rien n'est déposé sans votre accord. En savoir plus