Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
62ea54b
quartet
BlackSamorez Jun 9, 2025
dce76de
quartet qat -> quartet
BlackSamorez Jun 23, 2025
de7cdd8
format
BlackSamorez Jun 23, 2025
b5e2cfb
bf16 backward
BlackSamorez Jun 26, 2025
9632a75
interfaces
BlackSamorez Jun 27, 2025
7199b60
forward_method
BlackSamorez Jun 27, 2025
c2b5b29
quartet -> fp_quant
BlackSamorez Jul 1, 2025
55cbb2a
style
BlackSamorez Jul 1, 2025
d340904
Merge branch 'huggingface:main' into main
BlackSamorez Jul 1, 2025
72a6e4a
List -> list
BlackSamorez Jul 8, 2025
9d6c3d7
list typing
BlackSamorez Jul 14, 2025
7c38139
Merge branch 'main' of github.com:BlackSamorez/transformers
BlackSamorez Jul 14, 2025
53f1985
fixed format and annotations
BlackSamorez Jul 14, 2025
b7d7d3f
test_fp_quant
BlackSamorez Jul 14, 2025
c0b7bfc
docstrings and default dtypes
BlackSamorez Jul 14, 2025
294a465
Merge branch 'main' into main
SunMarc Jul 15, 2025
4dbbf10
better docstring and removed noop checks
BlackSamorez Jul 18, 2025
f666a49
docs
BlackSamorez Jul 18, 2025
4089500
pseudoquantization support to test on non-blackwell
BlackSamorez Jul 18, 2025
fc2d60d
pseudoquant
BlackSamorez Jul 18, 2025
02c924b
Pseudoquant docs
BlackSamorez Jul 18, 2025
f863273
Update docs/source/en/quantization/fp_quant.md
BlackSamorez Jul 18, 2025
c234f02
Update docs/source/en/quantization/fp_quant.md
BlackSamorez Jul 18, 2025
52c8bd1
Update docs/source/en/quantization/fp_quant.md
BlackSamorez Jul 18, 2025
709b2e6
Update src/transformers/utils/quantization_config.py
BlackSamorez Jul 22, 2025
f3df44b
Update tests/quantization/fp_quant_integration/test_fp_quant.py
BlackSamorez Jul 22, 2025
aa3d3c8
Update tests/quantization/fp_quant_integration/test_fp_quant.py
BlackSamorez Jul 22, 2025
76810b8
small test fixes
BlackSamorez Jul 22, 2025
e8f1192
dockerfile update
BlackSamorez Jul 22, 2025
1d31865
Merge branch 'main' of github.com:BlackSamorez/transformers
BlackSamorez Jul 22, 2025
b92ab57
spec link
BlackSamorez Jul 22, 2025
22ba0b4
removed `_process_model_after_weight_loading`
BlackSamorez Jul 22, 2025
ff03597
Merge branch 'main' into main
SunMarc Jul 22, 2025
235fc60
toctree
BlackSamorez Jul 22, 2025
5640282
Merge branch 'main' of github.com:BlackSamorez/transformers
BlackSamorez Jul 22, 2025
5f87d90
Merge branch 'main' into main
SunMarc Jul 23, 2025
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
3 changes: 3 additions & 0 deletions docker/transformers-quantization-latest-gpu/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ RUN git clone https://github.com/NetEase-FuXi/EETQ.git && cd EETQ/ && git submod
# RUN python3 -m pip install --no-cache-dir flute-kernel==0.4.1
# RUN python3 -m pip install --no-cache-dir git+https://github.com/Dao-AILab/fast-hadamard-transform.git

# Add fp-quant for quantization testing
RUN python3 -m pip install --no-cache-dir "fp-quant>=0.1.6"

# Add compressed-tensors for quantization testing
RUN python3 -m pip install --no-cache-dir compressed-tensors

Expand Down
2 changes: 2 additions & 0 deletions docs/source/en/_toctree.yml
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,8 @@
title: FBGEMM
- local: quantization/finegrained_fp8
title: Fine-grained FP8
- local: quantization/fp_quant
title: FP-Quant
- local: gguf
title: GGUF
- local: quantization/gptq
Expand Down
4 changes: 4 additions & 0 deletions docs/source/en/main_classes/quantization.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ Learn how to quantize models in the [Quantization](../quantization) guide.

[[autodoc]] QuarkConfig

## FPQuantConfig

[[autodoc]] FPQuantConfig

## AutoRoundConfig

[[autodoc]] AutoRoundConfig
66 changes: 66 additions & 0 deletions docs/source/en/quantization/fp_quant.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<!--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.

⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
rendered properly in your Markdown viewer.

-->

