核心架构

llama.cpp 仓库实现了一个用于 LLaMA 模型的 C++ 推理框架。其核心架构围绕 ggml 张量库进行数值计算,以及一组管理模型、上下文和推理过程的 C++ 类和函数。

关键组件

llama.cpp 架构的主要组件包括:

  1. ggml 张量库:所有数值运算的基础。
  2. 模型加载与管理 (llama_model):处理加载模型权重和参数。
  3. 上下文管理 (llama_context):管理推理状态,包括 KV 缓存和计算图。
  4. 推理执行:执行实际的令牌生成。

1. ggml 张量库

目的与功能:

ggml 是一个专为机器学习推理设计的张量库。它为创建和操作多维数组(张量)以及定义计算图提供了基础构建模块。

ggml 的主要特性包括:

  • 张量运算:一套全面的运算,如矩阵乘法、加法、激活函数等。(来源:ggml.hL8)
  • 计算图:允许将复杂计算定义为一个操作图。然后可以高效地执行此图。(来源:ggml.hL20)
  • 内存管理ggml 使用上下文 (ggml_context) 来管理张量的内存。张量通常在预定义的内存缓冲区内分配。(来源:ggml.hL30-L38)
  • 量化支持ggml 支持各种量化方案(例如 Q4_0、Q8_0)以减小模型大小并可能加速推理,相关函数通常位于 ggml-quants.h 中并在 ggml.c 内使用。(来源:ggml.cL11 引用 ggml-quants.h)
  • 自动微分:虽然在 llama.cpp 中主要用于推理,但 ggml 也具备自动微分的能力。(来源:ggml.hL10)
  • 后端系统 (ggml_backend)ggml 具有一个后端系统,允许将操作卸载到不同的硬件加速器,如 GPU(CUDA、Metal、OpenCL、SYCL、Vulkan)或专用硬件。(来源:ggml.cL7ggml-backend.h) 在架构中的角色: ggml 位于技术栈的最底层,提供核心计算能力。llama.cpp 中的所有更高级别的组件(模型加载、上下文管理、推理)都依赖 ggml 进行张量运算和图执行。**

在架构中的角色: llama_model 组件是与特定 LLaMA 模型交互的入口点。它提供原始权重和参数,然后由 llama_context 用于执行推理。

2. 模型加载与管理 (llama_model)

目的与功能:

llama_model 结构(及相关函数)负责从 GGUF (GGML Universal File Format) 文件加载 LLaMA 模型权重和参数。

关键方面包括:

  • 文件解析:读取 GGUF 文件格式,其中包含模型元数据、词汇表和张量数据。这主要由 llama_model_loader.cpp 处理。(通过 llama_load_model_from_file 间接被 llama.cpp 使用)
  • 超参数加载:提取模型超参数,如词汇表大小、嵌入维度、层数等。(来源:llama.cppL100 - model.load_hparams(ml))
  • 词汇表加载:加载分词器词汇表。(来源:llama.cppL105 - model.load_vocab(ml))
  • 张量加载:将模型权重(张量)加载到内存中。这通常涉及内存映射(mmap)以提高效率。(来源:llama.cppL115 - model.load_tensors(ml))
  • 设备映射:根据 split_mode 和 main_gpu 参数,将张量层分布到可用的计算设备(CPU、GPU)上。(来源:llama.cppL157-L180) 在架构中的角色: llama_model 组件是与特定 LLaMA 模型交互的入口点。它提供原始权重和参数,然后由 llama_context 用于执行推理。

3. 上下文管理 (llama_context)

目的与功能:

llama_context 结构(及相关函数)封装了推理过程的状态。这包括 KV (Key-Value) 缓存、模型的计算图以及内存缓冲区。

关键方面包括:

  • KV 缓存管理:存储先前令牌的注意力机制的键(Key)和值(Value),以加速后续令牌的生成。这对于高效的 Transformer 推理至关重要。(由 llama_context 内部管理,KV 缓存的张量是上下文 ggml_context_ 的一部分。)
  • 计算图构建:构建表示 LLaMA 模型前向传播的 ggml 计算图。(在 llama_new_context_with_model 及后续评估期间完成)。
  • 内存分配:使用 ggml 为 KV 缓存、计算缓冲区和其他运行时数据分配内存。
  • 批处理:通过 llama_batch 处理用于推理的序列批次。(来源:llama.h 中 llama_batch 的定义,在 llama_decode 中使用)。
  • 令牌评估:处理输入令牌并为下一个令牌生成 logits。

