Prhub

#36046 [Diffusion] Load Comfy NVFP4-AWQ text encoders

原始 PR 作者 mickqian 合并时间 2026-08-25 15:43 文件变更 7 提交数 23 评论 1 代码增减 +455 / -11

执行摘要

支持自动加载 Comfy NVFP4-AWQ 文本编码器

PR body 明确说明目标是“auto-detect Comfy NVFP4-AWQ encoder checkpoints from their per-layer metadata”,并希望“preserve the official row-wise INT8 embedding and packed NVFP4 storage”以及“honor AWQ input pre-scales, Comfy's high-nibble-first packing, and swizzled block scales”。同时刻意“dequantize only the active matrix for BF16/FP16 compute, without advertising native FP4 acceleration”,避免对用户承诺不存在的性能收益。该 PR 基于 #36044 引入的共享 Comfy NVFP4 检查点布局,使扩散文本编码器不需要用户指定量化参数即可工作。

值得精读,尤其是想理解“保留量化存储、按需反量化”这一设计权衡的读者。建议关注三个点:一是 inspect_comfy_quant_markers 如何用逐层 marker 验证替代显式量化参数;二是 ComfyFullPrecisionNvfp4LinearMethod 故意跳过 process_weights_after_loading 以保持可移植性的取舍;三是文档明确不承诺 FP4 加速的诚实表述方式。对扩展其他 Comfy 量化格式的开发者,本 PR 的标记解析与分派模式可以直接借鉴。

讨论亮点

该 PR 没有任何 review 评论或 GitHub 讨论线程,设计决策主要体现在 23 个提交的演进中:从 W4A8、W4A4 逐步扩展到 NVFP4 H3 DiT,再到本 PR 的 NVFP4-AWQ 文本编码器;其中“Preserve standard ModelOpt NVFP4 layout”与“Avoid overstating H3 NVFP4 validation”两个提交反映了实现中途曾对齐 ModelOpt 标准布局,并主动收敛文档措辞;多个 “merge encoder quant marker fix” 提交则说明编码器 marker 映射在 CI 中经历过反复修正。

实现拆解

实现按以下 5 个步骤拆解:

  1. 扩展检查点标记解析(quantization_utils.py):在 inspect_comfy_quant_markers 中新增 nvfp4 格式支持:把 U8 权重加入可标记 dtype;要求 weight_scale_2 为标量 F32、weight_scale 为 FP8 E4M3、权重二维且 weight_shape[1]*2 能被 16 整除;校验 pre_quant_scale 的 dtype 与形状,并在 marker 中写入 _has_pre_quant_scale;同时为 INT8 tensorwise 标记增加 _is_rowwise,为加载器分派提供依据。这一步是整个自动检测的入口,任何形状或 dtype 不符都会直接报错,避免带病加载。

  2. 新增 Comfy NVFP4 量化方法(新增 comfy_nvfp4.py):新增 _register_parameter 统一登记参数与并行维度;ComfyRowwiseInt8EmbeddingMethod 把 embedding 保存为 INT8 权重加逐行 FP32 scale,apply 直接抛 NotImplementedError,仅支持通过 embedding() 对命中行做 gather 后乘 scale 的按需反量化;ComfyFullPrecisionNvfp4LinearMethod 继承 ModelOptFp4LinearMethod,在 create_weights 中保留 U8 打包权重、FP8 块 scale、标量 weight_scale_2 和可选 pre_quant_scaleprocess_weights_after_loading 为空,明确放弃 ModelOpt 的 Blackwell 专用内核预处理,以便直接消费序列化表示。

  3. 接入文本编码器加载器(text_encoder_loader.py)_configure_encoder_quantization 相关分派逻辑识别 ComfyNvfp4Config_require_quantized_encoder_layers 的量化配置元组加入 ComfyNvfp4Config,确保所有序列化 layer marker 都必须被模型真实消费,否则抛 ComponentCheckpointUnsupportedErrorload_model 的权重后处理设备分支把 ComfyNvfp4ConfigQuantoInt8Config 归为一类,保持原始序列化布局不做 CPU 卸载。

  4. 扩展共享反量化器(srt/layers/quantization/dequantization.py)dequantize_nvfp4 新增 high_nibble_first: bool = False 参数,默认保持 ModelOpt/Quark 的低半字节对应偶数索引布局;Comfy 导出为高半字节优先,通过 (high, low) 交换解包顺序。原有调用方全部使用默认值,行为不变。

  5. 测试与文档配套test_text_encoder_loader.py 新增两个单元测试,分别覆盖 safetensors 元数据到 embedding/linear marker 的映射,以及带 pre_quant_scale 的 portable Linear 与 row-wise embedding 的前向输出;文档在 quantization.mdx 的对照表中增加 comfy-nvfp4-full-precision 行,并在 MiniMax-H3 cookbook 中说明 --component-paths.text_encoder 的无标志覆盖用法,明确该路径是“memory path rather than a native FP4 speed path”。

