Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
98 changes: 98 additions & 0 deletions megatron/core/ssm/gated_delta_net/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,8 +261,11 @@ def __init__(
)
self.recompute_norm_out = False
self.norm_out_checkpoint = None
self.recompute_in_proj_conv = False
# recompute_gated_delta_rule may need a checkpoint instance (same as norm_out) later
if self.config.recompute_granularity == "selective":
self.recompute_norm_out = "gdn_norm_out" in self.config.recompute_modules
self.recompute_in_proj_conv = "gdn_in_proj_conv" in self.config.recompute_modules

self.out_proj = build_module(
submodules.out_proj,
Expand Down Expand Up @@ -366,6 +369,101 @@ def _gated_norm_and_a2a(

return norm_out

def _in_proj_conv(
self, hidden_states, *, thd_cp_a2a_idx, batch, seq_len, cu_seqlens_q, packed_seq_params
):
"""inproj + all2all + split view + conv1d"""
# Input projection
nvtx_range_push(suffix="in_proj")
qkvzba, _ = self.in_proj(hidden_states)
nvtx_range_pop(suffix="in_proj")

# CP All to All: CP to HP
if self.cp_size > 1:
# # Pre-permute head dim so a single unsectioned a2a is equivalent to per-section a2a.
head_perm = _build_head_perm_for_split_sections(
self.in_proj_split_sections,
self.pg_collection.cp.size(),
torch.cuda.current_device(),
)
qkvzba = qkvzba.index_select(-1, head_perm)

if packed_seq_params is not None and packed_seq_params.qkv_format == 'thd':
qkvzba = tensor_a2a_cp2hp(
qkvzba,
seq_dim=0,
head_dim=-1,
cp_group=self.pg_collection.cp,
undo_attention_load_balancing=False,
)
if self.cp_size > 1:
qkvzba = qkvzba.index_select(0, thd_cp_a2a_idx)
else:
qkvzba = tensor_a2a_cp2hp(
qkvzba, seq_dim=0, head_dim=-1, cp_group=self.pg_collection.cp
)

# Transpose: s b x --> b s x
# From sbhd to bshd format
qkvzba = qkvzba.transpose(0, 1)

# Split the tensor into q, k, v, gate (z), and the variant-specific gate features
# (beta, alpha for GDN; f, b, w for GDN2)
qkv, gate, beta, alpha = torch.split(qkvzba, self.feat_dim_split, dim=-1)
gate = gate.reshape(batch, seq_len, -1, self.value_head_dim)

# Convolution on qkv
nvtx_range_push(suffix="conv1d")
seq_len = qkv.shape[1]
qkv_channels_split_sections = [
self.qk_dim_local_tp,
self.qk_dim_local_tp,
self.v_dim_local_tp,
]
conv1d_weight = get_parameter_local_cp(
self.conv1d.weight,
dim=0,
cp_group=self.pg_collection.cp,
split_sections=qkv_channels_split_sections,
)
conv1d_bias = (
get_parameter_local_cp(
self.conv1d.bias,
dim=0,
cp_group=self.pg_collection.cp,
split_sections=qkv_channels_split_sections,
)
if self.conv_bias
else None
)
if self.config.deterministic_mode:
qkv = qkv.transpose(1, 2).contiguous() # b, s, d -> b, d, s
conv_out = F.conv1d(
input=qkv, # Torch-native only accept [b, d, s] format input
weight=conv1d_weight,
bias=conv1d_bias,
stride=self.conv1d.stride,
padding=self.conv1d.padding,
dilation=self.conv1d.dilation,
groups=self.conv_dim_local_tp // self.cp_size,
)
qkv = self.act_fn(conv_out[..., :seq_len])
qkv = qkv.transpose(1, 2) # b, d, s -> b, s, d
else:
assert self.activation in ["silu", "swish"]
qkv, _ = causal_conv1d(
x=qkv, # FLA conv1d accepts [b, s, d] format input
weight=conv1d_weight.squeeze(1), # d, 1, w -> d, w
bias=conv1d_bias,
activation=self.activation,
initial_state=None,
output_final_state=False,
cu_seqlens=cu_seqlens_q,
)
nvtx_range_pop(suffix="conv1d")

return qkv, gate, beta, alpha

@jit_fuser
def _apply_gated_norm(self, x, gate):
# Output Norm
Expand Down
98 changes: 12 additions & 86 deletions megatron/core/ssm/gated_delta_net/gdn.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,15 @@
from typing import Optional

import torch
import torch.nn.functional as F

from megatron.core import tensor_parallel
from megatron.core.inference.contexts import BaseInferenceContext
from megatron.core.packed_seq_params import PackedSeqParams
from megatron.core.ssm.gated_delta_net.common import (
_build_head_perm_for_split_sections,
_build_thd_cp_a2a_perm,
_GDNBase,
causal_conv1d,
chunk_gated_delta_rule,
get_parameter_local_cp,
tensor_a2a_cp2hp,
torch_chunk_gated_delta_rule,
)
from megatron.core.utils import deprecate_inference_params, nvtx_range_pop, nvtx_range_push
Expand Down Expand Up @@ -126,101 +122,31 @@ def forward(
cu_seqlens_q = None
cu_seqlens_kv = None

# Input projection
nvtx_range_push(suffix="in_proj")
qkvzba, _ = self.in_proj(hidden_states)
nvtx_range_pop(suffix="in_proj")

# CP All to All: CP to HP
if self.cp_size > 1:
# # Pre-permute head dim so a single unsectioned a2a is equivalent to per-section a2a.
head_perm = _build_head_perm_for_split_sections(
self.in_proj_split_sections,
self.pg_collection.cp.size(),
torch.cuda.current_device(),
)
qkvzba = qkvzba.index_select(-1, head_perm)

thd_cp_a2a_idx, thd_cp_a2a_inv = None, None
if packed_seq_params is not None and packed_seq_params.qkv_format == 'thd':
qkvzba = tensor_a2a_cp2hp(
qkvzba,
seq_dim=0,
head_dim=-1,
cp_group=self.pg_collection.cp,
undo_attention_load_balancing=False,
)
if self.cp_size > 1:
# Permute at the seq dim so that a single unsectioned a2a
# is equivalent to per-sequence a2a.
# This also folds the ``_undo_attention_load_balancing`` step.
thd_cp_a2a_idx, thd_cp_a2a_inv = _build_thd_cp_a2a_perm(
cu_seqlens_q, self.cp_size, seq_len
)
qkvzba = qkvzba.index_select(0, thd_cp_a2a_idx)
else:
qkvzba = tensor_a2a_cp2hp(
qkvzba, seq_dim=0, head_dim=-1, cp_group=self.pg_collection.cp
)

# Transpose: s b x --> b s x
# From sbhd to bshd format
qkvzba = qkvzba.transpose(0, 1)

# Split the tensor into q, k, v, gate (z), and the variant-specific gate features
# (beta, alpha for GDN; f, b, w for GDN2)
qkv, gate, beta, alpha = torch.split(qkvzba, self.feat_dim_split, dim=-1)
gate = gate.reshape(batch, seq_len, -1, self.value_head_dim)

# Convolution on qkv
nvtx_range_push(suffix="conv1d")
seq_len = qkv.shape[1]
qkv_channels_split_sections = [
self.qk_dim_local_tp,
self.qk_dim_local_tp,
self.v_dim_local_tp,
]
conv1d_weight = get_parameter_local_cp(
self.conv1d.weight,
dim=0,
cp_group=self.pg_collection.cp,
split_sections=qkv_channels_split_sections,
)
conv1d_bias = (
get_parameter_local_cp(
self.conv1d.bias,
dim=0,
cp_group=self.pg_collection.cp,
split_sections=qkv_channels_split_sections,
)
if self.conv_bias
else None
in_proj_conv_func = partial(
self._in_proj_conv,
thd_cp_a2a_idx=thd_cp_a2a_idx,
batch=batch,
seq_len=seq_len,
cu_seqlens_q=cu_seqlens_q,
packed_seq_params=packed_seq_params,
)
if self.config.deterministic_mode:
qkv = qkv.transpose(1, 2).contiguous() # b, s, d -> b, d, s
conv_out = F.conv1d(
input=qkv, # Torch-native only accept [b, d, s] format input
weight=conv1d_weight,
bias=conv1d_bias,
stride=self.conv1d.stride,
padding=self.conv1d.padding,
dilation=self.conv1d.dilation,
groups=self.conv_dim_local_tp // self.cp_size,

if self.recompute_in_proj_conv:
qkv, gate, beta, alpha = tensor_parallel.checkpoint(
in_proj_conv_func, False, hidden_states
)
qkv = self.act_fn(conv_out[..., :seq_len])
qkv = qkv.transpose(1, 2) # b, d, s -> b, s, d
else:
assert self.activation in ["silu", "swish"]
qkv, _ = causal_conv1d(
x=qkv, # FLA conv1d accepts [b, s, d] format input
weight=conv1d_weight.squeeze(1), # d, 1, w -> d, w
bias=conv1d_bias,
activation=self.activation,
initial_state=None,
output_final_state=False,
cu_seqlens=cu_seqlens_q,
)
nvtx_range_pop(suffix="conv1d")
qkv, gate, beta, alpha = in_proj_conv_func(hidden_states)

A_log_local_cp = get_parameter_local_cp(self.A_log, dim=0, cp_group=self.pg_collection.cp)
dt_bias_local_cp = get_parameter_local_cp(
Expand Down
12 changes: 12 additions & 0 deletions megatron/core/transformer/transformer_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,8 @@ class TransformerConfig(ModelParallelConfig):
"moe": recompute the MoE layer.
"shared_experts": recompute the shared experts in the MoE layer.
"gdn_norm_out": recompute the GatedDeltaNet output norm and HP-to-CP all-to-all.
"gdn_in_proj_conv": recompute the GatedDeltaNet input projection, HP-to-CP all-to-all, split,
and convolution.
"moe_act", "layernorm", "mla_up_proj", and "gdn_norm_out" use output-discarding checkpointing,
"core_attn", "mlp", "moe", and "shared_experts" use normal checkpointing.
"""
Expand Down Expand Up @@ -1800,6 +1802,7 @@ def __post_init__(self):
"moe",
"shared_experts",
"gdn_norm_out",
"gdn_in_proj_conv",
}
invalid_modules = set(self.recompute_modules) - allowed_modules
assert not invalid_modules, (
Expand Down Expand Up @@ -1827,6 +1830,15 @@ def __post_init__(self):
"experimental_attention_variant='gated_delta_net'."
)

if (
"gdn_in_proj_conv" in self.recompute_modules
and self.experimental_attention_variant != "gated_delta_net"
):
raise ValueError(
"gdn_in_proj_conv in recompute_modules is only supported with "
"experimental_attention_variant='gated_delta_net'."
)

if "core_attn" in self.recompute_modules:
warnings.warn(
"If you are using transformer_engine as the transformer implementation, "
Expand Down
Loading