最近后台留言里“MoE”和“FPGA”这两个词一起出现的频率越来越高问的人还都带着点工程基础——不是纯新手是已经能调通LED流水灯、跑过几个图像处理demo的FPGA开发者想往大模型方向伸一脚。这倒是件挺有意思的事。MoEMixture of Experts混合专家模型这几年在大模型圈子里已经不算新概念了但真正在FPGA上把它跑出点名堂的资料还是比较零散。我花了不少时间把这两摊资料掺在一起捋了一遍这期就把我的理解、方案选型时的思路以及一些实测踩坑记录拿出来展开聊聊给同样对“MoE加FPGA”感兴趣的朋友一份入局参考。文章会覆盖三块内容MoE模型到底在做什么、FPGA实现MoE的硬件视角与简化设计、实操中高频踩坑点的排错心得。适合已经有FPGA基础、想往大模型推理方向转或者已经懂MoE但对硬件落地缺乏概念的朋友。1. MoE模型核心原理从“全量计算”到“按需激活”1.1 传统Transformer的瓶颈到底在哪先抛开MoE的复杂说法回到Transformer本身。一个标准Transformer块里计算量的大头基本都在两个地方多头自注意力MHA和前馈网络FFN。自注意力负责让token之间交换信息FFN则负责把每个token的特征做非线性变换。在LLaMA、GPT这类模型中FFN层有两个线性变换加一个激活函数参数量能占到整个模型的2/3以上。问题就出在这每次推理时不管输入的是什么内容FFN层的全部参数都要参与计算。哪怕你只是在问“1加1等于几”模型也会把700亿参数全部拉起来算一遍。这就像一个大公司不管谁来办业务所有部门的所有员工都得同时上岗这显然是一种浪费。大模型时代大家普遍用“参数量”来衡量模型大小但真正决定一次推理开销的是“激活参数”active parameters——也就是这条请求实际流经的参数个数。稠密模型里激活参数等于总参数而MoE要解决的就是把这个等式给拆开。1.2 稀疏门控机制MoE的“专家分工”是怎么运作的MoE的结构说起来很直白把原来单个FFN层替换成一组并行的FFN每个FFN叫一个“专家”expert。在它们前面加一个路由网络router也叫门控网络gate根据输入token的特征决定让哪几个专家来处理。这个过程通常叫稀疏门控。具体做法把token的隐藏向量喂给一个小的线性层得到它在每个专家上的得分再做Softmax归一化然后只取Top-K个专家参与计算。K最常见的是1或2Switch Transformer用的就是Top-1Mixtral 8x7B则用Top-2。这里有两点容易被新手忽略。第一路由判断是在token级别做的同一个序列里的不同token完全可能被路由到不同专家。第二被选中的Token只会去那K个专家那里计算剩下的专家空闲。这种“部分专家干活”的特性就是MoE效率优势的根源。1.3 效率账怎么算总参数量与激活参数量的区别拿大家经常看到的“26B总参、A4B激活”这类说法举例。前一个数表示模型的全部权重加在一起有约26B个参数后一个数表示一次推理真正参与计算的只有约4B个参数。其它约20B参数虽然是模型能力的一部分但当次推理根本没有被激活。传统Transformer的激活参数等于总参数训练和推理都面临“参数越大、每一步计算量越大”的死磕。MoE则在保持总参数量不缩水的前提下把单次推理的计算量压到接近一个小模型。这也是为什么很多厂商在发布“超大底座”的同时还能把推理成本控制在可接受范围内。不过这里要泼一盆冷水MoE降低的是计算量FLOPs并没有同步降低显存/存储开销。因为专家权重都在只是不全部参与计算所以模型文件大小依然和总参数量成正比。放到硬件上这就带来了一个很现实的问题算力用得少了但带宽压力一点没小。2. MoE推理在硬件层面的计算画像2.1 一次推理任务里到底有哪几步如果要给FPGA做设计必须先把一次MoE推理拆成细颗粒度的操作。拿一个典型的MoE Transformer层来说计算流程大致是这样的输入token先做LayerNorm得到归一化后的隐向量。隐向量经过注意力层完成token间信息交换。进入MoE层时隐向量先过路由线性层得到logits。对logits做Softmax再取Top-K得到选中的专家ID和归一化权重。根据专家ID把输入分发给对应的专家FFN每个专家内部做“线性变换-激活-线性变换”。最后把K个专家的输出按路由权重加权求和接残差输出。这里每一步都会映射到硬件运算单元。LayerNorm要算均值和方差涉及除法与开方路由层的Softmax涉及指数运算专家FFN则是典型的矩阵乘加。再加上注意力层里的QKV线性映射、Softmax、多头拼接整个数据通路比普通CNN复杂不少。2.2 稀疏性对硬件是机遇也是难题从硬件视角来看MoE的稀疏性是一把双刃剑。好处很诱人每个token只激活K个专家整体乘加运算量大幅下降在FPGA这种逻辑资源有限的平台上意味着同样的DSP资源也许能支撑更大模型。坏处也明显稀疏计算带来的访存是发散、不规则的。GPU擅长的是SIMT式的密集计算遇到“这个token去专家A那个token去专家B”的分叉会出现比较严重的线程束浪费。FPGA虽然可以做到完全定制化的数据通路但要做的是“动态路由权重复用专家并行”这种组合框架对控制逻辑和存储调度的要求非常高。更麻烦的是专家负载不均衡。实际输入中某些token会高频命中某些专家极端情况下有专家忙不过来同时其他专家闲得发慌。这在硬件上直接表现为忙的专家计算单元满载闲的计算单元空转整体利用率上不去。2.3 FPGA为什么在这个场景有存在感先说结论FPGA不是来和GPU比拼峰值算力的真要拼INT8密集算力中高端FPGA也拼不过同价位的GPU。FPGA的定位应该落在“定制化数据流”和“确定性的低延迟”上。GPU是通用架构适合数据并行度极高、分支少的计算。而MoE的路由决策是一个动态控制流专家权重需要按需搬运这种任务对GPU来说往往跑不出纸面算力。FPGA的可编程逻辑则允许我针对MoE的访存模式和计算模式设计专门的流水线把路由开销和专家计算重叠起来同时对外提供微秒级、可预测的延迟。另一个现实因素是可扩展性。FPGA本身经常作为“接口预处理”的角色出现在各类系统中比如图像处理项目里的MIPI/HDMI采集、工业控制里的BISS-C/AD7606传感器读取、通信系统中的PCIE/光口收发。在这些场景里FPGA本来就处在数据出入口位置如果能顺带把一部分MoE推理任务卸载下来整体系统的数据搬运路径会短很多。3. FPGA实现MoE的简化设计与关键参数估算3.1 把目标定得现实一点到底跑什么样的模型如果把目标定成“在FPGA上完整跑一个万亿参数MoE大模型”那基本属于不太可能的任务至少对个人开发者来说是这样。不要一上来就把步子迈太大建议把项目拆成两步走。第一步用FPGA跑通一个单一MoE层验证路由和专家计算的硬件通路第二步再用多层串联配合DDR做权重调度逐步扩大规模。我建议从“单层、8专家、Top-2、INT8量化”开始。为什么选这个配置8个专家的权重规模适合用DDR4存放Top-2能体现路由逻辑但又不至于让排序电路过于复杂INT8则正好能匹配大多数FPGA的DSP原生位宽。这里还要提醒一下FPGA上跑Transformer已经有不少IP可用比如Xilinx的DPU、FINN这类框架但它们主要是针对CNN或固定结构网络优化的对MoE的动态路由支持非常有限。所以大概率还是得自己写核心数据通路这既是门槛也是最有学习价值的部分。3.2 存储与带宽先算清楚权重放在哪做FPGA方案设计第一件事永远是估算存储和带宽。拿我上面说的“单层、8专家、Top-2”方案来算一笔账。假设模型隐藏维度hidden512专家FFN中间层维度intermediate2048。每个专家内部有两个线性层第一个是512x2048第二个是2048x512。单个专家参数量大约为512x20482048x512约2.1M个参数。使用INT8量化后约2.1MB。8个专家约16.8MB。FPGA片上存储BRAM/URAM容量通常在几MB到几十MB之间16.8MB权重全部放片上可以做到但在实际工程中你还要放激活值、中间结果和路由缓存所以更稳妥的做法是把专家权重放在片外DDR里每次路由结束后再按需搬运到片上。这就涉及到带宽问题。举一个具体数字DDR4-2400、64比特位宽的带宽约19.2GB/s实际可用按75%计算大概14.4GB/s。每个token激活2个专家每个专家的输入侧矩阵是512x2048单个token需要读取的权重其实是512个输入维度对应的那部分参数约为1MB。如果一次处理256个token也就是从DDR读出256MB换算下来约18ms。这个速度只能算“能跑”想再快就得考虑提高并行度或者把高频专家权重常驻片上。注意上面的带宽估算忽略了很多细节比如DDR的bank冲突和刷新开销但它足以帮你判断方案有无落地可能。先算数量级再做详细仿真这是FPGA工程的基本顺序。3.3 路由层与专家层的硬件拆分路由层在硬件上并不复杂在FPGA实现时需要拆成几个子模块。路由线性层一个大小为“num_experts x hidden”的矩阵乘法因为规模不大用一组MAC单元阵列就能搞定。SoftmaxFPGA上实现Softmax有固定套路先求最大值再算指数最后归一化。指数运算一般用查找表加分段线性拟合来近似精度控制在1%以内问题不大。Top-KK2时最简单做两轮“比较-淘汰”就行。K增大就需要排序网络资源消耗会明显上升这也是为什么很多硬件实现都把K控制在2以内。专家层则是整个设计的算力核心。每个专家的FFN结构固定计算模式高度规则最适合映射成脉动阵列Systolic Array或者乘加树。如果资源够可以让多个专家对应的计算阵列并行工作如果资源紧张则可以只例化一个“专家计算核”按路由结果分时复用。我的建议是采用“1个路由模块N个并行专家核”的架构。N一开始可以取2正好对应Top-2的最大并行需求。实际运行时路由模块给出专家ID对应的专家核接收输入数据并启动计算其他核保持空闲。这样的架构控制逻辑最简单也最容易调试。3.4 和外部系统的数据通路FMC、PCIE这类接口怎么选FPGA做MoE推理很少是纯离线单机运行一般都挂在上位机或者SoC后面数据通路的设计会直接影响整体吞吐。在原型验证阶段用FMC接口挂一块带有DDR和PCIe的载板是常见做法也可以用FPGA Mezzanine Card连接高速ADC/DAC或视频采集模块把实时数据直接送进路由模块。但要注意FMC本身只是一套物理连接规范它不定义协议数据搬运还是要靠你自己实现的逻辑。有人在STM32这类MCU上用FMC并行总线访问FPGA寄存器把FPGA当成一个协处理器来干活这在控制面场景非常合适但数据量一大就会卡在MCU端总线带宽上。生产环境更常见的还是PCIe。Xilinx的XDMA IP已经比较成熟配合描述符表可以实现DMA方式的高速数据搬运。用XDMA跑MoE推理时可以把“PCIe接收数据-缓存-路由-专家计算-PCIe发回结果”做成一条完整的流水线。需要注意XDMA的寄存器配置和中断机制有一定上手成本建议先在简单的PCIE回环Demo上跑通再往上叠加MoE逻辑。4. 实操踩坑与排查心得4.1 资源评估阶段最容易算错的三处FPGA资源评估看着简单实际动手时我踩过不少坑集中在下面三个地方。第一DSP数量不等于MAC数量。Xilinx 7系列和UltraScale的DSP48E2虽然可以执行乘法累加但INT8模式下一个DSP能不能当两个乘加器用取决于IP核的例化方式。做资源预算时最稳妥的办法是先在Vivado里单独例化一个小矩阵乘法IP跑一次综合看实际DSP消耗再推算整体规模。别看手册上写的“DSP Count”要看你目标频率下单DSP能跑到多少。第二BRAM的读写端口限制很容易被忽略。权重缓存如果用双端口BRAM两个端口要分别处理“读权重”“写权重”和“读中间结果”端口分配稍有重叠就会导致综合阶段面积翻倍。前面说的16.8MB专家权重如果全部映射到URAM通常没问题但如果用BRAM不仅容量要够还要预留足够的写端口给DDR刷新权重。曾遇到过一次BRAM利用率62%、布线时却出现严重拥塞的情况原因就是端口冲突导致的大量多路选择器。第三带宽瓶颈往往不在DDR而在片上互连。AXI总线虽然方便但多个主设备同时访问DDR时仲裁器的开销和总线占用是隐性的。实际用ILA抓波形时发现DDR利用率才40%AXI互连已经接近饱和。后来改成在DDR控制器前加一个自定义的权重预取模块一次性读取整个专家的连续权重块才把总线效率提上来。4.2 量化与数值精度路由比专家更娇气FPGA上跑模型INT8量化几乎是必选项。但我实测下来MoE模型里有两个地方对量化特别敏感LayerNorm和路由网络。LayerNorm涉及均值、方差、除法和开方量化后误差会被放大。这个问题的处理方式是“混合精度”LayerNorm用FP16计算路由线性层至少保留FP16专家内部矩阵运算则放心用INT8。路由层精度一掉Top-K选出来的专家可能直接选错这个错误是结构性的后面算得再准也白搭。Softmax的指数运算在FPGA里也容易踩精度坑。查找表分段数太少误差会在指数部分被放大尤其输入数值较大时Softmax输出概率分布会出现明显偏差。我的做法是做两级近似第一级查指数表第二级用一次除法归一化再用少量LUT做修正实测下来和CPU端FP32结果的平均误差控制在1%以内。4.3 专家负载失衡一个容易被忽略的定时炸弹MoE模型真正映射到硬件之后负载均衡的问题会比理论分析严重得多。原因很简单理论中的Softmax分布假设是均匀的但真实输入分布是不均匀的。处理这个问题的思路有两个层面。模型侧可以在训练时就加入辅助的负载均衡损失auxiliary loss强迫路由网络不要把token都集中到少数专家硬件侧要给专家设置容量上限expert capacity一旦超过则丢弃超额token或者把它们路由到备用专家。在FPGA实现里我强烈建议一开始就把这个机制设计进去。我第一版硬件里没处理这种情况实际测试时发现某些token路由到的专家正在忙数据只能排队等待一个突发请求就能把整条流水线阻塞住几百个周期。后来在路由模块和专家阵列之间加了一个小容量的按专家分组的FIFO缓存并设置超时丢弃策略问题才算缓解。提示在FPGA上做MoE不要只盯着计算单元和DDR带宽控制面上的拥塞与背压同样重要。硬件流水线的核心思想是让每个模块都尽量“一直有事做”一旦某个模块成为瓶颈整条链路的吞吐都会被打回原形。4.4 调试环境与工具链的一些建议最后聊点工程向的经验。我个人的开发环境是Vivado配合ModelSim做仿真核心模块都用AXI-Stream接口规范起来方便挂ILA在线调试。在MoE层还没有和外部系统对接时先在Vivado里用Testbench喂一批固定向量做Function仿真这个阶段能抓到的逻辑错误最多。等基本功能通了再挂到实际板卡上用ILA抓路由结果和专家输出。这里有个小技巧把路由网络输出的Top-2专家ID用GPIO引出来接逻辑分析仪能非常直观地看到token到底被分到了哪里。我靠这个办法发现过一次Softmax查找表符号位处理错误导致的专家选择异常这种问题光看波形很难定位。如果你打算在Zynq系列上做还可以利用ARM核跑Python脚本通过寄存器总线把配置参数和输入数据下发到FPGA逻辑再通过中断把结果读回来。这套“PS下发数据、PL跑计算”的模式开发效率会比纯Verilog仿真高很多尤其适合做算法参数扫描。结尾从MoE的原理走到FPGA实现整个过程最大的感受是MoE在算法层面拆解了“大模型必须大头账”这个铁律但真正想把这种稀疏优势落到硬件上面对的却是访存调度、负载均衡、量化精度这一连串工程问题。FPGA不是这个题目里最容易的答案但它能促使你把模型结构、计算流、存储排布全部想透彻这种约束反而是一种提升。如果你想动手我建议不要一上来就追求复现Gemma 4 26B这种模型而是先把一个单层MoE模块在Zynq或者Artix的板卡上跑通加上PCIE或FMC数据通路逐步扩展。再往深走还可以尝试把YOLO这类网络里的一部分层改为MoE结构在FPGA上对比性能和资源消耗——那会是另一个很有意思的项目。最后分享一个我自己的调试心得FPGA工程出问题时先确认数据通路有没有断再确认控制通路有没有乱最后才去怀疑算法精度。MoE里的路由和专家计算看似玄妙落到波形上也不过是“乘法、比较、搬运”这几件事踏实把数据流理清楚问题就解决一大半。
