[MM][CG] Support ViT full CUDA graph for Ernie-4.5-VL image inference - #45254
Conversation
|
Documentation preview: https://vllm--45254.org.readthedocs.build/en/45254/ |
|
Hi @shen-shanshan, could you also review when getting a chance? Thanks! |
|
Can you verify the multimodal accuracy with ViT CG as well? |
|
Thanks @Isotr0py! Finished verification on ERNIE-4.5-VL-28B-A3B on H100 with benchmarks AI2D and MMStar, toggling the ViT CUDA graph via compilation_config={"cudagraph_mm_encoder": ...} Both within stderr and show enabling the ViT CUDA graph does not change accuracy |
|
Hi @shen-shanshan @Isotr0py, could you review the PR when getting a chance? Thanks! |
|
This pull request has merge conflicts that must be resolved before it can be |
59d5051 to
2f0cc33
Compare
c44a606 to
54f466a
Compare
| | Architecture | Models | CG for Image | CG for Video | Dual-Path Graph | | ||
| | ------------ | ------ | ------------ | ------------ | --------------- | | ||
| | `DeepseekOCRForCausalLM` | `DeepSeek-OCR` | ✅︎ | ❌︎ | ✅︎ | | ||
| | `Ernie4_5_VLMoeForConditionalGeneration` | `ERNIE-4.5-VL` | ✅︎ | ❌︎ | ❌︎ | |
There was a problem hiding this comment.
As far as I know, this model supports video inference, does this PR support?
There was a problem hiding this comment.
Yes it supports video inference, but this PR supports image only for now
ERNIE's video path applies a use_temporal_conv in the resampler that changes the output token count (code), which needs separate handling. Thus I plan to work on it as a follow-up PR
This PR only adds the image encoder CUDA graph, and Eager video inference is unaffected. Thus ❌ in the Video column for now.
Does the follow-up PR plan sound good to you or any suggestions? Thanks!
| def get_input_modality(self, mm_kwargs: dict[str, Any]) -> str: | ||
| if "image_grid_thw" in mm_kwargs: | ||
| return "image" | ||
| raise AssertionError("This line should be unreachable.") |
There was a problem hiding this comment.
If it only supports image modality, this method is not needed, FYI, #44484.
There was a problem hiding this comment.
Thank you for pointing out! Removed
| def _get_pixel_values_by_modality(self, mm_kwargs: dict[str, Any]) -> torch.Tensor: | ||
| return mm_kwargs["pixel_values"] | ||
|
|
||
| def _get_grid_thw_by_modality(self, mm_kwargs: dict[str, Any]) -> list[list[int]]: | ||
| grid_thw = mm_kwargs["image_grid_thw"] | ||
| if not isinstance(grid_thw, list): | ||
| grid_thw = grid_thw.tolist() | ||
| return grid_thw |
There was a problem hiding this comment.
Thank you for pointing out!
| grid_thw_list = self._get_grid_thw_by_modality(batch_mm_kwargs) | ||
| num_valid = sum(t * h * w for t, h, w in grid_thw_list) | ||
| grid_thw = torch.tensor(grid_thw_list, device=output.device) | ||
| image_embeds = self.resampler_model(output[:num_valid], grid_thw) |
There was a problem hiding this comment.
In VariableResolutionResamplerModel, if use_temporal_conv=False, I suppose it's CUDA graph compatible? Since it only contains linear layer and activation layer.
There was a problem hiding this comment.
Yes. If use_temporal_conv=False the resampler is just linear and activation and would be CUDA graph compatible.
However ERNIE-4.5-VL-28B-A3B ships with use_temporal_conv=True from Ernie4_5_VLMoEConfig.__init__ in the model's bundled configuration_ernie4_5_vl.py
With that flag, VariableResolutionResamplerModel.forward always takes the fwd_placeholder path. Hence only ViT is put into the CUDA graph in this PR and the resampler stays on the eager path
Including the resampler in the CUDA graph for use_temporal_conv=False could be done in the follow-up PR
Does it sound good or any suggestions?
|
Hi @shen-shanshan, thank you for your feedback. I finished adding changes and reply. Could you review again when getting a chance? Thank you! |
|
This pull request has merge conflicts that must be resolved before it can be |
Signed-off-by: Qiuyang Yue <yueqiuyang1389@gmail.com>
Signed-off-by: Qiuyang Yue <yueqiuyang1389@gmail.com>
Signed-off-by: Qiuyang Yue <yueqiuyang1389@gmail.com>
…iuyang1389@gmail.com> Signed-off-by: Qiuyang Yue <yueqiuyang1389@gmail.com>
Signed-off-by: Qiuyang Yue <yueqiuyang1389@gmail.com>
fd2210f to
7a82531
Compare
Signed-off-by: Qiuyang Yue <yueqiuyang1389@gmail.com>
Signed-off-by: qyYue1389 <yueqiuyang1389@gmail.com>
Signed-off-by: qyYue1389 <yueqiuyang1389@gmail.com>
Signed-off-by: qyYue1389 <yueqiuyang1389@gmail.com>
Signed-off-by: Qiuyang Yue <yueqiuyang1389@gmail.com>
|
Thanks for the approval @Isotr0py! It looks like the fork PR's owner:branch ref (the :) trips the bootstrap branch-name check, so retriggering by pushing to my fork just re-hits this. |
|
Hi @Isotr0py, thanks for re-triggering the CI. Looks both CI failures are in test_can_initialize_large_subset on GLM-4.7-Flash / Glm4MoeLite models, which should be unrelated to this PR changes Could you please rerun the two failing initialization jobs or maybe merge when getting a chance? Thanks a lot! |
|
Hi @Isotr0py, sorry looks the CI failed again. Both failures are in Mamba and sampling test areas and involve pfnet/plamo-2-1b / facebook/opt-125m, none of which is related to this PR. Probably they are flaky tests? Could you please retry the two failing jobs (or re-run after a merge with main)? Pls let me know if we need to rebase this PR from main Thank you so much! |
|
Hi @shen-shanshan, sorry the CI failed again. Both failures are in Mamba and sampling test areas and involve pfnet/plamo-2-1b / facebook/opt-125m, looks none of which is related to this PR. Probably they are flaky tests? Could you please retry the two failing jobs and help merge (or re-run after a merge with main)? Pls let me know if we need to rebase this PR from main Thank you so much! |
|
/ci retry |
|
✅ No earlier CI build exists for this PR. Use |
|
/ci run |
|
✅ Triggered Buildkite CI #82516 for commit |
|
/ci retry |
|
✅ Queued 2 failed job(s) for retry in Buildkite CI #82516. |
|
/ci run |
|
✅ Triggered Buildkite CI #82578 for commit |
|
/ci retry |
|
✅ Queued 4 failed job(s) for retry in Buildkite CI #82578. |
Purpose
Adds encoder (ViT) CUDA graph support for
Ernie4_5_VLMoeForConditionalGeneration(image inputs), under the ViT Full CUDA Graph tracker #38175. Follows the Qwen3-VLSupportsEncoderCudaGraphreference.Key Changes
Splits the inline rotary /
cu_seqlens/max_seqlencomputation out ofErnie4_5_VisionTransformer.forwardintoprepare_encoder_metadata(), so it can be precomputed on the host and fed into the captured graph via fixed buffers. Ernie consumes a single rotaryfreqstensor, so the buffer set is 4 keys (pixel_values,rotary_pos_emb,cu_seqlens,max_seqlen)Capture boundary (ViT only) Ernie's spatial-merge & projection live in a separate
VariableResolutionResamplerModelwhose temporal path does host-side work (grid_thw.cpu().numpy()& dynamicindex_select) that can't be captured. The resampler therefore runs eagerly inpostprocess_encoder_output(graph path) andencoder_eager_forward(eager fallback)Image only Video has
use_temporal_convin the resampler that changes the output token count (code), which needs separate handling. A separate PR is needed for video and will be raised after this one is merged.Test
1. Functional
ernie45_vl(image,load_format=dummy) totests/models/multimodal/generation/test_vit_cudagraph.py.pytest tests/models/multimodal/generation/test_vit_cudagraph.py::test_vit_cudagraph_image[ernie45_vl]- E2E encoder CG capture & replay & eager-resampler postprocess2. Benchmark (H100 80GB)
3. Accuracy (CG on vs off)
CG toggled via
compilation_config={"cudagraph_mm_encoder": ...}on the same PR branch with lmms-eval and greedy decodingBoth within stderr and show enabling the ViT CUDA graph does not change accuracy.
Output parity (comparing raw outputs directly): greedy-decoded 50 images with the encoder CUDA graph ON vs OFF: 47/50 (94%) byte-identical outputs. The 3 diffs are single-letter flips on near-tie questions, expected since the graph replays from padded fixed buffers (numerically close, not bit-identical). The ViT CUDA graph does not change the model's outputs.
(MMStar's absolute is low for both arms, but that's the benchmark, not a vision/setup issue. The same model scores AI2D 0.65 in this setup, so vision is fine. MMStar's fine-grained multiple-choice is hard for Ernie here. It affects eager and CG identically, so it doesn't affect the accuracy comparison.)