Prhub

#39888 [Model] Use mm_features to compute mrope positions for PaddleOCR-VL

原始 PR 作者 grYe99 合并时间 2026-04-16 21:14 文件变更 2 提交数 3 评论 5 代码增减 +266 / -94

执行摘要

重构 PaddleOCR-VL 模型的 M-RoPE 位置计算,改用 mm_features 驱动。

根据关联issue #32656,目的是统一使用mm_features中的token索引计算M-RoPE位置,因为原有方法通过搜索input_tokens效率低且可能与预期token不对齐。PR body指出这是跟进重构任务的一部分,旨在提高多模态模型的一致性和性能。

该PR值得精读,关注iter_mm_grid_thw迭代器设计如何简化复杂位置计算,以及测试策略如何覆盖多模态场景,可作为类似重构的范例。

讨论亮点

review中gemini-code-assist[bot]指出两个高风险问题:一是mm_feature.data可能为None(缓存场景),访问会抛出TypeError;二是当文本长度为零时生成空张量导致后续max()崩溃。作者根据DarkLight1337建议调整实现更类似Qwen2.5-VL,可能已解决这些问题,最终DarkLight1337批准并确认精度无误。

实现拆解

  1. 新增辅助方法iter_mm_grid_thw:在文件vllm/model_executor/models/paddleocr_vl.py中添加新方法,用于从mm_features迭代提取网格信息(偏移、网格维度、时间因子),处理图像和视频模态,并排序确保顺序正确。
  2. 重构核心逻辑get_mrope_input_positions:在同一文件中,利用iter_mm_grid_thw简化位置计算,移除原有复杂搜索逻辑,直接基于mm_position和网格数据生成位置张量,提高代码可读性和效率。
  3. 添加单元测试覆盖:新增文件tests/model_executor/test_paddleocr_vl_mrope.py,包含文本only、单图像、多图像的测试用例,使用虚拟配置和fixture确保测试稳定性,验证重构后输出与预期一致。
  4. 性能验证与对齐:通过profiler截图展示CPU时间减少,并与Qwen2.5-VL实现相似,确保跨模型一致性。
文件 模块 状态 重要度
vllm/model_executor/models/paddleocr_vl.py 模型执行器 modified 7.72
tests/model_executor/test_paddleocr_vl_mrope.py 测试模块 added 7.67

关键符号

iter_mm_grid_thw get_mrope_input_positions

关键源码片段

vllm/model_executor/models/paddleocr_vl.py core-logic

源码主文件,包含 M-RoPE 位置计算的核心重构,新增 iter_mm_grid_thw 方法并简化 get_mrope_input_positions。

def iter_mm_grid_thw(
    self, mm_features: list[MultiModalFeatureSpec]
) -> Iterator[tuple[int, int, int, int, float]]:
    """
    迭代多模态特征并生成网格信息。
​
    参数:
        mm_features: 多模态特征规范列表
​
    生成:
        每个帧/图像的 (偏移, 网格_t, 网格_h, 网格_w, t_factor) 元组,其中偏移来自mm_position,网格维度根据spatial_merge_size调整,t_factor用于视频时间缩放。
    """
    spatial_merge_size = self.config.vision_config.spatial_merge_size
    tokens_per_second = getattr(self.config.vision_config, "tokens_per_second", 1.0)
    for mm_feature in sorted(mm_features, key=lambda f: f.mm_position.offset): # 按偏移排序确保顺序
        offset = mm_feature.mm_position.offset
        if mm_feature.modality == "image":
            t, h, w = mm_feature.data["image_grid_thw"].data.tolist() # 提取图像网格维度
            assert t == 1, f"Image must have 1 frame, got {t}" # 图像帧数必须为 1
            yield offset, 1, h // spatial_merge_size, w // spatial_merge_size, 1.0 # 返回处理后的网格信息
        elif mm_feature.modality == "video":
            t, h, w = mm_feature.data["video_grid_thw"].data.tolist() # 提取视频网格维度
            second_per_grid_ts = 1.0
            if mm_feature.data.get("second_per_grid_ts", None): # 检查是否有时间缩放因子
                second_per_grid_ts = mm_feature.data["second_per_grid_ts"].data.item()
            t_factor = second_per_grid_ts * tokens_per_second # 计算时间因子
            yield (
                offset,
                t,
                h // spatial_merge_size,
                w // spatial_merge_size,
                t_factor,
            )
        else:
            raise ValueError(f"Unsupported modality: {mm_feature.modality}") # 处理未知模态

评论区精华

缓存导致 mm_feature.data 为 None 的风险 正确性

gemini-code-assist[bot] 指出 mm_feature.data 可能为 None(缓存场景),直接访问会导致 TypeError,建议添加空检查或处理缓存。

结论:作者在后续提交中可能已调整实现,但 review 未明确显示修复;最终批准表明问题已解决或风险可接受。 · 已解决

空张量操作崩溃问题 正确性

gemini-code-assist[bot] 指出当 text_len 为 0 时生成空张量,后续调用 max() 会崩溃,建议添加 if text_len > 0 保护。

结论:类似地,作者可能已采纳建议,最终代码通过测试,问题得到解决。 · 已解决

实现对齐 Qwen2.5-VL 设计

DarkLight1337 建议调整实现更类似 Qwen2.5-VL,如使用 torch.from_numpy,作者回应并提交调整。

结论:实现最终与 Qwen2.5-VL 保持一致,提升跨模型一致性,并通过精度验证。 · 已解决

风险与影响

技术风险包括:

  1. 缓存场景下mm_feature.data为None未处理,可能导致运行时TypeError(review已指出但提交历史未明确修复);
  2. 文本长度为零时空张量操作崩溃(review建议添加if text_len > 0保护)。回归风险低,因测试覆盖充分;性能风险小,重构旨在优化。

对用户:PaddleOCR-VL模型的M-RoPE计算更高效准确,提升推理性能。对系统:作为统一重构的一部分,增强多模态模型接口一致性,便于维护和扩展。对团队:为其他模型重构提供参考模式。

缓存数据未处理 空张量操作

关联 Issue

#32656 [Tracker]: Use `mm_features` for M-RoPE calculation for all models
#39865 [Bugfix] check hasattr "silu_and_mul_per_block_quant"

完整报告

参与讨论