Prhub

#43798 [Bugfix] Convert Gemma4-MM ViT linear layers to vllm native impl

原始 PR 作者 Isotr0py 合并时间 2026-06-02 12:41 文件变更 4 提交数 6 评论 3 代码增减 +111 / -11

执行摘要

修复 Gemma4-MM ViT 量化线性层兼容性

PR #42825 通过模型特定补丁修复了 ViT 量化,但该补丁将在 BNB OOT 插件迁移 (#43529) 后废弃;PR #43440 因注意力掩码限制无法替换常见线性层。因此需要一个通用递归替换方案,通过 transformers 后端替换线性层实现 ViT 原生化。

值得精读。设计上选择通用递归替换而非模型特定补丁,体现了模块化封装思想。BitsAndBytesWeightParameter 的 dtype 修复技巧可复用。建议关注后续 LoRA 准确性修复。

讨论亮点

Isotr0py 请求 linitra24 测试 ViT LoRA 兼容性,后者测试后反馈 LoRA 加载成功但推理准确性存在问题,将在后续继续排查。Reviewer jeejeelee 和 mgoin 已批准。

实现拆解

  1. vllm/model_executor/models/transformers/utils.py 中新增 recursive_replace_linear 函数,递归遍历模块子层,将 nn.Linear 替换为 vLLM 的 ReplicatedLinear(根据量化配置),并包含内部辅助函数 _recursive_replace

  2. 修改 vllm/model_executor/models/gemma4_mm.py,在 Gemma4MultimodalEmbedderGemma4ForConditionalGeneration 中传递 quant_configprefix,并在 vision tower 和 audio tower 初始化后调用 recursive_replace_linear 替换其内部线性层。

  3. 新增 vllm/model_executor/layers/quantization/bitsandbytes.py 中的 BitsAndBytesWeightParameter 类,通过 cached_property 提供正确的 dtype,避免量化参数 dtype 不匹配导致加载失败。

  4. vllm/model_executor/model_loader/bitsandbytes_loader.py_stack_quantization_states 方法中,对 attention_k_eq_v=True 的模型(如 Gemma4)复制 k_proj 的量化状态到 v_proj,解决量化状态缺失问题。

文件 模块 状态 重要度
vllm/model_executor/models/transformers/utils.py 公共工具 modified 7.67
vllm/model_executor/models/gemma4_mm.py 模型定义 modified 7.18
vllm/model_executor/layers/quantization/bitsandbytes.py 量化层 modified 6.71
vllm/model_executor/model_loader/bitsandbytes_loader.py 权重加载 modified 6.39

关键符号

recursive_replace_linear _recursive_replace BitsAndBytesWeightParameter.dtype

关键源码片段

vllm/model_executor/models/transformers/utils.py core-logic

核心新增函数,提供递归替换线性层的通用工具,是本次变更的基石。

def recursive_replace_linear(
    model: nn.Module,
    quant_config: "QuantizationConfig | None",
    prefix: str = "",
):
    """Recursively replace linear modules in the model as needed."""
​
    def _recursive_replace(module: nn.Module, prefix: str):
        for child_name, child_module in module.named_children():
            new_module = child_module
            qual_name = maybe_prefix(prefix, child_name)
            # 遇到 nn.Linear 就替换为 vLLM 原生线性层
            if isinstance(child_module, nn.Linear):
                style = "replicate"
                new_module = replace_linear_class(
                    child_module,
                    style,
                    quant_config,
                    prefix=qual_name,
                )
            else:
                # 非 Linear 则递归进入子模块
                _recursive_replace(child_module, prefix=qual_name)
            if new_module is not child_module:
                setattr(module, child_name, new_module)
​
    _recursive_replace(model, prefix=prefix)

评论区精华

ViT LoRA 兼容性测试 正确性

Isotr0py 请求 linitra24 测试 ViT LoRA 兼容性;linitra24 测试后反馈 LoRA 加载成功但推理准确性有问题。

结论:准确性问题将在合并后继续排查。 · 已解决

风险与影响

  • 递归替换可能对非量化场景带来微小性能开销(原本 PyTorch 直接优化 nn.Linear)
  • BitsAndBytesWeightParameter 的 dtype 依赖 get_default_dtype(),若上下文默认 dtype 不符预期可能导致精度问题
  • k_eq_v 量化状态重复假设严格,若模型结构不一致可能触发断言失败
  • LoRA 推理准确性尚未完全验证,可能影响生产使用
  • 用户影响:直接消除 Gemma4-MM 量化模型无法启动或推理错误的问题,使 BNB 4bit/8bit 正常使用
  • 系统影响:引入的 recursive_replace_linear 可作为通用工具用于未来模型,提升维护性
  • 团队影响:减少模型特定补丁依赖,但需关注 LoRA 准确性和回归测试
dtype 潜在不匹配 递归替换影响非量化性能 k_eq_v 假设严格 LoRA 准确性待验证

关联 Issue

#43529 [Draft] Migrate bitsandbytes support to OOT plugin

完整报告

参与讨论