Polydesk-logotype
Polydesk.ai — Header

Beam Search : explorer plusieurs chemins pour trouver la meilleure séquence

Le beam search est un algorithme de décodage heuristique qui, à chaque étape de génération d’une séquence, maintient les k meilleures hypothèses en parallèle (k = beam width) au lieu de ne conserver que la plus probable. C’est le compromis standard entre le greedy decoding (rapide mais sous-optimal) et la recherche exhaustive (optimale mais impraticable), utilisé en traduction automatique, transcription vocale et génération de texte.

Beam Search · Fiche rapide
Type
Algorithme de décodage / recherche heuristique
Paramètre principal
Beam width (taille du faisceau), typiquement k=4 à 10
Cas spécial
k=1 → greedy search
Complexité
O(k × |V| × T) où |V| = taille du vocabulaire, T = longueur de la séquence
Usage principal
Traduction automatique, transcription vocale, résumé de texte
Alternative pour les tâches créatives
Nucleus sampling (top-p), sampling avec température
Première utilisation
Reconnaissance vocale (1976), puis NMT (2014+)

Le problème : trouver la meilleure séquence

Quand un modèle Seq2Seq ou un LLM génère du texte, il produit à chaque pas de temps une distribution de probabilités sur tout le vocabulaire. La séquence « idéale » est celle qui maximise la probabilité jointe de tous les tokens :

Séquence optimale = argmax P(y₁, y₂, ..., y_T | x)
                  = argmax ∏ P(y_t | y₁, ..., y_{t-1}, x)

Le problème : explorer toutes les combinaisons possibles est impossible. Pour un vocabulaire de 50 000 tokens et une séquence de 20 tokens, il y a 50 000²⁰ séquences candidates, un nombre astronomique. Il faut donc une stratégie de recherche intelligente.

Greedy search vs beam search

Le greedy search : rapide mais myope

Le greedy decoding sélectionne le token le plus probable à chaque étape, sans considérer l’impact sur les étapes futures. C’est rapide (un seul chemin à suivre) mais il peut rater la meilleure séquence globale en faisant des choix localement gourmands.

Exemple avec vocabulaire {A, B, C, <EOS>} :

Étape 1 : P(A)=0.5, P(B)=0.4, P(C)=0.1
Greedy → choisit A (0.5)

Étape 2 | A : P(B)=0.3, P(C)=0.6, P(<EOS>)=0.1
Greedy → choisit C (0.6)

Séquence greedy : A-C, probabilité = 0.5 × 0.6 = 0.30

Mais si on avait choisi B à l'étape 1 :
Étape 2 | B : P(A)=0.9, P(C)=0.05, P(<EOS>)=0.05
Séquence B-A, probabilité = 0.4 × 0.9 = 0.36

→ B-A (0.36) est meilleur que A-C (0.30), mais greedy l'a raté !

Le beam search : explorer k chemins en parallèle

Le beam search résout ce problème en maintenant les k meilleures hypothèses à chaque étape. Pour k=2 dans l’exemple ci-dessus, le beam search conserverait A (0.5) et B (0.4) à l’étape 1, les étendrait toutes les deux à l’étape 2, et trouverait correctement que B-A (0.36) est meilleur que A-C (0.30).

Le processus détaillé :

Étape 1 : on génère les probabilités de tous les tokens du vocabulaire. On conserve les k tokens les plus probables. Ce sont les k « faisceaux » (beams) initiaux.

Étape 2 : pour chacun des k faisceaux, on génère les probabilités du token suivant. On obtient k × |V| hypothèses. On ne conserve que les k meilleures (en score cumulé).

Étapes suivantes : on répète le processus. À chaque étape, on étend les k faisceaux, on évalue les k × |V| extensions, et on ne garde que les k meilleures.

Terminaison : quand un faisceau génère le token <EOS>, il est mis de côté comme séquence « terminée ». La recherche continue pour les faisceaux restants. À la fin, on retourne la meilleure séquence terminée.

Beam search ≠ recherche exhaustive Le beam search ne garantit pas de trouver la séquence optimale. Il est possible que la meilleure séquence globale ne fasse partie d’aucun des k faisceaux à une étape intermédiaire. Plus k est grand, plus la probabilité de trouver l’optimum augmente, mais la garantie n’existe qu’avec une recherche exhaustive (k = |V|, impraticable).

