Transformers 文档

生成策略

Hugging Face's logo
加入 Hugging Face 社区

并获得增强的文档体验

开始使用

生成策略

解码策略指示模型如何选择下一个生成的token。解码策略有多种类型,选择合适的策略对生成的文本质量有重大影响。

本指南将帮助您了解 Transformers 中可用的不同解码策略,以及如何及何时使用它们。

基本解码方法

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

贪婪搜索

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

贪婪搜索适用于输出相对较短且创造性不是优先考虑的任务。然而,当生成较长的序列时,它会因为开始重复自身而失效。

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-2-7b-hf")
inputs = tokenizer("Hugging Face is an open-source company", return_tensors="pt").to("cuda")

model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-7b-hf", torch_dtype=torch.float16).to("cuda")
# 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'

采样

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

通过设置 do_sample=Truenum_beams=1 启用多项式采样。

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-2-7b-hf")
inputs = tokenizer("Hugging Face is an open-source company", return_tensors="pt").to("cuda")

model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-7b-hf", torch_dtype=torch.float16).to("cuda")
# 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.'

束搜索

束搜索在每个时间步跟踪多个生成的序列(束)。在经过一定数量的步骤后,它选择整体概率最高的序列。与贪婪搜索不同,此策略可以“向前看”并选择整体概率更高的序列,即使初始token的概率较低。它最适合于输入接地任务,如描述图像或语音识别。您也可以在束搜索中使用 do_sample=True 进行每步采样,但束搜索仍会在步骤之间贪婪地剪除低概率序列。

请查看 束搜索可视化工具,了解束搜索的工作原理。

使用 num_beams 参数启用束搜索(应大于 1,否则等同于贪婪搜索)。

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-2-7b-hf")
inputs = tokenizer("Hugging Face is an open-source company", return_tensors="pt").to("cuda")

model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-7b-hf", torch_dtype=torch.float16).to("cuda")
# 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']"

高级解码方法

高级解码方法旨在解决特定的生成质量问题(例如重复)或在某些情况下提高生成吞吐量。这些技术更复杂,可能不适用于所有模型。

推测解码

推测或辅助解码并非搜索或采样策略。相反,推测解码添加了一个第二个较小的模型来生成候选token。主模型在单个 forward 传递中验证候选token,这整体上加快了解码过程。此方法对于 LLM 特别有用,因为生成token可能更昂贵和缓慢。有关更多信息,请参阅推测解码指南。

目前,推测解码仅支持贪婪搜索和多项式采样。批处理输入也不受支持。

使用 assistant_model 参数启用推测解码。您会注意到,如果辅助模型远小于主模型,速度提升最快。添加 do_sample=True 以启用重采样验证token。

贪婪搜索
多项式采样
from transformers import AutoModelForCausalLM, AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("HuggingFaceTB/SmolLM-1.7B")
model = AutoModelForCausalLM.from_pretrained("HuggingFaceTB/SmolLM-1.7B")
assistant_model = AutoModelForCausalLM.from_pretrained("HuggingFaceTB/SmolLM-135M")
inputs = tokenizer("Hugging Face is an open-source company", return_tensors="pt")

outputs = model.generate(**inputs, assistant_model=assistant_model)
tokenizer.batch_decode(outputs, skip_special_tokens=True)
'Hugging Face is an open-source company that provides a platform for developers to build and deploy machine'

推测解码也支持在 Pipeline 中使用 assistant_model 参数。

from transformers import pipeline
import torch

pipe = pipeline(
    "text-generation",
    model="meta-llama/Llama-3.1-8B",
    assistant_model="meta-llama/Llama-3.2-1B",
    torch_dtype=torch.bfloat16
)
pipe_output = pipe("Once upon a time, ", max_new_tokens=50, do_sample=False)
pipe_output[0]["generated_text"]

提示查找解码

提示查找解码是推测解码的一种变体,它使用重叠的N-gram作为候选token。它非常适用于摘要等输入接地任务。有关更多信息,请参阅提示查找解码指南。

