跳到正文
原文
Hugging Face Blog·· 23 天前精选AI 评分62

Transformers 现已支持运行 llama.cpp 的 GGUF 量化模型

Transformers now runs llama.cpp quants

AI 导读

Hugging Face 为 transformers 加入 GGUF 支持,可从 Hub 选择量化 checkpoint 并通过 from_pretrained 加载,在本地机器上生成文本。

推荐理由

原文给出在 transformers 中直接加载 GGUF 的用法与本地推理表现,读者可据此判断量化模型的本地使用方式。

正文 · AI 翻译

我们正在 transformers 中添加对高效运行 GGUF 模型的支持,这样你就可以通过熟悉的 transformers API,使用适合你笔记本电脑内存大小的检查点。从 Hub 上选择一个 GGUF,用 from_pretrained 加载它,然后就可以在你自己的机器上开始生成。

在笔记本电脑上运行 AI 模型已经变得容易得多,而 llama.cpp 在其中发挥了重要作用。它的推理引擎为 Ollama、LM Studio 和 Jan 等本地 AI 工具提供支持。与 MLX 等项目一起,它帮助本地推理成为日常使用中切实可行的选择。

一个近期的例子,展示了本地 AI 可以带来怎样的体验:

这就是我们目前的进展。说实话,感觉相当神奇 🧙‍♀️

Qwen3.6 27B 通过 Llama.cpp 在 MacBook Pro 上的 Pi 编程代理中运行

对于 @huggingface 代码库上的非平凡任务,这感觉非常、非常接近 Claude 中最新 Opus 的水平…… pic.twitter.com/lsIxLoUneU

— Julien Chaumond (@julien_c) April 24, 2026

GGUF 由 llama.cpp 团队开发,是一种广泛用于本地推理的格式。该团队还在 Hub 上的 ggml-org 下分享了量化检查点。Unsloth、LM Studio Community 和 bartowski 等发布者也提供了多种量化级别的即用型 GGUF 检查点,因此用户可以选择适合自己机器的版本。GGUF 模型已被下载数百万次。

我们也希望让使用 transformers 在本地运行这些模型变得更加容易。兼容性只有在模型运行起来令人愉快时才有用。为了让性能接近 llama.cpp,我们通过 kernels 库复用了其底层的 ggml 内核,并减少了 generate 中的开销。我们最初的重点是 Apple Silicon 上的本地推理,从 Qwen3.5 架构开始。

什么是 GGUF 文件格式?

GGUF 将模型权重和元数据(包括分词器信息和可选的聊天模板)打包在一个文件中。它支持不同的量化级别,让你可以用一些精度换取更小的内存占用。诸如 Q4_K_M 之类的变体混合了张量精度,主要使用 4 位权重,同时让敏感张量保持更高精度。

以下是量化如何改变 Unsloth 的 Qwen3.5-4B 的文件大小:

GGUF 变体 文件大小 权衡
BF16 8.42 GB 未量化参考
Q6_K 3.53 GB 比更小的变体精度更高
Q5_K_M 3.14 GB 在大小和精度之间的折中
Q4_K_M 2.74 GB 本地推理的实用起点

我们建议从 Q4_K_M 开始,如果你的内存更充裕,再尝试 Q5_K_M 或 Q6_K。更激进的量化可以帮助更大的模型装入内存,但质量上的权衡取决于模型和任务。请在你实际希望模型完成的工作上评估它。Hub 的 GGUF 文档 介绍了可用的量化类型。

使用 transformers 加载 GGUF

要开始使用,你需要:

  • 一台 Apple Silicon Mac。
  • 已发布的 ggml-quantization 内核构建 所支持的 PyTorch 版本,通常是最近两个 PyTorch 版本。
  • 最新版本的 transformers(目前是 main,直到下一个版本发布)以及兼容版本的 kernels。
pip install -U "git+https://github.com/huggingface/transformers.git" kernels

要加载 GGUF 模型,请将其 Hub model_id 和文件名作为 gguf_file 传给 from_pretrained。

