9 avril 2023

Laverdure, le perroquet stochastique

(Source: Wikimedia)

 

Version courte pour lecteur pressé

Happé par une (légère) obsession, votre humble serviteur s’est lancé dans la fabrication d’un générateur de texte algorithmique simpliste qu’il a baptisé Laverdure en hommage à Raymond Queneau. Aucune trace d’intelligence artificielle ici ; le truc pond des phrases telles que : « Le prestige à nos souples machines au nord de tatouages prélevés sur l’atlantique [sic]. »

Ce billet explique la genèse et le fonctionnement de ce projet de programmation récréatif. Mais peut-être avez-vous autre chose à faire de votre soirée ?

L’histoire d’une idée

C’était il y a longtemps. Je venais d’obtenir mon baccalauréat en informatique et j’avais la vague ambition de vouloir poursuivre mes études universitaires. Je me suis donc inscrit à la maîtrise et j’ai passé quelque temps au laboratoire d’intelligence artificielle à plancher sur un modèle de langage programmé en Prolog. À l’époque, on tentait encore de modéliser les éléments de la langue à partir du lexique, de la syntaxe et de la sémantique. Rien à voir avec l’approche actuelle qui laisse l’apprentissage profond inférer et calquer les propriétés de la langue (à la ChatGPT et compagnie).

C’était la fin des années 1980 et l’informatique n’était pas tout à fait ce qu’elle est aujourd’hui ; il s’agissait d’une discipline encore jeune et sur le point de connaître des avancées techniques spectaculaires. Ces révolutions à venir découleraient souvent de connaissances théoriques alors déjà connues (1).

J’ai eu bien du plaisir à programmer en Prolog sur une station Sun en écoutant le premier album de Noir Désir. Je n’ai cependant jamais vu le bout de ce projet, comme je n’ai jamais vu le bout de ma maîtrise. J’ai quitté l’université et me suis trouvé un emploi. Je poursuis depuis ce que je n’ai jamais osé appeler une carrière en informatique. Dans un univers parallèle, j’aurais marié ma passion de l’informatique à celle de la littérature, et j’aurais poursuivi des études supérieures à fouiller la question de la génération du texte. Peut-être serais-je aujourd’hui à la fine pointe du développement des modèles de langages ? Mais cet univers parallèle ne s’est pas avéré et j’ai continué mon chemin sur la route pleine d’embranchements et d’événements aléatoires que fut ma vie.

C’est en lisant des articles sur le fonctionnement des modèles de langue dernier cri (tel que ChatGPT) que j’ai eu l’idée d’un algorithme simpliste qui permettrait de générer du texte sans avoir à s’en faire avec les considérations syntaxiques et sémantiques.

C’est que lorsqu’il génère du texte, ChatGPT le fait un mot à la fois, en évaluant à chaque étape quel est le mot le plus probable (je simplifie) permettant de poursuivre la conversation. La façon de déterminer ce mot met à profit un modèle très complexe construit sur un gigantesque réseau neuronal (etc.).

Je me suis dit qu’il y aurait moyen de faire quelque chose de vaguement semblable en utilisant un simple tableau des séquences de deux mots contigus dans un corpus. On lit du texte, on compile la fréquence de chaque suite de deux mots, puis, à partir de ces données, on génère du texte. Par exemple, après le mot obsolescence, le programme serait porté à écrire un mot qu’on voit (trop) souvent ensuite, programmée par exemple. J’ai eu l’intuition que cet algorithme permettrait de conserver des traces de la syntaxe française et donnerait de meilleurs résultats que de simplement choisir des mots au hasard. (2)

Cette idée est vite devenue une obsession et pour m’en libérer, je me suis lancé dans un petit projet de programmation. Cet article vous présente les résultats de ce projet récréatif, que j’ai baptisé Laverdure, le perroquet stochastique.

Pourquoi ce nom ?

Le nom de Laverdure est un hommage à Raymond Queneau. Dans son roman Zazie dans le métro, Queneau met en scène un perroquet appelé Laverdure, qui répète souvent la formule « Tu causes, tu causes, c’est tout ce que tu sais faire ». (3)

J’ai volé la formule « perroquet stochastique » à l’article On the Dangers of Stochastic Parrots: Can Language Models Be Too Big? de Emily M. Bender, Timnit Gebru et autres. Cet article fort pertinent décrit les risques associés aux modèles contemporains de langage par apprentissage profond. Stochastic Parrot est devenu une expression consacrée dans le joyeux monde des générateurs de langage et de l’intelligence artificielle. (4)

Ce que Laverdure sait faire

