-
Notifications
You must be signed in to change notification settings - Fork 898
[New Model]Add DeepSeek V4 model support #3634
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Matrix-Z97
wants to merge
8
commits into
pytorch:main
Choose a base branch
from
Matrix-Z97:deepseek_v4
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+2,987
−8
Open
Changes from 6 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
90c57d1
Add deepseek_v4 model support
Matrix-Z97 c2d8a7a
Update torchtitan/models/deepseek_v4/compressor.py
Matrix-Z97 9a47905
Merge branch 'pytorch:main' into deepseek_v4
Matrix-Z97 2991579
Refactor DeepSeek V4 sparse attention routing
Matrix-Z97 78de492
[aux loss] framework: gradient injection and cross-rank reduction
sdmyzlp 7811710
Add DeepSeek V4 indexer aux loss
Matrix-Z97 811953e
Update DeepSeek V4 configs and sharding
Matrix-Z97 7d49617
Refactor DeepSeek V4 attention, RoPE, and aux loss
Matrix-Z97 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| # Copyright (c) Meta Platforms, Inc. and affiliates. | ||
| # All rights reserved. | ||
| # | ||
| # This source code is licensed under the BSD-style license found in the | ||
| # LICENSE file in the root directory of this source tree. | ||
|
|
||
| """Backend-agnostic machinery for model-internal auxiliary losses. | ||
|
|
||
| An auxiliary objective computed deep inside the model (MoE load-balance, DSA | ||
| indexer KL, ...) cannot be routed to the trainer's loss function under pipeline | ||
| parallelism, the single-tensor module-output contract, or fullgraph compile. | ||
| Instead its *gradient* is injected locally via an identity-forward | ||
| autograd.Function, and any cross-rank statistic it needs is reduced with a | ||
| single dispatched helper. | ||
|
|
||
| Everything here is loss-agnostic and parallel-backend-agnostic. The loss math | ||
| (which tensors, which axes are Partial, the denominator) lives in the calling | ||
| module; this module only provides the injection and the reduction primitive. | ||
| """ | ||
|
|
||
| import spmd_types as spmd | ||
| import torch | ||
| from torch.distributed.tensor import DTensor, Partial, Replicate | ||
|
|
||
| from torchtitan.distributed.spmd_types import current_spmd_mesh | ||
|
|
||
| __all__ = ["AuxLossInjection", "inject_aux_loss", "reduce_to_replicate"] | ||
|
|
||
|
|
||
| @spmd.register_autograd_function | ||
| class AuxLossInjection(torch.autograd.Function): | ||
| """Inject an auxiliary-loss gradient without changing the forward value. | ||
|
|
||
| Forward returns ``carrier`` unchanged, so the host module keeps a | ||
| single-tensor return (PP/compile-safe; no value flows to the model output). | ||
| ``aux_loss`` is a scalar built differentiably from module-local tensors; its | ||
| backward seed of 1 lets ordinary autograd propagate the aux-loss gradient | ||
| back into those tensors during the normal model backward. ``carrier`` must | ||
| be on the live autograd graph to the model loss so this backward fires. | ||
| """ | ||
|
|
||
| @staticmethod | ||
| # pyrefly: ignore [bad-override] | ||
| def forward(ctx, carrier: torch.Tensor, aux_loss: torch.Tensor) -> torch.Tensor: | ||
| ctx.save_for_backward(aux_loss) | ||
| return carrier | ||
|
|
||
| @staticmethod | ||
| def typecheck_forward( | ||
| carrier: torch.Tensor, aux_loss: torch.Tensor | ||
| ) -> torch.Tensor: | ||
| # Identity in forward: the output carries the same SPMD type as | ||
| # ``carrier``; ``aux_loss`` does not constrain the output type. | ||
| out = AuxLossInjection.apply(carrier, aux_loss) | ||
| spmd.assert_type(out, dict(spmd.get_local_type(carrier))) | ||
| return out | ||
|
|
||
| @staticmethod | ||
| def backward( # pyrefly: ignore [bad-override] | ||
| ctx, grad_carrier: torch.Tensor | ||
| ) -> tuple[torch.Tensor, torch.Tensor]: | ||
| (aux_loss,) = ctx.saved_tensors | ||
| return grad_carrier, torch.ones_like(aux_loss) | ||
|
|
||
|
|
||
| def inject_aux_loss(carrier: torch.Tensor, aux_loss: torch.Tensor) -> torch.Tensor: | ||
| """Attach ``aux_loss``'s gradient to ``carrier`` (identity in value).""" | ||
| return AuxLossInjection.apply(carrier, aux_loss) | ||
|
|
||
|
|
||
| def reduce_to_replicate(x: torch.Tensor, *, axes: list[str]) -> torch.Tensor: | ||
| """Differentiably all-reduce a ``Partial`` statistic to ``Replicate`` over | ||
| ``axes`` (e.g. ``["cp", "tp"]``), preserving placement on every other axis. | ||
|
|
||
| Which axes are ``Partial`` is a property of the parallelism config and is | ||
| chosen by the caller; this helper hides *how* the reduction is realized. | ||
| Only the ``full_dtensor`` and ``spmd_types`` backends are supported (both | ||
| carry the data/sequence axes in-band): a ``DTensor`` input takes the | ||
| ``full_dtensor`` path, anything else the ``spmd_types`` path. The ``default`` | ||
| backend is unsupported (CP/DP are out-of-band there); enabling the aux loss | ||
| under it is rejected at config time. Axes of size 1, or axes on which ``x`` | ||
| is not ``Partial``, are no-ops, so single-process / unit-test calls (plain | ||
| tensor, no current SPMD mesh) are pure-local. | ||
| """ | ||
| if not axes: | ||
| return x | ||
| if isinstance(x, DTensor): | ||
| return _reduce_full_dtensor(x, axes) | ||
| return _reduce_spmd_types(x, axes) | ||
|
|
||
|
|
||
| def _reduce_spmd_types(x: torch.Tensor, axes: list[str]) -> torch.Tensor: | ||
| mesh = current_spmd_mesh() | ||
| if mesh is None: | ||
| return x | ||
| names = mesh.mesh_dim_names or () | ||
| for axis in axes: | ||
| if axis in names and mesh.size(names.index(axis)) > 1: | ||
| x = spmd.redistribute( | ||
| x, | ||
| mesh.get_group(axis), | ||
| src=spmd.P, | ||
| dst=spmd.R, | ||
| backward_options={"op_dtype": x.dtype}, | ||
| ) | ||
| return x | ||
|
|
||
|
|
||
| def _reduce_full_dtensor(x: torch.Tensor, axes: list[str]) -> torch.Tensor: | ||
| # All data/seq axes are in-band on the DTensor's own mesh; redistribute the | ||
| # Partial axes in ``axes`` to Replicate and leave the rest (e.g. Shard(0) on | ||
| # the batch axes, which FSDP reduces) untouched. | ||
| if not isinstance(x, DTensor): | ||
| return x | ||
| names = x.device_mesh.mesh_dim_names or () | ||
| new = [ | ||
| Replicate() if (names[i] in axes and isinstance(p, Partial)) else p | ||
| for i, p in enumerate(x.placements) | ||
| ] | ||
| return x.redistribute(placements=new) if new != list(x.placements) else x | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see similar contribution in #3864
Can we make sure the design in #3000 suits the need for both?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this implementation was referenced from #3864. I'll need to look closer into #3000 to see if the design can cover both use cases. Will update this thread once I have more clarity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From a quick look, #3000 seems more like a MoE-specific interface, while here we would like to call the aux-loss capability through a common/shared interface that can also cover non-MoE losses such as the DSA indexer loss.
So for the current DeepSeek V4 use case, the #3864-style implementation seems more suitable to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll take a look at both