feat(weight-free): weight-free ONNX export for causal LMs (dynamo path) - #1181
feat(weight-free): weight-free ONNX export for causal LMs (dynamo path)#1181amarquic wants to merge 54 commits into
Conversation
Squash of prior dynamo work (16 WIP commits) into a single commit to simplify rebase onto mainline. Original history preserved at tag backup/before-rebase-20260501-212703. Contents: - Enable torch.dynamo-based export with inline compiler across causal LMs, VLMs, embedding, ASR models. - Subfunction extraction / nested compile region utilities. - Torch patches and custom-op utils consolidated under QEfficient/utils. - Dynamo-specific tests and e2e example. (cherry picked from commit 70f0cac) Signed-off-by: Sharvari Medhe <smedhe@qti.qualcomm.com>
…r models Signed-off-by: vbaddi <vbaddi@qti.qualcomm.com> (cherry picked from commit 087f8d4) Signed-off-by: Sharvari Medhe <smedhe@qti.qualcomm.com>
Signed-off-by: vbaddi <vbaddi@qti.qualcomm.com> (cherry picked from commit 5fa0d57) Signed-off-by: Sharvari Medhe <smedhe@qti.qualcomm.com>
…ck previous commit, if this fails Signed-off-by: vbaddi <vbaddi@qti.qualcomm.com> (cherry picked from commit 2ca7b45) Signed-off-by: Sharvari Medhe <smedhe@qti.qualcomm.com>
…mmit (nit(0502), if this fails Signed-off-by: vbaddi <vbaddi@qti.qualcomm.com> (cherry picked from commit 22d6385) Signed-off-by: Sharvari Medhe <smedhe@qti.qualcomm.com>
Signed-off-by: Sharvari Medhe <smedhe@qti.qualcomm.com> (cherry picked from commit 575545a) Signed-off-by: Sharvari Medhe <smedhe@qti.qualcomm.com>
Signed-off-by: Sharvari Medhe <smedhe@qti.qualcomm.com> (cherry picked from commit 806f2cf) Signed-off-by: Sharvari Medhe <smedhe@qti.qualcomm.com>
Signed-off-by: Sharvari Medhe <smedhe@qti.qualcomm.com> (cherry picked from commit 6dd6362) Signed-off-by: Sharvari Medhe <smedhe@qti.qualcomm.com>
Signed-off-by: Sharvari Medhe <smedhe@qti.qualcomm.com> (cherry picked from commit 3266f7c) Signed-off-by: Sharvari Medhe <smedhe@qti.qualcomm.com>
Signed-off-by: Sharvari Medhe <smedhe@qti.qualcomm.com>
Signed-off-by: Sharvari Medhe <smedhe@qti.qualcomm.com>
Signed-off-by: Sharvari Medhe <smedhe@qti.qualcomm.com>
Signed-off-by: Sharvari Medhe <smedhe@qti.qualcomm.com>
Signed-off-by: Sharvari Medhe <smedhe@qti.qualcomm.com>
Signed-off-by: Sharvari Medhe <smedhe@qti.qualcomm.com>
Signed-off-by: Sharvari Medhe <smedhe@qti.qualcomm.com>
Signed-off-by: Sharvari Medhe <smedhe@qti.qualcomm.com>
Signed-off-by: Sharvari Medhe <smedhe@qti.qualcomm.com>
Signed-off-by: Sharvari Medhe <smedhe@qti.qualcomm.com>
Signed-off-by: Sharvari Medhe <smedhe@qti.qualcomm.com>
Signed-off-by: Sharvari Medhe <smedhe@qti.qualcomm.com>
Signed-off-by: Sharvari Medhe <smedhe@qti.qualcomm.com>
Signed-off-by: Sharvari Medhe <smedhe@qti.qualcomm.com>
Signed-off-by: Sharvari Medhe <smedhe@qti.qualcomm.com>
Signed-off-by: Sharvari Medhe <smedhe@qti.qualcomm.com>
Signed-off-by: Sharvari Medhe <smedhe@qti.qualcomm.com>
Signed-off-by: Sharvari Medhe <smedhe@qti.qualcomm.com>
Signed-off-by: Sharvari Medhe <smedhe@qti.qualcomm.com>
…sal LMs New files: - exporter/weight_free.py: core weight-free dynamo export — builds meta model, traces with FakeTensors, promotes ONNX initializers to external data, builds weight_spec.json mapping ONNX names to safetensors checkpoint keys - exporter/weight_spec.py: WeightSpec format — describes external weight files and tensor→checkpoint-key mappings consumed by the QAIC compiler - exporter/checkpoint_transforms.py: checkpoint preparation pipeline — DtypeConversionCheckpointTransform, MoEExpertStackingCheckpointTransform (stacks per-expert tensors into batched layout), and GptOssMxfp4ExpertDequantSplitCheckpointTransform (dequant MXFP4 experts) - exporter/__init__.py: export new checkpoint transform classes - scripts/preprocess_moe_checkpoint.py: standalone MoE checkpoint prep script - examples/text_generation/weight_free_export_verify.py: e2e weight-free export verification (export → ORT → parity check) - examples/text_generation/weight_free_export_simple.py: minimal weight-free export example for causal LMs Signed-off-by: amarshar <amarshar@qti.qualcomm.com> Signed-off-by: Amar <amarshar@qti.qualcomm.com>
- gemma.py, gemma2.py: align cos/sin device for dynamo tracing - glm4_moe.py: __qeff_init__ uses meta-device guard for weight-free; register gate_proj/up_proj/down_proj_t directly on self.experts; remove get_all_projections() helper; fix rotary_dim computation - granite.py: full_like for causal mask; device alignment - granitemoe.py: weight-free subfunction export support - grok1.py: replace bool-mask expert loop with gather-BMM pattern; add __qeff_init__ stacking per-expert weights; meta-device guard - llama.py: fix decoder layer for dynamo — buffers, nested compile regions, rotary signature alignment - phi.py, phi3.py: QEffPhi3MLP to avoid SplitToSequence from chunk(2); device alignment fixes - qwen2.py: cos/sin device alignment - qwen3.py: extend qeff_apply_rotary_pos_emb signature and align cos/sin - qwen3_moe.py: weight-free dynamo export; expert weight stacking with meta-device guard; RAM reduction via view aliases (no clone) Signed-off-by: amarshar <amarshar@qti.qualcomm.com> Signed-off-by: Amar <amarshar@qti.qualcomm.com>
Signed-off-by: amarshar <amarshar@qti.qualcomm.com> Signed-off-by: Amar <amarshar@qti.qualcomm.com>
…o path get_sampling_inputs_and_outputs now accepts and populates dynamic_shapes alongside dynamic_axes, building Dim entries for each sampler input so the dynamo export path gets consistent symbolic shapes for samplers. Signed-off-by: amarshar <amarshar@qti.qualcomm.com> Signed-off-by: Amar <amarshar@qti.qualcomm.com>
…ee export - Add Phi3MLP → QEffPhi3MLP to KVCacheTransform: QEffPhi3MLP wraps the MLP to avoid SplitToSequence from chunk(2) in dynamo ONNX, which QAIC does not support - Wire QEffGrok1MoeBlock.__qeff_init__ into KVCacheExternalModuleMapperTransform: stacks per-expert weights during init so the gather-BMM forward works correctly for weight-free export Signed-off-by: amarshar <amarshar@qti.qualcomm.com> Signed-off-by: Amar <amarshar@qti.qualcomm.com>
In this branch the function lives in compile/mdp_generator.py not utils/_utils.py — importing it via utils/__init__.py caused ImportError. Signed-off-by: amarshar <amarshar@qti.qualcomm.com> Signed-off-by: Amar <amarshar@qti.qualcomm.com>
…calls Prevents meta/cpu device mismatch during weight-free export tracing. Signed-off-by: amarshar <amarshar@qti.qualcomm.com> Signed-off-by: Amar <amarshar@qti.qualcomm.com>
Signed-off-by: amarshar <amarshar@qti.qualcomm.com> Signed-off-by: Amar <amarshar@qti.qualcomm.com>
…arse_moe key mapping checkpoint_transforms.py: - Add _convert_bin_to_safetensors(): auto-converts .bin checkpoints to safetensors on first use via save_pretrained(safe_serialization=True); idempotent - CheckpointTransformPipeline.apply(): detect .bin files and convert before transform - MoEExpertStackingCheckpointTransform: extend EXPERT_RE and _LayerStacker.add() to handle Mixtral w1/w3/w2 naming (w1→gate, w3→up, w2→down) weight_free.py: - _resolve_checkpoint_dir(): fall back to downloading .bin files when no safetensors exist on HuggingFace Hub (e.g. GPT-J-6B); CheckpointTransformPipeline auto-converts - _find_checkpoint_key(): add lookup 5 to bridge mlp↔block_sparse_moe rename — transformers 5.x renamed the MoE block attribute, ONNX uses 'mlp' but Mixtral checkpoint has 'block_sparse_moe' Signed-off-by: amarshar <amarshar@qti.qualcomm.com> Signed-off-by: Amar <amarshar@qti.qualcomm.com>
…, Mistral, Mixtral, OLMo2 falcon/modeling_falcon.py: - QEffFalconModel.__qeff_init__: meta device guard for sin/cos — skip storing as nn.Parameter on meta device to avoid meta tensor ONNX initializer failure gpt_oss/modeling_gpt_oss.py: - qeff_apply_rotary_pos_emb: add device/dtype alignment for weight-free tracing - ctx_indices/short_read_idx: add device=position_ids.device to torch.arange granitemoe/modeling_granitemoe.py: - QEffGraniteMoeAttention.__qeff_init__: set num_heads/num_key_value_heads as instance attributes — required by @nested_compile_region inside decoder layer - past_key_value_update: fix 3-tuple unpack to 4-tuple - eager_attention_forward: use full_like for causal mask to avoid config.torch_dtype - QEffGraniteMoeTopKGating: drop .to(dtype) after softmax (SoftMax+Cast → QAIC crash) - QEffGraniteMoeDecoderLayer: return hidden_states directly instead of tuple mistral/modeling_mistral.py: - qeff_apply_rotary_pos_emb: add device/dtype alignment mixtral_moe/modeling_mixtral.py: - QEffMixtralExperts.__qeff_init__: meta device guard for gate/up/down projections - QEffMixtralModel.__qeff_init__: drop attention_scaling multiply (creates new tensor that becomes top-level ONNX initializer; direct reference avoids it) - QeffMixtralDecoderLayer: add @torch.compiler.nested_compile_region - QEffMixtralExperts: add to KVCacheTransform so __qeff_init__ is called - eager_attention_forward: use full_like for causal mask olmo2/modeling_olmo2.py: - qeff_apply_rotary_pos_emb: add device/dtype alignment pytorch_transforms.py: - Wire QEffMixtralExperts.__qeff_init__ (MixtralExperts→QEffMixtralExperts mapping) - Wire QEffGraniteMoeAttention.__qeff_init__ (already mapped, __qeff_init__ added) Signed-off-by: amarshar <amarshar@qti.qualcomm.com> Signed-off-by: Amar <amarshar@qti.qualcomm.com>
examples/text_generation/weight_free_export_verify.py: - Single-model smoke-test: weight-free dynamo export → compile → QPC inference - --no_weight_free: regular from_pretrained + dynamo path - --layers: override num_hidden_layers for fast testing - Config loaded without trust_remote_code first (fixes Falcon legacy config) examples/text_generation/continuous_batching_weightfree.py: - Single-model weight-free export with continuous batching (full_batch_size) - Same config fix for legacy models like Falcon examples/performance/compute_context_length/weight_free_ccl_verify.py: - Single-model CCL verification: compile with comp_ctx_lengths_prefill/decode - Prints specializations.json to confirm CCL slots were compiled - Default: ctx_len=4096, CCL=[1024,2048,4096], weight-free + dynamo Signed-off-by: amarshar <amarshar@qti.qualcomm.com> Signed-off-by: Amar <amarshar@qti.qualcomm.com>
…fter rebase The rebase auto-merge dropped the if not use_dynamo: guard introduced in Sharvari's d6f55bf commit. Restore it so TorchScript patches are not applied on the dynamo export path, and restore use_dynamo in the state dict so _cleanup_onnx_subfunctions can check it. Signed-off-by: Amar <amarshar@qti.qualcomm.com>
- Attention softmax: remove .to(query.dtype) — emits SoftMax->Cast in the subfunction ONNX which triggers optimizeSoftmax assertion in QAIC compiler - Router softmax: replace torch.softmax with manual Exp/Sum/Div — with num_local_experts=8, top_k_gates has 8 Einsum consumers; optimizeSoftmax asserts when a SoftMax node has more than one consumer Signed-off-by: Amar <amarshar@qti.qualcomm.com>
|
|
||
| # Normal export: compute derived params — values embedded in ONNX. | ||
| self.gate_proj = nn.Parameter(self.gate_up_proj[:, : self.expert_dim, :].detach().clone().transpose(1, 2)) | ||
| self.up_proj = nn.Parameter(self.gate_up_proj[:, self.expert_dim :, :].detach().clone().transpose(1, 2)) |
There was a problem hiding this comment.
nit: why did we do clone for normal export, when original was just .detach() and without .clone()?
same question for all moe related changes.
There was a problem hiding this comment.
We are seeing the aliasing issue in dynamo without clone () method. It will not export in dynamo without clone as it will get nodes with same name. And in weight free export we are not using this the else block anyway.
There was a problem hiding this comment.
Re: And in weight free export we are not using this the else block anyway. If not then we should keep it same as before right?
There was a problem hiding this comment.
But if user uses Weight_free_export=false then it will go via dynamo path and changing that will fail dynamo export because of aliasing issue.
| @@ -0,0 +1,193 @@ | |||
| #!/usr/bin/env python3 | |||
There was a problem hiding this comment.
nit: Can you add a readme (.md under /docs) on how this weightfree approach works? details on how to enable it? How download/conversion happens? and conversion being onetime thing, How we can reuse the .safetensors across machines and runs?
| @@ -0,0 +1,96 @@ | |||
| # | |||
There was a problem hiding this comment.
Let's create a 'weightfree/' directory and push these weightfree related things under it?
| pass | ||
|
|
||
| @staticmethod | ||
| def backward(ctx, grad_output): |
There was a problem hiding this comment.
nit: if we call model.eval(), is backward expected? Are these changes required?
| @@ -0,0 +1,318 @@ | |||
| # ----------------------------------------------------------------------------- | |||
There was a problem hiding this comment.
unit test infra is missing, pls work @smedhe and @quic-amitraj to get this added to avoid manual runs.
|
lint and format are missing. Pls follow the workflows to enable and repush once its fixed. |
- cache_utils.py: restore missing return k_out, v_out in QEffHybridCache.update() and QEffSlidingWindowCache.update(); remove orphan method blocks that were left floating inside wrong classes after rebase - exporter/__init__.py: use explicit re-export syntax (X as X) for F401 - checkpoint_transforms.py, glm4_moe, grok1, preprocess_moe_checkpoint.py: rename ambiguous variable I to ffn_dim / hidden_dim (E741) - gemma, gemma2, qwen3: remove unused mask_value assignment (F841) - Run ruff format across all 39 changed files Signed-off-by: Amar <amarshar@qti.qualcomm.com>
Creates tests/dynamo/ following the same structure as PR quic#1174: - conftest.py: markers, dynamo_workdir fixture, xdist-safe result capture, HTML/JSON report generation - utils/hash_manager.py: blake2b-keyed workdirs (collision-safe under -n auto) - utils/hardware_manager.py: file-lock QAIC device pool for HW lanes - utils/report_generator.py: HTML + JSON coverage matrix output Extends DynamoModelSpec in model_registry.py with two new flags: weight_free_supported - True for all validated causal LM architectures weight_free_cb_supported - True for all architectures where CB is also verified Both flags False for VLM text-side models (out of scope for this PR) Adds tests/dynamo/causal_lm/test_weight_free.py with 4 CPU-only lanes: 1. test_weight_free_export - meta-device export produces ONNX + weight_spec.json 2. test_weight_free_no_embedded_weights - ONNX has no large embedded initializers 3. test_weight_free_spec_coverage - all spec keys resolve in prepared_checkpoint/ 4. test_weight_free_cb_export - continuous_batching=True export produces artifacts All lanes are @pytest.mark.dynamo_export (CPU, no QAIC hardware) and @pytest.mark.regular. Run with: pytest tests/dynamo/causal_lm/test_weight_free.py -m 'dynamo_export' -n auto Signed-off-by: Amar <amarshar@qti.qualcomm.com>
…+ cleanup - exporter/weight_free/transforms.py: add MoEFusedExpertSplitCheckpointTransform for MoE checkpoints that already store experts in fused/stacked form (gate_up_proj [E,2I,H] + down_proj [E,H,I]). Splits them into the derived layout that QEff ONNX initializers expect (gate_proj/up_proj/down_proj_t). Registered in pipeline between MoEExpertStackingCheckpointTransform (old per-expert format) and DtypeConversionCheckpointTransform (dense fallback). - modeling_auto.py: register MoEFusedExpertSplitCheckpointTransform in QEFFAutoModelForCausalLM._checkpoint_transforms pipeline - customop: remove backward() stubs from CtxScatter/Gather — not needed for dynamo weight-free export (verified by test) - exporter/weight_free/core.py: fix sys.path for scripts/memory_profiling (extra .parent for new subdirectory depth) - docs/source/weight_free_export.md: add weight-free export documentation - examples/text_generation/weight_free/: update docstring paths and add __init__.py Signed-off-by: Amar <amarshar@qti.qualcomm.com>
Qwen3-MoE stores the router weight as mlp.router.weight in the checkpoint but the QEff wrapper accesses it as mlp.gate.weight. Add lookup strategy 6 to _find_checkpoint_key to handle this rename in both directions so the weight is correctly promoted to an external reference instead of remaining as an embedded meta tensor that causes SerdeError. Signed-off-by: Amar <amarshar@qti.qualcomm.com>
a5ebf6f to
7082a82
Compare
…pipeline - core.py: use config.dtype (not hardcoded float32) for meta model cast and checkpoint conversion; getattr fallback handles dtype=None configs - core.py: propagate enable_proxy flag through _build_meta_qeff_model - cache_utils.py: use v_out.dtype instead of torch.float32 for invalid-mask zeros_like in 5 KV cache update paths — avoids fp32↔bf16 dtype mismatch - modeling_llama.py: use attn_weights.dtype for masked_fill and softmax upcast instead of hardcoded float32 - export_compile_infer.py: add --dtype flag (default float16) to control model weight dtype; add enable_proxy=False - modeling_qeff.py: skip SplitTensorsTransform for weight-free export Signed-off-by: Amar <amarshar@qti.qualcomm.com>
7e7959d to
1f893d5
Compare
|
duplicate #1220 |
Summary
Adds weight-free ONNX export for causal LM models on top of the dynamo export infrastructure in #1178.
Weight-free export builds the model on meta device (no weights in RAM), exports ONNX graph structure only, and lets the QAIC compiler load weights directly from the original safetensors checkpoint at compile time via
weight_spec.json.New files
QEfficient/exporter/weight_free.py— core weight-free export: promotes ONNX initializers to external data, buildsweight_spec.json(5 checkpoint key lookup strategies including Mixtralmlp→block_sparse_moerename)QEfficient/exporter/weight_spec.py— weight spec format and I/OQEfficient/exporter/checkpoint_transforms.py— checkpoint prep pipeline: MoE expert stacking, dtype conversion,.bin→safetensors auto-convertexamples/text_generation/weight_free_export_verify.py— single-model smoke testexamples/text_generation/continuous_batching_weightfree.py— CB weight-free smoke testexamples/performance/compute_context_length/weight_free_ccl_verify.py— CCL verificationKey changes
QEfficient/base/modeling_qeff.py—use_weight_free_exportflag; meta-device guard for_model_offloaded_check()QEfficient/transformers/models/modeling_auto.py—use_weight_free_exportwired throughexport()andcompile(); CCLcomp_ctx_lengthsDim fixQEfficient/base/onnx_transforms.py— disableRewriteUnsupportedOpsTransformandSplitMultiInputMaxMinTransformfor dynamo pathQEfficient/utils/export_utils.py— dynamo guard onapply_torch_patches(); separate dynamo/TorchScript subfunction setup pathsQEfficient/utils/torch_patches.py—temporarily_enable/disable_nested_compile_regions()QEfficient/customop/— backward stubs forCtxScatter/Gather;GemmaRMSNormweight offset at runtimeTest plan