Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
7f4a3a0
Add Nunchaku Lite single-file quantization
rootonchair Jul 1, 2026
1a66ac9
Support config-backed Nunchaku Lite loading
rootonchair Jul 2, 2026
36b4fc4
Remove Nunchaku runtime manifest metadata loading
rootonchair Jul 2, 2026
db05e0b
Simplify Nunchaku compact config loading
rootonchair Jul 2, 2026
5d4822a
Add Nunchaku Lite quantization tests
rootonchair Jul 2, 2026
c630a88
Document Nunchaku Lite checkpoint loading
rootonchair Jul 3, 2026
ab246af
Refine Nunchaku Lite quantization docs
rootonchair Jul 3, 2026
57323cd
Remove unused Nunchaku smooth factor original weights
rootonchair Jul 3, 2026
5fef94f
Update docs/source/en/quantization/nunchaku.md
rootonchair Jul 3, 2026
fb33430
Update src/diffusers/quantizers/nunchaku/utils.py
rootonchair Jul 3, 2026
8e14847
Update src/diffusers/quantizers/nunchaku/nunchaku_quantizer.py
rootonchair Jul 3, 2026
f5c0179
Address Nunchaku Lite review feedback
rootonchair Jul 3, 2026
8ba15ff
Update Nunchaku Lite quantizer
rootonchair Jul 6, 2026
661d6f7
Rename Nunchaku Lite fp4 precision to nvfp4
rootonchair Jul 7, 2026
1c58a6f
Allow torch_dtype to override Nunchaku compute dtype
rootonchair Jul 7, 2026
4c77a51
Keep Nunchaku replacement modules on meta during loading
rootonchair Jul 8, 2026
16a9cd0
Require kernels when importing Nunchaku runtime utils
rootonchair Jul 8, 2026
7b186f0
Use kernels requirement decorators for Nunchaku tests
rootonchair Jul 8, 2026
452de40
Validate Nunchaku CUDA capability requirements
rootonchair Jul 8, 2026
b849b18
Merge branch 'main' into feature/nunchaku-lite-single-file
sayakpaul Jul 9, 2026
9284dd0
Apply suggestions from code review
rootonchair Jul 9, 2026
4806585
docs: update nunchaku quantization guide
rootonchair Jul 9, 2026
d687751
test: remove nunchaku quantization tests
rootonchair Jul 9, 2026
86cd7c8
fix: reject hopper for nunchaku lite
rootonchair Jul 9, 2026
fd58893
Apply suggestion from @rootonchair
rootonchair Jul 9, 2026
f6897af
docs: update nunchaku lite compile example
rootonchair Jul 10, 2026
3df700a
Merge branch 'feature/nunchaku-lite-single-file' of github.com:rooton…
rootonchair Jul 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/source/en/_toctree.yml
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@
title: bitsandbytes
- local: quantization/gguf
title: gguf
- local: quantization/nunchaku
title: Nunchaku Lite
- local: quantization/torchao
title: torchao
- local: quantization/quanto
Expand Down
4 changes: 4 additions & 0 deletions docs/source/en/api/quantization.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ Quantization techniques reduce memory and computational costs by representing we

[[autodoc]] quantizers.quantization_config.GGUFQuantizationConfig

## NunchakuLiteQuantizationConfig

[[autodoc]] quantizers.quantization_config.NunchakuLiteQuantizationConfig

## QuantoConfig

[[autodoc]] quantizers.quantization_config.QuantoConfig
Expand Down
146 changes: 146 additions & 0 deletions docs/source/en/quantization/nunchaku.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
<!--Copyright 2026 The HuggingFace Team. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.

-->

# Nunchaku Lite

Nunchaku Lite is a quantization backend for loading prequantized checkpoints in Diffusers. Use

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should provide some citations to the original Nunchaku repo since Nunchaku Lite is based off of it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not only the repo, I will reference their paper too

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is resolved

