Transformers documentation

生成策略

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

生成策略

解码策略决定了模型应如何选取下一个生成的 token。解码策略有多种类型,选择合适的策略对生成文本的质量有显著影响。

本指南将帮助你了解 Transformers 中可用的不同解码策略,以及它们的使用方式和适用场景。

基本解码方法

这些是成熟的解码方法,应作为文本生成任务的起点。

贪心搜索

贪心搜索是默认的解码策略。它在每一步选择下一个最可能的 token。除非在 GenerationConfig 中另行指定,否则该策略最多生成 20 个新 token。

贪心搜索在输出相对较短且不以创意为首要考量的任务中表现良好。然而,在生成长序列时,它会出现重复自身的问题,从而失效。

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'

采样

采样(Sampling),或多项式采样(multinomial sampling),会根据模型整个词表上的概率分布随机选择一个 token(与贪心搜索中仅选择最可能的 token 不同)。这意味着任何具有非零概率的 token 都有机会被选中。采样策略可以减少重复,并生成更具创造性和多样性的输出。

通过设置 do_sample=True 和 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.'

束搜索

Beam search 在每个时间步都会跟踪多个已生成的序列(即 beam)。经过若干步之后,它会选择整体概率最高的序列。与贪心搜索不同,这种策略能够“向前看”,即使初始 token 的概率较低,也能选出整体概率更高的序列。它最适合基于输入的任务,例如图像描述或语音识别。你也可以在 beam search 中使用 do_sample=True 来在每一步进行采样,但 beam search 仍会在步骤之间以贪心方式剪除低概率序列。

查看 beam search 可视化工具,了解 beam search 的工作原理。

使用 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, 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']"

自定义生成方法

自定义生成方法可以实现以下特殊行为:

  • 如果模型不确定,则让其继续思考;
  • 如果模型陷入停滞,回滚生成过程;
  • 使用自定义逻辑处理特殊标记;
  • 使用专用的 KV 缓存;

我们通过模型仓库启用自定义生成方法,并假定特定的模型标签和文件结构(详见下文小节)。此功能是自定义建模代码的扩展,因此同样需要设置 trust_remote_code=True。

如果模型仓库包含自定义的生成方法,最简单的尝试方式是加载该模型并使用它进行生成:

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'

具有自定义生成方法的模型仓库拥有一项特殊属性:其生成方法可以通过 generate() 的 custom_generate 参数从 任意模型中加载。这意味着任何人都可以创建并分享其自定义生成方法,使其能够与任意 Transformers 模型配合使用,而无需用户安装额外的 Python 包。

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'

你应该阅读包含自定义生成策略的仓库中的 README.md 文件,以了解是否存在新的参数以及输出类型的差异。如果没有,你可以假设其工作方式与基础的 generate() 方法相同。

您可以通过搜索其自定义标签 custom_generate 来找到所有自定义生成方法。

以 Hub 仓库 transformers-community/custom_generate_example 为例。其 README.md 指出,该仓库有一个额外的输入参数 left_padding,用于在提示词前添加一定数量的填充标记。

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'

如果自定义方法固定了 Python 依赖要求,而你的环境不满足这些要求,你将收到关于缺少依赖的异常。例如,transformers-community/custom_generate_bad_requirements 在其 custom_generate/requirements.txt 文件中定义了一组无法满足的依赖要求,如果你尝试运行它,将会看到下方的错误信息。

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)

相应地更新您的 Python 依赖项即可消除此错误信息。

创建自定义生成方法

要创建新的生成方法,您需要创建一个新的 Model 仓库,并向其中推送几个文件。

  1. 您用于设计生成方法的模型。
  2. custom_generate/generate.py,其中包含自定义生成方法的所有逻辑。
  3. custom_generate/requirements.txt,用于可选地添加新的 Python 依赖项和/或锁定特定版本,以正确使用您的方法。
  4. README.md,您应在此处添加 custom_generate 标签,并记录自定义方法中任何新的参数或输出类型差异。

添加完所有必需的文件后,您的仓库应如下所示

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

添加基础模型

