llama.cpp 新模型架构接入实战:从 GGUF 转换脚本到 GGML 推理图的完整四步流程
llama.cpp 新模型架构接入实战从 GGUF 转换脚本到 GGML 推理图的完整四步流程【免费下载链接】llama.cppLLM inference in C/C项目地址: https://gitcode.com/GitHub_Trending/ll/llama.cpp本文基于 llama.cpp 仓库自带的 HOWTO-add-model.md 编写完整覆盖向 llama.cpp 添加一个新模型架构的四个步骤用 Python 转换脚本生成 GGUF、在 C 侧注册架构、构建 GGML 推理图以及可选的多模态编码器接入并补充了文档中的高级技巧转换期张量折叠、ggml_rope_ext用法、部分旋转头等。读完后你可以独立提交一个能被cli、server、quantize等工具链正常加载并推理的新模型支持。整体流程与验证清单按官方文档接入一个新模型架构分四步Convert the model to GGUF—— 用 Python 转换脚本把 Hugging Face 模型转成 GGUFDefine the model architecture inllama.cpp—— 在 C 侧注册架构枚举、张量布局与元数据Build the GGML graph implementation—— 实现该架构的推理图构建逻辑Optional: Add multimodal encoder implementation—— 若模型支持多模态输入在libmtmd中新增编码器。完成以上步骤后即可提交 PR。同时文档强调必须验证各示例程序与主流 GGML 后端CUDA、Metal、CPU在新架构上均能正常工作重点检查以下工具均位于 tools/ 下clicompletionimatrixquantizeserver第一步把模型转换成 GGUF这一步在 Python 侧完成使用 gguf 库即仓库内置的 gguf-py 包编写转换脚本。根据架构不同可以选择convert_hf_to_gguf.py针对 Hugging Face 格式的模型绝大多数新模型走这条路径examples/convert_legacy_llama.py针对llama/llama2的.pth旧格式权重。转换脚本的职责是读取模型的 configuration、tokenizer、张量名与张量数据输出为 GGUF 元数据与 GGUF 张量。对 HF 模型而言需要落实的具体实现有四类。1.1 注册转换类ModelBase.register在 conversion/ 目录下新建一个继承TextModel纯文本模型或MmprojModel多模态投影模型的子类并用装饰器注册。以TextModel为例ModelBase.register(MyModelForCausalLM) ModelBase.example(user/model) class MyModel(TextModel): model_arch gguf.MODEL_ARCH.MYMODEL多模态模型则继承MmprojModelModelBase.register(MyModelForConditionalGeneration) ModelBase.example(user/model) class MyModel(MmprojModel): model_arch gguf.MODEL_ARCH.MYMODEL这些基类都定义在 conversion/base.py 中ModelBase从 第 80 行 开始提供register类方法第 1146 行把 Hugging Faceconfig.json里的architectures名字映射到转换类TextModel第 1179 行与MmprojModel第 2390 行分别封装文本生成模型与视觉投影模型的公共转换逻辑。conversion/ 目录中已有falcon.py、qwen.py、deepseek.py等上百个现成的转换实现可供参照。关于example参数它应指向一个用于测试的、真实有效的 Hugging Face 模型必要时可以登记多个优先选择非 gated 的模型若没有合适的就选一个极小的随机权重模型。1.2 在constants.py中定义 GGUF 张量布局在 gguf-py/gguf/constants.py 中添加三样东西MODEL_ARCH枚举项架构字符串MODEL_ARCH_NAMES中的人类可读架构名MODEL_TENSORS中该架构的张量清单。以falcon架构为例第 2373 行MODEL_ARCH.FALCON: [ MODEL_TENSOR.TOKEN_EMBD, MODEL_TENSOR.OUTPUT_NORM, MODEL_TENSOR.OUTPUT, MODEL_TENSOR.ATTN_NORM, MODEL_TENSOR.ATTN_NORM_2, MODEL_TENSOR.ATTN_QKV, MODEL_TENSOR.ATTN_OUT, MODEL_TENSOR.FFN_DOWN, MODEL_TENSOR.FFN_UP, ]这里列出的张量顺序定义了该架构在 GGUF 容器内的标准布局C 侧的模型加载与图构建代码就是依据这套命名来取张量的。命名要一次想清楚。文档特别提醒GGUF 架构字符串以及与之对应的src/models/name.cpp文件名见第三步要一开始就按照现有命名惯例确定。一旦社区发布了某个架构字符串的 GGUF 文件后续改名会破坏所有已发布文件这不是能留到跟进 PR 里清理的事情。1.3 张量名映射tensor_mapping.py把原模型PyTorch 权重里的张量名映射到 GGUF 标准名写入 gguf-py/gguf/tensor_mapping.py。通用规则是给 GGUF 新增张量名之前先确认等价命名是否已存在。若张量名属于重复的层/块结构用{bid}占位符代替层号。以注意力层的归一化张量为例block_mappings_cfg: dict[MODEL_TENSOR, tuple[str, ...]] { # Attention norm MODEL_TENSOR.ATTN_NORM: ( gpt_neox.layers.{bid}.input_layernorm, # gptneox transformer.h.{bid}.ln_1, # gpt2 gpt-j refact qwen transformer.blocks.{bid}.norm_1, # mpt ... ) }其中transformer.blocks.{bid}.norm_1会被映射为 GGUF 中的blk.{bid}.attn_norm。1.4 视情况覆写转换钩子根据模型的具体 configuration、tokenizer、代码和张量布局你可能需要覆写以下方法TextModel#set_gguf_parametersMmprojModel#set_gguf_parametersModelBase#set_vocabModelBase#modify_tensors最后一个modify_tensors会在后文“转换期张量修改”技巧中再次出现。文档还规定了一条硬约定张量名必须以.weight或.bias结尾。这是全仓库的命名惯例quantize等工具依赖该约定来处理权重。第二步在 llama.cpp 中定义模型架构张量与参数的布局必须在 C 源码中定义具体分五处新增架构枚举在 src/llama-arch.h 的enum llm_arch中加入新值。该枚举当前已包含LLM_ARCH_LLAMA、LLM_ARCH_FALCON、LLM_ARCH_GEMMA4等上百个架构新架构名需与第一步选定的 GGUF 架构字符串一致。注册架构名与元数据在 src/llama-arch.cpp 的LLM_ARCH_NAMES映射表中加入架构名如{ LLM_ARCH_FALCON, falcon }按需同步更新LLM_KV_NAMES、LLM_TENSOR_NAMES与LLM_TENSOR_INFOS。非标元数据加载如果模型有需要特殊解析的 GGUF 元数据在 src/llama-model-loader.cpp 的llama_model_loader构造逻辑中补充。RoPE 类型若模型使用 RoPE 位置编码在 src/llama-model.cpp 的llama_model_rope_type函数中为新架构添加 case。排查所有对llm_arch的分支遍历检查其他按llm_arch做 switch/遍历的位置例如 src/llama-model-saver.cpp 以及各类“必填 hparam 清单”如哪些架构需要 MoE 元数据。建议直接 grepLLM_ARCH_的使用点逐一核对——漏掉其中一处是新增架构后 CI 测试如test-llama-archs失败的常见原因。对应的测试文件为 tests/test-llama-archs.cpp。文档还给出一个容易被忽视的维度约定NOTE: ggml 中的维度顺序通常是 PyTorch 维度的逆序。也就是说 PyTorch 中 shape 为[vocab, dim]的权重在 ggml 中对应的是转置后的布局写图代码时要按 ggml 约定取值。第三步构建 GGML 推理图文档称这一步“the funniest part”——你需要为新架构提供推理图的实现代码位于src/llama-model.cpp及其模型实现目录 src/models/步骤如下新建结构体继承自llama_model_base实现build_arch_graph方法这是 src/llama-model.h 中声明的纯虚函数virtual std::unique_ptrllm_graph_context build_arch_graph(const llm_graph_params params) const 0;在其中用 ggml 算子搭建该架构的前向计算图参考现有实现build_arch_graph应返回一个构建好的图llm_graph_context可参照llama_model_llama、llama_model_dbrx、llama_model_bert等实现。从源码结构看当前仓库中 src/models/ 下已有 150 余个llama_model_arch.cpp文件如falcon.cpp、dbrx.cpp、bert.cpp每个都实现了自己的build_arch_graph注册到映射函数在 llama_model_mapping 中为你的架构添加 case返回新建的结构体实例。该函数是一个按llm_arch分支new出对应模型对象的大 switch是架构枚举与实现类之间的接线点。两点补充说明部分 ggml 后端并不支持所有算子若新架构用到的算子在某后端缺失可以在后续单独的 PR 中补齐后端实现调试推理图时可以使用 examples/eval-callback/ 提供的回调示例观察每一步图执行结果。第四步可选多模态编码器实现如果新模型支持多模态输入需要在libmtmd中新增编码器定义。llama.cpp 的多模态支持详见 docs/multimodal.md 与 tools/mtmd/ 源码目录。具体要做四件事转换脚本侧确保添加了继承MmprojModel或继承自同一基类的其他类的子类编码器定义在clip.cpp中添加对应 src/models/clip.cpp 所在体系下的编码器描述预处理器在mtmd.cpp中实现。多数情况下可以直接复用现有 preprocessor编码器 GGML 图若模型与现有实现差异很大写在独立文件中否则复用现有实现例如 siglip、pixtral、qwen 的编码器并附加一个模型特定的 projector。文档对libmtmd的设计约束也值得注意很多多模态编码器基于的视觉模型本身已被支持添加新编码器前务必先读tools/mtmd/models下的现有编码器定义在libmtmd中扩展已有模型通常优于复制代码调试多模态预处理器与编码器可用tools/mtmd/debug/mtmd-debug.cpp在libmtmd中添加模型特定的 API 或 CLI 是反模式——该库的目标是提供易用且与模型无关的多模态流水线绝大多数情况下不需要改动llama-mtmd-cli若模型需要特定 prompt要么让用户自行提供要么写进 Jinja chat 模板音频生成类模型参见tools/mtmd/README-dev.md。实战技巧Tips and Tricks原文档专门沉淀了若干“从踩坑里总结出来”的技巧是整份 HOWTO 中信息密度最高的部分。优先在转换期做张量修改而不是在推理图里做如果模型在计算图中对张量有常量级的修改例如norm(1 weight)这类归一化或者涉及张量转置/切块permutations/chunking把这些修改放到转换脚本里做而不是在图代码里做。这样推理图更简单也省掉了运行时多余的算子。文档给出的两个实例Gemma 3在转换时就把norm(1 weight)中的1 折叠进权重图里只需做普通的 RMS normQwen3-Next在转换时modify_tensors完成张量转置图代码直接消费已转置好的权重。但存在一个例外weight * scale这类常数缩放的乘法通常不应折叠进权重而应保留到推理时应用。原因是 scale 在概念上作用在激活值上而非权重上把它乘进权重会损害数值稳定性且会平移权重的数值范围、可能导致量化效果变差。正确做法是把 scale 作为独立的 GGUF 元数据键写入例如%s.attention.output_scale、%s.attention.value_scale、%s.embedding_scale然后在图中应用。与ggml_rope_ext合作PyTorch 实现通常显式计算freq_cis/sin/cos分量而在 llama.cpp 中大多数 RoPE 操作都可以交给ggml_rope_ext处理——它不需要 sin/cos 矩阵既省内存又能让 GGML 的 RoPE kernel 与其他算子做融合。ggml_rope_ext的详细说明见 ggml/include/ggml.h 中的代码内注释。不过ggml_rope_ext只提供模型所用 RoPE 实现的一个子集从 PyTorch 移植到 llama.cpp 时往往需要一些“创造性适配”。文档列举的典型场景与解法2D RoPE视觉模型libmtmd用GGML_ROPE_TYPE_NORMAL的排布实现 2D RoPE——把输入张量切成两半分别对两半调用ggml_rope_ext再用ggml_concat拼回交错的视觉 RoPE 频率Kimi-K2.5 视觉编码器权重必须在转换期转置才能复用build_rope_2d()函数“按比例” RoPEGemma 4技巧是把rope_freqs的最后几个维度设成一个极大值使这些维度不被旋转实现见 convert_hf_to_gguf.py 中的Gemma4Model类位置缩放某些模型要求输入位置[0, 1, 2, ...]变成[0, 0.5, 1, ...]此时传入freq_scale 0.5f即可学习式 RoPE 频率某些模型不用powf(freq_base, -2.0 * i / n_dims)而使用学习得到的频率。此时通过rope_freqs张量对应ggml_rope_ext的c参数提供学习频率并设freq_base 1.0f。特别注意GGML 中rope_freqs存的是倒数theta pos[i] / rope_freqs转换时可能需要把频率取倒数。只旋转头的一部分nope 部分许多模型只旋转每个 head 的一部分其余维度保持原样常称 “nope” 部分。不要用视图views加ggml_concat来实现——效率低。两种布局都可以用单个 RoPE 算子完成[rope|nope]旋转维度在前给ggml_rope_ext传入小于 head 大小的n_dims从n_dims到末尾的维度会被原样拷贝[nope|rope]旋转维度在后对 RoPE 结果调用ggml_rope_set_offset(cur, n_offs)其中n_offs是前导未旋转部分的尺寸[n_offs, n_offs n_dims)之外的维度原样拷贝。约束条件n_offs必须为偶数n_offs n_dims必须能放入行内且不支持视觉 RoPE另外频率是相对旋转窗口计算的。文档给出的例子DeepSeek-V4 的 query、key 与压缩 KV 张量都用[nope|rope]布局因此 src/models/deepseek4.cpp 先对整个张量做 RoPE再调用ggml_rope_set_offset(cur, n_embd_head_nope)。例外有些模型会对 nope 部分施加额外算子例如 src/models/deepseek32.cpp这种额外算子无法像 RoPE 那样选择性作用所以这类模型仍然需要视图加ggml_concat的做法。规范与参考资料GGUF 格式规范转换脚本与张量布局的最终依据是 GGUF 规范仓库内 gguf-py/ 即该格式的 Python 实现ggml/src/gguf.cpp 与 ggml/include/gguf.h 为 C/C 侧实现新增架构字符串与张量名时应先查阅现有定义保持风格一致。参考实现历史 PR 脉络文档“Resources”一节罗列了多个可作为范本的历史 PR涵盖 YaRN RoPE scaling、Baichuan 串行模型、attention bias、Mixtral、BERT embeddings、Grok-1、Command R Plus、DBRX 架构支持等。这些 PR 展示了各自时期“转换脚本 架构注册 图实现”三段式接法的完整形态是新增相似架构时的最佳对照物另有一篇“如何把 HuggingFace 模型转换为 GGUF”的讨论帖可作入门补充。相关文档多模态支持见 docs/multimodal.md各后端的算子覆盖情况见 docs/ops.md各后端目录下有对应的*.csv算子清单可用于提前判断新架构所需算子在目标后端是否齐备。小结一张检查清单把上面的流程压缩成提交 PR 前的自查清单检查项位置转换类已注册ModelBase.registerexampleconversion/MODEL_ARCH/MODEL_ARCH_NAMES/MODEL_TENSORS已定义gguf-py/gguf/constants.py张量名映射已加入含{bid}处理gguf-py/gguf/tensor_mapping.py张量名以.weight/.bias结尾转换脚本输出enum llm_arch新值 LLM_ARCH_NAMES等映射src/llama-arch.h、src/llama-arch.cpp特殊元数据解析 / RoPE 类型 casesrc/llama-model-loader.cpp、src/llama-model.cpp已 grep 全部LLM_ARCH_使用点src/含 src/llama-model-saver.cpp 等新结构体继承llama_model_base并实现build_arch_graph已注册进llama_model_mappingsrc/models/、src/llama-model.cpp多模态模型编码器定义、preprocessor、projector 齐备tools/mtmd/cli/completion/imatrix/quantize/server与 CPU/CUDA/Metal 后端验证通过tools/按此清单逐项落实新架构的支持就能从“能加载”走到“能推理、能量化、能上服务端”并顺利通过 CI 中的架构一致性测试。【免费下载链接】llama.cppLLM inference in C/C项目地址: https://gitcode.com/GitHub_Trending/ll/llama.cpp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考