Skip to content

Add projection-specific INT2/INT3/INT4/INT8 QMoE export - #764

Open
titaiwangms wants to merge 6 commits into
mainfrom
draft/int3-qmoe-export
Open

titaiwangms wants to merge 6 commits into
mainfrom
draft/int3-qmoe-export

Conversation

@titaiwangms

@titaiwangms titaiwangms commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Add projection-specific QMoE export for Olive fused expert checkpoints.
FC1 and FC2 independently support INT2, INT3, INT4, and INT8: all 16 pairs
are accepted subject to format validation, without an experimental opt-in.

This is export support, not runtime qualification. INT3 follows the proposed
raw-weight direction in microsoft/onnxruntime#32657 and consumes the native Torch
INT3 checkpoint format introduced by microsoft/Olive#2716. INT3 output retains
draft-format/runtime-warning metadata; this PR does not provide INT3 ORT kernels
or claim that released ORT can execute it.

Changes

  • Resolve FC1/FC2 widths independently instead of using an explicit pair whitelist.
    This intentionally includes (2,3), (3,2), (3,8), (8,3), (2,2), and
    (2,8), in addition to the other valid pairs.
  • Remove the experimental INT3 flag and opt-in rejection. Export requires an
    explicitly quantized checkpoint with compatible projection configurations;
    ordinary unquantized models do not become INT3 automatically.
  • Require exact Olive expert overrides and a supported model-wide INT4 fallback
    for projection-specific layouts. Preserve the existing uniform INT4 path.
  • Preserve row-local, LSB-first packed unsigned INT3 codes with symmetric zero
    point 4. For pairs containing INT3, interleave FC1 gate/up payloads lazily during
    serialization in public HF builds; direct preprocessing remains eager by
    default. Otherwise retain graph-side interleaving. Preserve FC2's K-last layout
    without requantization.
  • Validate shared power-of-two group sizes, aligned dimensions, sidecar shapes,
    packed zero points, and finite nonnegative scales. Reject scale conversion
    overflow when the adapter supplies the activation dtype. INT3-containing pairs
    additionally require block size 32/64/128 and FP16/BF16 activation/scale
    agreement. Non-INT3 layouts retain the standard loader's dtype conversion.
  • Apply the same numeric scale checks to the legacy uniform INT4 path without
    imposing the projection-specific shape ABI. Rejecting previously accepted
    negative, NaN, infinite, or activation-cast-overflowing scales is an intentional
    validation tightening.
  • Bind deferred FC1 weights, scales, and asymmetric zero points through uncached
    lazy tensors, rather than retaining transformed copies for every layer.
    Keep transform accounting component-scoped when prefix stripping produces
    identical local initializer names.
  • Preserve INT2/INT4/INT8 zero-point semantics and validate optional explicit
    offset-4 INT3 zero points before omitting them from the graph.
  • Emit projection-width attributes where needed and retain INT3 draft-format
    metadata without assigning an unapproved schema version.
  • Cover all 16 pairs across FP16/BF16, independent packing/dequantization oracles,
    CPU execution of graph-side interleaving, invalid scales, local-checkpoint
    public builds, and external-data save/reload.
  • Pass the model activation dtype through all three Qwen3.5-MoE preprocessing
    paths: text, VL global quantization, and VL decoder component quantization.
    Add 57 regression cases covering all seven INT3-containing pairs across
    FP16/BF16, incompatible dtypes, and non-INT3 scale conversion behavior.

Validation

Latest offline CPU validation includes the merged main, Qwen3.5-MoE dtype
propagation, uniform scale checks, deferred serialization, and component routing:

PYTHONPATH="$PWD/src" HF_HUB_OFFLINE=1 TRANSFORMERS_OFFLINE=1 \
  TOKENIZERS_PARALLELISM=false PYTHONDONTWRITEBYTECODE=1 CUDA_VISIBLE_DEVICES='' \
  timeout 300 /home/titaiwang/miniconda3/envs/dev/bin/python -m pytest \
  --rootdir="$PWD" -c "$PWD/pyproject.toml" \
  src/mobius/models/int3_qmoe_test.py src/mobius/models/qwen35_test.py \
  src/mobius/models/moe_test.py \
  src/mobius/_weight_utils_test.py src/mobius/_flags_test.py \
  src/mobius/components/_moe_test.py \
  src/mobius/_model_package_test.py::TestModelPackageApplyWeights \
  src/mobius/weights/_adapters_test.py \
  src/mobius/integrations/_weight_loading_test.py \
  src/mobius/integrations/transformers/_builder_test.py \
  -q -m 'not integration and not arch_validation and not golden and not generation' \
  --tb=short

Passed: 728 tests, 1 deselected, in 52.36 seconds. Coverage includes
distinct per-layer payloads, source-storage aliasing, release of transformed
tensors between callbacks, repeated saves, asymmetric FC1 zero points, and
positive/negative component-local-name collision regressions.

