Skip to content

Fix Olive-quantized Qwen3 MoE export - #525

Open
titaiwangms wants to merge 2 commits into
mainfrom
fix/moe-packed-fused-expert-weights
Open

Fix Olive-quantized Qwen3 MoE export#525
titaiwangms wants to merge 2 commits into
mainfrom
fix/moe-packed-fused-expert-weights

Conversation

@titaiwangms

Copy link
Copy Markdown
Contributor

Summary

  • preserve packed Olive/GPTQ/AWQ fused expert tensors through generic MoE weight renaming so the QMoE packer receives the original expert-major layout
  • keep unquantized fused-expert splitting unchanged and centralize packed-sidecar suffix detection
  • emit the supported ORT GenAI qwen3 runtime type for qwen3_moe, preserving Qwen3 reasoning token metadata
  • add weight-preprocessing, graph, runtime-config, and regression coverage

This completes the generic Qwen3-MoE KQuant path built on #517. Before this change, packed gate_up_proj_qweight and _scales sidecars collided after being split into the same per-expert .weight keys, causing a deterministic weight-shape mismatch during export.

Validation

  • 220 passed — focused MoE, weight utility, Qwen3.5, and QMoE fusion tests
  • 94 passed, 8 skipped — quantized/MoE graph tests
  • 192 passed — ORT GenAI integration tests
  • Ruff check and format passed on all changed Python files
  • full Qwen/Qwen3-30B-A3B Olive KQuant + Mobius export completed successfully
  • exported 16 GB package loaded with ORT GenAI 0.16.0-dev on an A100 80 GB
  • Qwen3 reasoning IDs resolved correctly (bor=151667, eor=151668)
  • CUDA greedy generation produced coherent output and correctly answered 17 * 23 = 391

Preserve packed fused expert sidecars until QMoE packing and emit a supported Qwen3 ORT GenAI runtime type with reasoning-token metadata.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: e10674c1-6909-4b09-9f5a-d41b28c89d2d
@titaiwangms
titaiwangms requested review from a team and a lite review from Copilot August 20, 2026 19:37
@github-actions

github-actions Bot commented Aug 20, 2026

Copy link
Copy Markdown

Performance Comparison

Comparing 66e9c5e94ee2a6

Model Metric Baseline Current Delta
bert (feature-extraction) model_size_bytes 359 KB 359 KB +0.0%
bert (feature-extraction) num_nodes 60 60 +0.0%
falcon model_size_bytes 364 KB 364 KB +0.0%
falcon num_nodes 66 66 +0.0%
gemma2 model_size_bytes 428 KB 428 KB +0.0%
gemma2 num_nodes 105 105 +0.0%
gpt2 model_size_bytes 388 KB 388 KB +0.0%
gpt2 num_nodes 54 54 +0.0%
llama model_size_bytes 425 KB 425 KB +0.0%
llama num_nodes 60 60 +0.0%
llama (static-cache) model_size_bytes 425 KB 425 KB +0.0%
llama (static-cache) num_nodes 56 56 +0.0%
mamba (ssm-text-generation) model_size_bytes 296 KB 296 KB +0.0%
mamba (ssm-text-generation) num_nodes 94 94 +0.0%
phi3 model_size_bytes 421 KB 421 KB +0.0%
phi3 num_nodes 58 58 +0.0%
phi3 (static-cache) model_size_bytes 421 KB 421 KB +0.0%
phi3 (static-cache) num_nodes 54 54 +0.0%
qwen2 model_size_bytes 425 KB 425 KB +0.0%
qwen2 num_nodes 60 60 +0.0%
qwen2 (static-cache) model_size_bytes 425 KB 425 KB +0.0%
qwen2 (static-cache) num_nodes 56 56 +0.0%
qwen3_5_moe (hybrid-text-generation) model_size_bytes 506 KB 506 KB +0.0%
qwen3_5_moe (hybrid-text-generation) num_nodes 264 264 +0.0%
qwen3_5_text (hybrid-text-generation) model_size_bytes 458 KB 458 KB +0.0%
qwen3_5_text (hybrid-text-generation) num_nodes 126 126 +0.0%
qwen3_5_vl (hybrid-qwen-vl) model_size_bytes 977 KB 977 KB +0.0%
qwen3_5_vl (hybrid-qwen-vl) num_nodes 428 428 +0.0%
t5 (seq2seq) model_size_bytes 836 KB 836 KB +0.0%
t5 (seq2seq) num_nodes 166 166 +0.0%
whisper (speech-to-text) model_size_bytes 1008 KB 1008 KB +0.0%
whisper (speech-to-text) num_nodes 128 128 +0.0%

