Transformers documentation

Estrategias de generación

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v5.22.1).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Estrategias de generación

Una estrategia de decodificación determina cómo un modelo debe seleccionar el siguiente token generado. Existen muchos tipos de estrategias de decodificación, y elegir la adecuada tiene un impacto significativo en la calidad del texto generado.

Esta guía te ayudará a comprender las diferentes estrategias de decodificación disponibles en Transformers y cómo y cuándo utilizarlas.

Métodos básicos de decodificación

Estos son métodos de decodificación bien establecidos y deben ser tu punto de partida para las tareas de generación de texto.

Búsqueda voraz

La búsqueda voraz es la estrategia de decodificación predeterminada. Selecciona el token más probable en cada paso. A menos que se especifique en GenerationConfig, esta estrategia genera un máximo de 20 tokens nuevos.

La búsqueda voraz funciona bien para tareas con salidas relativamente cortas donde la creatividad no es una prioridad. Sin embargo, falla al generar secuencias más largas porque comienza a repetirse.

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'

Muestreo

El muestreo, o muestreo multinomial, selecciona un token de forma aleatoria según la distribución de probabilidad sobre todo el vocabulario del modelo (a diferencia del token más probable, como en la búsqueda codiciosa). Esto significa que cada token con una probabilidad distinta de cero tiene la posibilidad de ser seleccionado. Las estrategias de muestreo reducen la repetición y pueden generar salidas más creativas y diversas.

Habilite el muestreo multinomial con do_sample=True y 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.'

Búsqueda por haz

La búsqueda por haz (beam search) mantiene el seguimiento de varias secuencias generadas (haces) en cada paso de tiempo. Después de un cierto número de pasos, selecciona la secuencia con la probabilidad global más alta. A diferencia de la búsqueda codiciosa (greedy search), esta estrategia puede “mirar hacia adelante” y elegir una secuencia con una probabilidad global más alta, incluso si los tokens iniciales tienen una probabilidad más baja. Es la más adecuada para tareas basadas en la entrada, como describir una imagen o el reconocimiento de voz. También puede usar do_sample=True con la búsqueda por haz para muestrear en cada paso, pero la búsqueda por haz aún recortará de forma codiciosa las secuencias de baja probabilidad entre pasos.

Echa un vistazo al visualizador de beam search para ver cómo funciona el beam search.

Active la búsqueda por haz con el parámetro num_beams (debe ser mayor que 1, de lo contrario es equivalente a la búsqueda codiciosa).

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étodos de generación personalizados

Los métodos de generación personalizados permiten un comportamiento especializado, como:

  • que el modelo continúe pensando si no está seguro;
  • revertir la generación si el modelo se atasca;
  • manejar tokens especiales con lógica personalizada;
  • utilizar cachés KV especializadas;

Habilitamos métodos de generación personalizados a través de repositorios de modelos, asumiendo una etiqueta de modelo y una estructura de archivos específicas (consulte la subsección siguiente). Esta función es una extensión del código de modelado personalizado y, al igual que este, requiere establecer trust_remote_code=True.

Si un repositorio de modelos contiene un método de generación personalizado, la forma más sencilla de probarlo es cargar el modelo y generar con él:

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'

Los repositorios de modelos con métodos de generación personalizados tienen una propiedad especial: su método de generación se puede cargar desde cualquier modelo a través del argumento custom_generate de generate(). Esto significa que cualquier persona puede crear y compartir su método de generación personalizado para que funcione potencialmente con cualquier modelo de Transformers, sin necesidad de que los usuarios instalen paquetes de Python adicionales.

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'

Debes leer el archivo README.md del repositorio que contiene la estrategia de generación personalizada para ver si existen diferencias en los nuevos argumentos y el tipo de salida. De lo contrario, puedes asumir que funciona como el método base generate().

Puedes encontrar todos los métodos de generación personalizados buscando su etiqueta personalizada, custom_generate.

Considere el repositorio del Hub transformers-community/custom_generate_example como ejemplo. El README.md indica que tiene un argumento de entrada adicional, left_padding, que añade un número de tokens de relleno antes del 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 el método personalizado tiene requisitos de Python fijos que tu entorno no cumple, se producirá una excepción por requisitos faltantes. Por ejemplo, transformers-community/custom_generate_bad_requirements tiene un conjunto de requisitos imposible definido en su archivo custom_generate/requirements.txt, y verás el mensaje de error siguiente si intentas ejecutarlo.

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)

Actualizar los requisitos de Python en consecuencia eliminará este mensaje de error.

Creación de un método de generación personalizado

Para crear un nuevo método de generación, debe crear un nuevo repositorio de Model y subir algunos archivos a él.

  1. El modelo con el que has diseñado tu método de generación.
  2. custom_generate/generate.py, que contiene toda la lógica de tu método de generación personalizado.
  3. custom_generate/requirements.txt, usado para agregar opcionalmente nuevos requisitos de Python y/o fijar versiones específicas para usar correctamente su método.
  4. README.md, donde debe añadir la etiqueta custom_generate y documentar aquí cualquier argumento nuevo o diferencia en el tipo de salida de su método personalizado.

Después de que hayas agregado todos los archivos requeridos, tu repositorio debería verse así

your_repo/
├── README.md          # include the 'custom_generate' tag
├── config.json
├── ...
└── custom_generate/
    ├── generate.py
    └── requirements.txt