使用 prompt_lookup_num_tokens 参数启用提示查找解码。

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("HuggingFaceTB/SmolLM-1.7B")
model = AutoModelForCausalLM.from_pretrained("HuggingFaceTB/SmolLM-1.7B", torch_dtype=torch.float16).to("cuda")
assistant_model = AutoModelForCausalLM.from_pretrained("HuggingFaceTB/SmolLM-135M", torch_dtype=torch.float16).to("cuda")
inputs = tokenizer("Hugging Face is an open-source company", return_tensors="pt").to("cuda")

outputs = model.generate(**inputs, assistant_model=assistant_model, max_new_tokens=20, prompt_lookup_num_tokens=5)
tokenizer.batch_decode(outputs, skip_special_tokens=True)
'Hugging Face is an open-source company that provides a platform for developers to build and deploy machine learning models. It offers a variety of tools'

自推测解码

早期退出使用来自语言建模头的早期隐藏状态作为输入,有效跳过层以产生较低质量的输出。较低质量的输出用作辅助输出,并应用自推测来使用剩余层修复输出。这种自推测方法最终生成的结果与原始模型的生成结果相同(或具有相同的分布)。

辅助模型也是目标模型的一部分,因此可以共享缓存和权重,从而降低内存需求。

对于使用早期退出训练的模型,将 assistant_early_exit 传递给 generate()

from transformers import AutoModelForCausalLM, AutoTokenizer

prompt = "Alice and Bob"
checkpoint = "facebook/layerskip-llama3.2-1B"

tokenizer = AutoTokenizer.from_pretrained(checkpoint)
inputs = tokenizer(prompt, return_tensors="pt")

model = AutoModelForCausalLM.from_pretrained(checkpoint)
outputs = model.generate(**inputs, assistant_early_exit=4, do_sample=False, max_new_tokens=20)
tokenizer.batch_decode(outputs, skip_special_tokens=True)

通用辅助解码

通用辅助解码 (UAD) 允许主模型和辅助模型使用不同的分词器。主模型的输入token被重新编码为辅助模型token。候选token在辅助编码中生成,然后重新编码为主模型候选token。候选token的验证方式与推测解码中解释的一样。

重新编码涉及将token ID解码为文本,并使用不同的分词器对文本进行编码。为了防止重新编码期间出现分词差异,UAD 会在源编码和目标编码之间找到最长公共子序列,以确保新token包含正确的提示后缀。

tokenizerassistant_tokenizer 参数添加到 generate() 以启用 UAD。

from transformers import AutoModelForCausalLM, AutoTokenizer

prompt = "Alice and Bob"

assistant_tokenizer = AutoTokenizer.from_pretrained("double7/vicuna-68m")
tokenizer = AutoTokenizer.from_pretrained("google/gemma-2-9b")
inputs = tokenizer(prompt, return_tensors="pt")

model = AutoModelForCausalLM.from_pretrained("google/gemma-2-9b")
assistant_model = AutoModelForCausalLM.from_pretrained("double7/vicuna-68m")
outputs = model.generate(**inputs, assistant_model=assistant_model, tokenizer=tokenizer, assistant_tokenizer=assistant_tokenizer)
tokenizer.batch_decode(outputs, skip_special_tokens=True)
['Alice and Bob are sitting in a bar. Alice is drinking a beer and Bob is drinking a']

对比搜索

对比搜索是一种解码策略,旨在减少重复,即使在生成较长序列时也是如此。此策略会比较生成的token与先前token的相似程度,如果它们更相似,则应用惩罚。

使用 penalty_alphatop_k 参数启用对比搜索。penalty_alpha 管理应用的惩罚,top_k 是要返回的最有可能的token数量。

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-2-7b-hf")
inputs = tokenizer("Hugging Face is an open-source company", return_tensors="pt").to("cuda")

model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-7b-hf", torch_dtype=torch.float16).to("cuda")
# explicitly set to 100 because Llama2 generation length is 4096
outputs = model.generate(**inputs, max_new_tokens=100, penalty_alpha=0.6, top_k=4)
tokenizer.batch_decode(outputs, skip_special_tokens=True)
'Hugging Face is an open-source company that provides a platform for building and deploying AI models.\nHugging Face is an open-source company that provides a platform for building and deploying AI models. The platform allows developers to build and deploy AI models, as well as collaborate with other developers.\nHugging Face was founded in 2019 by Thibault Wittemberg and Clément Delangue. The company is based in Paris, France.\nHugging Face has'

