Repository navigation
[OMNIML-6073] [3/3] Calibrate fused experts on grouped_mm and torch_a2a instead of forcing eager - #3074
[OMNIML-6073] [3/3] Calibrate fused experts on grouped_mm and torch_a2a instead of forcing eager#3074shengliangxu wants to merge 31 commits into
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review.
|
| Layer / File(s) | Summary |
|---|---|
Backend normalization and interception checks modelopt/torch/quantization/plugins/huggingface.py, modelopt/torch/quantization/model_quant.py, tests/unit/torch/quantization/plugins/test_fused_experts.py, CHANGELOG.rst |
Backend normalization preserves supported implementations and redirects unsupported ones according to grouped-linear availability. Calibration tracks whether its forward loop ran and checks interception for applicable fused experts. Tests cover normalization and missed interception. |
Grouped execution capture and GPTQ modelopt/torch/quantization/model_calib.py, modelopt/torch/quantization/plugins/huggingface.py, modelopt/torch/utils/moe.py, tests/unit/torch/quantization/plugins/test_fused_experts.py, tests/unit/torch/utils/test_moe_a2a.py |
Capture hooks split grouped batches by expert offsets for input calibration and GPTQ. Tests cover grouped capture, Hessian behavior, fake quantization, and transport gradients. MoE documentation describes quantization of the grouped linear operation. |
Rank-local weights and distributed validation modelopt/torch/quantization/plugins/huggingface.py, modelopt/torch/quantization/model_calib.py, modelopt/torch/quantization/utils/core_utils.py, tests/unit/torch/quantization/plugins/test_fused_experts.py |
Expert-weight lookup and FP8 scale access use local shards. FSDP2 writeback skips DTensor parameters not owned by FSDP units. Tests cover expert-parallel calibration, folding, hooks, and GPTQ helpers. |
Priority:
Estimated code review effort: 4 (Complex) | ~45 minutes
Change: Feature
Sequence Diagram(s)
sequenceDiagram
participant Calibration
participant FusedExperts
participant GroupedLinearHook
participant CaptureHooks
Calibration->>FusedExperts: run calibration forward
FusedExperts->>GroupedLinearHook: pass grouped inputs and expert offsets
GroupedLinearHook->>CaptureHooks: split inputs by expert offsets
CaptureHooks->>Calibration: collect per-expert inputs or Hessians
Suggested reviewers: cjluo-nv, chadvoegele
Merge Risk: 
Minimal · previous review f1a1b
-
No actionable issue was identified in the reviewed calibration and expert-parallel paths. The PR is mergeable after normal checks.
Pre-merge checks: 
5
-
Full details: Security Anti-Patterns
-
Explanation
No security anti-patterns were introduced. The reviewed diff changes no example Python files and no pyproject.toml or requirements.txt files. Added-line searches found no torch.load(..., weights_only=False), numpy.load(..., allow_pickle=True), hardcoded trust_remote_code=True, eval/exec on input, or # nosec comments. The only eval-related addition is the safe model.eval() call in a test.
-
Finishing Touches: 💡 1
-
🛠️ Fix failing CI checks: 💡
-
- Commit to this branch
- Create a new PR
📝 Generate docstrings
-
- Commit to this branch
- Create a new PR
🧪 Generate unit tests (beta)
-
- Commit to this branch
- Create a new PR
-
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Comment @coderabbitai help to get the list of available commands.
|
/ok to test e5a7530 |
The interception check reads the forward count only for a module whose hooks never fired, so once one has, the count is not needed. Testing an eager forward's routing syncs the host, so it now runs only until the first hook call of each calibration rather than on every forward, inference and QAT included. The check's outcome is unchanged. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
…ured The weight-input captures (local Hessian, imatrix, GPTQ) chose their hook from config._experts_implementation when they were registered and raised when the other path ran. The interceptors already record the path that ran: _grouped_offs is set by the grouped one and cleared by the eager one. One hook now reads it at call time, so the captures follow whichever forward ran and the refusal and the second hook are gone. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Under data parallelism the forward count depends on each rank's data, so a bypassing backend raises only on the ranks whose experts saw rows. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Here the captures split the grouped batch per expert, so local Hessian, imatrix and GPTQ calibrate the experts' weights from their inputs on grouped_mm as on eager. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
The interception check passes once a hook fired, so the forward stops counting and skips its routing test, which syncs the host. The test fails with that gate removed. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
The weight-input captures pick their branch by _grouped_offs, which the interceptors set before every input quantizer call. Left set after the forward, a capture reached any other way would split its input by offsets from a finished forward instead of skipping it, and the module would keep the offsets tensor alive between forwards. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
A rank whose experts saw no rows does not raise; it waits in its next collective until the launcher stops it or the collective times out, so the error is in another rank's log. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
…omment The comment #2745 added names the function by the name it has there; this PR renames it, and it still keeps such a model on eager. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
calibrate() imported the HF plugin for its interception check and caught ImportError only. A plugin whose body fails otherwise (a transformers version skew raising AttributeError, say) is one import_plugin only warns about, and the import reran it and failed every calibration with that error. The check now reads the plugin from sys.modules: one that did not load installed no fused-experts wrappers to check. A test breaks the plugin's import and calibrates; it fails without the change. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
The weight-access window's ownership filter moved to its own PR, with its tests in test_utils.py; the expert-parallel calibration tests here still run under it, with the dense part FSDP2-sharded. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
_local_experts took a DTensor whose global expert count differs from the module's for a shard over the experts. A weight placed otherwise, every expert replicated, say, has a local shard that still holds every expert, and indexing it by local expert read the global ones without a word. It now raises, naming the placement. The native FP8 wrapper's unpack_weight also refuses weights sharded over the experts: it filled a buffer for every expert from this rank's experts. One two-rank test covers both and fails without either check. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
The weight-input captures split an expert-sorted batch by the grouped offsets, read as exclusive cumulative ends, one per expert, as transformers' grouped_mm forward and a2a_moe build them. Offsets of another form (a leading zero, too few entries, ends past the rows) would credit experts with other experts' rows without a word; the split now raises. A test feeds each form and fails without the check. The _grouped_offs comment names every capture that reads it, and its other job: telling a capture which forward ran. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
The plugin imports from torch.distributed.tensor under a guard, since a torch without distributed lacks it. _local_experts, on every grouped projection and expert weight read, reached DTensor through an unguarded attribute chain instead, which would fail every fused-MoE quantization there. It now uses the guarded import, as tensor_quantizer does. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
The offsets check covered the count of ends and the last one, but ends out of order passed it and credited an expert with another expert's rows. It now also requires the ends never to decrease, from zero, and the test gains that case, which fails without it. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
A first end of zero is fine (expert 0 got no rows); what the check refuses is the inclusive [0, c0, c0 + c1, ...] form, one end too many. The docstring and the test case's id now say so. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Calibration now runs fused experts on the backend transformers picks, grouped_mm by default, and the way back to earlier releases' scales is loading with experts_implementation="eager", which hf_ptq.py had no way to pass. The flag reaches every load path: get_model (the compressed- tensors branch included), the low-memory load, and, through the config the meta model is built from, the FSDP2 one. A test loads a tiny Qwen3-MoE with eager and fails without the plumbing; the changelog entry names the flag. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
calibrate() now raises when a fused-experts forward ran but reached neither quantizer hook, where main exported weight-derived input scales with a warning: a script that used to produce a checkpoint now stops. The Backward Breaking Changes section says so. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
…ly when given The FSDP2 load builds its meta model from a pre-fetched config, so the flag is set on that config's _experts_implementation, as build_meta_causal_lm sets a pre-fetched config's attn_implementation, rather than through an AutoConfig kwarg. A test captures the config the FSDP2 loader receives and fails without it. get_model passes the flag on its compressed-tensors branch only when given, as on every other load, instead of an explicit None. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
normalize_experts_impl_on_the_fly rewrote the experts backend without a word. On main it always forced eager, but a caller can now ask for one (a load's experts_implementation, hf_ptq's --experts_implementation), and an unintercepted backend redirected to grouped_mm, or experts that quantize on the eager forward only, override that choice. Each change now warns once, naming both backends and why; a supported value, the common case, stays quiet. The redirect test expects the warning and fails without it. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
None already dispatches to the eager F.linear forward, so moving it to eager for experts that quantize on the eager forward only changes nothing; it warned anyway, for every such model, through the experts' own config too. The FP8 test now covers a grouped_mm and a None backend, and counts the warnings: one, then none. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Every rank redirects its experts alike, so an expert-parallel run printed one copy of the notice per rank. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Transformers' _experts_implementation setter carries the value to the sub-configs, so the one assignment on the pre-fetched config also reaches the text config Qwen3.5-MoE's experts dispatch on. The FSDP2 test now runs a VLM config too and checks its text config; it fails if the value reaches the root config only. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
A class no backend change redirects needs its own quantized wrapper; meanwhile the rest of the model quantizes with these modules' quantizers disabled, which the check honours. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
…tation load_model now reads args.experts_implementation; the test's stand-in for hf_ptq's parsed arguments lacked it, so both of its cases failed with an AttributeError in the trtllm hf_ptq lane. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
d50b2c3 to
ec9e09c
Compare
|
/ok to test ec9e09c |
A two-or-more-GPU test of the expert-parallel calibration lifecycle on stock transformers: a tiny Qwen3-MoE loaded with its experts sharded over every rank by transformers' own expert-parallel load on the torch_a2a styles, seeded with the experts' groups, quantized with FP8 through distributed_forward_loop, each rank on its own batch. Every expert's weight scales and every activation scale match the whole model calibrated on every rank's batch. On transformers >= 5.16 quantizer insertion drops the transport's forward wrappers, which the loop re-plants: the test fails without the re-plant, and without the conversion keeping torch_a2a (#3074). The scales' syncs and the test's gathers give up in seconds on bounded groups. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
|
Review: I'm blocking on evidence and scope, not code correctness. The code looked careful. 1. The default backend change has no accuracy data, only scale deltas. The PR shows 4,647 activation scales moving by up to 10.2% on Qwen3-30B-A3B and attributes it to combine rounding that flips near-tied top-k routes. That explains the change, but doesn't show whether it matters. Before the default flips I'd ask for:
2. No maintainer other than the author has signed off. Every approval so far is from a bot, and the bot's own latest review still lists the maintainer sign-off on the 3. The evidence comes from older revisions. The byte-for-byte checkpoint comparisons, including " 4. No determinism check for grouped_mm. The PR shows main's eager repeat is byte-identical, but not a grouped_mm repeat, and not a cross-architecture comparison. 5. Scope: the EP correctness fix should land on its own. Indexing EP DTensors by global expert, which inflates expert weight scales and makes Smaller points, not blocking:
|
Note
[3/3] of the OMNIML-6073 split: #3088 (FSDP2's weight-access window), #2745 (
grouped_mmquantization) and this PR (calibration ongrouped_mmandtorch_a2a). It was split out of #2745 on 2026-10-27, night, and the FSDP2 window moved out of it into #3088 on 2026-10-28, at the bot review's request. #3067 (Nemotron-H) sits on this PR.Base:
main. #3088, #2745 and #2736 merged on 2026-10-28, so this PR sits on main, rebased with its own diff unchanged.In the tree: #2741's helper base merges #3067, which carries this PR and #2745.
Series layout
mainWhat does this PR do?
Type of change: New feature, with a backward-incompatible change to MoE calibration (see the changelog entry)
#2745 quantizes the
grouped_mmexperts forward, transformers' default, while calibration still runs every model on eager. This PR moves calibration ontogrouped_mmand thetorch_a2aexpert-parallel forward, and makes everything that reads the experts' inputs or weights during calibration work there. Each gap below otherwise failed silently, by collecting no amax or the wrong one rather than by raising.normalize_experts_impl_on_the_flyreplacesforce_eager_experts_impl_on_the_fly.eager,grouped_mmandtorch_a2aare kept; a backend nothing intercepts (batched_mm, the opaquesonicmoekernel, hub kernels) is moved togrouped_mmbefore calibration, or toeageron a transformers without_grouped_linear. A model whose experts quantize only on the eager forward keeps main's forced eager: native FP8 experts, a class with its own wrapper (GPT-OSS's quantizesdown_proj's input by intercepting the eager forward's matmuls), and Nemotron-H's non-gated experts until [OMNIML-6073] Quantize Nemotron-H's experts under grouped_mm, with their wrapper in its model folder #3067. Such a model is refused ontorch_a2a, before any config changes, instead of calibrating on raw FP8 bytes or not at all.grouped_mmandtorch_a2afeed a projection's input quantizer one expert-sorted batch. The weight-input captures (local Hessian, imatrix, GPTQ) split it back per expert by the cumulative token offsets the grouped interception records, and a capture fed by the other path than the experts' config selects is refused rather than attributed to the wrong experts.fold_weightindexed them by local position too, which on a DTensor reads the global expert; with the dense part FSDP2-sharded, FSDP2's weight-access window had first gathered every expert onto every rank.calibrate()raises, naming the modules, when a fused-experts forward that had to reach a quantizer ran but neither hook fired: a hand-writtenbmmforward on a class without@use_experts_implementation, which no config change redirects. Eachcalibrate()judges its own forwards and raises with the model back in its training mode. Not reported: experts no calibration token reached (Ernie-4.5-VL's vision experts under text-only calibration, which fall back to weight-derived scales as on main), an eager forward whose rows were all routed to other ranks' experts, and weight-only experts under max or MSE, whose weight quantizers calibrate on the weights alone.Behavior change. main forced MoE calibration onto eager; transformers resolves an unrequested backend to
grouped_mm, so fused-experts models now calibrate there. A single-GPU Qwen3-30B-A3B export, NVFP4 or FP8, matches main in every weight and weight scale, but 4,647 activation scales move, by up to 10.2%. main's own repeat is byte-identical, and loading withexperts_implementation="eager"reproduces main's checkpoint byte for byte (all 74,835 tensors for NVFP4 and 56,211 for FP8), so the backend alone moves them. The changelog says so.down_projinput (33 layers, 128 experts each)gate_projandup_projinput (1 layer)The largest moves are 10.2% on the experts (layer 25's
down_projinput) and 7.8% on attention.Why they move. Both forwards feed an input quantizer the same rows: a single GPU has no padding rows (transformers' sentinel rows exist only under its own expert parallelism, and it zeroes them), the routing weights apply after
down_projon both, and neither casts. They round the block's output differently. Eager adds a token's 8 expert outputs into a bf16 buffer withindex_add_, rounding after each add, whilegrouped_mm_experts_forwardsums them in fp32 and rounds once, and its GEMMs run other kernels. From the first MoE block's output on, the residual stream differs by rounding, and where a router's 8th and 9th scores nearly tie, the routing flips. A flipped token feedsdown_projanother expert's activations, anddown_proj's input scale is a max over routed (token, expert) rows, so it moves by more than rounding would. The moved scales fit that: the experts' sharedgate_proj/up_projinput, whose rows are every token however it is routed, moved in one layer only, against 33 fordown_proj.Why
grouped_mmoff expert parallelism too, rather than eager there andgrouped_mmontorch_a2aonly: it is the forward transformers runs the model on when nothing else is asked for, its fp32 sum is the more precise of the two combines, and expert-parallel calibration runs the grouped path anyway (torch_a2aruns the local experts through_grouped_linear), so a model calibrates on one forward whether or not it is sharded.experts_implementation="eager"keeps earlier releases' checkpoints.Usage
Testing
tests/unit/torch/quantization/plugins/test_fused_experts.py, against synthetic and real transformers experts modules:_grouped_linear;grouped_mmandtorch_a2amatch eager's per expert, and a capture follows whichever path ran;down_proj's input on the eager forward.torch_a2aforward like the whole quantized module: the fake-quantized case oftest_moe_a2a.py's whole-module test;test_moe_a2a.py's chunked-hops test also runs forward and backward through hops sliced below the limit, and checks the output and input gradients bit for bit against the unsliced run._grouped_linearoverride does. Its hop-slicing comments say what the slicing guarantees, every hop withinall_to_all_single's int32 element offsets, rather than that it prevents a hang: with the slicing disabled, hops past 2**31 elements completed exactly on NCCL 2.29, 2.30 and 2.31, including at 32 ranks over the network on 2.29 and 2.30.down_proj's input amax equal to main's; Ernie-4.5-VL-MoE quantizes in FP8 and NVFP4 under text-only calibration, as on main.grouped_mm, NVFP4 and FP8; Qwen2.5-0.5B (dense) is byte-identical. On GPU, Nemotron-3-Nano-30B-A3B's NVFP4 calibration with its experts pinned to eager and [OMNIML-6073] Quantize Nemotron-H's experts under grouped_mm, with their wrapper in its model folder #3067's withexperts_implementation="eager"give byte-identical exports, all 18,012 tensors.grouped_mmis kept, and redirected to, only where_grouped_linearcan be intercepted; one capability constant gates the interception, the supported set and the redirect target;torch_a2atest fixtures [OMNIML-6073] [2/3] Quantize fused experts under grouped_mm; leave transposed layouts unquantized #2745 no longer carries._grouped_offswhen it returns, so a capture reached outside a forward skips instead of splitting its input by a finished forward's offsets; the interception check's docstring says that a rank whose experts saw no rows waits in its next collective until the launcher stops it or the collective times out, so the error is in another rank's log. Rebased over [OMNIML-6073] [2/3] Quantize fused experts under grouped_mm; leave transposed layouts unquantized #2745's cleanup.e7c449fcf87, after [OMNIML-6073] [2/3] Quantize fused experts under grouped_mm; leave transposed layouts unquantized #2745 moved onto main7092db6ac5band took its last review rounds. Conflicts only in the changelog (both sides' entries kept),_QuantFusedExperts's docstring (it keeps [OMNIML-6073] [2/3] Quantize fused experts under grouped_mm; leave transposed layouts unquantized #2745's note that thegrouped_mmfallback does not callF.linear) and the fused-experts tests ([OMNIML-6073] [2/3] Quantize fused experts under grouped_mm; leave transposed layouts unquantized #2745's transposed-container tests kept). The FP8 experts wrapper's comment from [OMNIML-6073] [2/3] Quantize fused experts under grouped_mm; leave transposed layouts unquantized #2745 now namesnormalize_experts_impl_on_the_fly, this PR's name for the function that keeps it on eager. pre-commit, mypy included, passes at this head against main.calibrate()'s interception check reads the HF plugin fromsys.modulesinstead of importing it, so a plugin that failed to load (whichimport_pluginonly warns about) no longer fails every calibration with its error.test_a_plugin_that_failed_to_load_leaves_calibration_alonebreaks the plugin's import and fails without the change. pre-commit, mypy included, passes at this head.test_fused_experts.py,test_moe_a2a.pyandtest_utils.pypass on CPU (200 passed, 6 skipped), and pre-commit, mypy included, passes against main._local_expertsrefuses a DTensor whose local shard does not hold the module's experts (one replicated over the ranks, say) instead of indexing the global experts, and the native FP8 wrapper'sunpack_weightrefuses expert weights sharded over the experts instead of filling a buffer for every expert. One two-rank test covers both and fails without either. The suggested all-reduce in the interception check is declined (a collective in every distributedcalibrate()). Focused tests pass (201 passed, 6 skipped), and pre-commit passes against main.grouped_mmanda2a_moebuild the offsets, and raises on any other form (a leading zero would credit each expert with the previous one's rows). A test feeds three such forms and fails without the check. The_grouped_offscomment names all three captures and the attribute's role as the which-forward-ran signal. Rebased onto the helper base re-merged with [OMNIML-6073] [1/3] Gather only FSDP2's own parameters in its weight-access window #3088's update; focused tests pass (204 passed, 6 skipped), and pre-commit passes against main._local_expertschecks for a DTensor through the plugin's guardedtorch.distributed.tensorimport, so a torch without distributed does not fail every fused-MoE call. Rebased onto the helper base re-merged with [OMNIML-6073] [1/3] Gather only FSDP2's own parameters in its weight-access window #3088's update; focused tests pass (204 passed, 6 skipped), and pre-commit passes against main.hf_ptq.pygains--experts_implementation, passed on every load path:get_model(the compressed-tensors branch included), the low-memory load, and the FSDP2 load, through the config its meta model is built from. The changelog's way back to earlier releases' scales is now reachable from the script. A test loads a tiny Qwen3-MoE witheagerand fails without the plumbing. Thetests/examples/hf_ptqunit tests pass (110), and pre-commit passes against main._experts_implementation, asbuild_meta_causal_lmsetsattn_implementation, and a test captures the config the FSDP2 loader receives (it fails without the assignment);get_modelpasses the flag only when given, on every branch.calibrate()'s new error for a fused-experts forward that reaches neither hook has its own changelog entry. Rebased onto [OMNIML-6073] [2/3] Quantize fused experts under grouped_mm; leave transposed layouts unquantized #2745, itself rebased onto mainbc0b6e03237after [OMNIML-6073] [1/3] Gather only FSDP2's own parameters in its weight-access window #3088 merged; focused andtests/examples/hf_ptqtests pass (316 passed, 6 skipped), and pre-commit passes against main.Nonebackend pinned to eager, which already dispatches to the eager forward. Rebased onto [OMNIML-6073] [2/3] Quantize fused experts under grouped_mm; leave transposed layouts unquantized #2745 on maine9f432affbb(Bump minimum torch to 2.10 and drop older-torch code paths #3091):DTensornow comes from the plugin's plaintorch.distributed.tensorimport, since the guarded import is gone with the torch 2.10 floor. Focused andtests/examples/hf_ptqtests pass (325 passed, 6 skipped), and pre-commit passes against main.warn_rank_0, so an expert-parallel run prints one copy. Rebased onto [OMNIML-6073] [2/3] Quantize fused experts under grouped_mm; leave transposed layouts unquantized #2745's comment and warning-text update;_is_fused_experts_module's new docstring namesnormalize_experts_impl_on_the_fly, which keeps GPT-OSS on eager here.test_fused_experts.pyandtest_utils.pypass (197), and pre-commit passes against main.2e253a509d0, 2026-10-28, late night: the FSDP2 test runs a Qwen3.5-MoE config too and checks the text config its experts dispatch on, which the one assignment reaches through transformers' recursive_experts_implementationsetter (5.5.4 and 5.18; a tiny Qwen3.5-MoE built on meta dispatches its experts oneagerwith the flag and ongrouped_mmwithout it). The test fails when the value reaches the root config only, and the comment at the assignment says the setter carries it. Declined: resolving the routing indices' parameter name from the forward's signature, since every transformers MoE block passes them positionally. Thetests/examples/hf_ptqunit tests pass on 5.18 and 5.5.4 (120), and pre-commit passes against main.042e4bf1e55(approved), 2026-10-28, late night: the interception error names the ways forward: a class no backend change redirects needs its own quantized wrapper, and meanwhile the rest of the model quantizes with these modules' quantizers disabled, which the check honours. Declined: caching the grouped offsets' host copy across the two projections' captures (one small sync per experts module per batch under GPTQ, local Hessian and imatrix, whose per-expert matmuls dominate). pre-commit passes against main.1ca8bd1fc4dafter [OMNIML-6073] [2/3] Quantize fused experts under grouped_mm; leave transposed layouts unquantized #2745 merged (squashed with the tree of its last head, so this PR's tree is unchanged); the base is nowmain, so the GPU tests run. pre-commit passes against main.tests/examples/hf_ptq/test_hf_ptq_vision_quantization.pyin the trtllmhf_ptqlane (2 failed, 267 passed): its stand-in for hf_ptq's parsed arguments lacked the newexperts_implementation, whichload_modelnow reads. The stand-in gains it; the file passes on transformers 5.18 and 5.5.4.bdc45368d10([OMNIML-6052] Add the distribution plan, a run-scoped process group, and collectives that fail together #2736 merged), with its own diff unchanged. Its tests with [OMNIML-6073] Quantize Nemotron-H's experts under grouped_mm, with their wrapper in its model folder #3067's, [OMNIML-6052] Add the distribution plan, a run-scoped process group, and collectives that fail together #2736'stest_distributed.pyand thehf_ptqunit tests pass (376), and pre-commit passes against main.df2e3fe4a76, GPU lanes included, passes.Before your PR is "Ready for review"
grouped_mm, so some activation scales differ from earlier releases;experts_implementation="eager"restores the earlier checkpoint exactly.CONTRIBUTING.md: N/A042e4bf1e55; the error-message change after it is from that review.Additional Information
Split out of #2745 (part 3 of 21 of expert-parallel PTQ for Hugging Face MoE models), which merged on 2026-10-28.
Summary by CodeRabbit
grouped_mmbackend instead of forcing eager execution. Calibrated activation scales may differ; setexperts_implementation="eager"to use the earlier calibration behavior.torch_a2araises an error.