129 lines
4.5 KiB
Python
129 lines
4.5 KiB
Python
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
import torch
|
|
import torch.nn as nn
|
|
import torch.nn.functional as F
|
|
from transformers.models.qwen3_moe.modeling_qwen3_moe import Qwen3MoeMLP
|
|
|
|
from slime.backends.fsdp_utils.kernels.fused_experts import (
|
|
DownProjFunction,
|
|
GateUpProjFunction,
|
|
MoeSumReduceFunction,
|
|
SiluAndMulFunction,
|
|
)
|
|
|
|
|
|
def fused_experts_impl(
|
|
hidden_states: torch.Tensor,
|
|
w1: torch.Tensor,
|
|
w2: torch.Tensor,
|
|
topk_weights: torch.Tensor,
|
|
topk_ids: torch.Tensor,
|
|
):
|
|
assert hidden_states.shape[1] == w1.shape[2], "Hidden size mismatch"
|
|
assert topk_weights.shape == topk_ids.shape, "topk shape mismatch"
|
|
assert hidden_states.is_contiguous(), "Hidden_states must be contiguous"
|
|
assert w1.is_contiguous(), "Expert weights1 must be contiguous"
|
|
assert w2.is_contiguous(), "Expert weights2 must be contiguous"
|
|
assert hidden_states.dtype in [torch.bfloat16]
|
|
|
|
intermediate_cache1 = GateUpProjFunction.apply(
|
|
hidden_states,
|
|
w1,
|
|
topk_weights,
|
|
topk_ids,
|
|
)
|
|
intermediate_cache2 = SiluAndMulFunction.apply(intermediate_cache1)
|
|
intermediate_cache3 = DownProjFunction.apply(
|
|
intermediate_cache2,
|
|
w2,
|
|
topk_weights,
|
|
topk_ids,
|
|
)
|
|
output_hidden_states = MoeSumReduceFunction.apply(
|
|
intermediate_cache3,
|
|
hidden_states.shape,
|
|
)
|
|
return output_hidden_states
|
|
|
|
|
|
class StandardDispatcher:
|
|
def __init__(self, num_experts: int, num_local_experts: int):
|
|
self.moe_ep_size = 1
|
|
self.num_experts = num_experts
|
|
self.num_local_experts = num_local_experts
|
|
self.moe_ep_rank = 0
|
|
self.local_expert_mapping = None
|
|
|
|
if self.moe_ep_size > 1:
|
|
self.local_expert_mapping = torch.full((self.num_experts,), -1, dtype=torch.int32, device="cuda")
|
|
self.local_expert_mapping[
|
|
self.moe_ep_rank * self.num_local_experts : (self.moe_ep_rank + 1) * self.num_local_experts
|
|
] = torch.arange(0, self.num_local_experts, dtype=torch.int32, device="cuda")
|
|
|
|
def dispatch(self, topk_ids) -> torch.Tensor:
|
|
if self.local_expert_mapping is not None:
|
|
return self.local_expert_mapping[topk_ids]
|
|
return topk_ids
|
|
|
|
|
|
class Qwen3MoeSparseMoeBlock(nn.Module):
|
|
dispatcher = None
|
|
runner = None
|
|
|
|
def __init__(self, config):
|
|
super().__init__()
|
|
self.num_experts = config.num_experts
|
|
self.top_k = config.num_experts_per_tok
|
|
self.norm_topk_prob = config.norm_topk_prob
|
|
|
|
# gating
|
|
self.gate = nn.Linear(config.hidden_size, config.num_experts, bias=False)
|
|
|
|
self.experts = nn.ModuleList(
|
|
[Qwen3MoeMLP(config, intermediate_size=config.moe_intermediate_size) for _ in range(self.num_experts)]
|
|
)
|
|
|
|
if Qwen3MoeSparseMoeBlock.dispatcher is None:
|
|
Qwen3MoeSparseMoeBlock.dispatcher = StandardDispatcher(
|
|
num_experts=config.num_experts, num_local_experts=config.num_experts
|
|
)
|
|
|
|
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
|
|
batch_size, sequence_length, hidden_dim = hidden_states.shape
|
|
hidden_states = hidden_states.view(-1, hidden_dim)
|
|
# router_logits: (batch * sequence_length, n_experts)
|
|
router_logits = self.gate(hidden_states)
|
|
|
|
routing_weights = F.softmax(router_logits, dim=1, dtype=torch.float)
|
|
routing_weights, selected_experts = torch.topk(routing_weights, self.top_k, dim=-1)
|
|
|
|
if self.norm_topk_prob: # only diff with mixtral sparse moe block!
|
|
routing_weights /= routing_weights.sum(dim=-1, keepdim=True)
|
|
# we cast back to the input dtype
|
|
routing_weights = routing_weights.to(hidden_states.dtype)
|
|
|
|
selected_experts = Qwen3MoeSparseMoeBlock.dispatcher.dispatch(selected_experts)
|
|
|
|
w13_weight = torch.stack(
|
|
[torch.cat([layer.gate_proj.weight, layer.up_proj.weight], dim=0) for layer in self.experts]
|
|
)
|
|
w2_weight = torch.stack([layer.down_proj.weight for layer in self.experts], dim=0)
|
|
|
|
final_hidden_states = fused_experts_impl(
|
|
hidden_states.to(torch.bfloat16),
|
|
w13_weight,
|
|
w2_weight,
|
|
routing_weights,
|
|
selected_experts,
|
|
)
|
|
|
|
return final_hidden_states, router_logits
|
|
|
|
|
|
def apply_true_on_policy_patch_for_qwen3_moe():
|
|
from transformers.models.qwen3_moe import modeling_qwen3_moe
|
|
|
|
modeling_qwen3_moe.Qwen3MoeSparseMoeBlock = Qwen3MoeSparseMoeBlock
|