# FP-Quant

[FP-Quant](https://github.com/IST-DASLab/FP-Quant) is a family of quantization algorithms tailored for the Blackwell generation of Nvidia GPUs. The goal is to allow for efficient post-training quantization (PTQ) and quantization-aware trainin (QAT) of LLMs in the [MXFP4 and NVFP4 data-types](https://www.opencompute.org/documents/ocp-microscaling-formats-mx-v1-0-spec-final-pdf).

Currently, only PTQ with MXFP4 is supported. Models can either be quantized on the fly with `quantization_config=FPQuantConfig()`:

```python
from transformers import AutoModelForCausalLM, AutoTokenizer, FPQuantConfig
import torch

model = AutoModelForCausalLM.from_pretrained(
"qwen/Qwen3-8B",
quantization_config=FPQuantConfig(),
device_map="cuda",
torch_dtype=torch.bfloat16,
)
```

or pre-processed with GPTQ for better quality (see [FP Format Quantization Harness](https://github.com/IST-DASLab/FP-Quant)).

A **Blackwell-generation GPU is required** to run the kernels. Runtime support for FP-Quant is implemented through the [QuTLASS](https://github.com/IST-DASLab/qutlass) library and a lightweight PyTorch interface lib [`fp_quant`](https://github.com/IST-DASLab/FP-Quant/tree/master/inference_lib). We recommend installing the former **from source** and the latter with `pip install fp_quant`.

Users **without a Blackwell-generation GPU** , can use the method with `quantization_config=FPQuantConfig(pseudoquant=True)` without having to install [QuTLASS](https://github.com/IST-DASLab/qutlass). This would provide no speedups but would fully emulate the effect of quantization.
Comment thread
MekkCyber marked this conversation as resolved.

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.

instead of having the user to specify pseudoquant, can we do automatic fallback if the user don't have the required hardware ?

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.

maybe the following can help you

compute_capability = torch.cuda.get_device_capability()
major, minor = compute_capability

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 want users to be more conscious about it so I would rather leave it like this.


> [!TIP]
> Find models pre-quantized with FP-Quant in the official ISTA-DASLab [collection](https://huggingface.co/collections/ISTA-DASLab/fp-quant-6877c186103a21d3a02568ee).

## torch.compile

FP-Quant is fully compatible with [torch.compile](https://pytorch.org/tutorials/intermediate/torch_compile_tutorial.html).

```python
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer, FPQuantConfig

model = AutoModelForCausalLM.from_pretrained(
"qwen/Qwen3-8B",
quantization_config=FPQuantConfig(),
device_map="cuda",
torch_dtype=torch.bfloat16,
)

model.forward = torch.compile(model.forward, mode="max-autotune", fullgraph=True)
```

## Speedups

FP-Quant currently performs best for very large batch size processing.

See [QuTLASS README](https://github.com/IST-DASLab/qutlass/blob/main/README.md) for speedups.
1 change: 1 addition & 0 deletions docs/source/en/quantization/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Use the Space below to help you pick a quantization method depending on your har
| [bitsandbytes](./bitsandbytes) | 🟢 | 🟡 | 🟢 | 🟡 | 🔴 | 🟡 | 🟢 | 4/8 | 🟢 | 🟢 | 🟢 | https://github.com/bitsandbytes-foundation/bitsandbytes |
| [compressed-tensors](./compressed_tensors) | 🔴 | 🟢 | 🟢 | 🟢 | 🔴 | 🔴 | 🔴 | 1/8 | 🟢 | 🟢 | 🟢 | https://github.com/neuralmagic/compressed-tensors |
| [EETQ](./eetq) | 🟢 | 🔴 | 🟢 | 🔴 | 🔴 | 🔴 | ? | 8 | 🟢 | 🟢 | 🟢 | https://github.com/NetEase-FuXi/EETQ |
| [FP-Quant](./fp_quant) | 🟢 | 🔴 | 🟢 | 🔴 | 🔴 | 🔴 | 🟢 | 4 | 🔴 | 🟢 | 🟢 | https://github.com/IST-DASLab/FP-Quant |
| [GGUF / GGML (llama.cpp)](../gguf) | 🟢 | 🟢 | 🟢 | 🔴 | 🟢 | 🔴 | 🔴 | 1/8 | 🔴 | [See Notes](../gguf) | [See Notes](../gguf) | https://github.com/ggerganov/llama.cpp |
| [GPTQModel](./gptq) | 🔴 | 🟢 | 🟢 | 🟢 | 🟢 | 🟢 | 🔴 | 2/3/4/8 | 🟢 | 🟢 | 🟢 | https://github.com/ModelCloud/GPTQModel |
| [AutoGPTQ](./gptq) | 🔴 | 🔴 | 🟢 | 🟢 | 🔴 | 🔴 | 🔴 | 2/3/4/8 | 🟢 | 🟢 | 🟢 | https://github.com/AutoGPTQ/AutoGPTQ |
Expand Down
2 changes: 2 additions & 0 deletions src/transformers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@
"HqqConfig",
"QuantoConfig",
"QuarkConfig",
"FPQuantConfig",
"SpQRConfig",
"TorchAoConfig",
"VptqConfig",
Expand Down Expand Up @@ -961,6 +962,7 @@
EetqConfig,
FbgemmFp8Config,
FineGrainedFP8Config,
FPQuantConfig,
GPTQConfig,
HiggsConfig,
HqqConfig,
Expand Down
47 changes: 47 additions & 0 deletions src/transformers/integrations/fp_quant.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# 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.
"FP-Quant integration file"

from ..utils import (
is_fp_quant_available,
)


if is_fp_quant_available():
from fp_quant import FPQuantConfig as FPQuantLinearConfig
from fp_quant import FPQuantDtype

from transformers.utils.quantization_config import FPQuantConfig


def adapt_fp_quant_config(config: FPQuantConfig):
if config.forward_dtype == "mxfp4":
forward_dtype = FPQuantDtype.MXFP4
else:
raise ValueError(f"Unsupported forward dtype: {config.forward_dtype}")

if config.backward_dtype == "bf16":
backward_dtype = FPQuantDtype.BF16
else:
raise ValueError(f"Unsupported backward dtype: {config.backward_dtype}")

return FPQuantLinearConfig(
forward_dtype=forward_dtype,
forward_method=config.forward_method,
backward_dtype=backward_dtype,
store_master_weights=config.store_master_weights,
hadamard_group_size=config.hadamard_group_size,
pseudoquantization=config.pseudoquantization,
modules_to_not_convert=config.modules_to_not_convert,
)
4 changes: 4 additions & 0 deletions src/transformers/quantizers/auto.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
EetqConfig,
FbgemmFp8Config,
FineGrainedFP8Config,
FPQuantConfig,
GPTQConfig,
HiggsConfig,
HqqConfig,
Expand All @@ -49,6 +50,7 @@
from .quantizer_eetq import EetqHfQuantizer
from .quantizer_fbgemm_fp8 import FbgemmFp8HfQuantizer
from .quantizer_finegrained_fp8 import FineGrainedFP8HfQuantizer
from .quantizer_fp_quant import FPQuantHfQuantizer
from .quantizer_gptq import GptqHfQuantizer
from .quantizer_higgs import HiggsHfQuantizer
from .quantizer_hqq import HqqHfQuantizer
Expand All @@ -67,6 +69,7 @@
"aqlm": AqlmHfQuantizer,
"quanto": QuantoHfQuantizer,
"quark": QuarkHfQuantizer,
"fp_quant": FPQuantHfQuantizer,
"eetq": EetqHfQuantizer,
"higgs": HiggsHfQuantizer,
"hqq": HqqHfQuantizer,
Expand All @@ -89,6 +92,7 @@
"aqlm": AqlmConfig,
"quanto": QuantoConfig,
"quark": QuarkConfig,
"fp_quant": FPQuantConfig,
"hqq": HqqConfig,
"compressed-tensors": CompressedTensorsConfig,
"fbgemm_fp8": FbgemmFp8Config,
Expand Down
183 changes: 183 additions & 0 deletions src/transformers/quantizers/quantizer_fp_quant.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
# Copyright 2025 The HuggingFace Inc. 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.
from typing import TYPE_CHECKING, Any, Optional

from .base import HfQuantizer
from .quantizers_utils import get_module_from_name


if TYPE_CHECKING:
from ..modeling_utils import PreTrainedModel

from ..utils import is_fp_quant_available, is_qutlass_available, is_torch_available, logging
from ..utils.quantization_config import QuantizationConfigMixin


if is_torch_available():
import torch

logger = logging.get_logger(__name__)


class FPQuantHfQuantizer(HfQuantizer):
"""
Quantizer for the FP-Quant method. Enables the loading of prequantized models and in-flight quantization of full-precision models.
"""

requires_calibration = False
requires_parameters_quantization = True
is_qat_trainable = False
required_packages = ["fp_quant"]

def __init__(self, quantization_config: QuantizationConfigMixin, **kwargs):
super().__init__(quantization_config, **kwargs)
self.quantization_config = quantization_config

def validate_environment(self, device_map, **kwargs):
if not torch.cuda.is_available():
raise NotImplementedError(
"FPQuant quantization is only supported on GPU. Please use a different quantizer."
)

if not is_qutlass_available() and not self.quantization_config.pseudoquantization:
raise ImportError(
"Using `fp_quant` with real quantization requires a **Blackwell GPU** and qutlass: `git clone https://github.com/IST-DASLab/qutlass.git && cd qutlass && pip install --no-build-isolation .`. You can use `FPQuantConfig(pseudoquantization=True, ...)` to use Triton-based pseudo-quantization. It doesn't provide any speedups but emulates the quantization behavior of the real quantization."
)

if self.quantization_config.pseudoquantization:
logger.warning(
"Using pseudo-quantization for FP-Quant. This doesn't provide any speedups but emulates the quantization behavior of the real quantization."
)

if not is_fp_quant_available():
raise ImportError("Using `fp_quant` quantization requires fp_quant: `pip install fp_quant`")

if device_map is None:
raise ValueError(
"You are attempting to load a FPQuant model without setting device_map."
" Please set device_map comprised of 'cuda' devices."
)
elif isinstance(device_map, dict) and ("cpu" in device_map.values() or "disk" in device_map.values()):
raise ValueError(
"You are attempting to load a FPQuant model with a device_map that contains a CPU or disk device."
" This is not supported. Please remove the CPU or disk device from the device_map."
)

def update_torch_dtype(self, torch_dtype: "torch.dtype") -> "torch.dtype":
if torch_dtype is None:
logger.info("`torch_dtype` is None. Setting `torch_dtype=torch.bfloat16` for qutlass compatibility.")
torch_dtype = torch.bfloat16
elif torch_dtype != torch.bfloat16:
raise ValueError(
f"Invalid `torch_dtype` {torch_dtype}. fp_quant quantization only supports `torch_dtype=torch.bfloat16`."
)

return torch_dtype

def create_quantized_param(
self,
model: "PreTrainedModel",
param_value: "torch.Tensor",
param_name: str,
target_device: "torch.device",
state_dict: dict[str, Any],
unexpected_keys: Optional[list[str]] = None,
):
module, _ = get_module_from_name(model, param_name)

# The module holds either:
# * `weight` when `store_master_weights=True`
# * `qweight` and `scales` when `store_master_weights=False` and `pseudoquantization=False`
# * `dqweight` when `store_master_weights=False` and `pseudoquantization=True`

if param_name.endswith(".qweight"):
# Loading a real quantized checkpoint without master weights
module.qweight = torch.nn.Parameter(
param_value.to(target_device),
requires_grad=False,
)
module.weight = None
module.dqweight = None
return

if param_name.endswith(".dqweight"):
# Loading a pseudo-quantized checkpoint without master weights
module.dqweight = torch.nn.Parameter(param_value.to(target_device))
module.weight = None
module.qweight = None
module.scales = None
return

# Loading master weights or an unquantized checkpoint
module.weight = torch.nn.Parameter(param_value.to(target_device))
# Let pre-forward handle the quantization and set None where necessary
module.pre_forward()

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.

really nice to put all the quantization logic there


if unexpected_keys is not None and param_name in unexpected_keys:
unexpected_keys.remove(param_name)

def _process_model_before_weight_loading(
self,
model: "PreTrainedModel",
**kwargs,
):
from fp_quant import replace_with_fp_quant_linear

from ..integrations.fp_quant import adapt_fp_quant_config

replace_with_fp_quant_linear(
model,
fp_quant_linear_config=adapt_fp_quant_config(self.quantization_config),
)
model.config.quantization_config = self.quantization_config

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

def update_missing_keys(self, model, missing_keys: list[str], prefix: str) -> list[str]:
from fp_quant import FPQuantLinear

fp_quant_names = {name for name, module in model.named_modules() if isinstance(module, FPQuantLinear)}

def should_exclude(key: str) -> bool:
if key.endswith(".weight") or key.endswith(".bias"):
return False
full_key = f"{prefix}.{key}"
return any(name in key or name in full_key for name in fp_quant_names)

return [key for key in missing_keys if not should_exclude(key)]

@property
def is_trainable(self, model: Optional["PreTrainedModel"] = None):
return False

def is_serializable(self, safe_serialization=None):
return True

def check_quantized_param(
self,
model: "PreTrainedModel",
param_value: "torch.Tensor",
param_name: str,
state_dict: dict[str, Any],
**kwargs,
) -> bool:
from fp_quant import FPQuantLinear

module, tensor_name = get_module_from_name(model, param_name)
if isinstance(module, FPQuantLinear) and tensor_name in ["weight", "qweight", "dqweight"]:
# Only quantize weights of FPQuantLinear modules that are not already quantized
return True
else:
return False
Loading