Conversion & Quantization
|
This content was generated with the assistance of AI and should be verified against the official documentation of each tool and framework before being relied on in production. This section’s bibliography lists the reference material consulted while preparing these pages. |
This page was written against transformers 5.17.0, optimum 2.3.0 (and, in a separate environment, optimum-onnx 0.1.0 with optimum 2.1.x), ONNX Runtime 1.30.0,
coremltools 9.0, litert-torch 0.9.4, LiteRT-LM 0.17.1, mlx-lm 0.31.3, bitsandbytes 0.50.2, llm-compressor 0.14.0
and llama.cpp v0.5.0, together with Apple’s Core AI documentation for iOS and macOS 27, as checked on 2026-09-30. Most models you need are already published in the format you want (see
Model Formats); you convert yourself when the model is new, when it is your
own fine-tune (see Fine-tuning with PEFT and TRL), or when you
need a quantization level nobody published. The theory of what quantization does to the weights, the scale and
zero-point formula, and the GGUF quant types is on
Quantization for Inference and is not repeated here; this page is
the tool-by-tool how-to.
A converted model is a derivative work: it inherits the licence and any gating terms of the original checkpoint (see
Choosing Models and Licences), so check the model card before
you publish a conversion. Converters also execute code from the checkpoint repository when you pass
trust_remote_code: leave it off unless you have read the code and pinned a revision, and prefer safetensors
checkpoints over pickled pytorch_model.bin files.
The conversion pipelines at a glance
Every path starts from the same place, a Hub checkpoint in safetensors, and ends in a runtime-specific artefact. The
diagram shows the tool that performs each step; the sections below give one command per path.
The same choice as a table, from the runtime back to the tool:
| Target runtime | Artefact | Tool that produces it |
|---|---|---|
llama.cpp, Ollama, LM Studio |
|
|
ONNX Runtime, Transformers.js |
|
|
Core ML (Apple, all chips) |
|
|
Core AI (iOS and macOS 27 and later) |
|
|
LiteRT, LiteRT-LM (Android, iOS, web) |
|
|
MLX (Apple silicon) |
MLX |
|
ExecuTorch (Android, iOS, microcontrollers) |
|
|
vLLM, SGLang, |
NF4, GPTQ, FP8 |
bitsandbytes, GPTQModel, llm-compressor |
GGUF
llama.cpp ships a Python converter and a C++ quantizer. The flow is two steps, with an optional calibration step in between: convert the checkpoint to a high-precision GGUF, optionally measure an importance matrix, then quantize.
hf download Qwen/Qwen3-0.6B --local-dir ./Qwen3-0.6B
# 1. Convert: safetensors -> 16-bit GGUF (run from a llama.cpp checkout)
python convert_hf_to_gguf.py ./Qwen3-0.6B --outfile qwen3-0.6b-bf16.gguf --outtype bf16
# 2. Quantize: 16-bit GGUF -> Q4_K_M
./llama-quantize qwen3-0.6b-bf16.gguf qwen3-0.6b-q4_k_m.gguf Q4_K_M
--outtype auto keeps the precision the checkpoint was stored in, which is what you want when the model is already
16-bit. The converter needs a model architecture that llama.cpp implements; an unsupported architecture fails at this
step, and the fix is a newer llama.cpp, not a flag.
An importance matrix (imatrix) records which weights matter most on a calibration text; the quantizer then spends its bits where they hurt least, which helps most at 4 bits and below. You measure it on the 16-bit GGUF and pass it to the quantizer:
./llama-imatrix -m qwen3-0.6b-bf16.gguf -f calibration.txt -o imatrix.gguf -ngl 99
./llama-quantize --imatrix imatrix.gguf qwen3-0.6b-bf16.gguf qwen3-0.6b-q4_k_m.gguf Q4_K_M
Use calibration text that resembles what the model will see (chat, code, or your domain language); a mismatched calibration set can make a quant worse for your use case than the plain one. Quant type names and their size and quality trade-offs are on Quantization for Inference, and the runtime on llama.cpp.
If you would rather not run anything locally, the GGUF-my-repo Space (ggml-org/gguf-my-repo) runs the same
conversion and quantization in the browser and pushes the result to your Hub account. It is convenient for one-off
conversions of models you are allowed to redistribute, and it needs a Hub token with write access, so sign in only to
the Space hosted under the ggml-org organisation.
References: llama.cpp — quantize, llama.cpp — imatrix and GGUF-my-repo.
ONNX
Three tools produce ONNX, and they differ in how much they do for you.
Optimum exports any supported transformers model to ONNX Runtime graphs. The exporter lives in the separate
optimum-onnx package; it validates the exported graph against the PyTorch model and, for decoder models, includes the
past-key-value cache by default.
pip install "optimum-onnx[onnxruntime]"
optimum-cli export onnx --model HuggingFaceTB/SmolLM2-360M-Instruct --dtype fp16 smollm2_onnx/
optimum-onnx 0.1.0, the latest release on PyPI on 2026-09-30, pins optimum~=2.1.0 and
transformers>=4.36,<4.58, so installing it downgrades a transformers 5 environment; run the export in a separate
virtual environment (for example python -m venv .venv-onnx) and keep the transformers 5 baseline in your main one.
from optimum.onnxruntime import ORTModelForCausalLM
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("smollm2_onnx")
model = ORTModelForCausalLM.from_pretrained("smollm2_onnx")
inputs = tokenizer("ONNX is", return_tensors="pt")
print(tokenizer.batch_decode(model.generate(**inputs, max_new_tokens=20)))
The ONNX Runtime GenAI model builder is the route for LLMs you will run with the onnxruntime-genai API (the
generate loop, KV cache and tokenizer handling included). It exports and quantizes in one command and selects kernels
for the execution provider you name:
pip install onnxruntime-genai
python -m onnxruntime_genai.models.builder \
-m HuggingFaceTB/SmolLM2-360M-Instruct -o ./smollm2-int4 -p int4 -e cpu -c ./cache
-p is the precision (fp32, fp16, bf16, int4, int8) and -e the execution provider (for
example cpu, cuda, dml or webgpu).
Olive is Microsoft’s optimisation toolkit for ONNX Runtime: it chains conversion, graph optimisation and quantization and targets a device and execution provider, which makes it the tool for NPUs and mixed hardware.
pip install olive-ai
olive optimize --model_name_or_path HuggingFaceTB/SmolLM2-360M-Instruct \
--device cpu --provider CPUExecutionProvider --precision int4
Olive’s command set has been renamed before (auto-opt earlier, optimize in the documentation checked on the page
date), so run olive --help against your installed version. For Transformers.js (see
Model Formats) the onnx-community repositories already contain the
multi-precision layout the library expects; mirror that layout if you publish your own.
References: Optimum ONNX — Export a model, ONNX Runtime GenAI model builder and Olive.
Core ML
coremltools converts a traced or exported PyTorch model to an ML Program (.mlpackage). Conversion is done at
half precision by default for ML Programs; compression is a separate, post-training step in coremltools.optimize
(linear 8-bit or 4-bit quantization, palettization, pruning).
import torch
import coremltools as ct
from coremltools.optimize.coreml import (
OpLinearQuantizerConfig, OptimizationConfig, linear_quantize_weights,
)
model = MyModel().eval() # any torch.nn.Module
example = torch.rand(1, 3, 224, 224)
traced = torch.jit.trace(model, example)
mlmodel = ct.convert(
traced,
inputs=[ct.TensorType(name="image", shape=example.shape)],
convert_to="mlprogram",
minimum_deployment_target=ct.target.iOS17,
compute_precision=ct.precision.FLOAT16,
)
config = OptimizationConfig(global_config=OpLinearQuantizerConfig(mode="linear_symmetric"))
compressed = linear_quantize_weights(mlmodel, config=config)
compressed.save("Model.mlpackage")
Large language models need extra care (stateful KV cache, flexible shapes) that the generic converter does not give you; for those, start from an existing Apple or community conversion or from the conversion recipes in Apple’s Core ML documentation, and compare the output with the PyTorch model before shipping. On the device, Core ML chooses CPU, GPU or Neural Engine per layer. The Apple-side integration is on Apple Platforms; Apple’s own frameworks are also covered in Apple Intelligence and Machine Learning.
References: coremltools — Converting from PyTorch and coremltools — Quantization.
Core AI
Core AI is Apple’s newer on-device stack for iOS and macOS 27 and later; its model asset is the .aimodel. According to
Apple’s documentation and the apple/coreai-torch repository, the authoring path is: export the PyTorch model with
torch.export, hand the resulting ExportedProgram to the coreai-torch package, which lowers ATen operations into
Core AI operations, optimise and compress the program (Apple publishes coreai-optimization for that), and save the
.aimodel. Apple also publishes coreai-models (apple/coreai-models on GitHub), a gallery of export recipes and
Swift runtime helpers for popular open models, where each model’s README gives the export command.
pip install coreai-torch coreai-core
import torch
model = MyModel().eval()
example = (torch.rand(1, 3, 224, 224),)
exported = torch.export.export(model, example) # the ExportedProgram coreai-torch consumes
The converter classes, option names and compression API of coreai-torch are deliberately not reproduced here: the
framework is new, the pages this sub-section could confirm on 2026-09-30 describe the workflow but not a stable
signature, and the API may change between releases. Follow the coreai-torch documentation for the conversion call and
the README of the model in coreai-models for a ready-made recipe. Because Core AI requires the newest operating
systems, keep a Core ML .mlpackage (previous section) as the fallback for older devices.
References: Apple — Core AI, coreai-torch and coreai-models.
LiteRT and LiteRT-LM
LiteRT is the successor to TensorFlow Lite, and litert-torch is the successor to ai-edge-torch: it converts PyTorch
models to .tflite for LiteRT on Android, iOS, embedded and web. For generative models it has a Hugging Face export
path that writes a .litertlm bundle, a single file containing the quantized prefill and decode graphs, token
embedder, tokenizer and LLM metadata, ready for the LiteRT-LM runtime on CPU, GPU or NPU.
pip install litert-torch
litert-torch export_hf --model=google/gemma-3-270m-it --output_dir=./gemma3-270m-litertlm
from litert_torch.generative.export_hf import export
export.export(
model="google/gemma-3-270m-it", # Hub ID or local safetensors directory
output_dir="./gemma3-270m-litertlm",
quantization_recipe="dynamic_wi8_afp32", # built-in recipe name or a JSON recipe path
)
Gated models such as Gemma need you to accept the licence on the Hub and authenticate (hf auth login) before the
export can download them. MediaPipe’s LLM Inference API is in maintenance; new Android and cross-platform projects
should target LiteRT-LM. Pre-converted bundles live under the litert-community organisation.
References: LiteRT — Convert PyTorch GenAI models and litert-torch.
MLX
mlx-lm converts and quantizes in one command and can push the result to the Hub, which is how the mlx-community
repositories are made. The output is MLX-format safetensors for Apple silicon.
pip install mlx-lm
mlx_lm.convert --hf-path Qwen/Qwen3-0.6B -q --q-bits 4 --q-group-size 64 --mlx-path ./qwen3-0.6b-4bit
from mlx_lm import load, generate
model, tokenizer = load("./qwen3-0.6b-4bit")
print(generate(model, tokenizer, prompt="MLX is", max_tokens=30))
-q turns quantization on; mlx_lm.convert --help lists the bit-width, group-size and upload options of your version.
MLX quantization is weight-only with a group scale, the same idea as the GPTQ and AWQ group sizes on
Quantization for Inference.
References: mlx-lm and mlx-community.
ExecuTorch
ExecuTorch is PyTorch’s on-device runtime. The pipeline exports the model with torch.export, lowers it to a backend
through a partitioner (XNNPACK for CPUs, Core ML for iOS, Vulkan and Qualcomm for Android GPUs and NPUs), and
serialises a .pte file.
import torch
from executorch.backends.xnnpack.partition.xnnpack_partitioner import XnnpackPartitioner
from executorch.exir import to_edge_transform_and_lower
model = MyModel().eval()
example = (torch.rand(1, 3, 224, 224),)
exported = torch.export.export(model, example)
program = to_edge_transform_and_lower(exported, partitioner=[XnnpackPartitioner()]).to_executorch()
with open("model.pte", "wb") as f:
f.write(program.buffer)
For Hub models, optimum-executorch wraps the export, including LLM-specific optimisations and quantization:
pip install optimum-executorch
optimum-cli export executorch --model HuggingFaceTB/SmolLM2-135M-Instruct --task text-generation \
--recipe xnnpack --use_custom_sdpa --use_custom_kv_cache --qlinear 8da4w --qembedding 8w \
--output_dir ./smollm2-executorch
The optimum-executorch README documents installing from source; check it for the current install route. --qlinear 8da4w means 8-bit dynamic activations with 4-bit weights for linear layers, and --qembedding 8w
quantizes the embeddings to 8-bit weights. Pre-exported .pte files are published by the pytorch and
software-mansion organisations; react-native-executorch (see
React Native) consumes them.
References: ExecuTorch — Getting started and optimum-executorch.
GPU-server quantization
The tools above target devices. To serve a model from a GPU with transformers, vLLM or SGLang (see
vLLM and SGLang) you use one of three libraries.
bitsandbytes quantizes at load time, with no calibration data and no separate step, through transformers. It is
the basis of QLoRA (see Fine-tuning with PEFT and TRL).
import torch
from transformers import AutoModelForCausalLM, BitsAndBytesConfig
bnb = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16,
bnb_4bit_use_double_quant=True,
)
model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen3-0.6B", quantization_config=bnb, device_map="auto")
GPTQModel runs calibrated GPTQ quantization (and other methods) and saves a checkpoint that transformers, vLLM and
SGLang load directly:
from gptqmodel import GPTQModel, GPTQConfig
calibration = [...] # a few hundred representative text samples
model = GPTQModel.load("Qwen/Qwen3-0.6B", GPTQConfig(bits=4, group_size=128))
model.quantize(calibration, batch_size=1)
model.save("Qwen3-0.6B-gptq-4bit")
Source: GPTQModel README, "Quantization" (GPTQModel 7.5.0 on PyPI
on 2026-09-30). GPTQConfig here is GPTQModel’s own class, imported from gptqmodel, not the transformers class of the
same name; the README keeps QuantizeConfig as the broad factory and uses the concrete GPTQConfig, AWQConfig,
FP8Config and similar classes for each method. Older GPTQModel releases passed a QuantizeConfig(bits=4, group_size=128)
instead, so match the example to the version you install.
llm-compressor (the vLLM project’s library, which absorbed the deprecated AutoAWQ) applies weight, activation and
KV-cache quantization and saves in the compressed-tensors format that vLLM reads. FP8 dynamic quantization needs no
calibration data (the PyPI package is named llmcompressor, installed with pip install llmcompressor):
from llmcompressor import oneshot
from llmcompressor.modifiers.quantization import QuantizationModifier
from transformers import AutoModelForCausalLM, AutoTokenizer
model_id = "Qwen/Qwen3-0.6B"
model = AutoModelForCausalLM.from_pretrained(model_id, dtype="auto")
tokenizer = AutoTokenizer.from_pretrained(model_id)
recipe = QuantizationModifier(targets="Linear", scheme="FP8_DYNAMIC", ignore=["lm_head"])
oneshot(model=model, recipe=recipe)
model.save_pretrained("Qwen3-0.6B-FP8-Dynamic")
tokenizer.save_pretrained("Qwen3-0.6B-FP8-Dynamic")
Pick bitsandbytes when you fine-tune or prototype, and GPTQModel or llm-compressor when you publish a checkpoint for a server. The formats and their quality trade-offs are on Quantization for Inference.
References: Transformers — bitsandbytes, GPTQModel and llm-compressor.
After converting
-
Compare the converted model with the original on a handful of prompts (or a small evaluation set) before you ship; conversions can silently change tokenizer, chat template or numerics.
-
Keep the tokenizer and chat template alongside the weights: GGUF embeds them, the other formats ship them as files.
-
Record the source
revision, the tool version and the quantization settings in the model card if you publish it. -
Publish with
hf uploadonly if the source licence allows redistribution, and keep the original licence file.
References
-
llama.cpp (quantize and imatrix READMEs under
tools/) -
LiteRT and litert-torch