Repository navigation
Conversation
|
Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually. Contributors can view more details about this message here. |
Contributor
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: true
|
This was referenced Oct 30, 2026
Merged
Draft
meenchen
force-pushed
the
weimingc/qwen36-sub4bit-recipes
branch
from
November 2, 2026 16:44
8617993 to
1562724
Compare
meenchen
force-pushed
the
weimingc/qwen36-sub4bit-recipes
branch
from
November 2, 2026 23:24
1562724 to
cdb5f9f
Compare
meenchen
force-pushed
the
weimingc/autoquant-kl-context
branch
2 times, most recently
from
November 3, 2026 20:10
1fd2215 to
1658a6f
Compare
meenchen
force-pushed
the
weimingc/qwen36-sub4bit-recipes
branch
from
November 3, 2026 20:10
cdb5f9f to
7fe074e
Compare
meenchen
force-pushed
the
weimingc/autoquant-kl-context
branch
from
November 3, 2026 22:33
1658a6f to
0986299
Compare
meenchen
force-pushed
the
weimingc/qwen36-sub4bit-recipes
branch
from
November 3, 2026 22:33
7fe074e to
11dbfb0
Compare
meenchen
force-pushed
the
weimingc/autoquant-kl-context
branch
from
November 4, 2026 05:22
0986299 to
bfecf53
Compare
meenchen
force-pushed
the
weimingc/qwen36-sub4bit-recipes
branch
2 times, most recently
from
November 4, 2026 07:06
06113c9 to
5491bd8
Compare
meenchen
force-pushed
the
weimingc/autoquant-kl-context
branch
from
November 4, 2026 07:06
bfecf53 to
117544b
Compare
meenchen
force-pushed
the
weimingc/qwen36-sub4bit-recipes
branch
from
November 4, 2026 16:32
5491bd8 to
ba7ef3d
Compare
meenchen
force-pushed
the
weimingc/autoquant-kl-context
branch
2 times, most recently
from
November 4, 2026 18:52
24168a1 to
4b2ee1b
Compare
meenchen
force-pushed
the
weimingc/qwen36-sub4bit-recipes
branch
from
November 4, 2026 18:52
ba7ef3d to
b4ea7d4
Compare
meenchen
force-pushed
the
weimingc/autoquant-kl-context
branch
from
November 4, 2026 20:20
4b2ee1b to
1cdfb9e
Compare
meenchen
force-pushed
the
weimingc/qwen36-sub4bit-recipes
branch
2 times, most recently
from
November 4, 2026 21:50
0e6b4f0 to
8f12518
Compare
meenchen
force-pushed
the
weimingc/autoquant-kl-context
branch
2 times, most recently
from
November 5, 2026 17:49
52ad620 to
92d35d5
Compare
meenchen
force-pushed
the
weimingc/qwen36-sub4bit-recipes
branch
2 times, most recently
from
November 5, 2026 22:36
5c6c5c8 to
d43f209
Compare
meenchen
force-pushed
the
weimingc/autoquant-kl-context
branch
from
November 5, 2026 22:36
92d35d5 to
4097d58
Compare
meenchen
force-pushed
the
weimingc/autoquant-kl-context
branch
from
November 5, 2026 23:57
4097d58 to
fbf446b
Compare
meenchen
force-pushed
the
weimingc/qwen36-sub4bit-recipes
branch
from
November 5, 2026 23:57
d43f209 to
e6a2eff
Compare
meenchen
force-pushed
the
weimingc/autoquant-kl-context
branch
from
November 6, 2026 00:09
fbf446b to
177fad8
Compare
meenchen
force-pushed
the
weimingc/qwen36-sub4bit-recipes
branch
2 times, most recently
from
November 6, 2026 00:24
e365f56 to
c1d1fc7
Compare
meenchen
force-pushed
the
weimingc/autoquant-kl-context
branch
2 times, most recently
from
November 6, 2026 00:58
2c167dc to
e78d3cc
Compare
meenchen
force-pushed
the
weimingc/qwen36-sub4bit-recipes
branch
from
November 6, 2026 00:58
c1d1fc7 to
6d44deb
Compare
meenchen
added a commit
that referenced
this pull request
Nov 6, 2026
…from the source weights (#3154) ### What does this PR do? Type of change: Bug fix / new feature [1/5] of the sub-4-bit AutoQuantize series (merge in order). Series (merge in order): #3154 → #3151 → #3152 → #3153 → #3155 GPTQ and imatrix overwrite the weights they quantize. `auto_quantize` rejected both, and accepting them as-is would let one candidate's calibration change the weights every later candidate — and the unquantized scoring reference — sees. AutoQuantize now snapshots (to CPU) only the weights a GPTQ/imatrix candidate's calibration rewrites: the parameters its enabled weight quantizers cover, and for imatrix alone only those imatrix encodes (not Q8_0; a fused projection when any of its experts is). After such a candidate calibrates, its GGML quantizers replay the codes it pinned while the source weights are restored for the next candidate; the snapshot is freed before sensitivity scoring. After the solve, the selected candidates' weights are left as their codes decode, as a plain GPTQ run leaves them, and no candidate is left replaying. GGML fake quant now returns exactly the reconstruction export decodes, so scoring and the exported model see the same weights (BF16 included). Refused before the model is converted: any candidate with an enabled layerwise `export_dir`, empty included, whether its options are a dict or a `LayerwiseConfig`, whose export would leave the model in export form before scoring (`hf_ptq.py` already refused it for AutoQuantize recipes); and, in a search with a weight-rewriting candidate, any candidate whose layerwise `checkpoint_dir` resume restores the layers' weights into new Parameters the source-weight restore cannot reach (`calib_mutates_weights=False` resumes are accepted). Refused before any calibration: a weight-rewriting candidate whose weight quantizer is not a single GGML quantizer or rotates the weight, or whose algorithm list runs anything besides GPTQ, imatrix and max (AWQ or SmoothQuant would transform the weight GPTQ encodes); and meta or DTensor weights it would rewrite, weights stored packed or scaled (a native FP8 checkpoint's), or ones whose storage another parameter shares (tied weights, or views of them). These checks need the converted quantizers to know which weights a candidate rewrites, so `auto_quantize` hands back the unconverted modules, and the configs that conversion changed (including nested sub-configs), when the search fails before calibrating any candidate, and the same model can be searched again. Only a search with a weight-rewriting candidate saves the modules for this, and it drops them once these checks pass, so calibration and scoring never hold the weights conversion replaces (HF Conv1D's transposed copies); other searches save nothing. A failure during calibration restores the source weights and drops the modes that calibration added. `auto_quantize` hands back the pre-quant-scale folding policy on every exit, including a failure during scoring. Weights a calibration leaves alone (imatrix on Q8_0) simply quantize the source weight. NVFP4/FP8-only searches take no snapshot and never slice fused-expert weights after the solve. `auto_quantize` now accepts `gptq`, `imatrix` and algorithm lists such as `[gptq, imatrix]`. #### Why snapshot and replay Every candidate after a GPTQ/imatrix calibration, and the unquantized scoring reference, must see the source weights. The alternatives cost more: - **Copy the model, or its state dict:** this holds every weight, about 70 GB for Qwen3.6-35B-A3B in BF16. The snapshot holds only the weights a rewriting candidate quantizes, on the host, and is freed before scoring. - **Make GPTQ/imatrix leave weights alone:** this would change calibrators that `mtq.quantize` shares, and the chosen codes would still have to be kept somewhere. The GGML quantizer already pins those codes for export, so replaying the pin makes the scored, selected and exported weights the same codes. #### Reading order 1. `_AutoQuantizeBaseSearcher.before_search`: snapshot (`_snapshot_source_weights`; its checks are `_rewritten_weight` and `_check_restorable`), then for each candidate calibrate, replay its pins and restore the source weights. 2. `_write_selected_codes_to_weights` (after the solve), and `GGMLFormat._pinned_rows` / `full_pin` (the replay). 3. `auto_quantize`: the window in which a failure hands back the unconverted modules, and the folding policy it hands back on every exit. ### Usage ```python iq2_gptq = { "quant_cfg": [{"quantizer_name": "*", "enable": False}, {"quantizer_name": "*weight_quantizer", "cfg": {"num_bits": "iq2_s", "block_sizes": {-1: 256}, "backend": "ggml", "effective_bits": 2.5625}}], "algorithm": [{"method": "gptq", "block_size": 256, "perc_damp": 0.3, "importance_weighted": True}, {"method": "imatrix"}], } model, state = mtq.auto_quantize(model, constraints={"effective_bits": 3.0}, quantization_formats=[(iq2_gptq, "IQ2_S_GPTQ"), mtq.NVFP4_DEFAULT_CFG], ...) ``` ### Testing - New tests in `tests/unit/torch/quantization/test_ggml_gptq.py`, `test_autoquant.py`, `plugins/test_fused_experts.py` and `tests/gpu/torch/quantization/test_ggml_gptq_cuda.py`. I reverted each fix in place and checked that its test fails. They cover: - other candidates' scores are unaffected by GPTQ; - mixed selection: a GPTQ layer next to a BF16 layer, a GGML entry replacing an earlier INT4 entry, and imatrix on Q8_0; - the snapshot holds only rewritten weights (a fused `down_proj` that is disabled or Q8_0), takes a whole fused `gate_up_proj` with one Q8_0 expert, and a BF16 selection hands back the source weights; - refusals (non-GGML, tied, an aliased view, a rotated GPTQ or imatrix target, AWQ then GPTQ, a layerwise `checkpoint_dir`, native FP8 weights (GPU), and fused MoE whose root, nested and experts' configs conversion changes) hand back the unconverted model and its configs, which searches again; - calibration failures (GPTQ rewrite then failure, `[gptq, layerwise imatrix]` failing midway) restore the weights, the folding policy and the mode list; - the folding policy comes back after a scoring failure; - exact BF16 replay; - no fused-expert indexing after a search without pins; - an `algorithm: []` candidate keeps the saved state restorable. - Layerwise directories (`test_huggingface.py`), on a tiny Llama with discoverable decoder layers: an FP8 candidate with an `export_dir`, with an empty `export_dir`, and with a `checkpoint_dir` beside a Q8_0 GPTQ candidate, each given as a dict and as a `LayerwiseConfig`, is refused before conversion, leaving the modules, the weights and the working directory untouched, and the same model searches again. Without the refusals, the layers are exported and scoring fails on the packed weights, or the max candidate's checkpoint is written. - Conv1D weight lifetime (`test_huggingface.py`): an INT8 search saves no module state, and a Q8_0 GPTQ search drops it before calibration, so the weights conversion replaces are dead in every calibration and scoring forward. - Regression check against the base commit (RTX 6000 Ada, BF16, FP8 + INT4 searches): a 24-layer GPT-2 (Conv1D), a 16-layer Llama and a fused-MoE Qwen3, with gradient scoring and with KL for Llama, give the same scores, selections and output logits, the same search time (within 4%) and the same memory. On GPT-2 the weights held after conversion are 0 MiB, against 576 MiB before 5acdcc4. - Unit suites `tests/unit/torch/quantization` + `tests/unit/recipe` on CPU (16 xdist workers): 2151 passed. The rest fail the same way on `main` at this branch's base on the test host: - diffusers lacks `q_bmm_quantizer` (4 failures); - `test_diffusers_wan_conv3d` fails to collect (16 errors); - one to four `test_nemotron_3_5_super_vl_autoquant_recipe` cases fail under xdist and pass serially. - `pre-commit run mypy --all-files` passes; ruff check and ruff format pass on the touched files. - Rollback cost, paid only by searches with a GPTQ/imatrix candidate: saving the module attributes takes 2 MiB and 0.16 s on Qwen3.6-35B-A3B (837 modules), and 69 MiB on a 24.6k-module non-fused MoE. - GPU (RTX 6000 Ada): `tests/gpu/torch/quantization -k "auto_quant or autoquant or ggml or gptq or imatrix"` 57 passed, including `test_fsdp2_autoquant.py` and the native FP8 refusal. The deepspeed and onnx packages on the host stop 3 files from collecting. - Stack restacked on this; the same unit suites at [5/5]: 2148 passed, with the same failures. - Qwen3.6-35B-A3B on GB300: searches over IQ2_{XXS,XS,S} (imatrix and local GPTQ), NVFP4, FP8, BF16; see PR 5 for results. ### Before your PR is "*Ready for review*" - Is this change backward compatible?: ✅ - If you copied code from any other sources or added a new PIP dependency, did you follow guidance in `CONTRIBUTING.md`: N/A - Did you write any new necessary tests?: ✅ - Did you update Changelog?: ✅ - Did you get Claude approval on this PR?: review comments addressed (007a287, 83df05c, 848bd95, 357fb38, 59ea14d, 609ed1c, 9f76d8d, 7a21227, 7e028ac, 5acdcc4, f75da89, a9f070f, 1808e13) ### Additional Information Supersedes #3082 … #3057 and #3113 … #3120 with a smaller design (~720 source lines across the series). Synced with main by a merge (99f82c8), which keeps the commits the review threads cite. #3197 made the IQ formats re-encode a weight under its pinned scales and importance ahead of the pinned-rows lookup that honours `PIN_OVERRIDES_WEIGHT`, so during a search a GPTQ or imatrix candidate would have quantized the source weight under its scales instead of replaying its codes; b7be410 puts the override first there too (caught by `test_a_replayed_pin_fake_quantizes_to_exactly_its_decoding_in_bf16`). 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> <!-- coderabbit-ai-generated:v1 --> ## Summary by CodeRabbit * **New Features** * AutoQuantize supports GPTQ and iMatrix candidates with GGML formats. * Selected candidates retain their calibrated quantized results in the returned model. * **Bug Fixes** * Candidate scores are no longer affected by weight changes made while evaluating other candidates. * Source weights and quantization settings are restored when candidate evaluation fails; unsupported weight-rewrite configurations are rejected. * Failed conversions before calibration begins restore the model’s prior state. * Fake quantization better preserves reconstructed values in narrow data types. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: weimingc <17592131+meenchen@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
meenchen
force-pushed
the
weimingc/autoquant-kl-context
branch
from
November 6, 2026 06:13
e78d3cc to
5e21a27
Compare
meenchen
force-pushed
the
weimingc/qwen36-sub4bit-recipes
branch
from
November 6, 2026 06:13
6d44deb to
3d850cb
Compare
meenchen
force-pushed
the
weimingc/autoquant-kl-context
branch
from
November 6, 2026 21:37
5e21a27 to
a8d97a1
Compare
meenchen
force-pushed
the
weimingc/qwen36-sub4bit-recipes
branch
2 times, most recently
from
November 6, 2026 23:01
2c512de to
4ca71c1
Compare
meenchen
force-pushed
the
weimingc/autoquant-kl-context
branch
2 times, most recently
from
November 7, 2026 02:15
15b44f5 to
92431b3
Compare
meenchen
force-pushed
the
weimingc/qwen36-sub4bit-recipes
branch
from
November 7, 2026 02:15
4ca71c1 to
9425349
Compare
Routed experts search IQ2_XXS/IQ2_XS/IQ2_S (local GPTQ + imatrix), NVFP4, FP8 and BF16 at 2.75, 3.0 and 3.25 effective bits over all linears except embeddings, lm_head and vision; other linears stay BF16. Sequential GPTQ finalizes the selected formats. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: weimingc <17592131+meenchen@users.noreply.github.com>
The bit cost counts weights only and the IQ2 candidates are W2A16, so W4A4 NVFP4 and W8A8 FP8 were scored with activation error the budget does not pay for. Use W4A16 NVFP4 and W8A16 FP8 instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: weimingc <17592131+meenchen@users.noreply.github.com>
meenchen
force-pushed
the
weimingc/autoquant-kl-context
branch
from
November 7, 2026 05:32
92431b3 to
c543ea0
Compare
meenchen
force-pushed
the
weimingc/qwen36-sub4bit-recipes
branch
from
November 7, 2026 05:32
9425349 to
3798a0f
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Type of change: New example (recipes)
[5/5] of the sub-4-bit AutoQuantize series (after #3153).
Series (merge in order): #3154 → #3151 → #3152 → #3153 → #3155
Qwen3.6-35B-A3B AutoQuantize recipes at 2.75 / 3.0 / 3.25 effective bits (all linears except embeddings, lm_head and vision): routed experts choose IQ2_XXS / IQ2_XS / IQ2_S (local GPTQ + imatrix), weight-only NVFP4 (W4A16), weight-only FP8 (W8A16) or BF16; other linears stay BF16. Context-aware KL scoring at the format nearest each budget; sequential GPTQ finalizes the selection.
Usage
python examples/hf_ptq/hf_ptq.py --pyt_ckpt_path Qwen/Qwen3.6-35B-A3B \ --recipe models/Qwen/Qwen3.6-35B-A3B/auto_quantize/iq2_gptq_nvfp4_fp8_experts_at_3p0bits \ --auto_quantize_checkpoint ck/search.pth --export_path out # search; rerun to finalize + exportTesting
Held-out KL vs BF16 (128 documents, 63.5k tokens; experts only, GPTQ-finalized):
_at_2p75bits_at_3p0bits_at_3p25bitsNVFP4 / FP8 are weight-only because the bit cost counts weights only and the IQ2 formats are W2A16; with W4A4 / W8A8 candidates the same search gives 0.1012 / 0.0761 / 0.0646. The weight-only KL was measured with
context: lowest; in the W4A4 runs the recipes' near-budget context (1at 3.0,2at 3.25) changed KL by at most 1.7%.AA-LCR / GPQA-Diamond / SciCode subtask (reduced repeats 4 / 8 / 4; BF16-folded checkpoints on vLLM; lowest-bit-context W4A4/W8A8-search checkpoints; the 2.75 / 3.0 assignments are identical to these recipes'):
The recipes load in the existing recipe unit tests.
Before your PR is "Ready for review"
🤖 Generated with Claude Code