Skip to content

support MTP module WOQ quantization and step3p5 model quantization#1526

Merged
xin3he merged 23 commits into
mainfrom
xinhe/3-10-mtp
Mar 12, 2026
Merged

support MTP module WOQ quantization and step3p5 model quantization#1526
xin3he merged 23 commits into
mainfrom
xinhe/3-10-mtp

Conversation

@xin3he

@xin3he xin3he commented Mar 10, 2026

Copy link
Copy Markdown
Contributor

Description

Background

When transformers loads a model, it only instantiates the sub-modules
declared in the model class. Some architectures include auxiliary
parameters ?? most notably MTP (Multi-Token Prediction) layers ?? that are
stored in the original checkpoint but are not part of the default module
graph. As a result, model.save_pretrained silently omits those tensors
from the quantized output.

What's changed?

This module provides :func:copy_missing_tensors_from_source, which detects
and copies such tensors from the original checkpoint into the saved output
directory. Detection relies on a user-supplied list of name-component
prefixes (missing_param_prefix, default ["mtp"]): a source tensor is
considered "missing" when it is absent from the saved output and at least
one dot-separated segment of its name starts with one of those prefixes.

Before writing, the function optionally:

  • dequantizes FP8 weights to BF16 (when a matching weight_scale_inv
    tensor is present in the source checkpoint), and
  • re-quantizes with RTN into the packed WOQ format used by the rest of
    the saved model (when quant_method == "auto-round" and
    packing_format == "auto_round:auto_gptq" are detected in
    config.json).

Additional support step3p5 since it requires mtp

Type of Change

  • Bug fix
  • New feature
  • Documentation update
  • Performance improvement
  • Code refactoring
  • Other (please specify):

Related Issues

Fixes or relates to #1477, #1534 #1529

Checklist Before Submitting

  • My code has been tested locally.
  • Documentation has been updated as needed.
  • New or updated tests are included where applicable.

Signed-off-by: Xin He <xin3.he@intel.com>
Copilot AI review requested due to automatic review settings March 10, 2026 14:17
@xin3he

xin3he commented Mar 10, 2026

Copy link
Copy Markdown
Contributor Author

@copilot Please help add a simple unittest for this logic.

Copilot AI commented Mar 10, 2026

Copy link
Copy Markdown
Contributor

@xin3he I've opened a new pull request, #1527, to work on those changes. Once the pull request is ready, I'll request review from you.

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 support for preserving auxiliary checkpoint parameters (not instantiated by transformers model classes, e.g. MTP) when exporting/saving quantized models, so they aren’t silently dropped from the output directory.

Changes:

  • Introduces copy_missing_tensors_from_source to detect and copy “missing” tensors (by name-prefix components) from the original checkpoint into the exported quantized folder, with optional FP8 dequant and WOQ RTN re-quantization.
  • Wires the missing-tensor copy step into the export save path and re-exports the helper from auto_round.utils.
  • Adds a duplicate-save guard in shard writing and removes now-unused imports.

Reviewed changes

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

Show a summary per file
File Description
auto_round/utils/model.py Removes unused imports related to prior FP8 handling.
auto_round/utils/missing_tensors.py New implementation for detecting/copying missing tensors, FP8 dequant, and WOQ RTN packing.
auto_round/utils/init.py Re-exports copy_missing_tensors_from_source and adjusts utils exports.
auto_round/export/utils.py Calls missing-tensor copy after save_pretrained.
auto_round/compressors/shard_writer.py Skips saving duplicate tensor names during sharded saves.

You can also share your feedback on Copilot code review. Take the survey.

Comment thread auto_round/compressors/shard_writer.py
Comment thread auto_round/utils/missing_tensors.py
Comment thread auto_round/utils/missing_tensors.py Outdated
Comment thread auto_round/utils/missing_tensors.py
Comment thread auto_round/utils/missing_tensors.py
Comment thread auto_round/utils/missing_tensors.py
Comment thread auto_round/utils/missing_tensors.py
@wenhuach21 wenhuach21 requested a review from n1ck-guo March 11, 2026 01:39
@wenhuach21

