Support DeepStack (Qwen3-VL) export - #601
Open
Tachion (SanjayAMD) wants to merge 1 commit into
Open
Conversation
…er, genai_config Signed-off-by: srgaddam <srgaddam@amd.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Adds DeepStack (Qwen3-VL) feature export support for the Qwen3-VL-4B recipe so intermediate vision hidden states are preserved through vision.onnx → embedding.onnx → decoder.onnx and surfaced in genai_config.json for onnxruntime-genai consumption.
Changes:
- Extend vision export to output
deepstack_features_{0,1,2}alongsideimage_features. - Update embedding export to accept DeepStack per-token features and scatter them into full-length
(batch, seq, hidden)tensors (deepstack_{0,1,2}). - Emit DeepStack tensor name arrays in
genai_config.jsonand rename output dims in Olive vision configs.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| Qwen-Qwen3-VL-4B-Instruct/builtin/codes/modeling_qwen3_vl.py | Captures DeepStack vision features and returns/scatters them through the model’s embedding path. |
| Qwen-Qwen3-VL-4B-Instruct/builtin/user_script.py | Updates IO configs and dummy inputs for DeepStack tensors; adds local model dir support. |
| Qwen-Qwen3-VL-4B-Instruct/builtin/optimize.py | Writes DeepStack input/output tensor name arrays into genai_config.json. |
| Qwen-Qwen3-VL-4B-Instruct/builtin/cuda/vision.json | Renames dim-0 for added DeepStack vision outputs to num_logical_patches. |
| Qwen-Qwen3-VL-4B-Instruct/builtin/cpu_and_mobile/vision.json | Renames dim-0 for added DeepStack vision outputs to num_logical_patches. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+886
to
890
| Returns a tuple `(image_features, deepstack_0, deepstack_1, deepstack_2)`. vision.onnx is | ||
| exported single-image (VisionState loops per-image in C++), so no per-image split is needed; | ||
| the DeepStack features are returned separately for per-layer injection in the text decoder. | ||
| """ | ||
| pixel_values = pixel_values.type(self.visual.dtype) |
Comment on lines
+25
to
+29
| # Find safetensors file(s): support local directory or HF hub repo id | ||
| if model_path and os.path.isdir(model_path): | ||
| model_dir = model_path | ||
| else: | ||
| from huggingface_hub import hf_hub_download |
Sunghoon Choi (hanbitmyths)
requested a review
from Akshay Sonawane (apsonawane)
September 2, 2026 19:37
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds DeepStack support to the Qwen3-VL-4B export recipe so the exported ONNX models carry the intermediate vision features the model needs for fine-grained visual understanding (OCR, documents, diagrams, grounding).
Qwen3-VL's vision encoder emits, in addition to the merged image embedding, a small set of intermediate hidden-state feature maps ("DeepStack" features — one per entry in
vision_config.deepstack_visual_indexes, i.e. 3 for the 4B model, from ViT layers[5, 11, 17]). These are scattered onto the image-token positions and injected into the decoder residual stream after the first few decoder layers (featurei→ after layeri), matching Hugging Face'sQwen3VLTextModel._deepstack_process.The previous recipe dropped these features (vision returned only the merged embedding,
update_genai_configwrote no DeepStack fields), so the exported model lost accuracy. This PR wires them through the vision export, the embedding scatter, andgenai_config.json.This is the export-side counterpart to the onnxruntime-genai runtime PR that consumes these tensors; both are required for end-to-end DeepStack.
How it works
The three sub-models exchange DeepStack tensors as follows:
During generation there are no image tokens, so
embedding.onnxemits all-zeros fordeepstack_{i}and the decoder Adds become no-ops.Changes (
Qwen-Qwen3-VL-4B-Instruct/builtin, 5 files)codes/modeling_qwen3_vl.pyQwen3VLVisionModel.forward: capture the hidden states atdeepstack_visual_indexes, run them through their per-index patch mergers, and return them separately as(image_features, *deepstack_feats)instead of folding them into the merged features.get_image_features: propagate the tuple.get_fused_input_embeddings: acceptdeepstack_features_0/1/2,masked_scattereach into full-length(batch, seq, hidden)zero tensors at image-token positions, and return(inputs_embeds, deepstack_0, deepstack_1, deepstack_2).user_script.pyget_vision_io_config: adddeepstack_features_0/1/2to vision output names.get_embedding_io_config: add the DeepStack inputs/outputs and dynamic axes.get_embedding_dummy_inputs: add matching dummy DeepStack tensors for tracing._load_base_model: accept a local model directory (not only an HF hub id).cpu_and_mobile/vision.json,cuda/vision.jsonRenameOutputDimsfor the 3 new vision outputs (dim-0 →num_logical_patches).optimize.pyupdate_genai_config: emit the DeepStack tensor-name arrays —vision.outputs.deepstack_features,embedding.inputs.deepstack_features,embedding.outputs.deepstack, anddecoder.inputs.deepstack.Validation
vision.jsonfiles parse.image_features+ one feature perdeepstack_visual_index(3), all with matching shape; and that the embedding scatter fills only image-token positions (all other positions remain zero, so the decoder Adds are no-ops during generation).Scope / notes
optimize.pyexport run was not included here (requires the ~8GB base checkpoint); the model changes are covered by the functional test and by the previously-exported, runtime-validated model produced by the equivalent code.