你有没有想过一件事:为什么手机上的AI助手,要么反应慢得让人着急,要么回答得不够聪明?
这背后藏着一个工程师们头疼了很久的矛盾。大模型聪明,但跑不动手机;小模型能跑,但笨。于是厂商们只能做各种版本,1B版本、3B版本、7B版本,分别塞给不同配置的设备。可问题是,每个版本都要单独训练、单独存储,你的手机里可能同时装着好几个"克隆兄弟",占用着宝贵的存储空间,而且用户没法根据当下的需求随时切换,比如电量低的时候想用省电的小模型,插着充电器的时候想用聪明的大模型。
苹果的一个研究团队最近发表了一篇论文,试图用一套方案同时解决这两个问题:既要让模型能根据设备性能灵活伸缩,又要让它在推理时足够省资源。这篇论文的名字叫《Stepped MoE》,中文可以理解成"分段式专家混合模型"。读完之后我最大的感受是,这不是一个单点突破,而是把几个各自有缺陷的老思路,拼接出了一个新的平衡点。
**核心问题:鱼和熊掌不可兼得**
要理解这篇论文的贡献,得先弄清楚业内此前的两条路分别卡在哪儿。
第一条路叫**MoE**
MoE:全称Mixture of Experts,混合专家模型,核心思路是模型里有很多个"专家"子网络,每次只激活其中一小部分来处理当前的词,这样模型可以做得很大,但计算量却不会同比例增长。
MoE的问题在于,它是逐词路由的,也就是说模型每生成一个字,都可能换一批专家来处理。这在云端服务器上问题不大,因为显存够大,可以把整个模型都放在内存里,用多少调多少。但放到手机这种内存紧张的设备上,麻烦就来了:每换一次专家,就要从硬盘往内存里搬运一批新的参数,这个搬运速度比芯片算力慢得多。结果就是,手机的算力被闲置了,大部分时间都花在了"等数据从硬盘搬过来"这件事上。论文里把这个现象叫做I/O瓶颈,意思是硬件忙着搬文件,而不是忙着做数学运算。
这就好比你在厨房做菜,刀工再快也没用,如果食材都锁在隔壁房间的冰箱里,你每做一道菜就要跑一趟去拿菜。专业厨师的技艺(算力)被浪费在了跑腿(搬运参数)上。如果不解决这个问题,再强大的芯片也发挥不出应有的速度,这就是为什么云端能跑的MoE模型,到了手机上反而变慢的原因。
第二条路是苹果自己之前提出的**IFP**
IFP:全称Instruction Following Pruning,指令跟随剪枝,做法是用一个额外的小模型,根据用户这一次提问的内容,提前决定好要用模型里的哪些参数,决定好之后在整个回答过程中都不再改变。
IFP解决了MoE的搬运问题,因为专家只在对话开始时选定一次,之后就常驻内存,不用反复搬运了。但新问题又冒出来了:一来需要额外训练和部署一个"剪枝器"模型,这本身就是额外负担;二来IFP是在单个神经元维度上做裁剪的,这个搜索空间极其庞大,训练起来特别吃力;三来也是最关键的,一旦对话开始,专家就定死了,没法根据对话内容的变化去调整,而且IFP只能基于已经训练好的模型去"改造",没法从零开始预训练。
说白了,MoE太灵活以至于太耗I/O,IFP太僵化以至于跟不上对话内容的变化。这篇论文要做的,就是在这两个极端之间找一个甜蜜点。
**解决方案:分段路由,一次决定一批专家**
论文给出的方案叫Stepped MoE,中文可以叫分段式混合专家。它的核心思路其实挺朴素:既然逐词换专家太频繁,整段对话定死专家又太僵化,那不如折中一下,每隔固定数量的词(论文里叫一个"段"),才重新选一次专家。
具体怎么实现的呢?模型被分成两部分,前面若干层是普通的稠密层(也就是所有参数都参与计算,不做任何裁剪),后面若干层是MoE层。模型架构里前12层是稠密层,后面44层是MoE层,整个模型有56层。
这里有个设计上的巧思值得说一下:论文没有像IFP那样单独训练一个"剪枝器"模型,而是直接用前面那些稠密层的输出,来生成后面MoE层该用哪些专家的打分。换句话说,模型自己给自己当了裁判,不需要外挂一个额外的网络。这个打分出来之后,会经过一个叫**SoftTopK**
SoftTopK:一种可以在训练过程中求导的排序选择方法,用来从一堆候选专家里挑出打分最高的若干个,同时保证整个挑选过程是可微分的,这样才能用梯度下降来训练。
的函数处理,筛选出得分最高的若干个专家,组成一个"掩码",决定接下来这一段文字要用哪些专家参数。这个掩码会管用固定长度的若干个词,用完了再重新生成一次。
这种设计还有一个额外的好处,叫流水线并行。因为所有层的专家是一次性全部选出来的,所以当前面几层正在计算的时候,后面几层要用到的专家参数就已经可以在后台异步从硬盘往内存里搬了,不用等到真正执行到那一层才临时去加载。这相当于把"决策"和"加载"两件事解耦开,决策先行一步,加载在后台悄悄跟上。
打个比方,这就像你去一家连锁餐厅吃饭,服务员一边点单一边把订单传给后厨,但聪明的后厨不会等你点完整桌菜才开始备料,而是看到第一道菜的订单就开始准备后面几道的食材,等你点完菜,后面的菜几乎已经同步在准备了。如果不这样做,而是像传统MoE那样每道菜都要现点现去库房取食材,那顾客等菜的时间会被无限拉长,这正是原始MoE在手机上卡顿的根本原因。
**让一个模型同时支持四种体型**
分段路由解决了I/O瓶颈,但论文还想解决另一个问题:怎么让同一个模型,既能在旗舰手机上跑出4B参数的聪明版本,又能在入门设备上跑出1B参数的轻量版本,而不需要分别训练四个模型、占用四份存储空间。
这里用到的思路借鉴了此前的**Matformer**
Matformer:一种让模型内部不同大小的子网络相互嵌套的训练方法,训练出来的一个大模型里天然包含着多个可以独立拆出来使用的小模型,拆出来的小模型和单独训练的同等大小模型效果相近。
的思想,把这个嵌套结构用到了专家选择上。具体来说,当用户需要1B参数的轻量模式时,模型只激活打分最高的少数几个专家;需要4B参数的完整模式时,激活更多专家。由于专家是按打分排序后从高到低选取的,小预算版本选出来的专家,天然是大预算版本选出的专家的一个子集。这就保证了,哪怕你切换到更省电的模式,模型选出来的依然是相对更重要的那批参数,而不是随机挑的。
为了让模型自己清楚当前被要求用多大的"体型"工作,论文还引入了一个叫**活跃尺寸嵌入**
活跃尺寸嵌入:一个根据当前目标参数预算查表得到的向量,加到输入的词向量上,用来明确告诉模型现在该按照哪种资源规格来工作。
的小技巧,相当于在模型耳边提前打了个招呼:接下来这段对话,你只能用4个专家,别想多了。论文的消融实验显示,这个小小的提示信号对模型在不同预算下保持稳定表现至关重要,去掉它之后模型的准确率会明显下滑。
这让我想到健身房里的可调节哑铃,同一套哑铃片,通过插销组合可以调出2.5公斤到20公斤的多种重量,不需要为每个重量单独买一套哑铃。但光有可拆卸的哑铃片还不够,你还得清楚告诉自己今天练的是哪个重量级别的训练计划,不然动作标准可能会跑偏,这就是活跃尺寸嵌入起到的作用,它让模型清楚自己此刻该以什么规格去工作,而不是稀里糊涂地混着练。
训练这种"一专多能"的模型并不轻松。论文提到一个很现实的矛盾:如果直接把所有预算档位混在一起训练,小预算版本因为几乎每次都会被选中,梯度更新很频繁;而大预算版本里那些"多出来的"专家参数,只有在大预算模式下才会被激活,梯度信号相对稀疏,训练不充分。
为了解决这个问题,论文用了两招。第一招叫批次复制,简单说就是把同一批训练数据复制四份,分别用四种不同的预算档位去跑一遍,保证每个档位看到的数据量是一致的。第二招是给损失函数加权,让更稠密(参数更多)的配置在计算损失时权重更高一些,逼着模型对这些不常被训练到的参数给出更强的学习信号。
**实测效果:比同体型的稠密模型更聪明**
说了这么多设计思路,效果到底怎么样?论文给出了一组预训练阶段的对比数据,我觉得很值得细看。
| 模型类型 | 激活参数 | 知识问答(Core-En-0s) | MMLU | 数学(GSM-8k) | 代码(HumanEval) |
|---|---|---|---|---|---|
| 稠密模型 | 1B | 0.662 | 0.470 | 0.174 | 0.132 |
| 稠密模型 | 2B | 0.690 | 0.552 | 0.260 | 0.156 |
| 稠密模型 | 3B | 0.704 | 0.578 | 0.345 | 0.184 |
| 稠密模型 | 4B | 0.716 | 0.610 | 0.411 | 0.205 |
| 灵活分段MoE | 1B | 0.640 | **0.5706** | **0.283** | **0.170** |
| 灵活分段MoE | 2B | 0.683 | **0.6144** | **0.459** | **0.243** |
| 灵活分段MoE | 3B | 0.701 | **0.6227** | **0.480** | **0.243** |
| 灵活分段MoE | 4B | 0.712 | **0.6317** | **0.483** | **0.248** |
| 纯MoE(上限对照) | 3B | 0.725 | 0.662 | 0.528 | 0.278 |
这张表里最值得关注的是知识类(MMLU)和数学类(GSM-8k)任务上的表现。拿3B这一档来说,灵活分段MoE在MMLU上拿到62.27%,而同等参数的稠密模型只有57.8%,差了将近5个百分点。在数学题上差距更夸张,灵活分段MoE是48%,稠密模型只有34.5%,相当于每做10道数学题,分段MoE能多做对一道多。
当然,这个方案也不是完美的。在最基础的知识常识测试(Core-En-0s)上,灵活分段MoE在1B这一档反而比稠密模型差了一点点,0.640对0.662。论文自己分析,可能是因为1B模型里,那12层固定要用于决策的稠密层占比太高(占了总层数的21%),挤占了本该用来学习具体知识的空间。这个坦诚的自我批评我觉得挺难得的,说明研究者没有选择性展示对自己有利的数据。
至于真正的MoE上限(表里最后一行,3B激活、12B总参数的纯MoE模型),它在所有指标上确实都更强,但代价是它必须把12B全部参数都放进内存,没法做弹性调整,也没解决I/O瓶颈问题。所以灵活分段MoE更像是在"全量MoE的准确率"和"稠密模型的可部署性"之间找到的一个折中方案。
**分段长度怎么选:快与准的拉锯战**
既然是"分段"路由,段落多长合适,自然是个关键问题。论文做了对比实验,把每隔32个词换一次专家,和每隔16个词换一次做了对比。
结果很直接:段越短,效果越好。16词一段的配置,在所有基准测试上都优于32词一段的配置,比如3B档位下,GSM-8k数学准确率从48%提升到51.3%,MMLU从62.27%提升到65.3%。这个现象也呼应了前面提到的,纯MoE(相当于每个词都换一次专家,段长等于1)效果最好,这说明路由越细,模型越能贴合当前词的真实语境。
但段越短,意味着专家重新加载的次数越频繁,也就意味着I/O压力越大。所以段长本质上是一个旋钮,往一边拧是更高的准确率,往另一边拧是更低的延迟,具体拧到哪个位置,取决于目标设备的硬盘读写速度和内存带宽。论文把这个参数留作了一个可配置的超参数,而不是写死的固定值。
论文还做了一个很聪明的补充实验:拿一个按16词段长训练好的模型,不重新训练,直接在推理阶段把段长改成32甚至64去测试。结果发现从16改到32,各项指标几乎没掉多少,但从32改到64,一些指标(比如一次性问答准确率Core-En-1s)就开始明显下滑了,从0.298掉到0.217。这说明同一个训练好的模型,在推理时适度调大段长来省I/O是可行的,但调得太猛还是会伤准确率,这是一个宝贵的工程参考。
**真实延迟测试:接近稠密模型,却更聪明**
讲了这么多准确率,推理速度到底怎么样?论文用一个模拟芯片的方法做了测试,核心指标叫**CHR**
CHR:全称Cache Hit Rate,缓存命中率,指当前解码步骤需要用到的专家,有多大比例已经提前存在于内存里了,不需要再从硬盘重新读取,这个比例越高,说明I/O压力越小。
结果显示一个很鲜明的对比:纯MoE模型哪怕缓存命中率提高到60%,延迟依然比稠密模型高出一大截,因为频繁换专家这件事本身就是硬伤。而分段MoE无论缓存命中率是多少,延迟曲线几乎是平的,紧贴着3B稠密模型的速度,并且始终比6B稠密模型快。换句话说,分段MoE用接近3B稠密模型的速度,交付了比4B稠密模型更好的准确率。
这就好比两家快递公司,一家承诺随时随地按需调货(纯MoE),听起来很灵活,但每次都要临时跑腿去仓库取货,客户实际拿到包裹的时间反而最慢;另一家提前规划好一段时间内要送的所有货物,一次性备齐(分段MoE),虽然不如前者那么"随叫随到",但综合下来送货反而更快更稳。如果不做这种"批量预判"的设计,那无论芯片算力多强,终端用户感知到的永远是卡顿,这正是论文反复强调的I/O瓶颈的本质。
**指令微调阶段的表现同样亮眼**
预训练效果不错,微调之后呢?论文用一个12B总参数的模型,分别测试了从1B到4B四档激活规模,在指令遵循、数学、代码、工具调用等任务上的表现。
| 任务类别 | 3B稠密模型 | 6B稠密模型 | 灵活分段MoE(1B) | 灵活分段MoE(3B) | 灵活分段MoE(4B) |
|---|---|---|---|---|---|
| IFEval指令级 | 83.69 | 86.45 | 76.74 | **85.25** | **85.37** |
| GSM-8k数学 | 64.60 | 73.80 | 52.20 | 72.20 | **74.10** |
| MBPP代码 | 38.30 | 45.90 | 35.30 | 46.20 | **46.50** |
| 工具调用 | 75.74 | 80.10 | 67.61 | 77.06 | 78.07 |
可以看到,3B和4B档位的灵活分段MoE,几乎全面追平甚至超过了同等活跃参数的6B稠密模型,而且远超3B稠密模型。尤其是数学和代码这两项,分段MoE的3B档位分别拿到72.2和46.2,都明显优于3B稠密模型的64.6和38.3。
这说明一件事:用一个共享参数池训练出来的多档位模型,在实际任务上并不是"样样通样样松"的妥协品,反而在高档位上能做到和独立训练的大模型相当的水平。
**专家选择是不是乱来的?**
有个问题可能你也会好奇:这个自动选专家的机制,到底靠不靠谱,会不会选得很随意?
论文做了一个很直观的验证实验,把不同对话场景下,相邻段落选出来的专家做余弦相似度对比。结果显示,当话题保持连贯时,前后段落选出的专家高度相似;当话题从A突然跳到完全不相关的B时,专家组合也会明显发生变化;而当话题从A跳到B又绕回A时,专家选择又会"认出"熟悉的话题,恢复成和最初相似的组合。
这其实挺让人安心的,说明这套路由机制不是在瞎选,而是真的在跟着语义内容走。如果话题没变,模型没必要频繁折腾专家,省下来的正是I/O开销;如果话题剧变,模型也有能力及时调整,不会死守着一套不合时宜的参数。
**这套方法能不能继续做大?**
论文还做了一个扩展性实验,把总参数从12B提高到20B,看看在相同激活参数下,总参数变大是不是依然有用。
结果是肯定的,几乎所有指标都有提升,比如GSM-8k在3B激活档位下,从12B总参数时的48%提升到20B总参数时的50.7%。这说明哪怕激活的参数量不变,光是扩大"专家池"的总规模,模型依然能从中受益,这和稀疏模型一贯的特性是吻合的。
不过论文也很诚实地指出了代价:训练这种灵活多档位模型所需要的算力,几乎相当于分别训练四个独立档位模型算力总和。论文附录里给出了具体的训练成本对比,灵活版本用了203435个TPU核心小时,而等效的稠密和静态MoE基线加起来大约是12万左右核心小时的量级。换句话说,灵活性是有代价的,这不是一顿免费的午餐,而是用更高的训练成本,换来了部署时更低的存储占用和更灵活的使用方式。
**写在后面**
读完这篇论文,印象最深的其实不是哪个具体的技术模块,而是研究者处理"鱼和熊掌"问题的方式。他们没有在MoE和IFP之间选一个更好的,而是去拆解各自的优点到底来自哪里,再重新组装。MoE的优点是能灵活适应内容变化,缺点是换得太频繁;IFP的优点是换得够少,缺点是换得太死板。分段路由本质上就是在"换的频率"这一个维度上,找了个中间值。这种思路听起来简单,但真正做出来,需要处理的细节一点都不简单,比如怎么让多档位共享参数还训练得动,怎么给模型一个清晰的"预算提示"。
论文里让我有点意外的是1B档位在基础常识测试上反而不如纯稠密模型这件事。研究团队没有回避这个弱点,还给出了自己的猜测,是固定的决策层占比在小模型里太高了。这种坦率的失败案例分析,比一味展示优秀数据更有参考价值,因为它告诉后来者,这个方向上还有哪些坑没填平。
还有一个细节值得玩味:论文提到如果训练时间拉得更长,纯MoE和分段MoE之间的差距可能会进一步拉大。这其实暗示了一个尚未验证的假设,分段路由的"让步"在短期训练中代价不明显,但长期来看,这种信息压缩是否会成为天花板,目前还没有答案。
Q&A
Q1:Stepped MoE解决了什么核心问题?
A:它试图同时解决两个此前独立存在的难题,一是让大模型在手机等内存受限设备上跑得不卡顿,二是让同一个模型能灵活切换1B到4B等不同参数规模,而不需要分别训练和存储多个版本。
Q2:Stepped MoE和此前的IFP、传统MoE有什么区别?
A:传统MoE每个词都要换一批专家,导致频繁从硬盘搬运参数,拖慢速度;IFP每次对话只选一次专家,速度快但不够灵活。Stepped MoE折中为每隔固定段落的词重新选一次专家,兼顾了速度和适应性。
Q3:Stepped MoE的准确率和同等大小的稠密模型相比怎么样?
A:在知识类和数学类任务上,Stepped MoE普遍比同等激活参数的稠密模型更准,比如3B档位在数学题GSM-8k上达到48%,而同等稠密模型只有34.5%,但在个别基础常识测试上表现略逊。







