Skip to content

Commit 5fbc23a

Browse files
LiJunscshxbaiyuzhongw-nvidia
authored
Deepseek v4 Support (#38)
### PR Category <!-- One of [ Train | Inference | Compress | Serve | RL | Core | Hardware | CICD | Tools | Others ] --> [Train] Most of codes are copied from Megatron-LM Dev branch. The dev branch is different with main branch or release version. Megatron LM PR: DeepSeek-V4: NVIDIA#4458 NVIDIA#4481 NVIDIA#4518 mHC: NVIDIA#2943 ### PR Types <!-- One of [ User Experience | New Features | Bug Fixes | Improvements | Performance | Breaking Change| Deprecations | Test Case | Docs | Others ] --> [New features] ### PR Description <!-- Describe what you’ve done --> Add DeepSeek V4 model into FlagScale and Megatron-FL Supported: 1. CSA and HCA 2. Hash Router 3. mHC 4. Engram(optional) Unsupported: 1. Sqrtsoftpuls router score function. ✅ 2. mHC recompute. ✅ 3. Overlap_grad_reduce and overlap_param_gather when Zero 1. ✅ 4. Any infra optimizations. ### NOTE: This is only a draft pr, please reivew to give more suggestions. such as: 1. File structure. - All modules are moved into Megatron-FL ### Next plan: 1. Distributed training. ✅ 3. Muon optimizer with Zero 1 adaptation. 🚧 4. Low precision is out of scope of this pr, limited by resource. 5. Maybe context parallel for sparse attention. 6. Welcome to give more suggestions. --------- Co-authored-by: Hongxiao Bai <hongxiaob@nvidia.com> Co-authored-by: Yuzhong Wang <yuzhongw@nvidia.com>
1 parent 783f79b commit 5fbc23a

40 files changed

Lines changed: 8336 additions & 381 deletions

megatron/core/distributed/finalize_model_grads.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,11 @@ def reset_model_temporary_tensors(config: TransformerConfig, model: List[torch.n
290290
"""
291291
for model_chunk in model:
292292
for module in get_attr_wrapped_model(model_chunk, 'modules')():
293-
if config.moe_router_enable_expert_bias and hasattr(module, 'expert_bias'):
293+
if (
294+
config.moe_router_enable_expert_bias
295+
and hasattr(module, 'expert_bias')
296+
and module.expert_bias is not None
297+
):
294298
module.local_tokens_per_expert.zero_()
295299
if (
296300
config.moe_router_load_balancing_type == "global_aux_loss"
@@ -312,7 +316,11 @@ def _update_router_expert_bias(model: List[torch.nn.Module], config: Transformer
312316
# cases where only the student is in training mode but the teacher is in eval mode
313317
# when using online knoweldge-distillation with Model-Optimizer. In this case, we want
314318
# to avoid updating teacher's expert_bias.
315-
if hasattr(module, 'expert_bias') and module.training:
319+
if (
320+
hasattr(module, 'expert_bias')
321+
and module.training
322+
and module.expert_bias is not None
323+
):
316324
tokens_per_expert_list.append(module.local_tokens_per_expert)
317325
expert_bias_list.append(module.expert_bias)
318326
# For hybrid models with both MoE and Dense layers, this list can be empty.

megatron/core/distributed/param_and_grad_buffer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ def start_param_sync(self, force_sync: bool = False):
345345
# fp32 when grad_reduce_in_fp32 is enabled).
346346
param_dtype = bucket.params_list[0].dtype
347347

348-
if max(bucket.layerwise_param_flat_sizes) == 0:
348+
if bucket.layerwise_param_flat_sizes is None or max(bucket.layerwise_param_flat_sizes) == 0: ##### FalgScale add #####
349349
# All ranks have empty params for this bucket — skip.
350350
bucket.layerwise_gather_list = None
351351
continue

megatron/core/extensions/transformer_engine.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
HAVE_TE = False
8181

8282
_TE_CONFIG_TYPE_KEY = "transformer_engine_config_type"
83+
from megatron.plugin.hetero.parallel_context import get_parallel_context ##### FlagScale add #####
8384

8485

8586
class TransformerEngineConfigType(enum.Enum):
@@ -1033,7 +1034,7 @@ def __init__(
10331034
sequence_parallel=self.config.sequence_parallel,
10341035
fuse_wgrad_accumulation=self.config.gradient_accumulation_fusion,
10351036
tp_group=tp_group if torch.distributed.is_initialized() else None,
1036-
tp_size=get_tensor_model_parallel_world_size(), # FlagScale Add
1037+
tp_size=self.config.tensor_model_parallel_size if get_parallel_context() is None else get_tensor_model_parallel_world_size(), ##### FlagScale Add #####
10371038
get_rng_state_tracker=(
10381039
get_cuda_rng_tracker if get_cuda_rng_tracker().is_initialized() else None
10391040
),
@@ -1543,7 +1544,7 @@ def __init__(
15431544
),
15441545
attn_mask_type=attn_mask_type.name,
15451546
sequence_parallel=self.config.sequence_parallel,
1546-
tp_size=get_tensor_model_parallel_world_size(), # FlagScale Add
1547+
tp_size=self.config.tensor_model_parallel_size if get_parallel_context() is None else get_tensor_model_parallel_world_size(), ##### FlagScale Add #####
15471548
get_rng_state_tracker=(
15481549
get_cuda_rng_tracker if get_cuda_rng_tracker().is_initialized() else None
15491550
),

megatron/core/fusions/fused_bias_dropout.py

Lines changed: 90 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1-
# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.
2-
from typing import Optional, Tuple
1+
# Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
from typing import TYPE_CHECKING, Optional, Tuple
33

44
import torch
55

66
from megatron.core.jit import jit_fuser
77

8+
if TYPE_CHECKING:
9+
from megatron.core.tensor_parallel.random import CheckpointManager
10+
811
# pylint: disable=missing-function-docstring
912

1013

@@ -80,7 +83,26 @@ def bias_dropout_add_fused_inference(
8083
return _bias_dropout_add_func(x_with_bias, residual, prob, False)
8184

8285

83-
def get_bias_dropout_add(training, fused):
86+
def get_bias_dropout_add(
87+
training, fused, mhc_recompute_manager: Optional['CheckpointManager'] = None
88+
):
89+
"""
90+
Get the bias-dropout-add function.
91+
92+
Args:
93+
training: Whether in training mode.
94+
fused: Whether to use fused implementation.
95+
mhc_recompute_manager: Optional CheckpointManager for checkpoint management.
96+
When provided, the returned function will wrap the BDA operation with
97+
CheckpointWithoutOutput for memory-efficient recomputation.
98+
99+
Returns:
100+
A callable that performs bias-dropout-add operation.
101+
"""
102+
if mhc_recompute_manager is not None:
103+
# Return a checkpointed version that handles tuple unpacking internally
104+
return _get_checkpointed_bda(training, fused, mhc_recompute_manager)
105+
84106
if fused:
85107
# jit scripting for a nn.module (with dropout) is not
86108
# triggering the fusion kernel. For now, we use two
@@ -92,3 +114,68 @@ def get_bias_dropout_add(training, fused):
92114
return bias_dropout_add_fused_inference
93115
else:
94116
return bias_dropout_add_unfused(training)
117+
118+
119+
def _get_checkpointed_bda(training, fused, mhc_recompute_manager: 'CheckpointManager'):
120+
"""
121+
Create a checkpointed bias-dropout-add function.
122+
123+
This function handles:
124+
1. Tuple unpacking for x_with_bias (required because save_for_backward can't save tuples)
125+
2. Non-tensor arguments like dropout probability (handled by CheckpointWithoutOutput)
126+
3. Auto-registration to the CheckpointManager
127+
128+
Args:
129+
training: Whether in training mode.
130+
fused: Whether to use fused implementation.
131+
mhc_recompute_manager: CheckpointManager for checkpoint management.
132+
133+
Returns:
134+
A callable that performs checkpointed bias-dropout-add operation.
135+
"""
136+
from megatron.core.tensor_parallel.random import CheckpointWithoutOutput
137+
138+
# Get the underlying BDA function
139+
if fused:
140+
if training:
141+
bda_func = bias_dropout_add_fused_train
142+
else:
143+
bda_func = bias_dropout_add_fused_inference
144+
else:
145+
bda_func = bias_dropout_add_unfused(training)
146+
147+
def _checkpointed_bda(x_with_bias, residual, prob):
148+
"""
149+
Checkpointed BDA that handles tuple unpacking internally.
150+
151+
Args:
152+
x_with_bias: Either a tuple (x, bias) or a single tensor x.
153+
residual: Residual tensor.
154+
prob: Dropout probability.
155+
156+
Returns:
157+
Output tensor after bias-dropout-add.
158+
"""
159+
# Create checkpoint with manager
160+
ckpt = CheckpointWithoutOutput(ckpt_manager=mhc_recompute_manager)
161+
162+
# Handle case where x_with_bias might be a single tensor (e.g., from IdentityOp)
163+
if isinstance(x_with_bias, tuple):
164+
x, bias = x_with_bias
165+
else:
166+
x = x_with_bias
167+
bias = None
168+
169+
# Wrapper function that re-packs the tuple for the actual BDA function
170+
def _bda_wrapper(output, bias, res, dropout):
171+
return bda_func((output, bias), res, dropout)
172+
173+
# Call checkpoint with unpacked arguments
174+
result = ckpt.checkpoint(_bda_wrapper, x, bias, residual, prob)
175+
176+
# No-op when manager is set - manager handles all discarding uniformly
177+
ckpt.discard_output_and_register_recompute(result)
178+
179+
return result
180+
181+
return _checkpointed_bda

megatron/core/fusions/fused_bias_swiglu.py

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,23 @@ def weighted_swiglu(y, weights):
4848
return res.to(dtype)
4949

5050

51+
@jit_fuser
52+
def clamped_swiglu(y, clamp_value):
53+
dtype = y.dtype
54+
y_1, y_2 = torch.chunk(y.to(torch.float32), 2, -1)
55+
y_1 = y_1.clamp(min=None, max=clamp_value)
56+
y_2 = y_2.clamp(min=-clamp_value, max=clamp_value)
57+
res = F.silu(y_1) * y_2
58+
return res.to(dtype)
59+
60+
61+
@jit_fuser
62+
def clamped_weighted_swiglu(y, weights, clamp_value):
63+
dtype = y.dtype
64+
res = clamped_swiglu(y, clamp_value) * weights
65+
return res.to(dtype)
66+
67+
5168
# gradient of tanh approximation of gelu
5269
# gradient of actual gelu is:
5370
# 0.5 * (1. + torch.erf(x * 0.70710678)) + 0.3989423 * x * torch.exp(-0.5 * x * x)
@@ -97,6 +114,36 @@ def weighted_swiglu_back(g, y, weights):
97114
return input_grad.to(input_dtype), weights_grad.to(w_dtype)
98115

99116

117+
@jit_fuser
118+
def clamped_swiglu_back(g, y, clamp_value):
119+
dtype = y.dtype
120+
y_1, y_2 = torch.chunk(y.to(torch.float32), 2, -1)
121+
y_1c = y_1.clamp(min=None, max=clamp_value)
122+
y_2c = y_2.clamp(min=-clamp_value, max=clamp_value)
123+
res = torch.cat(
124+
(
125+
g
126+
* torch.sigmoid(y_1c)
127+
* (1 + y_1c * (1 - torch.sigmoid(y_1c)))
128+
* y_2c
129+
* (y_1 <= clamp_value).to(g.dtype),
130+
g * F.silu(y_1c) * ((y_2 >= -clamp_value) & (y_2 <= clamp_value)).to(g.dtype),
131+
),
132+
-1,
133+
)
134+
return res.to(dtype)
135+
136+
137+
@jit_fuser
138+
def clamped_weighted_swiglu_back(g, y, weights, clamp_value):
139+
input_dtype = y.dtype
140+
w_dtype = weights.dtype
141+
input_grad = clamped_swiglu_back(g * weights, y, clamp_value)
142+
weights_grad = clamped_swiglu(y, clamp_value) * g.to(w_dtype)
143+
weights_grad = torch.sum(weights_grad, dim=-1, keepdim=True)
144+
return input_grad.to(input_dtype), weights_grad.to(w_dtype)
145+
146+
100147
class BiasSwiGLUFunction(torch.autograd.Function):
101148
"""Custom autograd function for SwiGLU activation with bias support."""
102149

@@ -190,20 +237,27 @@ def backward(ctx, grad_output):
190237

191238
class WeightedSwiGLUFunction(torch.autograd.Function):
192239
@staticmethod
193-
# bias is an optional argument
194-
def forward(ctx, input, weights, fp8_input_store):
240+
def forward(ctx, input, weights, fp8_input_store, clamp_value):
195241
input_for_backward = input.to(torch.float8_e4m3fn) if fp8_input_store else input
196242
ctx.save_for_backward(input_for_backward, weights)
197243
ctx.ori_input_dtype = input.dtype
198244
ctx.fp8_input_store = fp8_input_store
199-
return weighted_swiglu(input, weights)
245+
ctx.clamp_value = clamp_value
246+
if clamp_value is not None and clamp_value > 0:
247+
res = clamped_weighted_swiglu(input, weights, clamp_value)
248+
else:
249+
res = weighted_swiglu(input, weights)
250+
return res
200251

201252
@staticmethod
202253
def backward(ctx, grad_output):
203254
input, weights = ctx.saved_tensors
204255
input = input.to(ctx.ori_input_dtype) if ctx.fp8_input_store else input
205-
tmp, wgrad = weighted_swiglu_back(grad_output, input, weights)
206-
return tmp, wgrad, None
256+
if ctx.clamp_value is not None and ctx.clamp_value > 0:
257+
tmp, wgrad = clamped_weighted_swiglu_back(grad_output, input, weights, ctx.clamp_value)
258+
else:
259+
tmp, wgrad = weighted_swiglu_back(grad_output, input, weights)
260+
return tmp, wgrad, None, None
207261

208262

209263
def bias_swiglu_impl(input, bias, fp8_input_store=False, cpu_offload_input=False):
@@ -236,7 +290,7 @@ def bias_swiglu_impl(input, bias, fp8_input_store=False, cpu_offload_input=False
236290
return output if len(ori_shape) == 2 else output.view(ori_shape[0], ori_shape[1], -1)
237291

238292

239-
def weighted_bias_swiglu_impl(input, bias, weights, fp8_input_store=False):
293+
def weighted_bias_swiglu_impl(input, bias, weights, fp8_input_store=False, clamp_value=None):
240294
"""
241295
Token-wise-weighted bias swiglu fusion.
242296
"""
@@ -246,7 +300,7 @@ def weighted_bias_swiglu_impl(input, bias, weights, fp8_input_store=False):
246300
if bias is not None:
247301
raise NotImplementedError("Bias is not supported for weighted swiglu fusion")
248302
else:
249-
output = WeightedSwiGLUFunction.apply(input, weights, fp8_input_store)
303+
output = WeightedSwiGLUFunction.apply(input, weights, fp8_input_store, clamp_value)
250304

251305
return output if len(ori_shape) == 2 else output.view(ori_shape[0], ori_shape[1], -1)
252306

0 commit comments

Comments
 (0)