Le programme Laverdure a deux fonctions principales.
  • Il analyse des fichiers de texte et compile un corpus simple des unités lexicales qu’il y découvre.
  • En utilisant ce corpus, il génère du texte un mot à la fois selon divers algorithmes simplistes.

Les unités lexicales et le corpus

Laverdure peut découper un texte en unités lexicales, que j’appellerai ici token (5).

Pour Laverdure, un token est soit un mot, soit un groupe de mots reliés par un signe (comme l’apostrophe ou le trait d’union), soit une ponctuation. Le fait de considérer les signes de ponctuation comme des tokens permettra au générateur de texte d’insérer des ponctuations de façon naturelle, par exemple, de faire en sorte que le mot mais soit généralement précédé d’une virgule. Cela permettra aussi de former des phrases en insérant des points de temps à autre.

Pour Laverdure, un corpus est formé de deux ensembles : d’abord la liste des tokens rencontrés et leur fréquence, et ensuite la liste des couples de tokens adjacents et leur fréquence.

On peut demander à Laverdure de créer un nouveau corpus à partir d’un fichier de texte. Il peut aussi sur demande ajouter un fichier de texte à un corpus existant. On peut aussi interroger Laverdure à propos du contenu du corpus.

Par exemple, ci-après, le corpus créé à partir du texte du roman Vingt Mille Lieues sous les mers de Jules Verne, obtenu du site du projet Gutenberg.

Nombre de fichiers de texte:        1
   20000_lieues_sous_les_mers (153297 mots)
Nombre de mots dans le texte brut:  153297
Nombre de tokens uniques:           15822
Nombre de couples uniques:          70637

On constate que le roman comporte 153 297 tokens, dont 15 822 tokens uniques. Ces tokens forment 70 637 couples différents (séquences distinctes de deux tokens).

Si on interroge Laverdure à propos du token « poulpe », par exemple, on découvre qu’il y a 9 occurrences de ce mot dans le texte du roman, et 5 couples débutant avec ce mot (je rappelle que les ponctuations, comme le point et la virgule, sont aussi des tokens).

token="poulpe" n=9
("poulpe", ".") n=3
("poulpe", ",") n=3
("poulpe", "en") n=1
("poulpe", "sur") n=1
("poulpe", "calmar") n=1

Notons au passage que Vingt Mille Lieues sous les mers ne comporte aucune occurrence du mot « pieuvre » (6), trois fois le mot « Québec » et une fois le mot « Canada ».

Voici un autre exemple de ce que contient le corpus de ce roman. Le mot « navire » apparaît 78 fois. Voici la liste de chaque token qui suit le mot « navire » et, pour chacun, le nombre d’occurrences. 

token="navire" n=78
("navire", "!") n=2
("navire", ".") n=9
("navire", ",") n=17
("navire", "de") n=3
("navire", "et") n=1
("navire", "à") n=1
("navire", "se") n=2
("navire", "dont") n=1
("navire", "par") n=1
("navire", "en") n=3
("navire", "comme") n=1
("navire", "que") n=2
("navire", "qui") n=7
("navire", "?") n=6
("navire", "d'une") n=1
("navire", "n'avait") n=1
("navire", "était") n=1
("navire", "dans") n=1
("navire", "abandonné") n=1
("navire", "n'a") n=1
("navire", "avance") n=1
("navire", "tel") n=1
("navire", "résisterait") n=1
("navire", "moderne") n=1
("navire", "destiné") n=1
("navire", "construit") n=1
("navire", "ordinaire") n=1
("navire", "engagé") n=1
("navire", "submergé") n=1
("navire", "naviguait") n=1
("navire", "flottait") n=1
("navire", "s'enfonça") n=1
("navire", "l'argonaute") n=1
("navire", "cuirassé") n=1
("navire", "patriote") n=1
("navire", "insensé") n=1

Un corpus qui peut faire boule de neige

Laverdure peut additionner les données de plusieurs fichiers de texte dans le même corpus. Par exemple, si on ajoute aux données de Vingt Mille Lieues sous les mers le texte du roman Notre-Dame de Paris de Victor Hugo (aussi obtenu par le biais du site du projet Gutenberg), notre corpus prend de l’ampleur.

Nombre de fichiers de texte:        2
   20000_lieues_sous_les_mers (153297 mots)
   Notre-Dame_de_Paris (198713 mots)
Nombre de mots dans le texte brut:  352010
Nombre de tokens uniques:           27501
Nombre de couples uniques:          147579