No performance regressions.

@titaiwangms

Copy link
Copy Markdown
Contributor Author

The current all-files Lint failure is inherited from main after #378: src/mobius/_inspect_test.py is not Ruff-formatted. I opened the formatting-only #526 instead of mixing that unrelated change into this PR. The Qwen3-MoE changed files pass Ruff/lintrunner locally and the separate lintrunner check is green.

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

This PR fixes export of Olive/GPTQ/AWQ quantized Qwen3-MoE by preserving packed fused-expert sidecar tensors during HF→ONNX key renaming, ensuring the QMoE packer receives the correct expert-major layout. It also updates ORT GenAI auto-export to emit a supported runtime model.type for qwen3_moe, and adds regression coverage across weight preprocessing, graph construction, and runtime config generation.

Changes:

  • Preserve packed quantized MoE expert sidecar keys during generic MoE renaming via a shared is_packed_quant_key(...) predicate.
  • Map qwen3_moeqwen3 for ORT GenAI genai_config.json to avoid unsupported type errors while keeping Qwen3 reasoning-token metadata behavior.
  • Add targeted regression tests for the Qwen3-MoE quantized graph ABI and weight-preprocess behavior.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tests/build_graph_test.py Adds a graph/initializer ABI regression test for Olive-int4 Qwen3-MoE emitting QMoE + MatMulNBits with expected shapes.
src/mobius/models/moe.py Prevents splitting packed quantized expert sidecars during _rename_moe_expert_weights.
src/mobius/models/moe_test.py Adds end-to-end tests ensuring packed fused expert sidecars survive renaming and bind to QMoE parameters.
src/mobius/integrations/ort_genai/auto_export.py Maps qwen3_moe to ORT GenAI-supported qwen3 type.
src/mobius/integrations/ort_genai/auto_export_test.py Adds tests covering qwen3_moe model type resolution in both hf-id and config export modes.
src/mobius/_weight_utils.py Introduces is_packed_quant_key and centralizes packed-sidecar suffixes; reuses predicate in quantized preprocessing validation.
src/mobius/_weight_utils_test.py Adds unit tests for is_packed_quant_key.
CHANGELOG.md Documents the packed-sidecar preservation fix and the ORT GenAI qwen3_moe type resolution fix.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/mobius/models/moe_test.py
@github-actions

github-actions Bot commented Aug 20, 2026

Copy link
Copy Markdown

🏗️ Architecture Diff

Comparing 66e9c5e94ee2a6

Model Sub-model Changes Status

No architecture changes detected.


Legend: ⚪ No change · 🔵 Minor (attrs/inits) · 🟡 Moderate (nodes added/removed) · 🔴 Major (interface changed)

"""Whether ``key`` is a packed sidecar of the specific ``float_key``.

Unlike the module-level :func:`is_packed_quant_key` suffix predicate,
this matches the *exact* sidecar keys of one named float parameter.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What is a sidecar?

# (``<pname>_qweight``, see Olive's ``olive/common/quant/state_dict.py``), while
# GPTQ/AWQ store dotted sibling buffers on the owning module
# (``<module>.qweight``). Both conventions occur in raw HF checkpoints.
OLIVE_PACKED_QUANT_SUFFIXES = ("_qweight", "_scales", "_qzeros")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Use a frozen set?

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.

3 participants