Transformers 文档
优化推理
并获得增强的文档体验
开始使用
优化推理
使用大型语言模型(LLM)进行推理可能具有挑战性,因为它们需要存储和处理数十亿个参数。加载一个 70B 参数的 Llama 2 模型,需要 256GB 内存用于全精度权重,128GB 内存用于半精度权重。而目前最强大的 GPU(A100 和 H100)只有 80GB 内存。
除了内存需求之外,推理速度也很慢,因为 LLM 需要反复调用以生成下一个 token。随着生成过程的进行,输入序列会不断增长,处理时间也越来越长。
本指南将向您展示如何优化 LLM 推理以加速生成并减少内存使用。
尝试使用 Text Generation Inference (TGI),这是一个 Hugging Face 库,专门用于部署和提供高度优化的 LLM 进行推理。
静态 kv-缓存和 torch.compile
LLM 会为每个输入 token 计算键值(kv)对,并且每次都会执行相同的 kv 计算,因为生成的输出成为输入的一部分。然而,每次都执行相同的 kv 计算效率不高。
kv-缓存会存储过去的键和值,而不是每次都重新计算它们。因此,kv-缓存是动态的,并且随着每个生成步骤而增长,这使得您无法利用 torch.compile,这是一种将 PyTorch 代码融合到优化内核中的强大优化方法。
静态 kv-缓存通过将 kv-缓存大小预分配到最大值来解决这个问题,这样您就可以将其与 torch.compile 结合使用,从而将速度提高多达 4 倍。您的速度提升可能因模型大小(模型越大,速度提升越小)和硬件而异。
关注此问题以跟踪哪些模型(Llama、Gemma、Mistral 等)支持静态 kv-缓存和 torch.compile。
根据您的任务,有几种方法可以使用静态 kv-缓存。
- 对于基本用例,将 cache_implementation 设置为
"static"
(推荐)。 - 对于多轮生成或自定义生成循环,请直接初始化和处理 StaticCache。
- 对于更独特的硬件或用例,将整个 generate() 函数编译成单个图可能更好。
无论您如何使用静态 kv-缓存和 torch.compile,请使用 pad_to_multiple_of 将输入左填充到有限的一组值,以避免与形状相关的重新编译。
- 在模型的 GenerationConfig 中将 cache_implementation 设置为
"static"
。 - 调用 torch.compile 来编译带有静态 kv-缓存的前向传播。
from transformers import AutoTokenizer, AutoModelForCausalLM
import torch
import os
os.environ["TOKENIZERS_PARALLELISM"] = "false" # To prevent long warnings :)
tokenizer = AutoTokenizer.from_pretrained("google/gemma-2b")
model = AutoModelForCausalLM.from_pretrained("google/gemma-2b", torch_dtype="auto", device_map="auto")
model.generation_config.cache_implementation = "static"
model.forward = torch.compile(model.forward, mode="reduce-overhead", fullgraph=True)
input_text = "The theory of special relativity states "
input_ids = tokenizer(input_text, return_tensors="pt").to(model.device.type)
outputs = model.generate(**input_ids)
print(tokenizer.batch_decode(outputs, skip_special_tokens=True))
['The theory of special relativity states 1. The speed of light is constant in all inertial reference']
在底层,generate() 尝试重用相同的缓存对象以避免每次调用都重新编译,这对于充分利用 torch.compile 至关重要。请注意以下事项,以避免触发重新编译或生成速度低于预期。
- 如果批处理大小更改或最大输出长度在调用之间增加,则会重新初始化和重新编译缓存。
- 已编译函数的前几次调用会较慢,因为它正在被编译。
解码策略
解码也可以优化以加速生成。您可以使用一个轻量级的辅助模型来比 LLM 本身更快地生成候选 token,或者您可以使用这种解码策略的一个变体,它在以输入为基础的任务中特别有效。
推测解码
要了解更深入的解释,请查阅 辅助生成:低延迟文本生成的新方向 博客文章!
对于每个输入 token,模型权重在前向传播期间每次都会加载,这在模型拥有数十亿参数时既慢又麻烦。推测解码通过使用第二个更小、更快的辅助模型来生成候选 token,这些 token 在单个前向传播中由更大的模型进行验证,从而缓解了这种减速。如果验证的 token 是正确的,LLM 基本可以“免费”获得它们,而无需自己生成它们。准确性没有下降,因为验证前向传播确保生成与 LLM 自己生成时相同的输出。
为了获得最大的加速,辅助模型应该比 LLM 小得多,以便它可以快速生成 token。辅助模型和 LLM 模型还必须共享相同的 tokenizer,以避免重新编码和解码 token。
推测解码仅支持贪婪搜索和采样解码策略,并且不支持批处理输入。
通过加载辅助模型并将其传递给 generate() 来启用推测解码。
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
from accelerate.test_utils.testing import get_backend
device, _, _ = get_backend() # automatically detects the underlying device type (CUDA, CPU, XPU, MPS, etc.)
tokenizer = AutoTokenizer.from_pretrained("facebook/opt-1.3b")
inputs = tokenizer("Einstein's theory of relativity states", return_tensors="pt").to(device)
model = AutoModelForCausalLM.from_pretrained("facebook/opt-1.3b", torch_dtype="auto").to(device)
assistant_model = AutoModelForCausalLM.from_pretrained("facebook/opt-125m").to(device)
outputs = model.generate(**inputs, assistant_model=assistant_model)
tokenizer.batch_decode(outputs, skip_special_tokens=True)
["Einstein's theory of relativity states that the speed of light is constant. "]
提示查找解码
提示查找解码是推测解码的一种变体,也兼容贪婪搜索和采样。提示查找在输入接地任务(例如摘要)中特别有效,因为提示和输出之间通常存在重叠词。这些重叠的 N-gram 用作 LLM 候选 token。
要启用提示查找解码,请在 prompt_lookup_num_tokens 参数中指定应重叠的 token 数量。然后将此参数传递给 generate()。
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
from accelerate.test_utils.testing import get_backend
device, _, _ = get_backend() # automatically detects the underlying device type (CUDA, CPU, XPU, MPS, etc.)
tokenizer = AutoTokenizer.from_pretrained("facebook/opt-1.3b")
inputs = tokenizer("The second law of thermodynamics states", return_tensors="pt").to(device)
model = AutoModelForCausalLM.from_pretrained("facebook/opt-1.3b", torch_dtype="auto").to(device)
assistant_model = AutoModelForCausalLM.from_pretrained("facebook/opt-125m").to(device)
outputs = model.generate(**inputs, prompt_lookup_num_tokens=3)
print(tokenizer.batch_decode(outputs, skip_special_tokens=True))
['The second law of thermodynamics states that entropy increases with temperature. ']
注意力机制
Transformer 模型的一个已知问题是,自注意力机制在计算和内存方面随输入 token 的数量呈二次增长。这一限制在处理更长序列的 LLM 中更为明显。为了解决这个问题,可以尝试 FlashAttention2 或 PyTorch 的缩放点积注意力 (SDPA),它们是更节省内存的注意力实现。
FlashAttention-2
FlashAttention 和 FlashAttention-2 将注意力计算分解为更小的块,并减少对 GPU 内存的中间读/写操作次数,以加速推理。FlashAttention-2 在原始 FlashAttention 算法的基础上进行了改进,通过在序列长度维度上并行化,并更好地划分硬件上的工作以减少同步和通信开销。
要使用 FlashAttention-2,请在 from_pretrained() 中将 attn_implementation 设置为 "flash_attention_2"
。
from transformers import AutoModelForCausalLM, BitsAndBytesConfig
quant_config = BitsAndBytesConfig(load_in_8bit=True)
model = AutoModelForCausalLM.from_pretrained(
"google/gemma-2b",
quantization_config=quant_config,
torch_dtype=torch.bfloat16,
attn_implementation="flash_attention_2",
)
PyTorch 缩放点积注意力
缩放点积注意力 (SDPA) 在 PyTorch 2.0 中自动启用,它支持 FlashAttention、xFormers 和 PyTorch 的 C++ 实现。如果您使用的是 CUDA 后端,SDPA 会选择性能最佳的注意力算法。对于其他后端,SDPA 默认为 PyTorch C++ 实现。
只要您安装了最新版本的 PyTorch,SDPA 就会自动支持 FlashAttention-2。
使用 torch.nn.attention.sdpa_kernel 上下文管理器来显式启用或禁用四种注意力算法中的任何一种。例如,使用 SDPBackend.FLASH_ATTENTION
启用 FlashAttention。
import torch
from torch.nn.attention import SDPBackend, sdpa_kernel
from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained(
"google/gemma-2b",
torch_dtype=torch.bfloat16,
)
with sdpa_kernel(SDPBackend.FLASH_ATTENTION):
outputs = model.generate(**inputs)
量化
量化通过以较低精度存储模型权重来减小模型大小。这会降低内存使用量,并在 GPU 内存受限时使 LLM 推理更易于访问。
如果您不受 GPU 限制,则不必对模型进行量化,因为它可能会略微增加延迟(AWQ 和融合 AWQ 模块除外),因为需要额外的步骤来量化和反量化权重。
有许多量化库可用(有关更多详细信息,请参阅量化指南),例如 Quanto、AQLM、VPTQ、AWQ 和 AutoGPTQ。欢迎尝试它们,看看哪个最适合您的用例。我们还建议阅读《🤗 Transformers 中原生支持的量化方案概述》博客文章,其中比较了 AutoGPTQ 和 bitsandbytes。
使用下面的模型内存计算器估算和比较加载模型所需的内存。例如,尝试估算加载 Mistral-7B-v0.1 所需的内存。
要以半精度加载模型,请在 from_pretrained() 中将 torch_dtype 参数设置为 torch.bfloat16
。这需要 13.74GB 内存。
from transformers import AutoTokenizer, AutoModelForCausalLM
import torch
model = AutoModelForCausalLM.from_pretrained(
"mistralai/Mistral-7B-v0.1", torch_dtype=torch.bfloat16, device_map="auto",
)
要加载量化模型(8 位或 4 位),请尝试 bitsandbytes 并将 load_in_4bit 或 load_in_8bit 参数设置为 True
。以 8 位加载模型只需要 6.87 GB 内存。
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
import torch
quant_config = BitsAndBytesConfig(load_in_8bit=True)
model = AutoModelForCausalLM.from_pretrained(
"mistralai/Mistral-7B-v0.1", quantization_config=quant_config, device_map="auto"
)