Repository navigation
[OMNIML-6071] Calibrate a distributed model on per-rank data - #2740
shengliangxu wants to merge 35 commits into
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 Review skippedAuto reviews are disabled on base/target branches other than the default branch. 🗂️ Base branches to auto review: 3
Please check the settings in the CodeRabbit UI or the
|
| Layer / File(s) | Summary |
|---|---|
Per-rank batch-size probing modelopt/torch/utils/dataset_utils.py, tests/unit/torch/utils/test_dataset_utils.py |
Each rank probes its local GPU and contributes its regulated batch size to a minimum reduction. Device-specific probes do not retry smaller batches. |
Calibration data sharding modelopt/torch/utils/distributed.py, tests/unit/torch/utils/test_distributed_calibration.py |
Adds map-style and iterable dataset sharding. The rebuilt loader preserves its settings and handles uneven or short inputs as tested. |
Calibration forward loop and wrapper restoration modelopt/torch/utils/distributed.py, tests/unit/torch/utils/test_distributed_calibration.py |
Adds expert-parallel mesh validation, A2A wrapper restoration, temporary cache disabling, CUDA allocator cleanup, optional device movement, and calibration forwards. |
Rank-local calibration guard modelopt/torch/quantization/model_quant.py, modelopt/torch/utils/distributed.py, tests/unit/torch/utils/test_distributed_calibration.py |
Rejects selected rank-local calibration methods when the model has a recorded multi-rank plan. The guard runs before calibration or quantization changes the model. |
Priority:
Estimated code review effort: 3 (Moderate) | ~25 minutes
Change: Feature
Sequence Diagram(s)
sequenceDiagram
participant Caller
participant distributed_forward_loop
participant DataLoader
participant Model
Caller->>distributed_forward_loop: Create calibration callback
distributed_forward_loop->>DataLoader: Read calibration batches
distributed_forward_loop->>Model: Reinstate wrappers and run batch forward
Model-->>distributed_forward_loop: Return from forward
Suggested reviewers: kevalmorabia97
Merge Risk: 
Moderate · previous review 60b26
-
Some distributed calibration configurations can produce inconsistent scales, and automatic batch sizing fails for an unrecorded FSDP2 model. Address these paths before merging.
Pre-merge checks: 
5
-
Full details: Security Anti-Patterns
-
Explanation
PASS. The pull request adds no listed security anti-pattern. Changed Python code does not add
torch.load(..., weights_only=False),numpy.load(..., allow_pickle=True), hardcodedtrust_remote_code=True,eval()/exec()on external input, or# nosec. Nopyproject.tomlorrequirements.txtfile changes add dependencies.
-
✨ Finishing Touches: 💡 2
-
📝 Generate docstrings: 💡
-
- Commit to this branch
- Create a new PR
🛠️ Fix failing CI checks: 💡
-
- 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.
1b34e71 to
c99d63a
Compare
7f91f94 to
963e78a
Compare
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## shengliangx/hfep-4-meta-dcp-load #2740 +/- ##
===================================================================
Coverage ? 61.69%
===================================================================
Files ? 649
Lines ? 72627
Branches ? 0
===================================================================
Hits ? 44808
Misses ? 27819
Partials ? 0
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
/ok to test 963e78a |
c99d63a to
d0d9b5e
Compare
963e78a to
935bd7f
Compare
|
/ok to test 935bd7f |
935bd7f to
c3ccb86
Compare
d0d9b5e to
64f8588
Compare
c3ccb86 to
96159d7
Compare
64f8588 to
7da05a2
Compare
|
/ok to test 96159d7 |
96159d7 to
540716e
Compare
7da05a2 to
00e5f72
Compare
|
/ok to test 540716e |
00e5f72 to
42a2de6
Compare
540716e to
177c692
Compare
…h, reduce over the live group distributed_forward_loop takes the device to move batches to and the expert-parallel mesh whose all-to-all forwards it re-installs, where it took the session that carried both. The calibration batch size is still the smallest any rank allows, reduced with all_reduce over the live process group, each rank measuring its own GPU. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
…ibution plan get_max_batch_size agrees on one batch size across ranks when the model's recorded plan spreads it over several, rather than whenever a multi-rank process group is live. distributed_forward_loop takes the ep_mesh exactly when the plan is expert-parallel: an expert-parallel model without its mesh would run its experts without an EP group at the first step, and a mesh for any other model would be ignored. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
… whole stream The rank shard splits each stream's groups between the DataLoader workers, so a dataset that already splits itself by get_worker_info() is split twice and drops samples. hf_ptq loads with num_workers=0; say so for other callers. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
… CUDA distributed_forward_loop collects garbage and empties the CUDA cache before the first calibration forward and after the last, so NCCL can allocate its buffers outside the caching allocator. A process that never started CUDA has no pool to free, yet each call still paid a full garbage collection, about a second on a large heap: nearly 12 s of the unit run across this file's forward-loop tests. The release now runs only once CUDA is initialized. The forward-loop test that walked a 4-case shard grid keeps one case; shard_dataloader's own tests cover the striding. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
The test started two DataLoader workers per rank, forked from pytest's multithreaded process, which warns that fork may deadlock; in a sandbox where the workers could not use shared memory it hung until its timeout. The split it guards is decided in the shard's __iter__ from get_worker_info(), so the test now runs each worker's pass in this process as the worker it is told it is. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
vlm_dataset_utils._ShardedIterable already strides an iterable dataset across ranks, but it keeps ranks in step by padding each to a per-rank count the VLM loader knows. shard_dataloader wraps any loader's stream, which may have no length, so it drops the trailing partial group instead, and it must also split across the loader's workers, which the VLM loader never starts. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
The test simulated rank 1 of two by stubbing all_gather_object and the world size, and asserted the tuple this rank put on the wire. It now runs get_max_batch_size on both ranks of the shared gloo workers, each with its own GPU's memory faked, and checks that both take the smaller size through the real reduction, measuring only their own GPU with one probe forward. A rank left out of the reduction gives up on a group with a 10 s timeout instead of hanging the run. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Merge the shard tests that differ only in dataset kind or world size:
- the strided map-style test, the iterable strided-and-replay test and
the world-size-one test become one table over {map-style, iterable} x
world size {1, 2, 3}, which also checks the replay for both kinds;
- the map-style padding test and the iterable trailing-drop test become
one uneven-split test that keeps both kinds' asserts.
Fold the _reinstall_a2a_forwards replant test into the forward-loop
replant test (its router, colwise and children asserts; the rank-0 log
line it also checked is wording). Drop, each checked by breaking the
code it guards:
- the no-stacking test: the forward-loop replant test runs two passes
and fails when a repeat call wraps again;
- the no-tp-plan no-op case: removing that early return changes
nothing, so the case caught nothing (the no-mesh case stays: it is the
only guard of its early return);
- the device test's no-device case: the shard-routing test passes no
device and reads the values back.
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
The strided iterable shard drops a trailing incomplete group so every rank yields the same count. A stream with fewer samples than ranks has no complete group at all, so every rank silently calibrated on nothing. Refuse it instead, as the VLM loader's own sharding already does for num_samples < dp_size. Every rank sees the same stream, so all of them refuse it at the first batch, before any forward. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
shard_dataloader forwards the original loader's non-sampler settings to the rebuilt per-rank DataLoader, but not drop_last, so a loader that drops its incomplete final batch got one again. Forward it on both paths. Every rank holds the same number of samples (the sampler pads, the stream keeps whole groups), so ranks still run the same number of batches. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Each rank of a distributed model calibrates on its own data. AWQ (awq_lite, awq_clip, awq_full, and SVDQuant, which runs awq_lite) averages its activation scales over data parallelism but picks each layer's scale from its own rank's search loss; local_hessian and imatrix share nothing between ranks. The ranks' copies of a weight would come out different, so quantize() and calibrate() refuse these algorithms on a model whose recorded plan spans several ranks, before anything changes. Only a recorded plan counts: Megatron models record none, so their calibration is unchanged. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
nvfp4_act_headroom calibrates its weight scales with its weight_scale_algorithm, and lsq starts from its scale_algorithm's; either may be local_hessian, which keeps its statistics on the rank as well. The refusal now reads the methods those settings name too. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
…ral ranks Both take distributed_sync. Turned off, max_calibrate returns before its data-, expert- and tensor-parallel amax syncs, and mse passes the option on, so ranks calibrating on their own data keep different scales for the same quantizer. A config that turns it off, nested as another algorithm's scale algorithm included, is refused like the rank-local algorithms. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
…tion tests Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
From this PR on, get_max_batch_size, which hf_ptq calls with its default --batch_size 0, reads the model's distribution plan, and from the no-gather export on so does export_hf_checkpoint. Each refuses an FSDP2-sharded model with none recorded, and hf_ptq's --use_fsdp2 load, parallel_load_and_prepare_fsdp2, records none: it loads the model whole until hf_ptq moves onto load_pretrained. Record the FSDP2 plan right after that load. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
The job reduced over a group with a 10-second timeout and left it registered on the session's persistent cpu_dist_workers until the pool shut down; it now destroys it. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
…dients shard_dataloader replaces a map-style loader's sampler with the rank's DistributedSampler, so a loader that picked its own samples (a SubsetRandomSampler, or a batch_sampler) silently calibrated on the whole dataset; it is now refused. distributed_forward_loop runs its forwards under no_grad, as the single-node _forward_loop does, and the two new public functions carry type annotations. The comments on releasing the CUDA cache keep their reason and drop the measurements. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
shard_dataloader took an exactly-BatchSampler batch sampler for the one DataLoader builds from batch_size and checked the loader's sampler instead. A loader given batch_sampler=BatchSampler(SubsetRandomSampler(...), 4, False) still carries a default SequentialSampler, so its subset passed the picker refusal and was widened to the whole dataset, and its batch_size of None rebuilt the shard with auto-batching off. The shard now takes the picker from the plain BatchSampler itself, refusing a subset there too, and batches by that sampler's batch_size and drop_last. A loader with auto-batching off is refused. distributed_forward_loop refuses a batch that is not a mapping of forward keyword arguments by name, rather than failing on batch.items() inside the loop. auto_quantize refuses a rank-local calibration on a distributed model in its format check, before it converts the model, as quantize and calibrate do. _max_batch_size builds its probe input only where it probes. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
A loader built with batch_size=None has auto-batching off: it reads one dataset item per step, a pre-batched item included, and its rank's shard, rebuilt with batch_size=None, reads the same way. Sharding it was correct, so refusing it only turned such loaders away. shard_dataloader shards it again, on the map-style and the streaming path, and still refuses a sampler there that picks its own samples. The plain-BatchSampler look-through and the custom batch sampler refusal stay. The tests shard a map-style and a streaming loader with auto-batching off one item per step, and refuse a subset sampler under one. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Under expert parallelism each rank's per-expert quantizers hold its own experts, and max and GPTQ sync them over the data-parallel group their module's ParallelState names, which defaults to every rank: a slot holding expert 0 on one rank was merged with the slot holding another expert on the next, silently. quantize, calibrate and auto_quantize now refuse any calibration on an expert-parallel plan whose modules carry no expert-parallel group, before converting anything; a model whose routed experts carry their ParallelState calibrates as before. The guard is renamed for what it now refuses. get_max_batch_size reads the recorded plan, as that guard does: a model with no plan recorded in a run of several ranks (a Megatron model, say) is measured on every visible GPU again, as on main, instead of being refused. distributed_forward_loop also refuses an ep_mesh whose size is not the plan's ep_size, and the docstrings say that tensors nested in a batch value are not moved and what each DataLoader worker of a streaming shard decodes. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
The expert-parallel calibration guard passed once any module carried an expert-parallel group, an expert-parallel group of None (every rank) included. A router seeded with the group passed it while the routed experts kept the conversion default, a data-parallel group of every rank, which merges different experts' scales; so did experts seeded with a data-parallel group over every rank. The guard now checks every module holding routed experts: the modules carrying an expert-parallel group and, once quantization has converted them, the quantized modules under a container named experts, as transformers' expert-parallel plans name it. Each must carry an expert-parallel group and, for data parallelism, just the plan's edp ranks. _reinstall_a2a_forwards flagged a module once it re-planted its all-to-all forward and skipped it from then on, so a forward rebound again, by another conversion or an export restoring an earlier forward, kept no EP process group. It now skips a module only while its forward is still the wrapper planted there. get_max_batch_size's docstring no longer implies that a distributed model's probe retries after an out-of-memory error, and says that the batch size it arrives at is not probed. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
shard_dataloader(loader, rank, 1) now returns the loader itself, before the sampler check, so a single process keeps its own sampler, a subset or a shuffling one included, rather than having it refused or replaced by an unshuffled DistributedSampler. Recording the FSDP2 plan in hf_ptq's --use_fsdp2 load makes the refusal of rank-local calibration apply there on several GPUs: AWQ, SVDQuant, local Hessian and imatrix, also as an AutoQuantize candidate. The FSDP2 section of the hf_ptq README and the changelog now say so, and the refusal's message and docstring cover the ranks' shards of a weight as well as their copies. shard_dataloader's docstring states that a stream must yield the same samples in the same order on every rank and worker, a shuffling one with a fixed seed of its own; the strided shard's docstring and its short-stream comment read that as the requirement it is. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
hf_ptq quantizes a VLM's vision tower and its other non-language modules apart from the language model, with every quantizer disabled. Under an expert-parallel plan the calibration check refused each of them, since no module there carries an expert-parallel group, though they hold no routed experts whose scales could merge. A module with no expert-parallel group and no container named experts now passes. Experts that carry no group are still refused before conversion, and converted experts with a missing or wrong group before they calibrate. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
The first calibration pass planted a wrapper on every module the plan sends through torch_a2a, its guard recognising only a wrapper it had planted itself. A planned module quantization leaves alone, such as a router quantizer insertion does not convert, still holds transformers' wrapper from the load, and got a second one around it, running its transforms twice. install_forward stores a plain function as the forward and quantizer insertion a bound method over it, so a module is now planted only where its forward is a bound method, or none is set; the marker attribute goes. The transformers-version probe checked that either style has install_forward, then called it on both; it now checks the style about to be called. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
The batch size of a model distributed over several ranks was measured on cuda:LOCAL_RANK, which is not the rank's GPU under a launcher that shows each rank one GPU: there the device lookup failed on every rank but the first. It is measured on the current device now, the one the forward runs on. The docstring and the hf_ptq README also say why that size is not probed for out-of-memory, as a single process's is: a rank that runs out of memory partway through a forward leaves the others waiting in its collectives, so probing in lockstep would hang instead of backing off. Set the batch size explicitly when the estimate does not fit. The single-sample input's expansion now lives in the probing branch, its only user. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
A stream that splits itself across the loader's workers by get_worker_info(), as a Hugging Face streaming dataset does, was split again by rank and silently lost samples: the shards stayed disjoint and the step counts equal, so nothing failed. shard_dataloader now refuses a streaming loader with workers, and the rank's shard no longer splits its groups across workers it cannot have. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
shard_dataloader refuses a streaming loader with workers, so asking the stream to be the same in every worker, and explaining that torch seeds each worker differently, described a case that cannot arise. _StridedIterableShard's docstring carried the same clause. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
shard_dataloader let any RandomSampler through, but one drawing fewer samples than the dataset (num_samples) or with replacement picks which and how many samples the loader reads, as a subset does. The rank's DistributedSampler then replaced it over the whole dataset, so calibration silently ran on all of it instead of the few asked for. Such a sampler is now refused like the other pickers; a sampler that shuffles the whole dataset is still sharded as the rank's unshuffled share. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
The refusal reads the algorithm's options as the caller spelled them, which holds while every registered default syncs over the ranks; the comment names those defaults, so one that stops syncing is known to need refusing here. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
|
/ok to test b2acf58 |
Note
The expert-parallel PTQ series is now a tree instead of one stack (re-cut 2026-10-27, from main 1521053), so PRs that do not depend on each other can be reviewed and merged in parallel. This PR was [8/21]. Rebased onto main 7c3c868 later the same day, after #2739 merged, and onto main f03651e that night.
Base: #2736. The diff below is this PR's alone, and it merges after #2736.
Re-cut: this PR's diff is unchanged; only its base moved. Review fixes since are listed at the end of Testing.
Series layout
mainWhat does this PR do?
Type of change: New feature
The pieces a calibration loop needs once the model is spread across ranks.
shard_dataloadergives each rank its own shard of the data.DistributedSamplerwithdrop_lastoff, so every rank runs the same number of steps; a rank that exits early hangs the others on the per-step collectives.IterableDatasetgets a strided copy of the stream, which must yield the same samples in the same order on every rank (a stream that shuffles needs a fixed seed of its own). A streaming loader with worker processes is refused: a dataset that splits itself across workers, as a Hugging Face streaming dataset does, would lose samples silently. It is not main'svlm_dataset_utils._ShardedIterable, which pads each rank to a count its loader knows: this wraps any loader's stream, which may have no length.distributed_forward_loopbuilds theforward_loopthatmtq.quantizecalls. It moves each batch to thedeviceit is given and runs the full distributed forward, so the expert-parallel collectives fire inside it, with KV caching off as on the single-process path. Each batch is a mapping of the forward's keyword arguments; anything else is refused with aTypeErrorat the first batch.ep_meshits experts are sharded over, and the loop re-plants thetorch_a2aforward wrappers that quantizer insertion clobbers on transformers >= 5.16. A module whose forward is still the wrapper planted there is skipped, so passes do not stack wrappers; one whose forward was rebound since (another conversion, an export) is planted again. Any other model passes none. A mesh missing for an expert-parallel model, or given for another, raises.--batch_size 0sizes calibration withget_max_batch_sizeon each rank: it measured every visible GPU, and on an out-of-memory error halved the batch and probed again, a different number of collective-bearing forwards per rank. For a model whose recorded plan spreads it over several ranks it is now collective: each rank measures the GPU it computes on (torch.cuda.current_device()) from one forward of the single-sample input, without probing the size it arrives at (a rank running out of memory partway through a probe's forward would leave its peers waiting in that forward's collectives), and every rank takes the smallest size any rank allows, reduced withall_reduce(..., min)([OMNIML-6052] Add the distribution plan, a run-scoped process group, and collectives that fail together #2736). A single process, and a model with no plan recorded (a Megatron model, say), are unchanged.maxall-reduces its amax, GPTQ merges its Hessians across data-parallel replicas, and MSE and SmoothQuant work from max's synced amax. AWQ does not: it averages its activation scales over data parallelism but picks each layer's scale from its own rank's search, and a 2-rank probe showed the replicas' weights andpre_quant_scalediverging.local_hessianandimatrixshare nothing either, and neither do max and MSE withdistributed_syncoff.mtq.quantize,mtq.calibrateandmtq.auto_quantizerefuse these (awq_lite,awq_clip,awq_full,svdquant, which runsawq_lite,local_hessian,imatrix, and any config that turnsdistributed_syncoff, also as another algorithm'sweight_scale_algorithmorscale_algorithm) on a model whose recorded plan spans several ranks, before converting anything (auto_quantizein its format check). A model with no plan recorded, such as a Megatron model, is unchanged. On an expert-parallel plan, any calibration is refused unless every module holding routed experts carries the experts' expert-parallel group and, as its data-parallel group, just the plan'sedpranks, those holding the same experts: each rank's per-expert quantizers hold its own experts, and max's and GPTQ's data-parallel sync would otherwise merge different experts' scales. The modules holding routed experts are those carrying an expert-parallel group and, once quantization has converted them, the quantized modules under a container namedexperts, as transformers' expert-parallel plans name it; unseeded experts beside a seeded router are refused once converted, before they calibrate. [OMNIML-6060] Load an MoE expert-parallel: shard the experts at load time #2741's expert-parallel loader gives the routed experts those groups. A module with no routed experts and no expert-parallel group, such as a VLM's vision tower that hf_ptq quantizes on its own with every quantizer disabled, passes.--use_fsdp2load records its plan. The batch sizing above agrees across ranks only for a model that records its plan. hf_ptq loads FSDP2 throughparallel_load_and_prepare_fsdp2, which records none, until [OMNIML-6085] Run expert-parallel PTQ from hf_ptq.py #2754 moves it ontoload_pretrained; it now records the FSDP2 plan right after that load. On several GPUs,--use_fsdp2therefore now refuses the calibrations above (AWQ, SVDQuant, local Hessian, imatrix, also as an AutoQuantize candidate), which calibrated the ranks' shards of a weight differently; the README's FSDP2 section and the changelog's Backward Breaking Changes say so.shard_dataloaderrefuses a loader that picks its own samples (a custombatch_sampler, or a sampler other than sequential, random or distributed, also inside a plainBatchSampleror with auto-batching off) instead of rebuilding it, and keeps a plainBatchSampler's batch size anddrop_lastin the rank's shard; a loader with auto-batching off (batch_size=None) shards one item per step, as it loads. With one rank,shard_dataloaderreturns the loader itself, its sampler and shuffling included.distributed_forward_loopruns its forwards without gradients.get_max_batch_sizereads the recorded plan, as the calibration guard does.distributed_forward_looprefuses anep_meshwhose size is not the plan'sep_size.Usage
Testing
tests/unit/torch/utils/test_distributed_calibration.py(42 cases), on CPU, in one process except where noted. It covers:BatchSampler's batch size anddrop_lastincluded;use_cacheoff and the allocator cache freed around it in a process that started CUDA, and not in one that did not; a batch that is not a mapping refused;ep_meshrefused when missing for an expert-parallel plan, given for another, or over the whole world for anep_size=2plan;quantize,calibrateandauto_quantize(before conversion), also when another algorithm runs it for its weight scales or max is told not to sync, with the model left as it was;distributed_forward_loopruns on real GPU ranks in [OMNIML-6061] Load the dense part sharded, and size the load warmup to each rank #2742's expert-parallel calibration test, which checks each rank's per-expert scales against the whole model; this PR's own tests run it in one process.tests/examples/hf_ptq/test_hf_ptq_args.py: hf_ptq's--use_fsdp2load, stubbed, leaves the model recording its FSDP2 plan.tests/unit/torch/utils/test_dataset_utils.py, on two real gloo ranks (cpu_dist_workers) with only the CUDA memory queries faked per rank: each rank measures only its own GPU, probes once, and both take the smaller batch size. A rank keeping its own size, measuring every GPU, or skipping the reduction fails it, the last after the group's 10 s timeout rather than by hanging; main's single-process retry test still passes. The two-rank test also runs a model with no plan recorded, which each rank measures on every visible GPU, as a single process does, without a refusal.distributed_synccheck, hf_ptq's plan recording, the expert-parallel group refusal, or theep_meshsize check. Refusing every expert-parallel calibration, or reading the plan strictly inget_max_batch_size, fails a test too. Dropping the check on converted experts or the data-parallel size check, keeping a replant flag once set, or planting on every pass each fail a test. Rebuilding a lone rank's loader, refusing a module without routed experts, or passing any module with no expert-parallel group each fail a test too.BatchSamplerand with auto-batching off, and so is a custom batch sampler; a lone rank keeps each such loader as it is. The sampler, batching, non-mapping-batch andauto_quantizecases each fail without their fix (mutation-checked).tests/uniton transformers 5.18 and 5.5 and on torch 2.8, and the hf_ptq example tests, each with the same failures as main in that environment. CI runs the full unit suite.torch.cuda.current_device()); a streaming loader with workers is refused; the docstring and the hf_ptq README say why the measurement does not probe. Each behaviour fix has a test that fails without it; pre-commit, mypy included, and this PR's own unit tests pass at this head.--use_fsdp2are experimental, and refused when a candidate format is one of those this PR refuses.shard_dataloaderrefuses aRandomSamplerthat does not draw the whole dataset once (othernum_samples, or with replacement), as it refuses other samplers.Before your PR is "Ready for review"
hf_ptq.py --use_fsdp2on several GPUs with a calibration that keeps its statistics per rank (AWQ, SVDQuant, local Hessian, imatrix, also as an AutoQuantize candidate), now refused: these calibrated the ranks' shards of a weight differently (changelog, README). Otherwise additive;get_max_batch_sizechanges only for a model whose recorded plan spans several ranks.CONTRIBUTING.md: N/A--use_fsdp2's refusal; the feature's entry lands with [OMNIML-6085] Run expert-parallel PTQ from hf_ptq.py #2754.Additional Information
Part 8 of 21 of expert-parallel PTQ for Hugging Face MoE models; merges after #2739, which has merged.
Summary by CodeRabbit