Transformers documentation
生成策略
生成策略
解码策略决定了模型应如何选取下一个生成的 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 仓库,并向其中推送几个文件。
- 您用于设计生成方法的模型。
custom_generate/generate.py,其中包含自定义生成方法的所有逻辑。custom_generate/requirements.txt,用于可选地添加新的 Python 依赖项和/或锁定特定版本,以正确使用您的方法。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的方法,以及custom_generate的教程。
资源
阅读如何生成文本:使用 Transformers 进行语言生成时的不同解码方法博客文章,了解常见解码策略的工作原理。
Update on GitHub