Allez, pourquoi ne pas ajouter le texte du roman Germinal d’Émile Zola (toujours tiré de Gutenberg) ? Voici les statistiques de la combinaison de ces trois romans.

Nombre de fichiers de texte:        3
   20000_lieues_sous_les_mers (153297 mots)
   Notre-Dame_de_Paris (198713 mots)
   Germinal (198341 mots)
Nombre de mots dans le texte brut:  550351
Nombre de tokens uniques:           33835
Nombre de couples uniques:          207000

Des 550 351 tokens que totalisent ces 3 romans, Laverdure a extrait 33 835 tokens uniques et 207 000 (tout rond !) séquences de 2 tokens. Dans ce corpus, on trouve toujours 9 fois « poulpe », mais aussi 246 occurrences de « Quasimodo » (hé, hé).

Allez, ajoutons Les trois mousquetaires d’Alexandre Dumas. Et vive le projet Gutenberg.

Nombre de fichiers de texte:        4
   20000_lieues_sous_les_mers (153297 mots)
   Notre-Dame_de_Paris (198713 mots)
   Germinal (198341 mots)
   Les_Trois_Mousquetaires (259601 mots)
Nombre de mots dans le texte brut:  809943
Nombre de tokens uniques:           40165
Nombre de couples uniques:          272124

Dans ce corpus, le token « le » apparaît 16 918 fois, « neige » 34 fois, mais — sans surprise — on n’y trouve aucune fois le mot « logiciel » (vous pouvez me croire sur parole).

Les algorithmes de génération de texte

À partir de cette structure de données, j’ai bidouillé quelques algorithmes simples de génération de texte.

Mettez ça sur le compte de mon esprit cartésien, mais le texte généré de façon purement aléatoire ne m’a jamais satisfait ; ces jeux oulipiens montrent vite leurs limites et l’écriture automatique m’ennuie. Comme je le disais plus haut, j’espérais que les probabilités et la mémoire des paires de mots contigus tirés du corpus viennent mettre un peu d’ordre dans le hasard.

Voici le résultat de mes expériences. (Les résultats produits par Laverdure sont présentés tels que générés; tous les mots, y compris les noms propres, sont en minuscule, sauf lorsqu’ils sont au début d’une phrase.)

Un mot au hasard

Commençons avec le degré zéro de la génération de texte : tirons des mots au hasard dans le corpus.

Voici un texte de cent mots tirés au hasard de Notre-Dame de Paris.

Puante baladins arrivèrent alentour charnue hier d'anges l'insouciant ventre-mahom agissaient maçons garçon époque savourait expirèrent scènes equitem syriaque défaille ta charretiers d'embas l'incartade rondes l'admirable colosse saviez devrait battante conformément oscillations ressembler eussiez entr'ouvrit concierge appuis symbolisme imprimer patente voyage humeur chargés passeraient sifflantes saint-jean-le-rond boulangerie l'ave figures cyprin surtout détournaient erras justement mariait disponibles clémente d'îles parts assiégés dépité haletait mur sifflèrent adroitement charnier palestrina mannequin lentement d'ennemis luminaire persistez-vous mort ajouté hoctement marie alternative s'enfermait ineffables heureux rira sub jettent somptuosité septentrion soif l'écoutait cherché raison tournaient stryga conduits figura route chat d'amis ondulait dignes pieds écriée deuxièmement

On s’y attendait, les résultats sont aléatoires et inintéressants. Le seul intérêt vient du lexique du roman (ventre-mahom, embas, syriaque, les bribes de latin, etc.). Aussi, puisqu’il y a plus de 90 000 tokens uniques dans le corpus, avec cet algorithme, la probabilité de tirer une ponctuation est très faible, et le texte s’étire par conséquent en une longue phrase incohérente et en apparence infinie.

Un des mots suivants au hasard

Tentons de tirer profit de l’information de notre corpus. Après avoir ajouté un mot au texte, tirons un token au hasard parmi les tokens suivants répertoriés pour ce mot.

Voici ce que ça donne en utilisant Notre-Dame de Paris comme source. Le texte compte un peu plus de cent mots et débute par le mot « La ».

La monstrueuse bête en perspective que d'avoir de sanctuaire du brouillard faisait attendre au fripier qui portait en s'aidant seulement tout. Philippe-auguste qu'on vous garde bien quoi payer. Rends-les tous les découpures d'une croix d'argent à boucle de mahom en seconde avait entassé le contre-guet la période, répandues en 64. Allons essayer d'indiquer, tour près et s'adressant à une condition sans s'occuper du greffe pour ce gros eustache eût hasardé cette sœur gudule ! Réflexion fut obligé, hermès. Pasquedieu ! Plaudite, on pouvait dissimuler sa phrase, madamoiselle de s'arrêter sous tant dit tristan avait alors tu cessas de baladin.

