假设你住的老城区有一家小诊所,一位王医生包揽全部科室。内科病人要调血压药,骨科病人要正骨,一天最多要看二十种病。时间一长,王医生发现:给高血压病人开的药,用在骨折病人身上不但没帮助,反而耽误了治疗。这就是“多任务学习”里最让人头疼的负迁移——任务太多、彼此干扰,共享一个大脑反而什么都做不好。 后来医院改革,开了一排专科诊室:心内科、骨科、神经科,每个科室门口配一个分诊台。分诊护士根据病人的症状,把病人按不同比例分给不同专科医生。同一个病人,在心内科分诊台眼里重点是血压,在骨科分诊台眼里重点是 X 光片。医生们共享检查设备,但每个科室各自决定“怎么用这些医生”——这就是 MMoE(Multi-gate Mixture-of-Experts,多门控专家网络)的朴素图景:多个共享的专家网络,加上每个任务专属的门控网络,让任务按需取用专家知识。 这篇内容,我们从“为什么全科医生不够用”讲起,一步步拆开门控、看懂专家,再聊聊训练怎么不翻车、和其他变体怎么选,最后用代码搭一个能跑的 MMoE。
对 AI 从业者来说,MMoE 是推荐系统、广告 CTR/CVR 预估、风控反欺诈等“多目标建模”场景的标配方案。掌握它,等于拿到一张处理多任务冲突的职业通行证。
目录
可视化演示
本节配有交互动效(进入视口后自动加载,约 3KB)。
全科医生的困境:硬参数共享
老城诊所里的王医生,就是“硬参数共享”模型的化身。所谓硬参数共享,是让所有任务共用底层的神经网络参数,只在最上层为每个任务单独接一个输出头。就像王医生的一双手和听诊器是所有人共用的,只在病历本上分别记录“内科诊断”和“骨科诊断”。这种设计的好处是参数少、训练快,底层的通用特征(比如“病人年龄”)能被所有任务复用。
但问题也随之而来。王医生在给骨折病人正骨时,脑子里还惦记着高血压的用药方案;两种优化方向在共享的参数上“拔河”,梯度信号互相冲突。结果往往是:单看每个任务,效果都不如各自训练一个独立模型。这种“多任务一起学反而变差”的现象,就叫负迁移(negative transfer)。任务之间的相关性越低,负迁移越严重。
从数学上看,共享底层参数 $\theta_{shared}$ 的更新方向,是各任务损失函数梯度的加权和。方向相反的梯度互相抵消,参数就一直在原地打转。
如果两个任务像“预测点击率”和“预测是否喜欢”这样高度相关,共享底层完全够用。但如果任务是“预测点击率”和“预测视频是否完播”,用户的口味差异巨大,全科医生就开始手忙脚乱——这正是 MMoE 登场的时机。
医院改造:MMoE 网络结构
MMoE 的改造思路,是把“一个王医生包办一切”升级为“多个专科医生 + 每个科室门口的专属分诊台”。具体结构分三层:最底层是专家池——一组共享的专家网络 $f_1, f_2, \dots, f_N$,它们像一组专科医生,各自擅长不同角度的特征变换;中间是多门控网络——每个任务 $k$ 拥有一个独立门控 $g^k$,像分诊护士;最上层是任务塔 $h^k$,把融合后的表示映射为最终预测。
输入 $\mathbf{x}$ 同时送入所有专家,每个专家都给出一个“诊断意见” $f_i(\mathbf{x})$。对任务 $k$,门控 $g^k$ 把这些意见按权重叠起来,形成该任务专属的融合特征,再交给任务塔。整个前向过程可以写成一个公式:
注意这里的门控下标:专家是全局共享的,但每个任务的门控参数完全独立。任务 A 可以把 80% 权重给专家 1,任务 B 可以把 70% 权重给专家 3——同一个专家,在不同任务眼中分量不同。
用医院来比喻:医生还是那些医生,但心内科的分诊台只关心心脏相关的检查报告,骨科的分诊台只关心骨骼影像,同一份病历在不同科室被“各取所需”地解读。这样一来,专家可以专注学各自擅长的模式,任务之间的冲突被门控隔离开,负迁移大大缓解。
- 专家输出$f_i(\mathbf{x})$ 表示第 $i$ 个专家网络对输入 $\mathbf{x}$ 的映射结果,可以理解为“专科医生给出的治疗方案”。
- 门控权重$g_i^k(\mathbf{x})$ 是任务 $k$ 的门控为第 $i$ 个专家分配的权重,来自 softmax 输出,所有专家权重之和为 1,表示“分诊比例”。
- 加权融合将各专家输出按权重叠加,得到任务 $k$ 专用的融合表示,对应“分诊台按比例采纳多位医生的意见”。
- 任务塔变换融合结果再送入任务专属网络 $h^k$,得到最终预测 $y^k$,对应“科室主任做最终诊断”。
分诊台如何打分:门控网络机制
门控网络的工作,本质上是回答一个问题:“面对眼前的输入 $\mathbf{x}$,我们科室应该按什么比例去请各位专家?” 分诊护士不会凭空决定,她会先给每位专家打一个“适配分”,再把分数换算成比例。这个打分和换算过程,就是门控网络的完整机制。
门控本身是一个轻量级神经网络:先对输入做线性变换,得到每个专家的原始分数 $z_i$,再通过 softmax 函数把分数变成总和为 1 的概率分布。softmax 的特点是“赢家通吃但不过分极端”——分数高的专家拿到更大的权重,但分数低的也不会完全归零。用公式表示:
门控的动态性体现在“随输入变化”。同一个任务,面对“年轻用户”和“老年用户”两种输入,门控给出的专家组合完全不同。这就像分诊护士看见咳嗽病人会多叫呼吸科医生,看见摔伤病人会多叫骨科医生——分配方案不是写死在墙上的,而是根据眼前的具体情况实时调整。
在工程上,门控网络通常只包含一个或两个全连接层,参数量远小于专家网络。这也符合直觉:分诊台不需要像专科医生那样深的专业能力,它只需要“判断该找谁”。
- 原始分数$W_{gk}$ 是任务 $k$ 门控的权重矩阵,$\mathbf{x}$ 左乘它得到每个专家的未归一化分数,分数高意味着“更适配当前输入”。
- 指数放大对每个分数取自然指数 $e^{z_i}$,让正分数占比更大、负分数更小,放大专家之间的差异。
- 归一化分母把所有专家的指数分数求和,作为“总票数”,保证输出可解释为比例。
- 概率输出每个专家的指数分数除以总票数,得到 $(0,1)$ 区间的权重,且所有权重之和为 1。
别让医生闲下来:训练技巧与专家坍塌
医院运行一段时间后,你发现一个尴尬现象:心内科的分诊台总是把病人推给一号医生,二号、三号医生渐渐无人问津,医术荒废,最后一号医生成了“超级全科医生”,其他专家形同虚设。这就是 MMoE 训练中最经典的专家坍塌(expert collapse)。
坍塌的机制很直白:训练初期,某个专家恰好学得稍快,门控发现它给的损失下降更快,就不断加大它的权重;权重越大,这个专家的梯度越多,学得越快;其他专家梯度越少,越学不动。正反馈循环让门控收敛到“几乎总选一个专家”,共享池退化成了单专家,负迁移问题卷土重来。
缓解坍塌可以从几个方向入手。第一是门控 Dropout:在门控输出上随机丢弃部分权重,强制模型不能过度依赖某一个专家,相当于让分诊台偶尔“看花眼”,把病人误分给冷门医生,间接给冷门医生练手机会。第二是负载均衡正则:在损失函数中加入一项惩罚,鼓励各专家的平均门控权重接近均匀分布,让“接诊量”不要过于悬殊。第三是无害初始化:门控层的偏置初始化为较小值(或均匀值),避免一开始就偏心。
训练时还有两个实用细节:专家网络可以用稍大的学习率,让冷门专家更快追上;输入特征做 LayerNorm/BatchNorm 可以稳定门控的分数尺度。这些技巧的目标只有一个——让每个医生都有活儿干,医院整体才不会垮。
医院升级:与 PLE、CGC 等变体对比
MMoE 解决了“多个任务抢共享专家”的问题,但有个残留痛点:如果任务之间差异实在太大,光靠门控分配还不够,共享专家在同时服务两个差异巨大的任务时仍然会“精神分裂”。于是工程师们设计了两种升级版结构。
CGC(Customized Gate Control,定制门控控制) 的思路是“专属 + 共享”。在 MMoE 的共享专家之外,每个任务额外分配一组任务专属专家。专属专家只服务一个任务,确保核心知识不被其他任务干扰;共享专家负责捕捉通用模式。这相当于医院在共享诊室之外,给心内科和骨科各建了一间专属诊室——常规检查用共享设备,疑难杂症回自家诊室。
PLE(Progressive Layered Extraction,渐进式分层抽取) 更进一步,把“CGC 的一层结构”扩展成多层。每一层都有共享专家和各任务专属专家,层与层之间通过抽取网络传递信息。这样不仅任务之间有共享通道,不同层之间还有“渐进抽象”——低层共享简单模式,高层抽取复杂关联。PLE 适合任务关系复杂、且数据量充足的场景,但参数量和计算量也相应增大。
怎么选?简单记三个场景:任务相关度高、资源紧张,用硬共享就够了;任务中等冲突,用 MMoE 性价比最高;任务差异明显或需要极致效果,优先试 CGC/PLE。在实际推荐系统中,MMoE 常作为基线,CGC 作为升级选项,PLE 则用在业务复杂、预算充足的头部场景。
施工蓝图:TensorFlow 工程实现
纸上谈兵结束,最后把 MMoE 变成能跑的代码。用 TensorFlow/Keras 的函数式 API,搭建一个“2 个任务、3 个专家”的 MMoE 模型,完整代码见下一节。这里先讲实现要点。
第一,专家池用列表推导式创建:每个专家是一个两层的全连接网络,它们共享同一个输入,但参数各自独立。第二步,每个任务一个门控:门控是一个 Dense(n_experts, activation='softmax') 层,输入同样是 $\mathbf{x}$,输出维度等于专家数量。第三步,加权融合:把专家输出堆叠成形状 (batch, experts, dim) 的张量,再把门控权重扩展维度后逐元素相乘、求和,得到融合特征。这对应公式里 $\sum g_i f_i$ 的向量化实现。
工程上还有几个容易踩坑的点。一是 Embedding 输入:推荐场景的特征多为稀疏 ID,需要先接 Embedding 层把高维稀疏向量压缩成稠密向量,再送入 MMoE;二是多任务 Loss:每个任务塔输出一个 sigmoid,编译时传一个损失列表,可以给不同任务分配不同权重(比如点击率权重 0.6、转化率 0.4);三是梯度裁剪:多任务梯度叠加后容易爆炸,设置 clipnorm 是常规操作。最后,别忘记用 model.summary() 检查参数量——门控参数量应该远小于专家总量,否则分诊台比医生还复杂,就喧宾夺主了。
AI 应用场景
推荐系统多目标排序
阶段:模型训练与在线推理
解决的问题:短视频/电商场景需要同时预测点击率(CTR)、转化率(CVR)、完播率等多个目标。传统做法是每个目标单独训练一个模型,资源消耗大且无法共享用户行为特征;用硬参数共享又容易互相干扰。MMoE 让各目标共享特征表达,同时按需调配专家,显著缓解负迁移。
具体案例:某短视频 App 的推荐排序模型,输入用户画像、视频特征和上下文特征,三个任务塔分别预测点击、完播、点赞。改用 MMoE 后,相比硬共享基线,完播率任务的 AUC 提升约 1.2%,点击任务也不降反升,线上留存提升 0.4%。
智能客服意图与情感联合分析
阶段:自然语言理解(NLU)多任务建模
解决的问题:客服机器人需要同时判断用户意图(退款、投诉、咨询)和情感极性(正面、负面、中性)。意图分类关注语义关键词,情感分析关注情绪词,两个任务特征既有交叉又有冲突。MMoE 的门控能根据句子内容动态分配专家,让语义专家和情绪专家各司其职。
具体案例:某银行线上客服系统,将用户问题文本向量化后送入 MMoE,意图识别和情感分类两个任务塔共享 4 个专家。上线后意图识别准确率从 82% 提升到 87%,情感分类 F1 从 0.71 提升到 0.78,且模型参数量比两个独立模型减少 40%。
Python 演示
import numpy as npimport tensorflow as tffrom tensorflow.keras import layers, Model class MMoE(Model): """2 个任务共享 3 个专家的 MMoE 模型。""" def __init__(self, n_experts=3, expert_dim=16, n_tasks=2): super().__init__() self.n_experts = n_experts # 专家池:每个专家是一个全连接层 self.expert_net = [ layers.Dense(expert_dim, activation='relu') for _ in range(n_experts) ] # 每个任务一个门控,输出维度 = 专家数量 self.gate_net = [ layers.Dense(n_experts, activation='softmax') for _ in range(n_tasks) ] # 每个任务一个输出塔 self.task_net = [ layers.Dense(1, activation='sigmoid') for _ in range(n_tasks) ] def call(self, inputs): # 所有专家并行处理输入,堆叠成 (B, E, D) exp = tf.stack([net(inputs) for net in self.expert_net], axis=1) outs = [] for t in range(len(self.task_net)): # 门控为当前任务计算专家权重 g = self.gate_net[t](inputs) # (B, E) g = tf.expand_dims(g, axis=-1) # (B, E, 1) # 加权融合:权重 * 专家输出,再对专家维度求和 fused = tf.reduce_sum(exp * g, axis=1) # (B, D) outs.append(self.task_net[t](fused)) return outs def main(): # 模拟数据:256 个样本,8 维特征,2 个二分类任务 model = MMoE() x = np.random.randn(256, 8).astype(np.float32) y = [ np.random.randint(0, 2, 256).astype(np.float32), np.random.randint(0, 2, 256).astype(np.float32), ] model.compile( optimizer='adam', loss=['binary_crossentropy'] * 2, loss_weights=[0.6, 0.4], ) model.fit(x, y, epochs=3, batch_size=32, verbose=1) print('MMoE 训练完成') if __name__ == '__main__': main()
self.expert_net = [layers.Dense(...)]创建专家池,每个专家是独立的全连接网络,参数互不共享。self.gate_net = [layers.Dense(n_experts, activation='softmax')]每个任务一个门控,输出是专家数量维度的概率分布。exp = tf.stack([...], axis=1)把所有专家输出堆叠成(B, E, D)张量,便于后面按专家维度做加权。g = tf.expand_dims(g, axis=-1)把门控权重从(B, E)扩展成(B, E, 1),这样才能和专家输出逐元素相乘。fused = tf.reduce_sum(exp * g, axis=1)向量化实现公式 $\sum_i g_i f_i$,对专家维度求和得到融合特征。model.compile(loss=[...], loss_weights=[0.6, 0.4])多任务模型需要传损失列表,并用权重平衡各任务的重要性。
常见误区
误区很多人第一次见到 MMoE 的图,以为它是训练了多个独立模型,最后把结果投票或平均一下——就像随机森林那样。
正解MMoE 是一个模型内部的“分工写作”,不是多个模型的外部集成。专家网络之间共享同一份输入和损失信号,只是各自学不同侧重的表征;门控也不是投票器,而是可微的加权分配器,参数随训练端到端更新。集成模型各模型独立训练,MMoE 的专家则协同训练、互相牵制。
为何常见:因为“多个专家 + 权重组合”的外观和集成学习太像了,容易让人忽略它们其实在同一个模型里共享梯度。
误区有同学训练完看门控权重,发现任务 A 在专家 1 上权重是 0.7,任务 B 在专家 3 上权重是 0.8,就认为“任务 A 更依赖专家 1”,把门控权重当作任务的静态属性。
正解门控的输出是输入 $\mathbf{x}$ 的函数,同一个任务面对不同样本,分配方案完全不同。权重 0.7 只在某个输入附近成立,换一批用户可能就变成 0.3。要分析“任务依赖哪个专家”,应该统计门控权重的期望或分布,而不是看单次输出。
为何常见:因为模型可视化时常常只截取一个 batch 的门控输出,人脑天然倾向于把看到的数值解读成固定规律。
误区既然专家是共享的,那多加几个专家岂不是能 capture 更多特征?于是把专家数从 4 加到 32,结果效果不升反降。
正解专家过多会带来两个问题:一是加剧专家坍塌,门控更容易只依赖少数专家,其余专家变成“僵尸参数”;二是优化难度增加,小数据量下多余的专家开始过拟合噪声。实践上 2~8 个专家通常足够,堆数量不如调门控初始化和正则。
为何常见:因为“参数越多能力越强”的印象在深度学习里太深入人心,忽略了 MMoE 的专家是竞争共享关系,不是容量线性叠加。
误区在门控层加 Dropout 后,专家确实更多样了,于是认为这样就一劳永逸,再也不用关心坍塌。
正解Dropout 只是给门控引入随机扰动,延缓坍塌进程,不能从根本上改变“某些专家始终少被选中”的结构性失衡。彻底缓解需要组合手段:负载均衡正则、门控偏置初始化、专家输出归一化,甚至动态调整专家数量。Dropout 是“缓解剂”而非“特效药”。
为何常见:因为 Dropout 在防过拟合上的名声太响,人们容易把它当成“随机性万能药”,忽略了坍塌的根源是训练动态失衡而非过拟合。
面试题
Q1. MMoE 中的“多门控”指的是什么?
- A.多个专家各有一个独立门控
- B.每个任务拥有一个专属门控← 正确答案
- C.每个样本随机分配一个门控
- D.所有任务共用一个共享门控
答案:B。MMoE 的门控与任务一一对应,任务数 = 门控数。A 把门控挂到了专家上,C 把分配变成随机,D 退化成单门控,都不符合 MMoE 的“多任务专属分诊”思想。就像医院每个科室门口有自己的分诊台,而不是每个医生配一个分诊台。
Q2. 硬参数共享的多任务模型在哪一层共享参数?
- A.只在输出层共享权重
- B.底层共享、顶层任务分离← 正确答案
- C.专家各自独占全部参数
- D.顶层共享、底层任务分离
答案:B。硬参数共享的典型结构是底层网络全局共享,每个任务只在顶层接独立的输出头。这就像王医生共用一双手和听诊器,只在病历本上分开记录。A、D 把共享位置放反了,C 描述的是完全独立模型。
Q3. MMoE 中门控网络的输出通常经过哪个函数归一?
- A.修正线性单元激活函数
- B.S 型曲线映射函数
- C.归一化指数分布函数← 正确答案
- D.双曲正切激活函数
答案:C。门控输出是专家上的概率分布,要求所有权重和为 1 且非负,softmax 完全满足。ReLU 不归一,sigmoid 是逐元素独立映射、和不为 1,tanh 输出可负。softmax 就像分诊台把“适配分”换算成“分配比例”。
Q4. 负迁移在多任务学习中的典型表现是?
- A.任务越多训练速度越快
- B.共享参数相互拉扯导致效果差← 正确答案
- C.每个任务都收敛于局部最优
- D.模型参数量随任务数翻倍
答案:B。负迁移指多个任务在共享参数上的梯度方向互相冲突,导致联合模型的效果比单任务模型还差。A 是加速现象不是迁移问题,C 是优化层面的普遍现象,D 是参数规模的描述。就像王医生同时开降压药和止痛药,两种药互相抵消,病人病情反而恶化。
Q5. 专家坍塌指的是哪种现象?
- A.所有专家输出完全相同数值
- B.门控偏好少数专家其余闲置← 正确答案
- C.专家层参数在训练中逐渐消失
- D.任务塔坍塌成单层线性层
答案:B。专家坍塌是门控的“马太效应”:少数专家拿到绝大多数权重,其余专家梯度稀疏、学不到东西,最终退化为单专家模型。A 是输出同质化,C 是参数消失,D 描述的是任务塔退化,都不是坍塌的准确定义。
Q6. 下列哪些做法能缓解专家坍塌?
- A.门控层加入 Dropout← 正确答案
- B.使用负载均衡正则← 正确答案
- C.把专家数量无限增大
- D.固定门控权重不参与训练
答案:AB。Dropout 给门控注入随机性,负载均衡正则鼓励各专家接诊量均匀,都能缓解“赢家通吃”。无限增加专家会加剧坍塌且浪费参数;固定门控权重等于取消门控学习,两个都不对。就像给分诊台增加随机抽查和接诊量考核,才能让冷门医生也有病人练手。
Q7. CGC 相比 MMoE 的主要改进是?
- A.增加任务专属专家与共享专家← 正确答案
- B.移除门控只用最大池化
- C.将所有专家串行连接
- D.用循环网络替代专家层
答案:A。CGC 在共享专家之外为每个任务增加一组专属专家,既保留共享知识又有任务隔离,比 MMoE 更能应对任务差异大的场景。B、C、D 都改动了 MMoE 的基本结构,且违背 CGC 的设计初衷。相当于医院在共享诊室外给每个科室建了专属病房。
Q8. 关于 PLE 的说法正确的有?
- A.采用渐进式分层抽取结构← 正确答案
- B.每一层包含共享抽取网络← 正确答案
- C.专家之间完全不交互
- D.仅能处理两个任务场景
答案:AB。PLE 的核心是渐进式分层抽取,每层都包含共享专家和任务专属专家,层间通过抽取网络传递信息。C 说反了,PLE 恰恰强化了层间和任务间的交互。D 错误,PLE 支持任意多任务。PLE 像医院的分级转诊体系,基层处理小病、疑难杂症向专家层流转。
Q9. 在 MMoE 中,任务数 T、专家数 N 的关系是?
- A.门控数等于任务数专家共享← 正确答案
- B.门控数等于专家数任务数
- C.参数总量始终为任务乘专家
- D.两者数量必须完全相等
答案:A。MMoE 中门控数量等于任务数 T,专家池是全局共享的 N 个专家,T 和 N 没有绑定关系。B 把门控和专家数量搞混,C 错误因为专家本身可多层,D 更不合理。就像医院分诊台数量按科室来,医生的数量按规模来,两者互不约束。
Q10. 训练 MMoE 时工程上常做的设置包括?
- A.各任务 Loss 加权求和← 正确答案
- B.共享 Embedding 层← 正确答案
- C.门控权重全部初始化为零
- D.梯度裁剪防止异常更新← 正确答案
答案:ABD。多任务 Loss 加权平衡各目标重要性,共享 Embedding 在推荐场景中几乎必备,梯度裁剪防止多任务梯度叠加爆炸,这三点都是工程标配。C 错误:门控权重初始化为零会导致 softmax 输出均匀分布,这本身可行,但“全零初始化”若指权重矩阵为零,会破坏对称性训练,实际应随机小值初始化。分诊台刚开张时应该“一碗水端平”,但不能完全失去判断力。