Un modèle de langage ne lit pas des phrases : il manipule des nombres. La première étape de tout projet NLP consiste donc à transformer le texte en une suite d’identifiants de tokens issus d’un vocabulaire fixe.
Normaliser le texte
La normalisation rend comparables des écritures différentes d’un même mot :
import re
import unicodedata
def normaliser(texte):
texte = texte.lower() # casse
texte = unicodedata.normalize("NFD", texte) # sépare lettres et accents
texte = "".join(c for c in texte if unicodedata.category(c) != "Mn")
texte = re.sub(r"\s+", " ", texte).strip() # espaces multiples
return texte
print(normaliser(" L'Été est LÀ ! ")) # "l'ete est la !"
Chaque choix a un coût : retirer les accents confond « pêche » et « pèche », passer en minuscules efface la différence entre « Apple » et « apple ». Pour un classifieur TF-IDF, une normalisation forte aide souvent ; pour un modèle Transformer pré-entraîné, on ne normalise presque pas : son tokenizer a été conçu pour le texte brut. Règle essentielle : le même prétraitement doit être appliqué à l’entraînement et en production.
Tokeniser en mots
L’approche la plus simple découpe sur les caractères alphanumériques :
def tokeniser(texte):
return re.findall(r"\w+", normaliser(texte))
print(tokeniser("Le colis n'est pas arrivé."))
# ['le', 'colis', 'n', 'est', 'pas', 'arrive']
Des bibliothèques comme spaCy gèrent finement les élisions, les nombres ou les dates, et ajoutent la lemmatisation (« arrivées » → « arriver »). Le défaut des tokens-mots : le vocabulaire explose et tout mot absent de l’entraînement (faute de frappe, nom propre, néologisme) devient inconnu.
Les sous-mots et BPE
Les modèles modernes découpent en sous-mots : les mots fréquents restent entiers, les mots rares sont décomposés (« anticonstitutionnellement » → « anti », « constitution », « nellement »). Il n’y a plus de mot inconnu.
L’algorithme BPE (Byte Pair Encoding) apprend ce vocabulaire :
- partir de chaque mot découpé en caractères (ou en octets, pour couvrir tout Unicode) ;
- compter les paires adjacentes de symboles dans le corpus, pondérées par la fréquence des mots ;
- fusionner la paire la plus fréquente en un nouveau symbole ;
- recommencer jusqu’à la taille de vocabulaire souhaitée (souvent 32 000 à 200 000 tokens).
from collections import Counter
corpus = {("b", "a", "s"): 5, ("b", "a", "l"): 3, ("v", "a", "s"): 2}
paires = Counter()
for mot, freq in corpus.items():
for a, b in zip(mot, mot[1:]):
paires[(a, b)] += freq
print(paires.most_common(1)) # [(('a', 's'), 7)] -> on fusionne "a" + "s"
WordPiece (BERT) et Unigram (SentencePiece) suivent la même idée avec d’autres critères de fusion.
Les tokenizers des modèles
Avec Hugging Face, chaque modèle fournit son tokenizer :
from transformers import AutoTokenizer
tok = AutoTokenizer.from_pretrained("camembert-base")
enc = tok("Le colis n'est pas arrivé.")
print(enc["input_ids"]) # identifiants
print(tok.convert_ids_to_tokens(enc["input_ids"])) # morceaux de mots
Le nombre de tokens compte : il détermine la limite de contexte d’un modèle et le coût des API de LLM, facturées au token. Le français produit en général un peu plus de tokens que l’anglais pour un même contenu.
À retenir
Normalisation cohérente, tokenisation adaptée au modèle, et conscience du nombre de tokens : ces bases conditionnent tout le reste, du TF-IDF aux LLM.
Exercices
Les solutions et les tests restent sur le serveur. Dans l'espace étudiant, ton code est exécuté et corrigé, et le tuteur donne des indices avant la solution.
Normaliser et tokeniser
Écrivez
normaliser(texte)(minuscules, suppression des accents viaunicodedata, espaces multiples réduits et bords supprimés) ettokeniser(texte)qui renvoie la liste des mots (re.findall(r"\w+", ...)) du texte normalisé.import re import unicodedata def normaliser(texte): # TODO pass def tokeniser(texte): # TODO passUne étape de BPE
Le corpus est un dictionnaire
{tuple_de_symboles: fréquence}. Écrivezcompter_paires(corpus)qui renvoie unCounterdes paires adjacentes pondérées par la fréquence, etfusionner(paire, corpus)qui renvoie un nouveau corpus où chaque occurrence adjacente de la paire est remplacée par la concaténation des deux symboles.from collections import Counter def compter_paires(corpus): # TODO pass def fusionner(paire, corpus): # TODO passPourquoi des sous-mots ?
Quel est le principal avantage d’une tokenisation en sous-mots (BPE) par rapport à une tokenisation en mots ?
- Elle supprime la ponctuation
- Elle évite les mots inconnus en décomposant les mots rares en morceaux connus, avec un vocabulaire de taille maîtrisée
- Elle produit toujours moins de tokens
- Elle traduit automatiquement le texte
Quiz de fin de leçon
Pourquoi éviter de retirer les accents avant un modèle Transformer pré-entraîné ?
- Parce que c'est trop lent
- Parce que son tokenizer et ses poids ont été appris sur du texte brut : modifier le texte dégrade les représentations
- Parce que les accents sont des tokens spéciaux
- Parce que cela augmente le coût
Dans BPE, quelle paire fusionne-t-on à chaque étape ?
- La première paire du corpus
- La paire adjacente la plus fréquente
- La paire la plus rare
- Une paire tirée au hasard
Pourquoi compter les tokens d’un prompt avant d’appeler un LLM ?
- Pour corriger l'orthographe
- Parce que le coût et la limite de contexte s'expriment en tokens
- Parce que le modèle ne lit que les 100 premiers mots
- Pour choisir la température
La suite du cours
- Sac de mots, TF-IDF et classification de textes18 min
- Embeddings et recherche sémantique18 min
- Transformers, attention et grands modèles de langage20 min
- RAG : génération augmentée par la recherche20 min
- Adapter un modèle : fine-tuning léger (LoRA) et évaluation20 min