C’est déjà un peu mieux. Le texte est rythmé de ponctuations et on aperçoit ici et là quelques séquences qui se tiennent sur le plan de la syntaxe (« une croix d’argent à boucle de Mahom », « sans s’occuper du greffe pour ce gros Eustache », « on pouvait dissimuler »).

Un mot suivant fréquent ou au hasard (alias mode optimisé)

Je me suis ensuite dit qu’il y aurait moyen d’améliorer la qualité du texte en favorisant les mots suivants les plus fréquents selon les données du corpus.

J’ai d’abord essayé, pour chaque mot ajouté au texte, de choisir le token adjacent le plus fréquent, mais ce genre d’algorithme déterministe finit souvent par produire du texte qui se répète en boucle.

En guise de compromis, j’ai inventé ce que j’ai présomptueusement baptisé le mode optimisé. Ça consiste à choisir chaque prochain mot comme suit :
  • selon une certaine probabilité (par exemple 60 %), on choisit au hasard parmi tous les tokens suivants répertoriés ;
  • sinon, on choisit parmi une certaine proportion (30 % par exemple) des tokens suivants les plus fréquents.
Cet algorithme a l’avantage de favoriser les tokens suivants plus fréquents, mais aussi de se soumettre au hasard, ce qui génère plus de variété et évite les boucles.

Voici ce que ça donne, toujours avec Notre-Dame de Paris et en débutant par le mot « La ».

La meute y allume des marches de bimbeloterie qui sautaient en magie ? Tout paris n'est rien dire qu'il paraissait avoir suivi de mer agitée. C'étaient vingt francs pour cet art-roi, tremblant de douceur infinie : il retombait dans l'air très redouté cardinal on dirait, faute de chien. Faites là ? Ne s'était révélé ainsi : compère coppenole. J'allais. Mort-christ ! Répéta la plupart des filles et jardins, aux saints-innocents, pendaison de justice pour des halles, smelarda, gendarmes, silence et sifflantes, mais dès l'âge de solide que venait se condamne, monstre !

Le résultat n’est pas significativement supérieur à l’algorithme de la section précédente, qui choisissait au hasard parmi les mots suivants. À l’usage, il m’a semblé que j’obtenais des résultats généralement un peu meilleurs avec cet algorithme, mais il est possible aussi que ce soit de l’autosuggestion.

Voici un autre exemple, un texte d’un peu moins de 200 mots généré par cet algorithme en utilisant le corpus géant formé par Vingt Mille Lieues sous les mers, Notre-Dame de Paris, Germinal et Les trois mousquetaires. Le mot initial a été choisi au hasard.

Injuriait. L'ordre des syllabes, pourrait être muet de gazette des jurements, s'installèrent. Cornemahom ! Laissez tomber sans boire ? J'hésitais à petites taches très-rares. L’air des affaires vont, déchiqueté par 55° de visites que l'emprosthotonos succède à l'un sert à armentières, tellement alourdis de festubert, l'espoir mort en revanche de renoncer à quoi la même idée aussi parce qu'en devenant plus bien, entassés, hermès, requins, s’interpellant, nom d'encornets, certes, ébranlé. Qu'allais-tu faire en grande ouverte établissait par l’hôte avait posé ce navire se fournissaient une lime, prévenu de naissance inconnue. Mademoiselle ! Sans quelques mouvements que marie eût bien tranquillement, celles d'yves, poussé adroitement à apitoyer. Va-t'en donc comme l'agamemnon de monstres vaincus, tenue la ressaisir son effroi se retenir, sillonnés de celui-ci les brasiers. Vainement d'emprunter dix jours plus travailler ? Faisons feu succéda la nouvelle-zemble ? J’en use et choir ! M'écriai-je. N'est-ce donc pour s'en adoucit, dense, là-bas sa moustache noire sur pilotis, pouvaient faire du navire cuirassé d'argent ! Gueulait-il. Bon frère zacharie ! Quiconque porte pour l'atteindre de popeline gros qui s'enfonce suivant cassiodore, répandirent bientôt à tous vu.

(Vous ne serez pas surpris si je vous dis que le mot « agamemnon » provient de Notre-Dame de Paris, et que « par 55° » est tiré de Vingt Mille Lieues sous les mers.)

