Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
93ca713
Apply the model spec when deciding and loading expert-parallel
shengliangxu Oct 30, 2026
f649c17
Import the spec applier at the top of model_load_utils
shengliangxu Oct 30, 2026
319e6aa
Say an MTP head the FSDP2 load cannot build rules FSDP2 out too
shengliangxu Oct 30, 2026
8e5f89f
Load a MoE only its spec makes expert-parallel on GPUs, and run it
shengliangxu Oct 30, 2026
56873a3
Keep a spec-attached EP plan out of the loaded model's config
shengliangxu Oct 30, 2026
051bd5e
Read the plan to keep inside the load's collective refusal
shengliangxu Oct 30, 2026
be3b0c5
Warn when the load cannot read the plan its checkpoint declares
shengliangxu Oct 30, 2026
395e9fb
Refuse an MoE whose spec refuses EP with NotImplementedError
shengliangxu Oct 30, 2026
731e96b
Warn on the rank that cannot read the plan its checkpoint declares
shengliangxu Oct 30, 2026
f17a986
Name the transformers release the left-whole refusal asks for
shengliangxu Oct 30, 2026
6fa3305
Offer the transformers upgrade only where an EP degree could shard
shengliangxu Oct 30, 2026
2cea645
Name the EP degree to raise beside the upgrade when the caller set it
shengliangxu Oct 30, 2026
54269c9
Tell the decision's own refusal from the upgrade's by its pattern
shengliangxu Oct 30, 2026
b319920
Leave the model spec out of a single process's plan
shengliangxu Oct 30, 2026
4a12012
Apply no model spec for a model class the sharded loads refuse
shengliangxu Oct 30, 2026
b7abfed
Check that a rank without its GPU prepares no config passed in
shengliangxu Oct 30, 2026
9d817b2
Say a plan set on a config passed to the load is not kept either
shengliangxu Oct 30, 2026
769f345
Word the FSDP2 way around expert parallelism once
shengliangxu Oct 30, 2026
78cf24e
State that a spec's prepare may run for a plan that is not expert-par…
shengliangxu Oct 30, 2026
0214399
Take a config left unmarked as not yet prepared by the model spec
shengliangxu Oct 30, 2026
afb048e
Ask for the EP degree where the routing test expects the FSDP2 refusal
shengliangxu Oct 30, 2026
71e5d34
Say why the spec rules expert parallelism out in the left-whole refusal
shengliangxu Oct 31, 2026
6173bea
Expect the spec's floor refusal for an MoE below it, left to decide
shengliangxu Oct 31, 2026
fabb8a2
Leave the spec's expert-parallel refusals to the spec-aware decision
shengliangxu Oct 31, 2026
17dd0e8
Refuse a model its spec refuses in the expert-parallel load itself
shengliangxu Oct 31, 2026
70ce8c4
Read the checkpoint's config.json as UTF-8
shengliangxu Oct 31, 2026
d8059b3
Ask the model spec first in the expert-parallel load
shengliangxu Oct 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/source/guides/_distributed_loading.rst
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ Configs
``hf_config`` is the config ``resolve_distribution_plan`` returned with the plan handed in as
``distribution``: the load builds from it as given, so without that plan it is refused. A
``config`` passed for the load, or a path to one, is the one the plan is decided from, and a sharded
load takes it as its ``hf_config``.
load takes it as its ``hf_config``: the expert-parallel load a copy of it the model spec prepares
(``apply_distributed_support``), as deciding does.

Hub options
===========
Expand Down
6 changes: 5 additions & 1 deletion modelopt/torch/models/specs.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,11 @@ class EPSpec(SpecSection):
config to load. Reserved for checkpoints carrying their own modeling code, where the work is
patching classes or installing on-the-fly weight decompression. Runs after the declarative
fields above have been applied, so a ``prepare`` that returns another config than the one it
was handed must carry over the ``base_model_ep_plan`` they attached."""
was handed must carry over the ``base_model_ep_plan`` they attached.

It runs before the run's plan is decided, wherever the plan could still be expert-parallel, so
a run whose EP degree comes out at 1 has run it too: it must leave a load of the whole model,
on every rank or FSDP2-sharded, as it would be without it, and may run more than once."""

