当前位置: 首页 > article >正文

深度 | 昇腾NPU MoE算子实现:从TopKGating到Expert并行,稀疏激活的硬件适配

引言MoEMixture of Experts混合专家是大模型近年来最重要的架构演进之一。GPT-4、Mixtral-8×7B、Qwen1.5-MoE——几乎所有宣称超大规模的新模型都在用 MoE。核心逻辑很简单用多个独立的专家网络替代一个巨大的前馈网络每次只激活其中少数几个——用更少的计算量换取更大的模型容量。然而MoE 在硬件上实现起来一点都不简单。标准 MoE 有两个关键操作**Gating门控路由**决定每个 token 分配给哪些专家Expert 并行让多个专家在多张卡上并行计算。这两个操作都涉及到跨卡通信在昇腾 NPU 上需要用 HCCL昇腾集合通信库来实现。ops-transformer 仓库实现了专门针对昇腾 NPU 优化的 MoE 算子TopKGating 路由算法、Expert 并行调度、以及一个融合了 GateMatMulSoftmaxTopK 的单 kernel 实现。这篇文章深入解析这些算子的实现逻辑和调优方法。MoE 的计算模型与硬件瓶颈先说清楚 MoE 为什么在硬件上难优化。标准 Transformer 的 FFN 层每输入一个 token都会完整地过一遍两个线性层FFN(x) W2 * gelu(W1 * x)参数数量和计算量都是固定的跟输入 token 无关。MoE 把 FFN 层换成多个并行的专家网络MoE(x) Σᵢ softmax(Aᵢ·x)ᵢ · Expertᵢ(x)其中 Gating 网络 A 输出每个 expert 的权重SoftmaxTopK 选出权重最高的 K 个专家只激活这些 expert 的计算。被选中的 expert 接收相同的输入 x各自独立计算后用各自的权重做加权求和。以 Mixtral-8×7B 为例8 个专家每次只激活 2 个。模型总参数量约 46.7B但每次前向传播只实际计算 2/8 25% 的参数——这叫稀疏激活。理论计算量是 Dense 模型的 25%但模型容量相当于 46.7B 参数的 Dense 模型。稀疏激活节省计算但引入了两个新问题问题一负载不均衡。如果某个 expert 被所有 token 都选中它的计算量是其他 expert 的几十倍整个系统被拖慢。Gating 的质量直接决定负载均衡。问题二跨卡通信。当 experts 分布在不同 NPU 上时路由结果需要 All-to-All 通信——每个 NPU 都要把自己的 token 发给持有对应 expert 的 NPU数据要跨卡搬运。HCCL 的 All-to-All 带宽是瓶颈。ops-transformer 的 MoE 算子针对这两个问题都做了优化。TopKGating 的实现TopKGating 是 MoE 最核心的操作给定输入 x 和 expert 数量 N输出权重最高的 K 个 expert 的索引和对应的分数。朴素实现要算 N 个 expert 的分数然后排序取 TopK# 朴素 Gating 实现 def naive_topk_gating(x, num_experts, topk): # x: (batch_size, hidden_dim) # 输出: topk_indices, topk_weights all_scores [] for i in range(num_experts): score x W_gate[i] # (batch_size, 1) all_scores.append(score) all_scores torch.cat(all_scores, dim1) # (batch_size, num_experts) weights torch.softmax(all_scores, dim1) topk_weights, topk_indices torch.topk(weights, topk, dim1) return topk_indices, topk_weights这个实现有两个问题循环 N 次做矩阵乘法效率低排序 O(N log N) 在 expert 数量多时开销大。ops-transformer 的 TopKGating 做了两件事优化第一用单次矩阵乘法代替循环。把所有 expert 的门控向量拼接成一个大矩阵 W_gate_all形状是(hidden_dim, num_experts)。一次矩阵乘法x W_gate_all算出所有 expert 的分数避免 N 次独立计算。第二用 PartialSort 代替全排序。不是排序所有 N 个分数而是用 nth_element 这样的局部排序算法只找到 TopK 的位置——复杂度从 O(N log N) 降到 O(N K log K)。以下是 Ascend C 的核心实现// TopKGating 实现 // 文件位置ops-transformer/ops/moe/topk_gating.cpp template typename T __aicore__ void TopKGatingKernel( GlobalTensorT input, // (total_tokens, hidden_dim) GlobalTensorT gating_weight, // (num_experts, hidden_dim) 拼接的门控矩阵 GlobalTensorint topk_indices, // (total_tokens, topk) TopK expert 索引 GlobalTensorT topk_weights, // (total_tokens, topk) TopK expert 权重 GlobalTensorint expert_counts,// (num_experts) 每个 expert 被选中的次数用于负载均衡 const int num_experts, const int topk, const int capacity_per_expert // 每个 expert 每次能处理的最大 token 数 ) { // 1. 一次性计算所有 expert 的分数 // input: (total_tokens, hidden_dim) // gating_weight: (hidden_dim, num_experts) // scores: (total_tokens, num_experts) LocalTensorT scores AllocateL1(total_tokens * num_experts); MatMulT(scores, input, gating_weight); // Cube 单次计算所有 expert 分数 // 2. Softmax按行 // 每个 token 的 num_experts 个分数归一化 SoftmaxAxis1(scores); // inplace按 dim1 做 softmax // 3. Partial TopK局部排序只找 TopK不全排序 // 对每个 token找出分数最高的 K 个 expert for (uint32_t t 0; t total_tokens; t) { LocalTensorT score_row scores[t]; // nth_elementO(num_experts) 找到第 K 大的元素位置 // 比完整排序 O(num_experts log num_experts) 快 T pivot NthElement(score_row, topk - 1); // 找到第 topk 大的值 // 4. 收集 TopK 的索引和权重 uint32_t write_idx 0; for (uint32_t e 0; e num_experts; e) { if (score_row[e] pivot) { topk_indices[t][write_idx] e; topk_weights[t][write_idx] score_row[e]; AtomicAdd(expert_counts[e], 1); // 原子加统计负载 write_idx; if (write_idx topk) break; } } } // 5. 负载均衡检查 // 如果某个 expert 被选中的次数超过 capacity_per_expert丢弃多余的 token for (uint32_t e 0; e num_experts; e) { if (expert_counts[e] capacity_per_expert) { // 该 expert 超载按权重从低到高丢弃 token TruncateTopKForExpert(e, capacity_per_expert); } } }Expert 并行与 All-to-All 通信MoE 的第二个关键操作是 Expert 计算每个 token 被路由到对应的 expert 后需要把 token 发送到持有该 expert 的 NPU 上计算完成后结果再发回来。这个过程叫 All-to-All 通信——每个 NPU 既发送者也接收者数据量相同。在昇腾 NPU 上这个操作用 HCCL 实现# Expert 并行通信简化版 import torch from torch.distributed import init_process_group import torch_npu.nccl as nccl def moe_alltoall(token_input, expert_ids, num_experts): # token_input: (total_tokens, hidden_dim) # expert_ids: (total_tokens) 每个 token 归属的 expert ID # 1. 按 expert 分桶把相同 expert 的 token 聚合到一起 buckets {} for i, eid in enumerate(expert_ids): if eid not in buckets: buckets[eid] [] buckets[eid].append(i) # 2. 准备每个 expert 的输入聚合后的数据 expert_inputs {} for eid, indices in buckets.items(): # 收集所有发往 expert eid 的 token expert_inputs[eid] token_input[indices] # 3. All-to-All 发送 # 假设 experts 均匀分布在 world_size 个 NPU 上 # 每个 NPU 发送 total_tokens/world_size 个 token send_counts [len(buckets.get(i, [])) for i in range(num_experts)] recv_counts send_counts # All-to-All总发送数 总接收数 # HCCL All-to-All output torch.empty_like(token_input) torch.distributed._all_to_all_single( output, token_input, send_countssend_counts, recv_countsrecv_counts, groupnccl.clique ) return outputAll-to-All 的瓶颈在于 HCCL 的带宽延迟。以下是不同硬件规模下的 All-to-All 实测数据每 NPU 发送 8192 个 token每个 token 4096 维硬件规模通信耗时 (ms)通信占比单 NPU无通信0.80%2 NPU1.233%4 NPU1.958%8 NPU3.476%8 NPU 场景下通信时间占了前向传播的 76%。这是 MoE 并行的固有挑战——专家数量越多跨卡通信越频繁。ops-transformer 有一个针对这个问题的优化将 Expert 计算和 All-to-All 通信重叠Overlap。用 Double Buffer 流水线在 Expert 计算当前批次 token 的同时预处理下一批次的 token 路由——把通信和计算的时间叠加起来减少空闲等待。融合 MoE Kernelops-transformer 提供的最核心的优化是一个融合了 GateMatMulSoftmaxTopKDispatch 的单 kernel 实现。融合的核心收益是减少中间结果的 HBM 访问// 融合 MoE 前向 kernel // 文件位置ops-transformer/ops/moe/fused_moe_forward.cpp template typename T __aicore__ void FusedMoEForwardKernel( GlobalTensorT input, // (total_tokens, hidden_dim) GlobalTensorT gate_weight, // (num_experts, hidden_dim) GlobalTensorT experts_weight, // (num_experts, expert_dim, hidden_dim) GlobalTensorT output, // (total_tokens, hidden_dim) GlobalTensorint expert_counts, // (num_experts) 负载统计 const int num_experts, const int topk, const int capacity_per_expert ) { // Phase 1: GatingL1 计算 // 所有 expert 的门控分数一次性算出 LocalTensorT scores AllocateL1(total_tokens * num_experts); MatMul(scores, input, gate_weight); // Cube: (B, H) (H, E) → (B, E) Softmax(scores); // 按 token 维度归一化 // Phase 2: TopK 路由L1 计算 LocalTensorint topk_idx AllocateL1(total_tokens * topk); LocalTensorT topk_wt AllocateL1(total_tokens * topk); PartialTopK(scores, topk_idx, topk_wt, topk); // 局部排序 // Phase 3: Token DispatchL1 计算 // 按 expert 分桶把 token 排列成 expert 输入格式 // 这里完成 token 的重排序为 All-to-All 做准备 LocalTensorT dispatch_buf AllocateL1(total_tokens * hidden_dim); ReorderByExpert(dispatch_buf, input, topk_idx, topk_wt); // Phase 4: Expert 计算Cube 流水线 // 每个 expert 的 FFN 计算融合 MatMul SiLU MatMul for (int e 0; e num_experts; e) { LocalTensorT expert_in GetExpertInput(dispatch_buf, e); LocalTensorT gate_out Allocate ...(truncated)...总结本文深入解析了昇腾 NPU 上 MoE混合专家算子的实现与优化重点围绕TopKGating 路由和Expert 并行两大核心操作展开。核心优化TopKGating 优化使用单次矩阵乘法替代循环一次性计算所有 expert 的分数避免重复计算。采用PartialTopK局部排序代替全排序将复杂度从 O(N log N) 降至 O(N K log K)显著提升路由效率。Expert 并行与通信优化使用HCCL实现跨 NPU 的 All-to-All 通信并实测了通信耗时随硬件规模扩大的增长趋势8 NPU 时通信占比达 76%。提出Overlap 策略通过 Double Buffer 流水线将 Expert 计算与通信重叠减少等待时间。融合 MoE Kernel将 Gate、MatMul、Softmax、TopK、Dispatch 等操作融合为单 kernel使中间结果全程在 L1 缓存中流转HBM 访问从 O(7N) 降至 O(3)。实测融合算子在 Mixtral-8×7B 规模下可达3.61 倍加速。实操建议topk 参数选择建议从topk2开始根据实际负载均衡情况调整。topk 越大计算量越大但负载均衡越好。性能对比文章提供了完整的 Python 调用示例和 benchmark 代码方便开发者直接测试性能提升。适用场景本文内容适合需要在昇腾 NPU 上部署 MoE 架构模型如 Mixtral、Qwen-MoE 等的开发者尤其关注性能优化与通信效率的场景。仓库地址https://atomgit.com/cann/ops-transformer

