• 文档 >
  • 使用 C++ 运行 LLM
快捷方式

使用 C++ 运行 LLM

本指南解释了如何使用 ExecuTorch 的 C++ runner 库来运行已导出为 .pte 格式的 LLM 模型。Runner 库提供了一个用于 LLM 文本生成的高级 API,负责处理分词、推理和 token 生成。

先决条件

在开始之前,请确保您已

  1. 使用 export_llm API 导出了 .pte 格式的模型,如 开箱即用导出流行的 LLM导出自定义 LLM 中所述。

    • 请参阅 模型元数据 部分,了解需要序列化到 .pte 的重要元数据。

  2. 与您的模型兼容的分词器文件

    • 对于 HuggingFace 分词器,这是一个 JSON 文件 tokenizer.json

    • 对于 SentencePiece 分词器,这是一个 tokenizer.model 文件,通常与权重文件放在一起

  3. 安装了 CMake 和 C++ 编译器

    • CMake 版本 3.29 或更高版本

    • g++ 或 clang 编译器

模型元数据

元数据包含在导出步骤中应包含的几个重要配置参数,这些参数将由 runner 库使用。

  1. enable_dynamic_shape:模型是否支持动态输入形状

  2. max_seq_len:模型可以处理的最大序列长度

  3. max_context_len:KV 缓存的最大上下文长度

  4. use_kv_cache:模型是否使用 KV 缓存以实现高效生成

  5. get_bos_id:序列开始 (Beginning-of-sequence) token ID

  6. get_eos_ids:序列结束 (End-of-sequence) token IDs

导出时添加元数据

为确保您的模型具有必要的元数据,您可以在导出时使用导出配置中的 metadata 参数进行指定。

# export_llm
python -m extension.llm.export.export_llm \
  --config path/to/config.yaml \
  +base.metadata='{"get_bos_id":128000, "get_eos_ids":[128009, 128001], "get_max_context_len":4096}'

构建 Runner 库

ExecuTorch LLM runner 库可以使用 CMake 构建。要将其集成到您的项目中,请

  1. 将 ExecuTorch 添加到您的 CMake 项目的依赖项中

  2. 启用所需的组件(extension_module, extension_tensor 等)

  3. 将您的应用程序链接到 extension_llm_runner

以下是 CMake 配置的一个简化示例

# Enable required components
set_overridable_option(EXECUTORCH_BUILD_EXTENSION_MODULE ON)
set_overridable_option(EXECUTORCH_BUILD_EXTENSION_TENSOR ON)
set_overridable_option(EXECUTORCH_BUILD_EXTENSION_LLM_RUNNER ON)

# Add ExecuTorch as a dependency
add_subdirectory(executorch)

# Link against the LLM runner library
target_link_libraries(your_app PRIVATE extension_llm_runner)

构建 Llama Runner

ExecuTorch 在 examples/models/llama 目录中提供了一个完整的 Llama 模型 C++ runner 示例。此 runner 演示了如何使用 LLM runner 库来运行导出为 .pte 格式的 Llama 模型。

请注意,此 runner 库不仅限于 Llama 模型,还可以用于任何已导出为 .pte 的纯文本、仅解码器的 LLM 模型。

基本用法示例

以下是使用 runner 的一个简化示例

#include <executorch/extension/llm/runner/text_llm_runner.h>

using namespace executorch::extension::llm;

int main() {
  // Load tokenizer and create runner
  auto tokenizer = load_tokenizer("path/to/tokenizer.json", nullptr, std::nullopt, 0, 0);
  auto runner = create_text_llm_runner("path/to/model.pte", std::move(tokenizer));

  // Load the model
  runner->load();

  // Configure generation
  GenerationConfig config;
  config.max_new_tokens = 100;
  config.temperature = 0.8f;

  // Generate text with streaming output
  runner->generate("Hello, world!", config,
    [](const std::string& token) { std::cout << token << std::flush; },
    nullptr);

  return 0;
}

Runner API 架构

ExecuTorch LLM runner 库采用模块化架构设计,该架构将文本生成流水线中不同组件之间的关注点分离。

IRunner 接口

IRunner 接口(irunner.h)定义了 LLM 文本生成的核心功能。该接口作为与 LLM 模型交互的主要抽象。

class IRunner {
public:
  virtual ~IRunner() = default;
  virtual bool is_loaded() const = 0;
  virtual runtime::Error load() = 0;
  virtual runtime::Error generate(...) = 0;
  virtual runtime::Error generate_from_pos(...) = 0;
  virtual void stop() = 0;
};

让我们详细检查每个方法。

bool is_loaded() const

检查模型和所有必需的资源是否已加载到内存中并准备好进行推理。在尝试生成文本之前,此方法对于验证 runner 的状态很有用。

runtime::Error load()

加载模型并为其做好推理准备。这包括:

  • .pte 文件加载模型权重

  • 初始化任何必要的缓冲区或缓存

  • 准备执行环境

在任何生成尝试之前都应调用此方法。它返回一个 Error 对象,指示成功或失败。

