Skip to content

[spmd_types] manual loss parallel CE - #3467

Merged
pianpwk merged 5 commits into
mainfrom
gh/pianpwk/18/head
Jun 5, 2026
Merged

pianpwk merged 5 commits into
mainfrom
gh/pianpwk/18/head

Conversation

@pianpwk

@pianpwk pianpwk commented May 31, 2026 •

Copy link
Copy Markdown
Contributor

Adds the spmd_types integration to titan part-by-part, starting from replacing the torch.distributed.tensor.parallel.loss_parallel() DTensor-based context manager with a manually written _LossParallelCrossEntropy custom autograd function.

Written to support uneven sharding (on last rank), IGNORE_INDEX, verified bitwise against loss_parallel() in test cases. The PR adds spmd_types requirement to titan, as it includes typecheck_forward & testing.

Not yet wired into the training path - the later stack will wire it for the spmd backend path, but likely leaves DTensor & plain-tensor on the loss_parallel() path for now.

Stack from ghstack (oldest at bottom):

pianpwk added 2 commits May 31, 2026 16:22
[ghstack-poisoned]
[ghstack-poisoned]
pianpwk added 2 commits June 1, 2026 09:56
[ghstack-poisoned]
[ghstack-poisoned]
@pianpwk pianpwk changed the title Add standalone loss parallel cross entropy [spmd_types] manual loss parallel CE Jun 1, 2026
@pianpwk
pianpwk marked this pull request as ready for review June 1, 2026 17:01
@pianpwk
pianpwk requested a review from ezyang June 1, 2026 17:01
"""

@staticmethod
def typecheck_forward(

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.

Is typecheck_forward an spmd types native solution for autograd function?

This sounds a partial solution.

  • In torchtitan we shouldn't do partial mesh check, instead we should do full mesh check.
  • There are more than one way to handle things, including local_spmd field in Module. Is there way to consolidate?

Is it worth it to introduce a Node(Configurable) which

  • Module inherits
  • can still have in / out placements

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Is typecheck_forward an spmd types native solution for autograd function?

yes, see spmd_types docs, the typechecker will look for this method

# Set of autograd.Function subclasses registered with a typecheck_forward
# staticmethod.  When .apply() is intercepted by __torch_function__,
# typecheck_forward is called INSTEAD of .apply().  It should assert_type()
# on inputs, call .apply() to execute, and assert_type() on the output.

In torchtitan we shouldn't do partial mesh check, instead we should do full mesh check.

hmm, I feel this should be handled by earlier module boundaries where we declare activation placements, or the later lm_head sharding? (I should ensure that's explicit and asserted). This fn in theory shouldn't touch the DP/CP axes, so re-checking if DP/CP is on seems a bit messy.

There are more than one way to handle things, including local_spmd field in Module. Is there way to consolidate?

They should serve different purposes here - local_spmd regions are still transparent to typechecking, we just switch to local typecheck. With custom autograd typecheck the type propagation is opaque, so this is a black-box where typecheck_forward is trusted by the typechecker (no typecheck).

Node(Configurable)

this seems... useful for chunked loss, and maybe other contexts outside spmd types too. But I'm not sure about the scope of this - in MoE & VL there are some forward sections & functions that require local/no-typecheck - would the plan be to refactor all of them to Node

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.

OK that sounds convincing!

in MoE & VL there are some forward sections & functions that require local/no-typecheck - would the plan be to refactor all of them to Node

that seems a different topic, I think we should kill the non-DTensor code as much as possible, outside the modules that we apply local_spmd on.
@shuhuayu can also help on this.

)
output_type = dict(spmd.get_local_type(logits))
output_type[tp_group] = spmd.I
spmd.assert_type(result, output_type)

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.

My worry is that such autograd functions would not enforce typing on all mesh axes

@tianyu-l tianyu-l left a comment

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.

The only concern is that this code not being actually integrated, but I'll stamp.

Comment thread torchtitan/components/loss.py Outdated
)


def _vocab_shard_size_and_offset(

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.

the file is already big, maybe make this inline if it's only used for loss parallel

"""