prepared_ep_plan: dict[str, str] | None = None
"""The ``base_model_ep_plan`` ``prepare`` attaches, for a model whose plan only ``prepare``
Expand Down
38 changes: 26 additions & 12 deletions modelopt/torch/utils/moe.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,30 @@ def _ep_spec(model_type: str):
return getattr(get_spec(model_type), "ep_spec", None)


def _ep_floor(model_type: str) -> str:
"""The transformers release ``model_type``'s expert parallelism needs.

The expert-parallel load's own, or its spec's when that is later. A model whose spec states no
release, or that has no spec, still needs the load's: auto must pick no EP degree below it,
rather than reach the load and fail there.
"""
ep = _ep_spec(model_type)
stated = ep.requires_transformers if ep is not None else None
return max(_EP_MIN_TRANSFORMERS, stated or _EP_MIN_TRANSFORMERS, key=parse)
Comment on lines +159 to +168

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[SUGGESTION] _ep_floor factors this computation out of check_distributed_support, but model_load_utils._ep_load_refusal still inlines the identical expression with the version literal spelled out twice:

floor = max("5.7", (ep.requires_transformers if ep is not None else None) or "5.7", key=parse)

Since this PR already imports _ep_floor into model_load_utils, that line can become floor = _ep_floor(hf_config.model_type), which removes the last hardcoded "5.7" on that path (_EP_MIN_TRANSFORMERS is the single source elsewhere) and keeps the two floors from drifting if a spec-floor rule changes. The only behavioral difference is that the floor would then resolve the spec through moe._ep_spec rather than model_load_utils.get_spec; see the note on the its-spec test row for why that matters there.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved in c7619f8 by deleting the line rather than rewriting it. The floor arm of _ep_load_refusal is gone (see #2746 (comment)), so on that path the floor is _ep_floor's, through _spec_refuses_ep. The load's own check reads _EP_MIN_TRANSFORMERS from moe, and the local copy #2780 added is dropped here.



def _fsdp2_way_out(fsdp: str = "MODELOPT_FSDP_SIZE=world") -> str:
"""FSDP2 as a refusal names it, the way around expert parallelism ``fsdp`` asks for."""
# The FSDP2 load builds a causal LM and reads its tensors as stored, so a quantized or
# multimodal checkpoint (GPT-OSS's MXFP4, Llama-4's) needs a single process, as does one with
# an MTP head the load cannot build (--use_fsdp2 refuses Nemotron-H's).
return (
f"(--use_fsdp2 in examples/hf_ptq/hf_ptq.py, or {fsdp}) if the FSDP2 load supports the "
"checkpoint, which takes an unquantized causal LM with no MTP head it cannot build (such "
"as Nemotron-H's)"
)


def check_distributed_support(model_type: str) -> None:
"""Refuse ``model_type``'s expert parallelism if its spec does, or below its transformers floor.

Expand All @@ -167,26 +191,16 @@ def check_distributed_support(model_type: str) -> None:
expert-parallel plan handed in, meets these errors; both name the way around expert parallelism.
"""
ep = _ep_spec(model_type)
# The FSDP2 load builds a causal LM and reads its tensors as stored, so a quantized or
# multimodal checkpoint (GPT-OSS's MXFP4, Llama-4's) needs a single process, as does one with
# an MTP head the load cannot build (--use_fsdp2 refuses Nemotron-H's).
without_ep = (
"without expert parallelism: in a single process, or FSDP2-sharded (--use_fsdp2 in "
"examples/hf_ptq/hf_ptq.py, or MODELOPT_FSDP_SIZE=world) if the FSDP2 load supports the "
"checkpoint, which takes an unquantized causal LM with no MTP head it cannot build (such "
"as Nemotron-H's)"
f"without expert parallelism: in a single process, or FSDP2-sharded {_fsdp2_way_out()}"
)
if ep is not None and ep.unsupported:
raise NotImplementedError(
f"{model_type} cannot be quantized expert-parallel yet: {ep.unsupported} Quantize it "
f"{without_ep}."
)
# A model whose spec states no release, or that has no spec, still needs the load's floor: auto
# must pick no EP degree below it, rather than reach the load and fail there.
stated = ep.requires_transformers if ep is not None else None
floor = max(_EP_MIN_TRANSFORMERS, stated or _EP_MIN_TRANSFORMERS, key=parse)
try:
require_transformers(floor, f"{model_type} expert-parallel PTQ")
require_transformers(_ep_floor(model_type), f"{model_type} expert-parallel PTQ")
except RuntimeError as e:
raise RuntimeError(f"{e} Or quantize it {without_ep}.") from None

Expand Down
Loading
Loading