Transformers 中加载 GGUF 模型的原理与实践:gguf_file 参数、反量化机制与架构适配全解
GGUF 是 GGML 生态的单一文件模型格式,Transformers 允许通过 gguf_file 参数直接把 GGUF checkpoint(含 Q4_0、Q6_K 等量化权重)加载进 PyTorch 模型用于训练与微调。读完本文,你将掌握 GGUF 模型在 Transformers 中的完整加载链路:从 gguf_file 参数解析、GGUF 元数据到 HuggingFace config/tokenizer 的映射,到量化权重的反量化(dequantize to fp32)与架构级权重重排,并能理解官方测试如何验证不同量化类型的加载正确性。
一、GGUF 是什么,为什么 Transformers 要支持它
GGUF 是用于存储 GGML 推理模型的文件格式。GGML 是一个用 C/C++ 编写的快速轻量级推理框架,而 GGUF 是其 checkpoint 格式:一个单文件,同时包含模型元数据(架构参数、tokenizer 词表等)和全部张量权重。
GGUF 格式支持大量量化数据类型(完整量化类型列表见 GGUF 官方文档),显著节省内存,使得 Whisper、Llama 这类大模型可以在本地与边缘设备上完成推理。这也是社区在 Hub 上大量发布 *-Q4_K_M.gguf、*-Q6_K.gguf 文件的原因。
Transformers 对 GGUF 的定位是**"量化 checkpoint 的回载入口":把 GGUF 中量化存储的权重反量化为 fp32**,得到一个权重完整、与 PyTorch 兼容的模型,用于后续训练或微调。也就是说,GGUF 在这里不是推理加速格式,而是"以低精度文件分发、以全精度加载使用"的桥梁。
提示:支持 GGUF 加载的模型包括 Llama、Mistral、Qwen2、Qwen2Moe、Phi3、Bloom、Falcon、StableLM、GPT2、Starcoder2 等,完整列表可以从源码 GGUF 映射表 的键名得到。
二、基本用法:gguf_file 参数
给 [~PreTrainedModel.from_pretrained] 和 [~PreTrainedTokenizer.from_pretrained] 传入 gguf_file 参数,指定要加载的 GGUF 文件即可。注意需要先安装 gguf 包:
# pip install gguf
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM
model_id = "TheBloke/TinyLlama-1.1B-Chat-v1.0-GGUF"
filename = "tinyllama-1.1b-chat-v1.0.Q6_K.gguf"
dtype = torch.float32 # could be torch.float16 or torch.bfloat16 too
tokenizer = AutoTokenizer.from_pretrained(model_id, gguf_file=filename)
model = AutoModelForCausalLM.from_pretrained(model_id, gguf_file=filename, dtype=dtype)
几个要点:
gguf_file既可以是本地文件路径,也可以是 Hub 上的相对文件名。model_id提供仓库定位,gguf_file指定仓库中具体文件。dtype参数指定反量化后张量的目标类型,torch.float32/torch.float16/torch.bfloat16均可。若不显式指定,从源码 modeling_utils.py 看,当 checkpoint 文件以.gguf结尾时默认按torch.float32处理。- 加载完成后,得到的就是普通的 PyTorch 模型与 fast tokenizer,可正常训练、
generate与保存。
模型侧的加载链路
from_pretrained 内部对 gguf_file 的处理在 modeling_utils.py 中:
- 若
gguf_file是本地存在的文件,直接作为 resolved archive 使用; - 否则通过
cached_file从 Hub 下载并缓存该文件; - 权重解析阶段(modeling_utils.py)调用
load_gguf_checkpoint完成元数据解析与张量反量化,并把结果按tensors/config/tokenizer/tokenizer_config四组返回。
load_gguf_checkpoint 的签名为:
def load_gguf_checkpoint(gguf_checkpoint_path, return_tensors=False, model_to_load=None, torch_dtype=None):
其中 return_tensors=True 才会真正读取并反量化张量(只读元数据则更快);model_to_load 用于建立 GGUF 张量名到 Transformers 参数名的映射;torch_dtype 表示"反量化后立即转换到该 dtype 以节省内存"——这与 from_pretrained 的 dtype 参数直接衔接。
Tokenizer 侧的加载链路
tokenizer 的 gguf_file 处理有两条入口:
- tokenization_utils_base.py 中,
from_pretrained会把gguf_file直接注册为vocab_file,使原本需要多个词表文件的 tokenizer 也能从一个 GGUF 文件构造; - tokenization_utils_tokenizers.py 中,fast tokenizer 的构造流程是:
cached_file定位 GGUF 文件 →load_gguf_checkpoint取出config["model_type"]与tokenizer字典 → 调用convert_gguf_tokenizer重建 backend tokenizer。
三、GGUF 元数据如何变成 HuggingFace config 与 tokenizer
GGUF 文件的元数据以 general.* 与各架构前缀(如 llama.*、qwen2.*)的键值组织,且命名风格与 HuggingFace config 完全不同(例如 block_count vs num_hidden_layers)。Transformers 用三张映射表完成翻译,全部定义在 integrations/ggml.py 与 modeling_gguf_pytorch_utils.py:
GGUF_TO_TRANSFORMERS_MAPPING = {
"ignore": {
"GGUF": {"version": "version", "tensor_count": "tensor_count", "kv_count": "kv_count"},
"general": {"file_type": "file_type", "quantization_version": "quantization_version"},
},
"config": GGUF_CONFIG_MAPPING,
"tokenizer": {"tokenizer": GGUF_TOKENIZER_MAPPING["tokenizer"]},
"tokenizer_config": {"tokenizer": GGUF_TOKENIZER_MAPPING["tokenizer_config"]},
}
架构白名单
GGUF_SUPPORTED_ARCHITECTURES 直接取 config 映射表的键名,即当前支持加载的 GGUF 架构。从 GGUF_CONFIG_MAPPING 可以看到,涵盖:
general、llama、mistral、qwen2、qwen2_moe、gpt_oss、lfm2、qwen3、qwen3_moe、falcon、phi3、bloom、t5、stablelm、gpt2、starcoder2、mamba、nemotron、gemma2、gemma3、gemma4、umt5、deci、minimax_m2。
映射规则以 Llama 为例,展示了 GGUF 键到 HF config 字段的对照:
| GGUF 键 | HF config 字段 |
|---|---|
context_length |
max_position_embeddings |
block_count |
num_hidden_layers |
feed_forward_length |
intermediate_size |
embedding_length |
hidden_size |
rope.dimension_count |
head_dim |
rope.freq_base |
rope_theta |
attention.head_count |
num_attention_heads |
attention.head_count_kv |
num_key_value_heads |
attention.layer_norm_rms_epsilon |
rms_norm_eps |
vocab_size |
vocab_size |
各架构还有各自的特例,例如 mamba 映射了 ssm.conv_kernel、ssm.state_size 等 SSM 字段,gemma4 则把 attention.key_length 映射为 global_head_dim 以表达混合注意力。
架构识别的补丁逻辑
GGUF 中的 general.architecture 字段有时与 HuggingFace 的 model_type 不一致,load_gguf_checkpoint 中有一系列修正规则,可以从中读出当前实现支持的架构细节:
- llama.cpp 中 Mistral 与 Llama 共用同一 architecture,因此当架构为
llama且模型名含mistral时改写为mistral; - T5/UMT5 架构会补
is_gated_act=True,并按t5encoder前缀区分T5EncoderModel/UMT5EncoderModel; - Gemma3/Gemma4 的 GGUF 只包含文本主干权重,因此
model_type被改写为gemma3_text/gemma4_text(Gemma4 还会补全三个 EOG token 的eos_token_id); - StableLM 通过检查张量列表推断配置:存在
attn_q.bias等张量则use_qkv_bias=True,存在ffn_norm张量则判断use_parallel_residual; tie_word_embeddings通过张量列表推断:不存在output.weight张量则视为True(Falcon、Bloom 例外)。
此外,若 config 中缺少 vocab_size,实现会回退到从 tokenizer 的 tokens 列表取长度(源码)。
Tokenizer 的恢复
GGUF_TOKENIZER_MAPPING 定义了 GGUF 中 tokenizer 相关字段到 HF 字段的映射:
GGUF_TOKENIZER_MAPPING = {
"tokenizer": {
"ggml.model": "tokenizer_type",
"ggml.tokens": "tokens",
"ggml.scores": "scores",
"ggml.token_type": "token_type",
"ggml.merges": "merges",
"ggml.bos_token_id": "bos_token_id",
"ggml.eos_token_id": "eos_token_id",
"ggml.unknown_token_id": "unk_token_id",
"ggml.padding_token_id": "pad_token_id",
"ggml.add_space_prefix": "add_prefix_space",
},
"tokenizer_config": {
"chat_template": "chat_template",
"ggml.model": "model_type",
...
},
}
拿到这些原始字段后,convert_gguf_tokenizer 按架构选择对应的转换器(GGUF_TO_FAST_CONVERTERS 表)重建 tokenizers 库的 backend:
| 转换器 | 适用架构 | 实现要点 |
|---|---|---|
GGUFLlamaConverter |
llama、deci、decilm | BPE 词表 + merges,特殊 token 按 token_type==3 识别;Llama 3 走 byte-level pre-tokenizer 补丁 |
GGUFQwen2Converter |
qwen2/qwen3 及其 MoE、minimax_m2 | BPE,并补 <|im_start|> 等特殊 token |
GGUFPhi3Converter |
phi3 | BPE,补齐 Phi3 系列特殊 token |
GGUFGPTConverter |
bloom、falcon、stablelm、gpt2、starcoder2、mamba、nemotron | 复用 GPT2 转换逻辑 |
GGUFT5Converter |
t5、umt5 | Unigram 词表,</s> 后处理器 |
GGUFGemmaConverter |
gemma2/gemma3/gemma4 文本 | Unigram,空格用 ▁ 编码并做词表清洗 |
一个值得注意的细节:若 checkpoint 中没有 merges 字段,GGUFTokenizerSkeleton 会用 tokens 与 scores 在线重建 merges 列表(枚举所有可合并的 token 对并按分数排序),并打印 "Merges were not in checkpoint, building merges on the fly." 的警告——这解释了为什么部分 GGUF 文件能正常恢复 BPE tokenizer。
四、权重反量化与架构级张量处理
load_gguf_checkpoint 在 return_tensors=True 时的核心循环是(源码):
GGUFReader迭代所有张量,对每个张量调用 gguf-py 的dequantize(tensor.data, tensor.tensor_type)——这一步把 Q4_0、Q6_K、IQ2_XXS 等量化块还原为 fp32 数组;- 经过该架构的
TensorProcessor.process()做必要的形状重排/重命名; - 通过
tensor_key_mapping(GGUF 张量名 → HF 参数名)对齐后,转成torch张量,并按需to(torch_dtype)存入parsed_parameters["tensors"]。
张量名映射:get_gguf_hf_weights_map
GGUF 张量采用标准命名约定 blk.N.BB.weight/.bias(N 为层号,BB 为组件名)。get_gguf_hf_weights_map 借助 gguf-py 包的 MODEL_ARCH_NAMES 与 get_tensor_name_map(arch, num_layers) 生成 GGUF→HF 的名称映射:先实例化一个空壳 HF 模型拿到全部参数名,逐个查表转换;查不到的交给架构自己的 perform_fallback_tensor_mapping 兜底(常见于 MoE 的一对多映射)。该函数还会递归遍历子模块(如 Bloom 从 BloomModel 而非 BloomForCausalLM 转换而来的 checkpoint),因此要求同时安装 torch 与 gguf>=0.10.0,否则抛出明确的 ImportError。
TensorProcessor:每个架构的差异修正
GGUF 存储的张量布局与 HF 模型期望的并不总是一致,TENSOR_PROCESSORS 注册了各架构的处理器:
| 处理器 | 架构 | 做了什么 |
|---|---|---|
LlamaTensorProcessor |
llama | 对 attn_q/attn_k 权重做逆置换(GQA 场景下按 num_key_value_heads 重排),还原 llama.cpp 转换时的 head 间交错布局 |
Qwen2MoeTensorProcessor |
qwen2moe、qwen3moe | 把 ffn_gate_exps/ffn_up_exps 交错合并进 HF 的 gate_up_proj(第 2 维翻倍,gate 在前、up 在后);共享专家 gate 补维度 |
GptOssTensorProcessor |
gpt_oss | 把堆叠的专家张量 ffn_down/gate/up_projs 逐专家拆开;gate_up_projs 按半宽切分后转置 |
BloomTensorProcessor |
bloom | 逆还原 attn_qkv 的 QKV 交错 reshape(权重与 bias 分别处理) |
T5TensorProcessor |
t5、t5encoder | 从张量名中解析块号 bid 供后续对齐 |
GPT2TensorProcessor |
gpt2 | attn_qkv、ffn_down/up、attn_output 等权重转置;output.weight 特判为 lm_head.weight |
MambaTensorProcessor |
mamba | ssm_conv1d 补维度;ssm_a 做 log(-x) 还原指数化存储 |
NemotronTensorProcessor / Gemma2TensorProcessor |
nemotron、gemma2/gemma3 | norm.weight 减 1(还原 1+weight 式存储) |
Lfm2TensorProcessor |
lfm2 | shortconv.conv.weight 补中间维 |
MiniMaxM2TensorProcessor |
minimax-m2 | 同 Qwen2MoE 的 gate/up 交错合并,外加路由 bias 映射 |
这些"逆操作"的参照实现全部注释指向 llama.cpp 的 convert_hf_to_gguf.py,可以理解为:Transformers 执行的是 llama.cpp 转换脚本的逆向过程。
默认值补全
GGUF_CONFIG_DEFAULTS_MAPPING 只补"HF 默认值与 llama.cpp 约定不一致"的参数,例如 qwen3_moe 的 norm_topk_prob=True(HF 默认 False,但 llama.cpp 需要 True)、minimax_m2 的 use_routing_bias=True(GGUF 元数据不存储该开关)。
五、测试如何验证 GGUF 加载
仓库中的 tests/quantization/ggml/test_ggml.py 是这条功能的主测试入口(标注 @require_gguf @require_torch_accelerator @slow,需要加速器与 gguf 包)。其核心方法就是文档示例的最小复现:
def run_gguf_model(self, gguf_model_id: str, gguf_filename: str, expected_text: str):
tokenizer = AutoTokenizer.from_pretrained(gguf_model_id, gguf_file=gguf_filename)
model = AutoModelForCausalLM.from_pretrained(gguf_model_id, gguf_file=gguf_filename).to(torch_device)
text = tokenizer(self.example_text, return_tensors="pt").to(torch_device)
out = model.generate(**text, max_new_tokens=10)
self.assertEqual(tokenizer.decode(out[0], skip_special_tokens=True), expected_text)
测试用 TheBloke/TinyLlama-1.1B-Chat-v1.0-GGUF(Q4_0 / Q5_0 / Q8_0 标准量化)与 legraphista/Qwen2.5-0.5B-Instruct-IMat-GGUF(Q2_K~Q6_K 的 K 量化、IQ1_S 等 i-Matrix 量化)做参数化验证:加载后以 "Hello" 为 prompt 生成 10 个 token,并断言解码文本等于预期前缀。这组测试同时覆盖了反量化数值正确性与 tokenizer 重建正确性——两者必须都正确,生成结果才会命中预期。
六、微调后保存并转回 GGUF
按文档给出的流程,用 Transformers 调整完模型后,先 save_pretrained 存回 HF 格式,再交给 llama.cpp 的 convert-hf-to-gguf.py 转回 GGUF:
tokenizer.save_pretrained("directory")
model.save_pretrained("directory")
!python ${path_to_llama_cpp}/convert-hf-to-gguf.py ${directory}
这与本文描述的加载链路正好构成闭环:load_gguf_checkpoint 里的 TensorProcessor 做的是"GGUF → HF"的逆转换,而 llama.cpp 的转换脚本做的是"HF → GGUF",两者互为逆操作。
七、小结与注意事项
- 入口:
AutoModel*.from_pretrained(model_id, gguf_file=filename, dtype=...)与AutoTokenizer.from_pretrained(model_id, gguf_file=filename),gguf_file支持本地路径或 Hub 文件名;依赖gguf>=0.10.0与 PyTorch。 - 加载本质:GGUF 元数据经
GGUF_CONFIG_MAPPING/GGUF_TOKENIZER_MAPPING翻译为 HF config 与 tokenizer;量化张量经dequantize还原为 fp32,再按TensorProcessor重排并按dtype落地。 - 架构范围:以 GGUF_CONFIG_MAPPING 的键为准(llama/mistral/qwen 系列/gpt2 系/gemma 系/t5 系/mamba/bloom/stablelm/falcon/phi3 等);遇到不支持的架构会抛出
GGUF model with architecture {architecture} is not supported yet.。 - 精度提醒:从低比特量化文件反量化后,权重已经不可逆地受量化误差影响,因此该流程更适合"以 GGUF 为分发载体的 checkpoint 回载",而非替代原始全精度权重做精细微调;
dtype参数用于控制内存占用。 - 验证:可参考 tests/quantization/ggml/test_ggml.py 的"加载 + 短生成 + 断言解码文本"模式,对自己的 GGUF 文件做快速自检。
更多背景可继续阅读原文档 GGUF 页面。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00