Copy link
Copy Markdown
Contributor

is there anyway to control the bits for mtp?

Copilot AI and others added 2 commits March 11, 2026 09:44
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: xin3he <83260933+xin3he@users.noreply.github.com>

Copilot AI commented Mar 11, 2026

Copy link
Copy Markdown
Contributor

@xin3he I've opened a new pull request, #1528, to work on those changes. Once the pull request is ready, I'll request review from you.

Copilot AI and others added 3 commits March 11, 2026 10:21
…saved set (#1528)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: xin3he <83260933+xin3he@users.noreply.github.com>
Signed-off-by: Xin He <xin3.he@intel.com>
Signed-off-by: Xin He <xin3.he@intel.com>
@xin3he

xin3he commented Mar 11, 2026

Copy link
Copy Markdown
Contributor Author

is there anyway to control the bits for mtp?

I add --layer_config argument for it, please check.
AR_LOG_LEVEL=debug uv run auto-round /models/Qwen3.5-35B-A3B --it ers 0 --disable_opt_rtn --group_size 32 --layer_config "{mtp:{bits:8,data_type:int},mtp.fc:{bits:16,data_type:int}}"

Verified with vLLM

Signed-off-by: Xin He <xin3.he@intel.com>
@Saren-Arterius

Saren-Arterius commented Mar 11, 2026

Copy link
Copy Markdown

is there anyway to control the bits for mtp?

I add --layer_config argument for it, please check. AR_LOG_LEVEL=debug uv run auto-round /models/Qwen3.5-35B-A3B --it ers 0 --disable_opt_rtn --group_size 32 --layer_config "{mtp:{bits:8,data_type:int},mtp.fc:{bits:16}}"

Verified with vLLM

Hello, I got an error running this command: HF_TOKEN=... AR_LOG_LEVEL=debug uv run auto-round Qwen/Qwen3.5-35B-A3B --iters 0 --disable_opt_rtn --group_size 32 --layer_config "{mtp:{bits:8,data_type:int},mtp.fc:{bits:16}}"

I can confirm the build is auto-round==0.12.0.dev267+g9896f045 which has the same commit hash as 9896f04

Is your /models/Qwen3.5-35B-A3B a special version that could be found somewhere? Thanks

...
2026-03-11 16:35:57 DEBUG moe_experts_interface.py L333: Discovered unknown 3D projection: weight
2026-03-11 16:35:57 DEBUG moe_experts_interface.py L497: Skipping unfuse for Conv1d: does not support @use_experts_implementation
2026-03-11 16:35:57 INFO replace_modules.py L103: Experts (after replacement) [model.language_model.layers.0.mlp.experts] (SequentialQwen3_5MoeExperts):
SequentialQwen3_5MoeExperts(
  (0-255): 256 x Qwen3_5MoeMLP(
    (gate_proj): Linear(in_features=2048, out_features=512, bias=False)
    (up_proj): Linear(in_features=2048, out_features=512, bias=False)
    (down_proj): Linear(in_features=512, out_features=2048, bias=False)
    (act_fn): SiLUActivation()
  )
)
2026-03-11 16:35:57 INFO base.py L1604: Using predefined ignore_layers: ['model.language_model.layers.0.mlp.gate', 'model.language_model.layers.1.mlp.gate', 'model.language_model.layers.2.mlp.gate', 'model.language_model.layers.3.mlp.gate', 'model.language_model.layers.4.mlp.gate', 'model.language_model.layers.5.mlp.gate', 'model.language_model.layers.6.mlp.gate', 'model.language_model.layers.7.mlp.gate', 'model.language_model.layers.8.mlp.gate', 'model.language_model.layers.9.mlp.gate', 'model.language_model.layers.10.mlp.gate', 'model.language_model.layers.11.mlp.gate', 'model.language_model.layers.12.mlp.gate', 'model.language_model.layers.13.mlp.gate', 'model.language_model.layers.14.mlp.gate', 'model.language_model.layers.15.mlp.gate', 'model.language_model.layers.16.mlp.gate', 'model.language_model.layers.17.mlp.gate', 'model.language_model.layers.18.mlp.gate', 'model.language_model.layers.19.mlp.gate', 'model.language_model.layers.20.mlp.gate', 'model.language_model.layers.21.mlp.gate', 'model.language_model.layers.22.mlp.gate', 'model.language_model.layers.23.mlp.gate', 'model.language_model.layers.24.mlp.gate', 'model.language_model.layers.25.mlp.gate', 'model.language_model.layers.26.mlp.gate', 'model.language_model.layers.27.mlp.gate', 'model.language_model.layers.28.mlp.gate', 'model.language_model.layers.29.mlp.gate', 'model.language_model.layers.30.mlp.gate', 'model.language_model.layers.31.mlp.gate', 'model.language_model.layers.32.mlp.gate', 'model.language_model.layers.33.mlp.gate', 'model.language_model.layers.34.mlp.gate', 'model.language_model.layers.35.mlp.gate', 'model.language_model.layers.36.mlp.gate', 'model.language_model.layers.37.mlp.gate', 'model.language_model.layers.38.mlp.gate', 'model.language_model.layers.39.mlp.gate']
Traceback (most recent call last):
  File "/media/03a7d067-e0f6-4c60-98f4-d3ae2e2ca692/saren/AI/auto-round/.venv/bin/auto-round", line 10, in <module>
    sys.exit(run())
             ~~~^^
  File "/media/03a7d067-e0f6-4c60-98f4-d3ae2e2ca692/saren/AI/auto-round/.venv/lib/python3.13/site-packages/auto_round/__main__.py", line 836, in run
    start()
    ~~~~~^^
  File "/media/03a7d067-e0f6-4c60-98f4-d3ae2e2ca692/saren/AI/auto-round/.venv/lib/python3.13/site-packages/auto_round/__main__.py", line 566, in start
    tune(args)
    ~~~~^^^^^^
  File "/media/03a7d067-e0f6-4c60-98f4-d3ae2e2ca692/saren/AI/auto-round/.venv/lib/python3.13/site-packages/auto_round/__main__.py", line 775, in tune
    model, folders = autoround.quantize_and_save(export_dir, format=args.format)  # pylint: disable=E1101
                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/media/03a7d067-e0f6-4c60-98f4-d3ae2e2ca692/saren/AI/auto-round/.venv/lib/python3.13/site-packages/auto_round/compressors/base.py", line 965, in quantize_and_save
    model, _ = self.quantize()
               ~~~~~~~~~~~~~^^
  File "/media/03a7d067-e0f6-4c60-98f4-d3ae2e2ca692/saren/AI/auto-round/.venv/lib/python3.13/site-packages/auto_round/compressors/base.py", line 1730, in quantize
    self.configure_layer_config(enable_gguf_official_mixed=enable_gguf_official_mixed)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/media/03a7d067-e0f6-4c60-98f4-d3ae2e2ca692/saren/AI/auto-round/.venv/lib/python3.13/site-packages/auto_round/compressors/base.py", line 1627, in configure_layer_config
    self.layer_config, self.has_qlayer_outside_block, self.regex_config = set_layer_config(
                                                                          ~~~~~~~~~~~~~~~~^
        self.model,
        ^^^^^^^^^^^
    ...<10 lines>...
        fill_default_value=fill_default_value,
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/media/03a7d067-e0f6-4c60-98f4-d3ae2e2ca692/saren/AI/auto-round/.venv/lib/python3.13/site-packages/auto_round/compressors/utils.py", line 422, in set_layer_config
    raise ValueError(f"Invalid '{name}' in layer_config, no match found.")
ValueError: Invalid 'mtp' in layer_config, no match found.

@xin3he

xin3he commented Mar 11, 2026

Copy link
Copy Markdown
Contributor Author

Thank you for your attention. I think it's possible that the Transformers download skipped the MTP parameter download because the model structure doesn't include MTP. I suggest using hf download Qwen/Qwen3.5-35B-A3B --local-dir /models/Qwen3.5-35B-A3B.

xin3he and others added 6 commits March 11, 2026 19:11
Signed-off-by: Xin He <xin3.he@intel.com>
Signed-off-by: Xin He <xin3.he@intel.com>
Signed-off-by: Xin He <xin3.he@intel.com>
Signed-off-by: Xin He <xin3.he@intel.com>
xin3he added 2 commits March 12, 2026 10:43
Signed-off-by: Xin He <xin3.he@intel.com>
Signed-off-by: Xin He <xin3.he@intel.com>
Comment thread auto_round/compressors/utils.py
Comment thread auto_round/compressors/utils.py Outdated
Comment thread auto_round/modeling/fused_moe/replace_modules.py
Comment thread auto_round/modeling/fused_moe/step3_5_moe.py
Comment thread auto_round/__main__.py Outdated
Comment thread auto_round/special_model_handler.py
Comment thread test/test_cpu/models/test_moe_model.py Outdated
Comment thread auto_round/special_model_handler.py
Comment thread auto_round/modeling/fused_moe/replace_modules.py
Comment thread auto_round/export/utils.py
@wenhuach21

Copy link
Copy Markdown
Contributor

Thank you for your attention. I think it's possible that the Transformers download skipped the MTP parameter download because the model structure doesn't include MTP. I suggest using hf download Qwen/Qwen3.5-35B-A3B --local-dir /models/Qwen3.5-35B-A3B.

Could we add a checker for this? From the model config, we should be able to determine whether the model supports MTP. If the model state dict does not contain the corresponding tensor, we could log a warning or an error to suggest user to use hf download

@xin3he

xin3he commented Mar 12, 2026

Copy link
Copy Markdown
Contributor Author

Command to quantize mixed int4/8 model for Step3p5:
auto_round /dataset/xinhe/stepfun-ai/Step-3.5-Flash/ --scheme W4A16 --iters 0 --disable_opt_rtn --ignore_layers eh_proj,shared_head,layers.45 --layer_config "{mlp:{bits:8,data_type:int},self_attn:{bits:8,data_type:int},layers.46:{bits:8,data_type:int},layers.47:{bits:8,data_type:int}}"

Signed-off-by: Xin He <xin3.he@intel.com>
@xin3he

xin3he commented Mar 12, 2026

Copy link
Copy Markdown
Contributor Author

@Saren-Arterius It's truly a bug, and it's fixed now.

xin3he added 2 commits March 12, 2026 14:19
Signed-off-by: Xin He <xin3.he@intel.com>
Signed-off-by: Xin He <xin3.he@intel.com>

@yiliu30 yiliu30 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.

As other reviewers noted that this PR includes several changes.
It would be great, if we can spilit into:

  • Add Step 35 support
  • Copy MTP param
  • Optimizes shard writer

Comment thread test/test_cpu/utils/test_missing_tensors.py
Comment thread auto_round/__main__.py Outdated
Signed-off-by: Xin He <xin3.he@intel.com>
@xin3he

xin3he commented Mar 12, 2026

Copy link
Copy Markdown
Contributor Author

Verified with openai/gpt-oss-20b(no mtp), Qwen/Qwen3-0.6B-FP8(no mtp), stepfun-ai/Step-3.5-Flash(using last 3 layer for mtp) and qwen3.5 (using mtp tensor)

    # ------------------------------------------------------------------ #
    # Identify missing tensors via block-prefix statistics                 #
    # ------------------------------------------------------------------ #
    # A source tensor is considered "missing" when:
    #   1. its name is absent from the saved output, AND
    #   2. its parent layer name is NOT among the parent layers of any saved
    #      tensor (catches layers whose suffix changed, e.g. .weight ??
    #      .qweight after quantization), AND
    #   3. its "block prefix" ?? the path up to and including the first
    #      numeric segment, e.g. ``model.layers.0`` ?? is NOT present in the
    #      set of block prefixes collected from the saved output.

@xin3he

xin3he commented Mar 12, 2026

Copy link
Copy Markdown
Contributor Author

Could we add a checker for this? From the model config, we should be able to determine whether the model supports MTP. If the model state dict does not contain the corresponding tensor, we could log a warning or an error to suggest user to use hf download

It's fixed.

@xin3he

xin3he commented Mar 12, 2026

Copy link
Copy Markdown
Contributor Author

As other reviewers noted that this PR includes several changes. It would be great, if we can spilit into:

  • Add Step 35 support
  • Copy MTP param
  • Optimizes shard writer

Thank you. will keep currenct change due to complex context.

xin3he added 2 commits March 12, 2026 16:25
Signed-off-by: Xin He <xin3.he@intel.com>
Signed-off-by: Xin He <xin3.he@intel.com>
@xin3he xin3he changed the title support MTP params: copy, fp8 dequant, and WOQ RTN quantization support MTP module WOQ quantization and step3p5 model quantization Mar 12, 2026
@wenhuach21

Copy link
Copy Markdown
Contributor

Verified with openai/gpt-oss-20b(no mtp), Qwen/Qwen3-0.6B-FP8(no mtp), stepfun-ai/Step-3.5-Flash(using last 3 layer for mtp) and qwen3.5 (using mtp tensor)

    # ------------------------------------------------------------------ #
    # Identify missing tensors via block-prefix statistics                 #
    # ------------------------------------------------------------------ #
    # A source tensor is considered "missing" when:
    #   1. its name is absent from the saved output, AND
    #   2. its parent layer name is NOT among the parent layers of any saved
    #      tensor (catches layers whose suffix changed, e.g. .weight ??
    #      .qweight after quantization), AND
    #   3. its "block prefix" ?? the path up to and including the first
    #      numeric segment, e.g. ``model.layers.0`` ?? is NOT present in the
    #      set of block prefixes collected from the saved output.

Really nice work, thanks!

Comment thread auto_round/special_model_handler.py
Signed-off-by: Xin He <xin3.he@intel.com>
@xin3he xin3he merged commit 42fdb06 into main Mar 12, 2026
29 checks passed
@xin3he xin3he deleted the xinhe/3-10-mtp branch March 12, 2026 14:26
@davedgd

davedgd commented Mar 12, 2026

Copy link
Copy Markdown

@xin3he: I updated to the latest build today that includes this patch (42fdb06) and noticed that while I can successfully convert Gemma 3 models with this patch (e.g., unsloth/gemma-3-4b-it), they can no longer be loaded with vLLM v0.17.1 once converted. Reverting to the prior commit (837888d) causes the issue to go away.

Here is the relevant error while loading in vLLM with the successfully quantized model:

(EngineCore_DP0 pid=2325627)     loaded_params = module_load_weights(weights)
(EngineCore_DP0 pid=2325627)                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(EngineCore_DP0 pid=2325627)   File "/home/daved/miniforge3/envs/vllm/lib/python3.12/site-packages/vllm/model_executor/models/siglip.py", line 950, in load_weights
(EngineCore_DP0 pid=2325627)     param = params_dict[name]
(EngineCore_DP0 pid=2325627)             ~~~~~~~~~~~^^^^^^
(EngineCore_DP0 pid=2325627) KeyError: 'vision_model.encoder.layers.0.self_attn.qkv_proj.weight'
Loading safetensors checkpoint shards:  17% Completed | 1/6 [00:00<00:02,  1.85it/s]

Happy to provide more details!

@xin3he

xin3he commented Mar 15, 2026

Copy link
Copy Markdown
Contributor Author

Hi @davedgd Thanks for pointing out this issue.
I had fixed it here. #1547

@davedgd

davedgd commented Apr 14, 2026

Copy link
Copy Markdown

Hi @davedgd Thanks for pointing out this issue. I had fixed it here. #1547

@xin3he: Apologies for never replying -- I wanted to confirm this issue was fixed on my end. Appreciate your quick responses, as always!

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.

8 participants