Prhub

#35370 [diffusion] feat: load GGUF transformer checkpoints (MiniMax-H3)

原始 PR 作者 zijiexia 合并时间 2026-08-20 15:46 文件变更 23 提交数 9 评论 2 代码增减 +1788 / -45

执行摘要

MiniMax-H3 新增 GGUF 量化 transformer 加载路径

MiniMax-H3 的 transformer 在 BF16 下为 61.7 GiB,FL2VA 分区合计 135 GiB。Layerwise offload 已让模型能在消费级 GPU 上运行,剩余瓶颈是 checkpoint 下载、主机内存与逐层流式传输的字节量。本 PR 新增预量化 GGUF 路径,Q4_K_M DiT 仅 17.5 GiB,与在线 kitchen_int8 互补:GGUF 避免先下载并固定完整 BF16 DiT,而 kitchen_int8 仍是更快的计算导向路径。

值得精读。核心设计决策包括:以头部元数据先行 + mmap-backed 加载避免全量克隆;按张量级元数据做 per-layer 量化方法分派并复用 SRT 反量化内核;用实测数据否决 fused MMVQ/MMQ 而选择反量化 + 原生 GEMM;以及把配置冲突全部前置到下载前 fail-fast。特别建议关注 FP32 精度岛与 BF16 性能权衡的取舍逻辑,以及 _reject_lora_on_packed_weights 把校验放在 offload 物化之前的防御式设计。

讨论亮点

唯一的实质 review 交锋围绕 FP32 精度契约展开。

zijiexia:params_dtypeadaln_curve_grid 切到 _FP32_DTYPE 会让 pruned checkpoint 转入 FP32,且静默退出只接受 BF16 的 fused in-place modulation 内核,这是否为有意行为?

mickqian:这是有意的。发布的 pruned 实现(ComfyUI 源码)将曲线表和缩减的 AdaLN 投影都保持在 FP32,我们在 bdcb6e6 显式化了该精度契约并加了回归测试。隔离 BF16 A/B(1x GB300,1344x768,107 帧,50 步)显示 BF16 可启用 fused modulation 路径,denoise 从 95.77 秒降到 84.69 秒(-11.6%),即 FP32 是保真优先、性能让步的取舍。

该线程最终以设计确认并附带回归测试收尾,结论是刻意保留精度岛而非缺陷。

实现拆解

  1. 入口与选择协议transformer_load_utils.py 新增 resolve_transformer_gguf_to_load_resolve_gguf_quant_load_spec,支持本地文件、owner/repo/path/file.ggufowner/repo:QUANT_TYPE(仅当后缀唯一匹配时)三种选择方式;_validate_gguf_runtime_support 在下载前拒绝非 CUDA、显式 --quantization、SVDQuant、GGUF 组件上的 FSDP、AdaLN cache/online 路径等冲突组合。

  2. 元数据先行:新增 gguf_weights.pyread_gguf_tensor_meta 在参数对象存在前从文件头解析每个张量的逻辑形状与 packed 形状:量化张量要求为 2D .weight 且 inner 维度对齐 GGML 块大小,映射为 (out, row_bytes)uint8 布局并命名为 qweightgguf_weights_iterator 保持 packed 数据 mmap-backed,直到通用 loader 拷贝 TP 分片,避免加载期间克隆完整的 17.5 GiB 文件。

  3. 量化方法与内核选择:新增 quantization/gguf.pyGGUFConfig.get_quant_method 按张量元数据逐层分派——未量化类型走 UnquantizedLinearMethod,量化类型走新增的 GGUFLinearMethod,后者复用 SRT 的 dequantize_gguf_weight 反量化后执行原生 nn.functional.linear。内核选择以 H200 实测为准:256/2048 token 下 SRT MMQ 耗时 2.60 ms/20.61 ms,而反量化 + GEMM 仅 0.37 ms/0.91 ms,故不使用面向低 token LLM 的 fused MMVQ/MMQ。

  4. 模型契约适配minimax_h3.py 在 GGUF 量化下跳过 safetensors 的 qkv 重排(GGUF 已按 [q_all, k_all, v_all] 存储);修剪版社区 checkpoint(含 adaln_curve_grid)的 AdaLN 投影保持 FP32 精度岛以对齐发布的 ComfyUI 实现;MiniMaxH3MLP 对 GGUF 量化放开 fc1 激活缓冲复用,重新进入 fused silu_and_mul 内核。

  5. LoRA 与承载边界lora_pipeline.py 新增 _reject_lora_on_packed_weights,在 convert_to_lora_layers 与动态 set_lora 两个入口前置检查,目标层只有 qweight 而无 weight 时直接报错;校验放在关闭 offload 之前,避免内存受限部署被物化层 OOM。同时 is_target_layer 改为 getattr 兜底,恢复默认 LoRA 目标准入。

  6. 测试与文档:新增 test_gguf_diffusion.py(793 行,手写最小 GGUF v3 二进制),覆盖端序/损坏头、量化 packed 行布局、pruned 曲线形状、块对齐拒绝与 TP row/column/merged 切片;test_hf_transformers.py 覆盖 Hub 量化类型解析与歧义拒绝;test_minimax_h3_dit_contract.py 覆盖修剪曲线插值与 FP32 精度岛契约。文档同步更新 CLI、量化兼容性与 MiniMax-H3 用法。