相关文章:

深度 | 昇腾NPU MoE算子实现:从TopKGating到Expert并行,稀疏激活的硬件适配

引言 MoE(Mixture of Experts,混合专家)是大模型近年来最重要的架构演进之一。GPT-4、Mixtral-87B、Qwen1.5-MoE——几乎所有宣称"超大规模"的新模型都在用 MoE。核心逻辑很简单:用多个独立的"专家"网络替代…...

从零到亿级调用量:电商客服Agent重构实录(含对话状态机+意图跳转图+人工接管SLA协议)

更多请点击: https://codechina.net 第一章:从零到亿级调用量:电商客服Agent重构实录(含对话状态机意图跳转图人工接管SLA协议) 面对日均峰值超1.2亿次的客服请求,原有基于规则匹配的客服Bot在大促期间频繁…...

从电路振荡到种群竞争:常系数线性微分方程组在建模中的实战指南

从电路振荡到种群竞争:常系数线性微分方程组在建模中的实战指南微分方程是描述动态系统的数学语言,而常系数线性微分方程组则是其中最具工程实用价值的一类。不同于纯数学视角下的求解技巧,本文将带你穿越两个经典场景——电子工程中的RLC振荡…...

用Python处理DREAMER脑电数据集:从.mat文件到.npy文件的完整实战教程