lintrunner src/mobius/_model_package.py src/mobius/_model_package_test.py \
  src/mobius/_weight_utils.py src/mobius/integrations/_weight_loading.py \
  src/mobius/integrations/transformers/_builder.py src/mobius/models/granite.py \
  src/mobius/models/int3_qmoe_test.py src/mobius/models/llada.py \
  src/mobius/models/moe.py src/mobius/models/qwen35.py \
  src/mobius/models/qwen35_test.py src/mobius/weights/_adapters.py
git diff --check

Both passed. Ruff formatting and checking also passed for the same 12 Python
files. Scoped mypy reported 77 errors outside changed hunks; no project-wide
type-check success or fully established clean baseline is claimed.

Draft boundaries

  • All 16 pairs are exporter-supported layouts, not a runtime-qualified matrix.
    Execution depends on the target ORT version and execution provider.
  • Source checkpoint loading remains eager. Uncached lazy interleaving bounds
    additional FC1 payload retention, not total checkpoint or serialization peak
    memory. No full-checkpoint RSS or concurrent-writer qualification was run.
  • Direct callers opting into deferred preprocessing must forward the populated
    transform map when binding the returned weights. Explicit adapters and older
    preprocessing hooks retain their existing behavior.
  • No INT3 ORT kernel execution, checkpoint-to-runtime logits parity, real-model
    quality, or performance qualification was run. The CPU interleave tests
    exercise standard ONNX graph operations, not QMoE kernels.
  • Dense INT3 MatMulNBits, quantized embeddings, unsupported source layouts, and
    schema-unapproved tails are not enabled.
  • Final QMoE schema/version agreement, complete quantization provenance, and
    runtime qualification remain follow-up gates. This does not complete the
    end-to-end plan's P5 acceptance gate.
  • INT2 runtime/CI qualification is separate in Qualify INT2 QMoE CUDA execution in GPU CI #765 and is not
    included in this PR.

Gate raw Olive INT3 fused expert export behind an experimental flag. Validate packing, scales and source profiles, and cover public local-checkpoint export and external-data round trips. Runtime schema and kernel qualification remain pending.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: titaiwang <titaiwang@microsoft.com>
@github-actions

github-actions Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Performance Comparison

Comparing d734346b → c830b97d

Model Metric Baseline Current Delta
bert (feature-extraction) model_size_bytes 359 KB 359 KB +0.0% ⚪
bert (feature-extraction) num_nodes 68 68 +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 324 KB 324 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 269 269 +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 131 131 +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 461 461 +0.0% ⚪
t5 (seq2seq) model_size_bytes 836 KB 836 KB +0.0% ⚪
t5 (seq2seq) num_nodes 176 176 +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.

@github-actions

github-actions Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

🏗️ Architecture Diff

Comparing d734346b → c830b97d

Model Sub-model Changes Status
bert (feature-extraction) model 0 ⚪
falcon model 0 ⚪
gemma2 model 0 ⚪
gemma4 (gemma4) decoder 0 ⚪
gemma4 (gemma4) embedding 0 ⚪
gemma4 (gemma4) vision_encoder 0 ⚪
gemma4_text model 0 ⚪
gpt2 model 0 ⚪
llama model 0 ⚪
llama (static-cache) model 0 ⚪
mamba (ssm-text-generation) model 0 ⚪
phi3 model 0 ⚪
phi3 (static-cache) model 0 ⚪
qwen model 0 ⚪
qwen (static-cache) model 0 ⚪
qwen2 model 0 ⚪
qwen2 (static-cache) model 0 ⚪
qwen2_moe model 0 ⚪
qwen2_moe (static-cache) model 0 ⚪
qwen3 model 0 ⚪
qwen3 (static-cache) model 0 ⚪
qwen3_5_moe (hybrid-text-generation) model 0 ⚪
qwen3_5_text (hybrid-text-generation) model 0 ⚪
qwen3_5_vl (hybrid-qwen-vl) decoder 0 ⚪
qwen3_5_vl (hybrid-qwen-vl) embedding 0 ⚪
qwen3_5_vl (hybrid-qwen-vl) vision_encoder 0 ⚪
qwen3_moe model 0 ⚪
qwen3_moe (static-cache) model 0 ⚪
qwen3_next (hybrid-text-generation) model 0 ⚪
t5 (seq2seq) decoder 0 ⚪
t5 (seq2seq) encoder 0 ⚪
whisper (speech-to-text) decoder 0 ⚪
whisper (speech-to-text) encoder 0 ⚪

No architecture changes detected. ✅


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

@titaiwangms
titaiwangms marked this pull request as ready for review October 9, 2026 18:46
@titaiwangms
titaiwangms requested review from a team and a balanced review from Copilot October 9, 2026 18:46

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.

