Transformers documentation
Stratégies de génération
Stratégies de génération
Une stratégie de décodage indique comment un modèle doit sélectionner le jeton généré suivant. Il existe de nombreux types de stratégies de décodage, et le choix de la stratégie appropriée a un impact significatif sur la qualité du texte généré.
Ce guide vous aidera à comprendre les différentes stratégies de décodage disponibles dans Transformers et comment et quand les utiliser.
Méthodes de décodage de base
Ce sont des méthodes de décodage bien établies et doivent constituer votre point de départ pour les tâches de génération de texte.
Recherche gloutonne
La recherche gloutonne est la stratégie de décodage par défaut. Elle sélectionne le jeton le plus probable à chaque étape. Sauf spécification dans GenerationConfig, cette stratégie génère un maximum de 20 nouveaux jetons.
La recherche gloutonne fonctionne bien pour les tâches produisant des sorties relativement courtes où la créativité n'est pas une priorité. Cependant, elle échoue lors de la génération de séquences plus longues, car elle commence à se répéter.
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from accelerate import Accelerator
device = Accelerator().device
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-2-7b-hf")
inputs = tokenizer("Hugging Face is an open-source company", return_tensors="pt").to(device)
model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-7b-hf", dtype=torch.float16).to(device)
# explicitly set to default length because Llama2 generation length is 4096
outputs = model.generate(**inputs, max_new_tokens=20)
tokenizer.batch_decode(outputs, skip_special_tokens=True)
'Hugging Face is an open-source company that provides a suite of tools and services for building, deploying, and maintaining natural language processing'Échantillonnage
L'échantillonnage, ou échantillonnage multinomial, sélectionne aléatoirement un jeton en fonction de la distribution de probabilité sur l'ensemble du vocabulaire du modèle (contrairement au jeton le plus probable, comme dans la recherche gloutonne). Cela signifie que chaque jeton ayant une probabilité non nulle a une chance d'être sélectionné. Les stratégies d'échantillonnage réduisent la répétition et peuvent générer des sorties plus créatives et diversifiées.
Activez l'échantillonnage multinomial avec do_sample=True et num_beams=1.
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from accelerate import Accelerator
device = Accelerator().device
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-2-7b-hf")
inputs = tokenizer("Hugging Face is an open-source company", return_tensors="pt").to(device)
model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-7b-hf", dtype=torch.float16).to(device)
# explicitly set to 100 because Llama2 generation length is 4096
outputs = model.generate(**inputs, max_new_tokens=50, do_sample=True, num_beams=1)
tokenizer.batch_decode(outputs, skip_special_tokens=True)
'Hugging Face is an open-source company 🤗\nWe are open-source and believe that open-source is the best way to build technology. Our mission is to make AI accessible to everyone, and we believe that open-source is the best way to achieve that.'Recherche par faisceau
La recherche par faisceaux (beam search) suit plusieurs séquences générées (faisceaux) à chaque étape temporelle. Après un certain nombre d'étapes, elle sélectionne la séquence ayant la probabilité globale la plus élevée. Contrairement à la recherche gloutonne, cette stratégie peut « regarder en avant » et choisir une séquence dont la probabilité globale est plus élevée, même si les jetons initiaux ont une probabilité plus faible. Elle est particulièrement adaptée aux tâches ancrées dans l'entrée, comme la description d'une image ou la reconnaissance vocale. Vous pouvez également utiliser do_sample=True avec la recherche par faisceaux pour effectuer un échantillonnage à chaque étape, mais la recherche par faisceaux éliminera toujours de manière gloutonne les séquences à faible probabilité entre les étapes.
Découvrez le visualiseur de beam search pour voir comment fonctionne le beam search.
Activez la recherche par faisceau avec le paramètre num_beams (il doit être supérieur à 1, sinon c’est équivalent à une recherche gloutonne).
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from accelerate import Accelerator
device = Accelerator().device
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-2-7b-hf")
inputs = tokenizer("Hugging Face is an open-source company", return_tensors="pt").to(device)
model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-7b-hf", dtype=torch.float16).to(device)
# explicitly set to 100 because Llama2 generation length is 4096
outputs = model.generate(**inputs, max_new_tokens=50, num_beams=2)
tokenizer.batch_decode(outputs, skip_special_tokens=True)
"['Hugging Face is an open-source company that develops and maintains the Hugging Face platform, which is a collection of tools and libraries for building and deploying natural language processing (NLP) models. Hugging Face was founded in 2018 by Thomas Wolf']"Méthodes de génération personnalisées
Les méthodes de génération personnalisées permettent un comportement spécialisé tel que :
- laisser le modèle continuer à réfléchir s'il est incertain ;
- revenir en arrière dans la génération si le modèle est bloqué ;
- gérer les jetons spéciaux avec une logique personnalisée ;
- utiliser des caches KV spécialisés ;
Nous activons les méthodes de génération personnalisées via les dépôts de modèles, en supposant une balise de modèle et une structure de fichiers spécifiques (voir la sous-section ci-dessous). Cette fonctionnalité est une extension du code de modélisation personnalisé et, à ce titre, nécessite de définir trust_remote_code=True.
Si un dépôt de modèle contient une méthode de génération personnalisée, la façon la plus simple de l’essayer est de charger le modèle et de générer avec lui :
from transformers import AutoModelForCausalLM, AutoTokenizer
# `transformers-community/custom_generate_example` holds a copy of `Qwen/Qwen2.5-0.5B-Instruct`, but
# with custom generation code -> calling `generate` uses the custom generation method!
tokenizer = AutoTokenizer.from_pretrained("transformers-community/custom_generate_example")
model = AutoModelForCausalLM.from_pretrained(
"transformers-community/custom_generate_example", device_map="auto", trust_remote_code=True
)
inputs = tokenizer(["The quick brown"], return_tensors="pt").to(model.device)
# The custom generation method is a minimal greedy decoding implementation. It also prints a custom message at run time.
gen_out = model.generate(**inputs)
# you should now see its custom message, "✨ using a custom generation method ✨"
print(tokenizer.batch_decode(gen_out, skip_special_tokens=True))
'The quick brown fox jumps over a lazy dog, and the dog is a type of animal. Is'Les dépôts de modèles avec des méthodes de génération personnalisées possèdent une propriété particulière : leur méthode de génération peut être chargée depuis n’importe quel modèle via l’argument custom_generate de generate(). Cela signifie que n’importe qui peut créer et partager sa méthode de génération personnalisée pour qu’elle fonctionne potentiellement avec n’importe quel modèle Transformers, sans exiger des utilisateurs d’installer des paquets Python supplémentaires.
from transformers import AutoModelForCausalLM, AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-0.5B-Instruct")
model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2.5-0.5B-Instruct", device_map="auto")
inputs = tokenizer(["The quick brown"], return_tensors="pt").to(model.device)
# `custom_generate` replaces the original `generate` by the custom generation method defined in
# `transformers-community/custom_generate_example`
gen_out = model.generate(**inputs, custom_generate="transformers-community/custom_generate_example", trust_remote_code=True)
print(tokenizer.batch_decode(gen_out, skip_special_tokens=True)[0])
'The quick brown fox jumps over a lazy dog, and the dog is a type of animal. Is'Vous devez lire le fichier README.md du dépôt contenant la stratégie de génération personnalisée pour voir quelles sont les différences entre les nouveaux arguments et le type de sortie, s'il y en a. Sinon, vous pouvez supposer qu'elle fonctionne comme la méthode de base generate().
Vous pouvez trouver toutes les méthodes de génération personnalisées en recherchant leur balise personnalisée,
custom_generate.
Prenons le dépôt Hub transformers-community/custom_generate_example comme exemple. Le README.md indique qu'il possède un argument d'entrée supplémentaire, left_padding, qui ajoute un certain nombre de jetons de bourrage avant le prompt.
gen_out = model.generate(
**inputs, custom_generate="transformers-community/custom_generate_example", trust_remote_code=True, left_padding=5
)
print(tokenizer.batch_decode(gen_out)[0])
'<|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|>The quick brown fox jumps over the lazy dog.\n\nThe sentence "The quick'Si la méthode personnalisée a épinglé des exigences Python que votre environnement ne satisfait pas, vous obtiendrez une exception concernant des exigences manquantes. Par exemple, transformers-community/custom_generate_bad_requirements définit un ensemble d’exigences impossible dans son fichier custom_generate/requirements.txt, et vous verrez le message d’erreur ci-dessous si vous essayez de l’exécuter.
ImportError: Missing requirements in your local environment for `transformers-community/custom_generate_bad_requirements`: foo (installed: None) bar==0.0.0 (installed: None) torch>=99.0 (installed: 2.6.0)
Mettre à jour vos dépendances Python en conséquence supprimera ce message d'erreur.
Créer une méthode de génération personnalisée
Pour créer une nouvelle méthode de génération, vous devez créer un nouveau dépôt Model et y pousser quelques fichiers.
- Le modèle avec lequel vous avez conçu votre méthode de génération.
custom_generate/generate.py, qui contient toute la logique de votre méthode de génération personnalisée.custom_generate/requirements.txt, utilisé pour ajouter éventuellement de nouvelles dépendances Python et/ou verrouiller des versions spécifiques afin d'utiliser correctement votre méthode.README.md, où vous devez ajouter le tagcustom_generateet documenter ici les nouveaux arguments ou les différences de type de sortie de votre méthode personnalisée.
Après avoir ajouté tous les fichiers requis, votre dépôt doit ressembler à ceci
your_repo/
├── README.md # include the 'custom_generate' tag
├── config.json
├── ...
└── custom_generate/
├── generate.py
└── requirements.txtAjout du modèle de base
Le point de départ pour votre méthode de génération personnalisée est un dépôt de modèle comme tout autre. Le modèle à ajouter à ce dépôt doit être celui avec lequel vous avez conçu votre méthode, et il est destiné à faire partie d’une paire modèle-génération fonctionnelle et autonome. Lorsque le modèle de ce dépôt est chargé, votre méthode de génération personnalisée remplace generate. Pas d’inquiétude — votre méthode de génération peut toujours être chargée avec n’importe quel autre modèle Transformers, comme expliqué dans la section ci-dessus.
Si vous souhaitez simplement copier un modèle existant, vous pouvez
from transformers import AutoModelForCausalLM, AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("source/model_repo")
model = AutoModelForCausalLM.from_pretrained("source/model_repo")
tokenizer.save_pretrained("your/generation_method", push_to_hub=True)
model.save_pretrained("your/generation_method", push_to_hub=True)generate.py
C'est le cœur de votre méthode de génération. Elle doit contenir une méthode nommée generate, et cette méthode doit contenir un argument model en premier argument. model est l'instance du modèle, ce qui signifie que vous avez accès à tous les attributs et méthodes du modèle, y compris ceux définis dans GenerationMixin (comme la méthode de base generate).
generate.pydoit être placé dans un dossier nommécustom_generate, et non à la racine du dépôt. Les chemins de fichiers pour cette fonctionnalité sont codés en dur.
En interne, lorsque la méthode de base generate() est appelée avec un argument custom_generate, elle vérifie d’abord ses exigences Python (le cas échéant), puis localise la méthode generate personnalisée dans generate.py, et enfin appelle la méthode generate personnalisée. Tous les arguments reçus ainsi que model sont transmis à votre méthode generate personnalisée, à l’exception des arguments utilisés pour déclencher la génération personnalisée (trust_remote_code et custom_generate).
Cela signifie que votre generate peut comporter un mélange d'arguments originaux et d'arguments personnalisés (ainsi qu'un type de sortie différent), comme indiqué ci-dessous.
import torch
def generate(model, input_ids, generation_config=None, left_padding=None, **kwargs):
generation_config = generation_config or model.generation_config # default to the model generation config
cur_length = input_ids.shape[1]
max_length = generation_config.max_length or cur_length + generation_config.max_new_tokens
# Example of custom argument: add `left_padding` (integer) pad tokens before the prompt
if left_padding is not None:
if not isinstance(left_padding, int) or left_padding < 0:
raise ValueError(f"left_padding must be an integer larger than 0, but is {left_padding}")
pad_token = kwargs.pop("pad_token", None) or generation_config.pad_token_id or model.config.pad_token_id
if pad_token is None:
raise ValueError("pad_token is not defined")
batch_size = input_ids.shape[0]
pad_tensor = torch.full(size=(batch_size, left_padding), fill_value=pad_token).to(input_ids.device)
input_ids = torch.cat((pad_tensor, input_ids), dim=1)
cur_length = input_ids.shape[1]
# Simple greedy decoding loop
while cur_length < max_length:
logits = model(input_ids).logits
next_token_logits = logits[:, -1, :]
next_tokens = torch.argmax(next_token_logits, dim=-1)
input_ids = torch.cat((input_ids, next_tokens[:, None]), dim=-1)
cur_length += 1
return input_idsSuivez les bonnes pratiques recommandées ci-dessous pour vous assurer que votre méthode de génération personnalisée fonctionne comme prévu.
- N'hésitez pas à réutiliser la logique de validation et de préparation des entrées présente dans la fonction generate() d'origine.
- Épinglez la version de
transformersdans les dépendances si vous utilisez une méthode ou un attribut privé demodel. - Envisagez d’ajouter une validation du modèle, une validation des entrées, voire un fichier de test distinct pour aider les utilisateurs à vérifier la cohérence de votre code dans leur environnement.
Votre méthode generate personnalisée peut importer du code en importation relative depuis le dossier custom_generate. Par exemple, si vous avez un fichier utils.py, vous pouvez l'importer ainsi :
from .utils import some_functionSeuls les imports relatifs depuis le dossier custom_generate du même niveau sont pris en charge. Les imports depuis un dossier parent ou frère ne sont pas valides. L'argument custom_generate fonctionne également localement avec n'importe quel dossier contenant une structure custom_generate, ce qui est le flux de travail recommandé pour développer votre méthode de génération personnalisée.
gen_out = model.generate(**inputs, custom_generate="path/to/local/dir", trust_remote_code=True)Le chargement d’un répertoire local exécute toujours son
custom_generate/generate.py, ce qui nécessitetrust_remote_code=Truetout comme pour un dépôt du Hub. N’activeztrust_remote_code=Trueque pour du code que vous avez écrit ou examiné.
requirements.txt
Vous pouvez éventuellement spécifier des dépendances Python supplémentaires dans un fichier requirements.txt à l’intérieur du dossier custom_generate. Elles sont vérifiées à l’exécution et une exception est levée si elles sont manquantes, incitant ainsi les utilisateurs à mettre à jour leur environnement en conséquence.
README.md
Le fichier README.md racine du dépôt de modèle décrit généralement le modèle concerné. Toutefois, étant donné que le dépôt met l’accent sur la méthode de génération personnalisée, nous recommandons vivement de réorienter son contenu vers la description de cette méthode. En plus de la description de la méthode, nous recommandons de documenter les éventuelles différences d’entrées et/ou de sorties par rapport à la fonction generate() d’origine. Ainsi, les utilisateurs peuvent se concentrer sur les nouveautés et se référer à la documentation de Transformers pour les détails d’implémentation génériques.
Pour améliorer la découvrabilité, nous vous recommandons vivement d’ajouter le tag custom_generate à votre dépôt. Pour ce faire, le début de votre fichier README.md doit ressembler à l’exemple ci-dessous. Après avoir poussé le fichier, vous devriez voir le tag dans votre dépôt !
--- library_name: transformers tags: - custom_generate --- (your markdown content here)
Pratiques recommandées :
- Documentez les différences d'entrée et de sortie dans generate().
- Ajoutez des exemples autonomes pour permettre une expérimentation rapide.
- Décrivez les exigences non contraignantes, par exemple si la méthode ne fonctionne bien qu'avec une certaine famille de modèles.
Réutilisation de la préparation des entrées de generate
Si vous ajoutez une nouvelle boucle de décodage, vous souhaiterez peut-être conserver la préparation des entrées présente dans generate (extension du lot, masques d’attention, processeurs de logits, critères d’arrêt, etc.). Vous pouvez également passer un appelable à custom_generate pour réutiliser le pipeline de préparation complet de generate() tout en ne remplaçant que la boucle de décodage.
def custom_loop(model, input_ids, attention_mask, logits_processor, stopping_criteria, generation_config, **model_kwargs):
next_tokens = input_ids
while input_ids.shape[1] < stopping_criteria[0].max_length:
logits = model(next_tokens, attention_mask=attention_mask, **model_kwargs).logits
next_token_logits = logits_processor(input_ids, logits[:, -1, :])
next_tokens = torch.argmax(next_token_logits, dim=-1)[:, None]
input_ids = torch.cat((input_ids, next_tokens), dim=-1)
attention_mask = torch.cat((attention_mask, torch.ones_like(next_tokens)), dim=-1)
return input_ids
output = model.generate(
**inputs,
custom_generate=custom_loop,
max_new_tokens=10,
)Si vous publiez un dépôt
custom_generate, votre implémentation degeneratepeut elle-même définir un appelable et le passer àmodel.generate(). Cela vous permet de personnaliser la boucle de décodage tout en bénéficiant de la logique de préparation des entrées intégrée à Transformers.
Rechercher des méthodes de génération personnalisées
Vous pouvez trouver toutes les méthodes de génération personnalisées en recherchant leur balise personnalisée, custom_generate. En plus de la balise, nous avons constitué deux collections de méthodes custom_generate :
- Méthodes de génération personnalisées - Communauté — une collection de méthodes puissantes contributées par la communauté ;
- Méthodes de génération personnalisées - Tutoriels — une collection d'implémentations de référence pour des méthodes qui faisaient auparavant partie de
transformers, ainsi que des tutoriels pourcustom_generate.
Ressources
Lisez le billet de blog How to generate text: using different decoding methods for language generation with Transformers pour une explication du fonctionnement des stratégies de décodage courantes.
Update on GitHub