文件 模块 状态 重要度
python/sglang/multimodal_gen/runtime/loader/gguf_weights.py 加载器 added 8.98
python/sglang/multimodal_gen/runtime/layers/quantization/gguf.py 量化层 added 8.68
python/sglang/multimodal_gen/runtime/loader/transformer_load_utils.py 加载调度 modified 8.01
python/sglang/multimodal_gen/runtime/models/dits/minimax_h3.py 模型适配 modified 7.52
python/sglang/multimodal_gen/runtime/pipelines_core/lora_pipeline.py LoRA 管线 modified 6.91
python/sglang/multimodal_gen/test/unit/test_gguf_diffusion.py 单元测试 added 7.76
python/sglang/srt/layers/quantization/gguf.py SRT 量化层 modified 5.82
python/sglang/srt/utils/hf_transformers/common.py HF 工具 modified 6.08

关键符号

read_gguf_tensor_meta gguf_weights_iterator GGUFConfig.get_quant_method GGUFLinearMethod.create_weights GGUFLinearMethod.apply resolve_transformer_gguf_to_load _validate_gguf_runtime_support _reject_lora_on_packed_weights dequantize_gguf_weight

关键源码片段

python/sglang/multimodal_gen/runtime/pipelines_core/lora_pipeline.py core-logic

LoRA 与 packed 权重的兼容性防线:转换或 `set_lora` 前预检,避免模型被转换到一半或 offload 物化时 OOM。

def _reject_lora_on_packed_weights(self) -> None:
    """在任何层被替换前,先拒绝指向 packed 权重的 LoRA 请求。    `BaseLayerWithLoRA` 读取 `base_layer.weight`,而 GGUF 这类打包量化
    只注册 `qweight`。提前检查可以避免:
    1. 模型被转换到一半后失败;
    2. `set_lora` 在内存受限部署上先物化全部 offload 层导致 OOM。
    """
    for module_name in ("transformer", "transformer_2"):
        module = self.modules.get(module_name)
        if module is None:
            continue
        for name, layer in module.named_modules():
            if not self.is_target_layer(name):
                continue
            params = dict(layer.named_parameters(recurse=False))
            if "weight" not in params and "qweight" in params:
                raise ValueError(
                    f"LoRA 不支持 {module_name}.{name}:其权重以 packed "
                    "形式存储(GGUF),无法合并或叠加 adapter。请改为加载 "
                    "未量化 checkpoint 以使用 LoRA。"
                )
​
​
def set_lora(self, lora_name: str, lora_path: str) -> None:
    # ... 前面的参数校验逻辑省略 ...
    # 先校验再关闭 offload:offload 物化会把每层都搬到显存,
    # 在内存受限部署上会 OOM 而不是返回“不支持 LoRA”的错误。
    self._reject_lora_on_packed_weights()
    # Disable layerwise offload before convert_to_lora_layers ...

评论区精华

pruned AdaLN 检查点的 FP32 精度契约 设计

zijiexia 质疑 `params_dtype=(_FP32_DTYPE if arch.adaln_curve_grid is not None else _BF16_DTYPE)` 使 pruned checkpoint 转入 FP32,且会静默退出只接受 BF16 的 fused in-place modulation 内核,是否为有意行为。

结论:mickqian 确认有意为之:对齐发布的 ComfyUI 修剪实现(曲线表与缩减 AdaLN 投影均保持 FP32),已显式化精度契约并添加回归测试;隔离 BF16 A/B 显示 denoise 95.77 秒 → 84.69 秒(-11.6%),即 FP32 是保真优先的性能让步。 · 已解决

风险与影响

  1. 未端到端验证路径ref2va、Q4_K/Q4_K_M 之外的量化类型、多 GPU GGUF 均未做端到端验证,TP packed 切片只有单元测试覆盖;PR 中提到的 2x H100 验证机位仍在基础设施 provisioning 中,属于已知未闭合项。
  2. 软依赖新增:读取 GGUF 需要 gguf 包,_gguf_module() 在缺失时抛出 ImportError;若部署环境的依赖声明未同步更新,用户会在加载点才收到错误。
  3. 精度契约变更:pruned 检查点的 FP32 精度岛会绕过 BF16 fused modulation,端到端性能下降约 11.6%;这是有意的对齐行为,但容易被后续开发者误判为遗漏并"修复"。
  4. 校验依赖外部表GGML_QUANT_SIZES、SRT 的 DEQUANT_TYPES/UNQUANTIZED_TYPES_SUPER_BLOCK_DEQUANT_TYPES 三套集合需要与 GGUF 规范及 SRT 内核同步演进,否则可能产生假阳性或假阴性校验。
  5. LoRA 兼容性边界:GGUF packed 权重不支持 LoRA,已通过显式拒绝避免半转换状态,但用户必须切换到未量化 checkpoint 才能使用 LoRA,属于功能性限制。

用户侧:MiniMax-H3 现在可通过 --transformer-weights-path 加载 GGUF DiT,下载体积与主机内存占用显著下降(61.7 GiB → 17.5 GiB),配合 layerwise offload 可在消费级 GPU(如 RTX 5090,峰值 26.3 GiB)运行。系统侧:加载构建时间从约 19 秒降到约 1 秒,集群冷启动成本下降;量化 DiT 在长序列上避免 fused MMVQ/MMQ 回归(H200 上 256 token 从 2.60 ms 降到 0.37 ms)。团队侧:确立了 diffusion 复用 SRT GGUF 能力的边界与模式,Hub 解析与反量化内核不再重复实现;同时新增了 LoRA、多 GPU 等限制项,需要文档持续维护。

GGUF 多 GPU 路径未端到端验证 新增 gguf 包软依赖 FP32 精度岛牺牲约 11.6% 性能 LoRA 与 GGUF 不兼容显式拒绝 TP 切片与量化块对齐限制 量化类型表需与 GGUF 规范同步

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论