runtime::Error generate(
   const std::string& prompt,
   const GenerationConfig& config,
   std::function<void(const std::string&)> token_callback,
   std::function<void(const Stats&)> stats_callback)

用于文本生成的主要方法。它接受:

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

  • config:控制生成过程的配置参数

  • token_callback:一个回调函数,它接收每个生成的 token 作为字符串

  • stats_callback:一个回调函数,它在生成完成后接收性能统计数据

token 回调函数在每个 token 生成时被调用,允许流式输出。stats 回调函数在生成完成后提供详细的性能指标。

runtime::Error generate_from_pos(
   const std::string& prompt,
   int64_t start_pos,
   const GenerationConfig& config,
   std::function<void(const std::string&)> token_callback,
   std::function<void(const Stats&)> stats_callback)

generate() 的高级版本,允许从 KV 缓存的特定位置开始生成。这对于从先前的状态继续生成很有用。

void stop()

立即停止生成循环。这通常是从另一个线程调用以中断长时间运行的生成。

GenerationConfig 结构

GenerationConfig 结构控制生成过程的各个方面。

struct GenerationConfig {
  bool echo = true;                // Whether to echo the input prompt in the output
  int32_t max_new_tokens = -1;     // Maximum number of new tokens to generate
  bool warming = false;            // Whether this is a warmup run
  int32_t seq_len = -1;            // Maximum number of total tokens
  float temperature = 0.8f;        // Temperature for sampling
  int32_t num_bos = 0;             // Number of BOS tokens to add
  int32_t num_eos = 0;             // Number of EOS tokens to add

  // Helper method to resolve the actual max_new_tokens based on constraints
  int32_t resolve_max_new_tokens(int32_t max_context_len, int32_t num_prompt_tokens) const;
};

resolve_max_new_tokens 方法处理基于以下内容确定可以生成多少 token 的逻辑:

  • 模型的最大上下文长度

  • prompt 中的 token 数量

  • 用户指定的 maximum sequence length 和 maximum new tokens

实现组件

runner 库由几个专门的组件组成,它们协同工作。

TextLLMRunner

IRunner 接口的主要实现,负责协调文本生成过程。它管理:

  1. 输入文本的分词

  2. 使用 prompt token 预填充 KV 缓存

  3. 逐个生成新 token

  4. 收集性能统计数据

TextPrefiller

负责处理初始 prompt token 并填充 KV 缓存。主要功能:

  • 高效处理大型 prompt

  • 处理动态序列长度

  • 支持并行预填充以优化性能

TextTokenGenerator

以自回归方式逐个生成新 token。它:

  • 管理 token 生成循环

  • 应用基于温度的采样

  • 检测序列结束条件

  • 在生成时流式传输 token

TextDecoderRunner

与 ExecuTorch Module 接口以运行模型的前向传播。它:

  • 管理模型的输入和输出

  • 处理 KV 缓存更新

  • 通过采样将 logits 转换为 token

Tokenizer 支持

runner 库通过统一的接口支持多种分词器格式。

std::unique_ptr<tokenizers::Tokenizer> tokenizer = load_tokenizer(
    tokenizer_path,  // Path to tokenizer file
    nullptr,         // Optional special tokens
    std::nullopt,    // Optional regex pattern (for TikToken)
    0,               // BOS token index
    0                // EOS token index
);

支持的分词器格式包括:

  1. HuggingFace Tokenizers:JSON 格式分词器

  2. SentencePiece.model 格式分词器

  3. TikToken:BPE 分词器

  4. Llama2c:Llama2.c 格式的 BPE 分词器

对于自定义分词器,您可以在 pytorch-labs/tokenizers 存储库中找到实现。

其他 API

模型预热

为了获得更准确的计时和最佳性能,您应该在实际推理之前执行预热运行。

runner->warmup("Hello world", 10);  // Generate 10 tokens as warmup

预热期间:

  1. 创建一个具有以下属性的特殊 GenerationConfig

    • echo = false:prompt 不包含在输出中

    • warming = true:表示这是预热运行

    • max_new_tokens:设置为要生成的 token 数量

  2. 模型将运行整个生成流水线。

    • 加载模型(如果尚未加载)

    • 对 prompt 进行分词

    • 预填充 KV 缓存

    • 生成指定的 token 数量

  3. 预热期间的特殊行为:

    • token 不会显示到控制台。

    • runner 会记录“正在进行预热运行…”和“预热运行完成!”的消息。

  4. 预热后:

    • Stats 对象会被重置,以清除性能指标。

    • 模型将保持加载状态,并准备好进行实际推理。

预热对于准确的基准测试尤为重要,因为第一次推理通常包含一次性初始化成本,这会扭曲性能测量。

内存使用情况监控

您可以使用 Stats 对象监控内存使用情况。

std::cout << "RSS after loading: " << get_rss_bytes() / 1024.0 / 1024.0 << " MiB" << std::endl;

文档

访问全面的 PyTorch 开发者文档

查看文档

教程

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

查看教程

资源

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

查看资源