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. |
|
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
Tip 🎯 Customize your risk assessments
✅ Automatic approvals, with your rules
|
86b5475 to
70670ad
Compare
0f7188c to
1a4a8c3
Compare
1691197 to
a1bd87a
Compare
1a4a8c3 to
9b37689
Compare
a1bd87a to
e23cc89
Compare
9b37689 to
8c5691a
Compare
e23cc89 to
dbf9e33
Compare
8c5691a to
8d96d20
Compare
dbf9e33 to
c8c8ae5
Compare
8d96d20 to
2792a02
Compare
c8c8ae5 to
fc9558b
Compare
e7e3533 to
e9e1401
Compare
45e81d8 to
eb5da0d
Compare
c1b62a6 to
6e451f1
Compare
60303de to
f8c7b37
Compare
6e451f1 to
e170688
Compare
f8c7b37 to
7275f38
Compare
e170688 to
0be766b
Compare
7275f38 to
fe62ae5
Compare
8176ab3 to
ab91a8e
Compare
07e2459 to
d8d6753
Compare
ab91a8e to
9c31102
Compare
…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>
9c31102 to
ed6b499
Compare
d8d6753 to
80c3352
Compare
ed6b499 to
81bfb30
Compare
41c495f to
bf155a4
Compare
A layerwise algorithm such as sequential GPTQ makes each layer's result depend on the formats chosen for the layers before it, which a per-layer search cannot know. The search therefore scores candidates with their own (local) algorithms, and the selected per-layer formats are calibrated afterwards on the source weights. - mtq.get_auto_quantize_config takes the algorithm of the config it returns instead of always returning max. - AutoQuantize recipes take auto_quantize.finalize_algorithm. hf_ptq then stops after saving the search checkpoint; rerunning the same command finds the scores, re-solves for the recipe's budget, and quantizes the freshly loaded source model with the frozen formats, the finalize algorithm and the recipe's KV-cache config before exporting. - hf_ptq takes --auto_quantize_format_shard INDEX/COUNT and --auto_quantize_merge_shards to run the format shards in parallel and merge them into --auto_quantize_checkpoint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: weimingc <17592131+meenchen@users.noreply.github.com>
Finalizing needs the score table, but loading the checkpoint brought every searched format's calibration into memory first, on the GPUs it was saved from. Memory-map it on the CPU so the calibration stays on disk. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: weimingc <17592131+meenchen@users.noreply.github.com>
_run_auto_quantize_recipe read --auto_quantize_merge_shards and --auto_quantize_format_shard as attributes, so callers that build args without them, as the KV-cache AutoQuantize stage tests do, failed with AttributeError, before the recipe refusals those tests check. Default them to off, as autoquant_utils does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: weimingc <17592131+meenchen@users.noreply.github.com>
bf155a4 to
8653170
Compare
What does this PR do?
Type of change: New feature
[3/5] of the sub-4-bit AutoQuantize series (after #3151).
Series (merge in order): #3154 (merged) → #3151 → #3152 → #3153 → #3155
Sequential (layerwise) GPTQ makes each layer's result depend on the formats chosen for the layers before it, which a per-layer search cannot know. The search therefore scores candidates with their own local algorithms, and the frozen per-layer formats are calibrated afterwards on the source weights.
mtq.get_auto_quantize_config(state, algorithm=...)returns the frozen config with the given calibration algorithm instead of alwaysmax.auto_quantize.finalize_algorithm.hf_ptq.pythen stops after saving the search checkpoint; rerunning the same command finds the scores, re-solves for the recipe's budget, and quantizes the freshly loaded source model with the frozen formats, the finalize algorithm and the recipe's KV-cache config before exporting.hf_ptq.py --auto_quantize_format_shard I/Nand--auto_quantize_merge_shardsrun the format shards and merge them.Usage
Testing
forward_stephandles the early-stopped forward of layerwise candidate calibration (fails without the fix); recipe and hf_ptq helper suites pass (test_hf_ptq_args.py,test_autoquant_utils.py,test_example_utils.py: 191 tests)._run_auto_quantize_recipereads the shard flags with defaults: the KV-cache AutoQuantize stage tests buildargswithout them and failed withAttributeError(5 tests) before this.Before your PR is "Ready for review"
🤖 Generated with Claude Code