Aujourd’hui, les grands modèles de langage (LLM) fascinent par leur capacité à écrire, raisonner, ou même imiter des styles littéraires avec une étonnante fluidité. Pourtant, avant les réseaux de neurones profonds, les milliards de paramètres et les mécanismes d’attention, la modélisation du langage reposait sur des idées bien plus simples — mais tout aussi fondamentales.
Parmi ces idées figurent les modèles n-gram : une approche purement statistique où la probabilité d’un mot ne dépend que des quelques mots qui le précèdent. Pas de mémoire longue, pas d’attention, pas de transformer — juste des comptages, des fréquences, et un peu de bon sens mathématique. Et pourtant, ces modèles ont longtemps été au cœur des systèmes de reconnaissance vocale, de correction automatique ou de traduction statistique.
Dans cet article, je propose un voyage aux origines en implémentant un modèle n-gram entièrement en JAX, sur un petit corpus littéraire : TinyMolière, composé d’extraits des œuvres du dramaturge français.
Le modèle unigramme est la forme la plus élémentaire de modèle de langage. Il repose sur une hypothèse forte (et naïve) d’indépendance entre les mots : la probabilité d’une séquence \( w_1, w_2, \dots, w_T \) se factorise comme le produit des probabilités individuelles :
\[ P(w_1, w_2, \dots, w_T) = \prod_{t=1}^{T} P(w_t) \]
Chaque probabilité \( P(w) \) est estimée à partir de la fréquence empirique du mot dans le corpus d’entraînement :
\[ P(w) = \frac{\text{count}(w)}{N} \]
où \( \text{count}(w) \) est le nombre d’occurrences de \( w \), et \( N \) le nombre total de tokens observés.
Appliqué au corpus TinyMolière, ce modèle capture l’empreinte lexicale de Molière : ses mots les plus fréquents, ses tics de langage, ses préférences stylistiques. L’implémentation, entièrement réalisée en JAX (code sur GitHub), permet de calculer ces probabilités de manière vectorisée et efficace.
Voici les 20 mots les plus probables selon ce modèle unigramme :
| Mot | Probabilité (%) | Mot | Probabilité (%) | Mot | Probabilité (%) | Mot | Probabilité (%) | Mot | Probabilité (%) |
|---|---|---|---|---|---|---|---|---|---|
| de | 3.45 | et | 2.72 | vous | 2.38 | que | 2.27 | je | 2.14 |
| la | 1.50 | le | 1.45 | l' | 1.37 | est | 1.31 | il | 1.26 |
| qu' | 1.24 | un | 1.17 | en | 1.15 | ne | 1.10 | d' | 0.93 |
| qui | 0.93 | les | 0.90 | ce | 0.89 | pour | 0.89 | me | 0.88 |
Ces résultats ne surprennent guère : ils reflètent la prédominance des mots grammaticaux (pronoms, déterminants, conjonctions) dans le langage théâtral classique. Bien sûr, ce modèle ignore complètement le contexte — il ne saura jamais que « je vous salue » est plus naturel que « salue je vous ». Mais il capture déjà, avec une simplicité remarquable, l’empreinte statistique d’un auteur.
Pour comparer objectivement les modèles de langage, on utilise souvent la perplexité (perplexity). Intuitivement, elle mesure à quel point le modèle est « surpris » par un texte inconnu : plus la valeur est basse, mieux le modèle prédit les données.
Formellement, pour un corpus de validation contenant \( N \) tokens, la perplexité se calcule à partir de la log-vraisemblance moyenne :
\[ \text{Perplexity} = \exp\left( -\frac{1}{N} \sum_{t=1}^{N} \log P(w_t) \right) \]
Lorsqu’on applique cette formule au corpus de validation de TinyMolière avec notre modèle unigramme non lissé, on obtient… l’infini (inf). Pourquoi ? Parce que certains mots du texte de validation n’apparaissent jamais dans le corpus d’entraînement — ce qu’on appelle des mots out-of-vocabulary (OOV). Le modèle leur attribue une probabilité nulle, ce qui conduit à \( \log(0) = -\infty \), et donc à une perplexité infinie.
Ce phénomène illustre une faiblesse majeure des modèles non lissés : ils sont incapables de gérer les mots nouveaux ou rares. Même dans un petit corpus, la moindre variation — un mot oublié, une forme verbale absente — suffit à briser complètement l’évaluation. C’est précisément ce qui motive l’introduction de techniques de lissage, comme le lissage de Laplace ou de Good-Turing, que nous allons maintenant explorer.
Face à l’échec du modèle non lissé, une solution classique consiste à appliquer un lissage additif, aussi appelé lissage de Laplace. L’idée est simple : plutôt que d’assigner une probabilité nulle aux mots inconnus, on « réserve » une petite part de probabilité à tous les mots du vocabulaire — y compris ceux jamais observés.
Cela revient à ajouter 1 à chaque comptage avant de normaliser :
\[ P_{\text{Laplace}}(w) = \frac{\text{count}(w) + 1}{N + V} \]
où \( N \) est le nombre total de tokens dans le corpus d’entraînement, et \( V \) la taille du vocabulaire.
Grâce à cette correction, aucun mot n’a plus une probabilité nulle, ce qui permet au modèle de gérer les mots rares ou absents sans s’effondrer. Appliqué à TinyMolière, ce lissage ramène la perplexité sur le jeu de validation à une valeur finie : 478.39.
Ce chiffre peut sembler élevé — et il l’est, comparé aux LLM modernes (qui atteignent souvent des perplexités inférieures à 10 sur des corpus larges). Mais il faut garder à l’esprit que nous travaillons avec un modèle sans contexte, sur un corpus très petit (quelques milliers de mots), et dans une langue riche en variations morphologiques. Dans ce contexte, une perplexité de ~478 signifie simplement que, en moyenne, le modèle hésite entre environ 478 mots possibles à chaque position — ce qui reste cohérent avec les limites d’un unigramme.
Maintenant que nous avons posé les bases, passons à un modèle légèrement plus expressif : les bigrammes, qui prennent en compte le mot précédent pour prédire le suivant.
Les unigrammes, aussi utiles soient-ils pour capturer la fréquence globale des mots, ignorent complètement l’ordre et la structure du langage. Pour aller un peu plus loin sans quitter le terrain de la simplicité statistique, on peut adopter les bigrammes — un modèle n-gram avec \( n = 2 \).
L’idée est intuitive : la probabilité d’un mot dépend du mot qui le précède immédiatement. Formellement, une séquence \( w_1, w_2, \dots, w_T \) est modélisée comme :
\[ P(w_1, \dots, w_T) = \prod_{t=1}^{T} P(w_t \mid w_{t-1}) \]
où l’on introduit des marqueurs de début (<s>) et de fin (</s>) de phrase pour bien définir les bords. Chaque probabilité conditionnelle est estimée à partir des comptages observés :
\[ P(w_t \mid w_{t-1}) = \frac{\text{count}(w_{t-1}, w_t)}{\text{count}(w_{t-1})} \]
Mais cette formule cache un piège classique : dans un corpus fini, de nombreuses paires de mots n’apparaissent jamais. Leur probabilité devient alors nulle, ce qui rend le modèle incapable de traiter la moindre nouveauté — y compris des tournures grammaticalement correctes mais absentes de l’entraînement.
Pour éviter cet écueil, on recourt au lissage additif :
\[ P_{\text{lissé}}(w_t \mid w_{t-1}) = \frac{\text{count}(w_{t-1}, w_t) + \alpha}{\text{count}(w_{t-1}) + \alpha \cdot V} \]
où \( V \) est la taille du vocabulaire et \( \alpha > 0 \) un petit paramètre (ici, \( \alpha = 1 \)).
Mais avant même de parler de lissage, il faut construire un vocabulaire raisonnable. Dans un petit corpus comme TinyMolière, beaucoup de mots n’apparaissent qu’une ou deux fois — souvent des noms propres, des formes verbales rares, ou des erreurs de tokenisation. Si l’on inclut tous ces mots, on dilue la masse de probabilité sur des événements trop rares pour être fiables, et on augmente considérablement le risque de surapprentissage.
C’est pourquoi nous introduisons un seuil de fréquence minimale (min_freq = 30) : tout mot apparaissant moins de 30 fois dans le corpus d’entraînement est remplacé par un symbole générique <unk>. Ce choix n’est pas arbitraire ; il reflète un équilibre entre expressivité et stabilité. Trop de mots rares, et le modèle devient bruyant ; trop peu, et il perd la richesse lexicale propre à Molière. Le symbole <unk> joue alors un double rôle : il réduit la taille du vocabulaire, et sert de filet de sécurité pour les mots inconnus à l’évaluation.
Grâce à cette combinaison — vocabulaire filtré, symboles de phrase, et lissage additif — notre modèle bigramme devient non seulement utilisable, mais performant. Il atteint une perplexité de validation de 68.07, une nette amélioration par rapport aux 478 du modèle unigramme.
Cette baisse spectaculaire montre à quel point même une dépendance locale, aussi rudimentaire soit-elle, capture des régularités linguistiques essentielles.
Les unigrammes et bigrammes ne sont que des cas particuliers d’une famille plus large : les modèles n-gram. Leur principe repose sur l’hypothèse de Markov d’ordre \( n-1 \), qui stipule que la probabilité d’un mot ne dépend que des \( n-1 \) mots qui le précèdent. Ainsi, pour une séquence \( w_1, w_2, \dots, w_T \), on approxime :
\[ P(w_1, \dots, w_T) \approx \prod_{t=1}^{T} P(w_t \mid w_{t-n+1}, \dots, w_{t-1}) \]
où, par convention, les positions avant le début de la phrase sont remplies de symboles spéciaux (<s>).
En pratique, chaque probabilité conditionnelle est estimée à partir de fréquences observées :
\[ P(w_t \mid w_{t-n+1}, \dots, w_{t-1}) = \frac{\text{count}(w_{t-n+1}, \dots, w_t)}{\text{count}(w_{t-n+1}, \dots, w_{t-1})} \]
Comme pour les bigrammes, cette estimation brute est presque toujours complétée par une forme de lissage (Laplace, Good-Turing, Kneser-Ney, etc.) pour attribuer une probabilité non nulle aux séquences rares ou absentes.
Malgré leur simplicité et leur efficacité historique, les modèles n-gram souffrent de limites structurelles :
Ces faiblesses expliquent pourquoi les n-gram, bien qu’encore utilisés dans des systèmes embarqués ou comme composants de base, ont été largement supplantés par les modèles neuronaux, capables d’apprendre des représentations continues, de raisonner sur de longues distances, et de généraliser au-delà des motifs observés.
Pourtant, leur transparence, leur simplicité et leur faible coût en font un excellent point de départ — et un rappel utile que parfois, comprendre le passé est la meilleure façon d’apprécier le présent.
Si les grands modèles de langage ont largement supplanté les n-gram dans les applications grand public, ces derniers ne sont pas pour autant obsolètes. Leur simplicité, leur transparence et leur faible coût computationnel en font encore des outils précieux dans plusieurs contextes :
En implémentant un unigramme puis un bigramme sur TinyMolière avec JAX, nous avons vu comment, à partir de simples fréquences et d’un peu de lissage, on peut déjà capturer l’empreinte statistique d’un auteur, générer des séquences plausibles, et obtenir une évaluation quantitative via la perplexité.
Bien sûr, ces modèles ont des limites — horizon contextuel étroit, incapacité à généraliser au-delà des co-occurrences, sensibilité aux données rares. Mais c’est justement en les confrontant à ces limites que l’on comprend mieux ce que les architectures modernes apportent : non pas une magie, mais une capacité à apprendre des représentations, à raisonner au-delà de la mémoire locale, et à généraliser sémantiquement.
Pour approfondir vos connaissances et explorer des outils avancés, voici quelques ressources :
Ma recommandation musicale du jour : à écouter sans modération !
Écouter sur YouTube