无需额外配置:当权重保持打包在 Metal 上时,transformers 会自动加载兼容的 ggml/Metal 层内核,并使用 ggml-org/ggml-attn 作为注意力实现。如果无法获取该内核,模型会回退到 "sdpa" 并给出警告,你也可以随时通过显式传入 attn_implementation="sdpa" 来强制使用 "sdpa"。有关更多加载选项,请参阅 GGUF 文档。

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
)

这是唯一与 GGUF 相关的步骤。其余部分都是标准的 transformers API:

messages = [{"role": "user", "content": "Explain why the sky is blue in a few sentences."}]
inputs = tokenizer.apply_chat_template(
    messages,
    tokenize=True,
    add_generation_prompt=True,
    return_dict=True,
    return_tensors="pt",
).to(model.device)

with torch.inference_mode():
    outputs = model.generate(**inputs, max_new_tokens=256)

print(tokenizer.decode(outputs[0], skip_special_tokens=True))

如果没有兼容的量化内核,加载器会回退到对模型进行反量化,并占用更多内存。

使用你偏好的接口来服务 GGUF

你也可以将同一个检查点与 transformers serve 一起使用,它提供与 OpenAI 兼容的 API:

pip install -U "transformers[serving] @ git+https://github.com/huggingface/transformers.git" kernels

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

model 参数使用 <model_id>:<filename>.gguf:冒号前是 Hub 仓库(unsloth/Qwen3.5-4B-GGUF),冒号后是要加载的文件(Qwen3.5-4B-Q4_K_M.gguf)。这会从可能包含多个量化版本的仓库中选择一个特定的量化版本。

对于聊天模板支持思考的模型,添加 --reasoning off 可跳过思考,添加 --reasoning on 可启用思考。默认值 --reasoning auto 遵循聊天模板的默认设置。详情请参阅 推理选项。

你可以通过添加自定义的 OpenAI 兼容提供程序并配置以下设置,来连接 Jan 或 Pi 等客户端:

设置 值
基础 URL http://localhost:8000/v1
模型 ID unsloth/Qwen3.5-4B-GGUF:Qwen3.5-4B-Q4_K_M.gguf

transformers 会在你的 Mac 上运行模型,而客户端提供对话界面。支持此 API 的其他客户端也可以使用同一个端点。

与 llama.cpp 进行基准测试对比

我们衡量本地推理性能的参照是 llama.cpp。下面的对比聚焦于三个 GGUF 检查点:一个小型稠密模型、一个较大的稠密模型,以及一个专家混合模型。

llama.cpp 一列来自 llama-bench 工具(构建 5f55650a7,发行版 b10200,来自 ggml 0.18.0 的 Metal 后端),以 llama-bench -m <file> -p 0 -n 128 -r 3 方式运行,报告 tg128:在 128 个解码 token 上的 token 生成速率,取三次重复的平均值,不包含提示处理。transformers 一列是 generate,从 12 个 token 的提示生成相同的 128 个 token,取三次预热运行中的最佳值,并且包含预填充。

在 MacBook Pro M2 Max、32 GB 统一内存、macOS 26.6、PyTorch 2.12.1、kernels 0.17.0、接通电源的条件下测得。

基准测试脚本

import time
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer

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

model = AutoModelForCausalLM.from_pretrained(model_id, gguf_file=filename)
tokenizer = AutoTokenizer.from_pretrained(model_id, gguf_file=filename)
inputs = tokenizer("The capital of France is Paris. The capital of Germany is", return_tensors="pt")
inputs = inputs.to(model.device)


with torch.inference_mode():
    model.generate(**inputs, max_new_tokens=8, min_new_tokens=8, do_sample=False)  # warm up
    torch.mps.synchronize()
    for _ in range(3):
        time.sleep(90)  # let the machine cool: back-to-back runs decay by 10% or more
        start = time.perf_counter()
        model.generate(**inputs, max_new_tokens=128, min_new_tokens=128, do_sample=False)
        torch.mps.synchronize()
        print(f"{128 / (time.perf_counter() - start):.1f} tok/s")

至于另一列:

llama-bench -hf unsloth/Qwen3.5-4B-GGUF:Q4_K_M -p 0 -n 128 -r 3

