← Tous les articles

Comment fait-on entrer du son dans un modèle de langage

Un modèle de langage attend une file de nombres entiers. Le son en fournit seize mille par seconde. Voici la machinerie qui réconcilie les deux.

Ceci est une note de lecture. Le raisonnement, les exemples et la plupart des chiffres viennent de l’article de Václav Volhejn publié par Kyutai, qui explique comment on transforme du son en jetons pour un modèle de langage. Je l’ai lu, puis j’ai réimplémenté la mécanique pour vérifier que je l’avais comprise. Ce que j’ajoute est là : les ablations à la fin de chaque section, où je casse un mécanisme pour voir ce qu’il tenait.

Le son résiste

En 2025, la plupart des assistants vocaux ne comprennent pas vraiment la voix. Ils transcrivent, réfléchissent en texte, puis lisent leur réponse à voix haute. C’est efficace, mais c’est un emballage autour d’un modèle de texte, pas une écoute.

Volhejn propose un test cruel : parlez à un modèle nativement audio avec une voix très aiguë, et demandez-lui si votre voix est grave ou aiguë. Aucun ne sait répondre. L’information était pourtant dans le signal.

Du côté du texte, tout est réglé depuis longtemps. En 2015 déjà, Karpathy entraînait un réseau récurrent de trois couches sur un seul GPU et obtenait du code et du LaTeX plausibles, caractère par caractère.

Le son ne se laisse pas faire, et la raison tient en un nombre. Dix secondes d’audio, c’est 160 000 échantillons. WaveNet les prédit un par un : le résultat sonne comme une vraie voix humaine, avec le bon grain et la bonne respiration, mais ne produit presque jamais un mot correct.

Volhejn mène l’expérience naïve jusqu’au bout, en donnant du son brut à nanoGPT. Deux chiffres suffisent à expliquer l’échec : la fenêtre de contexte de 2 048 jetons représente 128 millisecondes — le modèle ne voit même pas un mot entier — et générer dix secondes demande trente minutes sur une H100.

Le texte, c’est une boîte de LEGO : les briques existent déjà, en nombre limité, on assemble. Le son, c’est du sable. Chaque grain ne veut rien dire tout seul, et pour écrire le mot bonjour il faut en placer seize mille par seconde.

La conclusion est immédiate : il faut réduire le nombre de décisions. Il faut compresser.

Comprimer avant de modéliser

Schéma : audio entrant, encodeur + RVQ, jetons entiers, modèle de langage Transformer, jetons prédits, décodeur, audio sortant.
Le modèle de langage en sandwich. Pour lui, rien ne change : ce sont des nombres qui entrent et des nombres qui sortent.

L’outil qui comprime et décomprime s’appelle un codec audio neuronal. Le mot est emprunté au MP3, mais ici il désigne simplement un découpeur de son en jetons.

L’autoencodeur

Schéma d'un autoencodeur : une entrée est comprimée par l'encodeur dans un espace latent, puis reconstruite par le décodeur.
Comprimer, puis reconstruire, et se juger sur la ressemblance.

Personne ne dit au modèle ce qu’il doit mettre dans son résumé. Il doit le découvrir seul, sous la contrainte que le résumé est trop petit pour tout contenir.

On vous demande de résumer un roman de trois cents pages sur une fiche bristol. Puis on donne votre fiche à quelqu’un qui doit réécrire le roman entier. Si le résultat ressemble à l’original, c’est que votre fiche contenait ce qui compte.

Pourquoi il faut quantifier

Un autoencodeur ordinaire produit des nombres à virgule, en quantité infinie. Un modèle de langage ne sait pas faire ça : il sait choisir un élément dans une liste finie. Il lui faut un vocabulaire.

Schéma de la quantification : des points continus sont remplacés par le numéro du centre le plus proche.
Remplacer une infinité de positions possibles par le numéro du centre le plus proche.

On ne vous donne pas de la peinture liquide, mais une boîte de 1 024 crayons numérotés. Vous perdez un peu de fidélité, mais vous pouvez désormais décrire votre dessin entier au téléphone, en récitant une suite de numéros.

Les centres ne sont pas décidés à l’avance : ils sont appris, par une procédure proche des k-moyennes. Et il y a un détail malin — si un centre n’est utilisé par personne pendant un moment, on le téléporte à la position d’un vecteur pris au hasard. Sans cela, un centre mal placé au départ resterait mort à jamais.

L’estimateur direct, ou le mensonge assumé

Voici le vrai obstacle. On veut quantifier pendant l’entraînement, pour que l’encodeur apprenne à produire des vecteurs faciles à arrondir :

z = encoder(x)
z_quantized = to_nearest_cluster(z)          # on arrondit
loss = reconstruction_loss(x, decoder(z_quantized))

