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 14, 23:45 UTC
[Model]: Fused MoE for nomic-embed-text-v2-moe (#18321)
Signed-off-by: isotr0py <2037008807@qq.com> Signed-off-by: Isotr0py <2037008807@qq.com> Signed-off-by: Isotr0py <mozf@mail2.sysu.edu.cn>
Isotr0py committed
Jul 29, 2025 at 18:13 UTC
a4528f0cac5d2857ccc56d2a2e1a1c43142643ce
2 files changed
+142
-113
vllm/model_executor/layers/fused_moe/fused_moe.py
+36
-11
@@ -7,6 +7,7 @@ import os
7
from typing import Any, Callable, Optional
8
9
import torch
10
+import torch.nn.functional as F
11
12
import vllm.envs as envs
13
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
@@ -1001,6 +1002,7 @@ def inplace_fused_experts(hidden_states: torch.Tensor,
1002
topk_weights: torch.Tensor,
1003
topk_ids: torch.Tensor,
1004
activation: str = "silu",
1005
+ is_act_and_mul: bool = True,
1006
apply_router_weight_on_input: bool = False,
1007
use_fp8_w8a8: bool = False,
1008
use_int8_w8a8: bool = False,
@@ -1018,7 +1020,8 @@ def inplace_fused_experts(hidden_states: torch.Tensor,
1020
a2_scale: Optional[torch.Tensor] = None,
1021
block_shape: Optional[list[int]] = None) -> None:
1022
fused_experts_impl(hidden_states, w1, w2, topk_weights, topk_ids, True,
1021
- activation, apply_router_weight_on_input, use_fp8_w8a8,
1023
+ activation, is_act_and_mul,
1024
+ apply_router_weight_on_input, use_fp8_w8a8,
1025
use_int8_w8a8, use_int8_w8a16, use_int4_w4a16,
1026
use_mxfp4_w4a4, per_channel_quant, global_num_experts,
1027
expert_map, w1_scale, w2_scale, w1_zp, w2_zp, a1_scale,
@@ -1032,6 +1035,7 @@ def inplace_fused_experts_fake(
1035
topk_weights: torch.Tensor,
1036
topk_ids: torch.Tensor,
1037
activation: str = "silu",
1038
+ is_act_and_mul: bool = True,
1039
apply_router_weight_on_input: bool = False,
1040
use_fp8_w8a8: bool = False,
1041
use_int8_w8a8: bool = False,
@@ -1167,6 +1171,7 @@ def outplace_fused_experts(
1171
topk_weights: torch.Tensor,
1172
topk_ids: torch.Tensor,
1173
activation: str = "silu",
1174
+ is_act_and_mul: bool = True,
1175
apply_router_weight_on_input: bool = False,
1176
use_fp8_w8a8: bool = False,
1177
use_int8_w8a8: bool = False,
@@ -1183,13 +1188,12 @@ def outplace_fused_experts(
1188
a1_scale: Optional[torch.Tensor] = None,
1189
a2_scale: Optional[torch.Tensor] = None,
1190
block_shape: Optional[list[int]] = None) -> torch.Tensor:
1186
- return fused_experts_impl(hidden_states, w1, w2, topk_weights, topk_ids,
1187
- False, activation, apply_router_weight_on_input,
1188
- use_fp8_w8a8, use_int8_w8a8, use_int8_w8a16,
1189
- use_int4_w4a16, use_mxfp4_w4a4,
1190
- per_channel_quant, global_num_experts,
1191
- expert_map, w1_scale, w2_scale, w1_zp, w2_zp,
1192
- a1_scale, a2_scale, block_shape)
1191
+ return fused_experts_impl(
1192
+ hidden_states, w1, w2, topk_weights, topk_ids, False, activation,
1193
+ is_act_and_mul, apply_router_weight_on_input, use_fp8_w8a8,
1194
+ use_int8_w8a8, use_int8_w8a16, use_int4_w4a16, use_mxfp4_w4a4,
1195
+ per_channel_quant, global_num_experts, expert_map, w1_scale, w2_scale,
1196
+ w1_zp, w2_zp, a1_scale, a2_scale, block_shape)
1197
1198
1199
def outplace_fused_experts_fake(
@@ -1199,6 +1203,7 @@ def outplace_fused_experts_fake(
1203
topk_weights: torch.Tensor,
1204
topk_ids: torch.Tensor,
1205
activation: str = "silu",
1206
+ is_act_and_mul: bool = True,
1207
use_fp8_w8a8: bool = False,
1208
use_int8_w8a8: bool = False,
1209
use_int8_w8a16: bool = False,
@@ -1253,6 +1258,7 @@ def fused_experts(
1258
topk_ids: torch.Tensor,
1259
inplace: bool = False,
1260
activation: str = "silu",
1261
+ is_act_and_mul: bool = True,
1262
apply_router_weight_on_input: bool = False,
1263
use_fp8_w8a8: bool = False,
1264
use_int8_w8a8: bool = False,
@@ -1283,6 +1289,8 @@ def fused_experts(
1289
or is_blackwell_deep_gemm_used())
1290
if (allow_deep_gemm and use_fp8_w8a8 and should_use_deep_gemm):
1291
assert apply_router_weight_on_input is False
1292
+ assert is_act_and_mul, (
1293
+ "DeepGemm only supports is_act_and_mul=True for now.")
1294
return deep_gemm_moe_fp8(
1295
hidden_states=hidden_states,
1296
w1=w1,
@@ -1319,6 +1327,7 @@ def fused_experts(
1327
topk_weights=topk_weights,
1328
topk_ids=topk_ids,
1329
activation=activation,
1330
+ is_act_and_mul=is_act_and_mul,
1331
apply_router_weight_on_input=apply_router_weight_on_input,
1332
use_fp8_w8a8=use_fp8_w8a8,
1333
use_int8_w8a8=use_int8_w8a8,
@@ -1345,6 +1354,7 @@ def fused_experts_impl(
1354
topk_ids: torch.Tensor,
1355
inplace: bool = False,
1356
activation: str = "silu",
1357
+ is_act_and_mul: bool = True,
1358
apply_router_weight_on_input: bool = False,
1359
use_fp8_w8a8: bool = False,
1360
use_int8_w8a8: bool = False,
@@ -1503,14 +1513,21 @@ def fused_experts_impl(
1513
per_channel_quant=per_channel_quant,
1514
block_shape=block_shape)
1515
1506
- if activation == "silu":
1516
+ # Activation function with multiplication
1517
+ if activation == "silu" and is_act_and_mul:
1518
torch.ops._C.silu_and_mul(intermediate_cache2,
1519
intermediate_cache1.view(-1, N))
1509
- elif activation == "gelu":
1520
+ elif activation == "gelu" and is_act_and_mul:
1521
torch.ops._C.gelu_and_mul(intermediate_cache2,
1522
intermediate_cache1.view(-1, N))
1523
+ # Activation function without multiplication
1524
+ elif activation == "silu":
1525
+ intermediate_cache2 = F.silu(intermediate_cache1.view(-1, N))
1526
+ elif activation == "gelu":
1527
+ intermediate_cache2 = F.gelu(intermediate_cache1.view(-1, N))
1528
else:
1513
- raise ValueError(f"Unsupported FusedMoe activation: {activation}")
1529
+ raise ValueError(f"Unsupported FusedMoe activation: {activation}, "
1530
+ f"with is_act_and_mul={is_act_and_mul}.")
1531
1532
qintermediate_cache2, a2q_scale = moe_kernel_quantize_input(
1533
A=intermediate_cache2,
@@ -1555,6 +1572,7 @@ def fused_moe(
1572
renormalize: bool,
1573
inplace: bool = False,
1574
activation: str = "silu",
1575
+ is_act_and_mul: bool = True,
1576
use_grouped_topk: bool = False,
1577
num_expert_group: Optional[int] = None,
1578
topk_group: Optional[int] = None,
@@ -1591,6 +1609,9 @@ def fused_moe(
1609
Defaults to False.
1610
- activation (str): The activation function to apply after the first
1611
MoE layer.
1612
+ - is_act_and_mul (bool): If True, use activation-and-mul function for
1613
+ activation (self-gated activation), otherwise use activation function
1614
+ for activation (ungated activation).
1615
- num_expert_group: Optional[int]: additional parameter for grouped_topk
1616
- topk_group: Optional[int]: additional parameter for grouped_topk
1617
- use_grouped_topk: If True, use grouped_topk instead of fused_topk
@@ -1627,6 +1648,9 @@ def fused_moe(
1648
Returns:
1649
- torch.Tensor: The output tensor after applying the MoE layer.
1650
"""
1651
+ if not is_act_and_mul:
1652
+ assert inplace is False, (
1653
+ "is_act_and_mul=False is not supported with inplace=True")
1654
1655
if use_grouped_topk:
1656
assert num_expert_group is not None and topk_group is not None
@@ -1647,6 +1671,7 @@ def fused_moe(
1671
topk_ids,
1672
inplace=inplace,
1673
activation=activation,
1674
+ is_act_and_mul=is_act_and_mul,
1675
use_fp8_w8a8=use_fp8_w8a8,
1676
use_int8_w8a8=use_int8_w8a8,
1677
use_int8_w8a16=use_int8_w8a16,
vllm/model_executor/models/bert_with_rope.py
+106
-102
@@ -10,9 +10,12 @@ from transformers import PretrainedConfig
10
from vllm.attention import Attention, AttentionType
11
from vllm.compilation.decorators import support_torch_compile
12
from vllm.config import CacheConfig, VllmConfig
13
-from vllm.distributed import get_tensor_model_parallel_world_size
13
+from vllm.distributed import (divide, get_tensor_model_parallel_rank,
14
+ get_tensor_model_parallel_world_size,
15
+ tensor_model_parallel_all_reduce)
16
from vllm.model_executor.layers.activation import (get_act_and_mul_fn,
17
get_act_fn)
18
+from vllm.model_executor.layers.fused_moe import fused_moe
19
from vllm.model_executor.layers.linear import (ColumnParallelLinear,
20
MergedColumnParallelLinear,
21
QKVParallelLinear,
@@ -26,6 +29,8 @@ from vllm.model_executor.model_loader.weight_utils import default_weight_loader
29
from vllm.model_executor.models import SupportsV0Only
30
from vllm.model_executor.models.interfaces import SupportsQuant
31
from vllm.model_executor.models.utils import WeightsMapper
32
+from vllm.model_executor.utils import set_weight_attrs
33
+from vllm.platforms import current_platform
34
from vllm.sequence import IntermediateTensors
35
36
@@ -201,114 +206,101 @@ class BertWithRopeMLP(nn.Module):
206
return hidden_states
207
208
204
-class NomicRouter(nn.Module):
209
+class NomicMoE(nn.Module):
210
206
- def __init__(self, hidden_size: int, moe_num_experts: int, moe_top_k: int):
211
+ def __init__(
212
+ self,
213
+ num_experts: int,
214
+ top_k: int,
215
+ hidden_size: int,
216
+ intermediate_size: int,
217
+ hidden_act: str,
218
+ params_dtype: Optional[torch.dtype] = None,
219
+ tp_size: Optional[int] = None,
220
+ ):
221
super().__init__()
208
- self.moe_top_k = moe_top_k
209
- self.layer = ReplicatedLinear(hidden_size, moe_num_experts, bias=False)
222
211
- def forward(
212
- self, x: torch.Tensor
213
- ) -> tuple[torch.Tensor, torch.Tensor, torch.LongTensor]:
214
- weights = self.layer(x.view(-1, x.shape[-1]))[0].softmax(
215
- dim=-1, dtype=torch.float32)
216
- top_weights, top_experts = torch.topk(weights, self.moe_top_k, dim=-1)
217
- weights = weights.to(x.dtype)
218
- top_weights = top_weights.to(x.dtype)
219
- return weights, top_weights, top_experts # type: ignore
220
-
221
-
222
-class NomicExpertMLP(nn.Module):
223
-
224
- def __init__(self, hidden_size: int, ffn_hidden_size: int,
225
- moe_num_experts: int, ffn_act_fn: str):
226
- super().__init__()
223
+ self.tp_size = tp_size or get_tensor_model_parallel_world_size()
224
+ self.num_total_experts = num_experts
225
+ self.top_k = top_k
226
self.hidden_size = hidden_size
228
- self.ffn_hidden_size = ffn_hidden_size
229
- self.moe_num_experts = moe_num_experts
227
+ self.total_intermediate_size = intermediate_size
228
+ self.intermediate_size = divide(intermediate_size, self.tp_size)
229
+ self.hidden_act = hidden_act
230
231
+ if params_dtype is None:
232
+ params_dtype = torch.get_default_dtype()
233
+ self.params_dtype = params_dtype
234
+
235
+ self.router = ReplicatedLinear(self.hidden_size,
236
+ self.num_total_experts,
237
+ bias=False)
238
self.w1 = nn.Parameter(
232
- torch.empty(moe_num_experts * ffn_hidden_size, hidden_size))
239
+ torch.empty(self.num_total_experts,
240
+ self.intermediate_size,
241
+ self.hidden_size,
242
+ device=current_platform.device_type,
243
+ dtype=self.params_dtype))
244
self.w2 = nn.Parameter(
234
- torch.empty(moe_num_experts * ffn_hidden_size, hidden_size))
235
- self.activation_fn = get_act_fn(ffn_act_fn)
236
-
237
- def forward(self, x: torch.Tensor, expert_idx: int) -> torch.Tensor:
238
- expert_w1 = self.w1.view(self.moe_num_experts, self.ffn_hidden_size,
239
- self.hidden_size)[expert_idx]
240
- expert_w2 = self.w2.view(self.moe_num_experts, self.ffn_hidden_size,
241
- self.hidden_size)[expert_idx]
242
-
243
- x1 = x.matmul(expert_w1.t())
244
- act_out = self.activation_fn(x1)
245
- x2 = act_out.matmul(expert_w2)
246
- return x2
247
-
248
-
249
-class NomicExperts(nn.Module):
250
-
251
- def __init__(self, config, hidden_size: int, ffn_hidden_size: int,
252
- moe_num_experts: int):
253
- super().__init__()
254
- self.moe_num_experts = moe_num_experts
255
-
256
- self.mlp = NomicExpertMLP(hidden_size=config.n_embd,
257
- ffn_hidden_size=config.n_inner,
258
- moe_num_experts=moe_num_experts,
259
- ffn_act_fn=config.hidden_act)
260
- self.bias = nn.Parameter(torch.zeros(config.n_embd))
261
-
262
- def forward(self, x: torch.Tensor, weights: torch.Tensor,
263
- top_weights: torch.Tensor,
264
- top_experts: torch.LongTensor) -> torch.Tensor:
265
- q_len, hidden_size = x.shape
266
- x = x.view(-1, hidden_size)
267
- out = torch.zeros_like(x)
268
-
269
- expert_mask = nn.functional.one_hot(
270
- top_experts, num_classes=self.moe_num_experts).permute(2, 1, 0)
271
- for expert_idx in range(0, self.moe_num_experts):
272
- topk_idx, token_idx = torch.where(expert_mask[expert_idx])
273
- if token_idx.shape[0] == 0:
274
- continue
275
-
276
- token_list = token_idx.tolist()
277
- topk_list = topk_idx.tolist()
278
-
279
- expert_tokens = x[None, token_list].reshape(-1, hidden_size)
280
- expert_out = self.mlp(
281
- expert_tokens, expert_idx) * top_weights[token_list, topk_list,
282
- None]
283
-
284
- out.index_add_(0, token_idx, expert_out)
285
-
286
- out = out.reshape(q_len, hidden_size)
287
- return out + self.bias
288
-
289
-
290
-class NomicMoELayer(nn.Module):
291
-
292
- def __init__(self, config: PretrainedConfig):
293
- super().__init__()
294
-
295
- self.router = NomicRouter(
296
- config.n_embd,
297
- moe_num_experts=config.num_experts,
298
- moe_top_k=config.moe_top_k,
299
- )
245
+ torch.empty(self.num_total_experts,
246
+ self.hidden_size,
247
+ self.intermediate_size,
248
+ device=current_platform.device_type,
249
+ dtype=self.params_dtype))
250
+ self.bias = nn.Parameter(torch.zeros(self.hidden_size))
251
+ set_weight_attrs(self.w1, {
252
+ "weight_loader": self.weight_loader,
253
+ })
254
+ set_weight_attrs(self.w2, {
255
+ "weight_loader": self.weight_loader,
256
+ })
257
301
- self.experts = NomicExperts(
302
- config,
303
- hidden_size=config.n_embd,
304
- ffn_hidden_size=config.n_inner,
305
- moe_num_experts=config.num_experts,
306
- )
258
+ def weight_loader(
259
+ self,
260
+ param: nn.Parameter,
261
+ loaded_weight: torch.Tensor,
262
+ weight_name: str,
263
+ ):
264
+ # NOTE: Nomic-MoE has fused experts weights with shape
265
+ # (num_experts * intermediate_size, hidden_size)
266
+ tp_rank = get_tensor_model_parallel_rank()
267
+ param_data = param.data
268
+ shard_size = self.intermediate_size
269
+ shard = slice(tp_rank * shard_size, (tp_rank + 1) * shard_size)
270
+ if weight_name.endswith("w1"):
271
+ loaded_weight = loaded_weight.reshape(
272
+ self.num_total_experts,
273
+ self.total_intermediate_size,
274
+ self.hidden_size,
275
+ )[:, shard]
276
+ if weight_name.endswith("w2"):
277
+ loaded_weight = loaded_weight.reshape(
278
+ self.num_total_experts,
279
+ self.total_intermediate_size,
280
+ self.hidden_size,
281
+ )[:, shard].transpose(1, 2)
282
+ param_data.copy_(loaded_weight)
283
308
- def forward(self, x: torch.Tensor):
309
- weights, top_weights, top_experts = self.router(x)
310
- out = self.experts(x, weights, top_weights, top_experts)
311
- return out
284
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
285
+ num_tokens, hidden_size = hidden_states.shape
286
+ hidden_states = hidden_states.view(-1, self.hidden_size)
287
+ # router_logits: (num_tokens, n_experts)
288
+ router_logits, _ = self.router(hidden_states)
289
+ final_hidden_states = fused_moe(hidden_states,
290
+ self.w1,
291
+ self.w2,
292
+ router_logits,
293
+ self.top_k,
294
+ renormalize=False,
295
+ inplace=False,
296
+ activation=self.hidden_act,
297
+ is_act_and_mul=False)
298
+
299
+ if self.tp_size > 1:
300
+ final_hidden_states = tensor_model_parallel_all_reduce(
301
+ final_hidden_states)
302
+
303
+ return final_hidden_states.view(num_tokens, hidden_size) + self.bias
304
305
306
class BertWithRopeBlock(nn.Module):
@@ -332,7 +324,11 @@ class BertWithRopeBlock(nn.Module):
324
prefix=f"{prefix}.attention")
325
326
if moe:
335
- self.mlp = NomicMoELayer(config=config, )
327
+ self.mlp = NomicMoE(num_experts=config.num_experts,
328
+ top_k=config.moe_top_k,
329
+ hidden_size=config.hidden_size,
330
+ intermediate_size=config.intermediate_size,
331
+ hidden_act=config.hidden_act)
332
else:
333
if config.hidden_act in ["silu", "geglu"]:
334
self.mlp = BertWithRopeGatedMLP(
@@ -463,7 +459,11 @@ class BertWithRope(nn.Module, SupportsV0Only, SupportsQuant):
459
param = params_dict[name]
460
weight_loader = getattr(param, "weight_loader",
461
default_weight_loader)
466
- weight_loader(param, loaded_weight)
462
+ if name.endswith((".w1", ".w2")):
463
+ # Nomic-MoE has fused experts weights
464
+ weight_loader(param, loaded_weight, name)
465
+ else:
466
+ weight_loader(param, loaded_weight)
467
loaded_params.add(name)
468
return loaded_params
469
@@ -481,6 +481,10 @@ class NomicBertModel(BertWithRope):
481
"mlp.fc12": "mlp.gate_proj",
482
"mlp.fc2": "mlp.down_proj",
483
"norm2": "mlp_ln",
484
+ # MoE mapping
485
+ "experts.mlp.": "",
486
+ "experts.": "",
487
+ "router.layer": "router",
488
})
489
490