En définitive, on a beau tenter d’optimiser le choix du prochain mot parmi les mots suivants répertoriés, l’algorithme n’a qu’une vision circonscrite du texte qu’il génère. Ça n’assure la cohérence (ou une possible cohérence) que d’un couple de mots à la fois. Le pauvre programme marche en regardant le bout de ses pieds. Si des caractéristiques syntaxiques ou sémantiques d’un mot peuvent être conservées dans le suivant (par exemple, un article au pluriel sera probablement suivi par un substantif au pluriel), elles ne le sont pas au-delà. À l’échelle de la phrase, la cohérence syntaxique tiendra donc du pur hasard ; le fait par exemple qu’après un substantif au pluriel, trois mots plus loin, on pige un verbe à la troisième personne du pluriel.

Quand on se limite à une seule phrase, et par essais et erreurs, on peut produire des bouts de texte possédant un certain potentiel d’évocation. J’ai publié dans mon compte quoli.bet (qui, en quelque sorte, remplace maintenant  mon ancien compte touitteur @nanopoesie) quelques quatrains composés de phrases ainsi créées par Laverdure (7).



Conclusion

Quelques réflexions pour conclure.

Les tendances actuelles de l’intelligence artificielle générative impliquent de créer des corpus gigantesques composés de contenus souvent protégés par le droit d’auteur et sans obtenir la permission préalable des créateurs.

Malgré la nature privée de mon projet, il m’a semblé important que le corpus comporte un minimum d’information sur les documents qui le composent. Je me suis limité à répertorier le nom des fichiers, mais j’ai imaginé d’autres stratégies, comme l’ajout de métadonnées concernant les textes (auteurs, édition, etc.). J’ai aussi eu l’idée de stocker les données de fréquence de telle manière qu’on puisse les rattacher au texte d’origine. Cela permettrait d’interroger les statistiques propres à une œuvre du corpus (et non pas de l’ensemble) ; cela permettrait aussi de retirer la partie du corpus provenant d’une œuvre donnée. Je ne me suis cependant pas lancé dans ces projets dans le projet.

Par ailleurs, j’ai remarqué qu’il est intéressant d’utiliser un corpus tiré d’une unique œuvre de grande taille (comme un roman). Ça favorise l’uniformité du champ lexical, du style et des temps de verbe, notamment. Lorsqu’on génère du texte à partir d’une seule œuvre, on obtient une espèce de remix ou de collage (en anglais, on dirait mashup) dans lequel on reconnaît des traces du texte d’origine (ce qui donne tout son sens au concept de perroquet). Vingt Mille Lieues sous les mers de Jules Verne s’est avéré en cela un excellent corpus, avec son vocabulaire technique, propre aux choses maritimes et sous-marines.

En fin de compte, si ce projet de programmation fut une source de récréation, les résultats sont loin d’être satisfaisants et je me suis vite lassé de mon petit programme. Il me semble pour le moment avoir tiré tout le jus de ce concept. Laverdure va donc demeurer sagement planqué au fond du disque dur de mon ordinateur.

Ne restera que ce billet de blogue pour témoigner de l’existence éphémère de Laverdure, mon perroquet stochastique.



*



Notes :

(1) Par exemple, les langages de programmation dits orientés-objet, les bases de données relationnelles, les microprocesseurs à architecture RISC, la réseautique TCP/IP, et même les réseaux neuronaux.

(2) J’ai ensuite lu cet article de blogue de Stephen Wolfram, le mythique inventeur du logiciel Mathematica ; les deux premiers chapitres expliquent ce qu’un programme simple comme Laverdure tente de faire et en quoi les réseaux neuronaux sont infiniment supérieurs.

(3) Laverdure est un des nombreux personnages de perroquet dans la littérature.

(4) Précisons tout de suite que Laverdure est un programme algorithmique et naïf qui ne compte que quelques centaines de lignes de JavaScript, et qu’il n’a rien d’une intelligence artificielle.

(5) Lorsque je bricole de petits programmes personnels, j’utilise naturellement le français pour les variables et les commentaires explicatifs insérés dans le texte des programmes. Il m’arrive aussi parfois, par économie et pour me comprendre moi-même, d’utiliser des mots anglais consacrés. D’où ce token.

(6) Le mot pieuvre a été popularisé par Victor Hugo dans son roman Les travailleurs de la mer publié en 1866, à partir d’un mot du vieux normand en usage à Guernesey. Il semble que Verne, qui a publié Vingt Mille Lieues sous les mers à la même époque, soit en 1869, n’ait pas subi cette influence.

(7) quoli.bet est un réseau social fort sympathique, patenté fort professionnellement par David Turgeon et connecté au Fedivers.