news 2026/9/12 21:16:19

Diffusers 中使用 Quanto 后端进行模型量化:配置、实战与源码原理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Diffusers 中使用 Quanto 后端进行模型量化:配置、实战与源码原理

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 展开,讲解如何通过QuantoConfigfrom_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_dtypestr"int8"权重量化后的目标 dtype,仅支持"float8""int8""int4""int2"四种取值;post_init会做合法性校验,传入其他值将抛出ValueError
modules_to_not_convertlist[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.Conv2dnn.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 正确识别和装载。

跳过特定模块的量化

某些模块可能对精度极其敏感,需要保留原始精度。通过QuantoConfigmodules_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_outtransformer_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_serializablesupports_safetensors_serialization,而QuantoQuantizeris_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 映射
float88 位浮点,精度损失小,FLUX 实战示例的默认推荐CustomDtype.FP8
int88 位整数,唯一支持torch.compile的类型torch.int8
int44 位整数,压缩比更高CustomDtype.INT4
int22 位整数,压缩比最高,精度损失最大CustomDtype.INT2

选择建议:追求画质与速度平衡用float8;需要与torch.compile联动用int8;显存极度紧张时可尝试int4/int2并配合提示词与步数调整补偿画质。由于本文所述 Diffusers 集成仅量化nn.Linear权重,实际显存收益取决于目标模型中 Linear 层权重的占比。

底层实现与调用链小结

为了帮助读者在仓库中继续深入,这里汇总与 Quanto 后端相关的核心文件与职责:

文件职责
quantization_config.pyQuantoConfig定义:weights_dtypemodules_to_not_convert参数与合法性校验
quanto_quantizer.pyQuantoQuantizer:环境校验、模块替换入口、dtype 映射、显存预算调整、可训练/可序列化/可编译标记
utils.py_replace_with_quanto_layers:将nn.Linear替换为QLinear并冻结权重
auto.py"quanto"映射到QuantoQuantizerQuantoConfig
modeling_utils.pyfrom_pretrained中量化配置的合并、量化器实例化与validate_environment调用
base.pyDiffusersQuantizer基类的预处理/后处理流程骨架

几个值得注意的实现细节:

  • 显存预算自动缩减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 后端前请确认以下几点:

  1. 生命周期:Quanto 后端已弃用,将在 Diffusers 1.0.0 移除,加载时也会触发deprecate警告;长期项目建议评估迁移到 bitsandbytes(4/8 位、load_in_4bit等)或 torchao;
  2. 量化范围:仅nn.Linear权重,卷积等模块不会被量化;
  3. 设备映射:不支持多 GPU / CPU offload 的device_map
  4. 互操作:Quanto 库直接量化的模型无法通过 Diffusersfrom_pretrained加载,必须使用 Diffusers 的save_pretrained产物;
  5. 编译范围torch.compile仅适配int8权重。

在做出选择前,可以对照 量化总览文档 了解 Diffusers 支持的全部后端(bitsandbytes_4bitbitsandbytes_8bitggufquantotorchaomodeloptauto-roundnunchaku_litesdnq)及其适用场景,再结合本文的 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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 21:10:39

分页存储:把内存切成“等大的格子“

107 分页存储:把内存切成"等大的格子" 连续分配最大的问题是外部碎片——空闲空间被切碎,凑不出足够大的一块给新进程。 分页存储的思路很暴力:既然凑不出连续的,那就干脆不要求连续了! 基本思想 把物理内存和程序都切成固定大小的小块: 物理内存切成等大小…

作者头像 李华
网站建设 2026/9/12 21:08:06

电解铝与有色金属冶炼车间智能巡检:高温强磁环境下的无人化替代

电解铝车间的巡检是典型的"高温、强磁、高粉尘"作业&#xff1a;槽底空间低矮、环境温度高、磁场强度大&#xff0c;还有氟化氢烟气与铝渣堆积。人工巡检不仅劳动强度大&#xff0c;还长期暴露在职业危害中。以耐高温、抗强磁的巡检机器人替代人工执行槽间巡检与测温…

作者头像 李华