{"slug": "transformers-now-runs-llama-cpp-quants", "title": "Transformers now runs llama.cpp quants", "summary": "Hugging Face added support for running GGUF models in its transformers library, letting users load llama.cpp-format checkpoints via `from_pretrained` and generate locally, with initial focus on Apple Silicon and the Qwen3.5 architecture. The company said it reuses llama.cpp's underlying ggml kernels through the `kernels` library to bring performance close to llama.cpp while reducing overhead in `generate`. For Unsloth's Qwen3.5-4B, GGUF variants range from 8.42 GB at BF16 to 2.74 GB at Q4_K_M, the quantization Hugging Face recommends as a starting point.", "body_md": "[Image-Text-to-Text •  4B • Updated   •  908k  •  432](https://huggingface.co/unsloth/Qwen3.5-4B-GGUF)  \n\n# \n\t\tTransformers now runs llama.cpp quants\n\t\n\n [Update on GitHub](https://github.com/huggingface/blog/blob/main/transformers-llama-cpp-quants.md)\n\n**We're adding support for running GGUF models efficiently in transformers**, so you can use checkpoints sized for your laptop's memory through the familiar transformers APIs. Pick a GGUF from the Hub, load it with\n\n`from_pretrained`, and start generating on your own machine.\nRunning AI models on your laptop has become much easier, and [llama.cpp](https://github.com/ggml-org/llama.cpp) has been a big part of that. Its inference engine powers local AI tools such as Ollama, LM Studio, and Jan. Alongside projects like [MLX](https://github.com/ml-explore/mlx), it has helped make local inference a practical option for everyday use.\n\n*A recent example of what local AI can feel like:*\n\nThis is where we are right now. And i’m not gonna lie it feels pretty magical 🧙♀️\n\nQwen3.6 27B running inside of Pi coding agent via Llama.cpp on the MacBook Pro\n\nFor non-trivial tasks on the [@huggingface](https://x.com/huggingface?ref_src=twsrc%5Etfw) codebases, this feels very, very close to hitting the latest Opus in Claude… [pic.twitter.com/lsIxLoUneU](https://t.co/lsIxLoUneU)\n\n[April 24, 2026](https://x.com/julien_c/status/2047647522173104145?ref_src=twsrc%5Etfw)\n\n**GGUF**, developed by the llama.cpp team, is a widely used format for local inference. The team also shares quantized checkpoints under [ggml-org on the Hub](https://huggingface.co/ggml-org). Publishers such as [Unsloth](https://huggingface.co/unsloth), [LM Studio Community](https://huggingface.co/lmstudio-community), and [bartowski](https://huggingface.co/bartowski) also provide ready-to-use GGUF checkpoints in a range of quantizations, so users can pick the version that fits their machine. GGUF models have been downloaded millions of times.\n\nWe want to make it easier to run these models locally with transformers, too. Compatibility is only useful if the model is pleasant to run. To bring performance close to llama.cpp, we're reusing its underlying ggml kernels through the [`kernels`](https://huggingface.co/docs/kernels/index) library, and reducing overhead in `generate`. Our initial focus is local inference on Apple Silicon, starting with the Qwen3.5 architecture.\n\n## \n\t\tWhat is the GGUF file format?\n\t\n\n[GGUF](https://github.com/ggml-org/ggml/blob/master/docs/gguf.md) packages model weights and metadata, including tokenizer information and an optional chat template, in one file. It supports different quantization levels, letting you trade some precision for a smaller memory footprint. Variants such as `Q4_K_M` mix tensor precisions, using mostly 4-bit weights while keeping sensitive tensors at higher precision.\n\nHere's how quantization changes the file size of [Unsloth's Qwen3.5-4B](https://huggingface.co/unsloth/Qwen3.5-4B-GGUF/tree/main):\n\n| GGUF variant | File size | Tradeoff | \n|---|---|---|\n| `BF16` | 8.42 GB | Unquantized reference | \n| `Q6_K` | 3.53 GB | More precision than the smaller variants | \n| `Q5_K_M` | 3.14 GB | A middle ground between size and precision | \n| `Q4_K_M` | 2.74 GB | A practical starting point for local inference | \n\nWe suggest starting with `Q4_K_M`, then trying `Q5_K_M` or `Q6_K` if you have more memory available. More aggressive quantization can help larger models fit, but the quality tradeoff depends on the model and the task. Evaluate it on the work you actually want the model to do. The [Hub's GGUF documentation](https://huggingface.co/docs/hub/gguf#quantization-types) describes the available quantization types.\n\n## \n\t\tLoad GGUF with transformers\n\t\n\nTo get started, you need:\n\n- **An Apple Silicon Mac** .\n- **A PyTorch version supported by the published [ggml-quantization kernel builds](https://huggingface.co/kernels/ggml-org/ggml-quantization)** , usually the two latest PyTorch releases.\n- **The latest version of transformers (main for now, until the next release) and a compatible version of `kernels`** .\n\n```\npip install -U \"git+https://github.com/huggingface/transformers.git\" kernels\n```\n\nTo load a GGUF model, pass its Hub `model_id` and filename as `gguf_file` to `from_pretrained`.\n\nNo extra configuration is needed: when the weights stay packed on Metal, transformers automatically loads the compatible ggml/Metal layer kernels and uses `ggml-org/ggml-attn` as the attention implementation. If that kernel cannot be fetched, the model falls back to `\"sdpa\"` with a warning, and you can always force `\"sdpa\"` by passing `attn_implementation=\"sdpa\"` explicitly. See the [GGUF documentation](https://huggingface.co/docs/transformers/main/en/quantization/gguf) for more loading options.\n\n``` python\nimport torch\nfrom transformers import AutoModelForCausalLM, AutoTokenizer\n\nmodel_id = \"unsloth/Qwen3.5-4B-GGUF\"\nfilename = \"Qwen3.5-4B-Q4_K_M.gguf\"\n\ntokenizer = AutoTokenizer.from_pretrained(model_id, gguf_file=filename)\nmodel = AutoModelForCausalLM.from_pretrained(\n    model_id,\n    gguf_file=filename\n)\n```\n\nThat is the only GGUF-specific step. Everything after it is the standard transformers API:\n\n```\nmessages = [{\"role\": \"user\", \"content\": \"Explain why the sky is blue in a few sentences.\"}]\ninputs = tokenizer.apply_chat_template(\n    messages,\n    tokenize=True,\n    add_generation_prompt=True,\n    return_dict=True,\n    return_tensors=\"pt\",\n).to(model.device)\n\nwith torch.inference_mode():\n    outputs = model.generate(**inputs, max_new_tokens=256)\n\nprint(tokenizer.decode(outputs[0], skip_special_tokens=True))\n```\n\nWithout a compatible quantization kernel, the loader falls back to dequantizing the model and uses more memory.\n\n## \n\t\tServe GGUF with your preferred interface\n\t\n\nYou can also use the same checkpoint with [`transformers serve`](https://huggingface.co/docs/transformers/main/en/serve-cli/serving), which exposes an OpenAI-compatible API:\n\n```\npip install -U \"transformers[serving] @ git+https://github.com/huggingface/transformers.git\" kernels\n\ntransformers serve \"unsloth/Qwen3.5-4B-GGUF:Qwen3.5-4B-Q4_K_M.gguf\"\n```\n\nThe model argument uses `<model_id>:<filename>.gguf`: before the colon is the Hub repository (` unsloth/Qwen3.5-4B-GGUF`), and after it is the file to load (` Qwen3.5-4B-Q4_K_M.gguf`). This selects a specific quantization from a repository that may contain several.\n\nFor models whose chat template supports thinking, add `--reasoning off` to skip it or `--reasoning on` to enable it. The default, `--reasoning auto`, follows the chat template’s default. See the [reasoning options](https://huggingface.co/docs/transformers/main/en/serve-cli/serving#enable-reasoning-on-the-server) for details.\n\nYou can connect a client such as [Jan](https://www.jan.ai/docs/desktop/remote-models/custom-endpoint) or [Pi](https://pi.dev) by adding a custom OpenAI-compatible provider with these settings:\n\n| Setting | Value | \n|---|---|\n| Base URL | `http://localhost:8000/v1` | \n| Model ID | `unsloth/Qwen3.5-4B-GGUF:Qwen3.5-4B-Q4_K_M.gguf` | \n\ntransformers runs the model on your Mac, while the client provides the conversation interface. The same endpoint can be used by other clients that support this API.\n\n## \n\t\tBenchmarking against llama.cpp\n\t\n\nOur reference for local inference performance is llama.cpp. The comparison below focuses on three GGUF checkpoints: a small dense model, a larger dense model, and a mixture-of-experts model.\n\nThe llama.cpp column comes from the [`llama-bench`](https://github.com/ggml-org/llama.cpp/tree/master/tools/llama-bench) tool (build `5f55650a7`, release b10200, Metal backend from ggml 0.18.0), run as `llama-bench -m <file> -p 0 -n 128 -r 3`, which reports `tg128`: the token-generation rate over 128 decoded tokens, averaged across three repetitions, with prompt processing excluded. The transformers column is `generate` producing the same 128 tokens from a 12-token prompt, best of three warmed runs, and it includes prefill.\n\nMeasured on a MacBook Pro M2 Max, 32 GB unified memory, macOS 26.6, PyTorch 2.12.1, kernels 0.17.0, plugged in.\n\n## The benchmark script\n\n``` python\nimport time\nimport torch\nfrom transformers import AutoModelForCausalLM, AutoTokenizer\n\nmodel_id, filename = \"unsloth/Qwen3.5-4B-GGUF\", \"Qwen3.5-4B-Q4_K_M.gguf\"\n\nmodel = AutoModelForCausalLM.from_pretrained(model_id, gguf_file=filename)\ntokenizer = AutoTokenizer.from_pretrained(model_id, gguf_file=filename)\ninputs = tokenizer(\"The capital of France is Paris. The capital of Germany is\", return_tensors=\"pt\")\ninputs = inputs.to(model.device)\n\nwith torch.inference_mode():\n    model.generate(**inputs, max_new_tokens=8, min_new_tokens=8, do_sample=False)  # warm up\n    torch.mps.synchronize()\n    for _ in range(3):\n        time.sleep(90)  # let the machine cool: back-to-back runs decay by 10% or more\n        start = time.perf_counter()\n        model.generate(**inputs, max_new_tokens=128, min_new_tokens=128, do_sample=False)\n        torch.mps.synchronize()\n        print(f\"{128 / (time.perf_counter() - start):.1f} tok/s\")\n```\n\nFor the other column:\n\n```\nllama-bench -hf unsloth/Qwen3.5-4B-GGUF:Q4_K_M -p 0 -n 128 -r 3\n```\n\nTransformers is close to llama.cpp across all three checkpoints. The chart uses the same measurements described above; it does not imply identical benchmark conditions, since the Transformers measurement includes prefill while `llama-bench` reports decode-only throughput.\n\n## \n\t\ttransformers and llama.cpp\n\t\n\nWhen [GGML and llama.cpp joined Hugging Face](https://huggingface.co/blog/ggml-joins-hf), we described their complementary roles: llama.cpp provides a foundation for local inference, while transformers provides a foundation for model definition. GGUF support brings those two closer together.\n\n**llama.cpp remains our recommended engine when your priority is efficient local inference.** Its dedicated runtime, memory management, and broad hardware support are built around that goal. This integration gives developers a convenient way to work with the same GGUF checkpoints inside transformers:\n\n- **Experiment with GGUF in Python and PyTorch.** Inspect intermediate activations with hooks, modify a model's forward pass, or prototype custom layers using familiar PyTorch tools.\n- **Evaluate GGUF models.** Use your existing transformers evaluation workflows to measure the quality of quantized checkpoints.\n- **Validate GGUF conversions.** For us as developers, loading the original checkpoint and its GGUF conversion in transformers makes it easier to check that the weights were converted correctly, accounting for quantization error.\n- **Try new decoding ideas.** Use custom logits processors and stopping criteria with`generate` , or write your own generation loop in Python.\n- **Fine-tune from a GGUF checkpoint.** Dequantize the weights and continue with a standard transformers training workflow.\n\nFor that last case, use `GgufConfig(dequantize=True)`:\n\n``` python\nimport torch\nfrom transformers import AutoModelForCausalLM, GgufConfig\n\nmodel = AutoModelForCausalLM.from_pretrained(\n    \"unsloth/Qwen3.5-4B-GGUF\",\n    gguf_file=\"Qwen3.5-4B-Q4_K_M.gguf\",\n    quantization_config=GgufConfig(dequantize=True),\n    dtype=torch.bfloat16,\n)\n```\n\n## \n\t\tBeyond GGUF: ggml kernels for more models\n\t\n\n**The bigger opportunity is bringing ggml's performance to models that llama.cpp does not support.**\n\ntransformers already provides the PyTorch implementations of these architectures. With ggml kernels and quantization schemes available in PyTorch, we can work toward accelerating their supported operations without first implementing the entire model in llama.cpp. This is especially useful for new architectures, research models, and custom variants that may never receive a dedicated llama.cpp implementation.\n\nThat opportunity extends beyond the GGUF format itself. A kernel operates on tensors; it does not require the whole model to come from a GGUF file. The same building blocks can be integrated into other transformers models and loading workflows. This also opens a path to other modalities: computer vision models, audio models, and multimodal models could reuse compatible attention, normalization, and matrix multiplication kernels without first having a full implementation in llama.cpp. Each architecture still needs integration and validation; the initial GGUF examples here cover text generation.\n\n## \n\t\tFast local inference with Python and PyTorch\n\t\n\nWe also wanted to show how far we can get while keeping the model and generation loop in Python. **With the right kernels and an efficient generation loop, Python and PyTorch can deliver strong local inference performance.** The kernels handle the heavy computation, while the generation loop keeps the GPU busy by avoiding unnecessary synchronization.\n\nOur focus was to make eager execution fast without requiring `torch.compile`. For interactive use, we wanted a quick start and a steady stream of tokens, without compilation pauses or recompilation when input shapes change. The two main pieces of that work are the kernels and `generate` itself.\n\n### \n\t\tReusing ggml's Metal kernels\n\t\n\nA kernel is a small program that performs an operation on the GPU. PyTorch supplies general-purpose implementations; a specialized kernel can do less work, combine several operations, or read quantized weights directly in their stored format.\n\nThe `kernels` library lets us distribute compatible builds of ggml's Metal kernels on the Hub and call them from transformers. That brings ggml's work into the PyTorch model without replacing the model with a separate inference runtime.\n\n| Kernel | What it does | \n|---|---|\n| [`ggml-quantization`](https://huggingface.co/kernels/ggml-org/ggml-quantization) | Reads packed quantized weights for matrix operations, including the selected experts in an MoE model. It avoids expanding the whole weight matrix before each decode operation. | \n| [`ggml-norm`](https://huggingface.co/kernels/ggml-org/ggml-norm) | Fuses normalization operations, including the zero-centered RMSNorm used by Qwen3.5 and Qwen3.8. | \n| [`ggml-attn`](https://huggingface.co/kernels/ggml-org/ggml-attn) | Provides ggml's Metal flash attention for prompt processing and token decoding. | \n| [`ggml-gated-delta-net`](https://huggingface.co/kernels/ggml-org/ggml-gated-delta-net) | Accelerates the gated delta network used in the linear-attention layers of the Qwen3.5 and Qwen3.8 hybrid architectures. | \n| [`topk`](https://huggingface.co/kernels/transformers-community/topk) | Selects the experts for each token in an MoE model, combining softmax and top-k routing. This is our own Metal implementation. | \n\nThe first four packages build on ggml's kernels; the top-k kernel addresses a separate bottleneck in MoE routing. Together they reduce the GPU work needed for each generated token.\n\nTo show the contribution of the layer kernels, we compare the same packed GGUF checkpoints with and without them. The quantization kernel stays enabled in both configurations: disabling it would also change how weights are represented and would measure a different tradeoff.\n\n### \n\t\tKeeping the CPU and GPU working together\n\t\n\nFaster kernels only help if the GPU has work to do. During generation, the CPU schedules GPU operations and controls the loop that produces the next token. Reading a result back from the GPU can force the CPU to wait until queued operations finish. Repeating even a small wait for every token can noticeably reduce throughput.\n\nTwo changes address this in `generate`, which results in improvements for all transformers models (not just when running GGUF files):\n\n- **[Drop an unnecessary attention mask early (#48814)](https://github.com/huggingface/transformers/pull/48814).** When a supported decoder-only input has no padding, its all-ones padding mask can be removed at the start of generation. Downstream attention code no longer needs to inspect that mask repeatedly to determine whether it can be skipped. Causal attention is still preserved.\n- **[Defer the stopping check (#47975)](https://github.com/huggingface/transformers/pull/47975).** On supported paths,`generate` copies the stopping decision asynchronously and consumes it on the following step. The CPU can keep scheduling work while the GPU runs. Streaming tokens use the same approach, and any extra step past the stopping condition is removed from the result.\n\nThese changes improve the generation loop around the model, so their usefulness extends beyond GGUF. They complement the kernel work: kernels reduce the cost of an operation, while fewer synchronization points let CPU scheduling and GPU execution overlap.\n\nThese measurements keep all layer kernels enabled; the bars isolate the changes to the generation loop.\n\n## \n\t\tCurrent limitations and next steps\n\t\n\nThe initial target is a single interactive conversation on Apple Silicon. There are a few boundaries to keep in mind:\n\n- **The packed inference path is MPS-only for now.** GGUF import through dequantization remains a separate option; support for the file format does not imply that packed kernels are available on every device.\n- **Padding and batching still need work.** Unpadded inputs benefit from the mask optimization described above. Padded batches cannot take the same shortcut and can have lower performance. We want to extend the work to`generate_batch` on MPS.\n- **Architecture coverage is limited.** The packed loader currently covers the Qwen3.5 dense and MoE architectures, including compatible Qwen3.8 checkpoints. Adding support for other architectures is relatively straightforward, and we’ll expand coverage gradually.\n\nIf you have a GGUF model you would like to use in transformers, [open an issue](https://github.com/huggingface/transformers/issues) with the checkpoint and your use case. That will help us prioritize support for the models people are running locally.\n\n## \n\t\tAcknowledgments\n\t\n\nWe would like to thank [Arthur Zucker](https://huggingface.co/ArthurZ) for initiating this work and reviewing all of my PRs, and [Cyril Vallez](https://huggingface.co/cyrilvallez) for the `generate` PRs. We are grateful to [Sayak Paul](https://huggingface.co/sayakpaul), the [llama.cpp team](https://github.com/ggml-org/llama.cpp), and Bertrand Chevalier for their help integrating the kernels. We also thank [Aritra Roy Gosthipaty](https://huggingface.co/ariG23498) and [Pedro Cuenca](https://huggingface.co/pcuenq) for reviewing this blog post, and [Lysandre Debut](https://huggingface.co/lysandre) for overseeing the project.", "url": "https://wpnews.pro/news/transformers-now-runs-llama-cpp-quants", "canonical_source": "https://huggingface.co/blog/transformers-llama-cpp-quants", "published_at": "2026-09-22 00:00:00+00:00", "updated_at": "2026-09-22 10:52:29.339275+00:00", "lang": "en", "topics": ["large-language-models", "ai-infrastructure", "ai-tools", "developer-tools", "mlops"], "entities": ["Hugging Face", "transformers", "llama.cpp", "GGUF", "Qwen3.5", "Unsloth", "PyTorch", "Apple Silicon"], "alternates": {"html": "https://wpnews.pro/news/transformers-now-runs-llama-cpp-quants", "markdown": "https://wpnews.pro/news/transformers-now-runs-llama-cpp-quants.md", "text": "https://wpnews.pro/news/transformers-now-runs-llama-cpp-quants.txt", "jsonld": "https://wpnews.pro/news/transformers-now-runs-llama-cpp-quants.jsonld"}}