Repository navigation
Conversation
ExpertsRoutingBypass.batch_size is consumed in exactly one place, behind a
guard that silently collapses to a single chunk when the value is not a
positive int:
if is_moe_subset and batch_size is not None and batch_size > 0 and execute_forward:
So batch_size="2" (the shape a value takes after a JSON round trip), 0 and -3
all quantize successfully with no batching and no warning - while the option
exists precisely to keep expert modules from all being resident at once on a
large MoE checkpoint.
Validated with the repo's own tiny Qwen3-MoE fixture: all three values ran to
completion before this change. They now raise at config construction, which is
where the user can still fix the typo.
There was a problem hiding this comment.
Review: fix(moe): reject a routing-bypass batch size that cannot batch
Verdict: Approve with minor comments. The validation is correctly placed at construction time, the error is actionable, and valid configurations (None / positive int) are unchanged.
Verified
tests/test_moe_bypass_batch_size.py: 14 passed.- Against unmodified
main: 9 failed / 5 passed, so the new tests have real regression value. ruff check gptqmodel/quantization/config.py tests/test_moe_bypass_batch_size.py: clean.- Real dict/JSON path:
QuantizeConfig(moe={"routing": {"class": "ExpertsRoutingBypass", "batch_size": ...}})rejects"2",2.0,0,-3,True; accepts4;to_dict()round-trips. - Unit tests were run CPU-only with GPUs masked; the ROCm check used only devices 2 and 3 (2× Radeon AI PRO R9700).
Findings
tests/test_moe_expert_batching.py:105-111—test_no_batching_when_batch_size_is_zerostill documentsbatch_size=0as "batching disabled", which contradicts the new contract. It only survives because it assignsrouting.batch_size = 0after construction, bypassing__post_init__. Please update it to assert rejection (or remove it). If post-construction mutation should also be impossible, considerfrozen=Trueor a defensive check instage_subset.py.tests/test_moe_bypass_batch_size.py:41-43—test_moe_config_wraps_the_validationdoes not actually exerciseMoEConfig: theValueErroris raised byExpertsRoutingBypass(batch_size=0)beforeMoEConfig(...)is called. A test throughQuantizeConfig(moe={"routing": {"class": "ExpertsRoutingBypass", "batch_size": "2"}})would cover the deserialization path this PR is about.- PR description: an integer survives a JSON round-trip as
int, not"2"; and whenis_moe_subsetis true, a string reachesbatch_size > 0and raisesTypeErrorrather than silently falling back to one chunk. Only0/negative values are silent no-ops. The fix is still valid — just the rationale needs a small correction. - Consistency:
ExpertsRoutingOverride.__post_init__coerces numeric strings ("2"→2), while this change rejects them. If strict rejection is intended, please document it in the field help; otherwise consider coercing for consistency. - Nits: the two
ValueErrorbranches could share one message; the new test is CPU-only, so adding# GPU=-1before the imports would put it in the CPU CI bucket; the SPDX year is 2025 while new files use 2026.
Notes
tests/test_moe_config.py::TestMoEConfig::test_moe_routing_bypass_batch_sizegot past fixture setup here but OOM'd during the clone stage on 2×32GB ROCm, so the end-to-end valid-path run could not complete on this host.tests/test_moe_expert_batching.pyis 9/9 failing on bothmainand this PR due to an unrelated telemetry mock issue, so it is not a regression from this change.- The Ruff CI run is currently
action_required(fork approval), so there are no CI results yet.
Review follow-ups on the routing-bypass validation:
- numeric strings are coerced, matching ExpertsRoutingOverride which already
coerces num_experts_per_tok, so a value that arrived through JSON keeps working
instead of being rejected
- the staged executor re-validates the attribute, because it can be assigned
after construction and would otherwise slip past the batch_size > 0 guard
- test_moe_expert_batching documented batch_size=0 as "batching disabled" and
only passed by mutating the attribute post-construction; it now asserts the
rejection instead
- the MoEConfig test went through the constructor rather than the dict payload
the fix is about; it now exercises QuantizeConfig(moe={...})
- CPU-only marker, SPDX year, and one shared error message
All five addressed in 27558d3.
test_moe_expert_batching.py is 9/9 failing here too, same telemetry mock issue |
Summary
ExpertsRoutingBypass.batch_sizeis consumed behind one guard in the stagedexecutor:
so a value that cannot batch is not an error - it is whatever the guard does
with it:
The first row is the bug: a knob advertised as "reduce VRAM pressure during MoE
quantization" quietly does nothing, and the OOM arrives anyway.
What Changed
normalize_bypass_batch_size()validates the value and coerces numericstrings. Coercion matches
ExpertsRoutingOverride, which already coercesnum_experts_per_tokthe same way, so a value that came through JSON worksinstead of being rejected for its type.
ExpertsRoutingBypass.__post_init__runs it, so a bad value fails atconstruction, where the user can still fix the typo.
construction and would otherwise slip past the guard above.
Accepted:
Noneand positive ints, plus numeric strings coerced to int.Rejected, all with one message:
0, negatives, floats, booleans, non-numericstrings, and non-scalars. The field help says which is which.
Tests
Covered:
Noneand positive ints pass through unchanged"2"-> 2," 16 "-> 16)0,-1,-3,2.0,True,"0","-3","abc", a list and a dict raiseQuantizeConfig(moe={"routing": {"class": "ExpertsRoutingBypass", "batch_size": ...}})coerces
"2"and rejects0Reverting the validation: 15 failed / 6 passed.
End to end on the tiny Qwen3-MoE fixture, through the dict payload:
tests/test_moe_expert_batching.pyused to documentbatch_size=0as "batchingdisabled" and only survived by assigning the attribute after construction; it now
asserts the rejection. That file is 9/9 failing in this environment for an
unrelated telemetry mock issue, and fails identically on unmodified
main; thereplacement test passes in isolation.
ruff checkis clean. The new test file carries# GPU=-1so it lands in theCPU CI bucket.