chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
"""
|
||||
This file specifies how MLC's Mixtral parameter maps from other formats, for example HuggingFace
|
||||
PyTorch, HuggingFace safetensors.
|
||||
"""
|
||||
|
||||
import functools
|
||||
|
||||
import numpy as np
|
||||
|
||||
from mlc_llm.loader import ExternMapping
|
||||
from mlc_llm.quantization import Quantization
|
||||
|
||||
from .mixtral_model import MixtralConfig, MixtralForCausalLM
|
||||
|
||||
|
||||
def huggingface(model_config: MixtralConfig, quantization: Quantization) -> ExternMapping:
|
||||
"""Returns a parameter mapping that maps from the names of MLC LLM parameters to
|
||||
the names of HuggingFace PyTorch parameters.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
model_config : MixtralConfig
|
||||
The configuration of the Mixtral model.
|
||||
|
||||
quantization : Quantization
|
||||
The quantization configuration.
|
||||
|
||||
Returns
|
||||
-------
|
||||
param_map : ExternMapping
|
||||
The parameter mapping from MLC to HuggingFace PyTorch.
|
||||
"""
|
||||
model = MixtralForCausalLM(model_config)
|
||||
if quantization is not None:
|
||||
model.to(quantization.model_dtype)
|
||||
_, _named_params, _ = model.export_tvm(
|
||||
spec=model.get_default_spec(),
|
||||
allow_extern=True,
|
||||
)
|
||||
named_parameters = dict(_named_params)
|
||||
|
||||
mapping = ExternMapping()
|
||||
|
||||
for i in range(model_config.num_hidden_layers):
|
||||
# Add QKV in self attention
|
||||
attn = f"model.layers.{i}.self_attn"
|
||||
mlc_name = f"{attn}.qkv_proj.weight"
|
||||
mlc_param = named_parameters[mlc_name]
|
||||
mapping.add_mapping(
|
||||
mlc_name,
|
||||
[
|
||||
f"{attn}.q_proj.weight",
|
||||
f"{attn}.k_proj.weight",
|
||||
f"{attn}.v_proj.weight",
|
||||
],
|
||||
functools.partial(
|
||||
lambda q, k, v, dtype: np.concatenate([q, k, v], axis=0).astype(dtype),
|
||||
dtype=mlc_param.dtype,
|
||||
),
|
||||
)
|
||||
|
||||
# Add gates in MLP (when MoE is enabled)
|
||||
mlp = f"model.layers.{i}.block_sparse_moe"
|
||||
mlc_mlp = f"model.layers.{i}.moe"
|
||||
mlc_name = f"{mlc_mlp}.e1_e3.weight"
|
||||
mlc_param = named_parameters[mlc_name]
|
||||
|
||||
def combine_expert_gate_up(*hf_params, dtype):
|
||||
stack = []
|
||||
for i in range(0, len(hf_params), 2):
|
||||
stack.append(np.concatenate([hf_params[i], hf_params[i + 1]], axis=0))
|
||||
return np.stack(stack, axis=0).astype(dtype)
|
||||
|
||||
mapping.add_mapping(
|
||||
mlc_name,
|
||||
functools.reduce(
|
||||
lambda a, b: a + b,
|
||||
[
|
||||
[
|
||||
f"{mlp}.experts.{expert_id}.w1.weight",
|
||||
f"{mlp}.experts.{expert_id}.w3.weight",
|
||||
]
|
||||
for expert_id in range(model_config.num_local_experts)
|
||||
],
|
||||
),
|
||||
functools.partial(
|
||||
combine_expert_gate_up,
|
||||
dtype=mlc_param.dtype,
|
||||
),
|
||||
)
|
||||
|
||||
mlc_name = f"{mlc_mlp}.e2.weight"
|
||||
mlc_param = named_parameters[mlc_name]
|
||||
mapping.add_mapping(
|
||||
mlc_name,
|
||||
[
|
||||
f"{mlp}.experts.{expert_id}.w2.weight"
|
||||
for expert_id in range(model_config.num_local_experts)
|
||||
],
|
||||
functools.partial(
|
||||
lambda *hf_params, dtype: np.stack(hf_params, axis=0).astype(dtype),
|
||||
dtype=mlc_param.dtype,
|
||||
),
|
||||
)
|
||||
|
||||
mlc_name = f"{mlc_mlp}.gate.weight"
|
||||
mlc_param = named_parameters[mlc_name]
|
||||
mapping.add_mapping(
|
||||
mlc_name,
|
||||
[f"{mlp}.gate.weight"],
|
||||
functools.partial(
|
||||
lambda x, dtype: x.astype(dtype),
|
||||
dtype=mlc_param.dtype,
|
||||
),
|
||||
)
|
||||
|
||||
# inv_freq is not used in the model
|
||||
mapping.add_unused(f"{attn}.rotary_emb.inv_freq")
|
||||
|
||||
for mlc_name, mlc_param in named_parameters.items():
|
||||
if mlc_name not in mapping.param_map:
|
||||
mapping.add_mapping(
|
||||
mlc_name,
|
||||
[mlc_name],
|
||||
functools.partial(
|
||||
lambda x, dtype: x.astype(dtype),
|
||||
dtype=mlc_param.dtype,
|
||||
),
|
||||
)
|
||||
return mapping
|
||||
@@ -0,0 +1,186 @@
|
||||
"""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)
|
||||
Reference in New Issue
Block a user