• 文档 >
  • 编译来自 Huggingface 的 LLM 模型
快捷方式

从 Huggingface 编译 LLM 模型

本教程将引导您了解如何使用 Torch-TensorRT 从 Huggingface 编译 LLM 模型。我们还将介绍 Torch-TensorRT 中的 KV 缓存,它可以极大地提高 LLM 推理的性能。代码可在 tools/llm 目录中找到。我们使用 run_llm.py 脚本来编译模型、生成输出并测量性能。

注意

这是一个**实验性版本**,API 可能会在未来版本中更改。

注意

Llama-2-7b-chat-hf 和 gpt2 模型的编译脚本和教程已整合到位于 tools/llm 目录下的统一 run_llm.py 脚本中。

tools/llm 目录概述

tools/llm 目录提供了以下工具来从 Huggingface 编译 LLM 模型

  • run_llm.py: 模型编译、生成输出和基准测试的主要入口点

  • 静态缓存实用程序:用于 KV 缓存优化的 static_cache_v1.pystatic_cache_v2.py

  • SDPA Attention:用于注册缩放点积注意力转换器和降级过程的 sdpa_converter.pyregister_sdpa.py

  • 测试组件:用于验证的模型特定测试文件

  • 实用函数:用于常见操作的 utils.pycache_utils.py

支持的模型

我们已正式验证对以下 LLM 系列的支持

模型系列

HuggingFace 模型卡

精度

支持 KV 缓存?

GPT-2

gpt2

FP16, FP32

LLaMA 2

meta-llama/Llama-2-7b-chat-hf

FP16, FP32

LLaMA 3.1

meta-llama/Llama-3.1-8B-Instruct

FP16, FP32

LLaMA 3.2

meta-llama/Llama-3.2-1B-Instruct
meta-llama/Llama-3.2-3B-Instruct

FP16, FP32

Qwen 2.5

Qwen/Qwen2.5-0.5B-Instruct
Qwen/Qwen2.5-1.5B-Instruct
Qwen/Qwen2.5-3B-Instruct
Qwen/Qwen2.5-7B-Instruct

FP16, FP32

run_llm.py 上手指南

主要入口点是 run_llm.py,它为模型编译和基准测试提供了完整的工作流程。

基本用法

python tools/llm/run_llm.py \
  --model meta-llama/Llama-3.2-1B-Instruct \
  --prompt "What is parallel programming?" \
  --precision FP16 \
  --num_tokens 128 \
  --cache static_v2 \
  --benchmark