用Python处理DREAMER脑电数据集:从.mat文件到.npy文件的完整实战教程在情感计算与神经科学交叉领域,DREAMER数据集因其同时包含脑电信号(EEG)和情感评分而备受研究者青睐。但原始数据以.mat格式存储,这种MATLAB专属格式…...

《Java 基础必学:ArrayList、HashMap 和泛型详解》

一、引言 1.为什么这些是 Java 基础的重点? ArrayList、HashMap 和泛型是Java集合框架的核心组成部分,广泛应用于实际开发中。 ArrayList:基于动态数组实现,支持快速随机访问,适合频繁查询和遍历的场景。HashMap&…...

数据标注中的权力博弈与主观性:从规则制定到模型偏见的全链路解析

1. 项目概述:当数据标注不再是“客观”的技术活“数据标注”,在很多人眼里,可能就是一个坐在电脑前,对着图片画框、打标签的“体力活”或“技术活”。它听起来中立、客观,是人工智能模型训练前一道标准化的工序。然而&…...

市面上靠谱的ERP/MES/定制开发/APP开发/软件开发公司

在数字化浪潮下,80%的实体企业都想通过ERP、MES或定制软件实现降本增效,但选对服务商比“买系统”更重要——用模板化系统的企业,70%会因为流程适配差、运维跟不上而半途而废;找外包开发的企业,又面临“开发完就甩手”…...

从需求到交付:深度拆解企业级软件定制开发的标准化流程

一、 引言:数字化转型的“标准化”与“定制化”博弈(内容概要:简述当前企业在选购通用SaaS软件与定制软件时的痛点。指出通用软件往往“大而全但难用”,而定制开发的核心在于精准契合业务场景。)二、 定制开发的四大核…...

RuoYi接口调试:Postman作为Spring Boot权限系统可信信使

1. 为什么RuoYi项目里Postman不是“配角”,而是调试生命线在RuoYi开发实战中,很多人把Postman当成一个“临时工具”——写完接口顺手点一下,成功了就扔一边,失败了就切回IDE疯狂加日志、重启服务、反复试错。我带过三届实习生&…...

同事还在手动整理文件,我已经让 Open Claw 全自动搞定了|Windows 一键部署

⚡OpenClaw 一键安装包|一键部署,告别复杂环境配置⚡ 适配系统 Windows10/11 64 位 当前版本 2.7.5 版本(虾壳云版) 核心优势 全程可视化操作,无需命令行、无需手动配置 Python/Node.js,内置所有运行…...

虚幻引擎Pak文件可视化分析工具原理与实践

1. 为什么一个Pak文件查看器值得花两周重写三遍?虚幻引擎项目打包后生成的.pak文件,对绝大多数开发者来说就是个“黑盒”——你清楚它装着所有资源:贴图、音频、蓝图、关卡数据,甚至UAsset序列化后的二进制结构;但你完…...

1000个文件重命名,1秒完成!批量文件重命名软件

前言: 大家好,这里是惠众资料库, 在日常办公、资料归档、素材整理、摄影剪辑等各类场景中,用户会积累大量图片、文档、视频、音频、文件夹等各类文件。为了实现文件分类规整、统一命名规范、方便快速检索调用,文件重命…...

计算机视觉与贝叶斯优化驱动的粉末饮料智能制备系统

1. 项目概述:从“冲一杯”到“冲好一杯”的自动化跃迁“机器人结合计算机视觉与贝叶斯优化实现粉末饮料制备自动化”,这个标题听起来有点学术,但说白了,我们做的就是把冲奶粉、泡蛋白粉、调咖啡这类“凭感觉”的手工活&#xff0c…...

【Lovable高阶开发者私藏技巧】:绕过平台限制实现自定义CSS/JS注入与第三方SDK深度对接

更多请点击: https://kaifayun.com 第一章:Lovable无代码开发教程 Lovable 是一款面向业务人员与轻量级开发者的可视化应用构建平台,它通过拖拽式界面、逻辑编排画布和内置数据连接器,将复杂功能封装为可复用的模块。无需编写传统…...

鸿蒙electron跨端框架PC导出管家实战:把交付前的检查、复制和导出做成一个工坊

前言 欢迎加入鸿蒙PC开发者社区,共同打造开发者工具生态:鸿蒙PC开发者社区 :https://harmonypc.csdn.net/ 项目开源地址:https://AtomGit.com/lqjmac/ele-daochuguanjia 我做 导出管家 时最先确认的,不是颜色和布局…...

Hexo 排坑记:删除所有文章后首页无法访问(Cannot GET)

背景 最近在使用 Hexo Butterfly 主题搭建个人博客时,遇到一个奇怪的问题:我把 source/_posts 下的所有文章都删掉后,重新生成并启动本地服务器,访问 http://localhost:4000 竟然直接显示 Cannot GET /,首页完全打不开…...

前端实习面试手写题分享

在寻找前端实习的过程中,我们会发现,面试除了考察算法题之外,手写题同样也是高频考点。尤其是在准备中大厂前端面试时,手写能力几乎是必不可少的一部分。这篇文章将围绕几道经典高频手写题展开,包括手写深拷贝、实现寄…...

2026年5月4日 OCS技术方案路线选择与优劣深度调研报告

OCS技术方案路线选择与优劣深度调研报告 核心结论 光电路交换(OCS)正从Google的"独家方案"演变为AI算力网络的通用基础设施。Google TPU v8i采用的Boardfly架构首次将OCS引入大规模MoE推理场景,标志着OCS应用从训练侧向推理侧的跨…...

别再死记ResNet结构了!用Python手搓一个ResUnet,从代码里真正搞懂残差连接

从零实现ResUnet:用Python代码彻底理解残差连接的本质在计算机视觉领域,图像分割一直是极具挑战性的任务之一。传统的U-Net架构因其独特的编码器-解码器结构和跳跃连接而广受欢迎,但随着网络深度的增加,性能提升却遇到了瓶颈。这时…...

