Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 7.3k
[Quantization] Add Quanto backend#10756
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
ff50418ba5bba7aa8cdaf39e20e2f52050af4c14c2f67d97c5cff237f734c097472f18e96686e4ae86917b841dce090177559f1249e5a3d0b136d232c7f303c80d4d4d355e6a9a72fef79901e4c4b6e24c29684f6cf9a780736f874eabed7f512c28dbaef7c963559f156db084516f22830b7348afff1b8163687bb7fb666cad1d5d5ab9cadeebc22cf4694e1b46a32File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,148 @@ | ||
| <!--Copyright 2025 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. | ||
| --> | ||
| # Quanto | ||
| [Quanto](https://github.com/huggingface/optimum-quanto) is a PyTorch quantization backend for [Optimum](https://huggingface.co/docs/optimum/en/index). It has been designed with versatility and simplicity in mind: | ||
| - All features are available in eager mode (works with non-traceable models) | ||
| - Supports quantization aware training | ||
| - Quantized models are compatible with `torch.compile` | ||
| - Quantized models are Device agnostic (e.g CUDA,XPU,MPS,CPU) | ||
| In order to use the Quanto backend, you will first need to install `optimum-quanto>=0.2.6` and `accelerate` | ||
| ```shell | ||
| pip install optimum-quanto accelerate | ||
| ``` | ||
| Now you can quantize a model by passing the `QuantoConfig` object to the `from_pretrained()` method. Although the Quanto library does allow quantizing `nn.Conv2d` and `nn.LayerNorm` modules, currently, Diffusers only supports quantizing the weights in the `nn.Linear` layers of a model. The following snippet demonstrates how to apply `float8` quantization with Quanto. | ||
| ```python | ||
| import torch | ||
| from diffusers import FluxTransformer2DModel, QuantoConfig | ||
| model_id = "black-forest-labs/FLUX.1-dev" | ||
| quantization_config = QuantoConfig(weights_dtype="float8") | ||
| transformer = FluxTransformer2DModel.from_pretrained( | ||
| model_id, | ||
| subfolder="transformer", | ||
| quantization_config=quantization_config, | ||
| torch_dtype=torch.bfloat16, | ||
| ) | ||
| pipe = FluxPipeline.from_pretrained(model_id, transformer=transformer, torch_dtype=torch_dtype) | ||
| pipe.to("cuda") | ||
| prompt = "A cat holding a sign that says hello world" | ||
| image = pipe( | ||
| prompt, num_inference_steps=50, guidance_scale=4.5, max_sequence_length=512 | ||
| ).images[0] | ||
| image.save("output.png") | ||
| ``` | ||
| ## Skipping Quantization on specific modules | ||
| It is possible to skip applying quantization on certain modules using the `modules_to_not_convert` argument in the `QuantoConfig`. Please ensure that the modules passed in to this argument match the keys of the modules in the `state_dict` | ||
| ```python | ||
| import torch | ||
| from diffusers import FluxTransformer2DModel, QuantoConfig | ||
| model_id = "black-forest-labs/FLUX.1-dev" | ||
| quantization_config = QuantoConfig(weights_dtype="float8", modules_to_not_convert=["proj_out"]) | ||
| transformer = FluxTransformer2DModel.from_pretrained( | ||
| model_id, | ||
| subfolder="transformer", | ||
| quantization_config=quantization_config, | ||
| torch_dtype=torch.bfloat16, | ||
| ) | ||
| ``` | ||
| ## Using `from_single_file` with the Quanto Backend | ||
| `QuantoConfig` is compatible with `~FromOriginalModelMixin.from_single_file`. | ||
| ```python | ||
| import torch | ||
| from diffusers import FluxTransformer2DModel, QuantoConfig | ||
| ckpt_path = "https://huggingface.co/black-forest-labs/FLUX.1-dev/blob/main/flux1-dev.safetensors" | ||
| quantization_config = QuantoConfig(weights_dtype="float8") | ||
| transformer = FluxTransformer2DModel.from_single_file(ckpt_path, quantization_config=quantization_config, torch_dtype=torch.bfloat16) | ||
| ``` | ||
| ## Saving Quantized models | ||
| Diffusers supports serializing Quanto models using the `~ModelMixin.save_pretrained` method. | ||
| The serialization and loading requirements are different for models quantized directly with the Quanto library and models quantized | ||
| with Diffusers using Quanto as the backend. It is currently not possible to load models quantized directly with Quanto into Diffusers using `~ModelMixin.from_pretrained` | ||
| ```python | ||
| import torch | ||
| from diffusers import FluxTransformer2DModel, QuantoConfig | ||
| model_id = "black-forest-labs/FLUX.1-dev" | ||
| quantization_config = QuantoConfig(weights_dtype="float8") | ||
| transformer = FluxTransformer2DModel.from_pretrained( | ||
| model_id, | ||
| subfolder="transformer", | ||
| quantization_config=quantization_config, | ||
| torch_dtype=torch.bfloat16, | ||
| ) | ||
| # save quantized model to reuse | ||
| transformer.save_pretrained("<your quantized model save path>") | ||
| # you can reload your quantized model with | ||
| model = FluxTransformer2DModel.from_pretrained("<your quantized model save path>") | ||
| ``` | ||
| ## Using `torch.compile` with Quanto | ||
| Currently the Quanto backend supports `torch.compile` for the following quantization types: | ||
| - `int8` weights | ||
| ```python | ||
| import torch | ||
| from diffusers import FluxPipeline, FluxTransformer2DModel, QuantoConfig | ||
| model_id = "black-forest-labs/FLUX.1-dev" | ||
| quantization_config = QuantoConfig(weights_dtype="int8") | ||
| transformer = FluxTransformer2DModel.from_pretrained( | ||
| model_id, | ||
| subfolder="transformer", | ||
| quantization_config=quantization_config, | ||
| torch_dtype=torch.bfloat16, | ||
| ) | ||
| transformer = torch.compile(transformer, mode="max-autotune", fullgraph=True) | ||
Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great that this works. | ||
| pipe = FluxPipeline.from_pretrained( | ||
| model_id, transformer=transformer, torch_dtype=torch_dtype | ||
| ) | ||
| pipe.to("cuda") | ||
| images = pipe("A cat holding a sign that says hello").images[0] | ||
| images.save("flux-quanto-compile.png") | ||
| ``` | ||
| ## Supported Quantization Types | ||
| ### Weights | ||
| - float8 | ||
| - int8 | ||
| - int4 | ||
| - int2 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -128,6 +128,10 @@ | ||
| "GitPython<3.1.19", | ||
| "scipy", | ||
| "onnx", | ||
| "optimum_quanto>=0.2.6", | ||
| "gguf>=0.10.0", | ||
| "torchao>=0.7.0", | ||
| "bitsandbytes>=0.43.3", | ||
DN6 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| "regex!=2019.12.17", | ||
| "requests", | ||
| "tensorboard", | ||
| @@ -235,6 +239,11 @@ def run(self): | ||
| ) | ||
| extras["torch"] = deps_list("torch", "accelerate") | ||
| extras["bitsandbytes"] = deps_list("bitsandbytes", "accelerate") | ||
| extras["gguf"] = deps_list("gguf", "accelerate") | ||
| extras["optimum_quanto"] = deps_list("optimum_quanto", "accelerate") | ||
| extras["torchao"] = deps_list("torchao", "accelerate") | ||
| if os.name == "nt": # windows | ||
| extras["flax"] = [] # jax is not supported on windows | ||
| else: | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -2,6 +2,15 @@ | ||
| from typing import TYPE_CHECKING | ||
| from diffusers.quantizers import quantization_config | ||
| from diffusers.utils import dummy_gguf_objects | ||
| from diffusers.utils.import_utils import ( | ||
| is_bitsandbytes_available, | ||
| is_gguf_available, | ||
| is_optimum_quanto_version, | ||
| is_torchao_available, | ||
| ) | ||
| from .utils import ( | ||
| DIFFUSERS_SLOW_IMPORT, | ||
| OptionalDependencyNotAvailable, | ||
| @@ -11,6 +20,7 @@ | ||
| is_librosa_available, | ||
| is_note_seq_available, | ||
| is_onnx_available, | ||
| is_optimum_quanto_available, | ||
| is_scipy_available, | ||
| is_sentencepiece_available, | ||
| is_torch_available, | ||
| @@ -32,7 +42,7 @@ | ||
| "loaders": ["FromOriginalModelMixin"], | ||
| "models": [], | ||
| "pipelines": [], | ||
| "quantizers.quantization_config": ["BitsAndBytesConfig", "GGUFQuantizationConfig", "TorchAoConfig"], | ||
DN6 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| "quantizers.quantization_config": [], | ||
| "schedulers": [], | ||
| "utils": [ | ||
| "OptionalDependencyNotAvailable", | ||
| @@ -54,6 +64,55 @@ | ||
| ], | ||
| } | ||
| try: | ||
| if not is_bitsandbytes_available(): | ||
| raise OptionalDependencyNotAvailable() | ||
| except OptionalDependencyNotAvailable: | ||
| from .utils import dummy_bitsandbytes_objects | ||
| _import_structure["utils.dummy_bitsandbytes_objects"] = [ | ||
| name for name in dir(dummy_bitsandbytes_objects) if not name.startswith("_") | ||
| ] | ||
| else: | ||
| _import_structure["quantizers.quantization_config"].append("BitsAndBytesConfig") | ||
| try: | ||
| if not is_gguf_available(): | ||
| raise OptionalDependencyNotAvailable() | ||
| except OptionalDependencyNotAvailable: | ||
| from .utils import dummy_gguf_objects | ||
| _import_structure["utils.dummy_gguf_objects"] = [ | ||
| name for name in dir(dummy_gguf_objects) if not name.startswith("_") | ||
| ] | ||
| else: | ||
| _import_structure["quantizers.quantization_config"].append("GGUFQuantizationConfig") | ||
| try: | ||
| if not is_torchao_available(): | ||
| raise OptionalDependencyNotAvailable() | ||
| except OptionalDependencyNotAvailable: | ||
| from .utils import dummy_torchao_objects | ||
| _import_structure["utils.dummy_torchao_objects"] = [ | ||
| name for name in dir(dummy_torchao_objects) if not name.startswith("_") | ||
| ] | ||
| else: | ||
| _import_structure["quantizers.quantization_config"].append("TorchAoConfig") | ||
| try: | ||
| if not is_optimum_quanto_available(): | ||
| raise OptionalDependencyNotAvailable() | ||
| except OptionalDependencyNotAvailable: | ||
| from .utils import dummy_optimum_quanto_objects | ||
| _import_structure["utils.dummy_optimum_quanto_objects"] = [ | ||
| name for name in dir(dummy_optimum_quanto_objects) if not name.startswith("_") | ||
| ] | ||
| else: | ||
| _import_structure["quantizers.quantization_config"].append("QuantoConfig") | ||
| try: | ||
| if not is_onnx_available(): | ||
| raise OptionalDependencyNotAvailable() | ||
| @@ -598,7 +657,38 @@ | ||
| if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT: | ||
| from .configuration_utils import ConfigMixin | ||
| from .quantizers.quantization_config import BitsAndBytesConfig, GGUFQuantizationConfig, TorchAoConfig | ||
| try: | ||
| if not is_bitsandbytes_available(): | ||
| raise OptionalDependencyNotAvailable() | ||
| except OptionalDependencyNotAvailable: | ||
| from .utils.dummy_bitsandbytes_objects import * | ||
| else: | ||
| from .quantizers.quantization_config import BitsAndBytesConfig | ||
| try: | ||
| if not is_gguf_available(): | ||
| raise OptionalDependencyNotAvailable() | ||
| except OptionalDependencyNotAvailable: | ||
| from .utils.dummy_gguf_objects import * | ||
| else: | ||
| from .quantizers.quantization_config import GGUFQuantizationConfig | ||
| try: | ||
| if not is_torchao_available(): | ||
| raise OptionalDependencyNotAvailable() | ||
| except OptionalDependencyNotAvailable: | ||
| from .utils.dummy_torchao_objects import * | ||
| else: | ||
| from .quantizers.quantization_config import TorchAoConfig | ||
| try: | ||
| if not is_optimum_quanto_available(): | ||
| raise OptionalDependencyNotAvailable() | ||
| except OptionalDependencyNotAvailable: | ||
| from .utils.dummy_optimum_quanto_objects import * | ||
| else: | ||
| from .quantizers.quantization_config import QuantoConfig | ||
| try: | ||
| if not is_onnx_available(): | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -245,6 +245,9 @@ def load_model_dict_into_meta( | ||
| ): | ||
| param = param.to(torch.float32) | ||
| set_module_kwargs["dtype"] = torch.float32 | ||
| # For quantizers have save weights using torch.float8_e4m3fn | ||
| elif hf_quantizer is not None and param.dtype == getattr(torch, "float8_e4m3fn", None): | ||
| pass | ||
Comment on lines
+249
to
+250
Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How would the param be handled in that case? CollaboratorAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't wouldn't apply any casting and the parameter would be loaded as is into the model. | ||
| else: | ||
| param = param.to(dtype) | ||
| set_module_kwargs["dtype"] = dtype | ||
| @@ -292,7 +295,9 @@ def load_model_dict_into_meta( | ||
| elif is_quantized and ( | ||
| hf_quantizer.check_if_quantized_param(model, param, param_name, state_dict, param_device=param_device) | ||
| ): | ||
| hf_quantizer.create_quantized_param(model, param, param_name, param_device, state_dict, unexpected_keys) | ||
| hf_quantizer.create_quantized_param( | ||
| model, param, param_name, param_device, state_dict, unexpected_keys, dtype=dtype | ||
| ) | ||
| else: | ||
| set_module_tensor_to_device(model, param_name, param_device, value=param, **set_module_kwargs) | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have we verified this? Last time I checked only weight-quantized models were compatible with
torch.compile. Cc: @dacorvo.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True, but this should be fixed in pytorch 2.6 (I did not check though).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dacorvo I tried to run torch compile with float8 weights in the following way and hit an error during inference
Traceback:
The
torch.compilestep seems to work. The error is raised during the forward pass.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same with nightly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah same errors with nightly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's be specific that only int8 supports torch.compile for now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mentioned in the compile section of the docs
diffusers/docs/source/en/quantization/quanto.md
Line 105 in bb7fb66