DoLa

通过对比层进行解码 (DoLa) 是一种对比解码策略,旨在提高事实性和减少幻觉。此策略通过对比最终层和早期层之间的logit差异来工作。结果,特定层中本地化的事实知识被放大。不建议将 DoLa 用于 GPT-2 等较小的模型。

使用以下参数启用 DoLa。

  • dola_layers 是要与最终层进行对比的候选层。它可以是字符串(lowhigh),用于对比层的较低或较高部分。对于 TruthfulQA 等简答任务,建议使用 high。对于 GSM8K、StrategyQA、FACTOR 和 VicunaQA 等长答推理任务,建议使用 low

    当模型具有共享词嵌入时,会跳过第 0 层并从第 2 层开始。

    它也可以是表示 0 和总层数之间层索引的整数列表。第 0 层是词嵌入,第 1 层是第一个transformer层,依此类推。有关层索引范围的更多信息,请参阅下表,具体取决于模型层数。

    > 40 (0, 20, 2) (N - 20, N, 2)
    <= 40 range(0, N // 2, 2) range(N // 2, N, 2)
  • repetition_penalty 减少重复,建议设置为 1.2。

对比高层
对比特定层
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("HuggingFaceTB/SmolLM-1.7B")
model = AutoModelForCausalLM.from_pretrained("HuggingFaceTB/SmolLM-1.7B", torch_dtype=torch.float16).to("cuda")
inputs = tokenizer("What is the highest peak in the world??", return_tensors="pt").to("cuda")

outputs = model.generate(**inputs, max_new_tokens=50, dola_layers="high", do_sample=False)
tokenizer.batch_decode(outputs, skip_special_tokens=True)
" Mount EverestMount Everest, called Himalaya in Nepali, is the world's highest peak, lying almost 9.5 kilometers above the sea level and the tallest mountain from 19,036.91 ft. The mountain was"

多样化束搜索

多样化束搜索是束搜索的一种变体,它生成更多样化的输出候选以供选择。此策略衡量序列的差异性,如果序列过于相似,则会施加惩罚。为了避免高计算成本,束的数量被分成多个组。

使用 num_beamsnum_beam_groupsdiversity_penalty 参数启用多样化束搜索(num_beams 参数应可被 num_beam_groups 整除)。

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-2-7b-hf")
inputs = tokenizer("Hugging Face is an open-source company", return_tensors="pt").to("cuda")

model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-7b-hf", torch_dtype=torch.float16).to("cuda")
# explicitly set to 100 because Llama2 generation length is 4096
outputs = model.generate(**inputs, max_new_tokens=50, num_beams=6, num_beam_groups=3, diversity_penalty=1.0, do_sample=False)
tokenizer.batch_decode(outputs, skip_special_tokens=True)
'Hugging Face is an open-source company 🤗\nWe are an open-source company. Our mission is to democratize AI and make it accessible to everyone. We believe that AI should be used for the benefit of humanity, not for the benefit of a'

自定义解码方法

自定义解码方法支持专门的生成行为,例如:

  • 在不确定时让模型继续思考;
  • 如果模型卡住,则回滚生成;
  • 使用自定义逻辑处理特殊token;
  • 为高级模型增强输入准备;

我们通过模型仓库启用自定义解码方法,假定特定的模型标签和文件结构(参见下面的子部分)。此功能是自定义建模代码的扩展,因此需要设置 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 decoding 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 decoding 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 decoding 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,它在提示前添加一些填充token。

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/decoding_method", push_to_hub=True)
model.save_pretrained("your/decoding_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_codecustom_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 中使用任何私有方法/属性,请在需求中固定 transformers 版本。
  • 考虑添加模型验证、输入验证,甚至单独的测试文件,以帮助用户在其环境中进行代码健全性检查。

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

from .utils import some_function

仅支持来自同一级别 custom_generate 文件夹的相对导入。父/兄弟文件夹导入无效。custom_generate 参数也适用于包含 custom_generate 结构的任何目录。这是开发自定义解码方法的推荐工作流程。

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() 中记录输入和输出差异。
  • 添加自包含示例以实现快速实验。
  • 描述软性要求,例如该方法是否仅适用于某些模型系列。

资源

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

< > 在 GitHub 上更新