[Metax][Optimization] 优化 PaddleOCR-VL 在 Metax GPU 上的视觉路径开销#7619
[Metax][Optimization] 优化 PaddleOCR-VL 在 Metax GPU 上的视觉路径开销#7619Dryoung95 wants to merge 1 commit intoPaddlePaddle:developfrom
Conversation
|
Thanks for your contribution! |
c6b3374 to
162c6f5
Compare
|
目前没有看到编译失败、测试失败或 smoke test 失败的代码级证据,更像是 runner / Jenkins remoting 层问题。 麻烦帮忙 rerun 一下这条检查。 |
PaddlePaddle-bot
left a comment
There was a problem hiding this comment.
🤖 AI Code Review |
2026-04-25 00:59:14
📋 Review 摘要
PR 概述:针对 PaddleOCR-VL 在 Metax GPU 上的视觉推理路径做小步性能优化,通过减少不必要的 host/device 数据同步、合并视觉特征处理、引入位置编码 LFU 缓存等手段降低视觉分支开销。
变更范围:paddleocr_vl/projector.py、paddleocr_vl/siglip.py、paddleocr_vl/siglip_ops.py、worker/metax_model_runner.py
影响面 Tag:Models Metax
问题
| 级别 | 文件 | 概述 |
|---|---|---|
| 🟡 建议 | siglip.py:339-364 |
SiglipEncoderLayer.forward 中 batch=1 快速路径与通用路径逻辑完全重复 |
| ❓ 疑问 | siglip_ops.py:39 |
apply_rotary_pos_emb_vision 精度职责转移后缺少说明,新调用点可能静默引入精度损失 |
正确性核实
以下改动经代码库探索验证,均为预期行为:
-
SiglipMLP.forward去掉hidden_states[0]:是一处 bug fix。原本[0]在 3D 输入[1, seq, D]时取 batch=0(正确),但经SiglipEncoderLayer的 batch=1 快速路径 squeeze 后变为 2D[seq, D],再做[0]就只取第一行 token(错误)。移除[0]后行为正确。✓ -
位置编码改用
fetch_position_embedding_lfu_cache(embeddings, h, w)(传全量 tensor):interpolate_pos_encoding仅使用embeddings.shape[-1](隐藏维度 D),不依赖 embeddings 的具体内容,因此传全量 tensor 与传切片结果完全一致。✓ -
mm_num_token_func接受 tuple:paddleocr_vl_processor的mm_num_tokens实现中对grid_thw做了isinstance(paddle.Tensor)检测,非 Tensor 时直接map(int, thw)处理,支持 tuple 输入。✓ -
_build_merge_permutation替换 einops rearrange:numpy 手工 transpose(0,1,3,2,4)等价于原rearrange (t h p1 w p2) -> (t h w) (p1 p2 d)的 merge 逻辑,数学语义一致。✓
总体评价
整体改动方向清晰,优化点均有可复现的 profiling 支撑,功能验证充分。SiglipEncoderLayer 中的代码重复是后续维护的主要隐患,建议在合入前或后续 PR 中提取公共方法消除重复;其余为低风险疑问,不阻塞合入。
|
|
||
| hidden_states_out = residual + mlp_out | ||
|
|
||
| return (hidden_states_out.unsqueeze(0),) |
There was a problem hiding this comment.
🟡 建议 SiglipEncoderLayer.forward batch=1 快速路径与通用路径逻辑完全重复
第 339–364 行(快速路径)与第 366–391 行(通用路径)计算逻辑一模一样,仅返回时多了 unsqueeze(0)。如后续有人修改通用路径(如新增 dropout、调整 layernorm 位置)而忘记同步快速路径,容易引入难以发现的不一致。
建议提取为私有方法复用:
def _forward_2d(self, hidden_states_2d, ...):
residual = hidden_states_2d
x = self.self_attn(self.layer_norm1(hidden_states_2d), ...)
hs_post_attn = residual + x
out = hs_post_attn + self.mlp(self.layer_norm2(hs_post_attn))
return out
def forward(self, hidden_states, ...):
if hidden_states.dim() == 3 and hidden_states.shape[0] == 1:
return (self._forward_2d(hidden_states[0], ...).unsqueeze(0),)
...| @@ -37,27 +37,33 @@ def rotate_half(x): | |||
|
|
|||
|
|
|||
| def apply_rotary_pos_emb_vision(x, cos, sin): | |||
There was a problem hiding this comment.
❓ 疑问 apply_rotary_pos_emb_vision 不再保证 float32 精度
原实现在函数内做了 x.astype("float32") → 计算 → cast back,现在精度保证完全转移到调用方(native_neox_rope_embedding)。当前仓库中该函数只在 native_neox_rope_embedding 中调用,行为正确。但函数签名/注释没有说明「调用方需保证 x 是 float32」的新约定,若后续出现新调用点,容易在 bfloat16 下静默引入精度损失。建议在函数 docstring 或注释中注明 x 需为 float32。
背景
本 PR 面向 PaddleOCR-VL 在 Metax GPU 上的推理路径做小步性能优化,目标是降低视觉分支的额外开销,提升实际服务场景下的响应效率。
在前期 profiling 与多轮实验中,热点持续集中在
extract_vision_features_paddleocr()邻近路径,尤其是视觉特征提取后的元数据组织、位置编码准备以及 projector 邻近的数据流处理。因此,本次提交只保留已经验证通过、风险较低、收益可复现的一组改动。问题现象
在 Metax GPU 上运行 PaddleOCR-VL 时,视觉路径存在以下额外开销:
FD_ENABLE_MAX_PREFILL=1主路径下,projector 邻近数据流仍有可精简空间前期多轮实验表明:
本次改动
本 PR 仅保留最终确认可用的一组最小改动,涉及以下文件:
fastdeploy/model_executor/models/paddleocr_vl/projector.pyfastdeploy/model_executor/models/paddleocr_vl/siglip.pyfastdeploy/model_executor/models/paddleocr_vl/siglip_ops.pyfastdeploy/worker/metax_model_runner.py最终保留的核心优化点是:
extract_vision_features_paddleocr()中,projector 直接复用 worker 已有的 hostgrid_thw_lstFD_ENABLE_MAX_PREFILL=1主路径里一处固定的tensor -> cpu().numpy()元数据同步验证结果
功能与稳定性
已完成以下验证:
/health返回 200/v1/models返回正常SIGBUS、请求长时间不返回、客户端超时断开等问题性能结果
基于 Opt3 稳定基线进行对照,本次保留方案(Opt5)表现如下:
7.4039s -> 7.3870s,约-0.23%-6.07%-5.97%-9.73%整体结论:
profiling 结论
在保留方案的并发专项复验中:
extract_vision_features_paddleocr()邻近路径因此,本 PR 的定位是:
风险与限制
本 PR 也有明确边界:
后续计划
后续会继续围绕以下方向推进:
extract_vision_features_paddleocr()邻近数据流继续压缩说明
本 PR 只提交已验证通过且建议保留的代码改动,不包含前期实验分支中的高风险尝试与临时 profiling 产物。