自定义生成方法的起点是一个与其他模型仓库无异的模型仓库。需要添加到该仓库中的模型,应当是您设计该方法时所针对的模型,并且它旨在构成一个可独立运行的“模型-生成”组合。当加载该仓库中的模型时,您的自定义生成方法将覆盖 generate。请放心——正如上文所述,您的生成方法仍然可以与 Transformers 中的任何其他模型一起加载使用。

如果您只是想复制一个现有模型,可以

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

这是您生成方法的核心。它必须包含一个名为 generate 的方法,并且该方法必须将 model 作为其第一个参数。model 是模型实例,这意味着您可以访问模型中的所有属性和方法,包括在 GenerationMixin 中定义的方法(例如基础的 generate 方法)。

generate.py 必须放在名为 custom_generate 的文件夹中,而不能位于仓库的根目录。此功能的文件路径是硬编码的。

在底层,当使用 custom_generate 参数调用基础的 generate() 方法时,它会首先检查其 Python 依赖(如果有的话),然后在 generate.py 中定位自定义的 generate 方法,最后调用该自定义 generate。所有接收到的参数以及 model 都会转发到您的自定义 generate 方法中,但用于触发自定义生成的参数(trust_remote_code 和 custom_generate)除外。

这意味着您的 generate 可以混合使用原始参数和自定义参数(以及不同的输出类型),如下所示。

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

请遵循以下推荐做法,以确保您的自定义生成方法按预期工作。

  • 您可以自由复用原始 generate() 中用于验证和输入准备的逻辑。
  • 如果您在 model 中使用了任何私有方法/属性,请在 requirements 中固定 transformers 的版本。
  • 建议添加模型验证、输入验证,甚至单独创建一个测试文件,以便用户在其环境中对代码进行合理性检查。

您的自定义 generate 方法可以从 custom_generate 文件夹中相对导入代码。例如,如果您有一个 utils.py 文件,可以这样导入:

from .utils import some_function

仅支持从同级 custom_generate 文件夹进行相对导入。父级/同级文件夹的导入无效。custom_generate 参数在本地也可与任何包含 custom_generate 结构的目录配合使用,这是开发自定义生成方法的推荐工作流。

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

加载本地目录时仍会执行其中的 custom_generate/generate.py,因此与 Hub 仓库一样,需要设置 trust_remote_code=True。请仅对由您编写或已审查过的代码传入 trust_remote_code=True。

requirements.txt

您可以选择在 custom_generate 文件夹中提供一个 requirements.txt 文件,以指定额外的 Python 依赖项。这些依赖项会在运行时进行检查,如果缺失则会抛出异常,提示用户相应地更新其环境。

README.md

模型仓库根目录下的 README.md 通常用于描述其中的模型。但由于该仓库的重点是自定义生成方法,我们强烈建议将描述重心转向自定义生成方法。除了对该方法的说明外,我们还建议记录与原始 generate() 相比,输入和/或输出方面的任何差异。这样,用户就可以专注于新特性,并依赖 Transformers 文档来了解通用实现细节。

为了便于发现,我们强烈建议您在仓库中添加 custom_generate 标签。为此,您的 README.md 文件顶部应如下例所示。推送文件后,您应该能在仓库中看到该标签!

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

(your markdown content here)

推荐做法:

  • 在 generate() 中记录输入和输出的差异。
  • 添加自包含的示例,以便快速进行实验。
  • 描述软性要求,例如该方法是否仅在特定模型家族上表现良好。

复用 generate 的输入准备

如果您正在添加新的解码循环,可能希望保留 generate 中已有的输入准备逻辑(批次扩展、注意力掩码、logits 处理器、停止条件等)。您还可以向 custom_generate 传入一个 callable,以复用 generate() 的完整准备流程,同时仅覆盖解码循环部分。

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

如果您发布了一个 custom_generate 仓库,您的 generate 实现本身可以定义一个可调用对象并将其传递给 model.generate()。这样,您既能自定义解码循环,又能利用 Transformers 内置的输入准备逻辑。

查找自定义生成方法

您可以通过搜索其自定义标签 custom_generate 来找到所有自定义生成方法。除了该标签外,我们还精选了两个 custom_generate 方法的集合:

资源

阅读如何生成文本:使用 Transformers 进行语言生成时的不同解码方法博客文章,了解常见解码策略的工作原理。

Update on GitHub