Scoring et normalisation par longueur

Le beam search utilise des log-probabilités (au lieu de probabilités brutes) pour éviter les problèmes numériques de sous-dépassement :

Score(y₁, ..., y_T) = Σ log P(y_t | y₁, ..., y_{t-1}, x)

Les log-probabilités sont toujours négatives, donc le score cumulé diminue avec la longueur de la séquence. Conséquence : le beam search a un biais vers les séquences courtes (moins de termes négatifs = score plus élevé).

La solution standard est la normalisation par longueur (length penalty) :

Score normalisé = (1 / T^α) × Σ log P(y_t | ...)

Où :
- T = longueur de la séquence
- α = facteur de pénalité (typiquement 0.6 à 1.0)
- α = 0 → pas de normalisation
- α = 1 → division par la longueur exacte

Google Neural Machine Translation (GNMT) utilise α = 0.6, un bon point de départ empirique. Sans cette normalisation, les traductions sont souvent tronquées.

Choisir la taille du beam

Beam width (k) Qualité Vitesse Usage typique
1 (greedy) Baseline, erreurs possibles Très rapide Prototypage, démo, streaming temps réel
4-5 Bon compromis Rapide Traduction en production, transcription
8-10 Haute qualité Modéré Benchmarks, évaluation de modèles
20+ Marginalement meilleur Lent Recherche, cas spéciaux

Au-delà de k=10, les gains de qualité sont généralement marginaux tandis que le temps de décodage augmente linéairement. Un phénomène contre-intuitif a été observé : avec des beams très larges (k>100), la qualité peut se dégrader. Cela s’explique par le fait que les séquences à haute probabilité selon le modèle ne sont pas toujours les plus naturelles pour un humain (le modèle est imparfait).

Variantes du beam search

Diverse beam search

Le beam search classique a tendance à produire des hypothèses très similaires entre elles (les k faisceaux convergent vers des variantes proches). Le diverse beam search introduit une pénalité de similarité entre les faisceaux, forçant l’algorithme à explorer des chemins plus variés. Utile quand vous voulez proposer plusieurs traductions alternatives ou générer des réponses diversifiées.

Constrained beam search

Permet d’imposer des contraintes sur la séquence de sortie : certains mots doivent obligatoirement apparaître dans le résultat final. Hugging Face Transformers supporte cette variante via le paramètre force_words_ids dans model.generate(). Cas d’usage : forcer l’utilisation de termes techniques précis en traduction, ou garantir que certaines entités nommées apparaissent dans un résumé.

Early stopping

Quand les k faisceaux les plus probables ont tous généré <EOS>, la recherche s’arrête. Certaines implémentations s’arrêtent dès qu’un seul faisceau est terminé si son score dépasse le meilleur score possible des faisceaux restants.

Beam search vs sampling : quand utiliser quoi ?

Le beam search et le sampling (avec température, top-k, nucleus sampling) servent des objectifs différents :

Critère Beam search Sampling (top-p, top-k)
Objectif Trouver la séquence la plus probable Générer des séquences diverses et naturelles
Déterminisme Déterministe (même entrée → même sortie) Stochastique (résultats différents à chaque fois)
Répétitions Sujet aux répétitions (n-gram penalty nécessaire) Moins de répétitions naturellement
Texte factuel Excellent (traduction, transcription) Risque d’hallucinations
Texte créatif Ennuyeux, générique Excellent (histoires, dialogue)
Vitesse k× plus lent que greedy Même vitesse que greedy
Règle pratique Utilisez le beam search pour les tâches « fermées » où il existe une bonne réponse (traduction, transcription, résumé extractif). Utilisez le sampling pour les tâches « ouvertes » où la diversité est souhaitable (conversation, génération créative, brainstorming). Les LLM modernes comme GPT et Claude utilisent des stratégies de sampling par défaut, pas le beam search.

Implémentation avec Hugging Face

from transformers import AutoModelForSeq2SeqLM, AutoTokenizer

