Repetition Penalty : empêcher les LLM de tourner en boucle
Le repetition penalty (pénalité de répétition) est un mécanisme qui réduit la probabilité qu’un modèle de langue génère des tokens déjà présents dans la sortie, en appliquant un facteur de pénalité aux logits des tokens répétés. C’est un paramètre essentiel pour éviter le phénomène de « dégénérescence textuelle » (neural text degeneration) où le modèle se bloque dans des boucles répétitives.
- Types principaux
- Repetition penalty (multiplicatif), frequency penalty (additif proportionnel), presence penalty (additif fixe)
- Valeurs typiques
- Repetition penalty : 1.0-2.0 (1.0 = désactivé). Frequency/Presence penalty : 0.0-2.0
- Agit sur
- Les logits des tokens avant le softmax
- Complémentaire de
- No-repeat n-gram (contrainte dure vs pénalité souple)
- Risque si trop élevé
- Texte artificiel, vocabulaire forcé, incohérence
- Exposé dans
- Hugging Face (repetition_penalty), OpenAI (frequency_penalty, presence_penalty), Anthropic, Google
Pourquoi les LLM répètent-ils ?
La répétition est un problème fondamental de la génération autorégressive. À chaque pas, le modèle choisit le token le plus probable (ou échantillonne dans les plus probables). Si une phrase ou un motif a une forte probabilité dans le contexte actuel, il génère ce motif. Or, ce motif une fois généré crée un nouveau contexte dans lequel le même motif reste probable, ce qui crée une boucle auto-renforçante.
Exemple typique avec un LLM sans pénalité de répétition :
Prompt : "Explain the benefits of exercise"
Sortie sans pénalité :
"Exercise is beneficial for health. Exercise is beneficial for your
body. Exercise is beneficial for your mind. Exercise is beneficial
for your overall well-being. Exercise is beneficial..."
Ce phénomène est particulièrement marqué avec le greedy decoding (qui choisit toujours le token le plus probable), mais il peut aussi survenir avec le sampling si la température est basse ou si le modèle a des biais d’entraînement vers certaines formulations.
Les trois types de pénalités anti-répétition
Repetition penalty (multiplicatif)
C’est la version utilisée par Hugging Face Transformers et la plupart des frameworks open source. Le principe : si un token a déjà été généré, son logit est divisé par le facteur de pénalité θ avant le softmax.
Pour chaque token déjà généré :
Si logit > 0 : logit_pénalisé = logit / θ (réduit le score positif)
Si logit < 0 : logit_pénalisé = logit × θ (augmente le score négatif)
Exemple avec θ = 1.2 :
Token "exercise" (déjà généré), logit original = 5.0
Logit pénalisé = 5.0 / 1.2 = 4.17
→ Probabilité réduite, mais pas éliminée
Avec θ = 1.0, aucune pénalité n’est appliquée. Le paramètre est typiquement réglé entre 1.1 et 1.3 pour la plupart des cas d’usage.
| Valeur de θ | Effet | Usage recommandé |
|---|---|---|
| 1.0 | Aucune pénalité | Quand les répétitions ne posent pas problème |
| 1.1-1.2 | Pénalité légère | Bon point de départ pour la majorité des tâches |
| 1.3-1.5 | Pénalité modérée | Génération longue, texte créatif |
| > 1.5 | Pénalité forte | Risque de texte artificiel, à éviter sauf cas spécial |
Frequency penalty (additif proportionnel)
Utilisé par l’API OpenAI (GPT). Au lieu d’un facteur multiplicatif, la pénalité est additive et proportionnelle au nombre d’occurrences du token dans la sortie générée :
logit_pénalisé = logit - frequency_penalty × count(token)
Exemple avec frequency_penalty = 0.5 :
Token "exercise" apparaît 3 fois
logit_pénalisé = 5.0 - 0.5 × 3 = 3.5
Token "beneficial" apparaît 5 fois
logit_pénalisé = 4.0 - 0.5 × 5 = 1.5
→ Plus un token est répété, plus il est pénalisé
La frequency penalty est un nombre entre -2.0 et 2.0 dans l’API OpenAI. Les valeurs positives découragent les répétitions, les valeurs négatives les encouragent (rarement utile). En pratique, des valeurs entre 0.0 et 1.0 suffisent pour la plupart des cas.
Presence penalty (additif fixe)
Également utilisé par l’API OpenAI. Contrairement à la frequency penalty, la presence penalty applique une pénalité fixe, identique à chaque token qui a déjà été utilisé, quelle que soit le nombre de fois :
logit_pénalisé = logit - presence_penalty × (1 si token déjà vu, 0 sinon)
Exemple avec presence_penalty = 0.6 :
Token "exercise" (déjà vu 1 fois) : 5.0 - 0.6 = 4.4
Token "exercise" (déjà vu 5 fois) : 5.0 - 0.6 = 4.4 (même pénalité !)
Token "innovation" (jamais vu) : 3.0 - 0.0 = 3.0 (pas de pénalité)
La presence penalty encourage le modèle à introduire de nouveaux tokens/sujets plutôt qu’à revenir sur ceux déjà abordés. Elle est particulièrement utile pour le brainstorming ou la génération de listes diversifiées.
Comparaison des trois mécanismes
| Mécanisme | Type de pénalité | Proportionnel aux occurrences ? | API/Framework | Effet principal |
|---|---|---|---|---|
| Repetition penalty | Multiplicatif | Non (binaire : vu ou non vu) | Hugging Face, vLLM, Ollama | Réduit les boucles directes |
| Frequency penalty | Additif | Oui (pénalité × nombre d’occurrences) | OpenAI, Anthropic | Réduit les mots surreprésentés |
| Presence penalty | Additif fixe | Non (binaire : vu ou non vu) | OpenAI, Anthropic | Encourage la diversité thématique |
No-repeat n-gram : la contrainte dure
Contrairement aux pénalités (qui réduisent la probabilité sans l’éliminer), la contrainte no-repeat n-gram est une interdiction absolue. Si un trigramme a déjà été généré, il est impossible de le reproduire : la probabilité du token qui compléterait le trigramme est mise à zéro.
# Hugging Face Transformers
output = model.generate(
**inputs,
no_repeat_ngram_size=3, # interdit la répétition de trigrammes
max_new_tokens=200
)
Exemple :
Texte déjà généré : "le chat dort sur"
Si le modèle voulait écrire "le chat dort" à nouveau,
le token "dort" serait bloqué après "le chat"
L’avantage : efficacité garantie, zéro boucle possible. L’inconvénient : peut forcer des reformulations maladroites quand une répétition serait naturelle (par exemple, répéter un terme technique dans un document spécialisé).
Guide de réglage par cas d’usage
| Cas d’usage | Repetition penalty | Frequency penalty | Presence penalty | No-repeat n-gram |
|---|---|---|---|---|
| Conversation générale | 1.1 | 0.3 | 0.0 | Non (variabilité naturelle suffit) |
| Écriture créative longue | 1.2 | 0.5 | 0.3 | 3 |
| Brainstorming / listes | 1.1 | 0.3 | 0.8 | Non |
| Traduction | 1.0-1.1 | 0.0 | 0.0 | Non (la source contraint la sortie) |
| Résumé de texte | 1.2 | 0.4 | 0.0 | 3 |
| Code generation | 1.0-1.1 | 0.0 | 0.0 | Non (les patterns de code se répètent légitimement) |
Pièges courants
Pénalité trop élevée : avec un repetition_penalty > 1.5, le modèle évite même les mots courants comme « le », « de », « est » après leur première utilisation. Le résultat est du texte artificiellement varié qui semble écrit par quelqu’un qui utilise un thésaurus pour chaque mot. Exemple : au lieu de « l’IA est puissante et l’IA est rapide », le modèle écrit « l’IA s’avère puissante tandis que cette technologie se révèle véloce ». C’est parfois plus gênant que la répétition elle-même.
Ignorer le contexte technique : dans la rédaction technique, juridique ou scientifique, les termes précis doivent être répétés (pas de synonymes pour « gradient descent » ou « API endpoint »). Dans ces contextes, réduisez la pénalité ou désactivez-la.
Confondre les paramètres des différentes API : le repetition_penalty de Hugging Face (multiplicatif, valeur > 1.0) n’est pas la même chose que le frequency_penalty d’OpenAI (additif, valeur 0.0-2.0). Transférer une valeur d’un framework à l’autre sans adaptation produira des résultats inattendus.
Appliquer la pénalité au prompt : certaines implémentations pénalisent aussi les tokens du prompt (pas seulement ceux générés). Cela peut être problématique si le prompt contient des termes que le modèle devrait pouvoir répéter dans sa réponse. Vérifiez la documentation de votre framework.
Implémentation pratique
# Hugging Face Transformers
from transformers import AutoModelForCausalLM, AutoTokenizer
model = AutoModelForCausalLM.from_pretrained("mistralai/Mistral-7B-v0.1")
tokenizer = AutoTokenizer.from_pretrained("mistralai/Mistral-7B-v0.1")
inputs = tokenizer("Les avantages du deep learning sont", return_tensors="pt")
output = model.generate(
**inputs,
max_new_tokens=200,
do_sample=True,
temperature=0.7,
top_p=0.9,
repetition_penalty=1.15, # pénalité multiplicative légère
no_repeat_ngram_size=3, # pas de trigramme répété
)
print(tokenizer.decode(output[0], skip_special_tokens=True))
# API OpenAI
from openai import OpenAI
client = OpenAI()
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "List 10 benefits of exercise"}],
temperature=0.7,
frequency_penalty=0.5, # pénalité proportionnelle aux occurrences
presence_penalty=0.3, # encourage la diversité thématique
)
print(response.choices[0].message.content)
Recherche récente et alternatives
Contrastive search : une alternative qui sélectionne le token maximisant la probabilité tout en minimisant la similarité avec les tokens récemment générés (dans l’espace des embeddings). Plus sophistiqué que la pénalité brute, mais plus coûteux en calcul.
DeRep (2025) : une méthode post-hoc pour la génération de code qui détecte et supprime les blocs de code répétés après génération, plutôt que de modifier les probabilités pendant la génération. Résultats supérieurs aux pénalités classiques en termes de code fonctionnel.
Pénalité contextuelle : des travaux explorent des pénalités qui distinguent les répétitions « légitimes » (termes techniques, noms propres) des répétitions « dégénérées » (boucles). L’idée est d’appliquer la pénalité sélectivement, en fonction du type de token et du contexte.
Fenêtre de pénalité glissante : certaines implémentations (comme dans vLLM et TGI) permettent de limiter la pénalité aux N derniers tokens générés plutôt qu’à tout le texte. Par exemple, avec une fenêtre de 256 tokens, un mot utilisé au début d’un long texte peut être réutilisé sans pénalité 300 tokens plus tard. C’est particulièrement utile pour la génération de textes longs où la diversité lexicale absolue n’est pas réaliste.
Pénalité par token vs par mot : un piège subtil de la tokenisation. Le mot « exercice » peut être un seul token dans un tokenizer et deux tokens dans un autre (« exer » + « cice »). La pénalité s’applique par token, pas par mot. Cela signifie que la pénalité effective peut varier selon le tokenizer utilisé, un point à garder en tête lors de la migration entre modèles.
Comment la répétition émerge : analyse détaillée
Pour comprendre pourquoi les pénalités sont nécessaires, il faut comprendre le mécanisme d’auto-renforcement qui crée les boucles. Lors de la génération autorégressive, chaque token généré modifie le contexte pour le token suivant. Quand un modèle produit « L’IA est formidable. L’IA est », la séquence « formidable » a une probabilité élevée car le contexte ressemble fortement à celui qui vient de produire ce même mot.
Ce problème est aggravé par plusieurs facteurs :
Biais d’entraînement : les données d’entraînement contiennent des formulations récurrentes (« il est important de noter que… », « dans le contexte de… ») qui ont une probabilité élevée dans de nombreux contextes. Le modèle les sur-utilise naturellement.
Fenêtre de contexte limitée : dans les modèles autoregressifs, l’attention porte sur toute la séquence précédente. Quand le texte généré constitue la majeure partie du contexte, les patterns de ce texte s’auto-renforcent.
Distribution de température basse : avec une température faible, la distribution de probabilités est « pointue ». Le modèle est très confiant dans ses choix, ce qui amplifie les patterns dominants et rend les boucles quasi-inévitables sans pénalité.
Longueur de génération : le risque de répétition augmente avec la longueur du texte. Pour des sorties de 50 tokens, la pénalité est rarement nécessaire. Pour des sorties de 500+ tokens, elle devient quasi indispensable.
Interaction avec les autres paramètres de décodage
Le repetition penalty ne fonctionne pas en isolation. Il interagit avec les autres paramètres de génération :
Température : une température élevée (> 0.8) réduit naturellement les répétitions en aplatissant la distribution. Plus la température est basse, plus la pénalité de répétition est importante. Avec une température de 0.1, même un repetition_penalty de 1.3 peut être insuffisant.
Nucleus sampling (top-p) : le top-p réduit aussi les répétitions en introduisant de la stochasticité. Avec top-p 0.9 et température 0.7, une légère pénalité (1.1) suffit. Avec greedy decoding, il faut monter à 1.2-1.3.
Max tokens : pour les générations courtes, la pénalité est moins critique. Pour les générations longues (articles, rapports), c’est un paramètre incontournable.
Stop sequences : bien positionner les stop sequences peut éviter les boucles en terminant la génération avant que le modèle n’entre dans une boucle répétitive.
Questions fréquentes sur le repetition penalty
Quelle est la différence entre repetition penalty et frequency penalty ?
Le repetition penalty (Hugging Face) est multiplicatif et binaire : il divise le logit d’un token par un facteur θ dès qu’il a été vu, quel que soit le nombre de fois. Le frequency penalty (OpenAI) est additif et proportionnel : il soustrait un montant proportionnel au nombre d’occurrences du token. En pratique, la frequency penalty est plus fine car un mot répété 10 fois est plus pénalisé qu’un mot répété 2 fois, ce qui correspond mieux à l’intuition.
Quelle valeur de repetition penalty utiliser par défaut ?
Pour le repetition_penalty de Hugging Face, 1.1 à 1.15 est un excellent point de départ pour la plupart des tâches. Pour la frequency_penalty d’OpenAI, 0.3 à 0.5 convient à la conversation et à la génération de contenu. Montez progressivement par incréments de 0.1 si les répétitions persistent. Au-delà de 1.5 (Hugging Face) ou 1.0 (OpenAI), testez soigneusement car le texte peut devenir artificiel.
Le repetition penalty affecte-t-il la qualité du texte ?
Oui, dans les deux sens. Une pénalité bien réglée améliore la lisibilité en éliminant les boucles. Mais une pénalité excessive force le modèle à utiliser des synonymes inhabituels, à éviter des mots de fonction courants (articles, prépositions), et à produire un texte qui « sonne faux ». Pour la rédaction technique ou juridique, où la précision terminologique prime, il est préférable de réduire ou désactiver la pénalité.
Peut-on combiner plusieurs types de pénalités ?
Oui, et c’est souvent recommandé. Dans l’API OpenAI, frequency_penalty et presence_penalty se combinent naturellement (l’un cible la surreprésentation, l’autre encourage la diversité). Avec Hugging Face, combinez repetition_penalty avec no_repeat_ngram_size pour une double protection (pénalité souple + contrainte dure). Évitez cependant d’empiler trop de contraintes, ce qui sur-restreint la génération.
Le repetition penalty est-il nécessaire avec le nucleus sampling ?
Le nucleus sampling réduit naturellement les répétitions par rapport au greedy decoding, car la stochasticité empêche le modèle de suivre le même chemin indéfiniment. Cependant, une légère pénalité (1.1) reste recommandée pour les générations longues, car même avec le sampling, le modèle peut entrer dans des « quasi-boucles » (le même contenu avec des variations mineures). Pour les générations courtes (< 100 tokens), le nucleus sampling seul suffit généralement.