@staticmethod
def typecheck_forward(

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.

OK that sounds convincing!

in MoE & VL there are some forward sections & functions that require local/no-typecheck - would the plan be to refactor all of them to Node

that seems a different topic, I think we should kill the non-DTensor code as much as possible, outside the modules that we apply local_spmd on.
@shuhuayu can also help on this.

@fegin fegin left a comment

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.

LGTM, some change requests but not critical.

safetensors
einops
pillow
spmd_types==0.2.0

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.

The README.md still uses pip install -r requirements.txt. So we should also add this to the general requirements.txt

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think it's just a symlink to this one?

ctx,
logits: torch.Tensor,
labels: torch.Tensor,
tp_group: dist.ProcessGroup,

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.

Can we take a DeviceMesh? Most places in trainer still take DeviceMesh. So it is better to be consistent. Or is there any blocker?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think once spmd infra lands we can just call current_mesh() and stop threading this. I wasn't planning to move full DTensor to this as part of the stack yet

Comment thread torchtitan/components/loss.py Outdated
Comment on lines +62 to +63
``IGNORE_INDEX`` labels. Forward uses three TP all-reduces (max, sumexp,
gather); backward is fused (NLL + log-softmax) with zero collectives.

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.

nit: use three TP all-reduce to reduce the results of three oerators.

Comment thread torchtitan/components/loss.py Outdated
safe_labels >= vocab_start + local_vocab_size
)
local_labels = safe_labels - vocab_start
local_labels = local_labels.clone()

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.

Is this clone redundant? safe_labels - vocab_start creates a new tensor. So clone() seems to be redundant.

Comment thread torchtitan/components/loss.py Outdated
local_labels[out_of_range] = 0

local_result = torch.gather(log_probs, -1, local_labels.unsqueeze(-1))
local_result = local_result.clone()

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.

ditto

Comment thread torchtitan/components/loss.py Outdated
safe_labels >= ctx.vocab_start + ctx.local_vocab_size
)
local_labels = safe_labels - ctx.vocab_start
local_labels = local_labels.clone()

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.

same redundant clone()?

[ghstack-poisoned]
@pianpwk
pianpwk changed the base branch from gh/pianpwk/18/base to main June 5, 2026 16:55
@pianpwk
pianpwk merged commit fec0c17 into main Jun 5, 2026
9 of 13 checks passed
@pytorch pytorch deleted a comment from pytorch-bot Bot Jun 5, 2026
@pytorch pytorch deleted a comment from pytorch-bot Bot Jun 5, 2026
saforem2 added a commit to saforem2/torchtitan that referenced this pull request Jun 7, 2026
…tor still TODO

Documents the 34-commit pull from upstream/main (27aa490..641b5f6,
merge commit fb1c5a3, sitting in worktree ezpz-46th-47th-sync).
Two structural refactors hit ezpz:

  - PR pytorch#3458 (RoPE refactor): REPLAYED in commit 02dd1e7, verified
    by importing + building all agpt + moe flavors under the torch
    2.13 venv. Numerics smoke against baselines still pending.
  - PR pytorch#3269 (mixed-optimizer refactor): NOT YET STARTED. Affects
    OptimizersContainer.Config + 8 custom container subclasses in
    optimizer/containers.py + ~13 callsites in agpt/moe/competition
    config registries.

Also documents that spmd_types==0.2.1 was installed into .venv (new
upstream runtime dep from PR pytorch#3466/pytorch#3467/pytorch#3560) and notes the open
follow-ups (replay PR pytorch#3269, smoke against baselines, audit RL).

Doesn't update upstream-sync.md's "next-sync" reminder banner —
this sync isn't complete yet (ezpz branch is not on the worktree
HEAD).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ciflow/8gpu CLA Signed This label is managed by the Meta Open Source bot.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants