Skip to content

Add multi components recipes - #524

Open
Xiaoyu (xiaoyu-work) wants to merge 9 commits into
mainfrom
xiaoyu/mc
Open

Add multi components recipes#524
Xiaoyu (xiaoyu-work) wants to merge 9 commits into
mainfrom
xiaoyu/mc

Conversation

@xiaoyu-work

@xiaoyu-work Xiaoyu (xiaoyu-work) commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Describe your changes

Add and validate multi-component optimization recipes backed by Olive and Mobius:

  • Gemma 4 E2B: two independent PyTorch component builds under one shared HF output. The decoder uses asymmetric INT4 KQuant (group 32), vision uses symmetric INT4 RTN (group 128), and Olive assembles component-only shards with unchanged audio/embedding weights into a standard HF checkpoint before Mobius export.
  • Qwen2.5-VL-3B-Instruct: replace three independent configs and the custom model implementation with one Olive multi-build config over Mobius decoder, vision, and embedding components.
  • Qwen3-VL-2B-Instruct: export an FP32 Mobius package, dynamically quantize the decoder, convert vision/embedding internals to FP16 while preserving FP32 component interfaces, and assemble a directly loadable optimized ORT GenAI package.

The non-functional SD3 prototype was removed. Current Mobius SD3 graph construction does not match the released checkpoint architecture or pooled-conditioning contract, so retaining that recipe would be misleading.

Validation

  • Gemma 4: olive run assembled the mixed-quantized HF checkpoint; olive capture-onnx-graph --use_mobius_builder exported all four components. Decoder has 205 group-32 asymmetric MatMulNBits nodes, vision has 114 group-128 symmetric nodes. Text returned The capital of France is Paris. and image inference returned A cat is shown in the image.
  • Qwen2.5-VL: all three CPU INT4 component builds passed ONNX checker. Text returned The capital of France is Paris. and image inference returned Cat.
  • Qwen3-VL: all three optimized components passed ONNX checker. Text returned The capital of France is Paris. and image inference returned A cat.
  • CUDA-target Qwen2.5 components were built and passed ONNX checker; runtime inference was not run because this host has no CUDA execution provider.

Copilot AI review requested due to automatic review settings June 29, 2026 23:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds new multi-component Olive recipes and accompanying docs/scripts for:

  • Stable Diffusion 3 Medium (diffusers): export once via Mobius, then optimize specific ONNX components independently.
  • Qwen3-VL-2B-Instruct: two flows (export → per-component optimize, and GPTQ quantize decoder → export) plus an ORT GenAI inference script.

Changes:

  • Introduces SD3 multi-component optimization config, end-to-end ONNX inference script, and documentation.
  • Introduces Qwen3-VL multi-component configs (optimize components; GPTQ quantize-then-export), inference script, and documentation.
  • Registers both as recipes via info.yml and adds local artifact .gitignore rules.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
stabilityai-stable-diffusion-3-medium-diffusers/multi_comp/sd3_optimize_components.json Olive config to optimize SD3 exported ONNX components with per-component builds.
stabilityai-stable-diffusion-3-medium-diffusers/multi_comp/sd3_inference.py SD3 all-ONNX inference script (tokenizers + transformer + VAE decoder).
stabilityai-stable-diffusion-3-medium-diffusers/multi_comp/README.md Step-by-step instructions for export, per-component optimization, and inference.
stabilityai-stable-diffusion-3-medium-diffusers/multi_comp/info.yml Registers SD3 multi-component recipe metadata.
stabilityai-stable-diffusion-3-medium-diffusers/multi_comp/.gitignore Ignores exported/optimized outputs, cache, and generated images for SD3 flow.
stabilityai-stable-diffusion-3-medium-diffusers/LICENSE Adds model licensing notice and links for SD3 Medium weights.
Qwen-Qwen3-VL-2B-Instruct/multi_comp/vlm_quantize_then_export.json Olive config to GPTQ-quantize the decoder component (Torch stage) prior to export.
Qwen-Qwen3-VL-2B-Instruct/multi_comp/vlm_optimize_components.json Olive config to optimize exported ONNX components with different pipelines per component.
Qwen-Qwen3-VL-2B-Instruct/multi_comp/vlm_inference.py ORT GenAI inference script for text-only and image-conditioned generation.
Qwen-Qwen3-VL-2B-Instruct/multi_comp/README.md Documents both Qwen multi-component flows and ORT GenAI inference requirements.
Qwen-Qwen3-VL-2B-Instruct/multi_comp/info.yml Registers Qwen multi-component recipe metadata.
Qwen-Qwen3-VL-2B-Instruct/multi_comp/.gitignore Ignores exported/optimized outputs, cache, and quantized HF checkpoint for Qwen flow.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

from transformers import CLIPTokenizer, T5TokenizerFast

