Diffusers 中使用 Quanto 后端进行模型量化:配置、实战与源码原理
【免费下载链接】diffusers🤗 Diffusers: State-of-the-art diffusion models for image, video, and audio generation in PyTorch.项目地址: https://gitcode.com/GitHub_Trending/di/diffusers
导读
Quanto(optimum-quanto)是 HuggingFace Optimum 生态中的轻量级 PyTorch 量化后端,本指南围绕 Diffusers 仓库中 docs/source/en/quantization/quanto.md 展开,讲解如何通过QuantoConfig在from_pretrained/from_single_file加载流程中对扩散模型(以 FLUX.1 的 Transformer 为例)进行 float8/int8/int4/int2 权重量化,并深入源码解读其实现机制、保存与torch.compile集成的具体前提。读完本文,你将掌握在 Diffusers 中一键量化、局部跳过、保存复用量化模型及规避已知限制的完整实战方案。
[!WARNING] 根据 quanto.md 与 quanto_quantizer.py 中的说明,Quanto 后端已弃用(deprecated),并将在 Diffusers 1.0.0 版本中移除。新项目建议优先考虑 bitsandbytes 或 torchao 后端。本文内容仅面向仍在使用或需要维护既有 Quanto 量化流程的开发者。
Quanto 后端的定位与设计特性
Quanto 是面向 Optimum 生态的 PyTorch 量化后端,设计目标是"通用(versatility)"与"简单(simplicity)"。其核心特性如下:
- Eager 模式可用:所有功能在 eager 模式下即可工作,无需模型可被 trace,兼容各种自定义结构的模型;
- 支持量化感知训练(QAT):量化模块可继续参与训练或微调;
- 兼容
torch.compile:量化后的模型可与 PyTorch 编译优化配合使用(当前 Diffusers 集成中仅限 int8 权重,详见下文); - 设备无关:量化模型可在 CUDA、XPU、MPS、CPU 等不同设备上运行。
在 Diffusers 的量化后端体系中,Quanto 通过 AUTO_QUANTIZER_MAPPING 中的"quanto": QuantoQuantizer注册,对应的配置类为 AUTO_QUANTIZATION_CONFIG_MAPPING 中的"quanto": QuantoConfig。加载模型时,modeling_utils.py 会通过DiffusersAutoQuantizer.from_config自动实例化正确的量化器并执行量化流程,对用户而言只需传递一个配置对象即可。
环境安装与版本要求
使用 Quanto 后端需要安装两个依赖:
pip install optimum-quanto accelerate其中optimum-quanto有明确的最低版本要求。在 quanto_quantizer.py 的validate_environment方法中:
- 未安装
optimum-quanto时抛出 ImportError; - 版本低于
0.2.6时抛出ImportError,提示"Loading an optimum-quanto quantized model requiresoptimum-quanto>=0.2.6"; - 未安装
accelerate时同样抛出 ImportError。
此外,量化器类的required_packages = ["quanto", "accelerate"](quanto_quantizer.py)也印证了这两个依赖是硬性要求。建议同时将accelerate升级到>=0.27.0,因为该版本起才提供CustomDtype.FP8 / INT4 / INT2等自定义 dtype 映射(见下文adjust_target_dtype说明)。
核心用法:在from_pretrained中一键量化
Quanto 的接入方式是典型的"零改造":构造一个QuantoConfig对象,将其作为quantization_config参数传给from_pretrained()即可。以 FLUX.1-dev 的 Transformer 组件为例:
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, dtype=torch.bfloat16, ) pipe = FluxPipeline.from_pretrained(model_id, transformer=transformer, dtype=torch.bfloat16) pipe.to("cuda") # 或 "mps"、"xpu"、"cpu" 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")几个关键点:
- 量化只作用于Transformer 组件,加载时传入
subfolder="transformer"定位 FLUX.1 仓库中的子目录权重;其余组件(文本编码器、VAE、调度器)由FluxPipeline正常加载; - 配合
dtype=torch.bfloat16可同时享受低精度权重(量化)与低精度计算(bf16)的双重收益; - 量化后的 Transformer 通过
pipe.to("cuda")迁移到目标设备,由于 Quanto 量化权重是设备无关的,cuda/mps/xpu/cpu均可用。
从源码调用链看,from_pretrained内部会依次执行:validate_environment(环境与版本校验)→preprocess_model(base.py 中调用_process_model_before_weight_loading,完成模块替换并标记model.is_quantized = True)→ 权重加载 →postprocess_model。对 Quanto 而言,模块替换发生在 utils.py 的_replace_with_quanto_layers中。
QuantoConfig 参数详解
QuantoConfig定义在 quantization_config.py,是QuantizationConfigMixin的子类,通过quant_method = QuantizationMethod.QUANTO标识自身后端。它的全部参数如下:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
weights_dtype | str | "int8" | 权重量化后的目标 dtype,仅支持"float8"、"int8"、"int4"、"int2"四种取值;post_init会做合法性校验,传入其他值将抛出ValueError |
modules_to_not_convert | list[str] | None | 不参与量化的模块名列表,用于保留某些模块的原始精度(例如 Whisper encoder、Llava encoder、Mixtral 的 gate 层等场景) |
weights_dtype与底层量化类型的对应关系,可在 utils.py 中看到:
def _get_weight_type(dtype: str): return {"float8": qfloat8, "int8": qint8, "int4": qint4, "int2": qint2}[dtype]即"float8"→qfloat8、"int8"→qint8、"int4"→qint4、"int2"→qint2。
量化范围:仅限nn.Linear权重
虽然 Quanto 库本身支持量化nn.Conv2d和nn.LayerNorm等模块,但当前 Diffusers 集成只量化模型中的nn.Linear层权重。这一点在 utils.py 的实现中体现得很明确——递归遍历时仅对isinstance(module, nn.Linear)的模块执行替换,将其换为optimum.quanto.QLinear:
if isinstance(module, nn.Linear): with init_empty_weights(): qlinear = QLinear( in_features=module.in_features, out_features=module.out_features, bias=module.bias is not None, dtype=module.weight.dtype, weights=_get_weight_type(quantization_config.weights_dtype), ) model._modules[name] = qlinear model._modules[name].source_cls = type(module) model._modules[name].requires_grad_(False)同时,替换完成后会检查是否真的发生了替换(utils.py):
has_been_replaced = any(isinstance(replaced_module, QLinear) for _, replaced_module in model.named_modules()) if not has_been_replaced: logger.warning( f"{model.__class__.__name__} does not appear to have any `nn.Linear` modules. Quantization will not be applied." ... )也就是说,如果一个模型架构里没有任何nn.Linear层,量化将静默无效并输出告警——这是排查"量化后没效果"问题时的关键线索。
权重 dtype 到加载 dtype 的映射
在QuantoQuantizer.adjust_target_dtype(quanto_quantizer.py)中,accelerate>=0.27.0时会将weights_dtype映射为 accelerate 的CustomDtype,确保权重以正确的低精度格式挂载到 meta 设备上:
mapping = { "int8": torch.int8, "float8": CustomDtype.FP8, "int4": CustomDtype.INT4, "int2": CustomDtype.INT2, } target_dtype = mapping[self.quantization_config.weights_dtype]这也解释了为何 int4/int2 这类非 PyTorch 原生 dtype 也能被 accelerate 正确识别和装载。
跳过特定模块的量化
某些模块可能对精度极其敏感,需要保留原始精度。通过QuantoConfig的modules_to_not_convert参数即可跳过:
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"], # 例如 FLUX Transformer 的输出投影层 ) transformer = FluxTransformer2DModel.from_pretrained( model_id, subfolder="transformer", quantization_config=quantization_config, dtype=torch.bfloat16, )使用该参数时务必注意:
- 模块名必须与
state_dict中的键名一致,即传入的是model.named_modules()所暴露的层级名(如proj_out、transformer_blocks.0.attn.to_q等),否则匹配不上会导致该模块仍然被量化; - 在
_process_model_before_weight_loading(quanto_quantizer.py)中,modules_to_not_convert会被归一化为 list,并与 Diffusers 侧的keep_in_fp32_modules合并,一起传给_replace_with_quanto_layers; - 对应的替换逻辑在 utils.py:遍历到名字命中列表的模块时直接
continue,不执行 QLinear 替换。
使用from_single_file加载原始权重并量化
QuantoConfig同样兼容FromOriginalModelMixin.from_single_file,适用于只有一个.safetensors原始权重文件的场景(如 FLUX.1 的flux1-dev.safetensors):
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, dtype=torch.bfloat16, )该方式与from_pretrained的区别在于:权重来源是单文件原始 checkpoint 而非 Diffusers 目录结构,但量化配置的传递方式完全一致,量化流程同样经过QuantoQuantizer的预处理。
保存与重新加载量化模型
Diffusers 支持通过ModelMixin.save_pretrained将 Quanto 量化模型序列化保存:
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, dtype=torch.bfloat16, ) # 保存量化模型以便复用 transformer.save_pretrained("<your quantized model save path>") # 之后可以直接重新加载量化模型 model = FluxTransformer2DModel.from_pretrained("<your quantized model save path>")在保存时,modeling_utils.py 会检查hf_quantizer.is_serializable与supports_safetensors_serialization,而QuantoQuantizer的is_serializable属性返回True(quanto_quantizer.py),因此保存链路是通的。加载时,保存目录中的quantization_config会被 modeling_utils.py 检测到,自动走预量化(pre_quantized)路径,并先对模型执行freeze()以对齐 state_dict(utils.py)。
关键限制:与 Quanto 库直出的模型不互通
官方文档明确强调:用 Quanto 库直接量化得到的模型,目前无法通过 Diffusers 的from_pretrained加载。原因在于两者的序列化与加载约定不同:
- 经 Diffusers + Quanto 后端量化保存的模型,携带 Diffusers 约定的
quantization_config(含quant_method: "quanto")与冻结后的状态字典; - 直接用
optimum.quanto量化产生的模型缺乏这套配置元数据,DiffusersAutoQuantizer.from_pretrained在 auto.py 中会因为找不到quantization_config而抛出ValueError。
因此,若需要在 Diffusers 中复用量化模型,务必通过 Diffusers 自身的save_pretrained保存,而不是复用 Quanto 库的序列化产物。
结合torch.compile加速推理
Quanto 后端支持与torch.compile组合,但当前仅限int8权重量化类型:
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, dtype=torch.bfloat16, ) transformer = torch.compile(transformer, mode="max-autotune", fullgraph=True) pipe = FluxPipeline.from_pretrained( model_id, transformer=transformer, dtype=torch.bfloat16 ) pipe.to("cuda") # 或 "mps"、"xpu"、"cpu" images = pipe("A cat holding a sign that says hello").images[0] images.save("flux-quanto-compile.png")QuantoQuantizer.is_compileable属性返回True(quanto_quantizer.py),与"量化模型兼容torch.compile"的设计目标一致。实操建议:编译前先做一次 warmup 推理,避免首轮编译开销计入耗时统计;mode="max-autotune"适合追求极致性能的场景,但编译时间更长。
支持的量化类型一览
Weights(权重)
| 权重类型 | 说明 | accelerate dtype 映射 |
|---|---|---|
float8 | 8 位浮点,精度损失小,FLUX 实战示例的默认推荐 | CustomDtype.FP8 |
int8 | 8 位整数,唯一支持torch.compile的类型 | torch.int8 |
int4 | 4 位整数,压缩比更高 | CustomDtype.INT4 |
int2 | 2 位整数,压缩比最高,精度损失最大 | CustomDtype.INT2 |
选择建议:追求画质与速度平衡用float8;需要与torch.compile联动用int8;显存极度紧张时可尝试int4/int2并配合提示词与步数调整补偿画质。由于本文所述 Diffusers 集成仅量化nn.Linear权重,实际显存收益取决于目标模型中 Linear 层权重的占比。
底层实现与调用链小结
为了帮助读者在仓库中继续深入,这里汇总与 Quanto 后端相关的核心文件与职责:
| 文件 | 职责 |
|---|---|
| quantization_config.py | QuantoConfig定义:weights_dtype、modules_to_not_convert参数与合法性校验 |
| quanto_quantizer.py | QuantoQuantizer:环境校验、模块替换入口、dtype 映射、显存预算调整、可训练/可序列化/可编译标记 |
| utils.py | _replace_with_quanto_layers:将nn.Linear替换为QLinear并冻结权重 |
| auto.py | 将"quanto"映射到QuantoQuantizer与QuantoConfig |
| modeling_utils.py | from_pretrained中量化配置的合并、量化器实例化与validate_environment调用 |
| base.py | DiffusersQuantizer基类的预处理/后处理流程骨架 |
几个值得注意的实现细节:
- 显存预算自动缩减:
adjust_max_memory(quanto_quantizer.py)会将传入的max_memory各设备预算统一乘以 0.90,为量化过程预留 10% 余量; - 多卡限制:
validate_environment中明确拒绝"多 GPU 推理或 CPU/disk offload"场景——若device_map为 dict 且键数大于 1,会抛出ValueError(quanto_quantizer.py)。因此 Quanto 后端目前只支持单设备(单卡或纯 CPU/单 XPU 等)加载; - 可训练性:
is_trainable返回True,配合 Quanto 的量化感知训练(QAT)能力,量化模型可继续参与微调;替换后的QLinear权重requires_grad_(False),训练时依赖 Quanto 的freeze/unfreeze机制管理; - 缺失键处理:
update_missing_keys会剔除 QModule 内部的非weight/bias键,避免加载预量化模型时因键名差异报错(quanto_quantizer.py)。
已知限制与迁移建议
结合文档与源码,使用 Quanto 后端前请确认以下几点:
- 生命周期:Quanto 后端已弃用,将在 Diffusers 1.0.0 移除,加载时也会触发
deprecate警告;长期项目建议评估迁移到 bitsandbytes(4/8 位、load_in_4bit等)或 torchao; - 量化范围:仅
nn.Linear权重,卷积等模块不会被量化; - 设备映射:不支持多 GPU / CPU offload 的
device_map; - 互操作:Quanto 库直接量化的模型无法通过 Diffusers
from_pretrained加载,必须使用 Diffusers 的save_pretrained产物; - 编译范围:
torch.compile仅适配int8权重。
在做出选择前,可以对照 量化总览文档 了解 Diffusers 支持的全部后端(bitsandbytes_4bit、bitsandbytes_8bit、gguf、quanto、torchao、modelopt、auto-round、nunchaku_lite、sdnq)及其适用场景,再结合本文的 Quanto 细节,为你的模型部署方案做出准确的技术选型。
【免费下载链接】diffusers🤗 Diffusers: State-of-the-art diffusion models for image, video, and audio generation in PyTorch.项目地址: https://gitcode.com/GitHub_Trending/di/diffusers
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考