Mais prendre le centre le plus proche n’est pas dérivable. Aucun gradient ne remonte jusqu’à l’encodeur. La solution est d’une honnêteté rafraîchissante : on règle le problème en faisant comme s’il n’existait pas.

residual = z - to_nearest_cluster(z)
z_quantized = z - residual.detach()          # .detach() : oublie le gradient

À l’aller, la valeur transmise au décodeur est exactement la même. Au retour, le gradient traverse l’arrondi comme s’il n’était pas là. C’est l’estimateur direct.

Ce raisonnement ne tient que si les vecteurs restent proches de leur centre. On ajoute donc une pénalité proportionnelle à cette distance : la perte d’engagement. L’ensemble s’appelle un VQ-VAE — dont le variationnel est un vestige historique qui ne signifie plus rien.

Ce qui casse quand on retire chaque pièce

Voilà ma part du travail. J’ai entraîné un VQ-VAE en dimension latente 2 sur trois classes de Fashion MNIST, puis j’ai désactivé chaque mécanisme, un par un.

Configuration MSE test Centres actifs Perplexité
référence 0,02707 32/32 25,2
sans estimateur direct 0,06844 32/32 21,7
sans perte d’engagement 0,03310 32/32 16,1
sans téléportation 0,02866 24/32 19,2
codebook de 4 centres 0,04227 4/4 3,9

Trois lectures.

L’estimateur direct est le mécanisme indispensable. Sans lui, l’erreur est multipliée par 2,5. C’est cohérent : l’encodeur n’apprend plus que par la perte d’engagement, ce qui est un signal bien plus pauvre.

La téléportation ne change presque rien à l’erreur, mais tue un quart du codebook. Vingt-quatre centres sur trente-deux. C’est exactement le symptôme annoncé : un centre jamais choisi ne reçoit aucune mise à jour, donc ne bouge pas, donc ne sera jamais choisi. Le coût est invisible dans la MSE et bien réel dans le débit — on paie huit centres pour rien.

La perte d’engagement se voit surtout dans la perplexité, qui tombe de 25 à 16. L’erreur bouge peu, mais le codebook est nettement moins bien réparti.

Trois panneaux : l'espace latent en dimension 2 avec les centres du codebook, l'histogramme d'utilisation des centres, et les courbes d'entraînement.
La configuration de référence. En dimension 2, on voit littéralement où sont les centres.
Mêmes trois panneaux sans téléportation : l'histogramme montre plusieurs centres à zéro.
Sans téléportation. L'histogramme du milieu raconte tout : huit barres à zéro, huit centres morts.

La quantification résiduelle

Pour gagner en fidélité, on pourrait augmenter le nombre de centres. Mais il faut comparer chaque vecteur à chacun d’eux, et ça devient vite prohibitif.

L’astuce est combinatoire. Plutôt qu’un million de centres, on en crée deux ensembles de 1 024. Chaque vecteur est décrit par un couple d’entiers, ce qui donne plus d’un million de combinaisons pour seulement 2 048 centres stockés.

Pour identifier tous les élèves d’une école, on n’invente pas un identifiant par élève : on combine un prénom et un nom.

Reste à savoir comment répartir l’information. La réponse est déjà dans le code plus haut, sous le nom de résidu : l’écart entre le vecteur exact et sa version arrondie, c’est-à-dire ce que le premier arrondi n’a pas su capturer. On le quantifie à son tour, puis on quantifie le résidu du résidu.

Schéma : un premier vecteur approche la cible grossièrement, un second corrige l'erreur restante.
Chaque niveau corrige uniquement l'erreur laissée par le précédent.

Une balance à plateaux. Pour peser 780 g, vous posez le poids de 500 g, il manque 280 g ; celui de 200 g, il manque 80 g ; puis 50, puis 20, puis 10. Chaque poids ne corrige pas le total, il corrige l’erreur des précédents. Et vous vous arrêtez quand la précision vous suffit.

Sur mes images, quatre niveaux de 64 centres donnent ceci :

Niveaux MSE Norme du résidu restant
1 0,02280 2,31 → 0,094
2 0,01807 → 0,024
3 0,01712 → 0,0076
4 0,01688 → 0,0033

Le résidu est divisé par 700 entre le début et la fin, et chaque niveau rapporte environ la moitié du gain précédent. C’est la signature d’une RVQ en bonne santé — je vais y revenir longuement dans l’article suivant, parce que c’est précisément cette signature qui m’a manqué pendant des heures.

Grille d'images : la première ligne montre les originaux, chaque ligne suivante ajoute un niveau de quantification.
La balance à plateaux, en images. Chaque ligne ajoute un niveau de correction.

