@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / 0c5254b82a
Fork 0

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, 05:30 UTC

[oss] Init gpt-oss bf16 support (#22508)

Signed-off-by: Jee Jee Li <pandaleefree@gmail.com>

Jee Jee Li committed Aug 10, 2025 at 11:19 UTC 0c5254b82acc625112ce7adc10811514f1a42d52
4 files changed +342 -125
vllm/model_executor/layers/fused_moe/config.py
+5 -1
@@ -324,6 +324,8 @@ class FusedMoEConfig:
324
325 max_num_tokens: int = envs.VLLM_MOE_DP_CHUNK_SIZE
326
327 + has_bias: bool = False
328 +
329 def __post_init__(self):
330 if self.dp_size > 1:
331 logger.debug_once("Using FusedMoEConfig::max_num_tokens=%d",
@@ -413,7 +415,8 @@ class FusedMoEConfig:
415 in_dtype: torch.dtype,
416 max_num_tokens: int = envs.VLLM_MOE_DP_CHUNK_SIZE,
417 quant_config: Optional[Union[FusedMoEQuantConfig,
416 - QuantizationConfig]] = None
418 + QuantizationConfig]] = None,
419 + has_bias: bool = False,
420 ) -> "FusedMoEConfig":
421
422 _quant_config: Optional[FusedMoEQuantConfig] = None
@@ -482,4 +485,5 @@ class FusedMoEConfig:
485 in_dtype=in_dtype,
486 quant_config=_quant_config,
487 max_num_tokens=max_num_tokens,
488 + has_bias=has_bias,
489 )
vllm/model_executor/layers/fused_moe/fused_moe.py
+160 -109
@@ -275,6 +275,7 @@ def fused_moe_kernel(
275 a_ptr,
276 b_ptr,
277 c_ptr,
278 + b_bias_ptr,
279 a_scale_ptr,
280 b_scale_ptr,
281 topk_weights_ptr,
@@ -302,6 +303,8 @@ def fused_moe_kernel(
303 stride_bse,
304 stride_bsk,
305 stride_bsn,
306 + stride_bbe, # bias expert stride
307 + stride_bbn, # bias N stride
308 # Block size for block-wise quantization
309 group_n: tl.constexpr,
310 group_k: tl.constexpr,
@@ -317,6 +320,7 @@ def fused_moe_kernel(
320 use_int8_w8a8: tl.constexpr,
321 use_int8_w8a16: tl.constexpr,
322 per_channel_quant: tl.constexpr,
323 + HAS_BIAS: tl.constexpr,
324 ):
325 """
326 Implements the fused computation for a Mixture of Experts (MOE) using
@@ -414,7 +418,10 @@ def fused_moe_kernel(
418 else:
419 a_scale = tl.load(a_scale_ptr)
420 b_scale = tl.load(b_scale_ptr + off_experts)
417 -
421 + if HAS_BIAS:
422 + # bias shape: [num_experts, N]
423 + bias_ptrs = b_bias_ptr + off_experts * stride_bbe + offs_bn * stride_bbn
424 + bias = tl.load(bias_ptrs, mask=(offs_bn < N), other=0.0)
425 # -----------------------------------------------------------
426 # Iterate to compute a block of the C matrix.
427 # We accumulate into a `[BLOCK_SIZE_M, BLOCK_SIZE_N]` block
@@ -456,7 +463,8 @@ def fused_moe_kernel(
463 # Advance the ptrs to the next K block.
464 a_ptrs += BLOCK_SIZE_K * stride_ak
465 b_ptrs += BLOCK_SIZE_K * stride_bk
459 -
466 + if HAS_BIAS:
467 + accumulator = accumulator + bias[None, :]
468 if MUL_ROUTED_WEIGHT:
469 moe_weight = tl.load(topk_weights_ptr + offs_token,
470 mask=token_mask,
@@ -471,6 +479,7 @@ def fused_moe_kernel(
479 accumulator = (accumulator * a_scale * b_scale).to(compute_type)
480 else:
481 accumulator = accumulator.to(compute_type)
482 +
483 # -----------------------------------------------------------
484 # Write back the block of the output
485 offs_cn = pid_n * BLOCK_SIZE_N + tl.arange(0, BLOCK_SIZE_N)
@@ -499,7 +508,8 @@ def invoke_fused_moe_kernel(A: torch.Tensor,
508 use_int8_w8a16: bool,
509 use_int4_w4a16: bool,
510 per_channel_quant: bool,
502 - block_shape: Optional[list[int]] = None) -> None:
511 + block_shape: Optional[list[int]] = None,
512 + B_bias: Optional[torch.Tensor] = None) -> None:
513 assert topk_weights is not None or not mul_routed_weight
514 assert topk_weights is None or topk_weights.stride(1) == 1
515 assert sorted_token_ids.stride(0) == 1
@@ -531,7 +541,7 @@ def invoke_fused_moe_kernel(A: torch.Tensor,
541 A.size(0) * top_k * config['BLOCK_SIZE_M'])
542 grid = lambda META: (triton.cdiv(EM, META['BLOCK_SIZE_M']) * triton.cdiv(
543 B.size(1), META['BLOCK_SIZE_N']), )
534 -
544 + HAS_BIAS = B_bias is not None
545 if (use_int8_w8a16 or use_int4_w4a16) and \
546 block_shape is not None and block_shape[1] > 0:
547 assert B_scale is not None and B_scale.ndim == 3
@@ -611,6 +621,7 @@ def invoke_fused_moe_kernel(A: torch.Tensor,
621 A,
622 B,
623 C,
624 + B_bias,
625 A_scale,
626 B_scale,
627 topk_weights,
@@ -638,6 +649,8 @@ def invoke_fused_moe_kernel(A: torch.Tensor,
649 if B_scale is not None and B_scale.ndim == 3 else 0,
650 B_scale.stride(1)
651 if B_scale is not None and B_scale.ndim >= 2 else 0,
652 + B_bias.stride(0) if B_bias is not None else 0,
653 + B_bias.stride(1) if B_bias is not None else 0,
654 0 if block_shape is None else block_shape[0],
655 0 if block_shape is None else block_shape[1],
656 MUL_ROUTED_WEIGHT=mul_routed_weight,
@@ -647,6 +660,7 @@ def invoke_fused_moe_kernel(A: torch.Tensor,
660 use_int8_w8a8=use_int8_w8a8,
661 use_int8_w8a16=use_int8_w8a16,
662 per_channel_quant=per_channel_quant,
663 + HAS_BIAS=HAS_BIAS,
664 BLOCK_SIZE_K=BLOCK_SIZE_K,
665 **config,
666 )
@@ -1024,40 +1038,43 @@ def inplace_fused_experts(
1038 w2_zp: Optional[torch.Tensor] = None,
1039 a1_scale: Optional[torch.Tensor] = None,
1040 a2_scale: Optional[torch.Tensor] = None,
1027 - block_shape: Optional[List[int]] = None) -> None: #noqa: UP006
1041 + block_shape: Optional[List[int]] = None,
1042 + w1_bias: Optional[torch.Tensor] = None,
1043 + w2_bias: Optional[torch.Tensor] = None) -> None: #noqa: UP006
1044 fused_experts_impl(hidden_states, w1, w2, topk_weights, topk_ids, True,
1045 activation, is_act_and_mul,
1046 apply_router_weight_on_input, use_fp8_w8a8,
1047 use_int8_w8a8, use_int8_w8a16, use_int4_w4a16,
1048 use_mxfp4_w4a4, per_channel_quant, global_num_experts,
1049 expert_map, w1_scale, w2_scale, w1_zp, w2_zp, a1_scale,
1034 - a2_scale, block_shape)
1035 -
1036 -
1037 -def inplace_fused_experts_fake(
1038 - hidden_states: torch.Tensor,
1039 - w1: torch.Tensor,
1040 - w2: torch.Tensor,
1041 - topk_weights: torch.Tensor,
1042 - topk_ids: torch.Tensor,
1043 - activation: str = "silu",
1044 - is_act_and_mul: bool = True,
1045 - apply_router_weight_on_input: bool = False,
1046 - use_fp8_w8a8: bool = False,
1047 - use_int8_w8a8: bool = False,
1048 - use_int8_w8a16: bool = False,
1049 - use_int4_w4a16: bool = False,
1050 - use_mxfp4_w4a4: bool = False,
1051 - per_channel_quant: bool = False,
1052 - global_num_experts: int = -1,
1053 - expert_map: Optional[torch.Tensor] = None,
1054 - w1_scale: Optional[torch.Tensor] = None,
1055 - w2_scale: Optional[torch.Tensor] = None,
1056 - w1_zp: Optional[torch.Tensor] = None,
1057 - w2_zp: Optional[torch.Tensor] = None,
1058 - a1_scale: Optional[torch.Tensor] = None,
1059 - a2_scale: Optional[torch.Tensor] = None,
1060 - block_shape: Optional[list[int]] = None) -> None:
1050 + a2_scale, block_shape, w1_bias, w2_bias)
1051 +
1052 +
1053 +def inplace_fused_experts_fake(hidden_states: torch.Tensor,
1054 + w1: torch.Tensor,
1055 + w2: torch.Tensor,
1056 + topk_weights: torch.Tensor,
1057 + topk_ids: torch.Tensor,
1058 + activation: str = "silu",
1059 + is_act_and_mul: bool = True,
1060 + apply_router_weight_on_input: bool = False,
1061 + use_fp8_w8a8: bool = False,
1062 + use_int8_w8a8: bool = False,
1063 + use_int8_w8a16: bool = False,
1064 + use_int4_w4a16: bool = False,
1065 + use_mxfp4_w4a4: bool = False,
1066 + per_channel_quant: bool = False,
1067 + global_num_experts: int = -1,
1068 + expert_map: Optional[torch.Tensor] = None,
1069 + w1_scale: Optional[torch.Tensor] = None,
1070 + w2_scale: Optional[torch.Tensor] = None,
1071 + w1_zp: Optional[torch.Tensor] = None,
1072 + w2_zp: Optional[torch.Tensor] = None,
1073 + a1_scale: Optional[torch.Tensor] = None,
1074 + a2_scale: Optional[torch.Tensor] = None,
1075 + block_shape: Optional[list[int]] = None,
1076 + w1_bias: Optional[torch.Tensor] = None,
1077 + w2_bias: Optional[torch.Tensor] = None) -> None:
1078 pass
1079
1080
@@ -1246,36 +1263,38 @@ direct_register_custom_op(
1263
1264
1265 def outplace_fused_experts(
1249 - hidden_states: torch.Tensor,
1250 - w1: torch.Tensor,
1251 - w2: torch.Tensor,
1252 - topk_weights: torch.Tensor,
1253 - topk_ids: torch.Tensor,
1254 - activation: str = "silu",
1255 - is_act_and_mul: bool = True,
1256 - apply_router_weight_on_input: bool = False,
1257 - use_fp8_w8a8: bool = False,
1258 - use_int8_w8a8: bool = False,
1259 - use_int8_w8a16: bool = False,
1260 - use_int4_w4a16: bool = False,
1261 - use_mxfp4_w4a4: bool = False,
1262 - per_channel_quant: bool = False,
1263 - global_num_experts: int = -1,
1264 - expert_map: Optional[torch.Tensor] = None,
1265 - w1_scale: Optional[torch.Tensor] = None,
1266 - w2_scale: Optional[torch.Tensor] = None,
1267 - w1_zp: Optional[torch.Tensor] = None,
1268 - w2_zp: Optional[torch.Tensor] = None,
1269 - a1_scale: Optional[torch.Tensor] = None,
1270 - a2_scale: Optional[torch.Tensor] = None,
1271 - block_shape: Optional[List[int]] = None, #noqa: UP006
1266 + hidden_states: torch.Tensor,
1267 + w1: torch.Tensor,
1268 + w2: torch.Tensor,
1269 + topk_weights: torch.Tensor,
1270 + topk_ids: torch.Tensor,
1271 + activation: str = "silu",
1272 + is_act_and_mul: bool = True,
1273 + apply_router_weight_on_input: bool = False,
1274 + use_fp8_w8a8: bool = False,
1275 + use_int8_w8a8: bool = False,
1276 + use_int8_w8a16: bool = False,
1277 + use_int4_w4a16: bool = False,
1278 + use_mxfp4_w4a4: bool = False,
1279 + per_channel_quant: bool = False,
1280 + global_num_experts: int = -1,
1281 + expert_map: Optional[torch.Tensor] = None,
1282 + w1_scale: Optional[torch.Tensor] = None,
1283 + w2_scale: Optional[torch.Tensor] = None,
1284 + w1_zp: Optional[torch.Tensor] = None,
1285 + w2_zp: Optional[torch.Tensor] = None,
1286 + a1_scale: Optional[torch.Tensor] = None,
1287 + a2_scale: Optional[torch.Tensor] = None,
1288 + block_shape: Optional[List[int]] = None, #noqa: UP006
1289 + w1_bias: Optional[torch.Tensor] = None,
1290 + w2_bias: Optional[torch.Tensor] = None,
1291 ) -> torch.Tensor:
1292 return fused_experts_impl(
1293 hidden_states, w1, w2, topk_weights, topk_ids, False, activation,
1294 is_act_and_mul, apply_router_weight_on_input, use_fp8_w8a8,
1295 use_int8_w8a8, use_int8_w8a16, use_int4_w4a16, use_mxfp4_w4a4,
1296 per_channel_quant, global_num_experts, expert_map, w1_scale, w2_scale,
1278 - w1_zp, w2_zp, a1_scale, a2_scale, block_shape)
1297 + w1_zp, w2_zp, a1_scale, a2_scale, block_shape, w1_bias, w2_bias)
1298
1299
1300 def outplace_fused_experts_fake(
@@ -1300,7 +1319,9 @@ def outplace_fused_experts_fake(
1319 w2_zp: Optional[torch.Tensor] = None,
1320 a1_scale: Optional[torch.Tensor] = None,
1321 a2_scale: Optional[torch.Tensor] = None,
1303 - block_shape: Optional[list[int]] = None) -> torch.Tensor:
1322 + block_shape: Optional[list[int]] = None,
1323 + w1_bias: Optional[torch.Tensor] = None,
1324 + w2_bias: Optional[torch.Tensor] = None) -> torch.Tensor:
1325 return torch.empty_like(hidden_states)
1326
1327
@@ -1332,33 +1353,34 @@ def dispatch_fused_experts_func(inplace: bool) -> Callable[..., torch.Tensor]:
1353
1354 # TODO (bnell): replace this with modular op. Can get rid of inplace/outplace
1355 # torch ops.
1335 -def fused_experts(
1336 - hidden_states: torch.Tensor,
1337 - w1: torch.Tensor,
1338 - w2: torch.Tensor,
1339 - topk_weights: torch.Tensor,
1340 - topk_ids: torch.Tensor,
1341 - inplace: bool = False,
1342 - activation: str = "silu",
1343 - is_act_and_mul: bool = True,
1344 - apply_router_weight_on_input: bool = False,
1345 - use_fp8_w8a8: bool = False,
1346 - use_int8_w8a8: bool = False,
1347 - use_int8_w8a16: bool = False,
1348 - use_int4_w4a16: bool = False,
1349 - use_mxfp4_w4a4: bool = False,
1350 - per_channel_quant: bool = False,
1351 - global_num_experts: int = -1,
1352 - expert_map: Optional[torch.Tensor] = None,
1353 - w1_scale: Optional[torch.Tensor] = None,
1354 - w2_scale: Optional[torch.Tensor] = None,
1355 - w1_zp: Optional[torch.Tensor] = None,
1356 - w2_zp: Optional[torch.Tensor] = None,
1357 - a1_scale: Optional[torch.Tensor] = None,
1358 - a2_scale: Optional[torch.Tensor] = None,
1359 - block_shape: Optional[list[int]] = None,
1360 - allow_deep_gemm: bool = False,
1361 - allow_cutlass_block_scaled_grouped_gemm: bool = False) -> torch.Tensor:
1356 +def fused_experts(hidden_states: torch.Tensor,
1357 + w1: torch.Tensor,
1358 + w2: torch.Tensor,
1359 + topk_weights: torch.Tensor,
1360 + topk_ids: torch.Tensor,
1361 + inplace: bool = False,
1362 + activation: str = "silu",
1363 + is_act_and_mul: bool = True,
1364 + apply_router_weight_on_input: bool = False,
1365 + use_fp8_w8a8: bool = False,
1366 + use_int8_w8a8: bool = False,
1367 + use_int8_w8a16: bool = False,
1368 + use_int4_w4a16: bool = False,
1369 + use_mxfp4_w4a4: bool = False,
1370 + per_channel_quant: bool = False,
1371 + global_num_experts: int = -1,
1372 + expert_map: Optional[torch.Tensor] = None,
1373 + w1_scale: Optional[torch.Tensor] = None,
1374 + w2_scale: Optional[torch.Tensor] = None,
1375 + w1_zp: Optional[torch.Tensor] = None,
1376 + w2_zp: Optional[torch.Tensor] = None,
1377 + a1_scale: Optional[torch.Tensor] = None,
1378 + a2_scale: Optional[torch.Tensor] = None,
1379 + block_shape: Optional[list[int]] = None,
1380 + allow_deep_gemm: bool = False,
1381 + allow_cutlass_block_scaled_grouped_gemm: bool = False,
1382 + w1_bias: Optional[torch.Tensor] = None,
1383 + w2_bias: Optional[torch.Tensor] = None) -> torch.Tensor:
1384 # For now, disable DeepGemm for small N (<= 512) until better
1385 # permute/unpermute ops are available.
1386 # However, on B200, we use DeepGemm for all cases because they only support
@@ -1423,7 +1445,10 @@ def fused_experts(
1445 w2_zp=w2_zp,
1446 a1_scale=a1_scale,
1447 a2_scale=a2_scale,
1426 - block_shape=block_shape)
1448 + block_shape=block_shape,
1449 + w1_bias=w1_bias,
1450 + w2_bias=w2_bias,
1451 + )
1452
1453
1454 def fused_experts_impl(
@@ -1451,6 +1476,8 @@ def fused_experts_impl(
1476 a1_scale: Optional[torch.Tensor] = None,
1477 a2_scale: Optional[torch.Tensor] = None,
1478 block_shape: Optional[list[int]] = None,
1479 + w1_bias: Optional[torch.Tensor] = None,
1480 + w2_bias: Optional[torch.Tensor] = None,
1481 ) -> torch.Tensor:
1482 # Check constraints.
1483 if use_int4_w4a16:
@@ -1591,7 +1618,19 @@ def fused_experts_impl(
1618 use_int8_w8a16=use_int8_w8a16,
1619 use_int4_w4a16=use_int4_w4a16,
1620 per_channel_quant=per_channel_quant,
1594 - block_shape=block_shape)
1621 + block_shape=block_shape,
1622 + B_bias=w1_bias)
1623 +
1624 + # TODO fused kernel
1625 + def swiglu_oai(gate_up):
1626 + alpha = 1.702
1627 + limit = 7.0
1628 + gate, up = gate_up[..., ::2], gate_up[..., 1::2]
1629 + gate = gate.clamp(min=None, max=limit)
1630 + up = up.clamp(min=-limit, max=limit)
1631 + glu = gate * torch.sigmoid(gate * alpha)
1632 + gated_output = (up + 1) * glu
1633 + return gated_output
1634
1635 # Activation function with multiplication
1636 if activation == "silu" and is_act_and_mul:
@@ -1605,6 +1644,8 @@ def fused_experts_impl(
1644 intermediate_cache2 = F.silu(intermediate_cache1.view(-1, N))
1645 elif activation == "gelu":
1646 intermediate_cache2 = F.gelu(intermediate_cache1.view(-1, N))
1647 + elif activation == "swiglu_oai":
1648 + intermediate_cache2 = swiglu_oai(intermediate_cache1.view(-1, N))
1649 else:
1650 raise ValueError(f"Unsupported FusedMoe activation: {activation}, "
1651 f"with is_act_and_mul={is_act_and_mul}.")
@@ -1635,7 +1676,8 @@ def fused_experts_impl(
1676 use_int8_w8a16=use_int8_w8a16,
1677 use_int4_w4a16=use_int4_w4a16,
1678 per_channel_quant=per_channel_quant,
1638 - block_shape=block_shape)
1679 + block_shape=block_shape,
1680 + B_bias=w2_bias)
1681
1682 ops.moe_sum(intermediate_cache3.view(*intermediate_cache3.size()),
1683 out_hidden_states[begin_chunk_idx:end_chunk_idx])
@@ -1672,6 +1714,8 @@ def fused_moe(
1714 a1_scale: Optional[torch.Tensor] = None,
1715 a2_scale: Optional[torch.Tensor] = None,
1716 block_shape: Optional[list[int]] = None,
1717 + w1_bias: Optional[torch.Tensor] = None,
1718 + w2_bias: Optional[torch.Tensor] = None,
1719 ) -> torch.Tensor:
1720 """
1721 This function computes a Mixture of Experts (MoE) layer using two sets of
@@ -1766,7 +1810,9 @@ def fused_moe(
1810 w2_zp=w2_zp,
1811 a1_scale=a1_scale,
1812 a2_scale=a2_scale,
1769 - block_shape=block_shape)
1813 + block_shape=block_shape,
1814 + w1_bias=w1_bias,
1815 + w2_bias=w2_bias)
1816
1817
1818 class TritonExperts(mk.FusedMoEPermuteExpertsUnpermute):
@@ -1937,7 +1983,9 @@ class TritonExperts(mk.FusedMoEPermuteExpertsUnpermute):
1983 use_int8_w8a16=self.use_int8_w8a16,
1984 use_int4_w4a16=self.use_int4_w4a16,
1985 per_channel_quant=self.per_act_token_quant,
1940 - block_shape=self.block_shape)
1986 + block_shape=self.block_shape,
1987 + B_bias=None # TODO support B_bias
1988 + )
1989
1990 self.activation(activation, intermediate_cache2,
1991 intermediate_cache1.view(-1, N))
@@ -1948,26 +1996,29 @@ class TritonExperts(mk.FusedMoEPermuteExpertsUnpermute):
1996 intermediate_cache2, a2_scale, self.quant_dtype,
1997 self.per_act_token_quant, self.block_shape)
1998
1951 - invoke_fused_moe_kernel(qintermediate_cache2,
1952 - w2,
1953 - intermediate_cache3,
1954 - a2q_scale,
1955 - w2_scale,
1956 - w2_zp,
1957 - topk_weights,
1958 - sorted_token_ids,
1959 - expert_ids,
1960 - num_tokens_post_padded,
1961 - not apply_router_weight_on_input,
1962 - 1,
1963 - config,
1964 - compute_type=compute_type,
1965 - use_fp8_w8a8=self.use_fp8_w8a8,
1966 - use_int8_w8a8=self.use_int8_w8a8,
1967 - use_int8_w8a16=self.use_int8_w8a16,
1968 - use_int4_w4a16=self.use_int4_w4a16,
1969 - per_channel_quant=self.per_act_token_quant,
1970 - block_shape=self.block_shape)
1999 + invoke_fused_moe_kernel(
2000 + qintermediate_cache2,
2001 + w2,
2002 + intermediate_cache3,
2003 + a2q_scale,
2004 + w2_scale,
2005 + w2_zp,
2006 + topk_weights,
2007 + sorted_token_ids,
2008 + expert_ids,
2009 + num_tokens_post_padded,
2010 + not apply_router_weight_on_input,
2011 + 1,
2012 + config,
2013 + compute_type=compute_type,
2014 + use_fp8_w8a8=self.use_fp8_w8a8,
2015 + use_int8_w8a8=self.use_int8_w8a8,
2016 + use_int8_w8a16=self.use_int8_w8a16,
2017 + use_int4_w4a16=self.use_int4_w4a16,
2018 + per_channel_quant=self.per_act_token_quant,
2019 + block_shape=self.block_shape,
2020 + B_bias=None # TODO support B_bias
2021 + )
2022
2023 ops.moe_sum(intermediate_cache3, output)
2024
vllm/model_executor/layers/fused_moe/layer.py
+28 -12
@@ -255,7 +255,7 @@ class UnquantizedFusedMoEMethod(FusedMoEMethodBase, CustomOp):
255 self.fused_experts = fused_experts # type: ignore
256 self.topk_indices_dtype = None
257 self.moe = moe
258 -
258 + self.has_bias = self.moe.has_bias
259 self.rocm_aiter_moe_enabled = is_rocm_aiter_moe_enabled()
260 if self.rocm_aiter_moe_enabled:
261 from .rocm_aiter_fused_moe import rocm_aiter_fused_experts
@@ -291,7 +291,14 @@ class UnquantizedFusedMoEMethod(FusedMoEMethodBase, CustomOp):
291 requires_grad=False)
292 layer.register_parameter("w13_weight", w13_weight)
293 set_weight_attrs(w13_weight, extra_weight_attrs)
294 -
294 + if self.has_bias:
295 + w13_bias = torch.nn.Parameter(torch.zeros(
296 + num_experts,
297 + 2 * intermediate_size_per_partition,
298 + dtype=params_dtype),
299 + requires_grad=False)
300 + layer.register_parameter("w13_bias", w13_bias)
301 + set_weight_attrs(w13_bias, extra_weight_attrs)
302 # down_proj (row parallel)
303 w2_weight = torch.nn.Parameter(torch.empty(
304 num_experts,
@@ -301,6 +308,13 @@ class UnquantizedFusedMoEMethod(FusedMoEMethodBase, CustomOp):
308 requires_grad=False)
309 layer.register_parameter("w2_weight", w2_weight)
310 set_weight_attrs(w2_weight, extra_weight_attrs)
311 + if self.has_bias:
312 + w2_bias = torch.nn.Parameter(torch.zeros(num_experts,
313 + hidden_size,
314 + dtype=params_dtype),
315 + requires_grad=False)
316 + layer.register_parameter("w2_bias", w2_bias)
317 + set_weight_attrs(w2_bias, extra_weight_attrs)
318
319 def _maybe_pad_weight(self, weight: torch.Tensor) -> torch.Tensor:
320 # Pad the weight tensor. This is an optimization on ROCm platform, which
@@ -465,6 +479,8 @@ class UnquantizedFusedMoEMethod(FusedMoEMethodBase, CustomOp):
479 hidden_states=x,
480 w1=layer.w13_weight,
481 w2=layer.w2_weight,
482 + w1_bias=layer.w13_bias if self.has_bias else None,
483 + w2_bias=layer.w2_bias if self.has_bias else None,
484 topk_weights=topk_weights,
485 topk_ids=topk_ids,
486 inplace=True,
@@ -702,6 +718,7 @@ class FusedMoE(torch.nn.Module):
718 activation: str = "silu",
719 enable_eplb: bool = False,
720 num_redundant_experts: int = 0,
721 + has_bias: bool = False,
722 ):
723 super().__init__()
724 if params_dtype is None:
@@ -793,16 +810,15 @@ class FusedMoE(torch.nn.Module):
810 # since model_config is not set in the pytest test.
811 model_dtype = params_dtype
812
796 - moe = FusedMoEConfig.make(
797 - num_experts=self.global_num_experts,
798 - experts_per_token=top_k,
799 - hidden_dim=hidden_size,
800 - num_local_experts=self.local_num_experts,
801 - moe_parallel_config=self.moe_parallel_config,
802 - in_dtype=model_dtype,
803 - max_num_tokens=envs.VLLM_MOE_DP_CHUNK_SIZE,
804 - quant_config=quant_config,
805 - )
813 + moe = FusedMoEConfig.make(num_experts=self.global_num_experts,
814 + experts_per_token=top_k,
815 + hidden_dim=hidden_size,
816 + num_local_experts=self.local_num_experts,
817 + moe_parallel_config=self.moe_parallel_config,
818 + in_dtype=model_dtype,
819 + max_num_tokens=envs.VLLM_MOE_DP_CHUNK_SIZE,
820 + quant_config=quant_config,
821 + has_bias=has_bias)
822 self.moe_config = moe
823 self.quant_config = quant_config
824
vllm/model_executor/models/gpt_oss.py
+149 -3
@@ -160,7 +160,9 @@ class MLPBlock(torch.nn.Module):
160 renormalize=True,
161 quant_config=quant_config,
162 prefix=f"{prefix}.experts",
163 - apply_router_weight_on_input=False)
163 + apply_router_weight_on_input=False,
164 + has_bias=True,
165 + activation="swiglu_oai")
166
167 def forward(self, x: torch.Tensor) -> torch.Tensor:
168 t = self.norm(x)
@@ -262,8 +264,8 @@ class GptOssForCausalLM(nn.Module):
264 sampling_metadata)
265 return logits
266
265 - def load_weights(self, weights: Iterable[tuple[str,
266 - torch.Tensor]]) -> set[str]:
267 + def _load_weights_mxfp4(
268 + self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
269 rename_mapping = {
270 "self_attn": "attn",
271 "input_layernorm.weight": "attn.norm.weight",
@@ -469,3 +471,147 @@ class GptOssForCausalLM(nn.Module):
471 loaded_params.add(renamed_name)
472
473 return loaded_params
474 +
475 + def _load_weights_other(
476 + self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
477 + rename_mapping = {
478 + "self_attn": "attn",
479 + "input_layernorm.weight": "attn.norm.weight",
480 + "post_attention_layernorm.weight": "mlp.norm.weight",
481 + "embed_tokens": "embedding",
482 + }
483 +
484 + def maybe_rename(name: str) -> str:
485 + for remap_name, new_name in rename_mapping.items():
486 + if remap_name in name:
487 + return name.replace(remap_name, new_name)
488 + return name
489 +
490 + params_dict = dict(self.named_parameters())
491 + loaded_params: set[str] = set()
492 +
493 + tp_rank = get_tensor_model_parallel_rank()
494 + tp_size = get_tensor_model_parallel_world_size()
495 + intermediate_size = self.model_config.intermediate_size
496 +
497 + per_rank_intermediate_size = cdiv(intermediate_size, tp_size)
498 + # Calculate common slicing bounds for current rank
499 + tp_rank_start = tp_rank * per_rank_intermediate_size
500 + tp_rank_end = min((tp_rank + 1) * per_rank_intermediate_size,
501 + intermediate_size)
502 +
503 + # Attention heads per rank
504 + heads_per_rank = self.model_config.num_attention_heads // tp_size
505 + head_start = tp_rank * heads_per_rank
506 +
507 + use_ep = self.vllm_config.parallel_config.enable_expert_parallel
508 + ep_size = get_ep_group().world_size
509 + ep_rank = get_ep_group().rank
510 + num_experts = self.model_config.num_local_experts
511 + experts_per_rank = num_experts // ep_size
512 + ep_rank_start = ep_rank * experts_per_rank
513 + ep_rank_end = (ep_rank + 1) * experts_per_rank
514 +
515 + for name, weight in weights:
516 + if ".experts.gate_up_proj" in name and "bias" not in name:
517 + # Handle MLP gate and up projection weights
518 + new_name = name.replace(".experts.gate_up_proj",
519 + ".experts.w13_weight")
520 +
521 + # Extract gate and up projection parts
522 + # since the weight is shuffled, we can slice directly
523 + if use_ep:
524 + narrow_weight = weight[ep_rank_start:ep_rank_end, ...]
525 + else:
526 + narrow_weight = weight[:, :,
527 + 2 * tp_rank_start:2 * tp_rank_end]
528 +
529 + narrow_weight = narrow_weight.permute(0, 2, 1).contiguous()
530 + param = params_dict[new_name]
531 +
532 + param.copy_(narrow_weight)
533 + loaded_params.add(new_name)
534 +
535 + elif ".experts.down_proj" in name and "bias" not in name:
536 + # Handle MLP down projection weights
537 + new_name = name.replace(".experts.down_proj",
538 + ".experts.w2_weight")
539 +
540 + if use_ep:
541 + narrow_weight = weight[ep_rank_start:ep_rank_end, ...]
542 + else:
543 + narrow_weight = weight[:, tp_rank_start:tp_rank_end, :]
544 + narrow_weight = narrow_weight.permute(0, 2, 1).contiguous()
545 + param = params_dict[new_name]
546 +
547 + param.copy_(narrow_weight)
548 + loaded_params.add(new_name)
549 +
550 + elif "gate_up_proj_bias" in name:
551 + # Handle MLP gate and up projection biases
552 + new_name = name.replace("gate_up_proj_bias", "w13_bias")
553 +
554 + # Extract gate and up projection bias parts
555 + if use_ep:
556 + narrow_weight = weight[ep_rank_start:ep_rank_end, ...]
557 + else:
558 + narrow_weight = weight[:,
559 + 2 * tp_rank_start:2 * tp_rank_end]
560 +
561 + param = params_dict[new_name]
562 +
563 + param.copy_(narrow_weight)
564 + loaded_params.add(new_name)
565 +
566 + elif "down_proj_bias" in name:
567 + # Handle MLP down projection bias
568 + new_name = name.replace("down_proj_bias", "w2_bias")
569 +
570 + if use_ep:
571 + weight = weight[ep_rank_start:ep_rank_end, ...]
572 + else:
573 + # (only load on rank 0 to avoid duplication)
574 + if tp_rank != 0:
575 + weight.zero_()
576 + param = params_dict[new_name]
577 + param.copy_(weight)
578 + loaded_params.add(new_name)
579 + elif "sinks" in name:
580 + # Handle attention sinks (distributed across ranks)
581 + name = name.replace("self_attn", "attn")
582 + param = params_dict[name]
583 + narrow_weight = weight.narrow(0, head_start, heads_per_rank)
584 + param.data.copy_(narrow_weight)
585 + loaded_params.add(name)
586 + elif "q_proj" in name or "k_proj" in name or "v_proj" in name:
587 + shard_id = ("q" if "q_proj" in name else
588 + "k" if "k_proj" in name else "v")
589 + name = name.replace("self_attn", "attn")
590 + param_name = name.replace(f"{shard_id}_proj", "qkv")
591 + param = params_dict[param_name]
592 + weight_loader = param.weight_loader
593 + weight_loader(param, weight, loaded_shard_id=shard_id)
594 + loaded_params.add(param_name)
595 + else:
596 + # Handle all other weights with potential renaming
597 +
598 + renamed_name = maybe_rename(name)
599 + if renamed_name not in params_dict:
600 + continue
601 + param = params_dict[renamed_name]
602 + weight_loader = getattr(param, "weight_loader",
603 + default_weight_loader)
604 + weight_loader(param, weight)
605 + loaded_params.add(renamed_name)
606 +
607 + return loaded_params
608 +
609 + def load_weights(self, weights: Iterable[tuple[str,
610 + torch.Tensor]]) -> set[str]:
611 + quant_method = (self.model_config.quantization_config['quant_method']
612 + if hasattr(self.model_config, "quantization_config")
613 + else None)
614 + if quant_method == "mxfp4":
615 + return self._load_weights_mxfp4(weights)
616 + else:
617 + return self._load_weights_other(weights)