Skip to content

Commit 60ec6f7

Browse files
rootonchairsergereview[bot]sayakpaulgithub-actions[bot]stevhliu
authored
Add Nunchaku Lite single-file quantization (#14100)
* Add Nunchaku Lite single-file quantization * Support config-backed Nunchaku Lite loading * Remove Nunchaku runtime manifest metadata loading * Simplify Nunchaku compact config loading * Add Nunchaku Lite quantization tests * Document Nunchaku Lite checkpoint loading * Refine Nunchaku Lite quantization docs * Remove unused Nunchaku smooth factor original weights * Update docs/source/en/quantization/nunchaku.md Co-authored-by: sergereview[bot] <283583894+sergereview[bot]@users.noreply.github.com> * Update src/diffusers/quantizers/nunchaku/utils.py Co-authored-by: sergereview[bot] <283583894+sergereview[bot]@users.noreply.github.com> * Update src/diffusers/quantizers/nunchaku/nunchaku_quantizer.py Co-authored-by: sergereview[bot] <283583894+sergereview[bot]@users.noreply.github.com> * Address Nunchaku Lite review feedback * Update Nunchaku Lite quantizer * Rename Nunchaku Lite fp4 precision to nvfp4 * Allow torch_dtype to override Nunchaku compute dtype * Keep Nunchaku replacement modules on meta during loading * Require kernels when importing Nunchaku runtime utils * Use kernels requirement decorators for Nunchaku tests * Validate Nunchaku CUDA capability requirements * Apply suggestions from code review Co-authored-by: Sayak Paul <spsayakpaul@gmail.com> * docs: update nunchaku quantization guide * test: remove nunchaku quantization tests * fix: reject hopper for nunchaku lite * Apply suggestion from @rootonchair * docs: update nunchaku lite compile example * Apply style fixes * Apply suggestions from code review Co-authored-by: Steven Liu <59462357+stevhliu@users.noreply.github.com> Co-authored-by: Vinh H. Pham <phamvinh257@gmail.com> * update doc to be more reasonable * fix import that requires kernels --------- Co-authored-by: sergereview[bot] <283583894+sergereview[bot]@users.noreply.github.com> Co-authored-by: Sayak Paul <spsayakpaul@gmail.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Steven Liu <59462357+stevhliu@users.noreply.github.com>
1 parent deb1738 commit 60ec6f7

13 files changed

Lines changed: 879 additions & 0 deletions

File tree

docs/source/en/_toctree.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,8 @@
174174
title: bitsandbytes
175175
- local: quantization/gguf
176176
title: gguf
177+
- local: quantization/nunchaku
178+
title: Nunchaku Lite
177179
- local: quantization/torchao
178180
title: torchao
179181
- local: quantization/quanto

docs/source/en/api/quantization.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ Quantization techniques reduce memory and computational costs by representing we
3030

3131
[[autodoc]] quantizers.quantization_config.GGUFQuantizationConfig
3232

33+
## NunchakuLiteQuantizationConfig
34+
35+
[[autodoc]] quantizers.quantization_config.NunchakuLiteQuantizationConfig
36+
3337
## QuantoConfig
3438

3539
[[autodoc]] quantizers.quantization_config.QuantoConfig
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
<!--Copyright 2026 The HuggingFace Team. All rights reserved.
2+
3+
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
4+
the License. You may obtain a copy of the License at
5+
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
8+
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
9+
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
10+
specific language governing permissions and limitations under the License.
11+
12+
-->
13+
14+
# Nunchaku Lite
15+
16+
Nunchaku Lite is a quantization backend for loading prequantized checkpoints in Diffusers. Create compatible checkpoints with [diffuse-compressor](https://github.com/rootonchair/diffuse-compressor). It quantizes and exports a transformer, then packages it as a Diffusers pipeline.
17+
18+
Nunchaku Lite builds on the original [Nunchaku](https://github.com/nunchaku-ai/nunchaku) inference engine,
19+
[DeepCompressor](https://github.com/nunchaku-ai/deepcompressor) quantization library, and
20+
[SVDQuant paper](https://arxiv.org/abs/2411.05007).
21+
22+
## Install the CUDA kernels
23+
24+
The kernels package supplies the optimized CUDA kernels, which load automatically. Install it first.
25+
26+
```bash
27+
pip install -U kernels
28+
```
29+
30+
## Load a quantized pipeline
31+
32+
Load the prequantized pipeline with [`~DiffusionPipeline.from_pretrained`], which reads the quantization
33+
config from `config.json`.
34+
35+
```python
36+
import torch
37+
from diffusers import DiffusionPipeline
38+
39+
model_id = "rootonchair/ERNIE-Image-Turbo-nunchaku-lite-nvfp4"
40+
41+
pipe = DiffusionPipeline.from_pretrained(
42+
model_id, torch_dtype=torch.bfloat16,
43+
).to("cuda")
44+
45+
prompt = "A modern red armchair in a quiet studio, soft window light, realistic product photography"
46+
image = pipe(
47+
prompt=prompt,
48+
height=1024,
49+
width=1024,
50+
num_inference_steps=8,
51+
guidance_scale=1.0,
52+
).images[0]
53+
image.save("ernie-image-turbo-nunchaku-lite.png")
54+
```
55+
56+
> [!NOTE]
57+
> The exported state dict must match the target Diffusers model architecture exactly. For example, a checkpoint
58+
> quantized with fused QKV projections won't load into a model config that expects separate Q, K, and V projection
59+
> modules.
60+
61+
## Supported quantization types
62+
63+
Nunchaku Lite supports the following quantized linear layer formats.
64+
65+
> [!TIP]
66+
> Use `nvfp4` on Blackwell GPUs. Running `int4` checkpoints on Blackwell can be slower than `nvfp4`.
67+
68+
The CUDA kernels currently support the following NVIDIA GPU architectures:
69+
70+
- `sm_75` (Turing, for example RTX 2080)
71+
- `sm_80` (Ampere, for example A100)
72+
- `sm_86` (Ampere, for example RTX 3090 and RTX A6000)
73+
- `sm_89` (Ada, for example RTX 4090)
74+
- `sm_120` (Blackwell, for example RTX 5090)
75+
76+
> [!NOTE]
77+
> Hopper GPUs, such as `sm_90` H100 and H200, are not currently supported.
78+
79+
`nvfp4` checkpoints require a Blackwell or newer NVIDIA GPU. On Blackwell GPUs, use PyTorch >= 2.7 with CUDA >= 12.8.
80+
`int4` checkpoints require a Turing or newer NVIDIA GPU.
81+
82+
| Method | Precision | Group size | Notes |
83+
|---|---:|---:|---|
84+
| `svdq_w4a4` | `nvfp4` | 16 | Uses NVFP4 runtime kernels with SVDQ low-rank correction. |
85+
| `svdq_w4a4` | `int4` | 64 | Uses INT4 W4A4 kernels with SVDQ low-rank correction. |
86+
| `awq_w4a16` | `int4` | 64 | Uses INT4 weight-only AWQ-style kernels. |
87+
88+
## NunchakuLiteQuantizationConfig
89+
90+
The `config.json` file must include a [`NunchakuLiteQuantizationConfig`]. It defines the runtime
91+
`compute_dtype` and the target modules for each Nunchaku Lite quantization method.
92+
93+
- `compute_dtype`: runtime dtype for floating-point buffers in quantized modules, typically `torch.bfloat16`.
94+
- `svdq_w4a4`: SVDQ W4A4 target config with `precision`, `group_size`, `rank`, and `targets`.
95+
- `awq_w4a16`: AWQ W4A16 target config with `precision`, `group_size`, and `targets`.
96+
97+
Each entry in `targets` must point to a linear layer. Diffusers swaps each `svdq_w4a4` target for an SVDQ W4A4 layer and each `awq_w4a16` target for an AWQ W4A16 layer. The example below shows the
98+
expected shape with shortened target lists.
99+
100+
List each module you want to quantize under `svdq_w4a4` or `awq_w4a16`. A module can only use one method, so don't list the same target under both.
101+
102+
```json
103+
{
104+
"_class_name": "ErnieImageTransformer2DModel",
105+
"quantization_config": {
106+
"quant_method": "nunchaku_lite",
107+
"compute_dtype": "bfloat16",
108+
"svdq_w4a4": {
109+
"precision": "nvfp4",
110+
"group_size": 16,
111+
"rank": 32,
112+
"targets": ["layers.0.self_attention.to_q"]
113+
},
114+
"awq_w4a16": {
115+
"precision": "int4",
116+
"group_size": 64,
117+
"targets": ["final_linear"]
118+
}
119+
}
120+
}
121+
```
122+
123+
## torch.compile
124+
125+
Nunchaku Lite kernels and quantized linear layers are compatible with [`torch.compile`](../optimization/fp16#torchcompile).
126+
Compile the quantized transformer after loading the pipeline for faster inference.
127+
128+
```python
129+
pipe.transformer = torch.compile(pipe.transformer, mode="default", fullgraph=True)
130+
```
131+
132+
The compiled Nunchaku Lite NVFP4 pipeline runs 1.8x faster than the original BF16 pipeline (2.271s → 1.675s on an RTX PRO 6000).
133+
134+
## Resources
135+
136+
- [diffuse-compressor](https://github.com/rootonchair/diffuse-compressor)
137+
- [Nunchaku installation requirements](https://nunchaku.tech/docs/nunchaku/installation/installation.html)

docs/source/en/quantization/overview.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,30 @@ pipe = DiffusionPipeline.from_pretrained(
125125
image = pipe("photo of a cute dog").images[0]
126126
```
127127

128+
## Loading prequantized checkpoints
129+
130+
Some quantization backends support loading checkpoints that were already quantized and uploaded to the Hub. In this case, the quantization configuration is stored in the model's `config.json` and is read automatically — you do not need to create a [`~quantizers.PipelineQuantizationConfig`] or pass a `quantization_config` argument.
131+
132+
```py
133+
import torch
134+
from diffusers import DiffusionPipeline
135+
136+
pipe = DiffusionPipeline.from_pretrained(
137+
"rootonchair/ERNIE-Image-Turbo-nunchaku-lite-nvfp4",
138+
torch_dtype=torch.bfloat16,
139+
).to("cuda")
140+
```
141+
142+
The following backends support loading prequantized checkpoints out of the box.
143+
144+
| Backend | Notes |
145+
|---|---|
146+
| [bitsandbytes](./bitsandbytes) | Config is saved in `config.json`; no extra arguments needed. |
147+
| [GGUF](./gguf) | Uses `from_single_file` with Model classes; pipeline-level loading is not supported. |
148+
| [AutoRound](./autoround) | Only loading is supported; quantize first with the AutoRound CLI or Python API. |
149+
| [Nunchaku Lite](./nunchaku) | Config is saved in `config.json`; requires the `kernels` package. |
150+
| [ModelOpt](./modelopt) | Supports both quantizing on the fly and loading prequantized models. |
151+
128152
## Resources
129153

130154
Check out the resources below to learn more about quantization.

src/diffusers/__init__.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,18 @@
124124
else:
125125
_import_structure["quantizers.quantization_config"].append("NVIDIAModelOptConfig")
126126

127+
try:
128+
if not is_torch_available():
129+
raise OptionalDependencyNotAvailable()
130+
except OptionalDependencyNotAvailable:
131+
from .utils import dummy_nunchaku_lite_objects
132+
133+
_import_structure["utils.dummy_nunchaku_lite_objects"] = [
134+
name for name in dir(dummy_nunchaku_lite_objects) if not name.startswith("_")
135+
]
136+
else:
137+
_import_structure["quantizers.quantization_config"].append("NunchakuLiteQuantizationConfig")
138+
127139
try:
128140
if not is_auto_round_available():
129141
raise OptionalDependencyNotAvailable()
@@ -1014,6 +1026,14 @@
10141026
else:
10151027
from .quantizers.quantization_config import NVIDIAModelOptConfig
10161028

1029+
try:
1030+
if not is_torch_available():
1031+
raise OptionalDependencyNotAvailable()
1032+
except OptionalDependencyNotAvailable:
1033+
from .utils.dummy_nunchaku_lite_objects import *
1034+
else:
1035+
from .quantizers.quantization_config import NunchakuLiteQuantizationConfig
1036+
10171037
try:
10181038
if not is_auto_round_available():
10191039
raise OptionalDependencyNotAvailable()

src/diffusers/quantizers/auto.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,12 @@
2222
from .bitsandbytes import BnB4BitDiffusersQuantizer, BnB8BitDiffusersQuantizer
2323
from .gguf import GGUFQuantizer
2424
from .modelopt import NVIDIAModelOptQuantizer
25+
from .nunchaku import NunchakuLiteQuantizer
2526
from .quantization_config import (
2627
AutoRoundConfig,
2728
BitsAndBytesConfig,
2829
GGUFQuantizationConfig,
30+
NunchakuLiteQuantizationConfig,
2931
NVIDIAModelOptConfig,
3032
QuantizationConfigMixin,
3133
QuantizationMethod,
@@ -44,6 +46,7 @@
4446
"torchao": TorchAoHfQuantizer,
4547
"modelopt": NVIDIAModelOptQuantizer,
4648
"auto-round": AutoRoundQuantizer,
49+
"nunchaku_lite": NunchakuLiteQuantizer,
4750
}
4851

4952
AUTO_QUANTIZATION_CONFIG_MAPPING = {
@@ -54,6 +57,7 @@
5457
"torchao": TorchAoConfig,
5558
"modelopt": NVIDIAModelOptConfig,
5659
"auto-round": AutoRoundConfig,
60+
"nunchaku_lite": NunchakuLiteQuantizationConfig,
5761
}
5862

5963

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .nunchaku_quantizer import NunchakuLiteQuantizer
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
from __future__ import annotations
2+
3+
from typing import TYPE_CHECKING, Any
4+
5+
from ..base import DiffusersQuantizer
6+
7+
8+
if TYPE_CHECKING:
9+
from ...models.modeling_utils import ModelMixin
10+
11+
12+
from ...utils import is_kernels_available, logging
13+
14+
15+
logger = logging.get_logger(__name__)
16+
17+
18+
class NunchakuLiteQuantizer(DiffusersQuantizer):
19+
def __init__(self, quantization_config, **kwargs):
20+
super().__init__(quantization_config, **kwargs)
21+
self.compute_dtype = quantization_config.compute_dtype
22+
self.pre_quantized = quantization_config.pre_quantized
23+
24+
def validate_environment(self, *args, **kwargs):
25+
if not is_kernels_available():
26+
raise ImportError(
27+
"Loading Nunchaku checkpoints requires the Hugging Face `kernels` package. "
28+
"Install it with `pip install kernels`."
29+
)
30+
import torch
31+
32+
cuda_available = torch.cuda.is_available()
33+
if not cuda_available:
34+
raise ValueError("Loading Nunchaku checkpoints requires a CUDA-capable NVIDIA GPU.")
35+
36+
device_capability = torch.cuda.get_device_capability()
37+
38+
if device_capability[0] == 9:
39+
raise ValueError("Loading Nunchaku checkpoints is not supported on Hopper NVIDIA GPUs.")
40+
41+
has_nvfp4_config = (
42+
self.quantization_config.svdq_w4a4 is not None
43+
and self.quantization_config.svdq_w4a4["precision"] == "nvfp4"
44+
)
45+
has_int4_config = any(
46+
config is not None and config["precision"] == "int4"
47+
for config in (self.quantization_config.svdq_w4a4, self.quantization_config.awq_w4a16)
48+
)
49+
if has_nvfp4_config and device_capability < (10, 0):
50+
raise ValueError("Loading Nunchaku NVFP4 checkpoints requires a Blackwell or newer NVIDIA GPU.")
51+
if has_int4_config and device_capability < (7, 5):
52+
raise ValueError("Loading Nunchaku INT4 checkpoints on CUDA requires a Turing or newer NVIDIA GPU.")
53+
54+
def update_torch_dtype(self, torch_dtype):
55+
if torch_dtype is None:
56+
torch_dtype = self.compute_dtype
57+
else:
58+
self.compute_dtype = torch_dtype
59+
return torch_dtype
60+
61+
def _process_model_before_weight_loading(
62+
self,
63+
model: "ModelMixin",
64+
state_dict: dict[str, Any] | None = None,
65+
**kwargs,
66+
):
67+
from .utils import check_strict_state_dict_match, replace_with_nunchaku_linear
68+
69+
quantization_config = self.quantization_config.to_dict()
70+
num_replaced = replace_with_nunchaku_linear(model, quantization_config, self.compute_dtype)
71+
72+
if state_dict is not None:
73+
check_strict_state_dict_match(model, state_dict)
74+
logger.info(f"Applied Nunchaku quantization config with {num_replaced} targets.")
75+
76+
def _process_model_after_weight_loading(self, model: "ModelMixin", **kwargs):
77+
return model
78+
79+
@property
80+
def is_serializable(self):
81+
return False
82+
83+
@property
84+
def is_trainable(self) -> bool:
85+
return False
86+
87+
@property
88+
def is_compileable(self) -> bool:
89+
return True

0 commit comments

Comments
 (0)