- 人工智能
- 深度学习
- 机器学习
【免费下载链接】DeepCTR
Easy-to-use,Modular and Extendible package of deep-learning based CTR models .
本文聚焦 DeepCTR 开源仓库中
deepctr.models.multitask.mmoe模块(对应 Sphinx 文档 deepctr.models.multitask.mmoe.rst),完整讲解基于 Multi-gate Mixture-of-Experts(多门控专家混合)架构的多任务 CTR/CVR 建模方案。你将掌握 MMOE 的函数签名与全部参数语义、底层实现细节、参数约束校验逻辑,以及基于 examples/run_mtl.py 的端到端可运行训练评估流程。
一、MMOE 是什么:多任务学习中处理"任务相关性"的经典方案
在真实的推荐、广告、搜索业务中,模型往往需要同时预测多个目标,例如电商场景下的 CTR(点击率)与 CVR(转化率)、Census 收入预测场景下的"是否高收入"与"是否未婚"等。多任务学习(Multi-Task Learning, MTL)通过共享底层表示来提升数据利用率与泛化能力,但不同任务之间可能存在"负迁移"(negative transfer)——共享过深反而互相干扰。
MMOE(Multi-gate Mixture-of-Experts)由 Ma 等人在 KDD 2018 论文Modeling task relationships in multi-task learning with multi-gate mixture-of-experts中提出,其核心思想是:用多个并行的"专家网络"(Experts)共享输入表示,再为每个任务配备一个独立的"门控网络"(Gate),由门控网络按任务动态地对各专家输出做加权求和。这样每个任务都能学到适合自己的专家组合方式,从而在共享表示与任务独立性之间取得平衡。DeepCTR 将该架构实现为可直接调用的 Keras 模型工厂函数MMOE()。
二、架构与源码实现:一条调用链看懂 MMOE
MMOE 的完整实现位于 deepctr/models/multitask/mmoe.py,模型名MMOE与SharedBottom、ESMM、PLE一同在 deepctr/models/init.py 中导出,因此可以直接from deepctr.models import MMOE。
从源码结构看,MMOE()的构建流程可以拆解为如下四个阶段:
输入与特征处理:通过
build_input_features(dnn_feature_columns)建立特征输入层,再经input_from_feature_columns(features, dnn_feature_columns, l2_reg_embedding, seed)将稀疏特征(SparseFeat)映射为嵌入向量、稠密特征(DenseFeat)归一化后,由combined_dnn_input拼接为统一的 DNN 输入张量dnn_input。构建专家层(Experts):
for i in range(num_experts)循环中,每个专家都是一个独立的DNN(expert_dnn_hidden_units, ...)全连接网络,输出被tf.stack沿axis=1堆叠成形状为(None, num_experts, dim)的张量expert_concat。源码片段如下:
expert_outs = [] for i in range(num_experts): expert_network = DNN(expert_dnn_hidden_units, dnn_activation, l2_reg_dnn, dnn_dropout, dnn_use_bn, seed=seed, name='expert_' + str(i))(dnn_input) expert_outs.append(expert_network) expert_concat = Lambda(lambda x: tf.stack(x, axis=1))(expert_outs) # None,num_experts,dim- 构建门控网络(Gates)并加权融合:循环次数等于任务数(
num_tasks,即"一个 MMOE 层对应num_tasks个门")。每个任务的 Gate 先经过一个可选的DNN(gate_dnn_hidden_units, ...)(默认()表示直接使用输入),再通过一个Dense(num_experts, use_bias=False, activation='softmax')输出num_experts维的归一化权重;随后用reduce_sum(x[0] * x[1], axis=1)将专家堆叠张量与门权重逐元素相乘并按专家轴求和,得到该任务专属的融合表示gate_mul_expert:
for i in range(num_tasks): # one mmoe layer: nums_tasks = num_gates gate_input = DNN(gate_dnn_hidden_units, dnn_activation, l2_reg_dnn, dnn_dropout, dnn_use_bn, seed=seed, name='gate_' + task_names[i])(dnn_input) gate_out = Dense(num_experts, use_bias=False, activation='softmax', name='gate_softmax_' + task_names[i])(gate_input) gate_out = Lambda(lambda x: tf.expand_dims(x, axis=-1))(gate_out) gate_mul_expert = Lambda(lambda x: reduce_sum(x[0] * x[1], axis=1, keep_dims=False), name='gate_mul_expert_' + task_names[i])([expert_concat, gate_out]) mmoe_outs.append(gate_mul_expert)- 构建任务塔(Towers)与输出:每个任务从自己的 MMOE 融合表示出发,经过任务专属的
DNN(tower_dnn_hidden_units, ...),再接一个无偏置的Dense(1, use_bias=False)产生 logit,最后经PredictionLayer(task_type, name=task_name)输出。PredictionLayer的实现位于 deepctr/layers/core.py:binary任务会施加 sigmoid 得到概率,regression任务则直接输出回归值。最终Model(inputs=inputs_list, outputs=task_outs)返回一个多输入多输出的 Keras 模型。
从源码结构可以推断:MMOE 的门控权重完全由数据驱动、按任务学习,不同任务的 Gate 可以给同一批专家分配截然不同的权重,这正是它相比"所有任务共享同一底层"的 SharedBottom 更能缓解负迁移的原因。
三、API 参数详解:13 个参数的语义、默认值与取值范围
MMOE()的完整函数签名与默认值如下(来自 deepctr/models/multitask/mmoe.py):
MMOE(dnn_feature_columns, num_experts=3, expert_dnn_hidden_units=(256, 128), tower_dnn_hidden_units=(64,), gate_dnn_hidden_units=(), l2_reg_embedding=0.00001, l2_reg_dnn=0, seed=1024, dnn_dropout=0, dnn_activation='relu', dnn_use_bn=False, task_types=('binary', 'binary'), task_names=('ctr', 'ctcvr'))各参数含义整理如下:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
dnn_feature_columns | iterable | 必填 | 深度部分使用的全部特征,由SparseFeat/DenseFeat构成,与input_from_feature_columns直接对接 |
num_experts | int | 3 | 专家网络个数,必须大于 1,否则抛出ValueError("num_experts must be greater than 1") |
expert_dnn_hidden_units | list | (256, 128) | 每个专家 DNN 的层数与各层神经元数;可为空列表 |
tower_dnn_hidden_units | list | (64,) | 任务专属 DNN(塔)的层数与各层神经元数;可为空列表(示例中即设为[],直接由 MMOE 融合结果产出 logit) |
gate_dnn_hidden_units | list | () | 门控 DNN 的层数与各层神经元数;默认空表示门控直接从拼接输入做 softmax 加权 |
l2_reg_embedding | float | 0.00001 | 施加在嵌入向量上的 L2 正则强度 |
l2_reg_dnn | float | 0 | 施加在 DNN 权重上的 L2 正则强度 |
seed | int | 1024 | 随机种子,用于保证权重初始化与 dropout 的可复现性 |
dnn_dropout | float | 0 | DNN 的 dropout 概率,取值范围[0, 1) |
dnn_activation | str | 'relu' | DNN 使用的激活函数 |
dnn_use_bn | bool | False | 是否在激活前使用 BatchNormalization |
task_types | list of str | ('binary', 'binary') | 每个任务的损失类型,'binary'表示二分类 logloss,'regression'表示回归损失,例如['binary', 'regression'] |
task_names | list of str | ('ctr', 'ctcvr') | 每个任务的预测目标名,同时作为输出层与门控层的命名前缀 |
| 返回值 | — | — | 一个多输入多输出的 KerasModel实例,输出数量等于任务数 |
从 deepctr/layers/core.py 的DNN层实现看,dnn_dropout、dnn_use_bn、l2_reg_dnn、seed等参数最终都会透传给每一个专家、门控和任务塔 DNN,因此它们对模型的三类子网络是一致生效的。
四、参数约束与校验逻辑:写错会立即报错
MMOE()在建模前有一组显式的合法性校验(mmoe.py):
- 任务数必须大于 1:
num_tasks = len(task_names),若num_tasks <= 1抛出ValueError("num_tasks must be greater than 1")——MMOE 天然面向多任务,单任务请改用 DeepFM 等单目标模型; - 专家数必须大于 1:
num_experts <= 1时抛出ValueError("num_experts must be greater than 1"); - 任务类型与任务名数量一致:
len(task_types) != num_tasks时抛出ValueError("num_tasks must be equal to the length of task_types"); - 任务类型仅支持
binary与regression:传入其他值(如'multiclass')会抛出ValueError("task must be binary or regression, ... is illegal")。
这些校验意味着:与 ESMM 强制要求恰好两个任务(见 esmm.py)不同,MMOE 理论上支持 2 个及以上的任意数量任务,只需保证task_names、task_types一一对应。
五、完整可运行示例:Census 收入数据双任务训练
仓库提供了 MMOE 的开箱即用示例 examples/run_mtl.py,使用 Census Income 样本(census-income.sample)同时预测"年收入是否超过 5 万美元"(label_income)与"是否未婚"(label_marital)两个二分类任务。核心流程如下:
1. 数据预处理:读取census-income.sample,将income_50k映射为二值标签、由marital_stat构造未婚标签;稀疏特征用fillna('-1')填充并做LabelEncoder编码,稠密特征用MinMaxScaler归一化到[0, 1]。
2. 构造特征列:稀疏特征封装为SparseFeat(feat, data[feat].max() + 1, embedding_dim=4),稠密特征封装为DenseFeat(feat, 1),通过get_feature_names得到模型输入名列表。
3. 定义并训练模型(关键代码):
model = MMOE(dnn_feature_columns, tower_dnn_hidden_units=[], task_types=['binary', 'binary'], task_names=['label_income', 'label_marital']) model.compile("adam", loss=["binary_crossentropy", "binary_crossentropy"], metrics=['binary_crossentropy'], ) history = model.fit(train_model_input, [train['label_income'].values, train['label_marital'].values], batch_size=256, epochs=10, verbose=2, validation_split=0.2) pred_ans = model.predict(test_model_input, batch_size=256) print("test income AUC", round(roc_auc_score(test['label_income'], pred_ans[0]), 4)) print("test marital AUC", round(roc_auc_score(test['label_marital'], pred_ans[1]), 4))注意两个多任务建模要点:
- 损失必须是列表:
compile时loss传入与任务数等长的列表(这里是两个binary_crossentropy),metrics同理; - 标签必须是列表:
fit的y传入[train['label_income'].values, train['label_marital'].values],predict返回的pred_ans也是一个列表,pred_ans[0]、pred_ans[1]分别对应两个任务的预测概率,因此需要用roc_auc_score(test['label_income'], pred_ans[0])与roc_auc_score(test['label_marital'], pred_ans[1])分别评估两个任务的 AUC。
回归任务的适配方式同理:把对应位置的task_type改为'regression',loss改为'mean_squared_error',标签换成连续值即可(该约定与测试工具 tests/utils_mtl.py 中binary→binary_crossentropy、regression→mean_squared_error的映射一致)。
六、测试验证:MMOE 的可用性如何被保证
仓库的 tests/models/MTL_test.py 专门覆盖了 MMOE:
model = MMOE(dnn_feature_columns, num_experts=3, expert_dnn_hidden_units=(8,), tower_dnn_hidden_units=(8,), gate_dnn_hidden_units=(), task_types=['binary', 'binary'], task_names=['income', 'marital']) check_mtl_model(model, model_name, x, y_list, task_types=['binary', 'binary'])check_mtl_model(定义于 tests/utils_mtl.py)会依次验证:模型能否compile(adam + 逐任务 loss 列表)、能否fit一个 epoch、能否save_weights/load_weights往返、能否save_model/load_model(加载时需传入custom_objects)完整保存。这一测试链说明 MMOE 是"可训练、可保存、可加载"的完整 Keras 模型,可直接接入线上推理流程。
七、与其他多任务模型的定位差异
DeepCTR 的deepctr.models.multitask子包(deepctr/models/multitask/init.py)共提供四种多任务模型:
- SharedBottom:所有任务共享同一个底层 DNN,结构最简单,适合任务高度相关的场景;
- MMOE(本文主角):多专家 + 每任务独立门控,用数据驱动方式学习任务相关性;
- PLE:在 MMOE 基础上进一步引入"任务专属专家 + 共享专家"与多层门控(
num_levels、gate_dnn_hidden_units均可配置),针对性缓解负迁移; - ESMM:专为 CTR/CVR 全空间建模设计,强制两个
binary任务并通过ctcvr = ctr * cvr相乘建模转化链路(见 esmm.py)。
架构示意可参考仓库 docs/pics/multitaskmodels/MMOE.png,直观展示了多专家(experts)经多门控(gates)加权后分别接入各任务塔的拓扑结构。
选型建议(基于源码结构的推断,非官方结论):当多个业务目标间相关性较弱、或单个共享塔效果明显不均衡时,MMOE 是性价比很高的折中方案;若任务间负迁移严重,可进一步尝试 PLE。
八、使用注意事项小结
- 任务数约束:
task_names长度必须 ≥ 2,且与task_types等长;每个task_type只能是'binary'或'regression'; - 专家数约束:
num_experts必须 ≥ 2,默认 3 个专家是平衡容量与训练成本的常用起点; - 多输出约定:
compile/fit/predict均使用与任务数等长的列表;评估时逐个任务计算指标(如 AUC); - 特征列复用:
dnn_feature_columns与单任务 Deep 模型(如 DeepFM)完全一致,SparseFeat的embedding_dim、DenseFeat的transform_fn等用法通用,可通过deepctr.feature_column的SparseFeat/DenseFeat/get_feature_names直接复用; - 模型持久化:加载保存的模型文件时需要传入
deepctr.layers提供的custom_objects(测试中可见 tests/utils_mtl.py)。
综上,DeepCTR 的MMOE()以约百行源码完整实现了 KDD 2018 的多门控专家混合架构,配合run_mtl.py示例与MTL_test.py测试,是快速上手多任务 CTR 建模的低门槛入口:只需准备特征列与任务标签,即可在同一模型内同时输出多个任务概率,并逐个任务评估与调参。
- 人工智能
- 深度学习
- 机器学习
【免费下载链接】DeepCTR
Easy-to-use,Modular and Extendible package of deep-learning based CTR models .
相关推荐
DeepCTR SharedBottom 多任务学习模型:架构解析、参数详解与实战
DeepCTR SharedBottom 多任务学习模型:架构解析、参数详解与实战 导读 本文围绕 DeepCTR 开源仓库中 deepctr.models.m
人工智能深度学习机器学习Extism Go SDK 实战指南:在 Go 宿主应用中加载与调用 WebAssembly 插件
Extism Go SDK 实战指南:在 Go 宿主应用中加载与调用 WebAssembly 插件 Extism 是一套跨语言的插件运行时体系,其 Go SDK
人工智能深度学习机器学习DeepCTR Examples 实战指南:从 Criteo 分类、MovieLens 回归到多任务学习的完整示例解析
DeepCTR Examples 实战指南:从 Criteo 分类、MovieLens 回归到多任务学习的完整示例解析 本文是 DeepCTR 仓库中 exam
人工智能深度学习机器学习
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考