Transformers documentation

Estratégias de geração

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

Estratégias de geração

Uma estratégia de decodificação define como um modelo deve selecionar o próximo token gerado. Existem muitos tipos de estratégias de decodificação, e a escolha da estratégia adequada tem um impacto significativo na qualidade do texto gerado.

Este guia ajudará você a entender as diferentes estratégias de decodificação disponíveis no Transformers e como e quando usá-las.

Métodos básicos de decodificação

Estes são métodos de decodificação bem estabelecidos e devem ser seu ponto de partida para tarefas de geração de texto.

Busca gulosa

A busca gulosa é a estratégia de decodificação padrão. Ela seleciona o token mais provável a cada etapa. A menos que especificado em GenerationConfig, essa estratégia gera no máximo 20 novos tokens.

A busca gulosa funciona bem para tarefas com saídas relativamente curtas, em que a criatividade não é uma prioridade. No entanto, ela falha ao gerar sequências mais longas, pois começa a se repetir.

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'

Amostragem

A amostragem, ou amostragem multinomial, seleciona aleatoriamente um token com base na distribuição de probabilidade sobre todo o vocabulário do modelo (em oposição ao token mais provável, como na busca gulosa). Isso significa que todo token com probabilidade não nula tem a chance de ser selecionado. As estratégias de amostragem reduzem a repetição e podem gerar saídas mais criativas e diversificadas.

Ative a amostragem multinomial com do_sample=True e 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.'

Busca por feixe

A busca por feixe (beam search) acompanha várias sequências geradas (feixes) em cada passo de tempo. Após um certo número de passos, ela seleciona a sequência com a maior probabilidade global. Diferentemente da busca gulosa, essa estratégia pode “olhar para frente” e escolher uma sequência com maior probabilidade global, mesmo que os tokens iniciais tenham uma probabilidade menor. É mais adequada para tarefas baseadas na entrada, como descrever uma imagem ou reconhecimento de fala. Você também pode usar do_sample=True com a busca por feixe para amostrar em cada passo, mas a busca por feixe ainda eliminará de forma gulosa as sequências de baixa probabilidade entre os passos.

Confira o visualizador de beam search para ver como o beam search funciona.

Ative a busca por feixe com o parâmetro num_beams (deve ser maior que 1, caso contrário, é equivalente à busca gulosa).

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 geração personalizados

Métodos de geração personalizados permitem comportamentos especializados, como:

  • fazer o modelo continuar pensando se ele estiver incerto;
  • reverter a geração se o modelo travar;
  • tratar tokens especiais com lógica personalizada;
  • use caches de KV especializados;

Habilitamos métodos de geração personalizados por meio de repositórios de modelos, assumindo uma tag de modelo e uma estrutura de arquivos específicas (veja a subseção abaixo). Este recurso é uma extensão do código de modelagem personalizado e, como tal, requer a configuração de trust_remote_code=True.

Se um repositório de modelos contém um método de geração personalizado, a maneira mais fácil de testá-lo é carregar o modelo e gerar com ele:

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'

Repositórios de modelos com métodos de geração personalizados possuem uma propriedade especial: seu método de geração pode ser carregado a partir de qualquer modelo por meio do argumento custom_generate de generate(). Isso significa que qualquer pessoa pode criar e compartilhar seu método de geração personalizado para potencialmente funcionar com qualquer modelo do Transformers, sem exigir que os usuários instalem pacotes Python adicionais.

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'

Você deve ler o arquivo README.md do repositório que contém a estratégia de geração personalizada para ver quais são as diferenças nos novos argumentos e no tipo de saída, caso existam. Caso contrário, você pode assumir que funciona como o método base generate().

Você pode encontrar todos os métodos de geração personalizados buscando pela tag personalizada, custom_generate.

Considere o repositório do Hub transformers-community/custom_generate_example como exemplo. O README.md indica que ele possui um argumento de entrada adicional, left_padding, que adiciona um número de tokens de padding antes do 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'

Se o método personalizado tiver requisitos de Python fixados que o seu ambiente não atende, você receberá uma exceção sobre requisitos ausentes. Por exemplo, transformers-community/custom_generate_bad_requirements tem um conjunto impossível de requisitos definido em seu arquivo custom_generate/requirements.txt, e você verá a mensagem de erro abaixo se tentar executá-lo.

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)

Atualizar os requisitos do Python de acordo com isso removerá essa mensagem de erro.

Criando um método de geração personalizado

Para criar um novo método de geração, você precisa criar um novo repositório Model e enviar alguns arquivos para ele.

  1. O modelo com o qual você projetou seu método de geração.
  2. custom_generate/generate.py, que contém toda a lógica para o seu método de geração personalizado.
  3. custom_generate/requirements.txt, usado para adicionar opcionalmente novos requisitos do Python e/ou fixar versões específicas para usar corretamente o seu método.
  4. README.md, onde você deve adicionar a tag custom_generate e documentar aqui quaisquer novos argumentos ou diferenças no tipo de saída do seu método personalizado.

Depois de adicionar todos os arquivos necessários, seu repositório deve ficar assim

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

Adicionando o modelo base