GGUF generation throughput compared with llama.cpp

在三个检查点上,Transformers 都接近 llama.cpp。图表使用上述相同的测量结果;它并不意味着基准测试条件完全相同,因为 Transformers 的测量包含预填充,而 llama-bench 报告的是仅解码吞吐量。

transformers 与 llama.cpp

当 GGML 和 llama.cpp 加入 HF Mirror 时,我们描述了它们互补的角色:llama.cpp 为本地推理提供基础,而 transformers 为模型定义提供基础。GGUF 支持让这两者更加紧密地结合在一起。

当你的优先事项是高效的本地推理时,llama.cpp 仍是我们推荐的引擎。 其专用的运行时、内存管理和广泛的硬件支持都是围绕这一目标构建的。这一集成让开发者可以方便地在 transformers 中使用相同的 GGUF 检查点:

  • 在 Python 和 PyTorch 中试用 GGUF。 使用钩子检查中间激活值,修改模型的前向传播,或使用熟悉的 PyTorch 工具原型化自定义层。
  • 评估 GGUF 模型。使用你现有的 transformers 评估工作流来衡量量化检查点的质量。
  • 验证 GGUF 转换。对我们开发者而言,在 transformers 中加载原始检查点及其 GGUF 转换版本,可以更容易地检查权重是否被正确转换,同时考虑量化误差。
  • 尝试新的解码思路。将自定义 logits 处理器和停止条件与 generate 配合使用,或者用 Python 编写你自己的生成循环。
  • 从 GGUF 检查点进行微调。反量化权重,然后继续使用标准的 transformers 训练工作流。

对于最后一种情况,使用 GgufConfig(dequantize=True):

import torch
from transformers import AutoModelForCausalLM, GgufConfig

model = AutoModelForCausalLM.from_pretrained(
    "unsloth/Qwen3.5-4B-GGUF",
    gguf_file="Qwen3.5-4B-Q4_K_M.gguf",
    quantization_config=GgufConfig(dequantize=True),
    dtype=torch.bfloat16,
)

超越 GGUF:为更多模型提供 ggml 内核

更大的机会在于将 ggml 的性能带给 llama.cpp 不支持的模型。

transformers 已经提供了这些架构的 PyTorch 实现。借助 PyTorch 中可用的 ggml 内核和量化方案,我们可以着手加速其支持的操作,而无需先在 llama.cpp 中实现整个模型。这对于新架构、研究模型以及可能永远不会获得专门 llama.cpp 实现的自定义变体尤其有用。

这个机会不仅限于 GGUF 格式本身。内核作用于张量;它并不要求整个模型都来自 GGUF 文件。这些相同的构建块可以集成到其他 transformers 模型和加载工作流中。这也开辟了通往其他模态的路径:计算机视觉模型、音频模型和多模态模型可以复用兼容的注意力、归一化和矩阵乘法内核,而无需先在 llama.cpp 中拥有完整实现。每个架构仍然需要集成和验证;这里的初始 GGUF 示例涵盖文本生成。

使用 Python 和 PyTorch 进行快速本地推理

我们还希望展示在将模型和生成循环保持在 Python 中的情况下,我们能达到什么程度。借助合适的内核和高效的生成循环,Python 和 PyTorch 可以提供强大的本地推理性能。内核处理繁重的计算,而生成循环则通过避免不必要的同步来让 GPU 保持忙碌。

我们的重点是在不需要 torch.compile 的情况下让 eager 执行变快。对于交互式使用,我们希望快速启动并获得稳定的 token 流,不会因编译停顿或输入形状变化而重新编译。这项工作的两个主要部分是内核和 generate 本身。

复用 ggml 的 Metal 内核

内核是在 GPU 上执行操作的小程序。PyTorch 提供了通用实现;专门的内核可以完成更少的工作、组合多个操作,或者直接以存储格式读取量化权重。

kernels 库让我们能够在 Hub 上分发 ggml 的 Metal 内核的兼容构建,并从 transformers 中调用它们。这使 ggml 的工作成果进入 PyTorch 模型,而不是用单独的推理运行时替换该模型。

