Skip to content

feat: add Qwen3-VL custom ViT implementation for vision acceleration#1144

Open
theNiemand wants to merge 1 commit into
mainfrom
feature/qwen3-vl-vision-accel
Open

feat: add Qwen3-VL custom ViT implementation for vision acceleration#1144
theNiemand wants to merge 1 commit into
mainfrom
feature/qwen3-vl-vision-accel

Conversation

@theNiemand

Copy link
Copy Markdown
Collaborator

No description provided.

@theNiemand theNiemand requested a review from LLLLKKKK as a code owner June 29, 2026 03:50
@LLLLKKKK

Copy link
Copy Markdown
Collaborator

AI Code Review - PR #1144

Status: BLOCKING

Summary: P0/1 · P1/2 · P2/1 · P3/0

Blocking Issues

P0

  • forward() 返回 tuple,调用方用 .pooler_output/.deepstack_features 属性访问会 AttributeError @ rtp_llm/multimodal/multimodal_mixins/qwen3_vl/qwen3_vl_vit.py:467
    • 建议:方案一:定义 NamedTuple/dataclass 作为返回值(如 class Qwen3VLVisionOutput(NamedTuple): pooler_output: torch.Tensor; deepstack_features: list[torch.Tensor]),forward_blocks() 和 forward() 均返回此类型。方案二:在 mixin embedding()/batched_embedding() 中改为 tuple 解包。推荐方案一保持与 transformers model output 的一致性。

P1

  • forward() 返回类型注解 -> torch.Tensor 与实际返回 tuple 不符 @ rtp_llm/multimodal/multimodal_mixins/qwen3_vl/qwen3_vl_vit.py:460
    • 建议:修正 forward() 返回类型注解为 tuple[torch.Tensor, list[torch.Tensor]],或引入 NamedTuple 统一 forward()/forward_blocks() 返回类型。
  • 缺少测试覆盖:新增自定义 ViT 模型和修改后的 mixin 无任何测试 @ rtp_llm/multimodal/multimodal_mixins/qwen3_vl/qwen3_vl_vit.py:1
    • 建议:至少添加:1) Qwen3VLVisionModel 的 forward 单测(验证输出 shape 和返回类型与 transformers 参考实现一致);2) Qwen3_VLImageEmbedding.embedding() 的集成测试(验证 deepstack feature 提取);3) attention backend fallback 的测试。或添加 Qwen3-VL smoke test。

Non-blocking Suggestions

P2

  • _sdpa_varlen 创建 O(seq_length²) bool attention mask,大分辨率图片可能 OOM @ rtp_llm/multimodal/multimodal_mixins/qwen3_vl/qwen3_vl_vit.py:15
    • 建议:考虑在 SDPA fallback 中使用 nested tensor 或分块计算替代全量 attention mask,或在 seq_length 超过阈值时发出警告建议安装 flash_attn。低优先级,FA3/FA2 优先选取时不触发此路径。

Checklist Violations (11 fail / 77 total)

