Appearance
Cours 2 — Comment un LLM choisit le prochain token
Module 1 · Fondamentaux · Prérequis : Cours 1
Objectif réel : être capable d'expliquer pourquoi deux exécutions du même prompt peuvent différer et de justifier les réglages de génération d'un service LLM, sans confondre probabilité, qualité, vérité et reproductibilité.
Dans le premier cours, tu as appris que le réseau produit des scores pour le prochain token. Ici, nous allons ouvrir la partie située entre ces scores et le token effectivement émis. C'est une couche cruciale : les poids du modèle peuvent être identiques alors que le décodage produit des résultats différents.
1. Deux questions différentes : que préfère le modèle et que sélectionne le moteur ?
Imagine le contexte suivant :
text
Complète : « FastAPI est un framework ... »Le réseau neuronal peut attribuer des scores élevés à certains tokens, plus faibles à d'autres. Ces préférences sont ce que le modèle calcule. Le moteur d'inférence décide ensuite comment sélectionner le prochain token à partir de ces scores.
text
Modèle : « Voici mes scores pour chaque prochain token. »
Décodeur : « Selon mes règles, je sélectionne ce token-ci. »
Application : « Je vérifie que le résultat répond à mes contraintes. »Ces trois responsabilités ne doivent pas être confondues. Modifier la température ne réentraîne pas le modèle ; demander du JSON ne valide pas le JSON ; obtenir deux réponses identiques ne prouve pas qu'elles sont justes.
La chaîne de ce cours
text
Séquence de tokens déjà disponible
↓
Réseau neuronal → logits (scores bruts du prochain token)
↓
Température / autres transformations éventuelles
↓
Probabilités (softmax)
↓
Filtrage éventuel : top-k, top-p
↓
Renormalisation et choix : greedy ou sampling
↓
Un nouveau token
↓
Ajout à la séquence, puis nouvelle étapeNuance : cet ordre est un schéma pédagogique courant. Les détails exacts, les autres filtres et l'ordre de certaines opérations dépendent du moteur.
2. Le logit : un score relatif, pas un pourcentage
Pour chaque token du vocabulaire, le réseau produit un logit, c'est-à-dire un score brut. Une sortie fictive simplifiée pourrait être :
| Token possible | Logit |
|---|---|
Python | 2 |
Java | 1 |
Rust | 0 |
Un logit de 2 ne signifie ni 2 % ni 20 %. Ce qui compte est la position relative des scores : ici Python est favorisé, puis Java, puis Rust.
Pourquoi le modèle ne sort-il pas directement une phrase ?
Le réseau doit fournir au moteur une information exploitable pour chaque prochaine décision. Les logits représentent les préférences du réseau à une position donnée. La stratégie de décodage peut choisir de respecter strictement le maximum ou de permettre d'autres candidats plausibles.
Garde ce modèle mental :
text
Logits = préférences brutes du réseau
Probabilités = préférences normalisées
Décodage = règle qui transforme ces préférences en un choix effectif3. Softmax : transformer des scores en une distribution
Pour tirer au hasard en tenant compte des préférences du modèle, il faut des nombres compris entre 0 et 1 dont la somme vaut 1. C'est le rôle du softmax.
Formule, à comprendre sans avoir à la mémoriser :
text
P(i) = exp(logit_i) / somme(exp(logit_j) pour tous les tokens j)L'exponentielle rend les scores positifs, puis la division normalise leur somme à 1.
Avec les logits fictifs Python = 2, Java = 1, Rust = 0, le softmax donne approximativement :
| Token | Logit | Probabilité |
|---|---|---|
Python | 2 | 66,52 % |
Java | 1 | 24,47 % |
Rust | 0 | 9,00 % |
Le sens profond de cette opération n'est pas « le modèle est sûr à 66,52 % que Python est vrai ». C'est plutôt : si nous tirons le prochain token selon cette distribution simplifiée, Python représentera environ 66,52 % des tirages.
Un détail mathématique utile
Ajouter la même constante à tous les logits ne change pas le résultat du softmax : 2, 1, 0 et 12, 11, 10 décrivent la même distribution. Ce sont leurs écarts qui comptent, pas leur valeur absolue.
4. Greedy et sampling : deux façons de décider
Supposons une distribution :
text
" Python" : 60 %
" Java" : 25 %
" Rust" : 10 %
" PHP" : 5 %Greedy : choisir systématiquement le premier
Le greedy decoding sélectionne à chaque étape le token ayant la probabilité la plus élevée : ici Python.
Ce comportement limite la variabilité, mais optimiser localement chaque token n'optimise pas nécessairement la réponse complète. Un début très probable peut conduire ensuite à une suite moins intéressante qu'un autre début légèrement moins probable.
Sampling : tirer un candidat selon ses probabilités
Avec un tirage probabiliste, Python reste favorisé, mais Java peut être choisi. Imagine une roue dont les secteurs occupent respectivement 60 %, 25 %, 10 % et 5 %.
Sur de nombreux tirages réalisés dans les mêmes conditions, les fréquences devraient approcher ces proportions. Un seul tirage ne suffit donc pas pour « vérifier » que le modèle a une probabilité de 60 %.
Le sampling ne rend pas le modèle aléatoire au sens où tous les tokens seraient équiprobables : il exploite les probabilités calculées. La diversité résulte du fait que plusieurs continuations restent possibles.
Pourquoi ce choix est-il répété ?
Lorsque Java est choisi, la séquence devient différente de celle qui aurait résulté de Python. Le modèle recalcule alors une nouvelle distribution conditionnelle, adaptée à cette nouvelle séquence. Un petit écart sur le premier token peut donc entraîner des réponses finales très différentes.
text
Même prompt
├── " Python" → nouvelle distribution → ...
└── " Java" → autre distribution → ...Ce caractère auto-régressif explique une grande partie de la variabilité des textes générés.
5. La température : changer la concentration, pas la connaissance
La température modifie l'importance relative des écarts entre les logits avant le softmax.
Pour une température strictement positive T :
text
P(i | T) = exp(logit_i / T) / somme(exp(logit_j / T))La division par T est le mécanisme essentiel :
- Si
T < 1, les écarts augmentent : les favoris dominent davantage. - Si
T = 1, cette transformation ne change pas la distribution initiale. - Si
T > 1, les écarts diminuent : les candidats secondaires gagnent du poids.
Avec les mêmes logits [2, 1, 0] et sans autre filtre :
| Token | T = 0,5 | T = 1 | T = 2 |
|---|---|---|---|
| A (logit 2) | 86,68 % | 66,52 % | 50,65 % |
| B (logit 1) | 11,73 % | 24,47 % | 30,72 % |
| C (logit 0) | 1,59 % | 9,00 % | 18,63 % |
Regarde ce qui ne change pas : A reste devant B, qui reste devant C. Pour une température strictement positive, la température modifie les écarts de probabilité, pas le classement des logits.
C'est cette nuance, plus que l'expression « moins ou plus créatif », qui permet de comprendre le réglage.
Température et vérité
Si A correspond à une réponse fausse mais dominante, abaisser la température peut rendre la réponse fausse encore plus fréquente. temperature ne vérifie aucun fait, ne consulte aucune base et ne corrige aucune erreur du modèle.
Et temperature = 0 ?
On ne peut pas diviser mathématiquement par zéro. Les moteurs traitent généralement cette valeur comme un cas spécial visant une sélection très déterministe, souvent proche du greedy. Il faut regarder l'implémentation et ne pas promettre une identité parfaite des sorties sur toutes les machines ou versions.
Pourquoi ne pas toujours mettre la température très basse ?
Une application de classification privilégie souvent la stabilité. Une tâche exploratoire — proposer plusieurs formulations, générer des idées, envisager des angles différents — peut bénéficier de plusieurs suites plausibles. La température change la politique d'exploration, pas la compétence fondamentale du modèle.
6. Pourquoi filtrer les candidats avant le tirage ?
Un vocabulaire de LLM peut contenir un grand nombre de tokens. Après softmax, des continuations très peu probables peuvent rester techniquement sélectionnables. Selon le moteur et la tâche, on peut préférer restreindre le tirage aux candidats qui comptent vraiment.
Deux méthodes fréquentes : top-k et top-p. Elles ne choisissent pas directement une phrase ; elles restreignent les candidats du prochain token, à chaque étape.
7. Top-k : « ne considère que les k meilleurs »
Supposons cette distribution déjà calculée :
| Token | Probabilité initiale |
|---|---|
| A | 50 % |
| B | 20 % |
| C | 15 % |
| D | 10 % |
| E | 3 % |
| F | 2 % |
Avec top_k = 3, seuls A, B et C restent. Leur masse totale initiale est 50 + 20 + 15 = 85 %.
On renormalise ensuite ces trois probabilités pour qu'elles totalisent 100 % :
text
P(A après top-k) = 50 / 85 ≈ 58,82 %
P(B après top-k) = 20 / 85 ≈ 23,53 %
P(C après top-k) = 15 / 85 ≈ 17,65 %Le moteur sélectionne ensuite un token parmi les candidats restants, si la stratégie retenue est le sampling.
Ce que top-k ne sait pas faire
La taille k est fixe, même si la forme de la distribution change :
- Si le premier candidat a 99 %, garder 50 candidats n'apporte souvent pas grand-chose.
- Si les probabilités sont réparties entre de nombreux candidats raisonnables,
top_k = 3peut couper trop sévèrement.
Top-k règle donc la taille du choix, pas directement la masse de probabilité conservée.
8. Top-p : « garde assez de candidats pour couvrir une masse de probabilité »
Le nucleus sampling ou top_p prend les tokens du plus probable au moins probable et conserve le plus petit préfixe dont le cumul atteint ou dépasse p.
Exemple :
| Token | Probabilité initiale | Cumul |
|---|---|---|
| A | 40 % | 40 % |
| B | 30 % | 70 % |
| C | 15 % | 85 % |
| D | 10 % | 95 % |
| E | 5 % | 100 % |
Avec top_p = 0.80, les candidats retenus sont A, B et C, parce que 40 + 30 = 70 % ne suffit pas, mais 40 + 30 + 15 = 85 % franchit le seuil. La masse conservée peut donc dépasser 80 % : on ne coupe pas un token en morceaux.
Après filtrage, les probabilités des candidats retenus sont renormalisées.
Pourquoi top-p s'adapte à la difficulté locale
Compare deux distributions fictives :
text
Situation 1 : A = 96 %, les autres totalisent 4 %.
Situation 2 : A = 22 %, B = 20 %, C = 18 %, D = 16 %, ...Avec top_p = 0.90, la situation 1 peut ne garder que A, tandis que la situation 2 doit conserver plusieurs tokens. Le nombre de candidats s'adapte à la répartition des probabilités.
L'essence de la différence
top_k impose combien de candidats peuvent rester ; top_p impose quelle masse de probabilité cumulée doit être représentée. Aucun des deux ne garantit que les candidats restants soient vrais ou utiles.
9. Les réglages agissent les uns sur les autres
Il serait trompeur d'imaginer qu'on règle séparément « créativité = température » et « fiabilité = top-p ». Les opérations modifient la même distribution.
Par exemple, abaisser la température peut rendre A beaucoup plus dominant. Si le moteur applique ensuite top_p = 0.90, il peut ne conserver que A. Avec une température plus élevée, le même top-p pourrait conserver A, B et C.
text
Mêmes logits
├── température basse → distribution concentrée → top-p retient peu de tokens
└── température haute → distribution étalée → top-p retient davantage de tokensCertains moteurs combinent température, top-k, top-p et d'autres pénalités. Il faut inspecter la configuration du moteur plutôt que supposer un ordre universel.
Une nuance importante : top_p = 1 n'élague normalement pas de candidats via top-p ; mais d'autres filtres, comme top-k, peuvent rester actifs.
10. La seed : reproduire le tirage, pas prouver la vérité
Un sampling utilise en pratique un générateur pseudo-aléatoire. Une seed sert généralement à initialiser ce générateur.
python
seed = 42Avec la même séquence d'entrées, les mêmes réglages, le même moteur, le même matériel et la même seed, on obtient souvent une génération plus reproductible. Mais un changement de version, de backend de calcul ou de comportement numérique peut modifier le résultat.
Trois niveaux qu'il faut distinguer :
- Reproductibilité : à conditions comparables, retrouve-t-on la même sortie ?
- Stabilité : deux petites variations de prompt produisent-elles des sorties cohérentes ?
- Exactitude : la sortie répond-elle correctement au besoin et aux faits ?
Tu peux avoir une réponse parfaitement reproductible et systématiquement fausse. Ce sera important lorsque tu évalueras ton système de classification de notes.
11. Longueur, arrêt, prefill et decode
La limite de génération
Les APIs proposent souvent un maximum de nouveaux tokens : max_tokens, max_output_tokens, num_predict, selon le fournisseur. Ce n'est ni un nombre de mots ni forcément la même chose que la fenêtre de contexte.
Une limite de 30 tokens peut tronquer un JSON avant sa dernière accolade. Le bon réflexe est de vérifier si la génération s'est terminée normalement ou parce qu'une limite a été atteinte.
Comment le modèle termine-t-il sa réponse ?
Un moteur peut s'arrêter après un token de fin, une condition d'arrêt configurée ou l'atteinte d'une limite de génération. Un token de fin est un candidat particulier que le modèle peut produire ; ce n'est pas simplement « l'absence d'autres mots ».
Pourquoi la latence dépend-elle du prompt et de la réponse ?
Une génération comporte généralement deux grandes phases :
text
PREFILL : traiter le prompt fourni (ex. 3 000 tokens)
DECODE : générer les nouveaux tokens, l'un après l'autre (ex. 200)Le prétraitement du prompt et la génération auto-régressive ont des profils de calcul différents. Les moteurs utilisent notamment un KV cache pour réutiliser les calculs passés pendant le decode. Tu étudieras ce mécanisme plus tard ; retiens surtout que demander une longue réponse ajoute des étapes séquentielles.
12. Exemple concret : paramétrer Ollama depuis Python
Supposons Ollama installé localement et un modèle instruct déjà disponible. Remplace MON_MODELE par son nom réel :
python
import requests
payload = {
"model": "MON_MODELE",
"prompt": (
"Classe cette note en une seule catégorie parmi idea, task, fact, question :\n"
"Je souhaite ajouter une application mobile à Revyy."
),
"stream": False,
"options": {
"temperature": 0.1,
"top_p": 0.9,
"num_predict": 60,
"seed": 42,
},
}
response = requests.post(
"http://localhost:11434/api/generate",
json=payload,
timeout=120,
)
response.raise_for_status()
print(response.json()["response"])Ce que tu dois être capable de commenter, ligne par ligne :
- Le backend envoie des données et des consignes, pas des poids à entraîner.
temperatureagit sur la concentration de la distribution.top_prestreint la masse des candidats avant tirage selon l'implémentation.num_predictlimite le nombre de nouveaux tokens.seedcontribue à la reproductibilité, sans la garantir dans toutes les conditions.
Ce que ce code ne fait pas : vérifier si la catégorie appartient réellement à la liste autorisée, contrôler la validité d'un JSON ou empêcher une écriture erronée en base. Le module 2 abordera JSON Schema, sorties structurées et Pydantic.
13. Une décision d'ingénierie : stabilité ≠ correction métier
Considère ce mauvais pipeline :
python
raw = call_llm(note, temperature=0)
postgres.insert(category=raw)Il suppose implicitement que faible température = bonne catégorie. Or le modèle peut toujours :
- choisir une catégorie absente de la liste ;
- produire une explication à la place de la seule valeur attendue ;
- classer incorrectement une note ambiguë ;
- renvoyer une chaîne tronquée si la limite de tokens est atteinte.
Le pipeline robuste sépare au minimum :
text
Note brute conservée
↓
Génération d'une proposition
↓
Validation de la syntaxe et de l'énumération autorisée
↓
Contrôles métier / gestion des échecs
↓
Enregistrement d'une valeur validée, avec traçabilitéSi une catégorie doit être factuellement correcte, le schéma ne suffit pas non plus : un JSON valide peut contenir une mauvaise classification. Il faudra ultérieurement des exemples de test et une évaluation métier.
14. Expérience guidée : observer les distributions en Python
L'objectif est de voir les conséquences des paramètres sans installer de LLM. Ici, nous modélisons seulement le décodage d'une distribution fictive :
python
import math
import random
TOKENS = ["A", "B", "C"]
LOGITS = [2.0, 1.0, 0.0]
def softmax(values):
"""Softmax numérique avec stabilisation pour éviter les débordements."""
maximum = max(values)
weights = [math.exp(value - maximum) for value in values]
total = sum(weights)
return [weight / total for weight in weights]
def probabilities_at_temperature(logits, temperature):
if temperature <= 0:
raise ValueError("Utiliser une température strictement positive ici")
return softmax([value / temperature for value in logits])
for temperature in (0.5, 1.0, 2.0):
probabilities = probabilities_at_temperature(LOGITS, temperature)
print(temperature, dict(zip(TOKENS, [round(p, 4) for p in probabilities])))
# Pour observer des tirages :
rng = random.Random(42)
probabilities = probabilities_at_temperature(LOGITS, 1.0)
print(rng.choices(TOKENS, weights=probabilities, k=10))À observer : la première ligne de résultats est la plus concentrée, la dernière la plus étalée ; le classement reste identique. Les dix tirages ne reproduiront pas nécessairement les pourcentages exacts : c'est la fréquence sur un grand nombre de tirages qui tend vers la distribution.
Ce que cette expérience ne démontre pas
Nous réutilisons les mêmes logits fictifs à chaque tirage. Un véritable LLM recalcule des logits à chaque nouveau token, puisque le contexte grandit et peut changer. Cette expérience isole volontairement le décodage, pas la génération d'un modèle complet.
15. Synthèse : quatre leviers, quatre effets
| Levier | Ce qu'il contrôle | Ce qu'il ne garantit pas |
|---|---|---|
| Greedy / sampling | La règle de choix d'un token | La qualité globale de la réponse |
| Température | La concentration de la distribution | La vérité ou l'absence d'hallucinations |
| Top-k / top-p | L'ensemble des candidats éligibles | Que les candidats soient pertinents |
| Seed | Une partie de la reproductibilité du tirage | La reproductibilité universelle ou l'exactitude |
16. Mini-test — compréhension des mécanismes
Consigne : justifie chaque réponse. Évite « plus ou moins créatif » lorsque tu peux parler de probabilités et de décisions de décodage.
- Quelle est la différence exacte entre logit, probabilité softmax et token sélectionné ?
- Si A a 60 %, B 25 %, C 10 % et D 5 %, pourquoi B peut-il sortir en sampling alors que A domine ?
- Avec les logits
A = 2, B = 1, C = 0, pourquoi une température basse augmente-t-elle la domination de A ? Le classement peut-il s'inverser à une température positive ? - Un modèle attribue 95 % à une réponse fausse. Que peut-il se passer si tu diminues fortement la température ?
- Pourquoi
temperature = 0ne garantit-elle pas des réponses identiques dans tous les environnements ? - Pourquoi
top_k = 5ettop_p = 0.9ne désignent-ils pas la même règle de filtrage ? - Une limite de 100 nouveaux tokens est-elle équivalente à une fenêtre de contexte de 100 tokens ? Explique.
- Que représentent prefill et decode, et pourquoi leur distinction peut-elle aider à diagnostiquer la latence ?
- Explique à un collègue pourquoi « un token a 80 % de probabilité » ne signifie pas « cette phrase est vraie à 80 % ».
17. Exercice 1 — Faire les calculs à la main
On fournit cette distribution fictive du prochain token :
| Candidat | Probabilité |
|---|---|
Python | 55 % |
Java | 20 % |
PHP | 10 % |
Rust | 8 % |
Go | 5 % |
C | 2 % |
A. Top-k : avec top_k = 2, quels tokens restent ? Calcule leurs nouvelles probabilités après renormalisation, en montrant le dénominateur.
B. Top-p : avec top_p = 0.75, quels tokens restent ? Explique à quel token le seuil est atteint. Recommence avec top_p = 0.76 : le résultat change-t-il ?
C. Sens des chiffres : si Python a 55 %, peux-tu dire que le modèle est « sûr à 55 % » que FastAPI utilise Python ? Donne une formulation exacte.
D. Analyse : si tu passes d'une température de 1 à une température de 0,5, peux-tu réutiliser les probabilités initiales telles quelles pour calculer top_p, ou faut-il les transformer d'abord ? Justifie sans faire le calcul complet.
18. Exercice 2 — Concevoir le réglage et ses garde-fous
Ton worker reçoit une note et doit renvoyer exactement une catégorie parmi idea, task, fact, question.
Deux configurations te sont proposées :
text
Configuration A : temperature = 1.5 ; top_p = 1.0
Configuration B : temperature = 0.1 ; top_p = 0.9- Quelle configuration privilégierais-tu pour la stabilité de cette tâche et pourquoi, en parlant des logits et des candidats ?
- Explique pourquoi ce choix ne garantit toujours ni un format correct ni une catégorie sémantiquement correcte.
- Identifie au moins quatre contrôles applicatifs ou pratiques de validation à placer autour du modèle avant d'écrire
notes.categorydans PostgreSQL. - Une note est ambiguë entre
ideaettask. Pourquoi est-ce potentiellement utile de conserver la note brute et le résultat proposé, plutôt que d'écraser silencieusement la catégorie précédente ?
19. Exercice 3 — Expérience et interprétation
Exécute le script de la section 14, puis modifie-le :
- Remplace
LOGITSpar[4.0, 2.0, 1.0]et compare les distributions. - Effectue 1 000 tirages avec
T = 1, compte les occurrences de chaque token et compare les fréquences observées aux probabilités calculées. - Recommence avec
T = 0.5en conservant la même seed.
Question d'analyse : si tu obtenais à chaque fois le même premier token sur dix essais, pourrais-tu conclure que le sampling est désactivé ? Explique.
Bonus facultatif : code toi-même une fonction top_k_filter(probabilities, k) qui met à zéro les candidats éliminés puis renormalise les autres. Teste-la sur les probabilités de l'exercice 1. N'utilise pas de bibliothèque IA.
20. Question d'entretien technique
Un collègue affirme :
« Notre RAG ne produit plus d'hallucinations : nous avons mis sa température à zéro et sa seed à 42. »
Réponds en quatre à cinq phrases. Tu dois distinguer contrôle du tirage, reproductibilité, qualité des documents récupérés, vérification factuelle et validation de sortie.
21. Avant de passer au cours suivant
Tu maîtrises l'essentiel si tu peux, sans regarder tes notes :
- partir de trois logits et expliquer comment une distribution est obtenue ;
- dire précisément ce que modifie la température et ce qu'elle ne modifie pas ;
- déterminer les candidats conservés par top-k et top-p, puis expliquer la renormalisation ;
- montrer comment deux tirages différents peuvent entraîner deux suites différentes ;
- défendre une configuration de génération et proposer des garde-fous applicatifs.
À retenir absolument
Les poids du modèle produisent des préférences ; le décodage transforme ces préférences en tokens. Température, top-k, top-p et seed ne sont pas des mécanismes de vérité, de validation métier ou de mémoire.