Añadiendo el modelo base

El punto de partida para su método de generación personalizado es un repositorio de modelos como cualquier otro. El modelo que debe agregarse a este repositorio es el modelo con el que diseñó su método, y está destinado a ser parte de un par funcional y autónomo de modelo-generación. Cuando se carga el modelo de este repositorio, su método de generación personalizado reemplazará a generate. No se preocupe: su método de generación aún puede cargarse con cualquier otro modelo de Transformers, como se explica en la sección anterior.

Si simplemente quieres copiar un modelo existente, puedes

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

Este es el núcleo de su método de generación. Debe contener un método llamado generate, y este método debe contener un argumento model como su primer argumento. model es la instancia del modelo, lo que significa que tiene acceso a todos los atributos y métodos del modelo, incluidos los definidos en GenerationMixin (como el método base generate).

generate.py debe colocarse en una carpeta llamada custom_generate, y no en el nivel raíz del repositorio. Las rutas de archivo para esta función están codificadas de forma fija.

Por debajo, cuando se llama al método base generate() con un argumento custom_generate, primero comprueba sus requisitos de Python (si los hay), luego localiza el método generate personalizado en generate.py y, finalmente, llama al generate personalizado. Todos los argumentos recibidos y model se reenvían a su método generate personalizado, con la excepción de los argumentos utilizados para activar la generación personalizada (trust_remote_code y custom_generate).

Esto significa que su generate puede tener una mezcla de argumentos originales y personalizados (así como un tipo de salida diferente), como se muestra a continuación.

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_ids

Siga las prácticas recomendadas a continuación para asegurarse de que su método de generación personalizado funcione como se espera.

  • Puede reutilizar libremente la lógica de validación y preparación de entrada del generate() original.
  • Fija la versión de transformers en los requisitos si usas algún método o atributo privado en model.
  • Considera añadir validación del modelo, validación de la entrada o incluso un archivo de pruebas separado para ayudar a los usuarios a verificar que tu código funcione correctamente en su entorno.

Su método generate personalizado puede importar código de forma relativa desde la carpeta custom_generate. Por ejemplo, si tiene un archivo utils.py, puede importarlo de la siguiente manera:

from .utils import some_function

Solo se admiten importaciones relativas desde la carpeta custom_generate del mismo nivel. Las importaciones de carpetas padre o hermanas no son válidas. El argumento custom_generate también funciona localmente con cualquier directorio que contenga una estructura custom_generate, que es el flujo de trabajo recomendado para desarrollar su método de generación personalizado.

gen_out = model.generate(**inputs, custom_generate="path/to/local/dir", trust_remote_code=True)

Cargar un directorio local también ejecuta su custom_generate/generate.py, por lo que requiere trust_remote_code=True al igual que un repositorio de Hub. Solo pase trust_remote_code=True para código que haya escrito o revisado.

requirements.txt

Puedes especificar opcionalmente requisitos adicionales de Python en un archivo requirements.txt dentro de la carpeta custom_generate. Estos se comprueban en tiempo de ejecución y se lanzará una excepción si faltan, lo que anima a los usuarios a actualizar su entorno en consecuencia.

README.md

El README.md de nivel raíz en el repositorio del modelo suele describir el modelo en cuestión. Sin embargo, dado que el enfoque del repositorio es el método de generación personalizado, recomendamos encarecidamente cambiar su enfoque para describir el método de generación personalizado. Además de una descripción del método, recomendamos documentar cualquier diferencia en las entradas y/o salidas respecto al generate() original. De esta manera, los usuarios pueden centrarse en lo que es nuevo y confiar en la documentación de Transformers para los detalles de implementación genéricos.

Para mejorar la visibilidad, le recomendamos encarecidamente que agregue la etiqueta custom_generate a su repositorio. Para ello, la parte superior de su archivo README.md debe parecerse al ejemplo siguiente. Después de enviar el archivo, ¡debería ver la etiqueta en su repositorio!

---
library_name: transformers
tags:
  - custom_generate
---

(your markdown content here)

Prácticas recomendadas:

  • Documenta las diferencias de entrada y salida en generate().
  • Agregue ejemplos autocontenidos para facilitar la experimentación rápida.
  • Describe los requisitos no estrictos, como si el método solo funciona bien con una cierta familia de modelos.

Reutilización de la preparación de la entrada de generate

Si estás añadiendo un nuevo bucle de decodificación, es posible que quieras conservar la preparación de la entrada presente en generate (expansión de lotes, máscaras de atención, procesadores de logits, criterios de parada, etc.). También puedes pasar un callable a custom_generate para reutilizar el pipeline completo de preparación de generate() y sobrescribir solo el bucle de decodificación.

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 publicas un repositorio de custom_generate, tu implementación de generate puede definir un callable y pasarlo a model.generate(). Esto te permite personalizar el bucle de decodificación mientras sigues beneficiándote de la lógica de preparación de entradas integrada en Transformers.

Búsqueda de métodos de generación personalizados

Puedes encontrar todos los métodos de generación personalizados buscando su etiqueta personalizada, custom_generate. Además de la etiqueta, curamos dos colecciones de métodos custom_generate:

Recursos

Lee la publicación de blog Cómo generar texto: uso de diferentes métodos de decodificación para la generación de lenguaje con Transformers para una explicación de cómo funcionan las estrategias de decodificación más comunes.

Update on GitHub