关键参数

  • --model:HuggingFace LLM 的名称或路径

  • --tokenizer:(可选)分词器名称;默认为模型名称

  • --prompt:用于文本生成的输入提示

  • --precision:精度模式(FP16, FP32

  • --num_tokens:要生成的输出 token 数量

  • --cache:KV 缓存类型(static_v1static_v2,或为空表示不使用 KV 缓存)

  • --benchmark:启用基准测试模式以进行性能比较

  • --enable_pytorch_run:同时运行并比较 PyTorch 基线

其他用法示例

# Compare different models performance
python tools/llm/run_llm.py --model gpt2 --benchmark --enable_pytorch_run
python tools/llm/run_llm.py --model meta-llama/Llama-3.2-1B-Instruct --benchmark --enable_pytorch_run

# Generate the outputs (disable benchmarking) by specifying the number of tokens to generate. Default = 128
python tools/llm/run_llm.py --model gpt2 --prompt "What is parallel programming?" --num_tokens 128
python tools/llm/run_llm.py --model meta-llama/Llama-3.2-1B-Instruct --prompt "What is parallel programming?" --num_tokens 128

# Test different caching approaches
python tools/llm/run_llm.py --model meta-llama/Llama-3.2-1B-Instruct --cache static_v1
python tools/llm/run_llm.py --model meta-llama/Llama-3.2-1B-Instruct --cache static_v2

# Compare FP16 vs FP32 performance
python tools/llm/run_llm.py --model Qwen/Qwen2.5-1.5B-Instruct --precision FP16 --benchmark
python tools/llm/run_llm.py --model Qwen/Qwen2.5-1.5B-Instruct --precision FP32 --benchmark

Torch-TensorRT 中的 KV 缓存

我们提供两个版本的静态 KV 缓存:static_cache_v1static_cache_v2。在这两种实现中,我们将静态 KV 缓存张量作为模型输入/输出添加,而不将其存储为外部内存。KV 缓存的长度 = 输入序列长度 + 输出序列长度(由 --num_tokens 指定)。头的数量和头维度由模型配置决定。

静态缓存 v1

static_cache_v1.py 在模型图中实现 KV 缓存如下:

class StaticCacheV1Model(nn.Module):
    def __init__(self):
        super().__init__()

    def forward(self, q, k, v, key_cache, value_cache, start_idx, end_idx, is_causal=True):
        # Concatenate new key/value pairs with existing cache
        new_key_cache = torch.cat((key_cache[:, :, :start_idx, :], k, key_cache[:, :, end_idx:, :]), dim=2)
        new_value_cache = torch.cat((value_cache[:, :, :start_idx, :], v, value_cache[:, :, end_idx:, :]), dim=2)

        # Compute attention using the updated cache
        attn_output = torch._C._nn.scaled_dot_product_attention(
            q,
            new_key_cache[:, :, :end_idx, :],
            new_value_cache[:, :, :end_idx, :],
            dropout_p=0.0,
            is_causal=is_causal
        )

        return attn_output, new_key_cache, new_value_cache

在上面的代码中,我们将新的键/值对与现有缓存连接并更新它。为了计算注意力,我们使用更新后的缓存,并从缓存中收集直到并包括当前 token 索引的相应键/值。上述代码实际上是作为 FX 图转换过程实现的。当我们导入 static_cache_v1.py 模块时,我们使用装饰器 @_aten_lowering_pass 将其注册为 Torch-TensorRT 的降级过程。

注意

start_idxend_idx 是当前 token 在缓存中的起始和结束索引。对于预填充阶段,start_idx 为 0,end_idx 是输入序列的长度。对于解码阶段,start_idx 从输入序列长度开始,end_idx 等于 start_idx + 1start_idx 每次加 1,直到序列结束或达到要生成的最大 token 数。

静态缓存 v2

static_cache_v2.pystatic_cache_v1.py 类似,但它使用的切片操作更少。它在模型图中实现 KV 缓存如下:

class StaticCacheV2Model(nn.Module):
    def __init__(self):
        super().__init__()

    def forward(self, q, k, v, key_cache, value_cache, start_idx, end_idx, is_causal=True):
        concat_keys = torch.cat((key_cache[:, :, :start_idx, :], k), dim=2)
        concat_values = torch.cat((value_cache[:, :, :start_idx, :], v), dim=2)
        new_key_cache = torch.cat((concat_keys, key_cache[:, :, end_idx:, :]), dim=2)
        new_value_cache = torch.cat((concat_values, value_cache[:, :, end_idx:, :]), dim=2)
        attn_output = torch._C._nn.scaled_dot_product_attention(
              q, concat_keys, concat_values, dropout_p=0.0, is_causal=is_causal
        )

        return attn_output, new_key_cache, new_value_cache

在上面的代码中,我们将现有的键/值缓存与当前 token 的键/值连接起来。我们用它来直接计算注意力并更新键/值缓存,插入当前的键/值。上述代码实际上是作为 FX 图转换过程实现的。当我们导入 static_cache_v1.py 模块时,我们使用装饰器 @_aten_lowering_pass 将其注册为 Torch-TensorRT 的降级过程。start_idxend_idx 的定义与 static_cache_v1.py 中相同。

在使用静态 KV 缓存编译模型后,模型的输入签名会发生变化。新的输入签名为 (input_ids, position_ids, key_cache_0, value_cache_0, ..., start_idx, end_idx)。键/值缓存张量的数量等于模型中注意力头的数量。我们可以使用 generate_with_static_cache 函数来生成输出。

生成输出

我们使用自定义的 generate 函数来生成输出。此函数执行标准的自回归解码,不使用 KV 缓存。还有一个 generate_with_static_cache 函数,它执行带 KV 缓存的自回归解码。

generate_with_static_cache 函数负责为使用静态 KV 缓存编译的模型准备输入。模型输入为 input_idsposition_idskey_cache_0value_cache_0、...、start_idxend_idx。我们将键/值缓存张量初始化为零,对于每个生成的 token,新的键/值缓存张量是模型的输出。

SDPA 转换器 (sdpa_converter.py)

  • 使用 TRT Python API 转换缩放点积注意力操作。

  • 支持因果自注意力和标准自注意力。

SDPA 注册 (register_sdpa.py)

  • 这是一个 Torch-TensorRT 降级过程,它将 SDPA 的变体替换为 torch.nn.functional.scaled_dot_product_attention

  • 注册用于转换 torch.nn.functional.scaled_dot_product_attention 操作的 SDPA 转换器。

局限性和已知问题

  • 滑动窗口注意力(在 Gemma3 和 Qwen 3 模型中使用)尚不支持

  • 某些模型架构(例如 Phi-4)在导出 torch 模型时存在问题。

要求

  • Torch-TensorRT 2.8.0 或更高版本

  • Transformers v4.52.3

文档

访问全面的 PyTorch 开发者文档

查看文档

教程

为初学者和高级开发者提供深入的教程

查看教程

资源

查找开发资源并让您的问题得到解答

查看资源