[`diffuse-compressor`](https://github.com/rootonchair/diffuse-compressor) to create compatible checkpoints by choosing
or adapting a target config for the model architecture, quantizing and exporting the transformer, and packaging the
result as a Diffusers pipeline repository.

Nunchaku Lite builds on the original [Nunchaku](https://github.com/nunchaku-ai/nunchaku) inference engine,
[DeepCompressor](https://github.com/nunchaku-ai/deepcompressor) quantization library, and
[SVDQuant paper](https://arxiv.org/abs/2411.05007).

## Load a quantized pipeline

Load the packaged prequantized pipeline with [`~DiffusionPipeline.from_pretrained`]. Diffusers reads the quantization
config from `config.json`.

```python
import torch
from diffusers import DiffusionPipeline

model_id = "rootonchair/ERNIE-Image-Turbo-nunchaku-lite-nvfp4"

pipe = DiffusionPipeline.from_pretrained(
model_id, torch_dtype=torch.bfloat16,
).to("cuda")

prompt = "A modern red armchair in a quiet studio, soft window light, realistic product photography"
image = pipe(
prompt=prompt,
height=1024,
width=1024,
num_inference_steps=8,
guidance_scale=1.0,
use_pe=False,
).images[0]
image.save("ernie-image-turbo-nunchaku-lite.png")
```

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it compatible with offloading, too?


## Using Optimized CUDA Kernels with Nunchaku Lite

Nunchaku Lite uses optimized CUDA kernels through the `kernels` package. The kernels are loaded automatically when you
load a Nunchaku Lite checkpoint. Install the runtime dependency before loading a Nunchaku Lite checkpoint.

```bash
pip install -U kernels
```

> [!NOTE]
> The exported state dict must match the target Diffusers model architecture exactly. For example, a checkpoint
> quantized with fused QKV projections won't load into a model config that expects separate Q, K, and V projection
> modules.

## Supported Quantization Types

Nunchaku Lite supports the following quantized linear layer formats.

> [!TIP]
> Use `nvfp4` on Blackwell GPUs. Running `int4` checkpoints on Blackwell can be slower than `nvfp4`.

The CUDA kernels currently support the following NVIDIA GPU architectures:

- `sm_75` (Turing, for example RTX 2080)
- `sm_80` (Ampere, for example A100)
- `sm_86` (Ampere, for example RTX 3090 and RTX A6000)
- `sm_89` (Ada, for example RTX 4090)
- `sm_120` (Blackwell, for example RTX 5090)

> [!NOTE]
> Hopper GPUs, such as `sm_90` H100 and H200, are not currently supported.

`nvfp4` checkpoints require a Blackwell or newer NVIDIA GPU. On Blackwell GPUs, use PyTorch >= 2.7 with CUDA >= 12.8.
`int4` checkpoints require a Turing or newer NVIDIA GPU.

| Method | Precision | Group size | Notes |
|---|---:|---:|---|
| `svdq_w4a4` | `nvfp4` | 16 | Uses NVFP4 runtime kernels with SVDQ low-rank correction. |
| `svdq_w4a4` | `int4` | 64 | Uses INT4 W4A4 kernels with SVDQ low-rank correction. |
| `awq_w4a16` | `int4` | 64 | Uses INT4 weight-only AWQ-style kernels. |

## NunchakuLiteQuantizationConfig

The `config.json` file must include a compact [`NunchakuLiteQuantizationConfig`]. It defines the runtime
`compute_dtype` and the target modules for each Nunchaku Lite quantization method.

- `compute_dtype`: runtime dtype for floating-point buffers in quantized modules, typically `torch.bfloat16`.
- `svdq_w4a4`: SVDQ W4A4 target config with `precision`, `group_size`, `rank`, and `targets`.
- `awq_w4a16`: AWQ W4A16 target config with `precision`, `group_size`, and `targets`.

Each entry in `targets` must point to a linear layer. Diffusers replaces the linear layers listed under `svdq_w4a4`
with SVDQ W4A4 layers and the linear layers listed under `awq_w4a16` with AWQ W4A16 layers. The example below shows the
expected shape with shortened target lists.

> [!NOTE]
> A target module can only use one Nunchaku Lite quantization method. Do not list the same target under both
> `svdq_w4a4` and `awq_w4a16`.

```json
{
"_class_name": "ErnieImageTransformer2DModel",
"quantization_config": {
"quant_method": "nunchaku_lite",
"compute_dtype": "bfloat16",
"svdq_w4a4": {
"precision": "nvfp4",
"group_size": 16,
"rank": 32,
"targets": ["layers.0.self_attention.to_q"]
},
"awq_w4a16": {
"precision": "int4",
"group_size": 64,
"targets": ["final_linear"]
}
}
}
```

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we briefly summarize what would this config do? Would it apply a combination of svdq_w4a4 and awq_w4a16 to the targets?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have updated the details


## torch.compile

Nunchaku Lite kernels and quantized linear layers are compatible with [`torch.compile`](../optimization/fp16#torchcompile).
Compile the quantized transformer after loading the pipeline for faster inference.

```python
pipe.transformer = torch.compile(pipe.transformer, mode="default", fullgraph=True)
```

An ERNIE-Image-Turbo benchmark on an RTX PRO 6000 reported that Nunchaku Lite NVFP4 with `torch.compile` reduced the full pipeline latency from 2.271s to 1.675s. Compared to the original BF16 pipeline, the compiled
Nunchaku Lite NVFP4 pipeline reached a 1.8x speedup.

## Resources

- [diffuse-compressor](https://github.com/rootonchair/diffuse-compressor)
- [Nunchaku installation requirements](https://nunchaku.tech/docs/nunchaku/installation/installation.html)
2 changes: 1 addition & 1 deletion docs/source/en/quantization/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ There are two ways to use [`~quantizers.PipelineQuantizationConfig`] depending o

Initialize [`~quantizers.PipelineQuantizationConfig`] with the following parameters.

- `quant_backend` specifies which quantization backend to use. Currently supported backends include: `bitsandbytes_4bit`, `bitsandbytes_8bit`, `gguf`, `quanto`, and `torchao`.
- `quant_backend` specifies which quantization backend to use. Currently supported backends include: `bitsandbytes_4bit`, `bitsandbytes_8bit`, `gguf`, `nunchaku_lite`, `quanto`, and `torchao`.
- `quant_kwargs` specifies the quantization arguments to use.

> [!TIP]
Expand Down
20 changes: 20 additions & 0 deletions src/diffusers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,18 @@
else:
_import_structure["quantizers.quantization_config"].append("NVIDIAModelOptConfig")

try:
if not is_torch_available():
raise OptionalDependencyNotAvailable()
except OptionalDependencyNotAvailable:
from .utils import dummy_nunchaku_lite_objects

_import_structure["utils.dummy_nunchaku_lite_objects"] = [
name for name in dir(dummy_nunchaku_lite_objects) if not name.startswith("_")
]
else:
_import_structure["quantizers.quantization_config"].append("NunchakuLiteQuantizationConfig")

try:
if not is_auto_round_available():
raise OptionalDependencyNotAvailable()
Expand Down Expand Up @@ -1012,6 +1024,14 @@
else:
from .quantizers.quantization_config import NVIDIAModelOptConfig

try:
if not is_torch_available():
raise OptionalDependencyNotAvailable()
except OptionalDependencyNotAvailable:
from .utils.dummy_nunchaku_lite_objects import *
else:
from .quantizers.quantization_config import NunchakuLiteQuantizationConfig

try:
if not is_auto_round_available():
raise OptionalDependencyNotAvailable()
Expand Down
4 changes: 4 additions & 0 deletions src/diffusers/quantizers/auto.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@
from .bitsandbytes import BnB4BitDiffusersQuantizer, BnB8BitDiffusersQuantizer
from .gguf import GGUFQuantizer
from .modelopt import NVIDIAModelOptQuantizer
from .nunchaku import NunchakuLiteQuantizer
from .quantization_config import (
AutoRoundConfig,
BitsAndBytesConfig,
GGUFQuantizationConfig,
NunchakuLiteQuantizationConfig,
NVIDIAModelOptConfig,
QuantizationConfigMixin,
QuantizationMethod,
Expand All @@ -44,6 +46,7 @@
"torchao": TorchAoHfQuantizer,
"modelopt": NVIDIAModelOptQuantizer,
"auto-round": AutoRoundQuantizer,
"nunchaku_lite": NunchakuLiteQuantizer,
}

AUTO_QUANTIZATION_CONFIG_MAPPING = {
Expand All @@ -54,6 +57,7 @@
"torchao": TorchAoConfig,
"modelopt": NVIDIAModelOptConfig,
"auto-round": AutoRoundConfig,
"nunchaku_lite": NunchakuLiteQuantizationConfig,
}


Expand Down
1 change: 1 addition & 0 deletions src/diffusers/quantizers/nunchaku/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .nunchaku_quantizer import NunchakuLiteQuantizer
89 changes: 89 additions & 0 deletions src/diffusers/quantizers/nunchaku/nunchaku_quantizer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
from __future__ import annotations

from typing import TYPE_CHECKING, Any

from ..base import DiffusersQuantizer


if TYPE_CHECKING:
from ...models.modeling_utils import ModelMixin


from ...utils import is_kernels_available, logging


logger = logging.get_logger(__name__)


class NunchakuLiteQuantizer(DiffusersQuantizer):
def __init__(self, quantization_config, **kwargs):
super().__init__(quantization_config, **kwargs)
self.compute_dtype = quantization_config.compute_dtype
self.pre_quantized = quantization_config.pre_quantized

def validate_environment(self, *args, **kwargs):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: Hopper validation

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hopper validation added

if not is_kernels_available():
raise ImportError(
"Loading Nunchaku checkpoints requires the Hugging Face `kernels` package. "
"Install it with `pip install kernels`."
)
import torch

cuda_available = torch.cuda.is_available()
if not cuda_available:
raise ValueError("Loading Nunchaku checkpoints requires a CUDA-capable NVIDIA GPU.")

device_capability = torch.cuda.get_device_capability()

if device_capability[0] == 9:
raise ValueError("Loading Nunchaku checkpoints is not supported on Hopper NVIDIA GPUs.")

has_nvfp4_config = (
self.quantization_config.svdq_w4a4 is not None
and self.quantization_config.svdq_w4a4["precision"] == "nvfp4"
)
has_int4_config = any(
config is not None and config["precision"] == "int4"
for config in (self.quantization_config.svdq_w4a4, self.quantization_config.awq_w4a16)
)
if has_nvfp4_config and device_capability < (10, 0):
raise ValueError("Loading Nunchaku NVFP4 checkpoints requires a Blackwell or newer NVIDIA GPU.")
if has_int4_config and device_capability < (7, 5):
raise ValueError("Loading Nunchaku INT4 checkpoints on CUDA requires a Turing or newer NVIDIA GPU.")

def update_torch_dtype(self, torch_dtype):
if torch_dtype is None:
torch_dtype = self.compute_dtype
else:
self.compute_dtype = torch_dtype
return torch_dtype
Comment on lines +54 to +59

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the torch_dtype passed in from_pretrained don't match the compute_dtype specified in the quantization_config ? there will be an issue no ? Should be overwrite compute_dtype in that case ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes we should

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this is addressed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep


def _process_model_before_weight_loading(
self,
model: "ModelMixin",
state_dict: dict[str, Any] | None = None,
**kwargs,
):
from .utils import check_strict_state_dict_match, replace_with_nunchaku_linear

quantization_config = self.quantization_config.to_dict()
num_replaced = replace_with_nunchaku_linear(model, quantization_config, self.compute_dtype)

if state_dict is not None:
check_strict_state_dict_match(model, state_dict)
logger.info(f"Applied Nunchaku quantization config with {num_replaced} targets.")

def _process_model_after_weight_loading(self, model: "ModelMixin", **kwargs):
return model

@property
def is_serializable(self):
return False

@property
Comment thread
rootonchair marked this conversation as resolved.
def is_trainable(self) -> bool:
return False

@property
def is_compileable(self) -> bool:
return True
Loading
Loading