A high-throughput and memory-efficient inference and serving engine for LLMs
Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git
Synced Nov 4, 08:45 UTC
[Quantization] add humming mxfp4 moe backend (#41083)
Signed-off-by: Jinzhen Lin <jinzhen.ljz@antgroup.com>
Jinzhen Lin committed
May 3, 2026 at 09:36 UTC
08834cc3ceb86d77231666a5e07847f741637355
9 files changed
+384
-157
vllm/config/kernel.py
+2
@@ -118,6 +118,7 @@ MoEBackend = Literal[
118
"flashinfer_cutlass",
119
"flashinfer_cutedsl",
120
"marlin",
121
+ "humming",
122
"aiter",
123
"emulation",
124
]
@@ -148,6 +149,7 @@ class KernelConfig:
149
- "flashinfer_cutlass": Use FlashInfer with CUTLASS kernels
150
- "flashinfer_cutedsl": Use FlashInfer with CuteDSL kernels (FP4 only)
151
- "marlin": Use Marlin kernels (weight-only quantization)
152
+ - "humming": Use Humming Mixed Precision kernels
153
- "aiter": Use AMD AITer kernels (ROCm only)
154
- "emulation": use BF16/FP16 GEMM, dequantizing weights and
155
running QDQ on activations.
vllm/envs.py
+2
-2
@@ -1231,8 +1231,8 @@ environment_variables: dict[str, Callable[[], Any]] = {
1231
# if 1, force use indexed gemm
1232
# if 0, force use grouped gemm
1233
# if None, choose better gemm type automatically
1234
- "VLLM_HUMMING_MOE_GEMM_TYPE": lambda: maybe_convert_bool(
1235
- os.environ.get("VLLM_HUMMING_MOE_GEMM_TYPE", None)
1234
+ "VLLM_HUMMING_MOE_GEMM_TYPE": lambda: os.environ.get(
1235
+ "VLLM_HUMMING_MOE_GEMM_TYPE", None
1236
),
1237
# Whether to use DeepEPLL kernels for NVFP4 quantization and dispatch method
1238
# only supported on Blackwell GPUs and with
vllm/model_executor/layers/fused_moe/fused_humming_moe.py
+70
-49
@@ -4,7 +4,7 @@
4
5
import json
6
import math
7
-from typing import TYPE_CHECKING, Any
7
+from typing import Any
8
9
import torch
10
from humming import dtypes
@@ -16,7 +16,11 @@ from vllm import envs
16
from vllm.forward_context import get_forward_context
17
from vllm.logger import init_logger
18
from vllm.model_executor.layers.fused_moe.activation import MoEActivation
19
-from vllm.model_executor.layers.fused_moe.config import FusedMoEParallelConfig
19
+from vllm.model_executor.layers.fused_moe.config import (
20
+ FusedMoEConfig,
21
+ FusedMoEParallelConfig,
22
+ FusedMoEQuantConfig,
23
+)
24
from vllm.model_executor.layers.fused_moe.moe_align_block_size import (
25
moe_align_block_size,
26
)
@@ -34,21 +38,16 @@ from vllm.model_executor.layers.quantization.utils.quant_utils import QuantKey
38
from vllm.platforms import current_platform
39
from vllm.v1.worker.workspace import current_workspace_manager
40
37
-if TYPE_CHECKING:
38
- from vllm.model_executor.layers.quantization.humming import HummingMoEMethod
39
-
40
-
41
logger = init_logger(__name__)
42
43
44
def get_humming_moe_gemm_type() -> str:
45
env_gemm_type: str = envs.VLLM_HUMMING_MOE_GEMM_TYPE or ""
46
env_gemm_type = env_gemm_type.lower()
47
- if env_gemm_type in ["indexed", "grouped"]:
47
+ if env_gemm_type == "indexed":
48
gemm_type = env_gemm_type
49
- elif current_platform.has_device_capability(90):
50
- # for device that supports TMA, use grouped gemm
51
- gemm_type = "grouped"
49
+ elif env_gemm_type in ["grouped_contiguous", "grouped"]:
50
+ gemm_type = "grouped_contiguous"
51
else:
52
gemm_type = "indexed"
53
@@ -60,49 +59,44 @@ class HummingExpertsBase(mk.FusedMoEExpertsModular):
59
def __init__(
60
self,
61
layer: torch.nn.Module,
63
- quant_method: "HummingMoEMethod",
64
- prepare_finalize: mk.FusedMoEPrepareAndFinalizeModular | None = None,
62
+ moe_config: FusedMoEConfig,
63
+ quant_config: FusedMoEQuantConfig,
64
+ max_num_tokens: int | None = None,
65
+ num_dispatchers: int | None = None,
66
):
67
self.layer = layer
68
self.num_experts = self.layer.num_experts
69
self.global_num_experts = self.layer.global_num_experts
70
self.init_humming_moe()
71
71
- if prepare_finalize is not None:
72
- max_num_tokens: int | None = None
73
- num_dispatchers: int | None = None
74
- if self.is_batched:
75
- max_num_tokens = prepare_finalize.max_num_tokens_per_rank()
76
- num_dispatchers = prepare_finalize.num_dispatchers()
77
-
78
- assert quant_method.moe_quant_config is not None
79
- super().__init__(
80
- moe_config=quant_method.moe,
81
- quant_config=quant_method.moe_quant_config,
82
- max_num_tokens=max_num_tokens,
83
- num_dispatchers=num_dispatchers,
84
- )
85
- else:
86
- assert not self.is_batched
72
+ if self.is_batched():
73
+ assert max_num_tokens is not None and num_dispatchers is not None
74
+
75
+ super().__init__(
76
+ moe_config=moe_config,
77
+ quant_config=quant_config,
78
+ max_num_tokens=max_num_tokens,
79
+ num_dispatchers=num_dispatchers,
80
+ )
81
82
def init_humming_moe(self):
83
self.compute_config = {
84
"use_batch_invariant": envs.VLLM_BATCH_INVARIANT,
85
"use_f16_accum": envs.VLLM_HUMMING_USE_F16_ACCUM,
92
- "gemm_type": self.humming_gemm_type.value,
86
+ "gemm_type": self.humming_gemm_type().value,
87
}
88
self.w13_tuning_config = HummingMethod.get_default_tuning_configs(
89
layer=self.layer,
90
use_f16_accum=envs.VLLM_HUMMING_USE_F16_ACCUM,
91
use_batch_invariant=envs.VLLM_BATCH_INVARIANT,
98
- gemm_type=self.humming_gemm_type,
92
+ gemm_type=self.humming_gemm_type(),
93
sublayer_name="w13",
94
)
95
self.w2_tuning_config = HummingMethod.get_default_tuning_configs(
96
layer=self.layer,
97
use_f16_accum=envs.VLLM_HUMMING_USE_F16_ACCUM,
98
use_batch_invariant=envs.VLLM_BATCH_INVARIANT,
105
- gemm_type=self.humming_gemm_type,
99
+ gemm_type=self.humming_gemm_type(),
100
sublayer_name="w2",
101
)
102
self.compute_config_str = json.dumps(self.compute_config)
@@ -124,13 +118,13 @@ class HummingExpertsBase(mk.FusedMoEExpertsModular):
118
global_num_experts = self.global_num_experts
119
return math.ceil(global_valid_shape_m * num_experts / global_num_experts)
120
127
- @property
128
- def humming_gemm_type(self) -> HummingGemmType:
121
+ @staticmethod
122
+ def humming_gemm_type() -> HummingGemmType:
123
raise NotImplementedError
124
131
- @property
132
- def is_batched(self) -> bool:
133
- return self.activation_format() == mk.FusedMoEActivationFormat.BatchedExperts
125
+ @classmethod
126
+ def is_batched(cls) -> bool:
127
+ return cls.activation_format() == mk.FusedMoEActivationFormat.BatchedExperts
128
129
@staticmethod
130
def _supports_quant_scheme(
@@ -189,7 +183,7 @@ class HummingExpertsBase(mk.FusedMoEExpertsModular):
183
assert w1.size(0) == num_experts
184
assert w2.size(0) == num_experts
185
192
- if not self.is_batched:
186
+ if not self.is_batched():
187
num_tokens = a1.size(0)
188
assert topk_ids.size(0) == num_tokens
189
else:
@@ -201,7 +195,7 @@ class HummingExpertsBase(mk.FusedMoEExpertsModular):
195
196
def get_buffer_metas(self, M: int, topk: int, activation: MoEActivation):
197
num_experts = self.num_experts
204
- N = self.layer.intermediate_size
198
+ N = self.layer.intermediate_size_per_partition
199
K = self.layer.hidden_size
200
assert isinstance(num_experts, int)
201
assert isinstance(N, int)
@@ -218,7 +212,7 @@ class HummingExpertsBase(mk.FusedMoEExpertsModular):
212
# The output must be derived from workspace1.
213
214
output_shape: tuple[int, ...]
221
- if self.is_batched:
215
+ if self.is_batched():
216
max_num_tokens = self.max_num_tokens
217
num_dispatchers = self.num_dispatchers
218
assert max_num_tokens is not None and num_dispatchers is not None
@@ -227,7 +221,7 @@ class HummingExpertsBase(mk.FusedMoEExpertsModular):
221
output_shape = (num_experts, max_num_tokens * num_dispatchers, K)
222
else:
223
input_shape_m = M
230
- if self.humming_gemm_type != HummingGemmType.INDEXED:
224
+ if self.humming_gemm_type() != HummingGemmType.INDEXED:
225
input_shape_m = M * topk
226
real_shape_m = M * topk
227
output_shape = (M, K)
@@ -262,7 +256,7 @@ class HummingExpertsBase(mk.FusedMoEExpertsModular):
256
"dtype": torch_dtype_map[a_dtype],
257
},
258
"down_output": {
265
- "shape": output_shape if self.is_batched else (real_shape_m, K),
259
+ "shape": output_shape if self.is_batched() else (real_shape_m, K),
260
"dtype": torch_dtype_map[c_dtype],
261
},
262
"output": {
@@ -288,7 +282,7 @@ class HummingExpertsBase(mk.FusedMoEExpertsModular):
282
]
283
284
# batched moe use down_output as output
291
- if not self.is_batched:
285
+ if not self.is_batched():
286
required_buffers.append("output")
287
288
return buffer_metas, required_buffers
@@ -308,7 +302,7 @@ class HummingExpertsBase(mk.FusedMoEExpertsModular):
302
else:
303
workspace2_nbytes = max(workspace2_nbytes, nbytes)
304
311
- output_key = "down_output" if self.is_batched else "output"
305
+ output_key = "down_output" if self.is_batched() else "output"
306
output_shape = buffer_metas[output_key]["shape"]
307
308
return (workspace1_nbytes // 2,), (workspace2_nbytes // 2,), output_shape
@@ -395,6 +389,33 @@ class HummingExpertsBase(mk.FusedMoEExpertsModular):
389
):
390
raise NotImplementedError
391
392
+ @staticmethod
393
+ def is_supported_config(
394
+ cls: type[mk.FusedMoEExperts],
395
+ moe_config: FusedMoEConfig,
396
+ weight_key: QuantKey | None,
397
+ activation_key: QuantKey | None,
398
+ activation_format: mk.FusedMoEActivationFormat,
399
+ ) -> tuple[bool, str | None]:
400
+ if activation_format == mk.FusedMoEActivationFormat.BatchedExperts:
401
+ supported = cls.activation_format() == activation_format
402
+ reason = "activation_format mismatched"
403
+ elif activation_format == mk.FusedMoEActivationFormat.Standard:
404
+ if cls.activation_format() != mk.FusedMoEActivationFormat.Standard:
405
+ supported = False
406
+ reason = "activation_format mismatched"
407
+ else:
408
+ assert hasattr(cls, "humming_gemm_type")
409
+ gemm_type = cls.humming_gemm_type().value.lower()
410
+ preferred_gemm_type = get_humming_moe_gemm_type().lower()
411
+ supported = preferred_gemm_type == gemm_type
412
+ reason = "preferred gemm type mismatched"
413
+ else:
414
+ supported = False
415
+ reason = "unsupported activation_format"
416
+
417
+ return supported, None if supported else reason
418
+
419
420
class HummingIndexedExperts(HummingExpertsBase):
421
def finalize_weight_and_reduce_impl(self) -> mk.TopKWeightAndReduce:
@@ -404,8 +425,8 @@ class HummingIndexedExperts(HummingExpertsBase):
425
def activation_format() -> mk.FusedMoEActivationFormat:
426
return mk.FusedMoEActivationFormat.Standard
427
407
- @property
408
- def humming_gemm_type(self) -> HummingGemmType:
428
+ @staticmethod
429
+ def humming_gemm_type() -> HummingGemmType:
430
return HummingGemmType.INDEXED
431
432
def prepare_humming_moe_kwargs(
@@ -526,8 +547,8 @@ class HummingGroupedExperts(HummingExpertsBase):
547
def activation_format() -> mk.FusedMoEActivationFormat:
548
return mk.FusedMoEActivationFormat.Standard
549
529
- @property
530
- def humming_gemm_type(self) -> HummingGemmType:
550
+ @staticmethod
551
+ def humming_gemm_type() -> HummingGemmType:
552
return HummingGemmType.GROUPED_CONTIGUOUS
553
554
def main_apply(
@@ -619,8 +640,8 @@ class BatchedHummingGroupedExperts(HummingExpertsBase):
640
def activation_format() -> mk.FusedMoEActivationFormat:
641
return mk.FusedMoEActivationFormat.BatchedExperts
642
622
- @property
623
- def humming_gemm_type(self) -> HummingGemmType:
643
+ @staticmethod
644
+ def humming_gemm_type() -> HummingGemmType:
645
return HummingGemmType.GROUPED_MASKED
646
647
def main_apply(
vllm/model_executor/layers/fused_moe/layer.py
-3
@@ -1103,9 +1103,6 @@ class FusedMoE(PluggableLayer):
1103
return_success: bool = False,
1104
) -> bool | None:
1105
quant_config_name = self.quant_config and self.quant_config.get_name()
1106
- if quant_config_name == "humming":
1107
- assert hasattr(self.quant_method, "weight_schema")
1108
- quant_config_name = self.quant_method.weight_schema.quant_method
1106
if quant_config_name == "gpt_oss_mxfp4":
1107
# (FIXME) for gpt-oss all experts are combined
1108
if "bias" in weight_name:
vllm/model_executor/layers/fused_moe/oracle/mxfp4.py
+63
-1
@@ -68,6 +68,8 @@ class Mxfp4MoeBackend(Enum):
68
XPU = "XPU"
69
# Emulation
70
EMULATION = "EMULATION"
71
+ # Humming
72
+ HUMMING = "HUMMING"
73
74
75
# Backends that share the same TRTLLM weight format
@@ -130,6 +132,19 @@ def backend_to_kernel_cls(
132
133
return [UnfusedOAITritonExperts]
134
135
+ elif backend == Mxfp4MoeBackend.HUMMING:
136
+ from vllm.model_executor.layers.fused_moe.fused_humming_moe import (
137
+ BatchedHummingGroupedExperts,
138
+ HummingGroupedExperts,
139
+ HummingIndexedExperts,
140
+ )
141
+
142
+ return [
143
+ BatchedHummingGroupedExperts,
144
+ HummingGroupedExperts,
145
+ HummingIndexedExperts,
146
+ ]
147
+
148
elif backend == Mxfp4MoeBackend.MARLIN:
149
from vllm.model_executor.layers.fused_moe.fused_marlin_moe import (
150
MarlinExperts,
@@ -177,6 +192,7 @@ def map_mxfp4_backend(runner_backend: MoEBackend) -> Mxfp4MoeBackend:
192
"flashinfer_cutlass_afp8": Mxfp4MoeBackend.FLASHINFER_CUTLASS_MXFP4_MXFP8,
193
"triton": Mxfp4MoeBackend.TRITON,
194
"triton_unfused": Mxfp4MoeBackend.TRITON_UNFUSED,
195
+ "humming": Mxfp4MoeBackend.HUMMING,
196
"marlin": Mxfp4MoeBackend.MARLIN,
197
"aiter": Mxfp4MoeBackend.AITER,
198
"xpu": Mxfp4MoeBackend.XPU,
@@ -573,7 +589,21 @@ def convert_gpt_oss_weight_to_mxfp4_moe_kernel_format(
589
590
sf_block_size = 32 # mxfp4 block size
591
576
- if mxfp4_backend in (
592
+ if mxfp4_backend == Mxfp4MoeBackend.HUMMING:
593
+ from vllm.model_executor.layers.quantization.utils.humming_utils import (
594
+ prepare_humming_moe_layer,
595
+ )
596
+
597
+ prepare_humming_moe_layer(layer, {"quant_method": "gpt_oss_mxfp4"})
598
+ return (
599
+ layer.w13_weight,
600
+ layer.w2_weight,
601
+ layer.w13_weight_scale,
602
+ layer.w2_weight_scale,
603
+ getattr(layer, "w13_bias", None),
604
+ getattr(layer, "w2_bias", None),
605
+ )
606
+ elif mxfp4_backend in (
607
Mxfp4MoeBackend.MARLIN,
608
Mxfp4MoeBackend.BATCHED_MARLIN,
609
):
@@ -970,6 +1000,21 @@ def convert_weight_to_mxfp4_moe_kernel_format(
1000
w2_bias,
1001
)
1002
1003
+ if mxfp4_backend == Mxfp4MoeBackend.HUMMING:
1004
+ from vllm.model_executor.layers.quantization.utils.humming_utils import (
1005
+ prepare_humming_moe_layer,
1006
+ )
1007
+
1008
+ prepare_humming_moe_layer(layer, {"quant_method": "mxfp4"})
1009
+ return (
1010
+ layer.w13_weight,
1011
+ layer.w2_weight,
1012
+ layer.w13_weight_scale,
1013
+ layer.w2_weight_scale,
1014
+ getattr(layer, "w13_bias", None),
1015
+ getattr(layer, "w2_bias", None),
1016
+ )
1017
+
1018
if mxfp4_backend in (Mxfp4MoeBackend.MARLIN, Mxfp4MoeBackend.BATCHED_MARLIN):
1019
from vllm.model_executor.layers.quantization.utils.marlin_utils_fp4 import (
1020
prepare_moe_mxfp4_layer_for_marlin,
@@ -1175,6 +1220,7 @@ def make_mxfp4_moe_quant_config(
1220
swiglu_limit: float | None = None,
1221
w1_bias: torch.Tensor | None = None,
1222
w2_bias: torch.Tensor | None = None,
1223
+ layer: torch.nn.Module | None = None,
1224
) -> FusedMoEQuantConfig | None:
1225
"""Create a FusedMoEQuantConfig for the given MXFP4 backend."""
1226
if mxfp4_backend == Mxfp4MoeBackend.DEEPGEMM_MXFP4:
@@ -1234,6 +1280,14 @@ def make_mxfp4_moe_quant_config(
1280
gemm1_beta=gemm1_beta,
1281
gemm1_clamp_limit=swiglu_limit,
1282
)
1283
+ elif mxfp4_backend == Mxfp4MoeBackend.HUMMING:
1284
+ from vllm.model_executor.layers.fused_moe.layer import FusedMoE
1285
+ from vllm.model_executor.layers.quantization.utils.humming_utils import (
1286
+ get_humming_moe_quant_config,
1287
+ )
1288
+
1289
+ assert isinstance(layer, FusedMoE)
1290
+ return get_humming_moe_quant_config(layer)
1291
else:
1292
return ocp_mx_moe_quant_config(
1293
quant_dtype="mxfp4",
@@ -1254,6 +1308,7 @@ def make_mxfp4_moe_kernel(
1308
mxfp4_backend: Mxfp4MoeBackend,
1309
routing_tables: tuple[torch.Tensor, torch.Tensor, torch.Tensor] | None = None,
1310
shared_experts: torch.nn.Module | None = None,
1311
+ layer: torch.nn.Module | None = None,
1312
) -> mk.FusedMoEKernel:
1313
"""Create a FusedMoEKernel for the given MXFP4 backend."""
1314
is_monolithic = issubclass(experts_cls, mk.FusedMoEExpertsMonolithic)
@@ -1269,6 +1324,11 @@ def make_mxfp4_moe_kernel(
1324
1325
logger.info_once("Using %s", prepare_finalize.__class__.__name__)
1326
1327
+ extra_kwargs = {}
1328
+ if mxfp4_backend == Mxfp4MoeBackend.HUMMING:
1329
+ assert layer is not None
1330
+ extra_kwargs["layer"] = layer
1331
+
1332
# Create Experts.
1333
if prepare_finalize.activation_format == mk.FusedMoEActivationFormat.BatchedExperts:
1334
max_num_tokens = prepare_finalize.max_num_tokens_per_rank()
@@ -1278,11 +1338,13 @@ def make_mxfp4_moe_kernel(
1338
quant_config=moe_quant_config,
1339
max_num_tokens=max_num_tokens,
1340
num_dispatchers=prepare_finalize.num_dispatchers(),
1341
+ **extra_kwargs,
1342
)
1343
else:
1344
experts = experts_cls(
1345
moe_config=moe_config,
1346
quant_config=moe_quant_config,
1347
+ **extra_kwargs,
1348
)
1349
1350
kernel = mk.FusedMoEKernel(
vllm/model_executor/layers/quantization/humming.py
+29
-67
@@ -9,11 +9,9 @@ import regex as re
9
import torch
10
11
from vllm import envs
12
-from vllm.model_executor.layers.fused_moe.activation import MoEActivation
12
from vllm.model_executor.layers.fused_moe.config import (
13
FusedMoEConfig,
14
FusedMoEQuantConfig,
16
- FusedMoEQuantDesc,
15
)
16
from vllm.model_executor.layers.fused_moe.layer import (
17
FusedMoE,
@@ -32,7 +30,6 @@ from vllm.model_executor.layers.quantization.base_config import (
30
QuantizationConfig,
31
QuantizeMethodBase,
32
)
35
-from vllm.model_executor.layers.quantization.utils.quant_utils import GroupShape
33
from vllm.model_executor.model_loader.weight_utils import default_weight_loader
34
from vllm.model_executor.parameter import (
35
BasevLLMParameter,
@@ -215,6 +212,15 @@ class HummingConfig(QuantizationConfig):
212
def override_quantization_method(
213
cls, hf_quant_cfg, user_quant, hf_config=None
214
) -> QuantizationMethods | None:
215
+ if user_quant == "humming" and hf_config is not None:
216
+ model_type = hf_config.model_type
217
+ quant_method = hf_quant_cfg.get("quant_method", None)
218
+ if model_type == "gpt_oss" and quant_method == "mxfp4":
219
+ msg = (
220
+ "For gpt-oss model, use '--moe-backend humming' "
221
+ "instead of '--quantization humming'."
222
+ )
223
+ raise ValueError(msg)
224
return "humming" if user_quant == "humming" else None
225
226
def apply_vllm_mapper(self, hf_to_vllm_mapper: "WeightsMapper"):
@@ -299,8 +305,6 @@ class HummingConfig(QuantizationConfig):
305
force_weight_schema = schema
306
307
if weight_schema is not None:
302
- if weight_schema.quant_method == "gpt_oss_mxfp4" and layer_type != "moe":
303
- return None
308
input_schema = None
309
force_input_schema = None
310
@@ -335,12 +339,6 @@ class HummingConfig(QuantizationConfig):
339
elif isinstance(layer, LinearBase):
340
layer_type = "linear"
341
338
- # TODO: remove this after humming moe backend is ready
339
- quant_method = self.full_config.get("quant_method", None)
340
- moe_activation = getattr(layer, "activation", None)
341
- if quant_method == "mxfp4" and moe_activation == MoEActivation.SWIGLUOAI:
342
- self.full_config["quan_method"] = "gpt_oss_mxfp4"
343
-
342
quant_config = self.get_quant_config_for_layer(prefix, layer_type)
343
if quant_config is None:
344
if isinstance(layer, FusedMoE):
@@ -760,62 +758,18 @@ class HummingMoEMethod(FusedMoEMethodBase):
758
layer.register_buffer("locks", locks)
759
760
def get_fused_moe_quant_config(self, layer: torch.nn.Module) -> FusedMoEQuantConfig:
763
- self.process_weights_after_loading(layer)
764
-
765
- input_schema = self.input_schemas["w13"]
766
- weight_schema = self.weight_schemas["w13"]
767
-
768
- a_dtype = input_schema.a_dtype
769
- if a_dtype is None or a_dtype.num_bits == 16:
770
- a_quant_desc = FusedMoEQuantDesc(dtype=None)
771
- else:
772
- shape = GroupShape(row=1, col=-1)
773
- a_quant_desc = FusedMoEQuantDesc(dtype=str(a_dtype), shape=shape)
774
-
775
- weight_scale_group_size = weight_schema.weight_scale_group_size
776
- weight_scale_group_size_n = weight_schema.weight_scale_group_size_n
777
- weight_group_shape: tuple[int, ...] = ()
778
- if weight_scale_group_size_n > 1:
779
- weight_group_shape = GroupShape(
780
- row=weight_scale_group_size,
781
- col=weight_scale_group_size_n,
782
- )
783
- elif weight_scale_group_size == 0:
784
- weight_group_shape = GroupShape(row=-1, col=1)
785
- else:
786
- weight_group_shape = GroupShape(row=weight_scale_group_size, col=1)
787
-
788
- w1_quant_desc = FusedMoEQuantDesc(
789
- dtype=str(weight_schema.b_dtype),
790
- shape=weight_group_shape,
791
- scale=getattr(layer, "w13_weight_scale", None),
792
- alpha_or_gscale=getattr(layer, "w13_global_scale", None),
793
- zp=getattr(layer, "w13_zero_point", None),
794
- bias=getattr(layer, "w13_bias", None),
795
- )
796
-
797
- w2_quant_desc = FusedMoEQuantDesc(
798
- dtype=str(weight_schema.b_dtype),
799
- shape=weight_group_shape,
800
- scale=getattr(layer, "w2_weight_scale", None),
801
- alpha_or_gscale=getattr(layer, "w2_global_scale", None),
802
- zp=getattr(layer, "w2_zero_point", None),
803
- bias=getattr(layer, "w2_bias", None),
761
+ from vllm.model_executor.layers.quantization.utils.humming_utils import (
762
+ get_humming_moe_quant_config,
763
)
764
806
- return FusedMoEQuantConfig(
807
- _a1=a_quant_desc,
808
- _a2=a_quant_desc,
809
- _w1=w1_quant_desc,
810
- _w2=w2_quant_desc,
811
- )
765
+ return get_humming_moe_quant_config(layer)
766
767
def process_weights_after_loading(self, layer: torch.nn.Module) -> None:
768
if getattr(self, "processed", False):
769
return
770
self.processed = True
817
- self.weight_schemas = {}
818
- self.input_schemas = {}
771
+ layer.weight_schemas = {}
772
+ layer.input_schemas = {}
773
for sublayer_name, configs in layer.sublayer_configs.items():
774
input_schema = self.input_schema
775
weight_schema = self.weight_schema
@@ -858,8 +812,8 @@ class HummingMoEMethod(FusedMoEMethodBase):
812
param = torch.nn.Parameter(tensor, requires_grad=False)
813
setattr(layer, name, param)
814
861
- self.weight_schemas[sublayer_name] = weight_schema
862
- self.input_schemas[sublayer_name] = input_schema
815
+ layer.weight_schemas[sublayer_name] = weight_schema
816
+ layer.input_schemas[sublayer_name] = input_schema
817
818
# force requant (origin quant setting -> fp16/bf16 -> new_quant setting)
819
assert isinstance(weight_schema, HummingWeightSchema)
@@ -913,10 +867,11 @@ class HummingMoEMethod(FusedMoEMethodBase):
867
868
# use moe modular
869
experts: HummingIndexedExperts | HummingGroupedExperts
870
+ assert self.moe_quant_config is not None
871
if get_humming_moe_gemm_type() == "indexed":
917
- experts = HummingIndexedExperts(layer, self)
872
+ experts = HummingIndexedExperts(layer, self.moe, self.moe_quant_config)
873
else:
919
- experts = HummingGroupedExperts(layer, self)
874
+ experts = HummingGroupedExperts(layer, self.moe, self.moe_quant_config)
875
self.experts = experts
876
877
def select_gemm_impl(
@@ -927,12 +882,19 @@ class HummingMoEMethod(FusedMoEMethodBase):
882
from vllm.model_executor.layers.fused_moe import modular_kernel as mk
883
884
activation_format = prepare_finalize.activation_format
885
+ assert self.moe_quant_config is not None
886
if activation_format == mk.FusedMoEActivationFormat.BatchedExperts:
931
- return BatchedHummingGroupedExperts(layer, self, prepare_finalize)
887
+ return BatchedHummingGroupedExperts(
888
+ layer=layer,
889
+ moe_config=self.moe,
890
+ quant_config=self.moe_quant_config,
891
+ max_num_tokens=prepare_finalize.max_num_tokens_per_rank(),
892
+ num_dispatchers=prepare_finalize.num_dispatchers(),
893
+ )
894
elif get_humming_moe_gemm_type() == "indexed":
933
- return HummingIndexedExperts(layer, self, prepare_finalize)
895
+ return HummingIndexedExperts(layer, self.moe, self.moe_quant_config)
896
else:
935
- return HummingGroupedExperts(layer, self, prepare_finalize)
897
+ return HummingGroupedExperts(layer, self.moe, self.moe_quant_config)
898
899
def apply(
900
self,
vllm/model_executor/layers/quantization/mxfp4.py
+4
@@ -366,6 +366,7 @@ class GptOssMxfp4MoEMethod(FusedMoEMethodBase):
366
experts_cls=self.experts_cls,
367
routing_tables=layer._maybe_init_expert_routing_tables(),
368
shared_experts=layer.shared_experts,
369
+ layer=layer,
370
)
371
372
def process_weights_after_loading(self, layer):
@@ -404,6 +405,7 @@ class GptOssMxfp4MoEMethod(FusedMoEMethodBase):
405
gemm1_alpha=1.702,
406
gemm1_beta=1.0,
407
swiglu_limit=7.0,
408
+ layer=layer,
409
)
410
411
def select_gemm_impl(
@@ -692,6 +694,7 @@ class Mxfp4MoEMethod(FusedMoEMethodBase):
694
experts_cls=self.experts_cls,
695
routing_tables=layer._maybe_init_expert_routing_tables(),
696
shared_experts=layer.shared_experts,
697
+ layer=layer,
698
)
699
700
def process_weights_after_loading(self, layer):
@@ -729,6 +732,7 @@ class Mxfp4MoEMethod(FusedMoEMethodBase):
732
w1_bias=w1_bias,
733
w2_bias=w2_bias,
734
swiglu_limit=swiglu_limit,
735
+ layer=layer,
736
)
737
738
def select_gemm_impl(
vllm/model_executor/layers/quantization/utils/humming_moe_utils.py
deleted
-35
@@ -1,35 +0,0 @@
1
-# SPDX-License-Identifier: Apache-2.0
2
-# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
-import torch
4
-
5
-from vllm.model_executor.layers.fused_moe.moe_align_block_size import (
6
- moe_align_block_size,
7
-)
8
-
9
-
10
-def humming_moe_align(
11
- configs: list[int],
12
- topk_ids: torch.Tensor,
13
- num_experts: int,
14
- expert_map: torch.Tensor | None = None,
15
-) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
16
- assert len(configs) > 0 and len(configs) % 3 == 0
17
- # NOTE: we choose moe_block_size based on
18
- # num_tokens * top_k (= topk_ids.nelement())
19
- shape_m = topk_ids.nelement()
20
-
21
- for i in range(len(configs) // 3):
22
- if shape_m > configs[i * 3] and shape_m <= configs[i * 3 + 1]:
23
- block_size = configs[i * 3 + 2]
24
- break
25
- else:
26
- raise ValueError(f"Could not find a matching block_size for shape_m={shape_m}")
27
-
28
- return moe_align_block_size(
29
- topk_ids=topk_ids,
30
- block_size=block_size,
31
- num_experts=num_experts,
32
- expert_map=expert_map,
33
- pad_sorted_ids=False,
34
- ignore_invalid_experts=True,
35
- )
vllm/model_executor/layers/quantization/utils/humming_utils.py
new
+214
@@ -0,0 +1,214 @@
1
+# SPDX-License-Identifier: Apache-2.0
2
+# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
+from typing import Any
4
+
5
+import regex as re
6
+import torch
7
+from humming.layer import HummingInputSchema, HummingMethod
8
+from humming.schema import BaseWeightSchema
9
+
10
+from vllm import envs
11
+from vllm.model_executor.layers.fused_moe.config import (
12
+ FusedMoEQuantConfig,
13
+ FusedMoEQuantDesc,
14
+)
15
+from vllm.model_executor.layers.fused_moe.layer import FusedMoE
16
+from vllm.model_executor.layers.linear import LinearBase
17
+from vllm.model_executor.layers.quantization.utils.quant_utils import GroupShape
18
+
19
+
20
+def humming_is_layer_skipped(config: dict[str, Any], prefix: str):
21
+ if not config:
22
+ return True
23
+
24
+ keys = ["ignored_layers", "ignore", "modules_to_not_convert"]
25
+ ignored_layers: list[str] = []
26
+ for key in keys:
27
+ ignored_layers = config.get(key, []) or []
28
+ if not ignored_layers:
29
+ break
30
+
31
+ if any(module_name in prefix for module_name in ignored_layers):
32
+ return True
33
+ if "lm_head" in prefix:
34
+ return True
35
+
36
+ for regex in config.get("dynamic", {}):
37
+ if regex[:1] != "-":
38
+ continue
39
+ if re.match(regex[2:], prefix):
40
+ return True
41
+
42
+ return False
43
+
44
+
45
+def prepare_humming_layer(layer: LinearBase, quant_config: dict):
46
+ weight_schema = BaseWeightSchema.from_config(quant_config)
47
+ input_schema = HummingInputSchema()
48
+
49
+ shape_k_stacks = [layer.input_size_per_partition]
50
+ shape_n_stacks = layer.output_partition_sizes
51
+
52
+ # Step 1: convert weight to humming standard format
53
+ weight_schema, tensors = weight_schema.convert_humming(
54
+ tensors=layer.named_parameters(),
55
+ shape_n_stacks=shape_n_stacks,
56
+ shape_k_stacks=shape_k_stacks,
57
+ param_dtype=layer.params_dtype,
58
+ )
59
+
60
+ layer.weight_schema = weight_schema
61
+
62
+ for name, _ in list(layer.named_parameters()):
63
+ delattr(layer, name)
64
+
65
+ for name, tensor in tensors.items():
66
+ param = torch.nn.Parameter(tensor, requires_grad=False)
67
+ setattr(layer, name, param)
68
+
69
+ # Step 2: transform weight (humming standard format) for forwarding
70
+ HummingMethod.prepare_layer_meta(
71
+ layer=layer,
72
+ shape_n=layer.output_partition_sizes_sum,
73
+ shape_k=layer.input_size_per_partition,
74
+ weight_schema=weight_schema,
75
+ input_schema=input_schema,
76
+ pad_n_to_multiple=256,
77
+ pad_k_to_multiple=128,
78
+ has_bias=layer.has_bias,
79
+ torch_dtype=layer.param_dtype,
80
+ )
81
+
82
+ HummingMethod.transform_humming_layer(layer)
83
+
84
+
85
+def prepare_humming_moe_layer(layer: FusedMoE, quant_config: dict):
86
+ weight_schema = BaseWeightSchema.from_config(quant_config)
87
+ input_quant_config = envs.VLLM_HUMMING_INPUT_QUANT_CONFIG or {}
88
+ if humming_is_layer_skipped(input_quant_config, layer.layer_name):
89
+ input_schema = HummingInputSchema()
90
+ else:
91
+ # TODO: read input_quant_config from quant_config
92
+ input_schema = HummingInputSchema.from_config(input_quant_config)
93
+
94
+ is_gated = layer.activation.is_gated
95
+ shape_config = {
96
+ "w13": (
97
+ layer.moe_config.intermediate_size_per_partition * 2,
98
+ layer.moe_config.hidden_dim,
99
+ ),
100
+ "w2": (
101
+ layer.moe_config.hidden_dim,
102
+ layer.moe_config.intermediate_size_per_partition * (1 if is_gated else 2),
103
+ ),
104
+ }
105
+
106
+ layer.weight_schemas = {}
107
+ layer.input_schemas = {}
108
+
109
+ for sublayer_name in shape_config:
110
+ # Step 1: convert weight to humming standard format
111
+ tensors: dict[str, torch.Tensor] = dict(
112
+ (key.removeprefix(sublayer_name + "_"), value)
113
+ for key, value in layer.state_dict().items()
114
+ if key.startswith(sublayer_name + "_")
115
+ )
116
+
117
+ shape_n, shape_k = shape_config[sublayer_name]
118
+ shape_n_stacks = [shape_n]
119
+ shape_k_stacks = [shape_k]
120
+ if sublayer_name == "w13":
121
+ shape_n_stacks = [shape_n // 2] * 2
122
+
123
+ weight_schema_new, tensors = weight_schema.convert_humming(
124
+ tensors=tensors,
125
+ shape_n_stacks=shape_n_stacks,
126
+ shape_k_stacks=shape_k_stacks,
127
+ num_experts=layer.local_num_experts,
128
+ param_dtype=layer.params_dtype,
129
+ )
130
+
131
+ layer.weight_schemas[sublayer_name] = weight_schema_new
132
+ layer.input_schemas[sublayer_name] = input_schema
133
+
134
+ for name, _ in list(layer.named_parameters()):
135
+ if not name.startswith(sublayer_name + "_"):
136
+ continue
137
+ delattr(layer, name)
138
+
139
+ for name, tensor in tensors.items():
140
+ name = f"{sublayer_name}_{name}"
141
+ param = torch.nn.Parameter(tensor, requires_grad=False)
142
+ setattr(layer, name, param)
143
+
144
+ # Step 2: transform weight (humming standard format) for forwarding
145
+ HummingMethod.prepare_layer_meta(
146
+ layer=layer,
147
+ shape_n=shape_n,
148
+ shape_k=shape_k,
149
+ pad_n_to_multiple=256,
150
+ pad_k_to_multiple=128,
151
+ input_schema=input_schema,
152
+ weight_schema=weight_schema_new,
153
+ has_bias=layer.moe_config.has_bias,
154
+ num_experts=layer.num_experts,
155
+ torch_dtype=layer.params_dtype,
156
+ sublayer_name=sublayer_name,
157
+ )
158
+
159
+ HummingMethod.transform_humming_layer(layer, sublayer_name=sublayer_name)
160
+
161
+ if not hasattr(layer, "locks"):
162
+ device = layer.w13_weight.device
163
+ locks = torch.zeros(1024, dtype=torch.int32, device=device)
164
+ layer.register_buffer("locks", locks)
165
+
166
+
167
+def get_humming_moe_quant_config(layer: FusedMoE):
168
+ input_schema = layer.input_schemas["w13"]
169
+ weight_schema = layer.weight_schemas["w13"]
170
+
171
+ a_dtype = input_schema.a_dtype
172
+ if a_dtype is None or a_dtype.num_bits == 16:
173
+ a_quant_desc = FusedMoEQuantDesc(dtype=None)
174
+ else:
175
+ shape = GroupShape(row=1, col=-1)
176
+ a_quant_desc = FusedMoEQuantDesc(dtype=str(a_dtype), shape=shape)
177
+
178
+ weight_scale_group_size = weight_schema.weight_scale_group_size
179
+ weight_scale_group_size_n = weight_schema.weight_scale_group_size_n
180
+ weight_group_shape: tuple[int, ...] = ()
181
+ if weight_scale_group_size_n > 1:
182
+ weight_group_shape = GroupShape(
183
+ row=weight_scale_group_size,
184
+ col=weight_scale_group_size_n,
185
+ )
186
+ elif weight_scale_group_size == 0:
187
+ weight_group_shape = GroupShape(row=-1, col=1)
188
+ else:
189
+ weight_group_shape = GroupShape(row=weight_scale_group_size, col=1)
190
+
191
+ w1_quant_desc = FusedMoEQuantDesc(
192
+ dtype=str(weight_schema.b_dtype),
193
+ shape=weight_group_shape,
194
+ scale=getattr(layer, "w13_weight_scale", None),
195
+ alpha_or_gscale=getattr(layer, "w13_global_scale", None),
196
+ zp=getattr(layer, "w13_zero_point", None),
197
+ bias=getattr(layer, "w13_bias", None),
198
+ )
199
+
200
+ w2_quant_desc = FusedMoEQuantDesc(
201
+ dtype=str(weight_schema.b_dtype),
202
+ shape=weight_group_shape,
203
+ scale=getattr(layer, "w2_weight_scale", None),
204
+ alpha_or_gscale=getattr(layer, "w2_global_scale", None),
205
+ zp=getattr(layer, "w2_zero_point", None),
206
+ bias=getattr(layer, "w2_bias", None),
207
+ )
208
+
209
+ return FusedMoEQuantConfig(
210
+ _a1=a_quant_desc,
211
+ _a2=a_quant_desc,
212
+ _w1=w1_quant_desc,
213
+ _w2=w2_quant_desc,
214
+ )