model = AutoModelForSeq2SeqLM.from_pretrained("Helsinki-NLP/opus-mt-fr-en")
tokenizer = AutoTokenizer.from_pretrained("Helsinki-NLP/opus-mt-fr-en")

text = "Le chat noir dort paisiblement sur le tapis rouge."
inputs = tokenizer(text, return_tensors="pt")

# Greedy decoding (k=1)
greedy = model.generate(**inputs, max_length=50)
print("Greedy:", tokenizer.decode(greedy[0], skip_special_tokens=True))

# Beam search (k=5) avec length penalty
beam = model.generate(
    **inputs,
    max_length=50,
    num_beams=5,              # beam width
    length_penalty=0.6,       # normalisation par longueur
    no_repeat_ngram_size=3,   # évite les répétitions de trigrammes
    early_stopping=True       # arrêt quand tous les beams sont terminés
)
print("Beam:", tokenizer.decode(beam[0], skip_special_tokens=True))

# Beam search avec plusieurs sorties
beams_multi = model.generate(
    **inputs,
    max_length=50,
    num_beams=5,
    num_return_sequences=3,   # retourne les 3 meilleures hypothèses
    length_penalty=0.6
)
for i, b in enumerate(beams_multi):
    print(f"Hyp {i+1}:", tokenizer.decode(b, skip_special_tokens=True))

Le paramètre no_repeat_ngram_size est crucial en pratique : sans lui, le beam search a tendance à produire des séquences répétitives (le modèle se « bloque » dans une boucle de tokens à haute probabilité). Fixer cette valeur à 2 ou 3 élimine la plupart des répétitions.

Limites du beam search

Biais vers les séquences courtes : résolu par la normalisation par longueur, mais le paramètre α nécessite un réglage empirique.

Répétitions : le beam search peut générer des boucles répétitives. La pénalité de n-grammes est une correction efficace mais ad hoc.

Pas de diversité : les k faisceaux convergent souvent vers des variantes presque identiques. Le diverse beam search atténue ce problème.

Coût linéaire en k : chaque faisceau nécessite un passage forward séparé dans le decoder. Avec k=10, le décodage est 10× plus lent qu’en greedy.

Texte « ennuyeux » : en maximisant la probabilité, le beam search favorise les formulations les plus courantes et prévisibles. Pour la génération ouverte, c’est un défaut majeur qui explique pourquoi les LLM préfèrent le sampling.

Applications concrètes du beam search

Traduction automatique neuronale : c’est l’application historique et le cas d’usage où le beam search excelle le plus. Google Translate, DeepL, et les modèles NLLB (Meta) utilisent tous le beam search pour le décodage final. Un beam width de 4-5 avec normalisation par longueur est le standard industriel.

Reconnaissance vocale (ASR) : Whisper (OpenAI) utilise le beam search pour décoder les transcriptions à partir des sorties de son encoder audio. Dans ce contexte, le beam search peut être combiné avec un modèle de langue externe (language model fusion) pour améliorer la cohérence linguistique de la transcription.

Résumé de texte : les modèles comme BART et Pegasus utilisent le beam search pour générer des résumés extractifs ou abstractifs. La normalisation par longueur est particulièrement importante ici pour éviter des résumés trop courts.

Génération de légendes d’images : les modèles image-to-text utilisent le beam search pour décoder des descriptions cohérentes à partir des features visuelles extraites par le CNN ou le Vision Transformer.

Complétion de code : certains assistants de code utilisent le beam search pour proposer plusieurs suggestions de complétion, en particulier pour les complétions multi-lignes où la cohérence syntaxique est critique.

Optimisations pratiques

Batching des faisceaux : les k faisceaux peuvent être traités comme un batch sur le GPU, exploitant le parallélisme matériel. Le coût réel est donc inférieur à k× le greedy, car les opérations matricielles s’effectuent en parallèle.

Cache KV : dans les Transformers, chaque faisceau maintient son propre cache clé-valeur. Avec k=5 et une séquence longue, la mémoire GPU requise pour le cache est 5× celle du greedy. C’est souvent le facteur limitant en pratique, bien plus que le temps de calcul.

Pruning adaptatif : au lieu d’un beam width fixe, certaines implémentations utilisent un seuil de score relatif. Les faisceaux dont le score est trop éloigné du meilleur sont éliminés, ce qui peut accélérer le décodage de 40 % sans perte de qualité mesurable.

