Delete 4.人工智能 directory

This commit is contained in:
zzm
2023-07-22 21:50:05 +08:00
committed by GitHub
parent e8c1fad4da
commit d77a9b0c51
396 changed files with 0 additions and 19301 deletions

View File

@@ -1,129 +0,0 @@
## 背景与动机
在推荐系统的精排模块多任务学习的模型结构已成业界的主流获得了广阔的应用。多任务学习multi-task learning本质上是希望使用一个模型完成多个任务的建模。在推荐系统中多任务学习一般即指多目标学习multi-label learning不同目标输入相同的feature进行联合训练是迁移学习的一种。他们之间的关系如图
<div align=center>
<img src="https://pic1.zhimg.com/80/v2-130d040474f34095ec6d8c81133da538_1440w.jpg" style="zoom:60%;" />
</div>
下面我们先讨论三个问题
**一、为什么要用多任务学习?**
1很多业界推荐的业务天然就是一个多目标的建模场景需要多目标共同优化。以微信视频号推荐为例打开一个视频如图首页上除了由于视频自动播放带来的“播放时长”、“完播率”用户播放时长占视频长度的比例目标之外还有大量的互动标签例如“点击好友头像”、“进入主页”、“关注”、“收藏”、“分享”、“点赞”、“评论”等。究竟哪一个标签最符合推荐系统的建模目标呢
<div align=center>
<img src="https://pic4.zhimg.com/80/v2-c18fc1ec65e308ee2e1477d7868007db_1440w.jpg" style="zoom:30%;" />
</div>
如果要用一个词来概括所有各式各样的推荐系统的终极目标那就是“用户满意度”但我们无法找到一个显示的指标量化用户满意度。业界一般使用“DAU”、“用户日均使用时长”、“留存率”来作为客观的间接的“用户满意度”或者说算法工程师绩效评价指标。而这些指标都是难以通过单一目标建模的以使用时长为例长视频播放长度天然大于短视频。所幸的是虽然没有显式的用户满意度评价指标但是现在的app都存在类似上述视频号推荐场景的丰富具体的隐式反馈。但这些独立的隐式反馈也存在一些挑战
- 目标偏差:点赞、分享表达的满意度可能比播放要高
- 物品偏差:不同视频的播放时长体现的满意度不一样,有的视频可能哄骗用户看到尾部(类似新闻推荐中的标题党)
- 用户偏差:有的用户表达满意喜欢用点赞,有的用户可能喜欢用收藏
因此我们需要使用多任务学习模型针对多个目标进行预测,并在线上融合多目标的预测结果进行排序。多任务学习也不能直接表达用户满意度,但是可以最大限度利用能得到的用户反馈信息进行充分的表征学习,并且可建模业务之间的关系,从而高效协同学习具体任务。
2工程便利不用针对不同的任务训练不同的模型。一般推荐系统中排序模块延时需求在40ms左右如果分别对每个任务单独训练一个模型难以满足需求。出于控制成本的目的需要将部分模型进行合并。合并之后能更高效的利用训练资源和进行模型的迭代升级。
**二、为什么多任务学习有效?**
当把业务独立建模变成多任务联合建模之后,有可能带来四种结果:
<div align=center>
<img src="https://pic1.zhimg.com/80/v2-44927ccdd6caf9685d3d9d5367af98dc_1440w.jpg" style="zoom:60%;" />
</div>
多任务学习的优势在于通过部分参数共享,联合训练,能在保证“还不错”的前提下,实现多目标共同提升。原因有以下几种:
- 任务互助:对于某个任务难学到的特征,可通过其他任务学习
- 隐式数据增强:不同任务有不同的噪声,一起学习可抵消部分噪声
- 学到通用表达,提高泛化能力:模型学到的是对所有任务都偏好的权重,有助于推广到未来的新任务
- 正则化:对于一个任务而言,其他任务的学习对该任务有正则化效果
**三、多任务学习都在研究什么问题**
如上所述多任务的核心优势在于通过不同任务的网络参数共享实现1+1>2的提升因此多任务学习的一大主流研究方向便是如何设计有效的网络结构。多个label的引入自然带来了多个loss那么如何在联合训练中共同优化多个loss则是关键问题。
- 网络结构设计主要研究哪些参数共享、在什么位置共享、如何共享。这一方向我们认为可以分为两大类第一类是在设计网络结构时考虑目标间的显式关系例如淘宝中点击之后才有购买行为发生以阿里提出的ESMM为代表另一类是目标间没有显示关系例如短视频中的收藏与分享在设计模型时不考虑label之间的量化关系以谷歌提出的MMOE为代表。
- 多loss的优化策略主要解决loss数值有大有小、学习速度有快有慢、更新方向时而相反的问题。最经典的两个工作有UWLUncertainty Weight通过自动学习任务的uncertainty给uncertainty大的任务小权重uncertainty小的任务大权重GradNorm结合任务梯度的二范数和loss下降梯度引入带权重的损失函数Gradient Loss并通过梯度下降更新该权重。
## loss加权融合
一种最简单的实现多任务学习的方式是对不同任务的loss进行加权。例如谷歌的Youtube DNN论文中提到的一种加权交叉熵
$$
\text { Weighted CE Loss }=-\sum_{i}\left[T_{i} y_{i} \log p_{i}+\left(1-y_{i}\right) \log \left(1-p_{i}\right)\right]
$$
其中![[公式]](https://www.zhihu.com/equation?tex=T_i) 为观看时长。在原始训练数据中正样本是视频展示后用户点击了该视频负样本则是展示后未点击这个一个标准的CTR预估问题。该loss通过改变训练样本的权重让所有负样本的权重都为 1而正样本的权重为点击后的视频观看时长 ![[公式]](https://www.zhihu.com/equation?tex=T_i) 。作者认为按点击率排序会倾向于把诱惑用户点击(用户未必真感兴趣)的视频排前面而观看时长能更好地反映出用户对视频的兴趣通过重新设计loss使得该模型在保证主目标点击的同时将视频观看时长转化为样本的权重达到优化平均观看时长的效果。
另一种更为简单粗暴的加权方式是人工手动调整权重,例如 0.3\*L(点击)+0.7*L\*(视频完播)
这种loss加权的方式优点如下
- 模型简单,仅在训练时通过梯度乘以样本权重实现对其它目标的加权
- 模型上线简单和base完全相同不需要额外开销
缺点:
- 本质上并不是多目标建模而是将不同的目标转化为同一个目标。样本的加权权重需要根据AB测试才能确定。
## Shared-Bottom
最早的多任务学习模型是底层共享结构Shared-Bottom如图所示。
通过共享底层模块学习任务间通用的特征表征再往上针对每一个任务设置一个Tower网络每个Tower网络的参数由自身对应的任务目标进行学习。Shared Bottom可以根据自身数据特点使用MLP、DeepFM、DCN、DIN等Tower网络一般使用简单的MLP。
代码如下共享特征embedding共享底层DNN网络任务输出层独立loss直接使用多个任务的loss值之和。
```python
def Shared_Bottom(dnn_feature_columns, num_tasks=None, task_types=None, task_names=None,
bottom_dnn_units=[128, 128], tower_dnn_units_lists=[[64,32], [64,32]],
l2_reg_embedding=0.00001, l2_reg_dnn=0, seed=1024,dnn_dropout=0,
dnn_activation='relu', dnn_use_bn=False):
features = build_input_features(dnn_feature_columns)
inputs_list = list(features.values())
sparse_embedding_list, dense_value_list = input_from_feature_columns(features, dnn_feature_columns, l2_reg_embedding,seed)
#共享输入特征
dnn_input = combined_dnn_input(sparse_embedding_list, dense_value_list)
#共享底层网络
shared_bottom_output = DNN(bottom_dnn_units, dnn_activation, l2_reg_dnn, dnn_dropout, dnn_use_bn, seed=seed)(dnn_input)
#任务输出层
tasks_output = []
for task_type, task_name, tower_dnn in zip(task_types, task_names, tower_dnn_units_lists):
tower_output = DNN(tower_dnn, dnn_activation, l2_reg_dnn, dnn_dropout, dnn_use_bn, seed=seed, name='tower_'+task_name)(shared_bottom_output)
logit = tf.keras.layers.Dense(1, use_bias=False, activation=None)(tower_output)
output = PredictionLayer(task_type, name=task_name)(logit)
tasks_output.append(output)
model = tf.keras.models.Model(inputs=inputs_list, outputs=tasks_output)
return model
```
优点:
- 浅层参数共享互相补充学习任务相关性越高模型loss优化效果越明显也可以加速训练。
缺点:
- 任务不相关甚至优化目标相反时(例如新闻的点击与阅读时长),可能会带来负收益,多个任务性能一起下降。
一般把Shared-Bottom的结构称作“参数硬共享”多任务学习网络结构设计的发展方向便是如何设计更灵活的共享机制从而实现“参数软共享”。
参考资料:
[https://developer.aliyun.com/article/793252](https://link.zhihu.com/?target=https%3A//developer.aliyun.com/article/793252)
https://zhuanlan.zhihu.com/p/291406172
Gradnorm: Gradient normalization for adaptive loss balancing in deep multitask networks (ICML'2018)
UWL: Multi-task learning using uncertainty to weigh losses for scene geometry and semantics (CVPR'2018)
YoutubeDNN: Deep neural networks for youtube recommendations (RecSys'2016)

View File

@@ -1,162 +0,0 @@
# ESMM
不同的目标由于业务逻辑,有显式的依赖关系,例如**曝光→点击→转化**。用户必然是在商品曝光界面中先点击了商品才有可能购买转化。阿里提出了ESMM(Entire Space Multi-Task Model)网络显式建模具有依赖关系的任务联合训练。该模型虽然为多任务学习模型但本质上是以CVR为主任务引入CTR和CTCVR作为辅助任务解决CVR预估的挑战。
## 背景与动机
传统的CVR预估问题存在着两个主要的问题**样本选择偏差**和**稀疏数据**。下图的白色背景是曝光数据灰色背景是点击行为数据黑色背景是购买行为数据。传统CVR预估使用的训练样本仅为灰色和黑色的数据。
<div align=center>
<img src="https://pic4.zhimg.com/80/v2-2f0df0f6933dd8405c478fcce91f7b6f_1440w.jpg" alt="img" style="zoom:33%;" />
</div>
这会导致两个问题:
- 样本选择偏差sample selection biasSSB如图所示CVR模型的正负样本集合={点击后未转化的负样本+点击后转化的正样本}但是线上预测的时候是样本一旦曝光就需要预测出CVR和CTR以排序样本集合={曝光的样本}。构建的训练样本集相当于是从一个与真实分布不一致的分布中采样得到的,这一定程度上违背了机器学习中训练数据和测试数据独立同分布的假设。
- 训练数据稀疏data sparsityDS点击样本只占整个曝光样本的很小一部分而转化样本又只占点击样本的很小一部分。如果只用点击后的数据训练CVR模型可用的样本将极其稀疏。
## 解决方案
阿里妈妈团队提出ESMM借鉴多任务学习的思路引入两个辅助任务CTR、CTCVR(已点击然后转化),同时消除以上两个问题。
三个预测任务如下:
- **pCTR**p(click=1 | impression)
- **pCVR**: p(conversion=1 | click=1,impression)
- **pCTCVR**: p(conversion=1, click=1 | impression) = p(click=1 | impression) * p(conversion=1 | click=1, impression)
> 注意其中只有CTR和CVR的label都同时为1时CTCVR的label才是正样本1。如果出现CTR=0CVR=1的样本则为不合法样本需删除。
> pCTCVR是指当用户已经点击的前提下用户会购买的概率pCVR是指如果用户点击了会购买的概率。
三个任务之间的关系为:
<div align=center>
<img src="https://pic1.zhimg.com/80/v2-7bbeb8767db5d6a157852c8cd4221548_1440w.jpg" alt="img" style="zoom: 50%;" />
</div>
其中x表示曝光y表示点击z表示转化。针对这三个任务设计了如图所示的模型结构
<div align=center>
<img src="https://pic1.zhimg.com/80/v2-6d8189bfe378dc4bf6f0db2ba0255eac_1440w.jpg" alt="img" style="zoom:50%;" />
</div>
如图主任务和辅助任务共享特征不同任务输出层使用不同的网络将cvr的预测值*ctr的预测值作为ctcvr任务的预测值利用ctcvr和ctr的label构造损失函数
<div align=center>
<img src="https://pic3.zhimg.com/80/v2-0098ab4556a8c67a1c12322ea3f89606_1440w.jpg" alt="img" style="zoom: 33%;" />
</div>
该架构具有两大特点,分别给出上述两个问题的解决方案:
- 帮助CVR模型在完整样本空间建模即曝光空间X
<div align=center>
<img src="https://pic1.zhimg.com/80/v2-0b0c6dc7d4c38fa422a2876b7c4cc638_1440w.jpg" alt="img" style="zoom:33%;" />
</div>
从公式中可以看出pCVR 可以由pCTR 和pCTCVR推导出。从原理上来说相当于分别单独训练两个模型拟合出pCTR 和pCTCVR再通过pCTCVR 除以pCTR 得到最终的拟合目标pCVR 。在训练过程中模型只需要预测pCTCVR和pCTR利用两种相加组成的联合loss更新参数。pCVR 只是一个中间变量。而pCTCVR和pCTR的数据是在完整样本空间中提取的从而相当于pCVR也是在整个曝光样本空间中建模。
- 提供特征表达的迁移学习embedding层共享。CVR和CTR任务的两个子网络共享embedding层网络的embedding层把大规模稀疏的输入数据映射到低维的表示向量该层的参数占了整个网络参数的绝大部分需要大量的训练样本才能充分学习得到。由于CTR任务的训练样本量要大大超过CVR任务的训练样本量ESMM模型中特征表示共享的机制能够使得CVR子任务也能够从只有展现没有点击的样本中学习从而能够极大地有利于缓解训练数据稀疏性问题。
模型训练完成后可以同时预测cvr、ctr、ctcvr三个指标线上根据实际需求进行融合或者只采用此模型得到的cvr预估值。
## 总结与拓展
可以思考以下几个问题
1. 能不能将乘法换成除法?
即分别训练CTR和CTCVR模型两者相除得到pCVR。论文提供了消融实验的结果表中的DIVISION模型比起BASE模型直接建模CTCVRR和CVR有显著提高但低于ESMM。原因是pCTR 通常很小,除以一个很小的浮点数容易引起数值不稳定问题。
<div align=center>
<img src="https://pic3.zhimg.com/80/v2-c0b2c860bd63a680d27c911c2e1ba8a2_1440w.jpg" alt="img" style="zoom:53%;" />
</div>
2. 网络结构优化Tower模型更换两个塔不一致
原论文中的子任务独立的Tower网络是纯MLP模型事实上业界在使用过程中一般会采用更为先进的模型例如DeepFM、DIN等两个塔也完全可以根据自身特点设置不一样的模型。这也是ESMM框架的优势子网络可以任意替换非常容易与其他学习模型集成。
3. 比loss直接相加更好的方式
原论文是将两个loss直接相加还可以引入动态加权的学习机制。
4. 更长的序列依赖建模?
有些业务的依赖关系不止有曝光-点击-转化三层,后续的改进模型提出了更深层次的任务依赖关系建模。
阿里的ESMM2: 在点击到购买之前用户还有可能产生加入购物车Cart、加入心愿单Wish等行为。
<div align=center>
<img src="https://pic2.zhimg.com/80/v2-4f9f5508412086315f85d1b7fda733e9_1440w.jpg" alt="img" style="zoom:53%;" />
</div>
相较于直接学习 click->buy (稀疏度约2.6%)可以通过Action路径将目标分解以Cart为例click->cart (稀疏 度为10%)cart->buy(稀疏度为12%)通过分解路径建立多任务学习模型来分步求解CVR模型缓解稀疏问题该模型同样也引入了特征共享机制。
美团的[AITM](https://zhuanlan.zhihu.com/p/508876139/[https://cloud.tencent.com/developer/article/1868117](https://cloud.tencent.com/developer/article/1868117)):信用卡业务中,用户转化通常是一个**曝光->点击->申请->核卡->激活**的过程具有5层的链路。
<div align=center>
<img src="https://pic4.zhimg.com/80/v2-0ecf42e999795511f40ac6cd7b85eccf_1440w.jpg" alt="img" style="zoom:50%;" />
</div>
美团提出了一种自适应信息迁移多任务(**Adaptive Information Transfer Multi-taskAITM**框架该框架通过自适应信息迁移AIT)模块对用户多步转化之间的序列依赖进行建模。AIT模块可以自适应地学习在不同的转化阶段需要迁移什么和迁移多少信息。
总结:
ESMM首创了利用用户行为序列数据在完整样本空间建模并提出利用学习CTR和CTCVR的辅助任务迂回学习CVR避免了传统CVR模型经常遭遇的样本选择偏差和训练数据稀疏的问题取得了显著的效果。
## 代码实践
与Shared-Bottom同样的共享底层机制之后两个独立的Tower网络分别输出CVR和CTR计算loss时只利用CTR与CTCVR的loss。CVR Tower完成自身网络更新CTR Tower同时完成自身网络和Embedding参数更新。在评估模型性能时重点是评估主任务CVR的auc。
```python
def ESSM(dnn_feature_columns, task_type='binary', task_names=['ctr', 'ctcvr'],
tower_dnn_units_lists=[[128, 128],[128, 128]], l2_reg_embedding=0.00001, l2_reg_dnn=0,
seed=1024, dnn_dropout=0,dnn_activation='relu', dnn_use_bn=False):
features = build_input_features(dnn_feature_columns)
inputs_list = list(features.values())
sparse_embedding_list, dense_value_list = input_from_feature_columns(features, dnn_feature_columns, l2_reg_embedding,seed)
dnn_input = combined_dnn_input(sparse_embedding_list, dense_value_list)
ctr_output = DNN(tower_dnn_units_lists[0], dnn_activation, l2_reg_dnn, dnn_dropout, dnn_use_bn, seed=seed)(dnn_input)
cvr_output = DNN(tower_dnn_units_lists[1], dnn_activation, l2_reg_dnn, dnn_dropout, dnn_use_bn, seed=seed)(dnn_input)
ctr_logit = tf.keras.layers.Dense(1, use_bias=False, activation=None)(ctr_output)
cvr_logit = tf.keras.layers.Dense(1, use_bias=False, activation=None)(cvr_output)
ctr_pred = PredictionLayer(task_type, name=task_names[0])(ctr_logit)
cvr_pred = PredictionLayer(task_type)(cvr_logit)
ctcvr_pred = tf.keras.layers.Multiply(name=task_names[1])([ctr_pred, cvr_pred])#CTCVR = CTR * CVR
model = tf.keras.models.Model(inputs=inputs_list, outputs=[ctr_pred, cvr_pred, ctcvr_pred])
return model
```
测试数据集:
adult[https://archive.ics.uci.edu/ml/datasets/census+income](https://link.zhihu.com/?target=https%3A//archive.ics.uci.edu/ml/datasets/census%2Bincome)
将里面两个特征转为label完成两个任务的预测
- 任务1预测该用户收入是否大于50K
- 任务2预测该用户的婚姻是否未婚。
以上两个任务均为二分类任务使用交叉熵作为损失函数。在ESMM框架下我们把任务1作为CTR任务任务2作为CVR任务两者label相乘得到CTCVR任务的标签。
除ESSM之外之后的MMOE、PLE模型都使用本数据集做测试。
> 注意上述代码并未实现论文模型图中提到的field element-wise +模块。该模块实现较为简单即分别把用户、商品相关特征的embedding求和再拼接然后输入Tower网络。我们使用数据不具有该属性暂未区分。
参考资料:
https://www.zhihu.com/question/475787809
https://zhuanlan.zhihu.com/p/37562283
美团:[https://cloud.tencent.com/developer/article/1868117](https://link.zhihu.com/?target=https%3A//cloud.tencent.com/developer/article/1868117)
Entire Space Multi-Task Model: An Effective Approach for Estimating Post-Click Conversion Rate (SIGIR'2018)

View File

@@ -1,178 +0,0 @@
# MMOE
## 写在前面
MMOE是2018年谷歌提出的全称是Multi-gate Mixture-of-Experts 对于多个优化任务引入了多个专家进行不同的决策和组合最终完成多目标的预测。解决的是硬共享里面如果多个任务相似性不是很强底层的embedding学习反而相互影响最终都学不好的痛点。
本篇文章首先是先了解下Hard-parameter sharing以及存在的问题然后引出MMOE对理论部分进行整理最后是参考deepctr简单复现。
## 背景与动机
推荐系统中,即使同一个场景,常常也不只有一个业务目标。 在Youtube的视频推荐中推荐排序任务不仅需要考虑到用户点击率完播率也需要考虑到一些满意度指标例如对视频是否喜欢用户观看后对视频的评分在淘宝的信息流商品推荐中需要考虑到点击率也需要考虑转化率而在一些内容场景中需要考虑到点击和互动、关注、停留时长等指标。
模型中,如果采用一个网络同时完成多个任务,就可以把这样的网络模型称为多任务模型, 这种模型能在不同任务之间学习共性以及差异性,能够提高建模的质量以及效率。 常见的多任务模型的设计范式大致可以分为三大类:
* hard parameter sharing 方法: 这是非常经典的一种方式,底层是共享的隐藏层,学习各个任务的共同模式,上层用一些特定的全连接层学习特定任务模式。
<div align=center>
<img src="https://img-blog.csdnimg.cn/ed10df1df313413daf2a6a6174ef4f8c.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA57-75rua55qE5bCPQOW8ug==,size_1,color_FFFFFF,t_70,g_se,x_16#pic_center" alt="在这里插入图片描述" style="zoom:70%;" />
</div>
这种方法目前用的也有比如美团的猜你喜欢知乎推荐的Ranking等 这种方法最大的优势是Task越多 单任务更加不可能过拟合,即可以减少任务之间过拟合的风险。 但是劣势也非常明显就是底层强制的shared layers难以学习到适用于所有任务的有效表达。 **尤其是任务之间存在冲突的时候**。MMOE中给出了实验结论当两个任务相关性没那么好(比如排序中的点击率与互动,点击与停留时长),此时这种结果会遭受训练困境,毕竟所有任务底层用的是同一组参数。
* soft parameter sharing: 硬的不行,那就来软的,这个范式对应的结果从`MOE->MMOE->PLE`等。 即底层不是使用共享的一个shared bottom而是有多个tower 称为多个专家然后往往再有一个gating networks在多任务学习时给不同的tower分配不同的权重那么这样对于不同的任务可以允许使用底层不同的专家组合去进行预测相较于上面所有任务共享底层这个方式显得更加灵活
* 任务序列依赖关系建模这种适合于不同任务之间有一定的序列依赖关系。比如电商场景里面的ctr和cvr其中cvr这个行为只有在点击之后才会发生。所以这种依赖关系如果能加以利用可以解决任务预估中的样本选择偏差(SSB)和数据稀疏性(DS)问题
* 样本选择偏差: 后一阶段的模型基于上一阶段采样后的样本子集训练,但最终在全样本空间进行推理,带来严重泛化性问题
* 样本稀疏: 后一阶段的模型训练样本远小于前一阶段任务
<br>ESSM是一种较为通用的任务序列依赖关系建模的方法除此之外阿里的DBMTLESSM2等工作都属于这一个范式。 这个范式可能后面会进行整理,本篇文章不过多赘述。
通过上面的描述能大体上对多任务模型方面的几种常用建模范式有了解然后也知道了hard parameter sharing存在的一些问题即不能很好的权衡特定任务的目标与任务之间的冲突关系。而这也就是MMOE模型提出的一个动机所在了 那么下面的关键就是MMOE模型是怎么建模任务之间的关系的又是怎么能使得特定任务与任务关系保持平衡的
带着这两个问题下面看下MMOE的细节。
## MMOE模型的理论及论文细节
MMOE模型结构图如下。
<div align=center>
<img src="https://img-blog.csdnimg.cn/29c5624f2c8a46c097f097af7dbf4b45.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA57-75rua55qE5bCPQOW8ug==,size_2,color_FFFFFF,t_70,g_se,x_16#pic_center" alt="在这里插入图片描述" style="zoom:70%;" />
</div>
这其实是一个演进的过程首先hard parameter sharing这个就不用过多描述了 下面主要是看MOE模型以及MMOE模型。
### 混合专家模型
我们知道共享的这种模型结构,会遭受任务之间冲突而导致可能无法很好的收敛,从而无法学习到任务之间的共同模式。这个结构也可以看成是多个任务共用了一个专家。
先抛开任务关系, 我们发现一个专家在多任务学习上的表达能力很有限,于是乎,尝试引入多个专家,这就慢慢的演化出了混合专家模型。 公式表达如下:
$$
y=\sum_{i=1}^{n} g(x)_{i} f_{i}(x)
$$
这里的$y$表示的是多个专家的汇总输出,接下来这个东西要过特定的任务塔去得到特定任务的输出。 这里还加了一个门控网络机制,就是一个注意力网络, 来学习各个专家的重要性权重$\sum_{i=1}^{n} g(x)_{i}=1$。$f_i(x)$就是每个专家的输出, 而$g(x)_i$就是每个专家对应的权重。 虽然感觉这个东西,无非就是在单个专家的基础上多引入了几个全连接网络,然后又给这几个全连接网络加权,但是在我看来,这里面至少蕴含了好几个厉害的思路:
1. 模型集成思想: 这个东西很像bagging的思路即训练多个模型进行决策这个决策的有效性显然要比单独一个模型来的靠谱一点不管是从泛化能力表达能力学习能力上应该都强于一个模型
2. 注意力思想: 为了增加灵活性, 为不同的模型还学习了重要性权重,这可能考虑到了在学习任务的共性模式上, 不同的模型学习的模式不同,那么聚合的时候,显然不能按照相同的重要度聚合,所以为各个专家学习权重,默认了不同专家的决策地位不一样。这个思想目前不过也非常普遍了。
3. multi-head机制: 从另一个角度看, 多个专家其实代表了多个不同head, 而不同的head代表了不同的非线性空间之所以说表达能力增强了是因为把输入特征映射到了不同的空间中去学习任务之间的共性模式。可以理解成从多个角度去捕捉任务之间的共性特征模式。
MOE使用了多个混合专家增加了各种表达能力但是 一个门控并不是很灵活,因为这所有的任务,最终只能选定一组专家组合,即这个专家组合是在多个任务上综合衡量的结果,并没有针对性了。 如果这些任务都比较相似,那就相当于用这一组专家组合确实可以应对这多个任务,学习到多个相似任务的共性。 但如果任务之间差的很大,这种单门控控制的方式就不行了,因为此时底层的多个专家学习到的特征模式相差可能会很大,毕竟任务不同,而单门控机制选择专家组合的时候,肯定是选择出那些有利于大多数任务的专家, 而对于某些特殊任务,可能学习的一塌糊涂。
所以,这种方式的缺口很明显,这样,也更能理解为啥提出多门控控制的专家混合模型了。
### MMOE结构
Multi-gate Mixture-of-Experts(MMOE)的魅力就在于在OMOE的基础上对于每个任务都会涉及一个门控网络这样对于每个特定的任务都能有一组对应的专家组合去进行预测。更关键的时候参数量还不会增加太多。公式如下
$$
y_{k}=h^{k}\left(f^{k}(x)\right),
$$
where $f^{k}(x)=\sum_{i=1}^{n} g^{k}(x)_{i} f_{i}(x)$. 这里的$k$表示任务的个数。 每个门控网络是一个注意力网络:
$$
g^{k}(x)=\operatorname{softmax}\left(W_{g k} x\right)
$$
$W_{g k} \in \mathbb{R}^{n \times d}$表示权重矩阵, $n$是专家的个数, $d$是特征的维度。
上面的公式这里不用过多解释。
这个改造看似很简单只是在OMOE上额外多加了几个门控网络但是却起到了杠杆般的效果我这里分享下我的理解。
* 首先就刚才分析的OMOE的问题在专家组合选取上单门控会产生限制此时如果多个任务产生了冲突这种结构就无法进行很好的权衡。 而MMOE就不一样了。MMOE是针对每个任务都单独有个门控选择专家组合那么即使任务冲突了也能根据不同的门控进行调整选择出对当前任务有帮助的专家组合。所以我觉得单门控做到了**针对所有任务在专家选择上的解耦**,而多门控做到了**针对各个任务在专家组合选择上的解耦**。
* 多门控机制能够建模任务之间的关系了。如果各个任务都冲突, 那么此时有多门控的帮助, 此时让每个任务独享一个专家,如果任务之间能聚成几个相似的类,那么这几类之间应该对应的不同的专家组合,那么门控机制也可以选择出来。如果所有任务都相似,那这几个门控网络学习到的权重也会相似,所以这种机制把任务的无关,部分相关和全相关进行了一种统一。
* 灵活的参数共享, 这个我们可以和hard模式或者是针对每个任务单独建模的模型对比对于hard模式所有任务共享底层参数而每个任务单独建模是所有任务单独有一套参数算是共享和不共享的两个极端对于都共享的极端害怕任务冲突而对于一点都不共享的极端无法利用迁移学习的优势模型之间没法互享信息互为补充容易遭受过拟合的困境另外还会增加计算量和参数量。 而MMOE处于两者的中间既兼顾了如果有相似任务那就参数共享模式共享互为补充如果没有相似任务那就独立学习互不影响。 又把这两种极端给进行了统一。
* 训练时能快速收敛这是因为相似的任务对于特定的专家组合训练都会产生贡献这样进行一轮epoch相当于单独任务训练时的多轮epoch。
OK 到这里就把MMOE的故事整理完了模型结构本身并不是很复杂非常符合"大道至简"原理,简单且实用。
那么, 为什么多任务学习为什么是有效的呢? 这里整理一个看到比较不错的答案:
>多任务学习有效的原因是引入了归纳偏置,两个效果:
> - 互相促进: 可以把多任务模型之间的关系看作是互相 先验知识,也称为归纳迁移,有了对模型的先验假设,可以更好提升模型的效果。解决数据稀疏性其实本身也是迁移学习的一个特性,多任务学习中也同样会体现
> - 泛化作用不同模型学到的表征不同可能A模型学到的是B模型所没有学好的B模型也有其自身的特点而这一点很可能A学不好这样一来模型健壮性更强
## MMOE模型的简单复现之多任务预测
### 模型概貌
这里是MMOE模型的简单复现参考的deepctr。
由于MMOE模型不是很复杂所以这里就可以直接上代码然后简单解释
```python
def 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, dnn_dropout=0, dnn_activation='relu',
dnn_use_bn=False, task_types=('binary', 'binary'), task_names=('ctr', 'ctcvr')):
num_tasks = len(task_names)
# 构建Input层并将Input层转成列表作为模型的输入
input_layer_dict = build_input_layers(dnn_feature_columns)
input_layers = list(input_layer_dict.values())
# 筛选出特征中的sparse和Dense特征 后面要单独处理
sparse_feature_columns = list(filter(lambda x: isinstance(x, SparseFeat), dnn_feature_columns))
dense_feature_columns = list(filter(lambda x: isinstance(x, DenseFeat), dnn_feature_columns))
# 获取Dense Input
dnn_dense_input = []
for fc in dense_feature_columns:
dnn_dense_input.append(input_layer_dict[fc.name])
# 构建embedding字典
embedding_layer_dict = build_embedding_layers(dnn_feature_columns)
# 离散的这些特特征embedding之后然后拼接然后直接作为全连接层Dense的输入所以需要进行Flatten
dnn_sparse_embed_input = concat_embedding_list(sparse_feature_columns, input_layer_dict, embedding_layer_dict, flatten=False)
# 把连续特征和离散特征合并起来
dnn_input = combined_dnn_input(dnn_sparse_embed_input, dnn_dense_input)
# 建立专家层
expert_outputs = []
for i in range(num_experts):
expert_network = DNN(expert_dnn_hidden_units, dnn_activation, l2_reg_dnn, dnn_dropout, dnn_use_bn, seed=2022, name='expert_'+str(i))(dnn_input)
expert_outputs.append(expert_network)
expert_concat = Lambda(lambda x: tf.stack(x, axis=1))(expert_outputs)
# 建立多门控机制层
mmoe_outputs = []
for i in range(num_tasks): # num_tasks=num_gates
# 建立门控层
gate_input = DNN(gate_dnn_hidden_units, dnn_activation, l2_reg_dnn, dnn_dropout, dnn_use_bn, seed=2022, 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 multiply the expert
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_outputs.append(gate_mul_expert)
# 每个任务独立的tower
task_outputs = []
for task_type, task_name, mmoe_out in zip(task_types, task_names, mmoe_outputs):
# 建立tower
tower_output = DNN(tower_dnn_hidden_units, dnn_activation, l2_reg_dnn, dnn_dropout, dnn_use_bn, seed=2022, name='tower_'+task_name)(mmoe_out)
logit = Dense(1, use_bias=False, activation=None)(tower_output)
output = PredictionLayer(task_type, name=task_name)(logit)
task_outputs.append(output)
model = Model(inputs=input_layers, outputs=task_outputs)
return model
```
这个其实比较简单, 首先是传入封装好的dnn_features_columns 这个是
```python
dnn_features_columns = [SparseFeat(feat, feature_max_idx[feat], embedding_dim=4) for feat in sparse_features] \
+ [DenseFeat(feat, 1) for feat in dense_features]
```
就是数据集先根据特征类别分成离散型特征和连续型特征然后通过sparseFeat或者DenseFeat进行封装起来组成的一个列表。
传入之后, 首先为这所有的特征列建立Input层然后选择出离散特征和连续特征来连续特征直接拼接即可 而离散特征需要过embedding层得到连续型输入。把这个输入与连续特征拼接起来就得到了送入专家的输入。
接下来建立MMOE的多个专家 这里的专家直接就是DNN当然这个可以替换比如MOSE里面就用了LSTM这样的搭建模型方式非常灵活替换起来非常简单。 把输入过多个专家得到的专家的输出,这里放到了列表里面。
接下来建立多个门控网络由于MMOE里面是每个任务会有一个单独的门控进行控制所以这里的门控网络个数和任务数相同门控网络也是DNN接收输入得到专家个输出作为每个专家的权重把每个专家的输出加权组合得到门控网络最终的输出放到列表中这里的列表长度和task_num对应。
接下来, 为每个任务建立tower学习特定的feature信息。同样也是DNN接收的输入是上面列表的输出每个任务的门控输出输入到各自的tower里面得到最终的输出即可。 最终的输出也是个列表,对应的每个任务最终的网络输出值。
这就是整个MMOE网络的搭建逻辑。
**参考资料**:
* [MMOE论文](https://dl.acm.org/doi/pdf/10.1145/3219819.3220007)
* [Recommending What Video to Watch Next: A Multitask
Ranking System](https://dl.acm.org/doi/pdf/10.1145/3298689.3346997)
* [Multitask Mixture of Sequential Experts for User Activity Streams](https://research.google/pubs/pub49274/)
* [推荐系统中的多目标学习](https://zhuanlan.zhihu.com/p/183760759)
* [推荐精排模型之多目标](https://zhuanlan.zhihu.com/p/221738556)
* [Youtube视频推荐中应用MMOE模型](http://t.zoukankan.com/Lee-yl-p-13274642.html)
* [多任务学习论文导读Recommending What Video to Watch Next-A Multitask Ranking System](https://blog.csdn.net/fanzitao/article/details/104525843/)
* [多任务模型之MoSE](https://zhuanlan.zhihu.com/p/161628342)

View File

@@ -1,390 +0,0 @@
# PLE
**PLE**(Progressive Layered Extraction)模型由腾讯PCG团队在2020年提出主要为了解决跷跷板问题该论文获得了RecSys'2020的最佳长论文Best Lone Paper Award
## 背景与动机
文章首先提出多任务学习中不可避免的两个缺点:
- 负迁移Negative Transfer针对相关性较差的任务使用shared-bottom这种硬参数共享的机制会出现负迁移现象不同任务之间存在冲突时会导致模型无法有效进行参数的学习不如对多个任务单独训练。
- 跷跷板现象Seesaw Phenomenon针对相关性较为复杂的场景通常不可避免出现跷跷板现象。多任务学习模式下往往能够提升一部分任务的效果但同时需要牺牲其他任务的效果。即使通过MMOE这种方式减轻负迁移现象跷跷板问题仍然广泛存在。
在腾讯视频推荐场景下,有两个核心建模任务:
- VCR(View Completion Ratio):播放完成率,播放时间占视频时长的比例,回归任务
- VTR(View Through Rate) :有效播放率,播放时间是否超过某个阈值,分类任务
这两个任务之间的关系是复杂的在应用以往的多任务模型中发现要想提升VTR准确率则VCR准确率会下降反之亦然。
上一小节提到的MMOE网络存在如下几个缺点
- MMOE中所有的Expert是被所有任务所共享这可能无法捕捉到任务之间更复杂的关系从而给部分任务带来一定的噪声。
- 在复杂任务机制下MMOE不同专家在不同任务的权重学的差不多
- 不同的Expert之间没有交互联合优化的效果有所折扣
## 解决方案
为了解决跷跷板现象以及优化MMOE模型PLE在网络结构设计上提出两大改进
**一、CGC**(Customized Gate Control) 定制门控
PLE将共享的部分和每个任务特定的部分**显式的分开**强化任务自身独立特性。把MMOE中提出的Expert分成两种任务特定task-specific和任务共享task-shared。保证expert“各有所得”更好的降低了弱相关性任务之间参数共享带来的问题。
网络结构如图所示同样的特征输入分别送往三类不同的专家模型任务A专家、任务B专家、任务共享专家再通过门控机制加权聚合之后输入各自的Tower网络。门控网络把原始数据和expert网络输出共同作为输入通过单层全连接网络+softmax激活函数得到分配给expert的加权权重与attention机制类型。
<div align=center>
<img src="https://pic3.zhimg.com/80/v2-c92975f7c21cc568a13cd9447adc757a_1440w.jpg" style="zoom:40%;" />
</div>
任务A有 ![[公式]](https://www.zhihu.com/equation?tex=m_A) 个expert任务B有 ![[公式]](https://www.zhihu.com/equation?tex=m_B) 个expert另外还有 ![[公式]](https://www.zhihu.com/equation?tex=m_S) 个任务A、B共享的Expert。这样对Expert做一个显式的分割可以让task-specific expert只受自己任务梯度的影响不会受到其他任务的干扰每个任务保底有一个独立的网络模型)而只有task-shared expert才受多个任务的混合梯度影响。
MMOE则是将所有Expert一视同仁都加权输入到每一个任务的Tower其中任务之间的关系完全交由gate自身进行学习。虽然MMOE提出的门控机制理论上可以捕捉到任务之间的关系比如任务A可能与任务B确实无关则MMOE中gate可以学到让个别专家对于任务A的权重趋近于0近似得到PLE中提出的task-specific expert。如果说MMOE是希望让expert网络可以对不同的任务各有所得则PLE是保证让expert网络各有所得。
二、**PLE** (progressive layered extraction) 分层萃取
PLE就是上述CGC网络的多层纵向叠加以获得更加丰富的表征能力。在分层的机制下Gate设计成两种类型使得不同类型Expert信息融合交互。task-share gate融合所有Expert信息task-specific gate只融合specific expert和share expert。模型结构如图
<div align=center>
<img src="https://pic2.zhimg.com/80/v2-ff3b4aff3511e6e56a3b509f244c5ab1_1440w.jpg" style="zoom:40%;" />
</div>
将任务A、任务B和shared expert的输出输入到下一层下一层的gate是以这三个上一层输出的结果作为门控的输入而不是用原始input特征作为输入。这使得gate同时融合task-shares expert和task-specific expert的信息论文实验中证明这种不同类型expert信息的交叉可以带来更好的效果。
三、多任务loss联合优化
该论文专门讨论了loss设计的问题。在传统的多任务学习模型中多任务的loss一般为
<div align=center>
<img src="https://pic4.zhimg.com/80/v2-ec1a0ae2a4001fea296662a9a5a1942b_1440w.jpg" style="zoom:33%;" />
</div>
其中K是指任务数 ![[公式]](https://www.zhihu.com/equation?tex=w_k) 是每个任务各自对应的权重。这种loss存在两个关键问题
- 不同任务之间的样本空间不一致:在视频推荐场景中,目标之间的依赖关系如图,曝光→播放→点击→(分享、评论),不同任务有不同的样本空间。
<div align=center>
<img src="https://pic3.zhimg.com/80/v2-bdf39ef6fcaf000924294cb010642fce_1440w.jpg" style="zoom:63%;" />
</div>
PLE将训练样本空间作为全部任务样本空间的并集在分别针对每个任务算loss时只考虑该任务的样本的空 间一般需对这种数据集会附带一个样本空间标签。loss公式如下
<div align=center>
<img src="https://pic2.zhimg.com/80/v2-8defd1e5d1ba896bb2d18bdb1db4e3cd_1440w.jpg" style="zoom:40%;" />
</div>
其中, ![[公式]](https://www.zhihu.com/equation?tex=%5Cdelta_%7Bk%7D%5E%7Bi%7D+%5Cin%5C%7B0%2C1%5C%7D%2C+%5Cdelta_%7Bk%7D%5E%7Bi%7D+) 表示样本i是否处于任务k的样本空间。
- 不同任务各自独立的权重设定PLE提出了一种加权的规则它的思想是随着迭代次数的增加任务的权重应当不断衰减。它为每个任务设定一个初始权重 ![[公式]](https://www.zhihu.com/equation?tex=w_%7Bk%2C0%7D) ,再按该公式进行更新:
<div align=center>
<img src="https://pic1.zhimg.com/80/v2-2fbd23599bd2cd62222607e76cb975ec_1440w.jpg" style="zoom:40%;" />
</div>
## 实验
该论文的一大特点是提供了极其丰富的实验,首先是在自身大规模数据集上的离线实验。
第一组实验是两个关系复杂的任务VTR回归与VCR分类如表1实验结果证明PLE可以实现多任务共赢而其他的硬共享或者软共享机制则会导致部分任务受损。
<div align=center>
<img src="https://pic1.zhimg.com/80/v2-4a190a8a3bcd810fbe1e810171ddc25c_1440w.jpg" alt="img" style="zoom: 33%;" />
</div>
第二组实验是两个关系简单清晰的任务CTR与VCR都是分类任务且CTR→VCR存在任务依赖关系如表2这种多任务下基本上所有参数共享的模型都能得到性能的提升而PLE的提升效果最为明显。
<div align=center>
<img src="https://pic4.zhimg.com/80/v2-29baaf461d29a4eff32e7ea324ef7f77_1440w.jpg" alt="img" style="zoom: 50%;" />
</div>
第三组实验则是线上的A/B Test上面两组离线实验中其实PLE相比于其他baseline模型无论是回归任务的mse还是分类任务的auc提升都不是特别显著。在推荐场景中评估模型性能的最佳利器还是线上的A/B Test。作者在pcg视频推荐的场景中将部分用户随机划分到不同的实验组中用PLE模型预估VTR和VCR进行四周的实验。如表3所示线上评估指标总播放完成视频数量和总播放时间均得到了较为显著的提升而硬参数共享模型则带对两个指标都带来显著的下降。
<div align=center>
<img src="https://pic3.zhimg.com/80/v2-d6daf1d58fa5edd9fa96aefd254f71ee_1440w.jpg" alt="img" style="zoom: 50%;" />
</div>
第四组实验中作者引入了更多的任务验证PLE分层结构的必要性。如表4随着任务数量的增加PLE对比CGC的优势更加显著。
<div align=center>
<img src="https://pic4.zhimg.com/80/v2-0b13558bc7e95f601c60a26deaff9acf_1440w.jpg" alt="img" style="zoom:50%;" />
</div>
文中也设计实验单独对MMOE和CGC的专家利用率进行对比分析为了实现方便和公平每个expert都是一个一层网络每个expert module都只有一个expert每一层只有3个expert。如图所示柱子的高度和竖直短线分别表示expert权重的均值和方差。
<div align=center>
<img src="https://pic4.zhimg.com/80/v2-557473be41f7f6fa5efc1ff17e21bab7_1440w.jpg" alt="img" style="zoom:50%;" />
</div>
可以看到,无论是 MMoE 还是 ML-MMoE不同任务在三个 Expert 上的权重都是接近的,但对于 CGC & PLE 来说,不同任务在共享 Expert 上的权重是有较大差异的。PLE针对不同的任务能够有效利用共享 Expert 和独有 Expert 的信息,解释了为什么其能够达到比 MMoE 更好的训练结果。CGC理论上是MMOE的子集该实验表明现实中MMOE很难收敛成这个CGC的样子所以PLE模型就显式的规定了CGC这样的结构。
## 总结与拓展
总结:
CGC在结构上设计的分化实现了专家功能的分化而PLE则是通过分层叠加使得不同专家的信息进行融合。整个结构的设计是为了让多任务学习模型不仅可以学习到各自任务独有的表征还能学习不同任务共享的表征。
论文中也对大多数的MTL模型进行了抽象总结如下图
<div align=center>
<img src="https://pic2.zhimg.com/80/v2-d607cd8e14d4a0fadb4dbef06dc2ffa9_1440w.jpg" alt="img" style="zoom:50%;" />
</div>
不同的MTL模型即不同的参数共享机制CGC的结构最为灵活。
可以思考下以下几个问题:
1. 多任务模型线上如何打分融合?
在论文中,作者分享了腾讯视频的一种线上打分机制
<div align=center>
<img src="https://pic2.zhimg.com/80/v2-9a412b82d45877287df2429fc89afac5_1440w.jpg" alt="img" style="zoom:33%;" />
</div>
每个目标的预估值有一个固定的权重,通过乘法进行融合,并在最后未来排除视频自身时长的影响,使用 $ f(videolen)$对视频时长进行了非线性变化。其实在业界的案例中,也基本是依赖乘法或者加法进行融合,爱奇艺曾经公开分享过他们使用过的打分方法:
<div align=center>
<img src="https://pic1.zhimg.com/80/v2-661030ad194ae2059eace0804ef0f774_1440w.jpg" alt="img" style="zoom: 67%;" />
</div>
在业务目标较少时,通过加法方式融合新增目标可以短期内快速获得收益。但是随着目标增多,加法融合会 逐步弱化各字母表的重要性影响,而乘法融合则具有一定的模板独立性,乘法机制更加灵活,效益更好。融 合的权重超参一般在线上通过A/B test调试。
2. 专家的参数如何设置?
PLE模型存在的超参数较多其中专家和门控网络都有两种类型。一般来说task-specific expert每个任务1-2个shared expert个数在任务个数的1倍以上。原论文中的gate网络即单层FC可以适当增加调试。
3. ESMM、MMOE、PLE模型如何选择
- 个人经验无论任务之间是否有依赖关系皆可以优先尝试CGC。而多层CGC即PLE未必比CGC效果好且在相同参数规模小CGC普遍好于MMOE。对于相关性特别差的多任务CGC相对MMOE而言有多个专有expert兜底。
- 对于典型的label存在路径依赖的多任务例如CTR与CVR可尝试ESMM。
- 而在业界的实践案例中,更多的是两种范式的模型进行融合。例如美团在其搜索多业务排序场景上提出的模型:
<div align=center>
<img src="https://pic2.zhimg.com/80/v2-af16e969a0149aef9c2a1291de5c65d5_1440w.jpg" alt="img" style="zoom:50%;" />
</div>
总框架是ESMM的架构以建模下单CVR为主任务CTR和CTCVR为辅助任务。在底层的模块中则使用了CGC模块提取多任务模式下的特征表达信息。
4. 不同Tower能否输入不同的特征不同的expert使用不同的特征不同的门控使用不同的特征
MMOE、PLE原论文中介绍的模型均是使用同样的原始特征输入各个不同的expert也输入给第一层的gate。最顶层的Tower网络中则均是由一个gate融合所有expert输出作为输入。在实践中可以根据业务需求进行调整。
- 例如上图中美团提出的模型在CTR的tower下设置了五个子塔闪购子网络、买菜子网络、外卖子网络、优选子网络和团好货子网络并且对不同的子塔有额外输入不同的特征。
对于底层输入给expert的特征美团提出通过增加一个自适应的特征选择门使得选出的特征对不同的业务权重不同。例如“配送时间”这个特征对闪购业务比较重要但对于团好货影响不是很大。模型结构如图
<div align=center>
<img src="https://pic2.zhimg.com/80/v2-2e2370794bbd69ded636a248d8c36255_1440w.jpg" alt="img" style="zoom:50%;" />
</div>
特征选择门与控制expert信息融合的gate类似由一层FC和softmax组成输出是特征维度的权重。对于每一个特征通过该门都得到一个权重向量权重向量点乘原始特征的embedding作为expert的输入。
5. 多任务loss更高效的融合机制
推荐首先尝试两种简单实用的方法GrandNorm和UWL具体实现细节查看下文所附的参考资料。
- UWLUncertainty Weight通过自动学习任务的uncertainty给uncertainty大的任务小权重uncertainty小的任务大权重
- GradNorm结合任务梯度的二范数和loss下降梯度引入带权重的损失函数Gradient Loss并通过梯度下降更新该权重。
## 代码实践
主要是分两个层级在PLE的层级下由于PLE是分层上一层是输出是下一层的输入代码逻辑为
```python
# build Progressive Layered Extraction
ple_inputs = [dnn_input] * (num_tasks + 1) # [task1, task2, ... taskn, shared task]
ple_outputs = []
for i in range(num_levels):
if i == num_levels - 1: # the last level
ple_outputs = cgc_net(inputs=ple_inputs, level_name='level_' + str(i) + '_', is_last=True)
else:
ple_outputs = cgc_net(inputs=ple_inputs, level_name='level_' + str(i) + '_', is_last=False)
ple_inputs = ple_outputs
```
其中cgc_net函数则对应论文中提出的CGC模块我们把expert分成两类task-specific和task-shared为了方便索引expert list中expert的排列顺序为[task1-expert1, task1-expert2,...task2-expert1, task2-expert2,...shared expert 1... ],则可以通过双重循环创建专家网络:
```python
for i in range(num_tasks): #任务个数
for j in range(specific_expert_num): #每个任务对应的task-specific专家个数
pass
```
注意门控网络也分为两种类型task-specific gate的输入是每个任务对应的expert的输出和共享expert的输出我们同样把共享expert的输出放在最后方便索引
```python
for i in range(num_tasks):
# concat task-specific expert and task-shared expert
cur_expert_num = specific_expert_num + shared_expert_num
# task_specific + task_shared
cur_experts = specific_expert_outputs[
i * specific_expert_num:(i + 1) * specific_expert_num] + shared_expert_outputs
```
在最后一层中由于CGC模块的输出需要分别输入给不同任务各自的Tower模块所以不需要创建task-shared gate。完整代码如下
```python
def PLE(dnn_feature_columns, shared_expert_num=1, specific_expert_num=1, num_levels=2,
expert_dnn_hidden_units=(256,), 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')):
"""Instantiates the multi level of Customized Gate Control of Progressive Layered Extraction architecture.
:param dnn_feature_columns: An iterable containing all the features used by deep part of the model.
:param shared_expert_num: integer, number of task-shared experts.
:param specific_expert_num: integer, number of task-specific experts.
:param num_levels: integer, number of CGC levels.
:param expert_dnn_hidden_units: list,list of positive integer or empty list, the layer number and units in each layer of expert DNN.
:param tower_dnn_hidden_units: list,list of positive integer or empty list, the layer number and units in each layer of task-specific DNN.
:param gate_dnn_hidden_units: list,list of positive integer or empty list, the layer number and units in each layer of gate DNN.
:param l2_reg_embedding: float. L2 regularizer strength applied to embedding vector.
:param l2_reg_dnn: float. L2 regularizer strength applied to DNN.
:param seed: integer ,to use as random seed.
:param dnn_dropout: float in [0,1), the probability we will drop out a given DNN coordinate.
:param dnn_activation: Activation function to use in DNN.
:param dnn_use_bn: bool. Whether use BatchNormalization before activation or not in DNN.
:param task_types: list of str, indicating the loss of each tasks, ``"binary"`` for binary logloss, ``"regression"`` for regression loss. e.g. ['binary', 'regression']
:param task_names: list of str, indicating the predict target of each tasks
:return: a Keras model instance.
"""
num_tasks = len(task_names)
if num_tasks <= 1:
raise ValueError("num_tasks must be greater than 1")
if len(task_types) != num_tasks:
raise ValueError("num_tasks must be equal to the length of task_types")
for task_type in task_types:
if task_type not in ['binary', 'regression']:
raise ValueError("task must be binary or regression, {} is illegal".format(task_type))
features = build_input_features(dnn_feature_columns)
inputs_list = list(features.values())
sparse_embedding_list, dense_value_list = input_from_feature_columns(features, dnn_feature_columns,
l2_reg_embedding, seed)
dnn_input = combined_dnn_input(sparse_embedding_list, dense_value_list)
# single Extraction Layer
def cgc_net(inputs, level_name, is_last=False):
# inputs: [task1, task2, ... taskn, shared task]
specific_expert_outputs = []
# build task-specific expert layer
for i in range(num_tasks):
for j in range(specific_expert_num):
expert_network = DNN(expert_dnn_hidden_units, dnn_activation, l2_reg_dnn, dnn_dropout, dnn_use_bn,
seed=seed,
name=level_name + 'task_' + task_names[i] + '_expert_specific_' + str(j))(
inputs[i])
specific_expert_outputs.append(expert_network)
# build task-shared expert layer
shared_expert_outputs = []
for k in range(shared_expert_num):
expert_network = DNN(expert_dnn_hidden_units, dnn_activation, l2_reg_dnn, dnn_dropout, dnn_use_bn,
seed=seed,
name=level_name + 'expert_shared_' + str(k))(inputs[-1])
shared_expert_outputs.append(expert_network)
# task_specific gate (count = num_tasks)
cgc_outs = []
for i in range(num_tasks):
# concat task-specific expert and task-shared expert
cur_expert_num = specific_expert_num + shared_expert_num
# task_specific + task_shared
cur_experts = specific_expert_outputs[
i * specific_expert_num:(i + 1) * specific_expert_num] + shared_expert_outputs
expert_concat = tf.keras.layers.Lambda(lambda x: tf.stack(x, axis=1))(cur_experts)
# build gate layers
gate_input = DNN(gate_dnn_hidden_units, dnn_activation, l2_reg_dnn, dnn_dropout, dnn_use_bn,
seed=seed,
name=level_name + 'gate_specific_' + task_names[i])(
inputs[i]) # gate[i] for task input[i]
gate_out = tf.keras.layers.Dense(cur_expert_num, use_bias=False, activation='softmax',
name=level_name + 'gate_softmax_specific_' + task_names[i])(gate_input)
gate_out = tf.keras.layers.Lambda(lambda x: tf.expand_dims(x, axis=-1))(gate_out)
# gate multiply the expert
gate_mul_expert = tf.keras.layers.Lambda(lambda x: reduce_sum(x[0] * x[1], axis=1, keep_dims=False),
name=level_name + 'gate_mul_expert_specific_' + task_names[i])(
[expert_concat, gate_out])
cgc_outs.append(gate_mul_expert)
# task_shared gate, if the level not in last, add one shared gate
if not is_last:
cur_expert_num = num_tasks * specific_expert_num + shared_expert_num
cur_experts = specific_expert_outputs + shared_expert_outputs # all the expert include task-specific expert and task-shared expert
expert_concat = tf.keras.layers.Lambda(lambda x: tf.stack(x, axis=1))(cur_experts)
# build gate layers
gate_input = DNN(gate_dnn_hidden_units, dnn_activation, l2_reg_dnn, dnn_dropout, dnn_use_bn,
seed=seed,
name=level_name + 'gate_shared')(inputs[-1]) # gate for shared task input
gate_out = tf.keras.layers.Dense(cur_expert_num, use_bias=False, activation='softmax',
name=level_name + 'gate_softmax_shared')(gate_input)
gate_out = tf.keras.layers.Lambda(lambda x: tf.expand_dims(x, axis=-1))(gate_out)
# gate multiply the expert
gate_mul_expert = tf.keras.layers.Lambda(lambda x: reduce_sum(x[0] * x[1], axis=1, keep_dims=False),
name=level_name + 'gate_mul_expert_shared')(
[expert_concat, gate_out])
cgc_outs.append(gate_mul_expert)
return cgc_outs
# build Progressive Layered Extraction
ple_inputs = [dnn_input] * (num_tasks + 1) # [task1, task2, ... taskn, shared task]
ple_outputs = []
for i in range(num_levels):
if i == num_levels - 1: # the last level
ple_outputs = cgc_net(inputs=ple_inputs, level_name='level_' + str(i) + '_', is_last=True)
else:
ple_outputs = cgc_net(inputs=ple_inputs, level_name='level_' + str(i) + '_', is_last=False)
ple_inputs = ple_outputs
task_outs = []
for task_type, task_name, ple_out in zip(task_types, task_names, ple_outputs):
# build tower layer
tower_output = DNN(tower_dnn_hidden_units, dnn_activation, l2_reg_dnn, dnn_dropout, dnn_use_bn, seed=seed,
name='tower_' + task_name)(ple_out)
logit = tf.keras.layers.Dense(1, use_bias=False, activation=None)(tower_output)
output = PredictionLayer(task_type, name=task_name)(logit)
task_outs.append(output)
model = tf.keras.models.Model(inputs=inputs_list, outputs=task_outs)
return model
```
参考资料
Progressive Layered Extraction (PLE): A Novel Multi-Task Learning (MTL) Model for Personalized Recommendations (RecSys'2020)
https://zhuanlan.zhihu.com/p/291406172
爱奇艺:[https://www.6aiq.com/article/1624916831286](https://link.zhihu.com/?target=https%3A//www.6aiq.com/article/1624916831286)
美团:[https://mp.weixin.qq.com/s/WBwvfqOTDKCwGgoaGoSs6Q](https://link.zhihu.com/?target=https%3A//mp.weixin.qq.com/s/WBwvfqOTDKCwGgoaGoSs6Q)
多任务loss优化[https://blog.csdn.net/wuzhongqi](https://link.zhihu.com/?target=https%3A//blog.csdn.net/wuzhongqiang/article/details/124258128)