feat: Support nvidia/MiniMax-M3-NVFP4 (#31989)
This commit is contained in:
@@ -528,6 +528,11 @@ def _minimax_m3_overrides(server_args: Any, hf_config: Any) -> dict:
|
||||
page_resolved = 128
|
||||
if server_args.moe_runner_backend == "auto" and quant_resolved == "mxfp8":
|
||||
overrides["moe_runner_backend"] = "deep_gemm"
|
||||
elif (
|
||||
server_args.moe_runner_backend == "auto"
|
||||
and quant_resolved == "modelopt_mixed"
|
||||
):
|
||||
overrides["moe_runner_backend"] = "flashinfer_trtllm_routed"
|
||||
logger.info(
|
||||
"MiniMax-M3 on SM100: attention_backend="
|
||||
f"{overrides.get('attention_backend', server_args.attention_backend)}, page_size={page_resolved}, "
|
||||
|
||||
@@ -886,6 +886,8 @@ class FlashInferTrtllmFp4MoeQuantInfo(MoeQuantInfo):
|
||||
routing_method_type: int
|
||||
use_per_token_activation: bool = False
|
||||
|
||||
gemm1_alpha: Optional[torch.Tensor] = None
|
||||
gemm1_beta: Optional[torch.Tensor] = None
|
||||
gemm1_clamp_limit: Optional[torch.Tensor] = None
|
||||
|
||||
|
||||
@@ -1053,8 +1055,8 @@ def fused_experts_none_to_flashinfer_trtllm_fp4(
|
||||
gemm1_weights=quant_info.w13_weight,
|
||||
gemm1_weights_scale=quant_info.w13_weight_scale.view(torch.float8_e4m3fn),
|
||||
gemm1_bias=None,
|
||||
gemm1_alpha=None,
|
||||
gemm1_beta=None,
|
||||
gemm1_alpha=quant_info.gemm1_alpha,
|
||||
gemm1_beta=quant_info.gemm1_beta,
|
||||
gemm1_clamp_limit=quant_info.gemm1_clamp_limit,
|
||||
gemm2_weights=quant_info.w2_weight,
|
||||
gemm2_weights_scale=quant_info.w2_weight_scale.view(torch.float8_e4m3fn),
|
||||
@@ -1094,8 +1096,8 @@ def fused_experts_none_to_flashinfer_trtllm_fp4(
|
||||
gemm1_weights=quant_info.w13_weight,
|
||||
gemm1_weights_scale=quant_info.w13_weight_scale.view(torch.float8_e4m3fn),
|
||||
gemm1_bias=None,
|
||||
gemm1_alpha=None,
|
||||
gemm1_beta=None,
|
||||
gemm1_alpha=quant_info.gemm1_alpha,
|
||||
gemm1_beta=quant_info.gemm1_beta,
|
||||
gemm1_clamp_limit=quant_info.gemm1_clamp_limit,
|
||||
gemm2_weights=quant_info.w2_weight,
|
||||
gemm2_weights_scale=quant_info.w2_weight_scale.view(torch.float8_e4m3fn),
|
||||
|
||||
@@ -34,7 +34,7 @@ from sglang.srt.layers.quantization.fp4_utils import (
|
||||
fp4_quantize,
|
||||
get_fp4_gemm_runner_backend,
|
||||
)
|
||||
from sglang.srt.layers.quantization.fp8 import Fp8Config
|
||||
from sglang.srt.layers.quantization.fp8 import Fp8Config, Fp8LinearMethod, Fp8MoEMethod
|
||||
from sglang.srt.layers.quantization.fp8_utils import (
|
||||
apply_fp8_linear,
|
||||
apply_fp8_linear_bmm_flashinfer,
|
||||
@@ -634,10 +634,12 @@ class ModelOptMixedPrecisionConfig(ModelOptQuantConfig):
|
||||
fp8_config: ModelOptFp8Config,
|
||||
nvfp4_config: ModelOptFp4Config,
|
||||
nvfp4a16_config: ModelOptFp4Config,
|
||||
mxfp8_config: Fp8Config,
|
||||
) -> None:
|
||||
super().__init__(kv_cache_quant_algo, exclude_modules, packed_modules_mapping)
|
||||
self.quantized_layers = quantized_layers
|
||||
self.fp8_config = fp8_config
|
||||
self.mxfp8_config = mxfp8_config
|
||||
self.nvfp4_config = nvfp4_config
|
||||
self.nvfp4a16_config = nvfp4a16_config
|
||||
|
||||
@@ -685,7 +687,7 @@ class ModelOptMixedPrecisionConfig(ModelOptQuantConfig):
|
||||
kv_cache_quant_algo = "auto"
|
||||
else:
|
||||
kv_cache_quant_algo = config.get("kv_cache_quant_algo")
|
||||
exclude_modules = config.get("ignore")
|
||||
exclude_modules = config.get("ignore", config.get("exclude_modules"))
|
||||
quantized_layers = config.get("quantized_layers", {})
|
||||
else:
|
||||
quantization_section = cls.get_from_keys(config, ["quantization"])
|
||||
@@ -721,6 +723,13 @@ class ModelOptMixedPrecisionConfig(ModelOptQuantConfig):
|
||||
exclude_modules=[],
|
||||
packed_modules_mapping=packed_modules_mapping,
|
||||
)
|
||||
mxfp8_config = Fp8Config(
|
||||
is_checkpoint_fp8_serialized=True,
|
||||
activation_scheme="dynamic",
|
||||
weight_block_size=[1, 32],
|
||||
packed_modules_mapping=packed_modules_mapping,
|
||||
use_mxfp8=True,
|
||||
)
|
||||
nvfp4_config = ModelOptFp4Config(
|
||||
is_checkpoint_nvfp4_serialized=True,
|
||||
kv_cache_quant_algo=kv_cache_quant_algo,
|
||||
@@ -743,6 +752,7 @@ class ModelOptMixedPrecisionConfig(ModelOptQuantConfig):
|
||||
packed_modules_mapping=packed_modules_mapping,
|
||||
quantized_layers=quantized_layers,
|
||||
fp8_config=fp8_config,
|
||||
mxfp8_config=mxfp8_config,
|
||||
nvfp4_config=nvfp4_config,
|
||||
nvfp4a16_config=nvfp4a16_config,
|
||||
)
|
||||
@@ -820,6 +830,8 @@ class ModelOptMixedPrecisionConfig(ModelOptQuantConfig):
|
||||
return UnquantizedLinearMethod()
|
||||
if quant_algo == "FP8":
|
||||
return ModelOptFp8LinearMethod(self.fp8_config)
|
||||
if quant_algo == "MXFP8":
|
||||
return Fp8LinearMethod(self.mxfp8_config)
|
||||
if quant_algo == "NVFP4":
|
||||
return ModelOptFp4LinearMethod(self.nvfp4_config)
|
||||
if quant_algo == "W4A16_NVFP4":
|
||||
@@ -834,6 +846,8 @@ class ModelOptMixedPrecisionConfig(ModelOptQuantConfig):
|
||||
return None
|
||||
if quant_algo == "FP8":
|
||||
return ModelOptFp8MoEMethod(self.fp8_config)
|
||||
if quant_algo == "MXFP8":
|
||||
return Fp8MoEMethod(self.mxfp8_config)
|
||||
if quant_algo == "NVFP4":
|
||||
return ModelOptNvFp4FusedMoEMethod(self.nvfp4_config)
|
||||
if quant_algo == "W4A16_NVFP4":
|
||||
@@ -2282,17 +2296,33 @@ class ModelOptNvFp4FusedMoEMethod(FusedMoEMethodBase):
|
||||
(1 / w2_input_scale).to(torch.float32),
|
||||
)
|
||||
|
||||
swiglu_limit = layer.moe_runner_config.swiglu_limit
|
||||
if (
|
||||
swiglu_limit is not None
|
||||
and layer.moe_runner_config.is_gated
|
||||
and self.enable_flashinfer_trtllm_moe
|
||||
):
|
||||
copy_or_rebind_param(
|
||||
layer,
|
||||
"gemm1_clamp_limit",
|
||||
(swiglu_limit / layer.g1_alphas).to(torch.float32),
|
||||
if layer.moe_runner_config.is_gated and self.enable_flashinfer_trtllm_moe:
|
||||
gemm1_clamp_limit = (
|
||||
layer.moe_runner_config.gemm1_clamp_limit
|
||||
or layer.moe_runner_config.swiglu_limit
|
||||
)
|
||||
if gemm1_clamp_limit is not None:
|
||||
copy_or_rebind_param(
|
||||
layer,
|
||||
"gemm1_clamp_limit",
|
||||
(gemm1_clamp_limit / layer.g1_alphas).to(torch.float32),
|
||||
)
|
||||
|
||||
if layer.moe_runner_config.gemm1_alpha is not None:
|
||||
copy_or_rebind_param(
|
||||
layer,
|
||||
"gemm1_alpha",
|
||||
torch.full_like(
|
||||
layer.g1_alphas,
|
||||
layer.moe_runner_config.gemm1_alpha,
|
||||
dtype=torch.float32,
|
||||
),
|
||||
)
|
||||
copy_or_rebind_param(
|
||||
layer,
|
||||
"gemm1_beta",
|
||||
(1.0 / layer.g1_alphas).to(torch.float32),
|
||||
)
|
||||
|
||||
# TODO: for flashinfer always do MOE_NVFP4_DISPATCH
|
||||
layer.dispatcher.set_quant_config(
|
||||
@@ -2570,6 +2600,8 @@ class ModelOptNvFp4FusedMoEMethod(FusedMoEMethodBase):
|
||||
)
|
||||
|
||||
gemm1_clamp = getattr(layer, "gemm1_clamp_limit", None)
|
||||
gemm1_alpha = getattr(layer, "gemm1_alpha", None)
|
||||
gemm1_beta = getattr(layer, "gemm1_beta", None)
|
||||
quant_info = FlashInferTrtllmFp4MoeQuantInfo(
|
||||
w13_weight=layer.w13_weight.data,
|
||||
w2_weight=layer.w2_weight.data,
|
||||
@@ -2585,6 +2617,8 @@ class ModelOptNvFp4FusedMoEMethod(FusedMoEMethodBase):
|
||||
intermediate_size_per_partition=layer.intermediate_size_per_partition,
|
||||
routing_method_type=routing_method_type,
|
||||
use_per_token_activation=self.quant_config.use_per_token_activation,
|
||||
gemm1_alpha=gemm1_alpha.data if gemm1_alpha is not None else None,
|
||||
gemm1_beta=gemm1_beta.data if gemm1_beta is not None else None,
|
||||
gemm1_clamp_limit=gemm1_clamp.data if gemm1_clamp is not None else None,
|
||||
)
|
||||
|
||||
|
||||
@@ -79,6 +79,7 @@ from sglang.srt.model_loader.weight_utils import (
|
||||
maybe_remap_kv_scale_name,
|
||||
)
|
||||
from sglang.srt.models.minimax_m2 import MiniMaxM2RMSNormTP
|
||||
from sglang.srt.models.utils import WeightsMapper
|
||||
from sglang.srt.runtime_context import get_parallel, get_server_args
|
||||
from sglang.srt.utils import (
|
||||
add_prefix,
|
||||
@@ -1418,6 +1419,15 @@ class MiniMaxM3Model(nn.Module):
|
||||
|
||||
|
||||
class MiniMaxM3SparseForCausalLM(nn.Module):
|
||||
hf_to_sglang_mapper = WeightsMapper(
|
||||
orig_to_new_substr={".block_sparse_moe.": ".mlp."}
|
||||
)
|
||||
packed_modules_mapping = {
|
||||
"qkv_proj": ["q_proj", "k_proj", "v_proj"],
|
||||
"index_qkv_proj": ["index_q_proj", "index_k_proj", "index_v_proj"],
|
||||
"gate_up_proj": ["gate_proj", "up_proj"],
|
||||
}
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config: PretrainedConfig,
|
||||
@@ -1462,6 +1472,14 @@ class MiniMaxM3SparseForCausalLM(nn.Module):
|
||||
disable_reason = None
|
||||
if not getattr(self.config, "n_shared_experts", None):
|
||||
disable_reason = "No shared experts are defined in the config."
|
||||
elif (
|
||||
self.quant_config is not None
|
||||
and self.quant_config.get_name() == "modelopt_mixed"
|
||||
):
|
||||
disable_reason = (
|
||||
"Shared and routed experts may use different quantization formats "
|
||||
"in ModelOpt mixed-precision checkpoints."
|
||||
)
|
||||
elif not _is_cuda:
|
||||
disable_reason = "Shared experts fusion currently requires CUDA devices."
|
||||
elif _is_cuda and (_device_sm is not None) and (_device_sm < 80):
|
||||
|
||||
@@ -42,6 +42,7 @@ from sglang.srt.models.minimax_vl_common import (
|
||||
load_vision_weight,
|
||||
merge_vit_qkv_weights,
|
||||
)
|
||||
from sglang.srt.models.utils import WeightsMapper
|
||||
from sglang.srt.runtime_context import get_parallel, get_server_args
|
||||
from sglang.srt.utils import add_prefix, get_device_sm, is_cuda, log_info_on_rank0
|
||||
from sglang.srt.utils.hf_transformers_utils import get_rope_config
|
||||
@@ -54,6 +55,15 @@ _device_sm = get_device_sm()
|
||||
|
||||
|
||||
class MiniMaxM3SparseForConditionalGeneration(nn.Module):
|
||||
hf_to_sglang_mapper = WeightsMapper(
|
||||
orig_to_new_substr={".block_sparse_moe.": ".mlp."}
|
||||
)
|
||||
packed_modules_mapping = {
|
||||
"qkv_proj": ["q_proj", "k_proj", "v_proj"],
|
||||
"index_qkv_proj": ["index_q_proj", "index_k_proj", "index_v_proj"],
|
||||
"gate_up_proj": ["gate_proj", "up_proj"],
|
||||
}
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config,
|
||||
@@ -131,6 +141,14 @@ class MiniMaxM3SparseForConditionalGeneration(nn.Module):
|
||||
disable_reason = None
|
||||
if not getattr(text_config, "n_shared_experts", None):
|
||||
disable_reason = "No shared experts are defined in the config."
|
||||
elif (
|
||||
self.quant_config is not None
|
||||
and self.quant_config.get_name() == "modelopt_mixed"
|
||||
):
|
||||
disable_reason = (
|
||||
"Shared and routed experts may use different quantization formats "
|
||||
"in ModelOpt mixed-precision checkpoints."
|
||||
)
|
||||
elif not _is_cuda:
|
||||
disable_reason = "Shared experts fusion currently requires CUDA devices."
|
||||
elif (_device_sm is not None) and (_device_sm < 80):
|
||||
|
||||
Reference in New Issue
Block a user