note préalable
Les expériences présentées ici n’ont pas été faites en même temps, et les résultats à un essai particulier peuvent être différents suivant les mises à jour de chatgpt. Elles ont été menées en Français et en Anglais avec des résultats généralement similaires. J’ai utilisé GPT-4o, qui a fait un peu mieux que Mistral Large et un peu moins bien que claude-3-haiku.
Les tests peuvent être répétés avec un encodeur du chiffre de César et le tokenizer d’OpenAI.
Le chiffre de César
Lorsque César, en campagne en Gaule, écrivait à l’administrateur de ses domaines à Rome, il utilisait une astuce pour que ses messages ne soient lisibles que de celui-ci. Chaque lettre était remplacée par la lettre qui se trouve trois places devant dans l’alphabet: le A devenait un D, le B devenait un E, et caetera. Par exemple, « alea jacta est » devient « dohd mdfwd hvw ». Cette méthode de chiffrement simple est restée sous le nom de chiffre de César.
Le chiffre de César, trop simple et trop connu, n’offre plus aucune sécurité depuis longtemps. Néanmoins j’ai pensé qu’il serait un exercice intéressant pour tester les capacités de ChatGPT à reconnaître un texte chiffré. Que se passerait-il si j’essayais d’envoyer un message chiffré, sans contexte ni explications: ChatGPT parviendrait-il à reconnaître le code et le déchiffrer ?
Et c’est là qu’il se passe quelque chose d’étrange. Pour un humain, la partie difficile de cette tâche est de reconnaître ou trouver le code, mais une fois que celui-ci est trouvé le décodage est simple, il suffit de permuter les lettres du message une par une. ChatGPT, lui, s’il n’a aucun mal à détecter le code, a beaucoup de mal à le décoder correctement. Dans les tests que j’ai fait, ChatGPT reconnaissait en général le chiffre de César (avec plus de difficultés en français qu’en anglais), était capable d’expliquer son principe, voire donnait la correspondance entre lettres en clair et lettres chiffrées pour tout l’alphabet; mais le décodage est approximatif, voire carrément faux. Cela se vérifie particulièrement pour les mots longs et peu usités, isolés lexicalement comme « saperlipopette » ou « hurlberlu ».
Alors, d’où cela vient-il ? Pour comprendre il faut se pencher sur la première étape du traitement du texte par un modèle Transformer: la lexémisation (tokenisation en anglais), c’est-à-dire la décomposition du texte en lexèmes (tokens) et la vectorisation (embedding en anglais) de ces lexèmes.
La lexémisation
Pour faire des calculs sur du texte, et donc pour construire des modèles de langage, il est d’abord nécessaire de transformer le texte en chiffres. La façon la plus simple est de reprendre la façon dont le texte est généralement encodé sur les ordinateurs, c’est-à-dire avec un code binaire (convertible en nombre) associé à chaque lettre. Cela fonctionne mais n’est pas très efficace, car pour extraire le sens du texte il faut d’abord combiner ces lettres en mots. L’autre approche simple est d’attribuer un nombre par mot du dictionnaire, plutôt que par lettre. Cela épargne beaucoup de calculs à la machine mais cause d’autres problèmes:
- un vocabulaire beaucoup plus élevé est nécessaire. Au lieu de 26 nombres nécessaires pour encoder les lettres, on passe à plusieurs dizaines de milliers pour le vocabulaire complet d’une langue
- le modèle qui en résulte est très sensible à l’orthographe. Ainsi, lorsqu’une variante inhabituelle d’un mot est utilisée ou tout simplement lorsqu’une faute de frappe s’y glisse, le mot risque de ne pas être reconnu dans le vocabulaire. Le modèle ne sera pas capable de le traiter du tout.
La solution intermédiaire consiste à décomposer le texte en lexèmes, c’est-à-dire des fragments de mots que l’ont retrouve souvent et qui ont un sens. Par exemple, le suffixe -eur est un lexème qui porte du sens: il transforme une action en l’agent de cette action (« transport » -> « transporteur »). Les conjugaisons, les préfixes et suffixes courants, et les radicaux des mots constituent de bons lexèmes pour décomposer le texte. La lexémisation constitue une unité intermédiaire entre les lettres et les mots.
Mais comment distinguer efficacement les lexèmes qui valent le coup d’être dans notre vocabulaire ? Pourquoi y enregistrer « -eur » plutôt que « tkq » par exemple ? Heureusement nous n’avons pas besoin de créer la liste de lexèmes à la main, on utilise plutôt des méthodes statistiques. La plus courante est l’algorithme du Byte-Pair Encoding (Encodage de paires d’octets), qui groupe récursivement la paire d’octets qui apparaissent le plus souvent ensembles. Dans les faits, ce simple critère « les lettres apparaissent souvent ensembles » est suffisant pour obtenir des lexèmes satisfaisants.
Revenons à nos moutons
Alors, quel rapport avec notre sujet ? Eh bien comme le texte est toujours décomposé en lexèmes, le modèle ne « voit » que des lexèmes, pas des lettres individuelles. Il n’a pas accès au texte lui-même, mais à une suite de codes dont chacun correspond à un lexème, et n’a pas accès à la composition de ces lexèmes. On comprend qu’il ait du mal à épeler un mot.
En voyant les choses comme ça, on peut finalement être surpris que ChatGPT sache épeler des mots et manipuler les lettres une par une.
Combien y a-t-il de « R » dans « strawberry » ?
Depuis la sortie de ChatGPT, les utilisateurs ont remarqué qu’il était incapable de résoudre un simple problème: compter le nombre de « R » dans le mot « strawberry » ? (« fraise » en anglais). Ce problème persiste avec les versions les plus récentes (plus forcément avec « strawberry » maintenant qu’il a été inclus dans les données d’entraînement, mais essayez avec les C dans « archiduc » ou les S dans « assassin »).
L’explication est la même qu’avec le chiffre de César: le découpage en lexèmes ne correspond pas aux lettres individuelles. GPT-4o découpe « strawberry » en 3 lexèmes: st-raw-berry (le tokenizer d’OpenAI est disponible ici pour tester), il sait qu’il y a un R dans « raw » et un autre dans « berry » mais ne sait pas combien il y en a dedans.
Vous pouvez facilement trouver d’autres mots qui poseront le même problème. Il vous faut un mot où une lettre apparaît deux fois, dont une fois doublée (par exemple les S de « spadassin ») ou associée dans un digramme très courant comme « ch » ou « qu » (par exemple les U de « ubiquité »).
Notez qu’il existe une parade simple pour éviter ce problème: ajoutez « procède étape par étape » à votre question.
Addition de dernière minute: le patching
Ce 12 décembre, Meta a publié un papier exposant une approche différente: le patching. Cette approche consiste à grouper les lettres en séquences dynamiquement, sans vocabulaire fixé. L’architecture présentée comporte une première couche Transformer qui encode les lettres en représentations de patches, et des couches d’Attention croisée reliées directement aux lettres elles-mêmes ce qui permet au modèle de « voir » la composition des patches.
Les tests effectués par Meta montrent que non seulement cette méthode parmet de mieux saisir l’orthographe des mots, mais aussi que sa performance est meilleure à grande échelle que la lexémisation avec un vocabulaire fixé.