Module 5 — Décodage : température, top-k, top-p
Le modèle des modules précédents produit, à chaque étape, une distribution de probabilité sur son vocabulaire de jetons. Reste à choisir un jeton. Ce choix est le décodage, et c'est le levier que l'ingénieur ajuste le plus souvent en production — bien plus que les poids du modèle. Pour notre assistant de support, un décodage mal réglé transforme des réponses factuelles en variations créatives inutiles, ou des propositions naturelles en clones stériles. Ce module explique ce que chaque paramètre fait exactement à la distribution.
D'une distribution à un jeton : sept stratégies
Le modèle produit un vecteur de scores appelés logits. La probabilité de chaque jeton est obtenue par un softmax : . Toutes les stratégies de décodage se distinguent par comment elles utilisent ces probabilités.
| Stratégie | Sélection | Aléatoire | Usage type |
|---|---|---|---|
| Argmax (glouton) | non | test, réponse ultra-courte | |
| Recherche par faisceaux | conserve les meilleures séquences | non | traduction courte |
| Échantillonnage brut | tire selon | oui | rarement utile |
| Température | tire selon modifiée par | oui | tout usage général |
| Top-k | tire parmi les meilleurs | oui | limitation dure du bruit |
| Top-p (nucléus) | tire parmi la plus petite masse cumulée | oui | production de texte varié |
| Combiné top-k + top-p + | applique les trois filtres | oui | réglage fin |
Argmax et recherche par faisceaux sont déterministes ; à même invite, elles produisent toujours la même sortie. Les autres sont stochastiques ; deux appels identiques peuvent donner des réponses différentes, à moins de fixer la graine aléatoire.
La température, ou comment aplatir la distribution
La température divise les logits avant le softmax :
Une température basse () accentue les différences entre logits et concentre la masse sur les meilleurs jetons. Une température haute () aplatit la distribution et rend les jetons rares plus probables. À la limite , la distribution devient un point : c'est équivalent à l'argmax.
import numpy as np
logits = np.array([3.0, 2.0, 1.0, -1.0])
def softmax(x, T):
x = x / T
x = x - x.max()
e = np.exp(x)
return e / e.sum()
for T in [0.3, 1.0, 2.0]:
p = softmax(logits, T)
print(f"T = {T:>4} :", np.round(p, 3))
Sortie :
T = 0.3 : [0.955 0.045 0. 0. ]
T = 1.0 : [0.643 0.236 0.087 0.032]
T = 2.0 : [0.502 0.305 0.185 0.008]
À , le premier jeton capte 95 pour cent de la masse ; à , les trois meilleurs se partagent la moitié presque également. Le comportement change complètement, et pourtant c'est la même paramétrisation d'une même distribution.
Top-k et top-p : couper la queue
La distribution du softmax garde toujours une masse non nulle sur les jetons les plus improbables — dans un vocabulaire de 32 000 jetons, cette « queue » peut représenter quelques pour cent au total. C'est peu, mais tirer dedans produit des mots aberrants qui font dérailler la génération.
Top-k garde uniquement les jetons de plus haute probabilité et renormalise. Le paramètre est simple mais rigide : à distribution très concentrée, garde 40 jetons dont 35 négligeables ; à distribution très plate, coupe trop tôt.
Top-p (aussi appelé nucleus sampling) garde la plus petite ensemble de jetons dont la masse cumulée dépasse , puis renormalise. Il s'adapte automatiquement à la forme de la distribution : sur une distribution concentrée, il garde peu de jetons ; sur une distribution plate, il en garde plus.
def top_p(logits, p=0.9, T=1.0):
proba = softmax(logits, T)
ordre = np.argsort(-proba)
cumule = np.cumsum(proba[ordre])
coupe = np.searchsorted(cumule, p) + 1
filtre = np.zeros_like(proba)
filtre[ordre[:coupe]] = proba[ordre[:coupe]]
return filtre / filtre.sum()
print(np.round(top_p(logits, p=0.9, T=1.0), 3))
Dans la pratique, on combine les trois : température modérée (), top-p de et top-k élevé () comme filet de sécurité. Cette combinaison est le réglage par défaut de la plupart des interfaces publiques.
Réglages recommandés par usage
Il n'existe pas un « bon » décodage universel. Le tableau ci-dessous donne les valeurs qui fonctionnent, avec la raison.
| Usage | Température | Top-p | Top-k | Pourquoi |
|---|---|---|---|---|
| Support client, faits vérifiables | 0,2 - 0,3 | 0,9 | 40 | déterminisme perçu, moins d'errance |
| Génération de code | 0,0 - 0,2 | 1,0 | 40 | correction syntaxique prioritaire |
| Rédaction commerciale | 0,7 | 0,9 | 40 | style vivant, variations utiles |
| Brainstorming | 0,9 - 1,2 | 0,95 | 100 | diversité recherchée |
| Extraction structurée | 0,0 | 1,0 | 1 | argmax pur, aucune variation |
Pour notre assistant de support, une température autour de 0,3 avec top-p à 0,9 donne des réponses stables sans les rendre robotiques. Passer à 0,7 rend les mêmes réponses subtilement différentes d'un appel à l'autre, ce qui déroute les opérateurs qui les relisent.
Pénalités de répétition, l'outil du dernier recours
Les modèles ont tendance à répéter, surtout quand l'invite est mal construite ou que la température est basse. Deux pénalités existent.
- Pénalité de répétition : divise le logit d'un jeton déjà présent par un facteur (souvent 1,05 à 1,2).
- Pénalité de fréquence ou de présence (interface OpenAI) : soustrait un terme proportionnel au compte du jeton dans l'historique.
Ces pénalités règlent le symptôme, jamais la cause. Une répétition persistante signale presque toujours un problème de gabarit de conversation (module 3), une invite trop répétitive elle-même, ou un modèle mal adapté à la langue. Augmenter la pénalité au-delà de 1,3 est un mauvais signe : le modèle finit par éviter les jetons qu'il doit utiliser, ce qui produit un texte artificiellement bariolé.
Déterminisme et graine aléatoire
Pour tester ou pour tracer un bogue, on veut souvent des sorties reproductibles. Trois conditions doivent être remplies simultanément.
- Décodage glouton ou faisceau, ou échantillonnage avec une graine fixée.
- Précision numérique stable :
torch.use_deterministic_algorithms(True)et parfoistorch.backends.cudnn.deterministic = True. - Aucune opération dépendante du matériel qui varie entre GPU.
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer, set_seed
set_seed(42)
tok = AutoTokenizer.from_pretrained("meta-llama/Meta-Llama-3-8B-Instruct")
modele = AutoModelForCausalLM.from_pretrained(
"meta-llama/Meta-Llama-3-8B-Instruct",
torch_dtype=torch.bfloat16,
device_map="auto",
)
entree = tok("Bonjour, comment puis-je vous aider ?", return_tensors="pt").to(modele.device)
sortie = modele.generate(
**entree,
do_sample=True,
temperature=0.3,
top_p=0.9,
max_new_tokens=64,
)
Même avec ces précautions, la reproductibilité stricte n'est pas garantie sur GPU : les additions en virgule flottante ne sont pas associatives, et l'ordre dépend du parallélisme. On accepte un écart au niveau du bruit numérique.
Il est tentant de mettre pour « avoir la vérité ». En réalité, l'argmax choisit toujours le même jeton, ce qui verrouille aussi les erreurs : si le modèle a une petite probabilité d'halluciner à cette position, il hallucinera à chaque appel. Un décodage à température modérée avec échantillonnage donne parfois une réponse fausse, mais échoue plus rarement de la même manière, ce qui permet à un contrôle de détecter l'incohérence.
Une erreur classique en évaluation est de comparer deux modèles avec des réglages de décodage différents, hérités d'exemples publics. Une baisse de température de 0,7 à 0,3 change la performance sur MMLU de plusieurs points. Toujours fixer explicitement le décodage avant toute comparaison, et le documenter à côté du chiffre.
En résumé
- Le décodage transforme une distribution en un jeton ; température, top-k et top-p modifient la distribution avant tirage, tandis qu'argmax et faisceaux la déterminisent.
- La température aplatit ou concentre la distribution ; abaisser n'améliore pas la factualité, mais verrouille le comportement, y compris les erreurs.
- Top-p s'adapte à la forme de la distribution alors que top-k est rigide ; les combiner avec une température modérée est le réglage par défaut le plus robuste.
- Les pénalités de répétition traitent un symptôme et jamais la cause ; au-delà de 1,3, elles dégradent le texte plus qu'elles ne l'améliorent.
Module suivant : ce que le modèle peut réellement voir en une seule invocation — la fenêtre de contexte et ses limites.