3232from torch import nn
3333from transformers .models .glm4_moe import Glm4MoeConfig
3434
35+ from vllm ._aiter_ops import rocm_aiter_ops
3536from vllm .compilation .decorators import support_torch_compile
3637from vllm .config import CacheConfig , VllmConfig , get_current_vllm_config
3738from vllm .distributed import (
@@ -168,7 +169,16 @@ def __init__(
168169 self .physical_expert_start + self .n_local_physical_experts
169170 )
170171
171- if config .n_shared_experts is not None :
172+ # AITER fused shared-expert (FSE) gate; mirrors the deepseek_v2.py
173+ # pattern (see Glm4MoE / FusedMoE wiring there).
174+ self .is_rocm_aiter_moe_enabled = rocm_aiter_ops .is_fused_moe_enabled ()
175+ self .is_fusion_moe_shared_experts_enabled = (
176+ rocm_aiter_ops .is_fusion_moe_shared_experts_enabled ()
177+ )
178+
179+ if config .n_shared_experts is None or self .is_fusion_moe_shared_experts_enabled :
180+ self .shared_experts = None
181+ else :
172182 intermediate_size = config .moe_intermediate_size * config .n_shared_experts
173183 self .shared_experts = Glm4MoeMLP (
174184 hidden_size = config .hidden_size ,
@@ -178,8 +188,6 @@ def __init__(
178188 reduce_results = False ,
179189 prefix = f"{ prefix } .shared_experts" ,
180190 )
181- else :
182- self .shared_experts = None
183191
184192 self .experts = FusedMoE (
185193 shared_experts = self .shared_experts ,
@@ -194,12 +202,18 @@ def __init__(
194202 topk_group = config .topk_group ,
195203 prefix = f"{ prefix } .experts" ,
196204 scoring_func = "sigmoid" ,
205+ # aiter applies routed_scaling_factor internally; see deepseek_v2.py.
197206 routed_scaling_factor = self .routed_scaling_factor ,
198- apply_routed_scale_to_output = True ,
207+ apply_routed_scale_to_output = not self . is_rocm_aiter_moe_enabled ,
199208 e_score_correction_bias = self .gate .e_score_correction_bias ,
200209 enable_eplb = self .enable_eplb ,
201210 num_redundant_experts = self .n_redundant_experts ,
202211 router_logits_dtype = torch .float32 ,
212+ n_shared_experts = (
213+ config .n_shared_experts
214+ if self .is_fusion_moe_shared_experts_enabled
215+ else None
216+ ),
203217 )
204218
205219 def forward (self , hidden_states : torch .Tensor ) -> torch .Tensor :
@@ -469,15 +483,25 @@ def forward(
469483 def get_expert_mapping (self ) -> list [tuple [str , str , int , str ]]:
470484 # Params for weights, fp8 weight scales, fp8 activation scales
471485 # (param_name, weight_name, expert_id, shard_id)
486+ # FSE widens the mapping by n_shared_experts slots; see deepseek_v2.py.
487+ num_experts = self .config .n_routed_experts
488+ if (
489+ rocm_aiter_ops .is_fusion_moe_shared_experts_enabled ()
490+ and self .config .n_shared_experts
491+ ):
492+ num_experts += self .config .n_shared_experts
472493 return fused_moe_make_expert_params_mapping (
473494 self ,
474495 ckpt_gate_proj_name = "gate_proj" ,
475496 ckpt_down_proj_name = "down_proj" ,
476497 ckpt_up_proj_name = "up_proj" ,
477- num_experts = self . config . n_routed_experts ,
498+ num_experts = num_experts ,
478499 )
479500
480501 def load_weights (self , weights : Iterable [tuple [str , torch .Tensor ]]) -> set [str ]:
502+ rocm_aiter_moe_shared_expert_enabled = (
503+ rocm_aiter_ops .is_fusion_moe_shared_experts_enabled ()
504+ )
481505 stacked_params_mapping = [
482506 # (param_name, shard_name, shard_id)
483507 ("qkv_proj" , "q_proj" , "q" ),
@@ -494,6 +518,11 @@ def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
494518 spec_layer = get_spec_layer_idx_from_weight_name (self .config , name )
495519 if spec_layer is not None :
496520 continue
521+
522+ is_fusion_moe_shared_experts_layer = (
523+ rocm_aiter_moe_shared_expert_enabled and ("mlp.shared_experts" in name )
524+ )
525+
497526 for param_name , weight_name , shard_id in stacked_params_mapping :
498527 # Skip non-stacked layers and experts (experts handled below).
499528 if weight_name not in name :
@@ -506,6 +535,8 @@ def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
506535 # for mlp.experts[0].gate_gate_up_proj, which breaks load.
507536 if ("mlp.experts." in name ) and name not in params_dict :
508537 continue
538+ if is_fusion_moe_shared_experts_layer :
539+ continue
509540
510541 name = name .replace (weight_name , param_name )
511542 # Skip loading extra bias for GPTQ models.
@@ -527,65 +558,109 @@ def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
527558 break
528559 else :
529560 is_expert_weight = False
530- for mapping in expert_params_mapping :
531- param_name , weight_name , expert_id , shard_id = mapping
532- if weight_name not in name :
533- continue
534-
535- # Anyway, this is an expert weight and should not be
536- # attempted to load as other weights later
537- is_expert_weight = True
538-
539- # Do not modify `name` since the loop may continue here
540- # Instead, create a new variable
541- name_mapped = name .replace (weight_name , param_name )
542-
543- if is_pp_missing_parameter (name_mapped , self ):
544- continue
545-
546- param = params_dict [name_mapped ]
547- # We should ask the weight loader to return success or not
548- # here since otherwise we may skip experts with other
549- # available replicas.
550- weight_loader = typing .cast (
551- Callable [..., bool ], param .weight_loader
552- )
553- success = weight_loader (
554- param ,
555- loaded_weight ,
556- name_mapped ,
557- shard_id = shard_id ,
558- expert_id = expert_id ,
559- return_success = True ,
560- )
561- if success :
562- name = name_mapped
563- break
564- else :
565- if is_expert_weight :
566- # We've checked that this is an expert weight
567- # However it's not mapped locally to this rank
568- # So we simply skip it
569- continue
570-
571- # Skip loading extra bias for GPTQ models.
572- if name .endswith (".bias" ) and name not in params_dict :
573- continue
574-
575- # Remapping the name of FP8 kv-scale.
576- name = maybe_remap_kv_scale_name (name , params_dict )
577- if name is None :
578- continue
579-
580- if is_pp_missing_parameter (name , self ):
581- continue
582-
583- param = params_dict [name ]
584- weight_loader = getattr (
585- param , "weight_loader" , default_weight_loader
561+
562+ # FSE: split a widened mlp.shared_experts tensor into
563+ # n_shared_experts chunks; see deepseek_v2.py for details.
564+ num_chunks = 1
565+ split_dim = 0
566+ chunk_size = 0
567+ if is_fusion_moe_shared_experts_layer :
568+ num_chunks = getattr (self .config , "n_shared_experts" , 1 ) or 1
569+ split_dim = (
570+ 1
571+ if ("down_proj.weight" in name and loaded_weight .ndim > 1 )
572+ else 0
586573 )
587- weight_loader (param , loaded_weight )
588- loaded_params .add (name )
574+ total = loaded_weight .shape [split_dim ]
575+ if total % num_chunks != 0 :
576+ raise ValueError (
577+ f"FSE shared-expert weight { name } has dim "
578+ f"{ total } along axis { split_dim } which is not "
579+ f"divisible by n_shared_experts={ num_chunks } ."
580+ )
581+ chunk_size = total // num_chunks
582+
583+ for j in range (num_chunks ):
584+ chunk_name = name
585+ weight_to_load = loaded_weight
586+
587+ if is_fusion_moe_shared_experts_layer :
588+ chunk_slice = slice (j * chunk_size , (j + 1 ) * chunk_size )
589+ if loaded_weight .ndim == 1 :
590+ weight_to_load = loaded_weight [chunk_slice ]
591+ elif split_dim == 0 :
592+ weight_to_load = loaded_weight [chunk_slice , :]
593+ else :
594+ weight_to_load = loaded_weight [:, chunk_slice ]
595+ # Synthesize an expert-style name for expert mapping.
596+ chunk_name = name .replace (
597+ "mlp.shared_experts" ,
598+ f"mlp.experts.{ self .config .n_routed_experts + j } " ,
599+ )
600+
601+ for mapping in expert_params_mapping :
602+ param_name , weight_name , expert_id , shard_id = mapping
603+ if weight_name not in chunk_name :
604+ continue
605+
606+ # Anyway, this is an expert weight and should not be
607+ # attempted to load as other weights later
608+ is_expert_weight = True
609+
610+ # Do not modify `name` since the loop may continue here
611+ # Instead, create a new variable
612+ name_mapped = chunk_name .replace (weight_name , param_name )
613+
614+ if is_pp_missing_parameter (name_mapped , self ):
615+ continue
616+
617+ param = params_dict [name_mapped ]
618+ # We should ask the weight loader to return success
619+ # or not here since otherwise we may skip experts
620+ # with other available replicas.
621+ weight_loader = typing .cast (
622+ Callable [..., bool ], param .weight_loader
623+ )
624+ success = weight_loader (
625+ param ,
626+ weight_to_load ,
627+ name_mapped ,
628+ shard_id = shard_id ,
629+ expert_id = expert_id ,
630+ return_success = True ,
631+ )
632+ if success :
633+ if not is_fusion_moe_shared_experts_layer :
634+ name = name_mapped
635+ else :
636+ loaded_params .add (name_mapped )
637+ break
638+ else :
639+ if is_expert_weight :
640+ # We've checked that this is an expert weight
641+ # However it's not mapped locally to this rank
642+ # So we simply skip it
643+ continue
644+
645+ # Skip loading extra bias for GPTQ models.
646+ if name .endswith (".bias" ) and name not in params_dict :
647+ continue
648+
649+ # Remapping the name of FP8 kv-scale.
650+ name = maybe_remap_kv_scale_name (name , params_dict )
651+ if name is None :
652+ continue
653+
654+ if is_pp_missing_parameter (name , self ):
655+ continue
656+
657+ param = params_dict [name ]
658+ weight_loader = getattr (
659+ param , "weight_loader" , default_weight_loader
660+ )
661+ weight_loader (param , loaded_weight )
662+ if name is not None and not is_fusion_moe_shared_experts_layer :
663+ loaded_params .add (name )
589664
590665 return loaded_params
591666
0 commit comments