Une limite à garder en tête : la qualité maximale reste celle de la reconstruction sans quantification du tout. Si l’autoencodeur est mauvais, aucune quantification ne le sauvera. On améliore d’abord l’autoencodeur.

L’idée n’est pas neuve — SoundStream l’a appliquée aux codecs audio en 2021, et elle circule dans le traitement du signal depuis les années quatre-vingt.

Le multi-niveaux n’a pas de réponse canonique

Avec un codec à huit niveaux, chaque extrait devient un tableau à deux dimensions : le temps d’un côté, les niveaux de l’autre. Or un modèle de langage attend une seule file. Comment gérer huit jetons par instant ?

Stratégie Principe Avantage Inconvénient
Aplatissement un jeton par niveau et par instant, à la queue leu leu très simple, le modèle reste un modèle de texte ordinaire la séquence est multipliée par le nombre de niveaux
Motif parallèle tous les niveaux d’un instant prédits en même temps aucune perte de compression temporelle le modèle doit tout décider d’un coup, sans hiérarchie
Motifs décalés les niveaux sont retardés les uns par rapport aux autres compromis, testé dans MusicGen réglage délicat
Motifs hiérarchiques un modèle par échelle, ou entrelacement complexe meilleurs résultats rapportés en 2025 complexité importante

En 2025, aucune ne s’est imposée. Pour qui débute, c’est une bonne nouvelle : il n’y a pas de solution canonique qu’on serait censé connaître, il y a un espace de conception encore ouvert.

Mimi, et le jeton qui porte le sens

Quatre années de recherche ont produit bien mieux que le codec artisanal. Mimi, développé chez Kyutai pour Moshi, apporte trois choses.

Une perte adverse plutôt que spectrale : un discriminateur apprend à repérer les reconstructions, le codec apprend à le tromper. Comme le juge s’améliore en même temps que l’élève, il n’y a pas de recette figée à exploiter.

Un abandon aléatoire de niveaux pendant l’entraînement — le RVQ dropout. Le codec apprend à ne pas dépendre de tous ses niveaux, et l’on peut donc tronquer librement à l’usage. J’ai mesuré ce que ça coûte : avec dropout, 0,01688 de MSE à quatre niveaux ; sans, 0,01560. On paie environ 8 % de qualité pour la liberté de tronquer. Sans lui, il faudrait réentraîner un modèle par nombre de niveaux.

Et surtout 12,5 vecteurs par seconde au lieu de 125, soit dix fois moins. Le corpus de 10 000 heures tombe de 134 Go à 54 Go. À fenêtre de contexte égale, le modèle voit dix fois plus loin : là où il apercevait une fraction de mot, il voit une phrase.

Le fond et la forme

Le tout premier niveau de Mimi n’est pas de même nature que les autres. C’est le jeton sémantique, distillé depuis WavLM, et son rôle est de porter le sens sans nécessairement contribuer à la qualité de reconstruction.

L’expérience qui le démontre est élégante. On entraîne un modèle sur les 32 niveaux, puis à la génération on impose de force les jetons sémantiques de l’audio d’origine, en laissant le modèle inventer tout le reste. Le résultat : la voix obtenue est complètement différente, mais elle dit exactement la même chose.

Schéma : le premier niveau porte le contenu de la parole, les niveaux suivants portent la voix et le timbre.
Le premier niveau porte ce qui est dit, les autres portent la manière dont c'est dit.

Le jeton sémantique est le scénario du film. Les jetons acoustiques sont le casting, les décors et la lumière. On peut refaire le film avec d’autres acteurs sans changer une ligne du scénario.

Le fossé entre modalités

Le bilan de Volhejn est mesuré. Avec un codec, un modèle de langage tout à fait ordinaire produit de la parole partiellement cohérente ; sans codec, le même modèle exactement produit du grésillement. La démonstration est faite. Mais on reste très loin des modèles de texte.

Moshi modélise trois flux en parallèle : l’audio de l’utilisateur, son propre audio, et un flux de texte qui joue le rôle de monologue intérieur. Les ablations montrent que ce flux textuel aide massivement — et c’est là que l’article devient un peu mélancolique. L’essentiel du raisonnement semble délégué au texte, tandis que les flux audio finissent par ne servir que de reconnaissance et de synthèse vocales intégrées.

Plus d’un an après Moshi, personne n’explique vraiment pourquoi les modèles audio restent en retard. Volhejn appelle ça le fossé entre modalités, et conclut que c’est ce mystère non résolu qui rend le domaine passionnant.

Une piste à surveiller : certains travaux récents se passent entièrement de jetons discrets et travaillent dans un espace latent continu. Si cette approche mûrit, toute la machinerie décrite ici deviendrait un détour historique.


Voilà pour la mécanique telle qu’elle est censée fonctionner.