General Principles Checklist

  • [6.1] Software Engineering — LSP:子类/重写保持基类契约 → issue forward() 返回 tuple,调用方用 .pooler_output/.deepstack_features 属性访问会 AttributeError
    Qwen3_VLImageEmbedding 继承 Qwen2_5_VLImageEmbedding 但改变了 self.visual 的返回契约:父类 visual 返回结构化 model output,新 visual 返回 tuple。embedding()/batched_embedding() 用属性访问 .pooler_output/.deepstack_features,运行时 AttributeError
  • [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue 缺少测试覆盖:新增自定义 ViT 模型和修改后的 mixin 无任何测试
    新增 469 行自定义 ViT 实现(qwen3_vl_vit.py)+重写 mixin 嵌入逻辑,但 diff 中无任何新增测试。grep qwen3_vl 在 test/ 目录下无匹配
  • [6.1] Tests — 边界 case 覆盖(空、单元素、最大值) → issue 缺少测试覆盖:新增自定义 ViT 模型和修改后的 mixin 无任何测试
    新 ViT 模型无边界 case 测试(空 grid_thw、单 frame 视频、极大分辨率、空 deepstack_visual_indexes 等场景未覆盖)

RTP-LLM Checklist

  • [B] 正确性与逻辑 — 逻辑错误、off-by-one、null/zero 检查 → issue forward() 返回 tuple,调用方用 .pooler_output/.deepstack_features 属性访问会 AttributeError
    Qwen3VLVisionModel.forward() 返回 plain tuple 但调用方 qwen3_vl_mixin.py:124 用 .pooler_output/.deepstack_features 属性访问。runtime AttributeError
  • [B] 正确性与逻辑 — 边界 case(空输入、单元素、最大值) → issue forward() 返回 tuple,调用方用 .pooler_output/.deepstack_features 属性访问会 AttributeError
    无测试覆盖边界 case。如 deepstack_visual_indexes 为空列表时 deepstack_feature_lists 为空,torch.stack([]) 会在 mixin embedding() 中 crash。该路径当前被 P0 bug 遮蔽
  • [B] 正确性与逻辑 — 接口返回类型变更有兼容处理 → issue forward() 返回 tuple,调用方用 .pooler_output/.deepstack_features 属性访问会 AttributeError
    self.visual 从 HuggingFace Qwen3VLVisionModel(返回结构化 model output)替换为自定义 nn.Module(返回 tuple),但调用方 embedding()/batched_embedding() 未同步修改为 tuple 解包

Python Static-First Checklist

  • [P.A] 静态结构与类型纪律 — 数据容器用 dataclass/NamedTuple/TypedDict → issue forward() 返回 tuple,调用方用 .pooler_output/.deepstack_features 属性访问会 AttributeError
    forward_blocks() 返回裸 tuple (hidden_states, deepstack_feature_lists),应使用 NamedTuple 使调用方可通过命名属性访问——正是 P0 bug 的根因
  • [P.B] 错误处理 — 自定义异常继承领域基类 → checklist-only
    raise Exception('unknown mm url type') 使用基础 Exception 而非领域异常类,但此模式与父类 Qwen2_5_VLImageEmbedding 一致,非本 PR 新引入
  • [P.D] 性能反模式 — 成员检测用 set/dict 非 list → checklist-only
    qwen3_vl_vit.py:449 if layer_num in self.deepstack_visual_indexes 对 list 做成员检测 + .index() 查找,均 O(n)。列表长度极小(通常 3-5),实际性能影响可忽略,但可用 dict 优化
  • [P.F] 语言陷阱 — 禁止模块级 import 副作用 → checklist-only
    __flash_attn_varlen = resolve_attn_backend() 在模块加载时执行 try-import 探测确定 attention 后端,是标准初始化模式(参考 transformers/flash_attn 实现),刻意为之以避免每次 forward 都探测
  • [P.H] 类型标注 — 输入参数用 Sequence/Mapping/Iterable,返回值用具体类型 → issue forward() 返回类型注解 -> torch.Tensor 与实际返回 tuple 不符
    forward() 返回类型注解为 -> torch.Tensor 但实际返回 tuple[torch.Tensor, list[torch.Tensor]],类型标注与实际不符

Strengths

  • 将 ViT 从 HuggingFace transformers 提取为自管理实现,避免 transformers 版本升级导致的兼容性问题,利于性能定制
  • FA3/FA2/SDPA 三级 attention backend 自动选择,兼顾性能与兼容性,import-time 一次性解析
  • 文件从平铺重组为 qwen3_vl/ 子包结构,与 qwen3_5_moe/、qwen2_5_vl/ 等模块保持一致
  • 继承 Qwen2_5_VL 基类复用图片加载、视频处理、位置编码等逻辑,减少代码重复

@theNiemand theNiemand force-pushed the feature/qwen3-vl-vision-accel branch from 1016a8c to 2eeff67 Compare June 29, 2026 06:46
@LLLLKKKK

Copy link
Copy Markdown
Collaborator

AI Code Review - PR #1144

Status: BLOCKING

Summary: P0/1 · P1/1 · P2/0 · P3/0

Blocking Issues

P0

  • ViT forward() 返回 tuple,但 embedding()/batched_embedding() 以属性方式访问 .pooler_output/.deepstack_features,运行时 AttributeError @ rtp_llm/multimodal/multimodal_mixins/qwen3_vl/qwen3_vl_mixin.py:125
    • 建议:方案一:在 qwen3_vl_vit.py 定义 NamedTuple(如 Qwen3VLVisionOutput(pooler_output, deepstack_features)),forward_blocks 返回该类型;方案二:在 embedding()/batched_embedding() 中用 tuple 解包 embeds, deepstack_embeds = self.visual(…)。推荐方案一以保持与 Qwen3_5MoeVisionModel(使用 BaseModelOutputWithPooling)的接口一致。

P1

  • 新增 469 行 ViT 实现与重构 mixin 缺少任何测试覆盖 @ rtp_llm/multimodal/multimodal_mixins/qwen3_vl/qwen3_vl_vit.py:1
    • 建议:至少添加:(1) Qwen3VLVisionModel forward 的 shape 验证单测(不同 grid_thw 输入);(2) embedding()/batched_embedding() 端到端验证返回格式及 deepstack shape;(3) 若有小权重可用,补 smoke 测试。

Checklist Violations (6 fail / 81 total)

General Principles Checklist

  • [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue 新增 469 行 ViT 实现与重构 mixin 缺少任何测试覆盖
    469 行新 ViT 实现(含 attention、positional encoding、patch embedding、merger、deepstack 等完整推理链)及 mixin 重构,但无任何测试文件。P0 级别的返回类型不匹配本可被最简单的集成测试捕获。
  • [6.1] Tests — 边界 case 覆盖(空、单元素、最大值) → issue 新增 469 行 ViT 实现与重构 mixin 缺少任何测试覆盖
    embedding/batched_embedding 中的 split_sizes、position_ids 等路径在空 batch、单 token、超大分辨率等边界条件下缺少测试验证。无测试覆盖,边界 case(如 grid_thw 单帧、超大分辨率、空 deepstack_visual_indexes)未验证。

RTP-LLM Checklist

  • [B] 正确性与逻辑 — 逻辑错误、off-by-one、null/zero 检查 → issue ViT forward() 返回 tuple,但 embedding()/batched_embedding() 以属性方式访问 .pooler_output/.deepstack_features,运行时 AttributeError
    Qwen3VLVisionModel.forward() 返回 tuple,但 embedding()/batched_embedding() 通过 .pooler_output/.deepstack_features 属性访问,运行时 AttributeError。
  • [B] 正确性与逻辑 — 接口返回类型变更有兼容处理 → issue ViT forward() 返回 tuple,但 embedding()/batched_embedding() 以属性方式访问 .pooler_output/.deepstack_features,运行时 AttributeError
    原 transformers Qwen3VLVisionModel 返回 ModelOutput 对象(有 .pooler_output/.deepstack_features),本地实现改为返回 plain tuple 但调用方 embedding():125 和 batched_embedding():152 仍按属性访问,运行时 AttributeError。
  • [H] 测试与 CI — 测试覆盖充分:大重构等价覆盖,新功能端到端测试 → issue 新增 469 行 ViT 实现与重构 mixin 缺少任何测试覆盖
    新增 469 行 ViT 模型实现(含 attention、positional encoding、patch embedding、merger、deepstack 等完整推理链)及 mixin 重构,但无任何测试文件。这是一次重大重构(从 HF transformers 依赖切换为自定义实现),至少需要验证数值等价性的集成测试。

Python Static-First Checklist

  • [P.A] 静态结构与类型纪律 — 数据容器用 dataclass/NamedTuple/TypedDict → issue ViT forward() 返回 tuple,但 embedding()/batched_embedding() 以属性方式访问 .pooler_output/.deepstack_features,运行时 AttributeError
    Qwen3VLVisionModel.forward_blocks() 返回 (hidden_states, deepstack_feature_lists) 普通 tuple 作为结构化数据容器;使用 NamedTuple 可同时修复 P0 属性访问崩溃和数据容器规范

Strengths

  • 将 qwen3_vl_mixin 从平铺文件重构到 qwen3_vl/ 子目录,ViT 实现与 mixin 胶水分离,模块划分清晰,与 qwen3_5_moe/ 等同级目录保持一致
  • 自定义 ViT 实现解耦对 transformers 版本的强耦合,便于 deepstack 特性定制和后续性能优化
  • attention backend 级联选择(FA3 > FA2 > SDPA)设计合理,import-time 一次性解析,运行时零开销
  • 所有 import 路径(init.py、qwen3_5_moe_mixin.py)均已正确更新,无遗留引用

@theNiemand theNiemand force-pushed the feature/qwen3-vl-vision-accel branch from 2eeff67 to 124b770 Compare June 29, 2026 08:49
@LLLLKKKK

Copy link
Copy Markdown
Collaborator

AI Code Review - PR #1144

Status: BLOCKING

Summary: P0/0 · P1/1 · P2/1 · P3/1

Blocking Issues

P1

  • 新增 469 行自定义 ViT 实现无任何测试覆盖 @ rtp_llm/multimodal/multimodal_mixins/qwen3_vl/qwen3_vl_vit.py:1
    • 建议:至少添加:(1) ViT forward shape 一致性单测(验证输出维度和 deepstack 输出数量);(2) 与 HuggingFace transformers.Qwen3VLVisionModel 做 forward allclose 数值对比测试;(3) Qwen3-VL 模型端到端 smoke 测试。

Non-blocking Suggestions

P2

  • torch.stack(deepstack_embeds) 对空列表会崩溃 @ rtp_llm/multimodal/multimodal_mixins/qwen3_vl/qwen3_vl_mixin.py:128
    • 建议:在 torch.stack 前增加防御检查:if not deepstack_embeds: return embeds[0].to(self._data_type), pos_id, torch.empty(0, device=self._device, dtype=self._data_type)。或在 Qwen3VLVisionModel.init 中 assert len(config.deepstack_visual_indexes) > 0 给出清晰错误。

P3

  • forward_blocks 中 deepstack_visual_indexes 使用 list 做 O(n) 查找与索引 @ rtp_llm/multimodal/multimodal_mixins/qwen3_vl/qwen3_vl_vit.py:449
    • 建议:在 init 中预构建 self._deepstack_idx_map = {idx: i for i, idx in enumerate(config.deepstack_visual_indexes)},forward_blocks 中改为 idx = self._deepstack_idx_map.get(layer_num); if idx is not None: merger = self.deepstack_merger_list[idx]

Checklist Violations (6 fail / 92 total)

General Principles Checklist

  • [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue 新增 469 行自定义 ViT 实现无任何测试覆盖
    469 行自定义 ViT(attention、rotary embedding、position interpolation、deepstack merger)+ 178 行 mixin 代码,无任何测试。
  • [6.1] Tests — 边界 case 覆盖(空、单元素、最大值) → checklist-only
    无测试覆盖,自然也无边界 case 覆盖(单帧视频、极小/极大分辨率、空 deepstack_visual_indexes 等)。已在测试覆盖 issue 中统一反映。

RTP-LLM Checklist

  • [B] 正确性与逻辑 — 边界 case(空输入、单元素、最大值) → issue torch.stack(deepstack_embeds) 对空列表会崩溃
    torch.stack(deepstack_embeds) 在 config.deepstack_visual_indexes 为空时传入空列表会抛 RuntimeError。标准 Qwen3 config 不为空,但配置错误或自定义 config 场景缺少防御。
  • [D] 性能 — hot path per-forward 内存分配 → checklist-only
    _sdpa_varlen 每次调用分配 [1, seq_len, seq_len] bool 掩码。仅在 SDPA fallback 路径激活(FA3/FA2 均不可用时),非主要执行路径,但该路径被选中后每次 forward 都分配 O(n²) 内存。由于仅影响 fallback 且无直接正确性问题,不升级为 issue。
  • [H] 测试与 CI — 测试覆盖充分:大重构等价覆盖,新功能端到端测试 → issue 新增 469 行自定义 ViT 实现无任何测试覆盖
    469 行自定义 ViT 替换 HuggingFace 实现,属于新功能级变更,需要数值一致性测试和端到端 smoke case。

Python Static-First Checklist

  • [P.D] 性能反模式 — 成员检测用 set/dict 非 list → issue forward_blocks 中 deepstack_visual_indexes 使用 list 做 O(n) 查找与索引
    qwen3_vl_vit.py:449 if layer_num in self.deepstack_visual_indexes 后接 .index(layer_num) 对 list 做两次 O(n) 扫描,可用 dict 映射替代。

Strengths

  • 从 transformers 4.57.1 提取自定义 ViT 实现,消除对 HuggingFace 内部 _attn_implementation dispatch 的依赖,便于后续推理加速优化
  • 注意力后端自动选择 FA3 > FA2 > SDPA,模块加载时一次性解析,兼容不同 GPU 环境
  • 继承链设计合理:Qwen3_VLImageEmbedding 复用父类 get_position_ids、load_video、smart_resize 等通用逻辑,仅覆写差异部分
  • Import 路径从扁平结构迁移到 qwen3_vl/ 子目录,正确更新了 init.py 和 qwen3_5_moe_mixin.py 的所有引用

@theNiemand theNiemand force-pushed the feature/qwen3-vl-vision-accel branch from 124b770 to 2d18033 Compare June 29, 2026 11:54
@LLLLKKKK

Copy link
Copy Markdown
Collaborator

AI Code Review - PR #1144

Status: BLOCKING

Summary: P0/0 · P1/2 · P2/2 · P3/0

Blocking Issues

P1

  • 自定义 ViT 模型(~470行)缺少等价性验证测试 @ rtp_llm/multimodal/multimodal_mixins/qwen3_vl/qwen3_vl_vit.py:1
    • 建议:添加等价性测试:用相同输入分别调用 HF Qwen3VLVisionModel 和自定义 Qwen3VLVisionModel,验证输出 tensor allclose。至少覆盖单图、多图 batch、多帧视频三种场景。同时确认现有 Qwen3 VL smoke 测试覆盖此路径。
  • preprocess_input 新增 device 参数可能破坏 Qwen3_5MoeImageEmbedding 子类 @ rtp_llm/multimodal/multimodal_mixins/qwen3_vl/qwen3_vl_mixin.py:99
    • 建议:将 device 参数仅在 get_preprocess_params 返回中包含 device 时才传递给 image_processor,或在 Qwen3_5MoeImageEmbedding 中覆写 preprocess_input 以避免传递 device。

Non-blocking Suggestions

P2

  • SDPA 回退路径创建 O(seq_len²) 布尔掩码可能导致内存问题 @ rtp_llm/multimodal/multimodal_mixins/qwen3_vl/qwen3_vl_vit.py:15
    • 建议:在 SDPA 回退路径中按 cu_seqlens 分块处理(逐 segment 调用 F.scaled_dot_product_attention),避免构造完整 seq_length x seq_length mask。或加文档说明 SDPA 回退不适用于超大分辨率场景。
  • USE_LOCAL_PREPROCESS 环境变量缺少文档与配置传播 @ rtp_llm/multimodal/multimodal_mixins/qwen3_vl/qwen3_vl_mixin.py:113
    • 建议:统一为读取 vit_config.use_local_preprocess 而非独立环境变量。通过 mm_related_params 传入 vit_config 或在 init 中缓存该配置。

Checklist Violations (8 fail / 71 total)

General Principles Checklist

  • [6.1] Software Engineering — LSP:子类/重写保持基类契约 → issue preprocess_input 新增 device 参数可能破坏 Qwen3_5MoeImageEmbedding 子类
    Qwen3_VLImageEmbedding.preprocess_input 新增 device 参数并始终传递给 processor.image_processor()。子类 Qwen3_5MoeImageEmbedding 继承此方法但使用 Qwen2VLImageProcessor,可能不支持 device kwarg,运行时可能抛出 TypeError。
  • [6.1] Architecture — 兼容性:公开 API/持久数据/配置/环境迁移安全 → issue USE_LOCAL_PREPROCESS 环境变量缺少文档与配置传播
    新增 USE_LOCAL_PREPROCESS 环境变量与现有 vit_config.use_local_preprocess 功能重叠但机制不同,两套并行控制路径可能产生不一致行为。
  • [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue 自定义 ViT 模型(~470行)缺少等价性验证测试
    新增 ~470 行自定义 ViT 实现,将 HF 模型替换为手工提取的版本,但未添加任何单元测试或 smoke 测试来验证输出一致性。
  • [6.1] Tests — 边界 case 覆盖(空、单元素、最大值) → checklist-only
    自定义 ViT 涉及多个边界场景(grid_thw 中 t=0/h=0/w=0、单 patch、极大分辨率、多帧视频),但缺少测试覆盖。此问题已在缺少测试 P1 issue 中涵盖,不单独升级。

RTP-LLM Checklist

  • [D] 性能 — hot path per-forward 内存分配 → issue SDPA 回退路径创建 O(seq_len²) 布尔掩码可能导致内存问题
    _sdpa_varlen 每次调用分配 [1, seq, seq] bool tensor,在 fallback 场景下是 hot path 级别分配,高分辨率输入时可达 GB 级别。
  • [H] 测试与 CI — 测试覆盖充分:大重构等价覆盖,新功能端到端测试 → issue 自定义 ViT 模型(~470行)缺少等价性验证测试
    从 HuggingFace 提取完整 ViT 到本地实现属于大重构,但未新增任何测试验证等价性。应至少确认现有 Qwen3 VL smoke 测试覆盖此路径。

Python Static-First Checklist

  • [P.D] 性能反模式 — 成员检测用 set/dict 非 list → checklist-only
    qwen3_vl_vit.py:449 使用 layer_num in self.deepstack_visual_indexes(list O(k) 成员检测)和 .index() O(k) 线性扫描,但 k 通常仅 2-4 个元素,性能影响可忽略,不升级为 issue。
  • [P.F] 语言陷阱 — 禁止模块级 import 副作用 → checklist-only
    _qwen3_vl_vit.py 在模块级执行 resolve_attn_backend(),尝试 import flash_attn_interface 和 flash_attn 包。这是 ML 框架选择 GPU 后端的常见模式(HF 原代码也有类似行为),不构成具体缺陷。

Strengths

  • 从 HF transformers 提取自定义 ViT 实现并内置 FA3>FA2>SDPA 注意力后端 import 时一次性解析,消除了 per-forward 分支判断和对 HF 内部 API 的版本绑定依赖
  • 文件从单文件重构为 qwen3_vl/ 子目录,与 qwen2_5_vl/、qwen3_5_moe/ 等模型组织结构保持一致

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants