Skip to content

【Task.17】DeepEyes processor 动态 patch #191

Description

@yuki-younai

任务信息

  • 题目:TASK 17「DeepEyes processor 动态 patch」
  • 认领人:@yuki-younai
  • 背景:examples/deepeyes/ 当前通过 cp 把整份 qwen_vl.py 覆盖进 SGLang 安装目录,依赖 /sgl-workspace 且易随上游升级失效。
  • 目标:启动时用 monkey-patch 或 processor 注册机制,仅注入真实差异。
  • 验收标准:删除所有覆盖安装目录的 cp 与 /sgl-workspace 硬编码;patch 仅覆盖必要方法、重复 import/调用幂等;不兼容 SGLang 版本时 fail fast 并- 提示;同一输入下 patch 前后关键输出一致;DeepEyes 端到端 smoke 通过;有单测和升级说明。
  • 交付方式:Issue + PR。
  • 关联PR:@Task.17】DeepEyes processor 动态 patch #188

任务

examples/deepeyes/ 当前通过 cp 把整份 qwen_vl.py 覆盖进 SGLang 安装目录,依赖 /sgl-workspace 且易随上游升级失效。改用启动时 monkey-patch,仅注入真实差异。

问题根因

run_deepeyes_r3.sh 执行:

cp examples/deepeyes/qwen_vl.py /sgl-workspace/sglang/python/sglang/srt/multimodal/processors/qwen_vl.py

把 454 行旧版 qwen_vl.py 整文件覆盖到安装目录。问题:

  • 硬编码 /sgl-workspace,SGLang 升级即失效;
  • 整文件覆盖回退 Docker 构建时的 sglang.patch(v0.5.12 把 load_mm_data 切到 legacy_load_mm_data),引入隐蔽不一致;
  • 真实差异淹没在 400+ 行样板里,维护成本高。

真实差异

DeepEyes 多轮 rollout 发送预分词 input_ids(每图 N 个 <|image_pad|>),原生 QwenVLImageProcessor.process_mm_data_async 处理不了:

  • A. token 漂移:decode→retokenize 非无损,token 数变化,错位 mrope。
  • B. N×M 爆炸:N 个 <|image_pad|> 被当 N 个图片占位符,每个再展开成 M 个 patch token。

真正定制只有两处:

  1. _strip_image_token:load_mm_data 前折叠连续 <|image_pad|>;
  2. original_input_ids 保存/恢复:恢复原始 token 序列并据其重算 mrope。

方案

沿用 Relax 已有的「SGLang 启动时 monkey-patch」机制(与 OPD patch 模式一致),env flag 控制,默认关闭:

--deepeyes-qwen-vl-patch  →  RELAX_DEEPEYES_QWEN_VL_PATCH=1  →  SGLang 子进程启动时 apply
  • 仅替换 process_mm_data_async + 新增 _strip_image_token,其余从上游导入;
  • 幂等(_PATCH_FLAG);不兼容版本 fail fast(_verify_upstream_api 提示 sglang 0.5.12.post1);
  • 文本输入无副作用;返回值版本兼容(MultimodalProcessorOutput.from_dict / dict 回落);
  • 重写方法体而非纯 wrapper,因 mrope 必须用恢复后的 ids 重算。

验收对照

验收标准 状态
删除 cp / /sgl-workspace 硬编码 ✅
patch 仅覆盖必要方法、幂等 ✅
不兼容 SGLang 版本 fail fast ✅
同一输入前后关键输出一致 ✅ verify_patch_consistency.py
DeepEyes e2e smoke 通过 ✅ 多轮 rollout→train step
有单测和升级说明 ✅ 15 单测 + 双语文档

测试结果

  • 单测:pytest tests/backends/sglang/test_qwen_vl_patch.py → 15 passed
  • 真实 sglang 0.5.12.post1:_verify_upstream_api() PASS,apply_qwen_vl_patches() → True
  • 前后一致性:verify_patch_consistency.py → ALL PASS(文本输入 no-op;预分词输入只改 input_ids+mrope)
  • e2e smoke:DEEPEYES_PATCH=1 跑 Qwen3-VL-4B GRPO,完成 step 0/1/2,0 Traceback / 0 ERROR

实验环境

项目 配置
镜像 / OS / Python ghcr.io/redai-infra/relaxrl:latest / Ubuntu 24.04 / 3.12.3
SGLang 0.5.12.post1(patch 目标版本)
GPU 4 × NVIDIA H20
模型 / 算法 Qwen3-VL-4B-Instruct / GRPO
训练模式 / 并行 Colocate / TP=2, DP=2
批量 / 序列 rollout-batch 32, global-batch 256, n-samples 8 / prompt+response 各 2048
数据 datasets/deepeyes-v1/(两个 parquet 各 @[0:5000])
Judge 远程 OpenAI 兼容 endpoint(openai_judge_service.sh)
patch 启用 DEEPEYES_PATCH=1 bash examples/deepeyes/run_deepeyes_baseline.sh

升级说明

见 docs/{en,zh}/guide/sglang-patches.md(SGLang 升级 checklist)。

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions