执行摘要
- 一句话:为AMD平台Qwen3.5 MoE模型启用共享专家融合,减少内核启动以提升推理效率。
- 推荐动作:推荐工程师精读
can_fuse_shared_expert条件判断和权重映射逻辑,理解AMD特定优化路径;关注FP8兼容性为待办事项,可参考讨论中的技术权衡。
功能与动机
PR body指出:Qwen2 MoE和Qwen3.5 MoE模型使用共享专家,当shared_expert_intermediate_size == moe_intermediate_size时,可将共享专家与路由专家融合(topk+1),从而减少内核启动次数并提升推理效率。
实现拆解
- 环境检测与条件判断:在
python/sglang/srt/models/qwen2_moe.py中添加can_fuse_shared_expert函数,检查服务器参数(--disable-shared-experts-fusion)、配置属性(shared_expert_intermediate_size)和后端兼容性(DeepEP),确保融合仅在HIP平台且SGLANG_USE_AITER=1时启用。
- 专家计数与初始化扩展:
Qwen2MoeSparseMoeBlock初始化时新增support_shared_expert_fusion参数,计算num_fused_shared_experts,并更新top_k和num_experts以包含共享专家,影响后续MoE调度。
- 路由逻辑增强:
_forward_router_experts方法中,在top-k选择后调用_append_shared_to_topk_output将共享专家ID和权重追加到输出,实现单次调度中的融合前向。
- 权重加载适配:在
python/sglang/srt/models/qwen3_5.py中,Qwen3_5MoeForConditionalGeneration.load_weights方法使用_get_num_fused_shared_experts获取融合专家数,调整expert_params_mapping和fused_expert_params_mapping,将共享专家权重(如mlp.shared_expert.*)重映射到路由专家布局。
- 测试与配置配套:PR body包含准确性测试(GSM8K)和基准测试结果,但未直接修改测试文件;作者提及FP8精度问题需aiter升级,当前仅限BF16,后续PR将补全。
关键文件:
python/sglang/srt/models/qwen2_moe.py(模块 模型层;类别 source;类型 core-logic;符号 can_fuse_shared_expert, _get_shared_expert_weights, _append_shared_to_topk_output): 核心实现文件,新增共享专家融合判断函数并扩展MoE块逻辑,直接影响路由调度和专家计数。
python/sglang/srt/models/qwen3_5.py(模块 模型层;类别 source;类型 data-contract;符号 _get_num_fused_shared_experts): 权重加载适配文件,添加方法获取融合专家数量并更新映射逻辑,确保模型正确加载融合共享专家权重。
关键符号:can_fuse_shared_expert, _get_shared_expert_weights, _append_shared_to_topk_output, _get_num_fused_shared_experts
关键源码片段
python/sglang/srt/models/qwen2_moe.py
核心实现文件,新增共享专家融合判断函数并扩展MoE块逻辑,直接影响路由调度和专家计数。
def can_fuse_shared_expert(
config: PretrainedConfig,
) -> bool:
"""共享专家是否可作为额外MoE专家融合(Qwen3.5 + Aiter)。
调用方仍需基于`support_shared_expert_fusion`和`_use_aiter`门控。
"""
if (
get_global_server_args().disable_shared_experts_fusion is True # 服务器参数禁用融合
or getattr(config, "shared_expert_intermediate_size", 0) <= 0 # 配置未定义共享专家尺寸
or config.shared_expert_intermediate_size != config.moe_intermediate_size # 尺寸不匹配
or get_moe_a2a_backend().is_deepep() # 后端为DeepEP时不兼容
):
return False
return True
def _append_shared_to_topk_output(
self,
topk_output: StandardTopKOutput,
shared_expert_weights: torch.Tensor,
) -> StandardTopKOutput:
"""将共享专家追加到top-k输出,用于融合调度。
共享专家ID设为`self.num_experts`(即基础专家数),权重来自sigmoid(gate)。
"""
shared_expert_ids = torch.full_like(
topk_output.indices[:, :1],
self.num_experts,
device=topk_output.indices.device,
)
# 扩展indices和weights以包含共享专家
new_indices = torch.cat([topk_output.indices, shared_expert_ids], dim=-1)
new_weights = torch.cat([topk_output.weights, shared_expert_weights], dim=-1)
return StandardTopKOutput(new_indices, new_weights)
python/sglang/srt/models/qwen3_5.py
权重加载适配文件,添加方法获取融合专家数量并更新映射逻辑,确保模型正确加载融合共享专家权重。
def _get_num_fused_shared_experts(self):
"""获取融合共享专家的数量,用于权重加载时调整专家计数。
通过检查首层MLP的`num_fused_shared_experts`属性实现;若未启用则返回0。
"""
if not (
hasattr(self.model, "layers")
and len(self.model.layers) > 0
and hasattr(self.model.layers[0].mlp, "num_fused_shared_experts")
):
return 0
return self.model.layers[0].mlp.num_fused_shared_experts
def load_weights(self, weights: Iterable[Tuple[str, torch.Tensor]]):
"""加载权重,适配融合共享专家布局。
当启用融合时,`num_experts`增加`num_fused_shared_experts`,并将共享专家权重映射到路由专家索引。
"""
num_experts_base = self.config.num_experts
num_fused_shared_experts = self._get_num_fused_shared_experts()
num_experts = num_experts_base + num_fused_shared_experts # 调整总专家数
# 构建专家参数映射,包含基础专家和融合共享专家
expert_params_mapping = FusedMoE.make_expert_params_mapping(
ckpt_gate_proj_name="gate_proj",
ckpt_down_proj_name="down_proj",
ckpt_up_proj_name="up_proj",
num_experts=num_experts, # 使用调整后的专家数
)
# 处理共享专家权重重映射
if self.enable_shared_expert_fusion:
for name, loaded_weight in weights:
if f"mlp.shared_expert.gate_up_proj" in name:
# 将共享专家gate_up_proj拆分为gate和up,加载到对应专家索引
loaded_weight = loaded_weight.chunk(2, dim=-2)
weight_loader(param, loaded_weight[0], name_mapped, "w1", num_experts_base)
weight_loader(param, loaded_weight[1], name_mapped, "w3", num_experts_base)
elif f"mlp.shared_expert.down_proj" in name:
weight_loader(param, loaded_weight, name_mapped, "w2", num_experts_base)
评论区精华
- 变量名一致性:yichiche评论“Is there a specific reason for changing the name from router_logits to gate_logits? If not, we should keep it as is to avoid unnecessary changes.”,强调保持代码一致性以避免混淆。
- 日志清理:yichiche多次指出“We should remove this logging code from the production environment.”,要求移除调试日志以提升代码整洁性。
- DeepEP兼容性:yichiche建议“Add
or get_moe_a2a_backend().is_deepep() to check if deepep is enable.”,防止融合逻辑与DeepEP后端冲突,已整合到can_fuse_shared_expert中。
- 权重映射细节:yichiche询问“You may also need fused_expert_params_mapping here?”,zhentaocc解释共享专家通过现有逻辑映射,无需额外条目。
-
FP8问题与后续工作:zhentaocc提到“FP8 accuracy issue identified, will need aiter upgrade to fix split_k issue.”,指出当前PR限BF16,FP8支持需后续PR解决,并计划扩展共享gate融合。
-
变量名一致性与日志清理 (style): 作者可能已调整变量名,日志代码在后续提交中被移除,强调代码整洁性和一致性。
- DeepEP后端兼容性检查 (correctness): 检查已整合到can_fuse_shared_expert函数中,确保在DeepEP启用时禁用融合。
- 权重映射逻辑适配 (design): 权重加载已适配,共享专家权重被重映射到路由专家布局,设计决策基于现有架构扩展。
- FP8精度问题与后续工作 (correctness): FP8支持被识别为待办事项,作者计划后续PR解决,不影响当前BF16功能。
风险与影响
- 风险:- 回归风险:融合条件依赖
SGLANG_USE_AITER环境变量和HIP后端,若配置不当或平台不支持,可能导致MoE层行为异常或性能退化;权重加载改动涉及共享专家重映射,错误处理可能引发模型加载失败。
- 性能风险:融合逻辑增加初始化复杂度,但在启用时减少内核启动,实测吞吐量提升;未启用时对性能无影响。
- 兼容性风险:对FP8/MXFP4量化支持不完整,作者提及精度问题,需后续aiter升级,可能影响量化模型用户。
- 安全风险:无直接安全漏洞,但新代码分支需验证边界条件,防止配置错误导致服务中断。
- 影响:- 用户影响:AMD ROCm用户通过设置
SGLANG_USE_AITER=1可自动启用融合,获得约5%吞吐量提升(基准测试显示BF16下输出token吞吐量从767.82 tok/s增至802.79 tok/s),且准确性测试显示精度变化可忽略(GSM8K exact_match差异<0.003)。
- 系统影响:优化MoE层调度,减少GPU内核启动次数,提升整体推理效率;权重加载逻辑扩展支持融合布局,增强模型兼容性。
- 团队影响:需跟进FP8支持和共享gate融合等后续工作,可能推动AMD平台优化和MoE模块演进。
- 风险标记:条件依赖环境变量, FP8支持不完整, 权重加载复杂化
关联脉络
- PR #21773 [AMD][CI] Add GLM-5-MXFP4 accuracy and perf nightly tests for MI35x: 同为AMD平台相关PR,涉及CI测试扩展,可辅助理解AMD环境验证背景。
- PR #22606 [serving] replace O(n²) stream_buffer string concat with integer offset: 同为性能优化PR,共享减少计算开销的目标,可对比学习性能改进策略。
- PR #21232 [sgl] perf optimization for eplb: 涉及算法性能优化,与本PR的MoE层效率提升主题相关,反映团队持续性能优化趋势。
参与讨论