从纸质报表到Excel:PaddleOCR+Python自动化识别复杂表格(附完整代码)

金融表格自动化革命:用PaddleOCRPython实现纸质报表秒转Excel每次月末结算时,财务部的张经理总要面对堆积如山的纸质报表——供应商对账单、银行流水单、税务申报表,这些表格往往带有手写注释、合并单元格和模糊印章。传统的人工录入不仅耗时…...

保姆级教程:用Arbe或大陆4D毫米波雷达点云数据,手把手实现Freespace检测(附Python伪代码)

毫米波雷达点云实战:从数据到可行驶区域的完整工程指南在自动驾驶感知系统中,可行驶区域检测(Freespace)直接决定了车辆路径规划的可行空间边界。相比激光雷达和摄像头方案,4D毫米波雷达凭借全天候工作能力、成本优势和…...

别再为医学影像格式发愁了!3D Slicer 5.x 保姆级数据导入与格式转换指南

医学影像处理实战:3D Slicer 5.x全格式兼容指南与高效工作流医学影像研究的第一步往往就卡在数据导入环节——当你从医院PACS系统拿到DICOM序列,从合作方收到NRRD压缩包,或是下载公开数据集的NIFTI文件时,3D Slicer中那些灰色的&q…...

AI赋能科学教育:个性化学习与交互式模拟的技术实践

1. 项目概述:当AI遇见科学课堂作为一名在教育科技领域摸爬滚打了十多年的从业者,我亲眼见证了从幻灯片到在线视频,再到如今AI技术涌入课堂的整个历程。最近,我和团队深度参与了一个名为“AI赋能科学教育”的项目,这不仅…...

储能 PACK 与 BMS:怎么识别有真实出货的系统集成厂,避开组装贴牌

储能赛道的门槛看起来不高:买一批电芯,叫几家代工厂组装成 PACK,挂上自己的品牌,就能对外声称是"储能系统集成商"。这条路在 2021 年到 2024 年的行业高速期被走通过无数次。于是,有真实产线、真实并网项目、…...

神经纹理:让3D世界“活”起来的AI魔法,一篇讲透!

神经纹理:让3D世界“活”起来的AI魔法,一篇讲透! 引言:从“贴图”到“思考”的纹理革命 想象一下,一个虚拟角色不仅能动,其皮肤还能随着情绪微微泛红、在阳光下呈现真实的汗渍光泽——这不再是电影特效的…...

找工厂客户,天下工厂和企查查、天眼查这类平台哪个数据更靠谱?

做B2B销售或供应链采购的人,多半都碰过这样的困境:打开某个平台搜一个行业,出来几百条结果,逐条看下去才发现——这家是贸易公司,那家是空壳主体,还有一堆个体工商户,真正能对接生产的工厂没几个…...

C语言数组:从基础到实践

一、什么是数组数组就是相同类型数据的集合,这些数据在内存中连续存放,数组里的每个位置叫元素,用下标来访问。特别注意:数组的下标从0开始。以下代码就是一个简单的数组应用:二、数组的基本操作2.1 定义与初始化输出结…...

孩子学英语怎么选择

需要一点点建议哦...

rk3566 配置HDMI的屏的流程

一、确认硬件与固件硬件:RK3566 板载 Micro HDMI → 接 HDMI 显示器(用转接头 / 线)。固件:优先用官方带 HDMI 配置的镜像(如 hdmi 专用 img),避免默认关闭 HDMI 的版本。二、设备树&#xff08…...

自动化业务通报系统实现

问题解构:需求核心是构建一个基于Python的自动化业务通报系统,用于从多个.xls报表中提取数据,按团队统计指标完成情况,生成手机适配的通报图片,并通过Web界面展示。系统需支持灵活的配置管理,包括团队信息、…...