代码示例(创建上下文并执行推理 - 简化版):

在架构中的角色: llama_context 是推理的主力。它接收加载的 llama_model 并管理生成令牌所需的动态状态。单个模型可以存在多个上下文,从而允许并行推理会话(尽管线程安全需要用户应用程序仔细管理)。

4. 推理执行 (llama_decodellama_sample_token 等)

目的与功能:

这指的是驱动实际令牌生成过程的一组函数。

典型推理循环中的关键步骤:

  1. 分词 (Tokenization):用户输入文本使用模型的词汇表(例如 llama_tokenize)转换为令牌序列。
  2. 批次准备 (Batch Preparation):将令牌准备到 llama_batch 结构中,指定它们的位置、序列 ID 以及是否需要 logits。
  3. 解码 (llama_decode)llama_context 处理输入 llama_batch 的核心步骤。这包括:
    • 构建或更新 ggml 计算图。
    • 使用 ggml_graph_compute 或后端特定函数执行该图。
    • 更新 KV 缓存。
    • 为批处理中 logits[i] 为 true 的位置生成 logits。
  4. 采样 (llama_sample_tokenllama_sample_token_greedy 等):根据 llama_decode 生成的 logits 选择下一个令牌。可以采用各种采样策略(贪婪、top-k、top-p、温度)。
  5. 反分词 (Detokenization):将采样的令牌 ID 转换回文本(例如 llama_token_to_piece)。
  6. 循环 (Loop):将新采样的令牌添加到序列中,重复此过程。

图表:推理循环

mermaid

在架构中的角色: 这些函数协调 llama_modelllama_context 和 ggml 之间的交互,以执行端到端的文本生成过程。

整体数据流和交互

mermaid

图表说明:

  1. GGUF 模型文件由 llama_load_model_from_file 加载以创建 llama_model 实例。 (来源:llama.cppL131)
  2. llama_model 用于初始化一个或多个 llama_context 实例。 (来源:llama.h - llama_new_context_with_model 签名)
  3. 输入文本被分词。
  4. 推理循环:
    • 令牌被准备到 llama_batch 中。
    • llama_decode 使用 llama_context 处理批次。这涉及 ggml 进行图执行,并更新上下文中的 KV 缓存。 (来源:llama.h - llama_decode 签名)
    • 从上下文中获取 Logits。
    • 采样函数选择下一个令牌。
    • 令牌被转换为文本,并成为下一次迭代输入的一部分。
  5. 循环继续,直到满足停止条件。

设置和使用

  1. 构建 llama.cpp:遵循仓库主 README.md 中的构建说明。这通常涉及 CMake。
  2. 获取模型文件:下载 GGUF 格式的 LLaMA 模型权重。
  3. 与您的应用程序集成
    • 在您的 C/C++ 项目中包含 llama.h
    • 链接已编译的 llama.cpp 库。
    • 如上述示例和仓库的 /examples 目录中所示,使用 API 函数。

基本使用的关键 API 函数:

  • llama_backend_init() / llama_backend_free():初始化/反初始化 llama.cpp 后端(包括 ggml)。 (来源:llama.cppL50llama.cppL66)
  • llama_model_default_params() / llama_context_default_params():获取默认参数。
  • llama_load_model_from_file():加载模型。
  • llama_new_context_with_model():创建推理上下文。
  • llama_tokenize():将文本转换为令牌。
  • llama_batch_init() / llama_batch_free():管理令牌批次。
  • llama_decode():处理令牌并生成 logits。
  • llama_get_logits_ith():访问 logits。
  • 采样函数(例如 llama_sample_token_greedyllama_sample_repetition_penalties 等)。
  • llama_token_to_piece():将令牌 ID 转换为文本。
  • llama_free() / llama_free_model():释放资源。

核心架构设计得既高效又灵活,允许从简单的命令行推断到更复杂的服务器应用程序等各种用例。ggml 的使用为数值计算提供了坚实的基础,而 llama.cpp 层则为与 LLaMA 模型交互提供了更高级别的 API。