Transformers 现在支持直接运行 llama.cpp 的 GGUF 量化模型

0 阅读

GGUF 文件格式是什么?

GGUF 是由 llama.cpp 团队开发的一种文件格式,它把模型权重、分词器信息,甚至可选的聊天模板都打包进一个文件里。这种格式最大的特点是支持多种量化级别,让你可以用一点精度换更小的内存占用。

比如 Unsloth 发布的 Qwen3.5-4B 模型,不同量化版本的大小差异很明显:

  • BF16(未量化):8.42 GB
  • Q6_K:3.53 GB
  • Q5_K_M:3.14 GB
  • Q4_K_M:2.74 GB

Q4_K_M 这种混合精度方案,大部分权重用 4-bit,但对敏感层保留更高精度,是本地运行的一个实用起点。如果你机器内存够,可以试试 Q5_K_MQ6_K。不过具体效果还得看你实际要跑的任务,别光看数字。

用 Transformers 加载 GGUF 模型

想尝鲜,你得有台 Apple Silicon 的 Mac,并装上最新版的 PyTorch 和 Transformers(目前得从 main 分支安装)。加载过程出乎意料地简单,只需要在 from_pretrained 时指定 gguf_file 参数就行。

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer

model_id = "unsloth/Qwen3.5-4B-GGUF"
filename = "Qwen3.5-4B-Q4_K_M.gguf"

tokenizer = AutoTokenizer.from_pretrained(model_id, gguf_file=filename)
model = AutoModelForCausalLM.from_pretrained(
    model_id,
    gguf_file=filename
)

后面就完全是标准的 Transformers API 了,聊天模板、生成文本,一切照旧。库会自动检测并加载兼容的 ggml/Metal 内核来处理量化权重。如果没找到合适的内核,它会回退到先解量化再运行的模式,但这样会吃更多内存。

用你喜欢的界面来服务 GGUF 模型

除了在脚本里跑,你还能用 transformers serve 命令启动一个 OpenAI 兼容的 API 服务。这样,像 Jan 或 Pi 这样的客户端就能直接连上你本机的模型了。

transformers serve "unsloth/Qwen3.5-4B-GGUF:Qwen3.5-4B-Q4_K_M.gguf"

命令里的冒号分隔了模型仓库 ID 和具体的文件名,方便你在同一个仓库里切换不同量化版本。对于支持“思考”模式的模型,还可以通过 --reasoning 参数来开关这个功能。

和 llama.cpp 的性能对比

大家最关心的肯定是速度。毕竟 llama.cpp 是本地推理的标杆。官方在一台 M2 Max MacBook Pro 上做了基准测试,对比了三个不同规模的 GGUF 模型。

结果显示,Transformers 的性能已经非常接近 llama.cpp 了。测试用的是生成 128 个新 token 的速率。需要注意的是,Transformers 的测量包含了预填充(prefill)阶段,而 llama.cpp 的 llama-bench 工具默认只测解码(decode)阶段,所以 Transformers 的数据其实更“吃亏”一点。即便如此,差距依然很小,这说明这套方案是靠谱的。

Transformers 和 llama.cpp 的关系

这次集成并不是要取代 llama.cpp。官方明确说了,如果你追求极致的本地推理效率,llama.cpp 依然是首选。它的专用运行时、内存管理和广泛的硬件支持都是围绕这个目标打造的。

那么 Transformers 支持 GGUF 的意义在哪?在于灵活性和可编程性。你现在可以在熟悉的 Python/PyTorch 环境里:

  • 实验和调试:用钩子(hooks)检查中间激活值,或者修改模型的前向传播逻辑。
  • 评估模型:用现有的 Transformers 评估流程来测试量化模型的质量。
  • 验证转换:同时加载原始模型和 GGUF 版本,方便检查转换是否正确。
  • 尝试新解码策略:使用自定义的 logits 处理器或停止条件。
  • 微调模型:通过设置 dequantize=True,可以把量化权重解压成浮点数,然后接着用标准流程微调。

超越 GGUF:让 ggml 内核加速更多模型

更大的机会其实在于,把 ggml 高性能的内核带给那些 llama.cpp 还不支持的模型架构。

Transformers 本身就实现了大量模型。现在有了这些 PyTorch 可用的 ggml 内核,我们就有机会在不重写整个模型到 llama.cpp 的前提下,加速其中的关键操作。这对于新出的研究模型、自定义变体尤其有用。这条路甚至能通向多模态领域,比如视觉或音频模型,只要它们用到了兼容的注意力、归一化或矩阵乘法操作。

在 Python 和 PyTorch 里实现快速本地推理

这次工作的核心目标之一,就是证明一件事:只要内核够快、生成循环够高效,纯 Python 和 PyTorch 也能跑出强劲的本地推理性能。他们刻意避开了 torch.compile,就是为了保证交互体验的流畅——没有编译等待,输入长度变了也不用重新编译。

复用 ggml 的 Metal 内核

关键在于那几个从 ggml 借来的 Metal 内核,它们被封装在 kernels 库里,可以从 Hugging Face Hub 直接安装。

  • ggml-quantization:直接读取打包好的量化权重做矩阵运算,省去了每次解压整个权重矩阵的开销。
  • ggml-norm:融合了归一化操作,特别针对 Qwen 系列用的零中心 RMSNorm。
  • ggml-attn:提供了 ggml 的 Metal 闪存注意力实现。
  • ggml-gated-delta-net:加速 Qwen3.5/3.8 混合架构里的门控 Delta 网络。
  • topk:这是 Transformers 团队自己写的 Metal 内核,用于 MoE 模型中高效地选择专家。

把这些内核关掉再跑一遍,性能差距立竿见影,足见它们的贡献。

让 CPU 和 GPU 协同工作

光有快内核还不够,得让 GPU 一直有活干。之前的生成循环里有些不必要的同步点,会让 CPU 干等着 GPU 完事,每生成一个 token 就等一下,累积起来就很伤吞吐量。

为此,他们在 generate 函数里做了两个关键优化:

  1. 提前丢弃无用的注意力掩码:对于没有填充(padding)的输入,那个全 1 的掩码其实在生成一开始就可以扔掉,省得后面每次注意力计算都要检查一遍。
  2. 推迟停止检查:把判断是否停止生成的逻辑异步化,CPU 不用等 GPU 返回结果,可以立刻去安排下一步的计算。

这两个改动不仅对 GGUF 模型有效,对所有 Transformers 模型的生成速度都有提升。它们和内核优化是相辅相成的:内核降低了单次操作的成本,而减少同步点则让 CPU 和 GPU 能更好地并行工作。

当前限制和下一步计划

目前这个功能还处于早期阶段,有几个明显的边界:

  • 仅限 Apple Silicon (MPS):打包推理路径现在只在 Mac 上可用。其他设备还是得走解量化那条老路。
  • 批处理和填充支持不足:现在的优化主要针对单条、无填充的输入。带填充的批处理还不能享受同样的性能红利。
  • 架构覆盖有限:目前只支持 Qwen3.5(包括稠密版和 MoE 版)以及兼容的 Qwen3.8 模型。

官方表示会逐步扩展支持的架构。如果你有特别想在 Transformers 里跑的 GGUF 模型,不妨去 GitHub 开个 issue,告诉他们你的用例,这能帮助他们排定优先级。