Repository navigation
[OMNIML-6081] Fix the FSDP2 export of W4A8 (SequentialQuantizer) models, and refuse compressing them #3128
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[OMNIML-6081] Fix the FSDP2 export of W4A8 (SequentialQuantizer) models, and refuse compressing them #3128
Changes from all commits
2561773
20d76ff
db51121
b0ad5bd
580cdf7
78aa164
d538a02
060836a
75db98e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1005,17 +1005,26 @@ def patched_new(cls, data=None, requires_grad=True): | |
| def enable_fake_quant(module): | ||
| """Temporarily set the fake_quant attribute of a module to True. | ||
|
|
||
| This is used to prevent weight compression from being triggered during an unshard() call. | ||
| This is used to prevent weight compression from being triggered during an unshard() call. A | ||
| sequential weight quantizer (W4A8) holds the flag on each of its stages. | ||
| """ | ||
| original_fake_quant = [] | ||
| for m in module.modules(): | ||
| if hasattr(m, "weight_quantizer"): | ||
| original_fake_quant.append(m.weight_quantizer._fake_quant) | ||
| m.weight_quantizer._fake_quant = True | ||
| yield | ||
| for m in module.modules(): | ||
| if hasattr(m, "weight_quantizer"): | ||
| m.weight_quantizer._fake_quant = original_fake_quant.pop(0) | ||
| from ..nn import TensorQuantizer # local: ..nn imports this utils package at module scope | ||
|
|
||
| quantizers = [ | ||
| q | ||
| for m in module.modules() | ||
| if hasattr(m, "weight_quantizer") | ||
| for q in m.weight_quantizer.modules() | ||
| if isinstance(q, TensorQuantizer) | ||
| ] | ||
|
Comment on lines
+1013
to
+1019
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [SUGGESTION] This rewrite is the right shape for the W4A8 bug —
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
On the flag's consumers: besides |
||
| original_fake_quant = [q._fake_quant for q in quantizers] | ||
| for q in quantizers: | ||
| q._fake_quant = True | ||
| try: | ||
| yield | ||
| finally: | ||
| for q, fake_quant in zip(quantizers, original_fake_quant): | ||
| q._fake_quant = fake_quant | ||
|
|
||
|
|
||
| @contextmanager | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[IMPORTANT Compatibility] The guard itself is the right call — but the user-facing surface that steers people into exactly this combination is not updated, and the refusal lands late in an expensive job.
What. On more than one rank this now rejects every AWQ format on an FSDP2 model.
examples/hf_ptq --use_fsdp2is the documented multi-node PTQ path, and AWQ formats are first-class there:int4_awq,nvfp4_awq_liteandw4a8_awq_betaare all in_AUTO_QUANTIZE_QFORMATS(examples/hf_ptq/autoquant_utils.py:84-102), and the shipped recipesmodelopt_recipes/general/auto_quantize/w4a8_awq_beta_fp8_at_6p0bits.yamlplus themodelopt_recipes/model_type/*/ptq/w4a8_awq-kv_fp8_cast.yamlset are AWQ. The README's FSDP2 section (examples/hf_ptq/README.md:609-650) tells users AutoQuantize recipes "use the sametorchrunlaunch" and carries a note block listing the--use_fsdp2restrictions (Nemotron-H MTP,--calib_with_images) — this new one is missing from it.Why it matters. A user following that README with one of those shipped recipes on 2+ ranks now gets
NotImplementedErrorwhere they previously got a checkpoint. Refusing is better than the silently mismatched shards, so the behaviour change is fine — the problem is that nothing in the examples or recipe docs says so, and the refusal fires after the distributed model load and calibration-data setup, i.e. minutes into a multi-node job on a large model rather than at argument parsing.Suggested fix (docs + fail-fast, no change to this function):
--use_fsdp2note block inexamples/hf_ptq/README.md, next to the existing MTP /--calib_with_imagesnotes.examples/hf_ptq/example_utils.py:95-124, which already raises--use_fsdp2 does not support: ...before distributed loading:Alternatively, for
auto_quantizespecifically, dropping AWQ candidates from the search with a warning (rather than refusing the whole call) would keep the shipped AQ recipe runnable on multi-rank FSDP2 — your call, but thenw4a8_awq_beta_fp8_at_6p0bits.yamlshould say what it degrades to.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 060836a, both parts:
examples/hf_ptq/example_utils.py::validate_fsdp2_supportednow lists "AWQ calibration (awq_lite) on more than one rank" when the process group has more than one rank and the run would calibrate with AWQ. That includes--qformatpresets, a PTQ recipe'squantize, and an AutoQuantize recipe'scandidate_formatsandmodule_search_spaces. It mirrors the library's criterion (awq_lite/awq_full) and runs before the distributed load, so a multi-node job fails at startup. The library guard stays.examples/hf_ptq/README.md: a note next to the MTP note in the--use_fsdp2section says AWQ formats and recipes, AutoQuantize candidates included, are rejected on more than one rank before loading, and what to use instead.w4a8_awq_beta_fp8_at_6p0bitsfails loudly on several ranks instead of running a narrower search than the one asked for.tests/examples/hf_ptq/test_example_utils.py::test_fsdp2_refuses_awq_on_several_ranks_before_loadingcovers a W4A8 qformat and the W4A8 AutoQuantize recipe on 2 ranks (refused), FP8 on 2 ranks and W4A8 on 1 rank (allowed). Both refused cases fail without the check.