llama.cpp 使用 GGUF 模型进行推理流程。code aspect
1. 模型加载
加载 GGUF 文件,解析权重和配置
将权重加载到内存(支持内存映射 mmap 以减少内存占用)。
初始化上下文(llama_context),包括 KV 缓存(用于加速多轮对话)和计算图。
llama.cpp 文件中:
llama_model *model = llama_model_load_from_file(model_path, params);
llama_context * lctx = llama_init_from_model(model, cparams);
2. 预处理
将输入文本(prompt)通过分词器(tokenizer)转换为 token ID 序列。分词器信息通常存储在 GGUF 文件中,llama.cpp 使用它将文本编码为输入向量。
llama.cpp 中
std::vector<llama_token> tokens = llama_tokenize(model, prompt, true);
3. 推理 pipline
llama.cpp 构建了一个推理 pipeline,基于 GGUF 文件中的配置,首先执行 Transformer 的前向传播。包括:
嵌入层:将 token ID 转换为嵌入向量。
Transformer 层:循环执行多层 Transformer 模块,包括:
- embedding ?
- Multi-Head Attention :计算查询、键、值并应用注意力机制。
- 层归一化(Layer Normalization):归一化中间结果。
- 前馈网络(Feed-Forward Network):应用全连接层。
- 残差连接(Residual Connections):确保信息流动。
输出层:将最后一层的输出转换为 logits(对下一个 token 的概率分布)。
在构建Graph时,就已经添加了 kv-cache 图节点,存储注意力机制的中间结果,加速多轮推理。
然后通过采样chain(如贪婪采样、Top-K、Top-P)从 logits 中选择下一个 token。
最后将生成的 token 序列通过分词器解码为文本。
比如当我执行 llama-cli 时, 就会体现上述 pipline 。
上述推理 pipline 的组件
模型加载器:解析 GGUF 文件,提取权重和配置。
分词器:将输入文本转换为 token,并将输出 token 转换回文本。
计算图:基于 GGML 库,构建 Transformer 的计算图(包括注意力、前馈网络等)。
硬件加速模块:根据硬件(CPU、GPU、ARM)选择优化的计算内核(如 SIMD、CUDA、Metal)。
KV 缓存管理:存储注意力机制的键值对,加速多轮推理。
采样模块:从 logits 中选择下一个 token,支持多种采样策略。
工具接口:通过 llama-cli 或 llama-server 提供用户交互(命令行或 HTTP API)。
构建 pipline 过程
加载配置:从 GGUF 文件读取模型参数(层数、注意力头数、隐藏维度等)。
初始化计算图:根据配置,分配内存并构建 Transformer 的计算流程(通过 GGML 的操作符,如矩阵乘法、加法等)。
硬件适配:根据运行时环境(CPU/GPU),选择合适的计算内核。
推理循环:按 token 逐一执行前向传播、采样和输出。
Entrys
llama.cpp 项目通过其提供的各种工具( llama-cli、llama-server、llama-quantize 等)作为 entry point 。entry 通过调用核心的推理逻辑和相关组件来实现功能。
其中最小的 entry 就是 llama-simple & llama-chat。阅读这两个应用。
KAQ:构建 Transformer 的计算图,是如何体现的
这里所谓的动态构建其实是,llama.cpp 事先在 code 中编码了常见的几乎所有开源的模型架构。详见 llama-model.cpp 文件。共有 74 个 LLMs 结构,Grok…
KAQ:实际计算中如何选择计算 kernels
KAQ:KV 缓存管理,是如何体现的
详见后续