O ponto de partida para o seu método de geração personalizado é um repositório de modelo, assim como qualquer outro. O modelo a ser adicionado a esse repositório deve ser o modelo com o qual você projetou seu método, e ele deve fazer parte de um par funcional e autossuficiente de modelo-geração. Quando o modelo neste repositório é carregado, o seu método de geração personalizado substituirá generate. Não se preocupe — o seu método de geração ainda pode ser carregado com qualquer outro modelo do Transformers, conforme explicado na seção acima.

Se você simplesmente quiser copiar um modelo existente, pode

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 é o núcleo do seu método de geração. Ele deve conter um método chamado generate, e esse método deve conter um argumento model como seu primeiro argumento. model é a instância do modelo, o que significa que você tem acesso a todos os atributos e métodos do modelo, incluindo os definidos em GenerationMixin (como o método base generate).

generate.py deve ser colocado em uma pasta chamada custom_generate e não no nível raiz do repositório. Os caminhos de arquivo para esse recurso são fixos.

Por baixo dos panos, quando o método base generate() é chamado com um argumento custom_generate, ele primeiro verifica seus requisitos em Python (se houver), em seguida localiza o método generate personalizado em generate.py e, por fim, chama o generate personalizado. Todos os argumentos recebidos e o model são encaminhados para o seu método generate personalizado, com a exceção dos argumentos usados para acionar a geração personalizada (trust_remote_code e custom_generate).

Isso significa que o seu generate pode ter uma mistura de argumentos originais e personalizados (além de um tipo de saída diferente), conforme mostrado abaixo.

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 as práticas recomendadas abaixo para garantir que seu método de geração personalizado funcione como esperado.

  • Sinta-se à vontade para reutilizar a lógica de validação e preparação de entrada do generate() original.
  • Fixe a versão do transformers nos requisitos se você usar qualquer método/atributo privado em model.
  • Considere adicionar validação de modelo, validação de entrada ou até mesmo um arquivo de teste separado para ajudar os usuários a verificar a sanidade do seu código em seu ambiente.

Seu método generate personalizado pode importar código de forma relativa a partir da pasta custom_generate. Por exemplo, se você tiver um arquivo utils.py, pode importá-lo da seguinte forma:

from .utils import some_function

Apenas imports relativos da mesma pasta custom_generate são suportados. Imports de pastas pai/irmã não são válidos. O argumento custom_generate também funciona localmente com qualquer diretório que contenha uma estrutura custom_generate, o que é o fluxo de trabalho recomendado para desenvolver seu método de geração personalizado.

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

Carregar um diretório local ainda executa o seu custom_generate/generate.py, portanto, requer trust_remote_code=True da mesma forma que um repositório do Hub. Passe trust_remote_code=True apenas para código que você escreveu ou revisou.

requirements.txt

Você pode, opcionalmente, especificar requisitos adicionais de Python em um arquivo requirements.txt dentro da pasta custom_generate. Eles são verificados em tempo de execução e uma exceção será lançada caso estejam ausentes, orientando os usuários a atualizar seu ambiente de forma correspondente.

README.md

O arquivo README.md no nível raiz do repositório do modelo geralmente descreve o próprio modelo. No entanto, como o foco do repositório é o método de geração personalizado, recomendamos fortemente direcionar o foco para a descrição desse método. Além da descrição do método, recomendamos documentar quaisquer diferenças nas entradas e/ou saídas em relação ao generate() original. Dessa forma, os usuários podem focar no que é novo e recorrer à documentação do Transformers para os detalhes de implementação genéricos.

Para facilitar a descoberta, recomendamos fortemente que você adicione a tag custom_generate ao seu repositório. Para isso, o topo do seu arquivo README.md deve se parecer com o exemplo abaixo. Depois de enviar o arquivo, você deve ver a tag no seu repositório!

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

(your markdown content here)

Práticas recomendadas:

  • Documente as diferenças de entrada e saída em generate().
  • Adicione exemplos autossuficientes para permitir experimentação rápida.
  • Descreva requisitos flexíveis, como o fato de o método funcionar bem apenas com uma determinada família de modelos.

Reutilizando a preparação de entrada do generate

Se você estiver adicionando um novo loop de decodificação, pode ser que queira preservar a preparação de entrada presente em generate (expansão de lote, máscaras de atenção, processadores de logits, critérios de parada, etc.). Você também pode passar um callable para custom_generate para reutilizar o pipeline completo de preparação de generate(), sobrescrevendo apenas o loop de decodificação.

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,
)

Se você publicar um repositório custom_generate, sua implementação de generate pode, por sua vez, definir um objeto chamável e passá-lo para model.generate(). Isso permite personalizar o loop de decodificação, ao mesmo tempo em que você se beneficia da lógica de preparação de entrada integrada ao Transformers.

Encontrando métodos de geração personalizados

Você pode encontrar todos os métodos de geração personalizados buscando pela tag personalizada, custom_generate. Além da tag, curatamos duas coleções de métodos custom_generate:

Recursos

Leia o post no blog Como gerar texto: usando diferentes métodos de decodificação para geração de linguagem com Transformers para uma explicação de como funcionam as estratégias de decodificação mais comuns.

Update on GitHub