# 生成策略

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

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

## 基本解码方法

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

### 贪心搜索

贪心搜索是默认的解码策略。它在每一步选择下一个最可能的 token。除非在 [GenerationConfig](/docs/transformers/main/zh/main_classes/text_generation#transformers.GenerationConfig) 中另行指定，否则该策略最多生成 20 个新 token。

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

```py
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'
```

### 采样

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

通过设置 `do_sample=True` 和 `num_beams=1` 来启用多项式采样。

```py
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 仍会在步骤之间以贪心方式剪除低概率序列。

> [!TIP]
> 查看 [beam search 可视化工具](https://huggingface.co/spaces/m-ric/beam_search_visualizer)，了解 beam search 的工作原理。

使用 `num_beams` 参数启用束搜索（该参数应大于 1，否则等同于贪心搜索）。

```py
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 缓存；

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

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

```py
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()](/docs/transformers/main/zh/main_classes/text_generation#transformers.GenerationMixin.generate) 的 `custom_generate` 参数从 **任意** 模型中加载。这意味着任何人都可以创建并分享自己的自定义生成方法，使其能够与任意 Transformers 模型配合使用，而无需用户安装额外的 Python 包。

```py
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()](/docs/transformers/main/zh/main_classes/text_generation#transformers.GenerationMixin.generate) 方法相同。

> [!TIP]
> 您可以通过[搜索其自定义标签](https://huggingface.co/models?other=custom_generate) `custom_generate` 来找到所有自定义生成方法。

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

```py
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](https://huggingface.co/transformers-community/custom_generate_bad_requirements) 在其 `custom_generate/requirements.txt` 文件中定义了一组无法满足的依赖，如果你尝试运行它，就会看到下面的错误信息。

```text
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**](https://huggingface.co/new) 仓库，并向其中推送几个文件。

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

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

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

#### 添加基础模型

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

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

```py
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](/docs/transformers/main/zh/main_classes/text_generation#transformers.GenerationMixin) 中定义的方法（例如基础的 `generate` 方法）。

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

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

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

```py
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()](/docs/transformers/main/zh/main_classes/text_generation#transformers.GenerationMixin.generate) 中用于验证和输入准备的逻辑。
- 如果您在 `model` 中使用了任何私有方法/属性，请在 requirements 中固定 `transformers` 的版本。
- 建议添加模型验证、输入验证，甚至单独创建一个测试文件，以便用户在其环境中对代码进行合理性检查。

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

```py
from .utils import some_function
```

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

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

> [!WARNING]
> 加载本地目录时仍会执行其中的 `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()](/docs/transformers/main/zh/main_classes/text_generation#transformers.GenerationMixin.generate) 相比，输入和/或输出方面的任何差异。这样，用户就可以专注于新特性，并参考 Transformers 文档了解通用实现细节。

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

```text
---
library_name: transformers
tags:
  - custom_generate
---

(your markdown content here)
```

推荐做法：

- 在 [generate()](/docs/transformers/main/zh/main_classes/text_generation#transformers.GenerationMixin.generate) 中记录输入和输出的差异。
- 添加自包含的示例，以便快速进行实验。
- 描述软性要求，例如该方法是否仅在特定模型家族上表现良好。

### 重用 `generate` 的输入准备

如果您正在添加新的解码循环，可能希望保留 `generate` 中已有的输入准备逻辑（批次扩展、注意力掩码、logits 处理器、停止条件等）。您还可以向 `custom_generate` 传入一个 **callable**，以复用 [generate()](/docs/transformers/main/zh/main_classes/text_generation#transformers.GenerationMixin.generate) 的完整准备流程，同时仅覆盖解码循环部分。

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

> [!TIP]
> 如果您发布了一个 `custom_generate` 仓库，您的 `generate` 实现本身可以定义一个可调用对象并将其传递给 `model.generate()`。这样，您可以在自定义解码循环的同时，仍然受益于 Transformers 内置的输入准备逻辑。

### 查找自定义生成方法

您可以通过[搜索其自定义标签](https://huggingface.co/models?other=custom_generate) `custom_generate` 来找到所有自定义生成方法。除了该标签外，我们还精选了两个 `custom_generate` 方法的集合：

- [自定义生成方法 - 社区](https://huggingface.co/collections/transformers-community/custom-generation-methods-community-6888fb1da0efbc592d3a8ab6) —— 由社区贡献的一系列强大方法；
- [自定义生成方法 - 教程](https://huggingface.co/collections/transformers-community/custom-generation-methods-tutorials-6823589657a94940ea02cfec) -- 一系列参考实现，涵盖此前属于 `transformers` 的方法，以及 `custom_generate` 的教程。

## 资源

阅读[如何生成文本：使用 Transformers 进行语言生成时的不同解码方法](https://huggingface.co/blog/how-to-generate)博客文章，了解常见解码策略的工作原理。

