GUIDE · WILLIAM LEEMANS
Le modèle de bout en bout
Lecture ~49 min Vérifié le 16 août 2026
L’anatomie de la machine
La machine reçoit un texte et rend un token. Pas une réponse, pas une phrase, pas une décision : un token, et un seul, par passage. Ce que vous voyez s’écrire à l’écran est cette opération répétée sur un document qui s’allonge d’un token à chaque fois.
Tout le reste de ce Guide déroule cette phrase. Cette première section en est la traversée entière, tenue en une page. Vous pouvez vous y arrêter : elle ne retient pas les conclusions des dix qui suivent, elle les donne d’avance. Ce qu’elle ne donne pas, c’est de quoi chaque étage est fait, ni ce que chacun change à votre session.
Voici les étages qu’un passage traverse. L’entrée de chacun est la sortie du précédent ; la première est votre requête, c’est-à-dire vos messages, l’historique de la conversation et les résultats d’outils déjà obtenus.
| L’étage | Ce qui en sort |
|---|---|
| Le programme d’appel | un seul document, refait à chaque appel |
| Le découpage | une suite de numéros pris dans une liste finie |
| La mise en vecteurs | un vecteur par position, portant son rang |
| L’attention | chaque position, augmentée de ce qui la précède |
| Le réseau dense | la même position, transformée sans ses voisines |
| L’empilement | un dernier jeu de vecteurs, après vingt-huit tours |
| La projection | un score par entrée, pour la dernière position seule |
| La mise en probabilités | une probabilité par entrée, dont la somme fait 1 |
| Le tirage | un token, et un seul, selon vos réglages |
| La boucle | le document du passage suivant, ce token en plus |
Le dernier rang rouvre le premier, et c’est la seule boucle de la machine. Rien d’autre que le document ne passe d’un token au suivant — ce qui est gardé en chemin l’est pour éviter de recalculer, jamais pour se souvenir.
Trois choses n’entrent pas dans ce tableau, et elles occupent la seconde moitié du Guide.
- Le coût. Lire un document et l’écrire ne se paient pas de la même façon, la lecture se faisant en une fois et l’écriture un token à la fois. De là viennent le délai avant la première ligne, le débit régulier ensuite, et le second tour qui démarre plus vite que le premier.
- L’échelle. L’objet suivi ici tient dans un fichier d’un gigaoctet sur un portable, quand votre agent travaille sur des centaines de milliers de tokens. Les étages restent les mêmes, mais il faut leur ajouter de quoi rester payables, et il n’est plus vrai que tout le fichier serve à chaque token.
- L’origine. Ces poids sont le résidu d’un programme qui a tourné une fois, et ce qu’on peut encore écrire dessus après coup est plus étroit que le vocabulaire des offres ne le laisse croire.
Le reste du Guide est cette page, étage par étage.
Le document que reçoit le modèle
Un seul document, reconstruit en entier à chaque appel. Ce que vous appelez « la conversation » n’existe nulle part. À chaque appel, un programme rassemble instructions, tours précédents, fichiers lus et résultats d’outils en une seule chaîne, puis la remet au modèle, qui n’a jamais rien vu d’autre. « Le contexte est la seule réalité du modèle » en tire ce qu’il faut en faire. Cette section ouvre le document et regarde de quoi il est fait.
L’objet que ce Guide suit d’un bout à l’autre est la trace que vous avez déroulée dans « Ce qu’est un LLM » : Qwen2.5-Coder-1.5B-Instruct, quantifié à 4 bits, servi par llama.cpp sur un portable Apple M3 Pro. Tous les chiffres qui décrivent ce modèle sont lus dans son fichier de poids, celui dont le harnais de capture enregistre l’empreinte SHA-256, ou dans la capture elle-même. Aucun n’est un ordre de grandeur de confort. Les durées, elles, décrivent cette machine-là autant que ce modèle : leur forme se transporte, leur magnitude non. Et aucun chiffre ne décrit votre agent. Un modèle de 1,5 milliard de paramètres à 4 bits démontre un mécanisme, il ne mesure pas la machine que vous pilotez en session.
Le gabarit de conversation
Les rôles sont des chaînes de caractères, et rien d’autre. Le fichier de poids transporte, à côté des poids, un gabarit : un petit programme qui aplatit une liste de messages en une seule chaîne. Voici ce que celui de ce modèle produit pour un tour d’agent qui a lu un fichier.
<|im_start|>system
Tu réponds en français.<|im_end|>
<|im_start|>user
Quelle version de Pydantic utilise ce projet ?<|im_end|>
<|im_start|>assistant
<tool_call>
{"name": "read_file", "arguments": {"path": "pyproject.toml"}}
</tool_call><|im_end|>
<|im_start|>user
<tool_response>
pydantic = "^2.9"
</tool_response><|im_end|>
<|im_start|>assistant
Trois choses se lisent directement dessus, et chacune rend compte de quelque chose que vous avez déjà vu.
Le document se termine par une amorce, <|im_start|>assistant, sans
contrepartie de fermeture. Le modèle ne « répond » pas. Il continue un texte
dont la dernière ligne annonce que c’est à l’assistant d’écrire, ce qui est la
boucle de « Ce qu’est un LLM » vue depuis l’entrée.
Le résultat de l’outil n’a pas de rôle à lui. Le gabarit de ce modèle le
range dans un tour user et l’entoure de <tool_response>, une balise faite de
caractères ordinaires. Ce sont deux détails du programme, pas deux niveaux de
privilège. « Le contexte est la seule réalité du modèle » en tire ce qu’il faut
en faire ; ce qui se lit ici est la forme exacte que prend sa thèse. Un rôle est
une position dans une chaîne, et system un mot de sept lettres.
Enfin, si aucun message système n’est fourni, ce gabarit-là en écrit un d’office : « You are Qwen, created by Alibaba Cloud. You are a helpful assistant. » Une consigne que vous n’avez pas écrite, en anglais, en tête du document. Quand un modèle a un ton par défaut que vous n’avez pas demandé, cherchez d’abord une ligne comme celle-là.
Les tokens spéciaux
<|im_start|> et <|im_end|> sont deux entrées du vocabulaire, numérotées
151 644 et 151 645, tirées exactement comme le sont if ou return. Le modèle
ne les interprète pas : il les tire. Le fichier déclare la seconde comme token
de fin, et le programme qui pilote la génération s’arrête quand elle sort.
C’est tout ce que « s’arrêter » veut dire. Aucune règle ne décide que la réponse
est finie ; un token la finit.
Ce qui distingue ces marques du texte que vous tapez ne se joue pas dans le
modèle. Une marque de rôle est une seule entrée du vocabulaire, tandis que
la suite de caractères <|im_start|> tapée dans un message se découpe en tokens
ordinaires : le programme d’appel désactive l’interprétation des marqueurs sur
tout ce qui ne vient pas de lui. La frontière entre le balisage et le contenu
est tenue par le tokeniseur, à l’entrée, une fois. Rien, plus loin dans la
machine, ne la re-vérifie.
La capture le montre en négatif. Dans la trace A, à partir du dix-neuvième
token, <|fim_middle|> et <|endoftext|> entrent dans les cinq candidats de
tête, l’un marqueur de complétion au milieu d’un fichier, l’autre marqueur de
fin de document : 0,0008 % et 0,0007 % au dix-neuvième, sous 1 et 2 au
vingtième. Ils sont sur la même échelle que le code, dans la même liste. Ils ne
l’emportent jamais — les deux traces se sont arrêtées parce que la capture
demandait un nombre fixe de tokens, pas parce qu’un token de fin est sorti.
D’où deux observations familières. Une réponse coupée net au milieu d’une phrase n’a pas planté : une limite de tokens a été atteinte avant que le token de fin ne sorte. Et un modèle qui « n’arrive pas à s’arrêter » écrit un texte où ce token-là reste sous ses concurrents, tour après tour.
Le vocabulaire, une liste finie
Le fichier contient une liste de 151 936 entrées, arrêtée avant
l’entraînement et jamais rouverte depuis : 151 643 fragments de texte, 20
marqueurs de contrôle, 2 balises d’outil (<tool_call> et </tool_call>, qui
sont donc du vocabulaire et non de la syntaxe), et 271 emplacements laissés
vides. Tout ce que ce modèle écrira sortira de cette liste, et rien d’autre.
C’est une contrainte plus dure qu’elle n’en a l’air. Le nom de votre classe
n’existe pas dans la liste : il sera produit en trois ou quatre morceaux
concaténés. Les entrées sont numérotées, et le numéro ne veut rien dire. if
est l’entrée 421, fib la 15801, et trois espaces consécutifs forment l’entrée
262 tandis que quatre en forment une autre, la 257. Pour le modèle, l’indentation
est un symbole parmi cent cinquante mille, et non un espacement.
Une image devient des vecteurs
Une image jointe devient une suite de vecteurs qui prennent la place de
positions dans le document, et elle est donc du document au même titre que le
reste. Le fichier suivi n’a pas d’yeux, et son vocabulaire réserve pourtant les
entrées <|vision_start|>, <|image_pad|> et <|video_pad|>, la place que les
modèles de la même famille dotés d’un encodeur visuel viennent occuper. Le
mécanisme est là tout entier : l’encodeur ne décrit pas l’image en mots, il
produit directement ces vecteurs.
Son coût se mesure donc dans la même unité que le texte. C’est pourquoi une capture d’écran consomme du contexte, se facture, et pousse le début de la conversation vers la sortie de la fenêtre.
À ce stade, l’objet suivi tient en un compte : le prompt de la trace A fait 12 tokens, celui de la trace B en fait 37. Ni l’un ni l’autre n’a été passé au gabarit. La capture parle au modèle en complétion brute, pour que ce qu’on observe soit le modèle et non l’emballage.
Des tokens aux vecteurs
Le document est découpé, et à l’entrée du modèle il n’y a déjà plus de texte.
Le vecteur d’un token
Le fichier contient un tableau de 151 936 lignes de 1 536 nombres, une ligne par entrée du vocabulaire. Découper le document, c’est obtenir une suite de numéros ; entrer dans le modèle, c’est remplacer chaque numéro par sa ligne. Les 12 tokens de la trace A deviennent 12 vecteurs de 1 536 nombres, et le texte n’existe plus nulle part dans la machine.
Ces lignes portent un nom, les embeddings, et leurs coordonnées ne sont pas arbitraires : elles ont été ajustées pendant l’entraînement, et deux entrées qui apparaissent dans les mêmes voisinages finissent proches. C’est ce qui explique une chose que vous constatez tous les jours sans y penser, à savoir que deux formulations qui ne partagent presque aucun mot obtiennent la même réponse. La proximité qui compte se joue sur des vecteurs, jamais sur des chaînes de caractères.
Un mot, un vecteur par contexte
Le même token d’entrée n’a pas un vecteur : il en a un par contexte. La ligne du tableau est un point de départ, et rien de plus. Chaque étage que la suite décrit réécrit le vecteur de chaque position en fonction des positions qui la précèdent, et il n’existe aucun endroit dans la machine où l’on pourrait lire « le sens du mot ».
Prenez, dans la trace B, la position qui précède .dict : la dernière du
document au moment où ce token-là sort, c’est-à-dire les 37 tokens du prompt
plus les huit que le modèle a déjà écrits. À l’entrée, son vecteur est celui que
le tableau donne à son token, le même partout ailleurs. À la sortie du dernier
étage, il porte de quoi placer .dict en tête à 50,6 %. Il porte donc, entre
autres, la mention « Pydantic v2 » écrite tout en haut du document, et la
dépendance traverse en plus les huit tokens que le modèle vient de produire.
Voilà le mécanisme sous une observation banale : une phrase de cadrage placée en tête change la suite alors qu’aucun mot de votre demande n’a bougé. Elle n’a pas ajouté d’information « lue » quelque part. Elle a déplacé les vecteurs de tout ce qui la suit.
L’ordre est une information ajoutée
L’ordre est ajouté aux vecteurs, explicitement, par une rotation appliquée en fonction de leur rang : deux positions éloignées sont tournées l’une par rapport à l’autre d’un angle qui ne dépend que de leur écart. Rien, dans ce qui précède, ne le disait à la machine. La ligne du vocabulaire est la même où que le token tombe, et l’étage suivant compare les positions deux à deux, une opération qui, seule, ne distingue pas une liste de son mélange.
Cette rotation est un paramètre, pas une image : le fichier déclare sa fréquence de base, 1 000 000 pour ce modèle-ci. Le procédé s’appelle RoPE, et son nom ne dit rien de plus que ce paragraphe. La position est une propriété que l’on écrit dans les vecteurs, jamais une propriété du texte.
Ce que ça explique : la même liste réordonnée ne donne pas la même réponse, et deux consignes identiques n’ont pas le même effet selon l’endroit où elles tombent dans le document. Ne mettez pas ça sur le compte de la négligence — l’écart entre deux positions est une des grandeurs sur lesquelles le modèle a été ajusté.
Ce que fait une couche
Une couche réécrit toutes les positions en une fois. À l’entrée du premier étage, il y a une position par token, chacune tenant 1 536 nombres.
Requête, clé, valeur
L’opération s’appelle l’attention, et elle tient dans trois vecteurs. Chaque position produit, à partir de son propre vecteur et par trois multiplications matricielles distinctes, une requête, une clé et une valeur : Q, K et V sur les schémas. Aucune des trois ne « veut » ni ne « cherche » quoi que ce soit. Ce sont trois projections du même contenu, entraînées séparément, donc trois manières de le résumer.
Le calcul tient en trois temps.
- Pour la position que l’on met à jour, on multiplie sa requête par la clé de chaque position, ce qui donne un nombre par paire : un score de compatibilité entre ce que cette position projette et ce que l’autre expose. On le divise par la racine du nombre de dimensions concernées, 128 ici, soit environ 11,3, faute de quoi les scores explosent avec la taille des vecteurs.
- Ces scores passent par un softmax, la même opération qu’à la toute fin du passage. Elle les transforme en poids positifs dont la somme fait exactement 1.
- On additionne les valeurs de toutes les positions, pondérées par ces poids, et on ajoute le résultat au vecteur de la position mise à jour.
Le « exactement 1 » du deuxième temps est le point qui compte. Le poids donné à une position est pris à toutes les autres : c’est un partage, jamais une lecture. « Tokens, fenêtres et attention » vous fait constater que l’attention se dilue et que le bruit coûte ; voici pourquoi elle se dilue. Cent tokens de logs sans rapport n’occupent pas seulement de la place — ils reçoivent une part du même total, et ce qu’ils reçoivent, la ligne qui comptait ne l’a pas.
Un token ne voit que ce qui le précède
Avant le softmax du deuxième temps, les scores calculés avec les positions suivantes sont écartés. C’est le masque causal, et c’est tout ce qu’il est. La position 12 se met à jour à partir des positions 1 à 12 ; les positions 13 et au-delà n’entrent dans aucun de ses calculs, à aucun étage.
C’est le mécanisme exact de l’irréversibilité que « Ce qu’est un LLM » fait constater. Un token déjà produit ne se reprend pas, parce qu’il n’existe pas de chemin par lequel ce qui vient après pourrait entrer dans son calcul. La suite de la conversation ne relit pas le passé : elle s’ajoute au document, et les positions déjà écrites gardent la valeur qu’elles avaient. Votre démenti au tour suivant est une position de plus, pas une correction.
Plusieurs lectures en parallèle
Ce calcul n’est pas fait une fois mais douze, en parallèle, sur des tranches distinctes des 1 536 dimensions, 128 chacune. Douze jeux de projections donnent donc douze pondérations différentes des mêmes positions, concaténées à la fin puis remises en forme par une quatrième matrice. Ces douze exemplaires sont les têtes de l’attention, et douze est un nombre lu dans le fichier, pas une image.
La tentation est de leur distribuer des rôles : celle-ci suivrait la syntaxe,
celle-là les nombres. Rien dans le fichier ne dit ça. Il dit qu’il y a douze
jeux de poids distincts couvrant chacun 128 dimensions ; une spécialisation
éventuelle serait un résultat de l’entraînement, jamais une consigne
d’architecture. Ce qui est acquis tient dans le nombre : plusieurs pondérations
du même texte sont calculées à la fois, et une continuation satisfait plusieurs
contraintes d’un coup, l’indentation courante, la parenthèse ouverte douze
tokens plus tôt, le nom de la fonction en cours. La trace A produit + à
99,9985 % au beau milieu d’une expression récursive, et rien de ce qui la
contraint n’a été lu séparément.
Rassembler, puis transformer
L’attention est la seule opération de la machine qui fasse circuler de l’information entre les positions. Ce qui la suit dans le bloc, un réseau dense appelé MLP, travaille sur chaque position isolément, sans regarder ses voisines. Le calcul y tient en trois matrices : deux élargissent le vecteur de 1 536 à 8 960 nombres, une porte multiplie terme à terme les deux résultats, l’un servant de filtre à l’autre, et une troisième ramène le tout à 1 536. Cette porte s’appelle SwiGLU, et ce qu’elle change tient dans la phrase précédente : un des deux élargissements ne sert qu’à décider de ce qui passe de l’autre.
C’est là que sont les poids. Dans un bloc de ce modèle, les trois matrices de cet étage pèsent 41,3 millions de paramètres ; les quatre de l’attention en pèsent 5,5 millions. L’attention décide de ce qui est rassemblé, cet étage-ci contient l’essentiel de ce qui est appliqué au résultat.
L’observation dont il rend compte est celle de « Ce que le modèle sait » : un modèle produit un idiome correct sans que rien de pertinent ne soit dans votre contexte. Ce n’est pas l’attention qui l’a trouvé quelque part dans la fenêtre, il n’y était pas. Il sort de ces matrices-là, où l’entraînement a laissé des régularités que rien ne date ni ne référence.
Ce qui empêche le signal de se perdre
Deux dispositifs, discrets, sans lesquels rien de ce qui précède ne pourrait être empilé.
Le premier, la connexion résiduelle : chacun des deux étages écrit son résultat en l’ajoutant au vecteur qui lui est arrivé, au lieu de le remplacer. Le vecteur d’une position traverse donc le modèle entier et se fait annoter au passage, ce qui permet à la vingt-huitième couche de recevoir encore quelque chose de l’entrée.
Le second, la normalisation : avant chaque étage, le vecteur est ramené à une échelle standard, puis rééchelonné par une liste de coefficients apprise, 1 536 nombres par étage, soit 3 072 dans un bloc qui en compte 46,8 millions. Sans elle, les amplitudes dérivent d’une couche à l’autre et le calcul cesse d’être exploitable après quelques étages.
Aucun des deux ne change ce que le modèle dit ; les deux changent ce qu’on peut construire avec.
L’ensemble, normalisation, attention à douze têtes, normalisation, MLP, et deux additions au passage, est ce qu’on appelle un bloc Transformer. C’est le seul motif de la machine, et le voici nommé une fois pour toutes : la suite ne le réexpliquera pas, elle le répétera.
Pourquoi les empiler
Parce que la composition demande des étages. Ce modèle enchaîne 28 couches, identiques dans leur forme, différentes dans leurs poids, chacune prenant en entrée la sortie de la précédente.
Aucune ne décide de rien. Il n’y a pas d’étage où le modèle « choisirait » son
token, pas de couche qui porterait la réponse, pas d’endroit dans le fichier où
.dict l’emporte sur .model. Il y a 28 réécritures successives des mêmes
vecteurs, et une distribution qui n’existe qu’après la dernière. C’est la
réponse à la question que « Ce qu’est un LLM » laisse ouverte : ces cinq nombres
ne sont lus nulle part, ils sont le produit de tout le passage.
Une conséquence se vérifie sur la trace elle-même. Le token le plus facile de la
trace A, 2 à 99,9998 % après une expression déjà écrite, et le plus disputé de
la trace B, .dict à 50,6 % contre .model à 10,2 %, ont coûté exactement les
mêmes 28 couches. La machine ne réfléchit pas plus longtemps sur ce qui est
difficile. L’hésitation ne se voit pas dans le temps de calcul, elle se voit
dans les nombres qui sortent. Un modèle qui « prend son temps » sur une question
ardue écrit plus de tokens — il ne travaille pas davantage sur chacun.
Ce que gagne l’empilement, c’est donc la composition : à partir de la deuxième couche, l’opération ne porte plus sur le texte mais sur une représentation que les précédentes ont déjà réécrite. C’est ce qui permet à une continuation de dépendre d’un rapport entre deux éléments qui ne se touchent nulle part dans le document, le type déclaré ligne 4 et l’appel ligne 300, que rien ne relie sinon ce que les couches intermédiaires en ont fait.
Du vecteur au token tiré
Seule la dernière position sert à produire le token suivant : c’est elle qui porte tout ce que le document a laissé. À la sortie de la vingt-huitième couche, il y a pourtant toujours une position par token et 1 536 nombres par position.
La projection sur le vocabulaire
Ce vecteur passe une dernière normalisation, puis une matrice de 1 536 × 151 936, la plus grosse du fichier, 233 millions de nombres à elle seule, une colonne par entrée du vocabulaire. Elle rend un score par entrée. Pas cinq : 151 936, y compris les 271 emplacements vides et les marqueurs de contrôle.
Les cinq candidats du pas-à-pas de « Ce qu’est un LLM » sont donc la tête d’une liste longue de tout le vocabulaire, tronquée pour l’affichage. Cette liste est produite entière, à chaque token, dans les deux traces comme dans votre session.
Du score à la probabilité
Ces scores sont des nombres quelconques, positifs ou négatifs. On les rend comparables par le softmax, déjà croisé dans chaque couche : passer chaque score à l’exponentielle, puis diviser par la somme de toutes. L’opération garantit deux propriétés, et les deux se constatent.
La première : le total fait 1, donc seuls les écarts comptent. Un score n’a aucune valeur en soi — il n’en a que devant les 151 935 autres. La deuxième : aucune entrée ne tombe à zéro. Toute chaîne de caractères que ce modèle peut écrire a, à chaque token, une probabilité non nulle d’être écrite, y compris la plus absurde.
La capture le montre à la décimale près. À l’étape décisive de la trace B, les cinq candidats affichés valent 50,6 %, 14,3 %, 13,6 %, 10,2 % et 3,8 %, soit un total de 92,5 %. Les 7,5 % qui restent sont répartis sur les 151 931 autres entrées. Ce que le composant affiche est le sommet d’une distribution qui couvre toute la liste, et ce qu’il reçoit de llama.cpp, ce sont des logarithmes de probabilités, dont le pourcentage est calculé à l’affichage.
Le tirage
Il reste à choisir. Le réglage de température divise les scores avant l’exponentielle : plus il est bas, plus les écarts s’étirent et plus la tête écrase le reste ; à zéro il n’y a plus de tirage du tout, la tête sort à tous les coups. Les filtres de troncature usuels, top-k, top-p et min-p, coupent ensuite la queue de la liste, et les trois font cela et rien d’autre : ils ne touchent ni aux scores ni à leur ordre, ils décident seulement où la liste s’arrête. Seul le critère de coupe les sépare. Ne garder que les k premiers candidats, c’est le top-k ; en garder juste assez pour couvrir une masse de probabilité donnée, c’est le top-p. Puis un tirage au sort a lieu, pondéré par ce qui reste.
Les deux traces que vous avez déroulées sont reproductibles, chacune pour une
raison différente : la trace A est prise en décodage glouton, où il n’y a rien à
tirer ; la trace B tire pour de bon, filtres compris, mais à graine fixée. Votre
agent ne vous donne ni l’un ni l’autre. C’est ici que se trouve l’interne de la
règle posée par « Ce qu’est un LLM » : la même demande ne redonne pas la même
sortie, et .model à 10,2 % est un token qui sort une fois sur dix.
Le token tiré est ajouté au document, et le passage recommence : 28 couches, une projection, un tirage, pour chaque token de la réponse.
Prefill, décodage, cache
Une requête enchaîne des milliers de passages, et pas de la même manière selon qu’elle lit ou qu’elle écrit. Le passage décrit jusqu’ici en produit un.
Lire tout le document en une fois
Les 28 blocs traitent le document entier en un seul passage : les mêmes matrices, multipliées par une matrice de positions au lieu d’un vecteur. Les positions du prompt sont toutes connues d’avance, et le masque causal garantit que la position 12 se calcule sans rien savoir de la 13. C’est ce que chiffrent les deux traces : le prompt de 12 tokens est lu à 241 tokens par seconde, celui de 37 à 662. Le plus long obtient le meilleur débit parce que faire défiler 1,04 Gio de poids coûte à peu près la même chose pour 12 positions que pour 37, et que ce coût fixe s’amortit sur leur nombre.
Ce premier temps s’appelle le prefill. Il ne produit rien parce qu’il n’a rien à produire : son résultat est l’état de toutes les positions à la sortie des 28 blocs, et non un token. Le token, lui, sort de la dernière position, et celle-là est la dernière à pouvoir être calculée.
Un passage complet par token
Chaque token de sortie est un passage complet : 28 blocs, une projection sur 151 936 entrées, un tirage. Dès le premier token écrit, la propriété qui rendait le prefill possible tombe, puisque la position suivante ne peut pas être calculée avant que le token de la précédente ait été tiré et ajouté au document.
Les mêmes traces écrivent à 50 et 61 tokens par seconde, alors que le calcul par position est identique. Ce qui change, c’est qu’il n’y a plus rien à grouper : produire un token demande de faire défiler l’intégralité du fichier de poids, 1 117 320 768 octets, pour une position, là où le prefill fait défiler les mêmes octets une fois pour 37. Ce second temps s’appelle le décodage, et il ne s’amortit jamais — un token de plus est un passage de plus.
Ce que le cache garde
Chaque bloc calcule, pour chaque position, une clé et une valeur. Toutes deux ne dépendent que du vecteur de cette position, lui-même issu de ce qui la précède, jamais de ce qui la suit. Elles sont donc définitives dès leur calcul.
D’où le procédé : les garder. Le tour suivant n’a plus qu’à calculer la requête, la clé et la valeur des positions neuves, et à les confronter aux clés déjà stockées. Ces nombres conservés sont le cache de clés et de valeurs, le KV cache : 512 par position et par bloc sur le modèle suivi, 14 336 pour les 28. La raison de ce chiffre-là est le sujet de la section suivante.
C’est aussi ce qui fonde la condition que « Le coût et la latence » pose sans la démontrer. La clé de la position 40 a été calculée à partir d’un vecteur qui porte les positions 1 à 39 ; changez le token 3, et ce n’est plus la même clé, ni celle de la 41, ni aucune des suivantes. Le cache ne se recolle donc pas par morceaux : il se réutilise depuis le premier token et s’arrête au premier qui diffère. C’est le masque causal lu à l’envers, et non une limite d’implémentation.
Ce que le procédé fait gagner se mesure. Un prompt de 3 084 tokens demande 2 436 ms de lecture à froid, et 12 ms quand le préfixe est déjà en cache : 3 083 positions reprises, une seule recalculée. Cette dizaine de millisecondes ne bouge pas avec la longueur du préfixe. Ces positions ne sont pas relues plus vite : elles ne sont pas relues du tout.
Ce qui rend un passage tenable
À l’échelle où travaille votre agent, des centaines de milliards de poids et des contextes de plusieurs centaines de milliers de tokens, le passage décrit jusqu’ici ne tiendrait ni en mémoire ni en temps. Tout ce qui précède décrit un fichier de 1,04 Gio déroulé sur 12 et 37 tokens. L’objet suivi cesse ici d’être l’exemple et devient le point de comparaison : ce qui suit est ce qu’un modèle de production ajoute à celui que vous venez de regarder tourner.
Moins de clés que de requêtes
Le modèle suivi le fait déjà, et son fichier le déclare : 12 têtes d’attention, mais 2 jeux de clés et de valeurs. Les têtes forment deux groupes de six qui partagent les mêmes clés et les mêmes valeurs, seule la requête restant propre à chacune. Le procédé s’appelle l’attention par groupes de requêtes, GQA, et ce qu’il change tient dans le cache : deux jeux de 128 dimensions, une clé et une valeur, sur 28 blocs, soit les 14 336 nombres par position de la section précédente. Avec douze jeux ce serait 86 016, six fois plus, et sur la fenêtre que ce fichier déclare, l’écart va de 896 Mio à 5,25 Gio. On passe d’un cache plus petit que les poids à un cache cinq fois plus gros qu’eux.
Ce dont ça rend compte : un fournisseur qui garde votre préfixe entre deux tours garde ces nombres-là quelque part, pour vous et pour toutes les sessions ouvertes en même temps. C’est ce partage qui rend un cache de préfixe facturable à une fraction du tarif d’entrée.
Compresser le cache au lieu de le réduire
Partager les clés, c’est en avoir moins. On peut aussi les avoir toutes et ne pas les stocker.
Le mécanisme : pour chaque position, le modèle projette son vecteur sur un vecteur latent nettement plus court que les clés et les valeurs qu’il remplace, une projection de rang faible, apprise pendant l’entraînement et non appliquée après coup. C’est ce latent, et lui seul, qui va au cache. Au moment de l’attention, chaque tête reconstruit ses propres clés et valeurs à partir du latent, par une matrice qui lui est propre. Cette décompression n’est qu’une multiplication de plus, donc elle se fond dans les matrices de la tête : les clés et les valeurs complètes n’ont jamais à exister en mémoire.
Un détail dit pourquoi ce n’est pas gratuit. La rotation qui écrit la position dans les vecteurs s’applique aux clés et ne survit pas à la décompression : l’angle dépend de la position, la décompression est la même pour toutes. Une petite part de la clé est donc laissée hors du latent, non compressée, pour porter la position. Le procédé s’appelle l’attention latente multi-têtes, MLA, et l’architecture qui l’a introduit annonce sur cette base un cache réduit de 93,3 % par rapport au modèle dense de la même famille.
Ce dont ça rend compte : deux modèles qui annoncent la même fenêtre ne coûtent pas la même chose à servir, et l’écart ne se lit sur aucune fiche.
Ne jamais écrire la matrice des scores
Le premier temps de l’attention produit un score par paire de positions. Sur la fenêtre que ce fichier déclare, cette matrice fait 32 768 × 32 768, près de 1,1 milliard de scores pour une tête, 12,9 milliards pour les douze, plus de 24 Gio en 16 bits quand tous les poids tiennent dans 1,04 Gio. Et elle est écrite en mémoire, relue pour le softmax, relue encore pour pondérer les valeurs. Le goulot est là, dans les allers-retours, et non dans la multiplication.
Le remède est de découper : garder en mémoire rapide un bloc de requêtes, y faire défiler les blocs de clés et de valeurs, accumuler au passage. Sauf qu’un softmax a besoin de la somme des exponentielles de toute la ligne, exactement ce qu’on refuse de tenir.
Ce qui débloque le découpage est un softmax calculé en ligne. On tient trois nombres courants par ligne : le maximum rencontré jusqu’ici, la somme des exponentielles rapportée à ce maximum, et la somme pondérée des valeurs accumulée jusqu’ici. À chaque nouveau bloc, on calcule ses scores et on prend son maximum ; s’il dépasse le maximum courant, on multiplie la somme et le résultat déjà accumulé par l’exponentielle de l’écart entre l’ancien et le nouveau, un seul facteur scalaire qui corrige d’un coup tout ce qui avait été accumulé sous l’ancienne référence, puis on ajoute la contribution du bloc. À la fin, on divise par la somme. Le résultat est le softmax de la ligne entière, à l’arithmétique près, obtenu sans que la ligne ait jamais existé. Le maximum n’est pas là pour la beauté du geste : l’exponentielle d’un score brut déborde. Le procédé s’appelle FlashAttention, et ce softmax-là est tout ce qui le rend possible.
Ce dont ça rend compte : le coût supprimé est celui de la mémoire, jamais celui du calcul. Le nombre de multiplications reste quadratique, et c’est pourquoi allonger un contexte reste cher même sur un moteur récent.
Ne regarder que ce qui est proche
Autre voie, plus brutale : rétrécir le masque. Dans une partie des blocs, une position ne voit plus tout ce qui la précède mais seulement les quelques milliers de positions immédiatement antérieures, une fenêtre qui glisse avec elle, et le coût de ces blocs-là cesse de croître avec la longueur du document. L’information circule quand même de bout en bout, et c’est l’empilement qui l’explique : chaque bloc local la déplace de la largeur de sa fenêtre. Restent quelques blocs à attention complète, intercalés, dont le cache est alors le seul à grossir sans limite. Le procédé s’appelle l’attention locale, ou attention à fenêtre glissante.
Le modèle suivi ne fait rien de tel. Ce dont ça rend compte, chez ceux qui le font : une fenêtre énorme peut être annoncée sans que le coût par token suive, parce qu’elle n’est pas servie par le même mécanisme partout dans le document.
Étirer une fenêtre après coup
Le fichier suivi déclare une fenêtre de 32 768 tokens et une fréquence de rotation de 1 000 000, les deux nombres que l’entraînement a vus. Servir le même modèle sur 128 000 tokens ne revient donc pas seulement à accepter plus de positions : c’est lui présenter des angles qu’aucun exemple d’entraînement n’a produits.
Le mécanisme qui rend l’extension exploitable ne rééchelonne pas ces rotations de manière uniforme. Elles couvrent un spectre : les fréquences rapides encodent des écarts de quelques tokens, les lentes des écarts de plusieurs milliers. Les rapides sont laissées intactes, parce que localement rien ne doit changer. Les lentes, dont la période n’était même pas parcourue en entier dans la fenêtre d’origine, sont interpolées : leur angle est divisé par le facteur d’extension, si bien que la position 100 000 du modèle étendu retombe là où tombait la 25 000. Les fréquences intermédiaires reçoivent un mélange gradué des deux. S’y ajoutent une correction du softmax, les scores étant divisés par une température qui croît avec le facteur d’extension parce qu’une ligne plus longue aplatit la distribution, et un court réajustement sur des textes longs. Le procédé s’appelle YaRN.
C’est le mécanisme sous la règle que « Tokens, fenêtres et attention » énonce sans le donner : la fenêtre annoncée est une capacité, pas une performance. Une fenêtre étendue après coup n’est pas une fenêtre native, et ses positions lointaines sont servies par des angles interpolés et quelques milliers d’exemples de réajustement.
Écrire plusieurs tokens par passage
Reste le décodage et sa limite : un passage par token. Deux procédés la contournent, et ils se combinent.
Le premier ajoute au modèle, à côté de la projection finale sur le vocabulaire, une ou deux projections qui produisent depuis le même vecteur une distribution pour la position +2, puis +3. À l’entraînement c’est un signal de plus, chaque position devant porter davantage que le token immédiatement suivant ; au service, ce sont des propositions, bon marché et éventuellement fausses. C’est la prédiction multi-token.
Le second en fait quelque chose. Un modèle petit et rapide, ou ces projections-là, écrit d’avance quelques tokens ; le grand modèle les évalue alors en un seul passage, puisqu’ils sont tous connus. Ce n’est plus un décodage, c’est un prefill, et un prefill groupe. Il compare position par position sa propre distribution à celle qui a produit le brouillon et accepte par échantillonnage par rejet : chaque token passe avec une probabilité égale au rapport des deux, et au premier refus il est retiré d’une distribution corrigée, le reste du brouillon jeté. Cette règle est construite pour que la sortie suive exactement la distribution que le grand modèle aurait produite seul, et non pour l’approcher prudemment. Le procédé s’appelle le décodage spéculatif.
Ce dont ça rend compte : la vitesse d’écriture varie d’un endroit à l’autre d’une même réponse. Un bloc d’imports, une accolade fermante ou une formule répétée sont devinés juste sur plusieurs tokens d’affilée et sortent d’un coup ; une phrase dense se fait refuser au premier ou au deuxième et retombe à un passage par token.
Combien de modèle tourne vraiment
Depuis peu, le nombre inscrit sur la fiche ne le dit plus. La section précédente a rendu le passage tenable ; reste à savoir combien de modèle il traverse.
Remplacer le MLP par plusieurs
Les poids sont dans le MLP : sur les 46,8 millions de paramètres d’un bloc du modèle suivi, 41,3 sont ses trois matrices. C’est cette part-là qu’on démultiplie. Le procédé remplace l’unique MLP de chaque bloc par plusieurs dizaines, parfois plusieurs centaines, de MLP indépendants, et place devant eux une petite matrice qui note le vecteur de la position contre chacun et n’en retient que les deux ou huit mieux notés. Seuls ceux-là sont exécutés ; l’attention reste entière et commune. Ces MLP s’appellent des experts, la matrice qui les note un routeur, et l’architecture entière un mélange d’experts, MoE.
Le mot « expert » est le piège de cette section. Le routeur est une matrice entraînée avec le reste, sur un seul critère, réduire l’erreur de prédiction, assorti d’un terme qui répartit la charge pour qu’aucun expert ne prenne tout ni ne reste inutilisé. Ce qu’il sépare n’a aucune raison d’être nommable, et ce n’est ni « le français » ni « Python ». Le choix se refait d’ailleurs à chaque token et à chaque bloc : un mot de votre prompt ne traverse pas un expert — il traverse une combinaison différente à chaque étage.
Le nombre qui compte n’est pas celui de la fiche
De là, deux comptes au lieu d’un. Le modèle suivi est dense : 1,5 milliard de paramètres annoncés, 1,5 milliard traversés, à chaque token, sans exception. Sur un modèle à experts, le total additionne tous les experts, tandis que les paramètres activés ne comptent que ceux que le routeur a retenus, plus tout ce qui n’est pas un expert. L’architecture citée plus haut en donne la mesure dans sa génération suivante : 671 milliards de paramètres au total, 37 activés par token, un rapport de dix-huit.
Les deux nombres ne décrivent pas la même chose. Le total dit ce qu’il faut tenir en mémoire pour servir le modèle ; les paramètres activés disent combien de calcul chaque token déclenche, donc la vitesse et le prix au token. Tant que les modèles étaient denses c’était le même nombre, et une fiche pouvait n’en afficher qu’un.
Le même fichier, servi à des précisions différentes
Un poids est un nombre, et un nombre occupe le nombre de bits qu’on lui accorde. L’entraînement se fait couramment sur 16 bits, dans un format qui sacrifie des chiffres de précision pour garder de l’amplitude, assez pour que les gradients ne s’annulent ni ne débordent : c’est le BF16. Le service n’est pas tenu de garder ce format-là, et réduire ainsi la précision des poids s’appelle la quantification.
Le modèle suivi est justement servi réduit, et son fichier dit ce que ça recouvre : la réduction n’est pas uniforme. Sur ses 339 tenseurs, la plupart des matrices sont stockées autour de 4 bits par poids ; la projection finale sur le vocabulaire, la moitié des matrices de sortie du MLP et la moitié des matrices de valeurs sont gardées autour de 6 ; les coefficients de normalisation et les biais d’attention, 144 896 nombres, restent en flottants 32 bits pleins. Au total 1 117 320 768 octets pour 1 777 088 000 nombres stockés : 5,03 bits par poids en moyenne, contre 16 à l’entraînement. (Ce compte dépasse l’étiquette « 1,5 B » parce que le fichier matérialise la matrice du vocabulaire aux deux bouts ; en retirer une copie donne 1 543 714 304.)
Ce dont ça rend compte : la précision est un choix de service, jamais une propriété du modèle. Deux fournisseurs qui affichent le même nom ne servent pas forcément les mêmes nombres, et rien sur la fiche ne dit lesquels. Les formats de service les plus récents descendent à 8 bits, le FP8, et certains modèles sont désormais entraînés directement dans ce format.
Trois sections viennent de dire ce que ces poids traversent, ce qu’il faut leur ajouter pour que le passage tienne, et combien d’entre eux tournent réellement. Aucune n’a dit d’où ils viennent.
D’où viennent ces poids
Ils sont le résidu d’un programme qui a tourné une fois, pendant des semaines, sur des machines que personne ne rallumera. Les 339 tenseurs, les 28 blocs, les 1 536 nombres par position : tout en sort, et de nulle part ailleurs.
Le corpus, et ce qu’on en retire
Le modèle suivi a un rapport technique, et il donne le chiffre : son pré-entraînement a vu plus de 5 500 milliards de tokens. Mis en regard des 1 543 714 304 paramètres comptés plus haut, cela fait environ 3 500 tokens vus par nombre stocké.
Un corpus de cette taille est une chaîne de traitement, et non une bibliothèque. On part d’archives du web et de dépôts publics, et on retire : le balisage, la navigation, les gabarits recopiés d’un site à l’autre, les langues qu’on ne veut pas, les documents qu’un classifieur juge trop éloignés de la prose utile, et pour un modèle de code les fichiers générés et les dépôts sans licence. Ce qui reste est mélangé dans des proportions décidées à l’avance : tant de code, tant de prose, tant de mathématiques.
C’est le contenu exact de la question que « Ce que le modèle sait » apprend à se poser. « Massivement représenté dans un corpus public » ne veut pas dire « ça existe quelque part sur le web ». Ça veut dire que ça a survécu à cette chaîne-là, et qu’après elle il en restait assez d’exemplaires pour peser.
Assez, mais pas trop, et c’est le point contre-intuitif. Un même passage présent des milliers de fois, une licence, un en-tête, un paragraphe recopié de dépôt en dépôt, n’apprend rien de plus qu’une fois ; il fait autre chose. Les modèles entraînés sur des données non dédupliquées recrachent mot pour mot du texte de leur corpus dans plus d’un pour cent de ce qu’ils écrivent sans qu’on le leur demande ; entraînés sur les mêmes données dédupliquées, ils émettent dix fois moins souvent du texte mémorisé et atteignent la même précision en moins d’étapes. La déduplication ne fait donc pas que gagner de la place : elle empêche la répétition brute de devenir de la récitation.
Un seul geste, répété
Le programme d’entraînement fait exactement ce que ce Guide a décrit du document au token tiré, plus deux choses.
On prend un morceau du corpus et on le passe dans le modèle. Le masque causal fait que chaque position ne voit que ce qui la précède : toutes les positions du morceau sont donc des exercices simultanés, chacune produisant sa distribution sur les 151 936 entrées, et pour chacune on connaît déjà le token qui suivait réellement. Première chose ajoutée, la perte : un nombre qui vaut d’autant moins que la probabilité donnée au bon token était grande. Seconde, la rétropropagation, qui reparcourt le passage en sens inverse et rend, pour chacun des paramètres, la direction dans laquelle le déplacer pour faire monter cette probabilité. Un optimiseur décide alors de combien, en tenant à jour pour chaque poids une moyenne de ce que les pas précédents lui ont demandé, ce qui explique qu’entraîner réclame plusieurs fois la mémoire qu’il faut pour servir. Puis on recommence, sur le morceau suivant.
Il n’y a rien d’autre, et il faut relire la perte une seconde fois, parce qu’elle est le seul juge de toute l’opération : elle mesure la probabilité donnée à ce qui suivait, jamais la vérité de ce qui suivait. Une affirmation fausse massivement présente dans le corpus rapporte exactement autant qu’une vraie. Rien d’autre n’est mesuré, à aucun moment des semaines qui suivent. C’est l’interne de ce que « Limites et hallucinations » appelle le fonctionnement nominal. Le modèle n’a pas manqué un objectif de véracité — il n’en a jamais eu.
Pourquoi ça ne se refait pas
Parce qu’à cette échelle, il n’existe pas de version incrémentale de l’opération. Le fichier suivi pèse 1 117 320 768 octets parce qu’il est servi, à cinq bits par poids. À l’entraînement il faut tenir les poids en seize bits, plus un gradient par poids, plus l’état que l’optimiseur garde pour chacun, et le tout n’entre dans aucune machine. Le calcul se découpe donc dans trois directions à la fois : les morceaux de corpus sont répartis entre les machines, les blocs sont répartis entre elles, et les grandes matrices sont elles-mêmes coupées en tranches. Ces trois coupes s’appellent le parallélisme de données, de pipeline et de tenseurs, et chacune oblige les machines à s’échanger des résultats partiels à chaque pas. Un entraînement est un seul calcul étalé sur un millier de machines, et non un millier de calculs.
Un tel calcul doit survivre à ses propres pannes pendant des semaines. Le pré-entraînement du plus gros modèle de la famille Llama 3 a tourné sur jusqu’à 16 000 GPU H100, et ses auteurs relèvent, sur une fenêtre de 54 jours, 466 interruptions dont 419 imprévues, une toutes les trois heures environ, sur un travail qu’il faut reprendre là où il s’est arrêté.
Ce dont ça rend compte : la date de coupure est une propriété du fichier, et non une politique commerciale. Ce que « Ce que le modèle sait » énonce comme une règle, les poids sont figés et l’entraînement a eu lieu une fois, tient ici à la nature du geste. On ne le refait pas parce qu’une bibliothèque a changé d’API.
Pourquoi les modèles ont cessé de grossir
À budget de calcul fixé, on peut entraîner un gros modèle sur peu de données ou un plus petit sur davantage. Pendant quelques années, l’industrie a pris la première branche et la taille annoncée a servi de mesure. La mesure a tranché autrement. En entraînant plus de quatre cents modèles pour établir le rapport, une équipe a montré qu’à budget constant la taille et le nombre de tokens doivent doubler ensemble. Elle l’a vérifié en construisant un modèle de 70 milliards de paramètres, nourri de quatre fois plus de données, qui dépasse celui de 280 milliards auquel il coûte autant, et aussi ceux de 175 et de 530 milliards. Ce modèle s’appelle Chinchilla, et le rapport porte son nom.
C’est le premier des deux motifs pour lesquels le nombre affiché sur une fiche a cessé de vouloir dire quelque chose ; la section précédente a donné l’autre. Et le mouvement est allé plus loin que ce résultat, dans une direction qu’il n’énonce pas. Le point optimal décrit ici optimise l’entraînement, qui a lieu une fois, alors qu’un modèle servi paie à chaque requête. Il est donc rentable de dépasser ce point délibérément, et d’entraîner un petit modèle bien au-delà de ce que son budget d’entraînement justifierait, pour qu’il soit bon marché à servir ensuite. L’objet suivi en est exactement un : un milliard et demi de paramètres, plus de 5 500 milliards de tokens vus. Aucun argument de budget d’entraînement ne justifie ce rapport ; le coût de service le justifie entièrement.
Ce dont ça rend compte : deux modèles portant la même étiquette de taille à trois ans d’écart n’ont pas vu la même chose, et ce n’est pas le plus ancien qui a vu le plus. La taille annoncée date un modèle aussi mal qu’elle le mesure.
Ce qu’il reste de tout ça
Reprenons le rapport du début. Plus de 5 500 milliards de tokens sont entrés, un milliard et demi de nombres sont sortis. Ce qui a été gardé n’est pas un échantillon du corpus : c’est ce qui permettait de le prédire.
D’où la propriété qui gêne le plus l’intuition, et que le fichier rend
vérifiable : il n’y a pas d’endroit. Ces 339 tenseurs sont traversés par
tous les tokens de toutes vos requêtes, sans exception, comme les sections
précédentes l’ont montré passage après passage. Ce que le modèle « sait » de
Pydantic v2 n’occupe donc aucune région. C’est une inclinaison répartie sur un
très grand nombre de poids qui portent aussi tout le reste, et la même matrice
qui pousse .dict en tête pousse ailleurs vers autre chose. « Ce que le modèle
sait » énonce cette propriété comme une règle de travail ; ce qui vient d’être
décrit en est le mécanisme, et il en donne la conséquence structurelle. Dans ce
fichier, rien ne peut être retiré, corrigé ni daté séparément.
Ce qui borne d’avance tout ce qu’on peut encore lui faire.
Ce qu’on peut encore écrire dessus
Cinq gestes, et ce sont des variantes d’un seul. Ces poids ont été écrits une fois, et pourtant on n’a jamais cessé d’écrire dessus. Vus d’ici, les gestes qui le font sont des variantes de celui que la section précédente vient de décrire — un passage, une perte, un pas d’optimiseur, dont deux choses seulement changent, combien de poids ont le droit de bouger et ce que mesure la perte. Le dernier de la série ne change ni l’un ni l’autre, parce qu’il n’écrit rien du tout.
Montrer la réponse qu’on voulait
Le premier geste change le corpus, et rien d’autre. À la place des morceaux de
web, quelques milliers à quelques centaines de milliers de paires, une
demande et la réponse qu’on voulait, écrites dans le document exact que le
gabarit de conversation produit, avec ses <|im_start|> et son <|im_end|>. La
perte n’est calculée que sur les tokens de la réponse : on n’apprend pas au
modèle à produire les questions. C’est le fine-tuning supervisé, et il n’a
d’autre mécanisme que celui-là.
Une boucle se referme ici. La génération s’arrête parce qu’un token de fin est tiré comme les autres, disait-on plus haut ; ce geste-ci est ce qui le rend probable. Il termine chacune de ces milliers de réponses, et c’est là tout ce qui « installe » l’arrêt.
Combien de poids bougent ? Tous y ont droit, et pourtant le déplacement est minuscule. Sur une tâche d’affinage classique, optimiser 200 paramètres puis les reprojeter dans l’espace complet suffit à retrouver 90 % de la performance d’un affinage complet : la mise à jour vit dans un sous-espace de quelques centaines de dimensions, face au milliard et demi que le fichier compte. Un corpus sans commune mesure avec celui du pré-entraînement, une correction qui tient dans quelques centaines de dimensions : ni l’un ni l’autre ne laisse la place d’écrire un fait que le pré-entraînement n’avait pas.
Optimiser sur une préférence
Le deuxième geste ne peut pas se contenter de changer le corpus, parce qu’une préférence n’est pas une cible. On ne rétropropage pas « je préfère celle-ci » : la perte décrite plus haut réclame un token attendu à chaque position, et une comparaison n’en fournit aucun. Deux montages contournent l’obstacle.
Le premier entraîne un second modèle, dont le seul travail est de donner une note à une réponse, sur les paires collectées ; on optimise ensuite le premier pour que cette note monte. S’y ajoute obligatoirement un terme qui pénalise l’écart au modèle de départ : sans lui, l’optimisation trouve les réponses qui plaisent au noteur, qui sont rarement des réponses. C’est l’apprentissage par renforcement à partir de retours humains, le RLHF.
Le second se passe du noteur. L’algèbre de cet objectif permet de récrire sa solution optimale en fonction des seules probabilités que le modèle donne aux deux réponses de la paire ; le modèle de récompense disparaît, et il ne reste qu’une perte de classement calculée directement sur les paires. Même objectif, un modèle au lieu de deux : c’est l’optimisation directe des préférences, le DPO.
Ce que cette passe déplace ne se lit pas dans les poids, mais dans ce qu’elle achète. Les sorties d’un modèle de 1,3 milliard de paramètres ainsi post-entraîné ont été préférées à celles d’un modèle cent fois plus gros qui ne l’était pas. Ce que ce nombre mesure est exactement ce qui a été coché, une préférence. Il dit quelle part de ce que vous percevez comme la qualité d’un modèle a été écrite par cette passe-là, et il ne dit rien de ce que le modèle sait.
Récompenser le résultat, pas le trajet
Sur les tâches dont la réponse se vérifie mécaniquement, un résultat de calcul ou une suite de tests qui passe, le vérificateur est la récompense : ni démonstration ni préférence n’est nécessaire. On laisse le modèle produire une longue suite de tokens avant sa réponse, on ne note que la réponse, et ce qui l’a précédée est renforcé quand elle est juste. La génération la plus récente obtient ce comportement par apprentissage par renforcement seul, sans phase de démonstrations humaines. Ce sont les modèles qu’on vend comme des modèles de raisonnement, et ce qu’ils produisent avant de répondre est une délibération, le terme que retient « Limites et hallucinations ».
Deux choses en découlent, que vous avez constatées toutes les deux. Ces tokens intermédiaires sortent de la même boucle que les autres, un passage complet chacun : ils sont facturés et ils prennent du temps, même lorsque l’interface ne les montre pas. Et rien dans le signal d’entraînement ne demande à cette suite d’être un compte rendu fidèle de ce qui a produit la réponse, puisqu’elle n’est récompensée que par ce qui la suit. C’est le mécanisme sous la quatrième famille de défaillance de « Limites et hallucinations ».
Un correctif de rang faible
Celui-ci s’applique à un modèle déjà fini, là où les gestes précédents s’appliquent à un modèle qu’on est en train de fabriquer. Il tire parti du résultat cité plus haut : si la mise à jour vit dans un sous-espace minuscule, autant ne calculer que ce sous-espace.
On gèle la matrice d’origine et on lui ajoute le produit de deux matrices fines. Sur une matrice d’attention de l’objet suivi, 1 536 × 1 536, soit 2 359 296 nombres, un correctif de rang 8 s’écrit comme un 1 536 × 8 suivi d’un 8 × 1 536, soit 24 576 nombres : un centième. Le gradient et l’état d’optimiseur n’existent que pour ces deux matrices-là, et c’est l’essentiel de l’économie ; sur un modèle de 175 milliards de paramètres, le procédé annonce dix mille fois moins de paramètres entraînés et trois fois moins de mémoire GPU. C’est LoRA, l’adaptation de rang faible.
Deux propriétés de service en découlent, moins connues que l’économie. Le produit des deux matrices fines a exactement la forme de la matrice d’origine, donc on peut l’y additionner une fois pour toutes avant de servir : l’adaptateur ne coûte rien au moment de répondre. Et tant qu’on ne l’additionne pas, plusieurs adaptateurs cohabitent au-dessus d’un seul exemplaire des poids et se choisissent par requête.
Ce dont ça rend compte : le rang est le budget. Ce qui tient dans huit directions est une inflexion, un format, un ton, un vocabulaire de domaine. Ce que la section précédente a décrit comme réparti sur des millions de poids qui portent aussi tout le reste n’y entre pas.
Le seul geste qui n’écrit rien
Le dernier n’a ni perte, ni gradient, ni pas d’optimiseur. Il se construit d’abord un index : chaque passage de vos sources est passé dans un petit modèle séparé qui en rend un vecteur, la même sorte d’objet que les vecteurs de tokens du début, un par passage au lieu d’un par token. Au moment de la requête, votre question traverse ce même petit modèle, on retient les passages dont le vecteur en est le plus proche, et on les insère dans le document, devant votre question. Puis on appelle le modèle, inchangé. C’est la génération augmentée de récupération, le RAG.
Trois conséquences suivent de cette description, qu’on ne voit pas en regardant le résultat. Ce qui décide de ce qui remonte est une proximité calculée par un autre modèle que celui qui répond : le mode d’échec propre est le passage qui ressemble à la question sans y répondre, et le modèle qui répond n’a aucun moyen de savoir qu’il a été mal servi. Ce qui remonte entre dans le document, donc paie le prefill et occupe la fenêtre au même titre que le reste. Et parce qu’un index rend des passages différents d’un tour à l’autre, il insère un bloc variable au milieu du document : tout ce qui le suit cesse d’être un préfixe réutilisable, ce que la condition posée plus haut sanctionne exactement.
Voilà l’étendue de ce qui s’écrit encore sur ces poids. Le premier geste change le corpus, le deuxième change ce que mesure la perte, le troisième change qui la mesure, le quatrième change le nombre de poids autorisés à bouger. Le cinquième ne change rien du fichier : il change le document. C’est la seule différence de nature de toute la série, et c’est aussi la seule dont l’effet ne survit pas à l’appel.
Ce qui est vrai à chaque appel
Il n’y a jamais eu, dans toute la traversée, que deux objets : un document et un fichier de poids. Onze sections plus tôt, une requête entrait dans la machine ; elle en est ressortie un token à la fois, et rien de ce qu’on a ouvert en chemin n’a démenti la phrase du début. La machine reçoit un texte et rend un token.
Le fichier est figé, et les gestes qui l’écrivent ont eu lieu ailleurs et avant, jamais pendant que vous travaillez. Le document, lui, est reconstruit en entier à chaque appel, et c’est le seul endroit où vous écrivez quelque chose.
De là vient tout le reste, et il n’y a rien à ajouter pour l’obtenir.
- La même demande ne redonne pas la même réponse, parce que le dernier étage tire.
- Le modèle ne revient pas sur ce qu’il vient d’écrire, parce qu’aucune position ne voit celles qui la suivent.
- Un résultat d’outil pèse comme une consigne, parce que les deux sont des caractères dans la même chaîne.
- Ce qui est absent du document est absent pour de bon, quelle que soit la clarté avec laquelle vous y aviez pensé.
Il n’y a pas d’endroit, dans cette machine, où la réponse serait déjà. Il y a un texte que vous composez et une opération qui le continue. Quand un agent se comporte d’une manière qui me surprend, je ne cherche donc pas ce qu’il a voulu faire : il n’a rien voulu. Je relis le document tel qu’il était au moment où il l’a fait, et je regarde ce qu’il contenait.