Reranking : une technique avancée consiste à générer n hypothèses avec le beam search, puis à les réordonner avec un modèle séparé (par exemple un modèle bidirectionnel comme BERT) qui évalue la qualité globale de chaque hypothèse. Cette approche de « generate then rerank » est utilisée dans plusieurs systèmes de traduction de pointe.

Le beam search dans le paysage actuel du décodage

L’arrivée des LLM génératifs a repositionné le beam search dans l’écosystème des stratégies de décodage. Voici comment les principaux modèles et applications abordent le décodage :

Modèles de traduction (NLLB, mBART, opus-mt) : beam search avec k=4-5, normalisation par longueur, pénalité de n-grammes. C’est le standard incontesté.

Transcription vocale (Whisper) : beam search avec k=5 par défaut. Possibilité de combiner avec un language model pour les langues à faibles ressources.

LLM conversationnels (GPT, Claude, LLaMA) : sampling (temperature + top-p) par défaut. Le beam search n’est quasiment jamais utilisé car il produit du texte trop prévisible pour le dialogue.

Génération de code : mélange selon le cas. Les complétions courtes (une ligne) utilisent souvent le greedy. Les générations plus longues (fonctions, classes) peuvent bénéficier d’un beam search léger (k=2-3) pour la cohérence syntaxique, ou d’un sampling avec température basse (0.2-0.4).

Le beam search n’est donc pas un algorithme en déclin : il reste optimal pour les tâches de transduction structurée. Son territoire s’est simplement rétréci face aux stratégies de sampling qui dominent la génération ouverte.


Questions fréquentes sur le beam search

Quelle est la différence entre beam search et greedy decoding ?

Le greedy decoding ne considère qu’un seul chemin : il prend le token le plus probable à chaque étape. Le beam search maintient k chemins en parallèle et sélectionne les k meilleures séquences partielles à chaque étape. Le greedy est un cas spécial du beam search avec k=1. Le beam search trouve généralement de meilleures séquences car il peut « revenir sur ses pas » en abandonnant un chemin qui semblait bon au début mais qui mène à une impasse.

Le beam search trouve-t-il toujours la séquence optimale ?

Non. Le beam search est une heuristique, pas un algorithme exact. Seule la recherche exhaustive (qui explore toutes les combinaisons) garantit de trouver l’optimum, mais elle est impraticable pour des vocabulaires de milliers de tokens. En augmentant k, on augmente la probabilité de trouver l’optimum, mais il n’existe pas de garantie formelle quel que soit k.

Pourquoi les LLM comme GPT n’utilisent-ils pas le beam search ?

Les LLM modernes (GPT, Claude, LLaMA) utilisent des stratégies de sampling (temperature, top-p, top-k) pour la génération de texte. Le beam search maximise la probabilité, ce qui produit du texte « sûr » mais monotone et répétitif. Le sampling introduit de la stochasticité, rendant le texte plus naturel, créatif et varié. Pour les tâches factuelles (traduction, transcription), le beam search reste supérieur. Pour les tâches ouvertes (conversation, écriture), le sampling est préféré.

Quel beam width choisir ?

Pour la traduction automatique et la transcription : k=4 à 5 est le standard industriel. Au-delà de k=10, les gains sont marginaux. Pour le résumé : k=4 à 6 avec une bonne normalisation par longueur. Augmenter k ne compense jamais un modèle mal entraîné. Si le beam search avec k=5 donne des résultats médiocres, le problème vient du modèle, pas du décodage.

Comment éviter les répétitions dans le beam search ?

La technique la plus efficace est la pénalité de n-grammes (no_repeat_ngram_size) qui interdit la génération d’un n-gramme déjà produit. Avec no_repeat_ngram_size=3, aucune séquence de 3 tokens ne peut apparaître deux fois dans la sortie. D’autres approches incluent la pénalité de répétition (repetition_penalty), qui réduit la probabilité des tokens déjà générés, et le coverage mechanism, qui garantit que toutes les parties de l’entrée sont « couvertes » dans la sortie.

Polydesk.ai — Footer