内核 它的作用
ggml-quantization 读取用于矩阵运算的打包量化权重,包括 MoE 模型中被选中的专家。它避免了在每次解码操作之前展开整个权重矩阵。
ggml-norm 融合归一化操作,包括 Qwen3.5 和 Qwen3.8 使用的零中心 RMSNorm。
ggml-attn 为提示处理和 token 解码提供 ggml 的 Metal flash attention。
ggml-gated-delta-net 加速 Qwen3.5 和 Qwen3.8 混合架构中线性注意力层所使用的门控 delta 网络。
topk 为 MoE 模型中的每个 token 选择专家,结合 softmax 和 top-k 路由。这是我们自己的 Metal 实现。

前四个包基于 ggml 的内核构建;top-k 内核解决了 MoE 路由中的另一个瓶颈。它们共同减少了每生成一个 token 所需的 GPU 工作量。

为了展示层内核的贡献,我们对比了相同的打包 GGUF 检查点在启用和未启用它们时的表现。量化内核在两种配置中均保持启用:禁用它还会改变权重的表示方式,从而衡量的是另一种不同的权衡。

Throughput improvement from the layer kernels

让 CPU 和 GPU 协同工作

更快的内核只有在 GPU 有活可干时才有帮助。在生成过程中,CPU 调度 GPU 操作并控制生成下一个 token 的循环。从 GPU 读回结果可能会迫使 CPU 等待已排队的操作完成。即使每个 token 只需等待一小会儿,反复如此也会明显降低吞吐量。

在 generate 中有两项改动解决了这个问题,这为所有 transformers 模型带来了改进(而不仅仅是在运行 GGUF 文件时):

  • 尽早移除不必要的注意力掩码(#48814)。 当受支持的仅解码器输入没有填充时,其全为 1 的填充掩码可以在生成开始时移除。下游的注意力代码不再需要反复检查该掩码以确定是否可以跳过它。因果注意力仍然得到保留。
  • 推迟停止检查(#47975)。 在受支持的路径上,generate 会异步复制停止决策,并在下一步使用它。CPU 可以在 GPU 运行的同时继续调度工作。流式 token 使用相同的方法,并且超出停止条件之后的任何额外步骤都会从结果中移除。

这些改动改进了模型周围的生成循环,因此它们的价值不仅限于 GGUF。它们与内核工作相辅相成:内核降低了单个操作的成本,而更少的同步点让 CPU 调度与 GPU 执行得以重叠。

Throughput improvement from the generation loop changes

这些测量保持所有层内核启用;柱状图单独展示了生成循环中的改动。

当前限制与后续步骤

最初的目标是在 Apple Silicon 上进行单个交互式对话。有几点边界需要注意:

  • 打包推理路径目前仅支持 MPS。 通过反量化导入 GGUF 仍是一个单独的选项;对文件格式的支持并不意味着打包内核在每台设备上都可用。
  • 填充和批处理仍需改进。 未填充的输入受益于上述掩码优化。带填充的批次无法走同样的捷径,性能可能更低。我们希望将这项工作扩展到 MPS 上的 generate_batch。
  • 架构覆盖范围有限。 打包加载器目前覆盖 Qwen3.5 稠密和 MoE 架构,包括兼容的 Qwen3.8 检查点。添加对其他架构的支持相对直接,我们将逐步扩大覆盖范围。

如果你有想在 transformers 中使用的 GGUF 模型,请提交 issue,附上 checkpoint 和你的使用场景。这将帮助我们优先支持大家在本地运行的模型。

致谢

我们感谢 Arthur Zucker 发起这项工作并审阅了我的所有 PR,以及 Cyril Vallez 提供的 generate 个 PR。我们感谢 Sayak Paul、llama.cpp 团队和 Bertrand Chevalier 在集成内核方面的帮助。我们还感谢 Aritra Roy Gosthipaty 和 Pedro Cuenca 审阅这篇博文,以及 Lysandre Debut 监督该项目。

来源:Hugging Face Blog · hf-mirror.com