MODEL_ID = "stabilityai/stable-diffusion-3-medium-diffusers"
ONNX_DIR = "exported_sd3_full2"
Comment on lines +70 to +73
# Pad CLIP outputs to 4096 and concatenate
clip_l_padded = np.pad(clip_l_hidden, ((0, 0), (0, 0), (0, 4096 - 768))) # [1, 77, 4096]
clip_g_padded = np.pad(clip_g_hidden, ((0, 0), (0, 0), (0, 4096 - 1280))) # [1, 77, 4096]
encoder_hidden_states = np.concatenate([clip_l_padded, clip_g_padded, t5_hidden], axis=1) # [1, 410, 4096]
Comment on lines +137 to +146
# Verify exported model exists
transformer_path = os.path.join(args.onnx_dir, "transformer", "model.onnx")
if not os.path.exists(transformer_path):
print(f"Error: ONNX model not found at {args.onnx_dir}/")
print(
"Run: olive capture-onnx-graph --model_name_or_path "
"stabilityai/stable-diffusion-3-medium-diffusers "
"--use_mobius_builder --output_path exported_sd3_full2"
)
return
Comment on lines +4 to +11
- name: qwen3vl-2B-Instruct
file: vlm_optimize_components.json
eps:
- CPUExecutionProvider
devices:
- cpu
- name: qwen3vl-2B-Instruct
file: vlm_quantize_then_export.json
--steps N Number of denoising steps (default: 28)
--seed N Random seed (default: 42)
--output PATH Output image path (default: sd3_output.png)
--onnx_dir DIR Path to exported model directory (default: exported_sd3_full2)
complete HF model directory:

```
out/vlm_decoder_gptq_hf/

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated 5 comments.

Suppressed comments (6)

stabilityai-stable-diffusion-3-medium-diffusers/multi_comp/sd3_inference.py:20

  • The documented export and optimization flow creates exported_pkg, but the inference default and its recovery command use exported_sd3_full2 (and the README repeats that default). As a result, the documented inference command fails immediately after the documented export. Align the constant, recovery command, and README on exported_pkg.
ONNX_DIR = "exported_sd3_full2"

stabilityai-stable-diffusion-3-medium-diffusers/multi_comp/sd3_inference.py:63

  • The same CLIP output-contract issue occurs for CLIP-G: SD3 takes pooled projection from output 0 and conditioning from the penultimate hidden state, not output 0 as hidden and output 1 as pooled. Read the exported outputs by name and ensure the penultimate hidden state is exported.
    clip_g_hidden = out_g[0]  # last_hidden_state [1, 77, 1280]
    clip_g_pooled = out_g[1]  # text_embeds [1, 1280]

Qwen-Qwen3-VL-2B-Instruct/multi_comp/README.md:138

  • This path disagrees with the recipe's output_dir: "vlm_decoder_gptq_hf", the following export command, and .gitignore; Olive writes the checkpoint at the working-directory root, not under out/. Following this displayed path sends users to a directory that is never created.
out/vlm_decoder_gptq_hf/

Qwen-Qwen3-VL-2B-Instruct/multi_comp/README.md:115

  • This URL points to Microsoft's unrelated Apache Spark Mobius repository. The ONNX model builder referenced by Olive is onnxruntime/mobius, so this link does not provide the promised ORT GenAI examples.
For the `genai_config.json` structure, see the
[Mobius ORT GenAI examples](https://github.com/microsoft/mobius/tree/main/examples) which write the
config automatically.

Qwen-Qwen3-VL-2B-Instruct/multi_comp/vlm_inference.py:55

  • Passing the raw text alongside images does not insert Qwen's image placeholder tokens. The embedding graph uses those tokens to splice the vision features into the sequence, so this path can ignore the image or fail on incompatible inputs. Build an image/text chat message and apply the tokenizer's chat template before calling the processor, as builtin/inference.py:53-87 does.
    images = og.Images.open(image_path)
    inputs = processor(prompt, images=images)

Qwen-Qwen3-VL-2B-Instruct/multi_comp/README.md:99

  • MobiusBuilder writes the vision sidecar as image_processor.json, not vision_processor.json. Since no step creates the listed filename, the setup layout is inaccurate and users cannot match it to the generated GenAI config.

This issue also appears on line 113 of the same file.

  vision_processor.json      # Vision preprocessing config

Comment on lines +50 to +51
clip_l_hidden = out_l[0] # last_hidden_state [1, 77, 768]
clip_l_pooled = out_l[1] # text_embeds [1, 768]
Comment on lines +71 to +73
clip_l_padded = np.pad(clip_l_hidden, ((0, 0), (0, 0), (0, 4096 - 768))) # [1, 77, 4096]
clip_g_padded = np.pad(clip_g_hidden, ((0, 0), (0, 0), (0, 4096 - 1280))) # [1, 77, 4096]
encoder_hidden_states = np.concatenate([clip_l_padded, clip_g_padded, t5_hidden], axis=1) # [1, 410, 4096]
}
},
"engine": { "host": "local_system", "target": "local_system", "evaluate_input_model": false, "cache_dir": "cache" },
"builds": {
},
"passes": { "to_fp16": { "type": "OnnxFloatToFloat16" }, "dynamic_quant": { "type": "OnnxDynamicQuantization" } },
"engine": { "host": "local_system", "target": "local_system", "evaluate_input_model": false, "cache_dir": "cache" },
"builds": {
},
"passes": { "decoder_gptq": { "type": "Gptq", "bits": 4, "group_size": 128, "sym": true, "lm_head": false } },
"engine": { "host": "local_gpu", "target": "local_gpu", "evaluate_input_model": false, "cache_dir": "cache" },
"builds": {
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