🟡 Changes recommended

The documented programmatic opt-in relies on a private, test-oriented API rather than a supported public export.

2 open findings
What changed in this PR

Adds an opt-in experimental exporter for Olive INT3 QMoE checkpoints while explicitly deferring runtime support.

Changes:

  • Adds INT3 layout validation, packing, and QMoE attributes.
  • Introduces an experimental feature flag and documentation.
  • Adds comprehensive offline export and round-trip tests.
File Description
README.md Documents experimental usage and limitations.
src/​mobius/​_flags.py Adds the INT3 export feature flag.
src/​mobius/​_weight_utils.py Validates and preprocesses INT3 checkpoint data.
src/​mobius/​components/​_moe.py Emits draft INT3 QMoE graphs and metadata.
src/​mobius/​models/​moe.py Passes activation dtype into preprocessing.
src/​mobius/​models/​int3_qmoe_test.py Tests packing, validation, builds, and persistence.

🧠 Review effort: Balanced


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

Comment thread README.md Outdated
Comment thread src/mobius/_weight_utils.py Outdated
Allow all FC1/FC2 precision combinations in {2, 3, 4, 8} without an experimental opt-in while retaining layout validation and the INT3 runtime qualification warning. Add scale conversion guards and independent graph interleave and quantization convention coverage.

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

Copilot-Session: fba8a408-cd63-49f0-b8b4-5daa19ed42d4
Signed-off-by: titaiwang <titaiwang@microsoft.com>

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.

🟡 Changes recommended

INT3 export is not feature-gated and accepts combinations explicitly excluded by the stated draft scope.

2 open findings
2 resolved since last review

🧠 Review effort: Balanced

Comment thread src/mobius/_weight_utils.py
Comment thread src/mobius/_weight_utils.py
@titaiwangms titaiwangms changed the title Add experimental INT3 QMoE checkpoint export Add projection-specific INT2/INT3/INT4/INT8 QMoE export Oct 14, 2026
@titaiwangms
titaiwangms requested a balanced review from Copilot October 14, 2026 17:47

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.

🟡 Changes recommended

Qwen3.5-MoE preprocessing does not supply the newly mandatory activation dtype, causing its INT3 exports to fail.

1 open finding
2 resolved since last review

🧠 Review effort: Balanced

Comment thread src/mobius/_weight_utils.py
Thread the model dtype through text, VL global, and VL decoder-component preprocessing so valid INT3 expert layouts no longer fail the mandatory activation-dtype check. Add 57 adapter regression cases for FP16/BF16 INT3 pairs, dtype rejection, and non-INT3 scale conversion behavior.

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

Copilot-Session: fba8a408-cd63-49f0-b8b4-5daa19ed42d4
Signed-off-by: titaiwang <titaiwang@microsoft.com>

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.

🔵 Needs a closer look

Eager FC1 interleaving can nearly double host-memory usage for production MoE checkpoints, and the documented scale-validation contract has an uncovered exception.

0 open findings

1 resolved since last review
Previously missed (2)

In code that hasn't changed since last review

Medium severity Validate scales for the legacy uniform INT4 path

README.md:80

This states that every projection's scales receive finite/nonnegative and conversion checks, but the new checks only run when projection_layouts is nonempty. The legacy uniform (4,4) pair has requires_projection_preprocessing == False, continues through preprocess_olive_weights, and therefore accepts NaN/negative/overflowing scales. Either apply the validation to that path too or document the uniform-INT4 exception so the advertised 16-pair contract is accurate.

Medium severity Avoid retaining duplicate FC1 weights during preprocessing

src/​mobius/​_weight_utils.py:1279

This interleave materializes a contiguous copy of every FC1 packed weight (and scale tensor), while all original tensors remain referenced by state_dict and all transformed copies accumulate in renamed_experts. For production MoE checkpoints, FC1 is several gigabytes, so preprocessing can nearly double the routed-expert payload and make otherwise viable exports run out of host memory. Please transform these tensors through the streaming weight pipeline (or otherwise release each source tensor as soon as its transformed replacement is owned) instead of eagerly retaining both complete layouts.

🧠 Review effort: Balanced

Reject invalid legacy uniform INT4 scales and serialize FC1 transforms without caching duplicate checkpoint payloads. Preserve component-local transform routing and cover asymmetric zero points and distinct multilayer payloads.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: fba8a408-cd63-49f0-b8b4-5daa19ed42d4
Signed-off-by: titaiwang <titaiwang@microsoft.com>

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.

🔵 Needs a closer look

The draft INT3 operator contract and cross-cutting lazy serialization changes warrant final human review.

0 open findings

🧠 Review effort: Balanced

This branch has not been deployed

No deployments
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