文件 模块 状态 重要度
python/sglang/multimodal_gen/runtime/layers/quantization/comfy_nvfp4.py 量化方法 added 8.98
python/sglang/multimodal_gen/runtime/utils/quantization_utils.py 检查点解析 modified 6.6
python/sglang/multimodal_gen/test/unit/test_text_encoder_loader.py 单元测试 modified 6.02
python/sglang/multimodal_gen/runtime/loader/component_loaders/text_encoder_loader.py 加载器 modified 5.59
python/sglang/srt/layers/quantization/dequantization.py 反量化 modified 5.88
docs/docs/sglang-diffusion/quantization.mdx 文档 modified 3.22
docs/cookbook/diffusion/MiniMax/MiniMax-H3.mdx 文档 modified 3.08

关键符号

dequantize_nvfp4 inspect_comfy_quant_markers _register_parameter ComfyRowwiseInt8EmbeddingMethod.embedding ComfyRowwiseInt8EmbeddingMethod.create_weights ComfyFullPrecisionNvfp4LinearMethod.create_weights ComfyFullPrecisionNvfp4LinearMethod.process_weights_after_loading _require_quantized_encoder_layers

关键源码片段

python/sglang/srt/layers/quantization/dequantization.py core-logic

共享反量化函数 dequantize_nvfp4 新增 high_nibble_first 参数,用于区分 Comfy 与 ModelOpt 的 NVFP4 半字节打包顺序,影响所有 NVFP4 反量化调用方。

def dequantize_nvfp4(
    w_q: torch.Tensor,
    w_s: torch.Tensor,
    w_s2: Optional[torch.Tensor],
    out_dtype: torch.dtype = torch.bfloat16,
    high_nibble_first: bool = False,
) -> torch.Tensor:
    """NVFP4 -> out_dtype。    w_q: uint8 [..., out, in/2] 打包 E2M1;
    默认(ModelOpt/Quark)低半字节对应偶数索引;
    Comfy 导出则相反,需要传 high_nibble_first=True。
    w_s: FP8 E4M3 [..., out, in/16] 逐块 scale;
    w_s2: 可选 FP32 标量,整体乘到逐块 scale 上。
    """
    ... # batch/out_dim/in_dim 计算与 E2M1 LUT 准备保持不变
    low = (w_q & 0xF).to(torch.int64) # 低 4 位索引
    high = (w_q >> 4).to(torch.int64) # 高 4 位索引
    # Comfy 高半字节优先时,把高半字节放到偶数索引位置
    first, second = (high, low) if high_nibble_first else (low, high)
    deq = torch.empty(*batch, out_dim, in_dim, dtype=torch.float32, device=device)
    deq[..., 0::2] = lut[first]
    deq[..., 1::2] = lut[second]
    scale = w_s.to(torch.float32)
    if w_s2 is not None:
        scale = scale * w_s2.to(torch.float32)
    ... # 乘 scale 并转到 out_dtype

评论区精华

没有提炼出高价值讨论线程

当前评论区没有形成足够清晰的争议点或结论,后续有更多讨论时会体现在这里。

风险与影响

  1. 共享反量化器风险dequantize_nvfp4 被 SRT 的 ModelOpt/Quark 路径共用,新增 high_nibble_first 默认值为 False,保持旧行为;但如果未来其他 Comfy 检查点接入时忘记传 True,会静默产生错误数值而不是报错。
  2. 自动检测风险inspect_comfy_quant_markers 对 nvfp4 标记做了严格形状校验,一旦 Comfy 官方导出版本变化(如 scale 形状或 pre_quant_scale dtype 调整),加载会直接失败;没有提供显式开关绕过校验。
  3. 功能边界风险ComfyRowwiseInt8EmbeddingMethod.apply 直接抛异常,任何误将该 embedding 当作 Linear 调用的路径都会崩溃;文档已说明仅支持 lookup。
  4. 性能预期风险:PR 明确定位为内存路径而非原生 FP4 加速,但用户看到 NVFP4 标签可能误以为有 Blackwell 加速,需要依赖文档纠正。
  5. 验证覆盖风险:运行时验证仅依赖 NVIDIA CI,AMD/XPU 等后端没有覆盖此加载路径。

用户侧:MiniMax-H3 等扩散工作流现在可以直接用 --component-paths.text_encoder 指定 Comfy NVFP4-AWQ 编码器 safetensors,无需手动挑选量化选项,模型加载内存显著下降。系统侧:为 sglang.multimodal_gen 的组件量化分派增加了一个新的自动检测类别,并将检查点格式约定(高半字节优先、swizzled scale、AWQ 预缩放)固化在共享工具中,后续其他扩散模型可复用同一套机制。团队侧:该 PR 是“Comfy 检查点家族”支撑的最后一块拼图,降低了维护多套量化装载逻辑的负担,但也在共享 dequantize_nvfp4 上新增了一个需要所有调用方理解的分支。

共享反量化器新增打包顺序分支 自动检测缺少显式开关 运行时验证依赖 NVIDIA CI 无原生 FP4 加速需依赖文档纠偏

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论