Files
wehub-resource-sync 770d92cb1f
Lint / lint (push) Has been cancelled
Build Docs / Deploy Docs (push) Has been cancelled
Windows CI / Windows (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:23:58 +08:00

187 lines
7.2 KiB
Python

"""Implementation for Mistral architecture."""
import dataclasses
from tvm import tirx
from tvm.relax.frontend import nn
from tvm.relax.frontend.nn import Tensor, op
from mlc_llm import op as op_ext
from mlc_llm.model.llama.llama_model import (
LlamaAttention,
LlamaConfig,
LlamaForCausalLM,
LlamaModel,
)
from mlc_llm.nn import PagedKVCache
from mlc_llm.nn.expert import MixtralExperts
from mlc_llm.support import logging
from mlc_llm.support import tensor_parallel as tp
logger = logging.getLogger(__name__)
@dataclasses.dataclass
class MixtralConfig(LlamaConfig):
"""Configuration of the Mixtral model."""
num_local_experts: int = 0
num_experts_per_tok: int = 0
class MixtralMoE(nn.Module):
"""Mixture of experts"""
def __init__(self, config: MixtralConfig):
super().__init__()
self.num_experts_per_tok = config.num_experts_per_tok
self.num_local_experts = config.num_local_experts
if config.intermediate_size % config.tensor_parallel_shards != 0:
raise ValueError(
f"Cannot split MoE intermediate size {config.intermediate_size} "
f"evenly to {config.tensor_parallel_shards} GPUs."
)
self.intermediate_size = config.intermediate_size // config.tensor_parallel_shards
self.gate = nn.Linear(
in_features=config.hidden_size,
out_features=config.num_local_experts,
bias=False,
)
self.e1_e3 = MixtralExperts(
self.num_local_experts,
in_features=config.hidden_size,
out_features=2 * self.intermediate_size,
tensor_parallel_shards=config.tensor_parallel_shards,
)
self.e2 = MixtralExperts(
self.num_local_experts,
in_features=self.intermediate_size,
out_features=config.hidden_size,
tensor_parallel_shards=config.tensor_parallel_shards,
)
self.dtype = "float32"
def forward(self, x: Tensor):
def _expert_forward(x: Tensor, indptr: Tensor):
x1_x3 = self.e1_e3(x, indptr)
x1, x3 = op.split(x1_x3, indices_or_sections=2, axis=-1)
x = self.e2(op.silu(x1) * x3, indptr)
return x
experts_per_tok = self.num_experts_per_tok # activated experts per token
local_experts = self.num_local_experts # total number of experts
batch_size, seq_len, hidden_size = x.shape
num_tokens = batch_size * seq_len
x = x.reshape(num_tokens, hidden_size)
# gate: [num_tokens, local_experts]
gate: Tensor = self.gate(x)
# expert_weights: [num_tokens, experts_per_tok]
# expert_indices: [num_tokens, experts_per_tok]
expert_weights, expert_indices = op_ext.moe_misc.gating_softmax_topk(gate, experts_per_tok)
use_ft = (
op_ext.get_store().cutlass_group_gemm or op_ext.get_store().faster_transformer
) and self.dtype == "float16"
if num_tokens == 1:
# x: [num_tokens * experts_per_tok, hidden_size]
x = _expert_forward(x, expert_indices)
else:
# cumsum: [num_tokens * local_experts]
cumsum = op_ext.moe_misc.moe_cumsum(expert_indices, local_experts)
# indices: [num_tokens * experts_per_tok]
reverse_indices, token_indices = op_ext.moe_misc.get_indices(cumsum, expert_indices)
if use_ft:
# indptr: [num_local_experts]
indptr = op_ext.moe_misc.get_indptr(
cumsum, local_experts, num_tokens, inclusive=True, out_dtype="int64"
)
else:
# indptr: [num_local_experts + 1]
indptr = op_ext.moe_misc.get_indptr(
cumsum,
local_experts,
num_tokens,
inclusive=False,
out_dtype="int32",
)
# x: [num_tokens * experts_per_tok, hidden_size]
x = op.take(x, token_indices, axis=0)
x = _expert_forward(x, indptr)
x = op_ext.moe_misc.scatter_output(x, reverse_indices)
# x: [num_tokens, experts_per_tok, hidden_size]
x = x.reshape(num_tokens, experts_per_tok, hidden_size) * expert_weights.reshape(
num_tokens, experts_per_tok, 1
)
# x: [num_tokens, hidden_size]
x = op_ext.moe_misc.moe_sum(x, dim=1)
x = x.reshape(batch_size, seq_len, hidden_size)
return x
class MixtralDecoderLayer(nn.Module):
"""Mixtral decoder layer"""
def __init__(self, config: MixtralConfig):
eps = config.rms_norm_eps
self.self_attn = LlamaAttention(config)
self.moe = MixtralMoE(config)
self.input_layernorm = nn.RMSNorm(config.hidden_size, -1, eps, bias=False)
self.post_attention_layernorm = nn.RMSNorm(config.hidden_size, -1, eps, bias=False)
def _set_tp():
def _set(layer, hint):
layer.weight.attrs["shard_strategy"] = hint
hd = config.head_dim
q = self.self_attn.num_q_heads * hd
k = self.self_attn.num_kv_heads * hd
v = self.self_attn.num_kv_heads * hd
i = self.moe.intermediate_size
_set(
self.self_attn.qkv_proj,
tp.ShardSingleDim("_shard_qkv", segs=[q, k, v], dim=0),
)
_set(self.self_attn.o_proj, tp.ShardSingleDim("_shard_o", dim=1))
_set(self.moe.e1_e3, tp.ShardSingleDim("_shard_mlp_up", segs=[i, i], dim=1))
_set(self.moe.e2, tp.ShardSingleDim("_shard_mlp_down", dim=2))
self.tensor_parallel_shards = config.tensor_parallel_shards
_set_tp()
def forward(self, hidden_states: Tensor, attention_mask: Tensor, total_seq_len: tirx.Var):
"""Forward pass of a decoder layer; calculate attention, and add an residual connection."""
out = self.self_attn(self.input_layernorm(hidden_states), attention_mask, total_seq_len)
hidden_states = self._apply_residual(out, residual=hidden_states)
out = self.moe(self.post_attention_layernorm(hidden_states))
hidden_states = self._apply_residual(out, residual=hidden_states)
return hidden_states
def batch_forward(self, hidden_states: Tensor, paged_kv_cache: PagedKVCache, layer_id: int):
out = self.self_attn(self.input_layernorm(hidden_states), paged_kv_cache, layer_id)
hidden_states = self._apply_residual(out, residual=hidden_states)
out = self.moe(self.post_attention_layernorm(hidden_states))
hidden_states = self._apply_residual(out, residual=hidden_states)
return hidden_states
def _apply_residual(self, out, residual):
if self.tensor_parallel_shards > 1:
return op.ccl_allreduce(out, "sum") + residual
return out + residual
class MixtralModel(LlamaModel):
"""Exact same as LlamaModel."""
def __init__(self, config: MixtralConfig):
super().__init__(config)
self.layers = nn.ModuleList(
[MixtralDecoderLayer(config) for _ in range(config.num_hidden_layers)]
)
class MixtralForCausalLM(LlamaForCausalLM):
"""Same as LlamaForCausalLM."""
def __init__(self, config: MixtralConfig):
super().__init__(config)
self.model = MixtralModel(config)