Quantization reduces model memory usage by storing weights at lower precision. diffusers-workflow ships examples for BitsAndBytes, TorchAO, GGUF, and SDNQ, applied per-component in the pipeline - and any other backend with a config class (optimum-quanto, for example) works through the same dynamic config_type import.
Quantize individual components (transformer, text encoder, etc.) independently:
{
"pipeline": {
"transformer": {
"configuration": { "component_type": "FluxTransformer2DModel" },
"quantization_config": {
"configuration": { "config_type": "..." },
"arguments": { ... }
},
"from_pretrained_arguments": {
"model_name": "...",
"subfolder": "transformer",
"torch_dtype": "torch.bfloat16"
}
},
"configuration": { "component_type": "FluxPipeline" },
"from_pretrained_arguments": {
"model_name": "...",
"torch_dtype": "torch.bfloat16"
}
}
}The component is loaded separately with quantization, then the rest of the pipeline loads around it.
4-bit and 8-bit quantization via bitsandbytes:
"quantization_config": {
"configuration": { "config_type": "BitsAndBytesConfig" },
"arguments": {
"load_in_4bit": true,
"bnb_4bit_quant_type": "{nf4}",
"bnb_4bit_compute_dtype": "torch.bfloat16"
}
}Note: "{nf4}" uses braces to keep the string literal. Without braces, the type system would try to load nf4 as a Python class.
For 8-bit:
"arguments": { "load_in_8bit": true }Example: flux2-dev.json (a pre-quantized 4-bit checkpoint)
Quantization via TorchAO. quant_type must be an AOBaseConfig class (diffusers no
longer accepts string shorthands like "int4wo"). Name the class as a dotted
quant_type and it is instantiated automatically with no arguments before being passed
to TorchAoConfig:
"quantization_config": {
"configuration": { "config_type": "TorchAoConfig" },
"arguments": {
"quant_type": "torchao.quantization.Int8WeightOnlyConfig",
"modules_to_not_convert": ["proj_in", "proj_out"]
}
}Common choices: Int8WeightOnlyConfig (any CUDA card), Int4WeightOnlyConfig (smallest),
Float8DynamicActivationFloat8WeightConfig (fastest, requires compute capability 8.9+ -
RTX 40-series/Ada or newer).
Example: flux-torchao.json
Pair TorchAO with torch.compile. Int8 weight-only and float8 dynamic-activation
quant types get their fused-kernel speedups only under compilation - uncompiled they are
a memory win but often a speed loss. Compiled is not a guarantee either: int8 weight-only
on an RTX 3090 measured over a minute per denoising step for Flux dev, compiled, against
two seconds for bf16 (see RECIPES_24GB.md). Measure on the
card before recording a TorchAO recipe as fast. Add a compile block to the quantized component
(see ACCELERATION.md):
"configuration": {
"components": {
"transformer": {
"compile": { "repeated_blocks": true, "fullgraph": true }
}
}
}Load GGUF-format checkpoint files:
"quantization_config": {
"configuration": { "config_type": "GGUFQuantizationConfig" },
"arguments": {
"compute_dtype": "torch.bfloat16"
}
}GGUF models load from single files using from_single_file:
"from_pretrained_arguments": {
"from_single_file": "https://huggingface.co/city96/FLUX.1-dev-gguf/blob/main/flux1-dev-Q2_K.gguf",
"torch_dtype": "torch.bfloat16"
}Example: flux-gguf.json
SDNQ works two ways: quantize a component on the fly at load time, or load a pre-quantized model as a complete pipeline.
Quantizes the component while it loads - the pattern the LTX-2 and MiniMax H3 examples use for their large transformers and text encoders:
"quantization_config": {
"configuration": { "config_type": "sdnq.SDNQConfig" },
"arguments": {
"weights_dtype": "{uint4}",
"quantization_device": "cuda",
"return_device": "cuda",
"use_quantized_matmul": true,
"dequantize_fp32": false
}
}weights_dtype— the storage dtype (uint4,int8, ...; brace-escaped so it stays a string)quantization_device/return_device— where the quantization pass runs and where the finished component lands; quantizing oncudais much faster than on CPUuse_quantized_matmul— quantized matmul kernels (CUDA/XPU only)
Examples: text-to-video.json, video-with-audio.json
Pre-quantized SDNQ repos load as complete pipelines. The sdnq module must be imported before loading so it can register with diffusers:
{
"pipeline": {
"configuration": {
"component_type": "ZImagePipeline",
"pre_load_modules": ["sdnq"],
"sdnq_optimize": ["transformer", "text_encoder"]
},
"from_pretrained_arguments": {
"model_name": "Disty0/Z-Image-Turbo-SDNQ-uint4-svd-r32",
"torch_dtype": "torch.bfloat16"
}
}
}pre_load_modules— Imports sdnq before pipeline loading (registers quantization method)sdnq_optimize— Applies quantized matmul to listed components (CUDA/XPU only, skipped on MPS/CPU)
Example: z-image-sdnq.json
A modular pipeline pulls its component weights itself via load_components() rather than
through from_pretrained_arguments, so quantization is keyed by component name under
load_components.quantization_config instead of living on a separate component block:
"configuration": {
"component_type": "MiniMaxMusic3ModularPipeline",
"load_components": {
"dtype": "torch.bfloat16",
"quantization_config": {
"transformer": {
"configuration": { "config_type": "TorchAoConfig" },
"arguments": { "quant_type": "torchao.quantization.Int8WeightOnlyConfig" }
},
"language_model": {
"configuration": { "config_type": "transformers.TorchAoConfig" },
"arguments": { "quant_type": "torchao.quantization.Int8WeightOnlyConfig" }
}
}
}
}A component the map does not name loads unquantized. Note that a transformers-based
component (a text encoder, for example) takes the transformers.TorchAoConfig class,
not the diffusers one - the config_type still resolves either via the dynamic import
described below.
Any quantization backend that provides a config class works via the config_type field with a dotted module path:
"quantization_config": {
"configuration": { "config_type": "some_package.SomeQuantConfig" },
"arguments": { ... }
}The class is loaded dynamically via importlib.
| Framework | CUDA | MPS | CPU |
|---|---|---|---|
| BitsAndBytes | Yes | No | No |
| TorchAO | Yes | Partial | No |
| GGUF | Yes | Yes | Yes |
| SDNQ (load) | Yes | Yes | Yes |
| SDNQ (optimize) | Yes | No | No |