SD.Next Quantization provides full cross-platform quantization to reduce memory usage and increase performance for any device.
pip install openvino).diffusers>=0.40.0.For more info, please see the SD.Next SDNQ Wiki page: https://github.com/vladmandic/sdnext/wiki/SDNQ-Quantization
You can also check out the Diffusers SDNQ Docs page: https://huggingface.co/docs/diffusers/main/en/quantization/sdnq
pip install sdnq
Pre-quantized models can be found here: https://huggingface.co/collections/Disty0/sdnq
import torch
from sdnq import SDNQConfig # import sdnq to register it into transformers
pipe_or_quantized_model = AutoModel.from_pretrained(model_path, torch_dtype=torch.bfloat16)
from sdnq.loader import apply_sdnq_options_to_model
quantized_model = apply_sdnq_options_to_model(quantized_model, use_quantized_matmul=True)
For more information about the options, see SDNQ Wiki and SDNQConfig docstring.
from sdnq import SDNQConfig
from sdnq.common import use_torch_compile as triton_is_available
sdnq_config = SDNQConfig(
weights_dtype="int8", # see `sdnq.common.accepted_weight_dtypes` for all the supported dtypes.
quantized_matmul_dtype=None, # overrides the quantized matmul dtype to be different than weights_dtype format.
group_size=0, # 0 means auto, -1 means row-wise, -2 means tensor-wise
hadamard_group_size=256,
svd_rank=32,
svd_steps=8,
codebook_steps=24,
dynamic_loss_threshold=None, # None or negative number means auto select based on weights_dtype
use_svd=False,
use_hadamard=False,
use_codebook=False,
quant_conv=False,
quant_embedding=False,
use_quantized_matmul=triton_is_available, # use quantized matmul (False means no quantized matmul at all)
use_quantized_matmul_conv=False,
use_dynamic_quantization=False, # dynamically select a per layer quantization type based on the dynamic_loss_threshold
dequantize_fp32=True, # keeps the quant scales in FP32 and compute the de-quant steps in FP32. Highly recommended to enable this option
non_blocking=False,
add_skip_keys=True,
minimum_allowed_numel=16384, # layers that have less than minimum_allowed_numel elements in them will be skipped and added to modules_to_not_convert
minimum_allowed_channel_size=32, # layers that have less than minimum_allowed_channel_size channels in them will be skipped and added to modules_to_not_convert
modules_to_not_convert=["correction_coefs", "prediction_coefs", "lm_head", "embedding_projection"],
modules_to_not_use_matmul=["x_embedder"],
modules_dtype_dict={"int8": ["lm_head"]},
modules_quant_config={"embed_tokens_per_layer": {"quantization_device": "cpu"}},
quantization_device="cuda",
return_device="cuda",
)
quantized_model = AutoModel.from_pretrained(model_path, quantization_config=sdnq_config)
pipe_or_quantized_model.save_pretrained("path_to_save_the_quantized_model")
from sdnq import sdnq_post_load_quant
model = sdnq_post_load_quant(
model,
**kwargs_are_the_same_as_SDNQConfig,
)
from functools import wraps
# inference only kernel:
from sdnq.kernels.triton_atten import sdnq_triton_atten
# for training:
#from sdnq.kernels.triton_atten_backward import sdnq_triton_atten_with_backward as sdnq_triton_atten
sdpa_pre_sdnq_atten = torch.nn.functional.scaled_dot_product_attention
@wraps(sdpa_pre_sdnq_atten)
def sdpa_sdnq_atten(query: torch.FloatTensor, key: torch.FloatTensor, value: torch.FloatTensor, attn_mask: torch.Tensor | None = None, dropout_p: float = 0.0, is_causal: bool = False, scale: float | None = None, enable_gqa: bool = False, **kwargs) -> torch.FloatTensor:
if (
query.device.type != "cpu"
and (query.shape[-2] >= 32 and key.shape[-2] >= 32)
and (query.shape[-2] > 512 or key.shape[-2] > 512) # Skip TE
and query.shape[-3] > 1 # Skip VAE
):
return sdnq_triton_atten(
query=query, key=key, value=value, attn_mask=attn_mask,
is_causal=is_causal, scale=scale, enable_gqa=enable_gqa,
matmul_dtype="int8", # can be one of "disabled", "int8", "float8_e4m3fn", "float16".
pv_matmul_dtype="disabled", # can be one of "disabled", "int8", "float8_e4m3fn", "float16".
smooth_k=True,
use_hadamard=False,
hadamard_group_size=256,
do_quantize=True, # Set this to False to disable the quantized matmul usage
quantize_fp32=True, # Set this to False to disable upcasting to FP32 when quantizing
use_fp16_accum=False, # Set this to True to use FP16 accumulaton with matmul_dtype="float16" and pv_matmul_dtype="float16" or "disabled"
out_dtype=None, # Set this to a torch.dtype like torch.float32 if you want the output dtype to be different than inputs
)
else:
if enable_gqa:
kwargs["enable_gqa"] = enable_gqa
return sdpa_pre_sdnq_atten(query=query, key=key, value=value, attn_mask=attn_mask, dropout_p=dropout_p, is_causal=is_causal, scale=scale, **kwargs)
torch.nn.functional.scaled_dot_product_attention = sdpa_sdnq_atten
Block-sparse attention: sdnq_triton_atten also takes block_mask, a bool or int8 tensor of shape (batch or 1, heads or 1, ceil(QN / block_mask_m), ceil(KN / block_mask_n)) with block_mask_m and block_mask_n giving the block size in tokens. The kernel walks only the kept key blocks of each query block, the mask composes with attn_mask, and every autotune tile has to divide the block size (the default tile lists nest a 128x64 block). sdnq_triton_atten_with_backward takes the same three arguments and differentiates through the same selection.
For more information about the options, see SDNQ Wiki and SDNQConfig docstring.
Note:
scripts/dequantize_sdnq_training.py to dequantize an SDNQ Training model saved to the disk.from sdnq.training import sdnq_training_post_load_quant
from sdnq.common import use_torch_compile as triton_is_available
quantized_model = sdnq_training_post_load_quant(
model,
weights_dtype="uint8", # Check out `sdnq.common.accepted_weight_dtypes` for all the supported dtypes.
quantized_matmul_dtype=None, # overrides the quantized matmul dtype to be different than weights_dtype format.
group_size=32, # 0 means auto, -1 means row-wise, -2 means tensor-wise
hadamard_group_size=256,
svd_rank=32,
svd_steps=8,
codebook_steps=24,
use_svd=False,
use_hadamard=False,
use_codebook=False,
use_grad_ckpt=True, # disable this if you are not using gradient checkpointing
use_quantized_matmul=triton_is_available, # use quantized matmul on the forward pass and the backward pass (False means no quantized matmul at all)
use_static_quantization=True, # quantize the model weights (False means model weights will be kept unquantized and only quantized matmul (if enabled) will be used)
use_stochastic_rounding=True,
dequantize_fp32=True, # keeps the quant scales in FP32 and compute the de-quant steps in FP32. Highly recommended to enable this option
non_blocking=False,
add_skip_keys=True,
minimum_allowed_numel=16384, # layers that have less than minimum_allowed_numel elements in them will be skipped and added to modules_to_not_convert
minimum_allowed_channel_size=32, # layers that have less than minimum_allowed_channel_size channels in them will be skipped and added to modules_to_not_convert
modules_to_not_convert=["correction_coefs", "prediction_coefs", "lm_head", "embedding_projection"],
modules_to_not_use_matmul=["x_embedder"],
modules_dtype_dict={"int8": ["lm_head"]},
modules_quant_config={"embed_tokens_per_layer": {"quantization_device": "cpu"}},
quantization_device="cuda",
return_device="cuda",
)
from sdnq.training import convert_sdnq_model_to_training
from sdnq.common import use_torch_compile as triton_is_available
quantized_model = convert_sdnq_model_to_training(
quantized_model,
quantized_matmul_dtype=None, # overrides the quantized matmul dtype to be different than weights_dtype format.
use_grad_ckpt=True, # disable this if you are not using gradient checkpointing
use_quantized_matmul=triton_is_available, # use quantized matmul on the forward pass and the backward pass (False means no quantized matmul at all)
use_stochastic_rounding=True,
dequantize_fp32=True, # keeps the quant scales in FP32 and compute the de-quant steps in FP32. Highly recommended to enable this option
)
from sdnq.training import convert_training_model_to_sdnq
quantized_model = convert_training_model_to_sdnq(quantized_model)
from sdnq.optim import Adafactor, AdamW, CAME, Lion, Muon
optimizer = AdamW(
parameters,
use_quantized_buffers=True,
quantized_buffers_dtype="uint8",
quantized_buffers_group_size=32,
quantized_buffers_hadamard_group_size=256,
quantized_buffers_svd_rank=32,
quantized_buffers_svd_steps=8,
quantized_buffers_codebook_steps=24,
quantized_buffers_use_svd=False,
quantized_buffers_use_hadamard=False,
quantized_buffers_use_codebook=False,
final_norm_mode="clip", # can be one of ["none", "clip", "rms", "rms_clip", "relative", "muon"]
use_kahan=False,
use_cautious=False,
use_stochastic_rounding=True,
use_stochastic_buffers=True,
use_torch_compile=False,
offload_buffers=False,
offload_non_blocking=True,
)
from sdnq.training import SDNQTensor
state["exp_avg"] = SDNQTensor.from_float(
torch.zeros_like(p),
weights_dtype="int8",
group_size=32,
hadamard_group_size=256,
svd_rank=32,
svd_steps=8,
codebook_steps=24,
use_svd=False,
use_hadamard=False,
use_codebook=False,
use_stochastic_rounding=True,
dequantize_fp32=True, # keeps the quant scales in FP32 and compute the de-quant steps in FP32. Highly recommended to enable this option
layer_class_name=None, # can be "Linear", "Conv2d" etc.
)
0 or 1. Default is None (auto-detect)0 or 1. Default is None (auto-detect)0 or 1. Default is None (auto-detect)0 to disable fused SDNQ Triton MM kernels and use the regular unfused Triton MM kernels instead.0 or 1. Default is 11 to enable FP16 accumulation with FP16 matmul on SDNQ Triton MM kernels.0 or 1. Default is 0pip install openvino.0 or 1. Default is None (auto-detect)CPU. Default is HETERO:NPU,CPU if NPU is available else CPU0 or 1. Default is None (auto-detect)0 or 1. Default is 0 if Fused MM Kernels are in use, else 1.use_dynamic_quantization option and within the apply_sdnq_options_to_module function.0 or 1. Default is None (auto-detect)0 or 1. Default is None (auto-detect)0 or 1. Default is 0SDNQ_COMPILE_KWARGS is an advanced option, don't touch this if you don't know exactly what you are doing.{"fullgraph": true}. Default is None (auto-detect)mps. Default is None (auto-detect)bfloat16. Default is None (auto-detect)Python
100.0%
SD.Next Quantization provides full cross-platform quantization to reduce memory usage and increase performance for any device.
pip install openvino).diffusers>=0.40.0.For more info, please see the SD.Next SDNQ Wiki page: https://github.com/vladmandic/sdnext/wiki/SDNQ-Quantization
You can also check out the Diffusers SDNQ Docs page: https://huggingface.co/docs/diffusers/main/en/quantization/sdnq
pip install sdnq
Pre-quantized models can be found here: https://huggingface.co/collections/Disty0/sdnq
import torch
from sdnq import SDNQConfig # import sdnq to register it into transformers
pipe_or_quantized_model = AutoModel.from_pretrained(model_path, torch_dtype=torch.bfloat16)
from sdnq.loader import apply_sdnq_options_to_model
quantized_model = apply_sdnq_options_to_model(quantized_model, use_quantized_matmul=True)
For more information about the options, see SDNQ Wiki and SDNQConfig docstring.
from sdnq import SDNQConfig
from sdnq.common import use_torch_compile as triton_is_available
sdnq_config = SDNQConfig(
weights_dtype="int8", # see `sdnq.common.accepted_weight_dtypes` for all the supported dtypes.
quantized_matmul_dtype=None, # overrides the quantized matmul dtype to be different than weights_dtype format.
group_size=0, # 0 means auto, -1 means row-wise, -2 means tensor-wise
hadamard_group_size=256,
svd_rank=32,
svd_steps=8,
codebook_steps=24,
dynamic_loss_threshold=None, # None or negative number means auto select based on weights_dtype
use_svd=False,
use_hadamard=False,
use_codebook=False,
quant_conv=False,
quant_embedding=False,
use_quantized_matmul=triton_is_available, # use quantized matmul (False means no quantized matmul at all)
use_quantized_matmul_conv=False,
use_dynamic_quantization=False, # dynamically select a per layer quantization type based on the dynamic_loss_threshold
dequantize_fp32=True, # keeps the quant scales in FP32 and compute the de-quant steps in FP32. Highly recommended to enable this option
non_blocking=False,
add_skip_keys=True,
minimum_allowed_numel=16384, # layers that have less than minimum_allowed_numel elements in them will be skipped and added to modules_to_not_convert
minimum_allowed_channel_size=32, # layers that have less than minimum_allowed_channel_size channels in them will be skipped and added to modules_to_not_convert
modules_to_not_convert=["correction_coefs", "prediction_coefs", "lm_head", "embedding_projection"],
modules_to_not_use_matmul=["x_embedder"],
modules_dtype_dict={"int8": ["lm_head"]},
modules_quant_config={"embed_tokens_per_layer": {"quantization_device": "cpu"}},
quantization_device="cuda",
return_device="cuda",
)
quantized_model = AutoModel.from_pretrained(model_path, quantization_config=sdnq_config)
pipe_or_quantized_model.save_pretrained("path_to_save_the_quantized_model")
from sdnq import sdnq_post_load_quant
model = sdnq_post_load_quant(
model,
**kwargs_are_the_same_as_SDNQConfig,
)
from functools import wraps
# inference only kernel:
from sdnq.kernels.triton_atten import sdnq_triton_atten
# for training:
#from sdnq.kernels.triton_atten_backward import sdnq_triton_atten_with_backward as sdnq_triton_atten
sdpa_pre_sdnq_atten = torch.nn.functional.scaled_dot_product_attention
@wraps(sdpa_pre_sdnq_atten)
def sdpa_sdnq_atten(query: torch.FloatTensor, key: torch.FloatTensor, value: torch.FloatTensor, attn_mask: torch.Tensor | None = None, dropout_p: float = 0.0, is_causal: bool = False, scale: float | None = None, enable_gqa: bool = False, **kwargs) -> torch.FloatTensor:
if (
query.device.type != "cpu"
and (query.shape[-2] >= 32 and key.shape[-2] >= 32)
and (query.shape[-2] > 512 or key.shape[-2] > 512) # Skip TE
and query.shape[-3] > 1 # Skip VAE
):
return sdnq_triton_atten(
query=query, key=key, value=value, attn_mask=attn_mask,
is_causal=is_causal, scale=scale, enable_gqa=enable_gqa,
matmul_dtype="int8", # can be one of "disabled", "int8", "float8_e4m3fn", "float16".
pv_matmul_dtype="disabled", # can be one of "disabled", "int8", "float8_e4m3fn", "float16".
smooth_k=True,
use_hadamard=False,
hadamard_group_size=256,
do_quantize=True, # Set this to False to disable the quantized matmul usage
quantize_fp32=True, # Set this to False to disable upcasting to FP32 when quantizing
use_fp16_accum=False, # Set this to True to use FP16 accumulaton with matmul_dtype="float16" and pv_matmul_dtype="float16" or "disabled"
out_dtype=None, # Set this to a torch.dtype like torch.float32 if you want the output dtype to be different than inputs
)
else:
if enable_gqa:
kwargs["enable_gqa"] = enable_gqa
return sdpa_pre_sdnq_atten(query=query, key=key, value=value, attn_mask=attn_mask, dropout_p=dropout_p, is_causal=is_causal, scale=scale, **kwargs)
torch.nn.functional.scaled_dot_product_attention = sdpa_sdnq_atten
Block-sparse attention: sdnq_triton_atten also takes block_mask, a bool or int8 tensor of shape (batch or 1, heads or 1, ceil(QN / block_mask_m), ceil(KN / block_mask_n)) with block_mask_m and block_mask_n giving the block size in tokens. The kernel walks only the kept key blocks of each query block, the mask composes with attn_mask, and every autotune tile has to divide the block size (the default tile lists nest a 128x64 block). sdnq_triton_atten_with_backward takes the same three arguments and differentiates through the same selection.
For more information about the options, see SDNQ Wiki and SDNQConfig docstring.
Note:
scripts/dequantize_sdnq_training.py to dequantize an SDNQ Training model saved to the disk.from sdnq.training import sdnq_training_post_load_quant
from sdnq.common import use_torch_compile as triton_is_available
quantized_model = sdnq_training_post_load_quant(
model,
weights_dtype="uint8", # Check out `sdnq.common.accepted_weight_dtypes` for all the supported dtypes.
quantized_matmul_dtype=None, # overrides the quantized matmul dtype to be different than weights_dtype format.
group_size=32, # 0 means auto, -1 means row-wise, -2 means tensor-wise
hadamard_group_size=256,
svd_rank=32,
svd_steps=8,
codebook_steps=24,
use_svd=False,
use_hadamard=False,
use_codebook=False,
use_grad_ckpt=True, # disable this if you are not using gradient checkpointing
use_quantized_matmul=triton_is_available, # use quantized matmul on the forward pass and the backward pass (False means no quantized matmul at all)
use_static_quantization=True, # quantize the model weights (False means model weights will be kept unquantized and only quantized matmul (if enabled) will be used)
use_stochastic_rounding=True,
dequantize_fp32=True, # keeps the quant scales in FP32 and compute the de-quant steps in FP32. Highly recommended to enable this option
non_blocking=False,
add_skip_keys=True,
minimum_allowed_numel=16384, # layers that have less than minimum_allowed_numel elements in them will be skipped and added to modules_to_not_convert
minimum_allowed_channel_size=32, # layers that have less than minimum_allowed_channel_size channels in them will be skipped and added to modules_to_not_convert
modules_to_not_convert=["correction_coefs", "prediction_coefs", "lm_head", "embedding_projection"],
modules_to_not_use_matmul=["x_embedder"],
modules_dtype_dict={"int8": ["lm_head"]},
modules_quant_config={"embed_tokens_per_layer": {"quantization_device": "cpu"}},
quantization_device="cuda",
return_device="cuda",
)
from sdnq.training import convert_sdnq_model_to_training
from sdnq.common import use_torch_compile as triton_is_available
quantized_model = convert_sdnq_model_to_training(
quantized_model,
quantized_matmul_dtype=None, # overrides the quantized matmul dtype to be different than weights_dtype format.
use_grad_ckpt=True, # disable this if you are not using gradient checkpointing
use_quantized_matmul=triton_is_available, # use quantized matmul on the forward pass and the backward pass (False means no quantized matmul at all)
use_stochastic_rounding=True,
dequantize_fp32=True, # keeps the quant scales in FP32 and compute the de-quant steps in FP32. Highly recommended to enable this option
)
from sdnq.training import convert_training_model_to_sdnq
quantized_model = convert_training_model_to_sdnq(quantized_model)
from sdnq.optim import Adafactor, AdamW, CAME, Lion, Muon
optimizer = AdamW(
parameters,
use_quantized_buffers=True,
quantized_buffers_dtype="uint8",
quantized_buffers_group_size=32,
quantized_buffers_hadamard_group_size=256,
quantized_buffers_svd_rank=32,
quantized_buffers_svd_steps=8,
quantized_buffers_codebook_steps=24,
quantized_buffers_use_svd=False,
quantized_buffers_use_hadamard=False,
quantized_buffers_use_codebook=False,
final_norm_mode="clip", # can be one of ["none", "clip", "rms", "rms_clip", "relative", "muon"]
use_kahan=False,
use_cautious=False,
use_stochastic_rounding=True,
use_stochastic_buffers=True,
use_torch_compile=False,
offload_buffers=False,
offload_non_blocking=True,
)
from sdnq.training import SDNQTensor
state["exp_avg"] = SDNQTensor.from_float(
torch.zeros_like(p),
weights_dtype="int8",
group_size=32,
hadamard_group_size=256,
svd_rank=32,
svd_steps=8,
codebook_steps=24,
use_svd=False,
use_hadamard=False,
use_codebook=False,
use_stochastic_rounding=True,
dequantize_fp32=True, # keeps the quant scales in FP32 and compute the de-quant steps in FP32. Highly recommended to enable this option
layer_class_name=None, # can be "Linear", "Conv2d" etc.
)
0 or 1. Default is None (auto-detect)0 or 1. Default is None (auto-detect)0 or 1. Default is None (auto-detect)0 to disable fused SDNQ Triton MM kernels and use the regular unfused Triton MM kernels instead.0 or 1. Default is 11 to enable FP16 accumulation with FP16 matmul on SDNQ Triton MM kernels.0 or 1. Default is 0pip install openvino.0 or 1. Default is None (auto-detect)CPU. Default is HETERO:NPU,CPU if NPU is available else CPU0 or 1. Default is None (auto-detect)0 or 1. Default is 0 if Fused MM Kernels are in use, else 1.use_dynamic_quantization option and within the apply_sdnq_options_to_module function.0 or 1. Default is None (auto-detect)0 or 1. Default is None (auto-detect)0 or 1. Default is 0SDNQ_COMPILE_KWARGS is an advanced option, don't touch this if you don't know exactly what you are doing.{"fullgraph": true}. Default is None (auto-detect)mps. Default is None (auto-detect)bfloat16. Default is None (auto-detect)Python
100.0%