MoE环游记:1、从几何意义出发
By 苏剑林 | 2025-02-08 | 34626位读者 |前两年福至心灵之下,开了一个“Transformer升级之路”系列,陆续分享了主流Transformer架构的一些改进工作和个人思考,得到了部份读者的认可。这篇文章开始,我们沿着同样的风格,介绍当前另一个主流架构MoE(Mixture of Experts)。
MoE的流行自不必多说,近来火出圈的DeepSeek-V3便是MoE架构,传言GPT-4也是MoE架构,国内最近出的一些模型也有不少用上了MoE。然而,虽然MoE的研究由来已久,但其应用长时间内都不愠不火,大致上是从去年初的《Mixtral of Experts》开始,MoE才逐渐吸引大家的注意力,其显著优点是参数量大,但训练和推理成本都显著低。
但同时MoE也有一些难题,如训练不稳定、负载不均衡、效果不够好等,这也是它早年没有流行起来的主要原因。不过随着这两年关注度的提升,这些问题在很大程度上已经得到解决,我们在接下来的介绍中会逐一谈到这些内容。
问题定义 #
首先要指出的是,这里会用笔者自己的一种理解思路来介绍MoE,在必要的地方会附上相应的参考文献,但不会对MoE架构进行系统的追根溯源,还请读者见谅。
我们知道,Transformer模型由Attention层和MLP层组成,MoE替换的是模型中MLP层。MLP层又分FFN(FeedForward Network)和GLU(Gated Linear Unit)两种,主流的是GLU,但简单起见我们还是以FFN为例
\begin{equation}\boldsymbol{y} = f(\boldsymbol{x}\boldsymbol{W}^{(A)})\boldsymbol{W}^{(B)}\end{equation}
其中\boldsymbol{x}\in\mathbb{R}^{d}是输入向量(行向量),\boldsymbol{W}^{(A)}\in\mathbb{R}^{d\times D},\boldsymbol{W}^{(B)}\in\mathbb{R}^{D\times d}是两个参数矩阵,f是Element-wise的激活函数。设n是一个能整除D的整数,那么上述可以等价地用分块矩阵写成
\begin{equation}\boldsymbol{y} = f\big(\boldsymbol{x}\begin{bmatrix}\boldsymbol{W}^{(A)}_1 & \boldsymbol{W}^{(A)}_2 & \cdots & \boldsymbol{W}^{(A)}_n\end{bmatrix}\big)\begin{bmatrix}\boldsymbol{W}^{(B)}_1 \\ \boldsymbol{W}^{(B)}_2 \\ \vdots \\ \boldsymbol{W}^{(B)}_n\end{bmatrix} = \sum_{i=1}^n \underbrace{f(\boldsymbol{x}\boldsymbol{W}^{(A)}_i)\boldsymbol{W}^{(B)}_i}_{\boldsymbol{v}_i}\end{equation}
其中\boldsymbol{W}^{(A)}_i = \boldsymbol{W}^{(A)}_{[:,(i-1)c:ic]}, \boldsymbol{W}^{(B)}_i = \boldsymbol{W}^{(B)}_{[(i-1)c:ic,:]},c= D/n,这里的切片按照Python规则来。由此可见,FFN可以等价表示成n个向量\boldsymbol{v}_1,\boldsymbol{v}_2,\cdots,\boldsymbol{v}_n之和,每个向量代表了一个小模型f(\boldsymbol{x}\boldsymbol{W}^{(A)}_i)\boldsymbol{W}^{(B)}_i的输出,每个小模型计算量相同,这些小模型就是MoE中的“Expert”。
MoE提出的问题是:
能否只挑k个向量的和来逼近n个向量的和呢?这样就可以将计算量降低到k/n了。
模长排序 #
这个问题其实我们在《低秩近似之路(三):CR》已经探究过,写成数学公式是
\begin{equation}\mathop{\text{argmin}}_{\lambda_1,\lambda_2,\cdots,\lambda_n\in\{0,1\}}\left\Vert\sum_{i=1}^n \lambda_i \boldsymbol{v}_i - \sum_{i=1}^n\boldsymbol{v}_i\right\Vert^2\quad\text{s.t.}\quad \sum_{i=1}^n \lambda_i = k\end{equation}
记\gamma_i = 1 - \lambda_i,那么它又可以写成
\begin{equation}\mathop{\text{argmin}}_{\gamma_1,\gamma_2,\cdots,\gamma_n\in\{0,1\}}\left\Vert\sum_{i=1}^n \gamma_i \boldsymbol{v}_i\right\Vert^2\quad\text{s.t.}\quad \sum_{i=1}^n \gamma_i = n - k\end{equation}
这个问题的精确求解是比较困难的,但有一个简单的近似解:当\boldsymbol{v}_i两两正交时,我们有
\begin{equation}\left\Vert\sum_{i=1}^n \gamma_i \boldsymbol{v}_i\right\Vert^2 = \sum_{i=1}^n \gamma_i^2 \Vert\boldsymbol{v}_i\Vert^2 = \sum_{i=1}^n \gamma_i \Vert\boldsymbol{v}_i\Vert^2\end{equation}
上式最优解显然就是让模长\Vert\boldsymbol{v}_i\Vert最小的n-k个\gamma_i等于1,这又等价于说挑出模长最大的k个向量来逼近n个向量之和。当\boldsymbol{v}_i不满足两两正交的条件时,我们依然用它来作为一个近似解。它的几何意义也很直观,模长越大的向量,在求和过程中越不容易被抵消,从而作用越突出。
此外,在《低秩近似之路(三):CR》中我们还讨论了一种依概率采样的逼近过程,在方差最小的假设下得到的最优采样概率同样有正比于模长的特点,所以总的来说按向量模长排序是一个简单但不失有效的策略。
MoE初现 #
现在策略已经有了——“挑模长最大的k个向量”——可是细想之下我们会发现它并不实用:要挑模长最大的k个向量,就得把所有向量的模长都算出来,这又意味着要把所有的\boldsymbol{v}_i先算出来,可我们的原本目的却是减少\boldsymbol{v}_i的计算量!
为了解决这个矛盾,我们需要重新设计每个Expert模型,使得它的模长可以低成本地计算出来。什么意思呢?首先我们将\boldsymbol{v}_i归一化得到\boldsymbol{e}_i = \boldsymbol{v}_i/\Vert\boldsymbol{v}_i\Vert,这样每个\boldsymbol{e}_i的模长都相同了。接着我们定义
\begin{equation}\underbrace{[\rho_1,\rho_2,\cdots,\rho_n]}_{\boldsymbol{\rho}} = h(\boldsymbol{x}\boldsymbol{W}^{(R)})\quad\in\mathbb{R}_{\geq 0}^n\end{equation}
其中\boldsymbol{W}^{(R)}\in\mathbb{R}^{d\times n}是参数矩阵,h(\cdot)是一个\mathbb{R}\to\mathbb{R}_{\geq 0}的激活函数,说白了这就是一个d维到n维的线性变换加激活函数,所以计算量是比较小的,这部分模型在MoE中被称为“Router”。
\boldsymbol{\rho}的作用是什么呢?预测每个Expert的模长!换言之,我们将\rho_i作为第i个Expert的模长,\rho_i \boldsymbol{e}_i才是完整的Expert,它被分解为两部分:计算量比较小的模长\rho_i以及计算量比较大的方向\boldsymbol{e}_i。为了减少计算量,我们先计算出\boldsymbol{\rho},挑出最大的k个后才去计算相应的\boldsymbol{e}_i,最后乘上\rho_i并求和:
\begin{equation}\boldsymbol{y} = \sum_{i\in \mathop{\text{argtop}}_k \boldsymbol{\rho}} \rho_i \boldsymbol{e}_i\end{equation}
这便是MoE模型的基本公式。由于计算中只保留了Top-k部分,所以它本质上属于一种Sparse模型,而原本的FFN或者k=n时的模型,通常称为对应的Dense模型。
思路概括 #
不管是熟悉MoE还是不熟悉MoE的读者,可能都会对上述过程有点陌生,因为这是笔者自己闭门造车的一种MoE理解路线,但因为其几何意义更明确,所以本质上应该是更好理解的。
我们再来整理一下整个思路:
1、一个常规的Dense模型FFN,可以等价改写为n个Expert向量\boldsymbol{v}_1,\boldsymbol{v}_2,\cdots,\boldsymbol{v}_n之和;
2、为了节省计算量,我们试图挑出k个向量求和来逼近原本的n个向量之和;
3、转化为数学问题求解后,我们发现挑选规则是模长最大的k个向量;
4、直接去算n个Expert的模长然后选k个实际上是不省计算量的,所以要重新设计Expert;
5、将\boldsymbol{v}_i归一化得到\boldsymbol{e}_i,然后用另外的小模型(Router)预测模长\rho_i,最终的Expert为\rho_i \boldsymbol{e}_i;
6、此时,我们就可以先算全体\rho_i,挑出k个后才去计算\boldsymbol{e}_i,达到节省计算量的目的。
为何如此 #
可能有些读者疑问,为什么要做这个看似复杂的过程?原本的MoE不是挺好理解的吗?一般的MoE形式为
\begin{equation}\boldsymbol{y} = \sum_{i\in \mathop{\text{argtop}}_k \boldsymbol{\rho}} \rho_i \boldsymbol{v}_i\end{equation}
也就是求和前少了对\boldsymbol{v}_i的归一化,此时\rho_i也没有模长的意义,它纯粹是一个用来对Expert排序的打分模型(即Router)。可为什么将\rho_i乘到Expert上去就能让Router学会正确排序Expert呢?笔者发现只有《Sparse Backpropagation for MoE Training》对此给出了一个解释,但还是稍欠直观。
而在本文的几何视角下,我们会发现很多问题就“豁然开朗”了。我们将Expert重新参数化为\rho_i \boldsymbol{e}_i后,Dense模型对应于全体\rho_i \boldsymbol{e}_i求和,而MoE对应于\rho_i选Top-k后求和,这是Dense模型的一个有理论保证的逼近。我们没有去考虑Router如何选择Expert,只是每一步都尽可能逼近Dense模型,这可以说是既要大参数、又要小计算量的最佳选择。
现在\rho_i的几何意义是模长而不是概率,所以激活函数h(\cdot)就没有归一化的要求了,除了Softmax外,像Sigmoid、ReLU都可以考虑使用,也可以考虑我们在《Softmax后传:寻找Top-K的光滑近似》介绍的Top-k光滑近似。Router使用非归一化的激活函数,有助于避免k > 1时Expert之间的恶性竞争,有时候能取得更好的效果。
最后补充一点,我们前面定义\boldsymbol{e}_i = \boldsymbol{v}_i/ \Vert\boldsymbol{v}_i\Vert,目的是让所有\boldsymbol{e}_i模长相同,实际操作中不是一定要L2 Normalize,也可以是其他等价操作,比如gamma参数恒等于1的RMS Norm,它更符合我们的输出习惯。
文章小结 #
本文从Dense模型的最佳逼近出发来推导和理解MoE,得到了一种特定的MoE形式,它比现有MoE多了一个Normalize步骤,但能让MoE的几何意义更加明显。当然,不管Normalize与否,MoE之路都只是刚刚开始,更多的困难还在路上。
转载到请包括本文地址:https://spaces.ac.cn/archives/10699
更详细的转载事宜请参考:《科学空间FAQ》
如果您还有什么疑惑或建议,欢迎在下方评论区继续讨论。
如果您觉得本文还不错,欢迎分享/打赏本文。打赏并非要从中获得收益,而是希望知道科学空间获得了多少读者的真心关注。当然,如果你无视它,也不会影响你的阅读。再次表示欢迎和感谢!
如果您需要引用本文,请参考:
苏剑林. (Feb. 08, 2025). 《MoE环游记:1、从几何意义出发 》[Blog post]. Retrieved from https://spaces.ac.cn/archives/10699
@online{kexuefm-10699,
title={MoE环游记:1、从几何意义出发},
author={苏剑林},
year={2025},
month={Feb},
url={\url{https://spaces.ac.cn/archives/10699}},
}
February 15th, 2025
近似解的时候假设“当vi两两正交时”让我想到了高维空间随机取两个向量几乎正交的性质...
可以把故事往这一点靠~
February 16th, 2025
很巧妙的角度理解, 改进实现起来也很简单准备周末试一下
想问下苏神从这个角度怎么分析 expert choice? 标准 token choice 是每个 token 选角 topk 最大 experts 或这里理解的最大的几个模;expert choice 每个 expert 选 topk token 是否可以直接理解成每个 token 选 dynamic topk 的模。
expert choice我暂时还没想法,我先记上。dynamic k后面应该会谈到,但跟expert choice无关。
February 16th, 2025
苏老师好,我有一个问题,就是为什么p的计算量比v更小。
我知道了,是因为乘的矩阵更少是吗
是的,更少且更小。
February 17th, 2025
苏神,vi两两正交,这个假设是不是有点太强了,我理解来说moe的专家数量一般是冗余的,每个专家虽然包含不同的知识,但也储存着不少相同的知识
两两正交是作为精确解的充分条件,不满足这个条件通常依然是一个良好的近似。
February 21st, 2025
假设N个量的top k要求一定要算完N个向量,为了降低计算量,能不能转而考虑每个向量只抽取m个分量参与计算?其中m « N
是个思路,但你会发现实际计算量也不少,所以重新参数化Expert的理解方式时最简明的。
February 24th, 2025
苏神,这里Router得出来的pi会直接代入计算yi = piei。请问Router能不能仅作为寻址用,找出(预测出)top k模长的vi呢(后面求和直接用vi)?这样还可以减少归一化的计算量。
本来就不用真的做归一化,gate是学出来的,如果没有归一化,他会自动调整scale适应
你仔细看看思路概况的第五点,就是要做归一化呀,然后再乘以预测出来的模长。
那是理论上,实际上并不必要。
归一化是我这里提出的方案,它具有更清晰的几何意义,不归一化的实际表现通常也不错,只不过不容易对到本文的结合意义下。
@苏神的迷弟|comment-26722 你这样设计的话,Router就没法训练了。
February 24th, 2025
苏神,我观察到目前各家发布的MoE结构都不太一样,所以想请教下MoE模型的结构设计和scaling law和dense模型会有一些差异吗,比如设计成一个矮胖型还是瘦高型,以及pre ln和post ln的选择等等
MoE一般是矮胖的。MoE参数量大致上相当于一个intermediate_size非常大的FFN,比如普通FFN一般intermediate_size=4*hiddne_size,MoE一般intermediate_size=32*hiddne_size,但MoE通常选择激活1/8的Expert,所以激活参数一般也是4*hiddne_size,希望能逼近32*hiddne_size的dense模型效果。
February 25th, 2025
是不是可以离线去做,手动选择k个向量给矩阵降秩哈哈
早期有人做过直接用hash随机分配的。
February 28th, 2025
文中写“FFN可以等价表示成n个向量v1,v2,⋯,vn之和,每个向量代表了一个小模型f(xW(A)i)W(B)i
的输出。”此处的向量维度是D×D的,该向量是一个矩阵。所以说,是不是意味着该向量是指向量空间中的向量,矩阵可以视为广义的向量了?
此处的向量是d维的(即\boldsymbol{v}_i\in\mathbb{R}^d)。