Files
Zhou TaichangandClaude Opus 5 2ce15ab7a0 [Rebase] Rebase to vllm 0.27.0 (#5976)
Signed-off-by: tzhouam <tzhouam@connect.ust.hk>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-11 15:23:46 +00:00

179 lines
5.9 KiB
Python

# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
"""BitsAndBytes 4-bit quantization config for diffusion transformers.
Supports online (dynamic) NF4/FP4 weight-only quantization from BF16/FP16
checkpoints on CUDA GPUs.
"""
from typing import TYPE_CHECKING, Any, Optional
import torch
from torch.nn import Module
from vllm.model_executor.layers.linear import (
LinearBase,
LinearMethodBase,
UnquantizedLinearMethod,
)
from vllm.model_executor.layers.quantization import QuantizationMethods
from vllm.model_executor.layers.quantization.base_config import (
QuantizationConfig,
QuantizeMethodBase,
)
from vllm.model_executor.layers.quantization.utils.quant_utils import (
is_layer_skipped,
)
from vllm.model_executor.model_loader.weight_utils import initialize_single_dummy_weight
from vllm.model_executor.parameter import ModelWeightParameter
from vllm.model_executor.utils import replace_parameter
from vllm_omni.platforms import current_omni_platform
from vllm_omni.quantization._copy_missing_attrs import (
copy_missing_attrs as _copy_missing_attrs,
)
from vllm_omni.quantization.int8_config import LazyWeightMixin
if TYPE_CHECKING:
from vllm.model_executor.models.utils import WeightsMapper
class DiffusionBitsAndBytesConfig(QuantizationConfig):
"""BitsAndBytes 4-bit weight-only config for diffusion transformers.
Supports online (dynamic) quantization from BF16/FP16 checkpoints.
Works on CUDA GPUs with the optional ``bitsandbytes`` package installed.
"""
def __init__(
self,
quant_type: str = "nf4",
compress_statistics: bool = True,
ignored_layers: list[str] | None = None,
) -> None:
super().__init__()
if quant_type not in ("nf4", "fp4"):
raise ValueError(f"Unsupported quant_type {quant_type!r}; expected 'nf4' or 'fp4'")
self.quant_type = quant_type
self.compress_statistics = compress_statistics
self.ignored_layers = ignored_layers or []
@classmethod
def get_name(cls) -> QuantizationMethods:
return "bitsandbytes"
@classmethod
def get_supported_act_dtypes(cls) -> list[torch.dtype]:
return [torch.bfloat16, torch.float16]
@classmethod
def get_min_capability(cls) -> int:
return 75
@classmethod
def get_config_filenames(cls) -> list[str]:
return []
def apply_vllm_mapper(self, hf_to_vllm_mapper: "WeightsMapper"):
if self.ignored_layers is not None:
self.ignored_layers = hf_to_vllm_mapper.apply_list(self.ignored_layers)
@classmethod
def from_config(cls, config: dict[str, Any]) -> "DiffusionBitsAndBytesConfig":
quant_type = cls.get_from_keys_or(config, ["quant_type"], "nf4")
compress_statistics = cls.get_from_keys_or(config, ["compress_statistics"], True)
ignored_layers = cls.get_from_keys_or(config, ["ignored_layers"], None)
if not ignored_layers:
ignored_layers = cls.get_from_keys_or(config, ["modules_to_not_convert"], None)
return cls(
quant_type=quant_type,
compress_statistics=compress_statistics,
ignored_layers=ignored_layers,
)
def get_quant_method(
self,
layer: torch.nn.Module,
prefix: str,
) -> Optional["QuantizeMethodBase"]:
if isinstance(layer, LinearBase):
if is_layer_skipped(
prefix=prefix,
ignored_layers=self.ignored_layers,
fused_mapping=self.packed_modules_mapping,
):
return UnquantizedLinearMethod()
if current_omni_platform.is_cuda():
return BnBOnlineLinearMethod(self)
raise NotImplementedError("BitsAndBytes online quantization is only supported on CUDA.")
return None
class BnBOnlineLinearMethod(LazyWeightMixin, LinearMethodBase):
"""Online BitsAndBytes 4-bit linear method.
Loads BF16/FP16 checkpoint weights and quantizes them during loading.
"""
def __init__(self, quant_config: DiffusionBitsAndBytesConfig):
self.quant_config = quant_config
def process_weights_after_loading(self, layer: Module) -> None:
if getattr(layer, "_already_called_process_weights_after_loading", False):
return
if layer.weight.device == torch.device("meta"):
weight = ModelWeightParameter(
data=torch.empty_like(layer.weight, device=layer._load_device),
input_dim=1,
output_dim=0,
weight_loader=layer.weight.weight_loader,
)
_copy_missing_attrs(layer.weight, weight)
layer.register_parameter("weight", weight)
initialize_single_dummy_weight(layer.weight)
import bitsandbytes.functional as bnb_F
weight = layer.weight.data.contiguous()
if not weight.is_cuda:
weight = weight.cuda()
original_shape = tuple(weight.shape)
qweight, quant_state = bnb_F.quantize_4bit(
weight,
quant_type=self.quant_config.quant_type,
compress_statistics=self.quant_config.compress_statistics,
)
replace_parameter(
layer,
"weight",
torch.nn.Parameter(qweight, requires_grad=False),
)
layer.quant_state = quant_state
layer.bnb_shape = original_shape
layer._already_called_process_weights_after_loading = True
def apply(
self,
layer: torch.nn.Module,
x: torch.Tensor,
bias: torch.Tensor | None = None,
) -> torch.Tensor:
import bitsandbytes as bnb
ori_shape = x.shape
ori_dtype = x.dtype
x_2d = x.reshape(-1, ori_shape[-1])
out = bnb.matmul_4bit(
x_2d,
layer.weight.t(),
quant_state=layer.quant_state,
bias=bias,
)
return out.reshape(*ori_shape[:-1], -1).to(ori_dtype)