多使命学习之mmoe理论详解与实践


文章源码下载地址:点我下载inf.zhihang.info/resources/p…

书接上文,在前一篇文章 快看 esmm 模型理论与实践 中,咱们讲到MTL使命一般能够把能够分为两种: 串行与并行 。多个使命之间有较强相关的, 例如点击率与转化率,这一种一般咱们能够运用 ESMM 这种串行的使命进行 联系递进性与相关性 建模。而关于多个使命之间相比照较独立的,例如点击率与用户是否给出谈论的谈论率,一般能够挑选 MMOE 这种并行的使命进行 相关性与冲突性 的建模。

MMOE 模型全称是 Multi-gate Mixture-of-Experts, 该模型由 Google在 2018年 KDD 上发表的文章 Modeling Task Relationships in Multi-task Learning with Multi-gate Mixture-of-Experts 中提出的。

MMOE 模型自身在结构上借鉴了曾经的 MOE 模型,但又有必定的创新, 它能够说是提出了一种新的 MTL(Multi-Task Learning) 架构,对每个顶层使命均运用了一个 gate网络 去学习交融多个专家对当时 Task 的权重影响, 在很大程度上调节缓解 多方针使命相关性低导致的准确率低 的问题。

本文,咱们首要对mmoe模型进行理论与实践进程的阐述… 多任务学习之mmoe理论详解与实践


(1) mmoe和esmm模型的比照

说到 mmoe 模型,咱们一般总是会把它横向的和阿里巴巴提出的esmm模型进行比照,而纵向的和开始shared-bottom、moe进行比照,这儿咱们别离也从横向和纵向比照展开。

首先是 esmm模型 ,和mmoe相同 , 它也是一种(Multi-Task Learning,MTL) 模型。esmm 模型相关内容,能够去这儿 快看 esmm 模型理论与实践 查看。可是上文咱们也说了, esmm 咱们建模的是一种串行的使命,使命联系之间有 递进 的性质。而mmoe则不要求两个使命有递进的联系。

在这儿,咱们之所以一直着重esmm模型适合建模递进使命,咱们能够从esmm的丢失函数能够看到:

多任务学习之mmoe理论详解与实践
, 咱们能够要点重视下他的CTCVR丢失。

而建模的pCTCVR 的 概率建模逻辑 又能够这样表明:

多任务学习之mmoe理论详解与实践

读过上面介绍esmm文章的同学就能够看到,pCTCVR 使命的核心是: 咱们站在曝光的前提下,不知道点击与否,不知道转化与否,这个时分去预估点击而且转化的概率

如上所述,CTR 使命和 CVR使命 都是进行分类模型的0/1概率预估, 用到的特征类似, 场景也类似, 使命相关性十分高。而且,该模型运用的事务场景也要求了在曝光后,item先被点击才会有转化,没有被点击就压根谈不上转化了,这是一种严格的递进联系。一起CTR与CVR两者的概率相乘有着显着且理论正确的事务含义

咱们从 上篇文章 里也了解到: esmm 模型从使命的底子“丢失函数”上就保证了这种曝光前就去预估点击且转化的概率建模逻辑。 假如使命自身没有 严格递进且高度类似相关,那咱们这儿的丢失建模就没有含义了。

或则,咱们也能够改动esmm模型的丢失函数(去掉严格递进且高度类似相关的联系逻辑),用它去建模一些不那么相关的 multi-task 使命,毕竟是DNN模型,只需你能把跑起来,总是能够predict出一个成果的。可是假如丢失函数有了大的本质上的改动,esmm模型就失去了它的精华,那这个模型还叫esmm模型吗?

多任务学习之mmoe理论详解与实践

而关于本文介绍的MMOE模型,咱们对他的样本就没有那么多的要求,它能够进行并行与冲突性建模。当然,没有那么多的要求不是没有要求,至少两个使命的样本和特征是能同享的吧,事务能够有穿插的吧,最重要的是多个使命的丢失交融后能找到必定的优化含义的吧。

例如:视频推荐中,咱们有多个不同甚至可能发生冲突的方针,就像多个task是去预估用户是否会观看的一起还希望去预估用户的观看时长,一起预估用户关于视频的评分。这中心就一起有 分类使命回归使命 了。

这儿咱们引进一些 使命相关性衡量 的简略介绍:

咱们知道: Spearman相联系数仅评价单调联系,Pearson相联系数仅评价线性联系。例如: 假如联系是一个变量在另一个变量增加时增加,但数量不共同,则Pearson相联系数为正但小于+1。 在这种状况下,Spearman系数依然等于+1。

咱们这儿说的 两个使命的相关性,能够通过 两个使命的label 之间的 皮尔逊相联系数 来表明 。假定模型中包括两个回归使命,而数据通过采样生成,而且规则输入相同,输出label不同,求得他们的皮尔逊相联系数,相联系数越大,表明使命之间越相关,相联系数越大,表明使命之间越相关。


(2) mmoe 模型详解

说到 mmoe,因其一脉相承血浓于水,不得不说到 shared-bottom与moe 这两个模型。这儿,咱们通过比照三种模型的 异同 来 逐渐 引出 mmoe 模型的特点。 闲言少叙,上图:

多任务学习之mmoe理论详解与实践

从上面图中,咱们能够看出:在三个图(a,b,c)中,从下往上别离是模型的输入到输出的流程。

如上图a所示,假定咱们的模型中有N个使命,则在上层会有K个塔 (图中K=2)。其间,上层的每个塔对应一个特定使命的个性化常识学习,而底层的shared-bottom 层作为同享层,能够进行多个使命的常识搬迁

留意:输入的input在咱们的了解里,已经是各个sparse 或dense特征得到embeding 而且拼接之后的成果,理论上是一个[batch_size, embeding_concat_size]的tensor。


(2.1)原始的shared-bottom 模型

如上图所示:图a 是原始的 shared-bottom 结构,2个 task 同享躲藏层的输出成果, 输入到各自使命的tower, 一般的 tower 也便是几层 全衔接网络 ,最终一层便是分类层或回归层,和惯例的分类回归使命没有什么差异。


(2.2) moe网络

图b 便是开始版别的 moe 网络 ,咱们从图中能够看到有个gate网络,而且只要一个gate门控网络,一起图b中有三个专家网络。

咱们能够看到:gate门控网络的输出维度和专家个数相同,起到了交融多个专家常识的效果。交融完了之后会有一个公共的输出,该相同的输出别离输入到上面2个tower中。

惯例网络中,gate网络和tower网络均是几层 全衔接网络,仅仅最终的输出看状况考虑维度以及是否需求增加 relu 与 softmax 函数等。

这儿要留意一点便是: 咱们的input是别离作为各个专家和gate门控网络的输入,各个专家和门控网络别离独自初始化以及练习。gate网络的输出的各个维度和各个专家的输出进行加权求和,得到一个归纳专家维度的输出,然后相同的输入别离输入到上面两个不同的使命中。

这儿咱们从网络结构上能够显着看到MOE和初始网络的差异,多了一个多专家加权交融的门控网络,使得各个专家学习到的常识进行滑润的交融,能够让模型练习的更好。 多任务学习之mmoe理论详解与实践


(2.3)mmoe网络的提出

图c 便是咱们本文要要点介绍的 mmoe 网络了。 mmoe 说白了,便是一种新的MTL网络架构的创新。mmoe 实际上便是 多个门 的 moe 网络。 输入多个专家的进程和moe无任何差异,这儿唯一的不同是对每一个使命有一个门控网络

mmoe和moe最大的差别就在于输入上面使命的输入。 moe的使命tower输入的是通过同一个门控网络加权过的多个专家的输出,是相同的一个embeding。 而mmoe 的使命tower 输入的则是通过自己使命特有的门控网络加权过的多个专家的输出,关于不同使命是不同的。没有显着增加参数,却对网络的学习起到了重要的影响效果。

浅显了解 : 咱们网络中的每个专家都是能够学到一些关于多个使命的各自的专业常识,而咱们用多个门控网络,就相当于起到了一个Attention的效果。就例如: 咱们运用一个多方针使命网络去预估一个人别离得感冒和高血压的概率,咱们现在有多个专家都会相同的这个患者进行会诊,可是每个专家各有所长又各有所短,这个时分,咱们就通过一个门控网络去自动的学习关于某种病情应该多遵从哪个专家的定见,最终对各个专家的定见进行加权求和之后来归纳鉴定这个人患某种病的概率。

让每个专家发挥出各自的专长,是不是更有利于咱们实际的状况呢?

而开篇所说到的mmoe网络的 冲突性建模才能 也就来自于这多个门控网络关于多个使命可学习的调控才能多个专家加上多个门控网络,不同使命对应的门控网络能够学习到不同的 Experts 组合形式,模型更简单捕捉到子使命间的相关性和差异性,能够使得咱们多个使命交融的愈加滑润 ,最终打分得出的成果也愈加能够动态归纳多个专家的专长与才能,得出一个更有益于咱们事务方针的成果。

前文咱们已经介绍过, mmoe网络的提出首要便是提出了一个新的MTL架构。所以上文中,我就没有在引进一些不流畅难懂的公式,而是悉数选用了文字说明的形式来下进行阐述,希望能似的读者看起来更丝滑一些~ 多任务学习之mmoe理论详解与实践


(3) mmoe 模型实践与心得

其实相关于esmm模型,mmoe模型更好了解,构造样本等也愈加简单。

可是依然有一点便是: 咱们在运用tensorflow 或 pytorch 完成网络的进程中,多个专家以及门控网络的输入输出维度对应上有必定难度,有躲藏的暗坑在里面。不过这些在上文中,我也大约以文字的形式说清楚了,后面共享的代码源码我也依据自己的了解进行了具体的注释,希望对读者的了解有协助哈~

在实际运用mmoe的进程中,有同学会遇到:练习mmoe的进程中,发现多个gate的输出过度偏差 ,例如:(0.999,0.001)状况。 这一种状况开始感觉还是:(1) 网络的完成有问题,需求去排查下各个专家网络以及门控网络的初始化值有没有问题。 (2)排查下两个使命的标签状况,是不是两使命的标签呈现比较多的极端状况,也能够选用上面介绍的使命相关性衡量办法看一下两个使命的相关性。 在输入相同的状况下在网络理论上不应该出现这个问题我在运用进程中并没有遇到,所以只能给出一些猜想的解决方法…

当咱们遇到两个使命的方针差异特别巨大时,例如:预估视频点击率与观看时长。这个使命咱们应该直觉上就觉得标签的差异太过于大了,时长的label最好能够进行必定的处理。例如log处理。

log函数有着优秀的性质,通过log处理后方针会削弱量级,数据也会变得更契合正态散布,一起loss也更小和安稳 。loss的安稳关于多使命模型学习和练习来说是至关重要的,它影响着多个使命依据loss更新的梯度,最好咱们能够把多个方针的loss加权重调到同一量级,对这种差异比较大的问题总是能够起到缓解效果的额

一起在进行mmoe网络设计的进程中,咱们不只能够运用多个使命有同享的专家(官方版别) ,其实咱们也能够给每个使命加上各自共同的专家进行组合学习,希望模型能够学习到各个使命之间的个性与共性

别的, 咱们能够将mmoe 作为一种复杂的DNN layer ,咱们能够在网络中叠加多个 mmoe layer , 完成一些比较复杂的网络来学习一些比较复杂的多方针使命。


(4) 代码时光

talk is cheap , show me the code !!!

哎, 终于再次写到代码时光了!

关于mmoe 模型,才开始看源码到最终了解花了挺长期,中心首要的时刻都花在了完成的时分专家网络和多门控网络的输入输出维度对应上。下面的代码注释均写的比较具体,看的进程中,如有任何问题欢迎大众号留言讨论 ~


@欢迎重视微信大众号:算法全栈之路
#coding:utf-8
importnumpyasnp
importos
importargparse
importtensorflowastf
importlog_util
importparams_conf
fromdate_helperimportDateHelper
importdata_consumer
frommmoeimportMMoE
fromtensorflow.kerasimportlayers,Model
fromtensorflow.keras.optimizersimportAdam
fromtensorflow.keras.callbacksimportCallback
fromtensorflow.keras.initializersimportVarianceScaling
os.environ['TF_CPP_MIN_LOG_LEVEL']='2'
os.environ["CUDA_VISIBLE_DEVICES"]="-1"
importtensorflowastf
fromtensorflowimporttensordot,expand_dims
fromtensorflow.kerasimportlayers,Model,initializers,regularizers,activations,constraints,Input
fromtensorflow.keras.backendimportexpand_dims,repeat_elements,sum
classMMoE(layers.Layer):
"""
Multi-gateMixture-of-Expertsmodel.
"""
def__init__(self,
units,
num_experts,
num_tasks,
use_expert_bias=True,
use_gate_bias=True,
expert_activation='relu',
gate_activation='softmax',
expert_bias_initializer='zeros',
gate_bias_initializer='zeros',
expert_bias_regularizer=None,
gate_bias_regularizer=None,
expert_bias_constraint=None,
gate_bias_constraint=None,
expert_kernel_initializer='VarianceScaling',
gate_kernel_initializer='VarianceScaling',
expert_kernel_regularizer=None,
gate_kernel_regularizer=None,
expert_kernel_constraint=None,
gate_kernel_constraint=None,
activity_regularizer=None,
**kwargs):
"""
MethodforinstantiatingMMoElayer.
:paramunits:Numberofhiddenunits躲藏单元
:paramnum_experts:Numberofexperts专家个数,能够有同享专家,也能够有每个使命独立的专家
:paramnum_tasks:Numberoftasks使命个数,和tower个数共同
:paramuse_expert_bias:Booleantoindicatetheusageofbiasintheexpertweights.专家的权重是否增加偏置
:paramuse_gate_bias:Booleantoindicatetheusageofbiasinthegateweights.门控的权重是否增加偏置
:paramexpert_activation:Activationfunctionoftheexpertweights.专家激活函数
:paramgate_activation:Activationfunctionofthegateweights.门控激活函数
:paramexpert_bias_initializer:Initializerfortheexpertbias.专家偏置初始化
:paramgate_bias_initializer:Initializerforthegatebias.门控偏置初始化
:paramexpert_bias_regularizer:Regularizerfortheexpertbias.专家正则化
:paramgate_bias_regularizer:Regularizerforthegatebias.门控正则化
:paramexpert_bias_constraint:Constraintfortheexpertbias.专家偏置
:paramgate_bias_constraint:Constraintforthegatebias.门控偏置
:paramexpert_kernel_initializer:Initializerfortheexpertweights
:paramgate_kernel_initializer:Initializerforthegateweights
:paramexpert_kernel_regularizer:Regularizerfortheexpertweights
:paramgate_kernel_regularizer:Regularizerforthegateweights
:paramexpert_kernel_constraint:Constraintfortheexpertweights
:paramgate_kernel_constraint:Constraintforthegateweights
:paramactivity_regularizer:Regularizerfortheactivity
:paramkwargs:AdditionalkeywordargumentsfortheLayerclass隶属参数若干
"""
super(MMoE,self).__init__(**kwargs)
#Hiddennodesparameter
self.units=units
self.num_experts=num_experts
self.num_tasks=num_tasks
#Weightparameter
self.expert_kernels=None
self.gate_kernels=None
self.expert_kernel_initializer=initializers.get(expert_kernel_initializer)
self.gate_kernel_initializer=initializers.get(gate_kernel_initializer)
self.expert_kernel_regularizer=regularizers.get(expert_kernel_regularizer)
self.gate_kernel_regularizer=regularizers.get(gate_kernel_regularizer)
self.expert_kernel_constraint=constraints.get(expert_kernel_constraint)
self.gate_kernel_constraint=constraints.get(gate_kernel_constraint)
#Activationparameter
#self.expert_activation=activations.get(expert_activation)
self.expert_activation=expert_activation
self.gate_activation=gate_activation
#Biasparameter
self.expert_bias=None
self.gate_bias=None
self.use_expert_bias=use_expert_bias
self.use_gate_bias=use_gate_bias
self.expert_bias_initializer=initializers.get(expert_bias_initializer)
self.gate_bias_initializer=initializers.get(gate_bias_initializer)
self.expert_bias_regularizer=regularizers.get(expert_bias_regularizer)
self.gate_bias_regularizer=regularizers.get(gate_bias_regularizer)
self.expert_bias_constraint=constraints.get(expert_bias_constraint)
self.gate_bias_constraint=constraints.get(gate_bias_constraint)
#Activityparameter
self.activity_regularizer=regularizers.get(activity_regularizer)
self.expert_layers=[]
self.gate_layers=[]
#在初始化的进程中,先构建好网络结构
foriinrange(self.num_experts):
#有几个专家,这儿就增加几个dense层,dense层的输入为上面传入,当时层的输出维度为units的值,躲藏单元个数
self.expert_layers.append(layers.Dense(self.units,activation=self.expert_activation,
use_bias=self.use_expert_bias,
kernel_initializer=self.expert_kernel_initializer,
bias_initializer=self.expert_bias_initializer,
kernel_regularizer=self.expert_kernel_regularizer,
bias_regularizer=self.expert_bias_regularizer,
activity_regularizer=None,
kernel_constraint=self.expert_kernel_constraint,
bias_constraint=self.expert_bias_constraint))
#门控网络,门控网络的个数等于使命数目,可是取值数据的维度等于专家个数,mmoe对每个使命都要交融各个专家的定见。
#有几个使命,
foriinrange(self.num_tasks):
#num_tasks个门控,num_experts维数据
self.gate_layers.append(layers.Dense(self.num_experts,activation=self.gate_activation,
use_bias=self.use_gate_bias,
kernel_initializer=self.gate_kernel_initializer,
bias_initializer=self.gate_bias_initializer,
kernel_regularizer=self.gate_kernel_regularizer,
bias_regularizer=self.gate_bias_regularizer,activity_regularizer=None,
kernel_constraint=self.gate_kernel_constraint,
bias_constraint=self.gate_bias_constraint))
defcall(self,inputs):
"""
Methodfortheforwardfunctionofthelayer.
:paraminputs:Inputtensor
:paramkwargs:Additionalkeywordargumentsforthebasemethod
:return:Atensor
"""
#assertinput_shapeisnotNoneandlen(input_shape)>=2
#三个输出的网络
expert_outputs,gate_outputs,final_outputs=[],[],[]
#专家网络
#有几个专家循环几回
forexpert_layerinself.expert_layers:
#留意这儿是当时专家的改变
#输入的元素元素应该是全体embedingcontact之后的一堆浮点数维度数据。
#(batch_size,embedding_size,1)
expert_output=expand_dims(expert_layer(inputs),axis=2)
#nums_expert*(batch_size,unit,1)
expert_outputs.append(expert_output)
#同batch的数据,既然是沿着第一个维度对接,那底子就不用看第二个维度,那个axis的维度数目相加
#nums_expert*(batch_size,unit,1)->这儿contact之后,列表里num_experts个tensor在最终一个维度concat到一起,
#则最终维度变成了(batch_size,unit,nums_expert),只要最终一个维度的维度值改动了。
expert_outputs=tf.concat(expert_outputs,2)
#门控网络,每个门对每个专家均有一个散布函数.
forgate_layerinself.gate_layers:
#关于当时门控,[batch_size,num_units]->[nums_expert,batch_size,num_units]
#有多少个使命,就有多少个gate
#num_task*(batch_size,num_experts),这儿对每个专家只要一个数值,和专家的输出维度unit相乘需求拓展维度
gate_outputs.append(gate_layer(inputs))
#这儿每个门控对所有的专家进行加权求和
forgate_outputingate_outputs:
#对当时gate,忽略num_task维度,为(batch_size,1,num_experts)
expanded_gate_output=expand_dims(gate_output,axis=1)
#每个专家的输出和gate的数据维度相乘
#(batch_size,unit,nums_expert)*(batch_size,1*units,num_experts),因此1*units
#Ifxhasshape(s1,s2,s3)andaxisis1,theoutputwillhaveshape(s1,s2*rep,s3).
#这儿的本质是门控和专家的输出相乘维度不对,如上面所说,门控维度1和需求拓展到各个专家的输出维度unit,便利相乘。
#"*"算子在tensorflow中表明element-wiseproduct,即哈达马积,即两个向量按元素一个一个相乘,组成一个新的向量,成果向量与原向量尺寸相同。
weighted_expert_output=expert_outputs*repeat_elements(expanded_gate_output,self.units,axis=1)
#上面输出的维度是(batch_size,unit,nums_expert),对第二维nums_expert求和则该维度就变成一个数值->(batch_size,unit)
#这儿对各个专家的成果聚合之后,回来的是一个归纳专家对应的输出单元unit维度.
#最终有多个门控,上面多个塔,这儿回来的是num_tasks*batch*units这个维度。
final_outputs.append(sum(weighted_expert_output,axis=2))
#回来的矩阵维度num_tasks*batch*units
#回来多个门控,每个门控有归纳多个专家回来的维度units
#这儿final_outputs回来的是个list,元素个数等于门控个数也等于使命个数
returnfinal_outputs
definit_args():
parser=argparse.ArgumentParser(description='dnn_demo')
parser.add_argument("--mode",default="train")
parser.add_argument("--train_data_dir")
parser.add_argument("--model_output_dir")
parser.add_argument("--cur_date")
parser.add_argument("--log",default="../log/tensorboard")
parser.add_argument('--use_gpu',default=False,type=bool)
args=parser.parse_args()
returnargs
defget_feature_column_map():
key_hash_size_map={
"adid":10000,
"site_id":10000,
"site_domain":10000,
"site_category":10000,
"app_id":10000,
"app_domain":10000,
"app_category":1000,
"device_id":1000,
"device_ip":10000,
"device_type":10,
"device_conn_type":10,
}
feature_column_map=dict()
forkey,valueinkey_hash_size_map.items():
feature_column_map.update({key:tf.feature_column.categorical_column_with_hash_bucket(
key,hash_bucket_size=value,dtype=tf.string)})
returnfeature_column_map
defbuild_embeding():
feature_map=get_feature_column_map()
feature_inputs_list=[]
defget_field_emb(categorical_col_key,emb_size=16,input_shape=(1,)):
#print(categorical_col_key)
embed_col=tf.feature_column.embedding_column(feature_map[categorical_col_key],emb_size)
#层姓名不能够相同,否则会报错
dense_feature_layer=tf.keras.layers.DenseFeatures(embed_col,name=categorical_col_key+"_emb2dense")
feature_layer_inputs=dict()
#input和DenseFeatures必须要用dict来存和联合运用,深坑啊!!
feature_layer_inputs[categorical_col_key]=tf.keras.Input(shape=(1,),dtype=tf.dtypes.string,
name=categorical_col_key)
#保存供modelinput运用.
feature_inputs_list.append(feature_layer_inputs[categorical_col_key])
returndense_feature_layer(feature_layer_inputs)
embeding_map={}
forkey,valueinfeature_map.items():
#print("key:"+key)
embeding_map.update({key:get_field_emb(key)})
returnembeding_map,feature_inputs_list
defbuild_dnn_net(net,params_conf,name="ctr"):
#能够在下面接入残差网络
fori,dnn_hidden_sizeinenumerate(params_conf.DNN_HIDDEN_SIZES):#DNN_HIDDEN_SIZES=[512,128,64]
net=tf.keras.layers.Dense(dnn_hidden_size,activation="relu",name="overall_dense_%s_%s"%(i,name))(net)
returnnet
defbuild_model(emb_map,inputs_list):
#需求特别处理和穿插的特征,以及需求短接残差的特征,能够单独拿出来
define_list=[]
adid_emb=emb_map["adid"]
device_id_emd=emb_map["device_id"]
ad_x_device=tf.multiply(adid_emb,device_id_emd)
define_list.append(ad_x_device)
#直接能够拼接的特征
common_list=[]
forkey,valueinemb_map.items():
common_list.append(value)
#embedingcontact
net=tf.keras.layers.concatenate(define_list+common_list)
#SetupMMoElayer
#回来的矩阵维度num_tasks*batch*units
#回来多个门控,每个门控有归纳多个专家回来的维度units
#这儿final_outputs回来的是个list,元素个数等于门控个数也等于使命个数
mmoe_layers=MMoE(units=4,num_experts=8,num_tasks=2)(net)
output_layers=[]
#BuildtowerlayerfromMMoElayer
#对每个mmoelayer,后面均接着2层dense到输出,
#list,元素个数等于门控个数也等于使命个数
forindex,task_layerinenumerate(mmoe_layers):
#对当时task,batch*units维度的数据,介入躲藏层
tower_layer=layers.Dense(units=8,activation='relu',kernel_initializer=VarianceScaling())(task_layer)
#这儿unit为1,当时使命为2分类
output_layer=layers.Dense(units=1,name="task_%s"%(index),activation='sigmoid',
kernel_initializer=VarianceScaling())(tower_layer)
output_layers.append(output_layer)
#Compilemodel
#这儿界说了模型骨架,input为模型输入参数,而output_layers是一个列表,列表里回来了2个使命各自的logit
#其实别离回来了每个task的logit,logit这儿为分类数目维度的数组,2维过softmax
model=Model(inputs=[inputs_list],outputs=output_layers)
returnmodel
deftrain():
output_root_dir="{}/{}/{}".format(params_conf.BASE_DIR,args.model_output_dir,args.cur_date)
os.mkdir(output_root_dir)
model_full_output_dir=os.path.join(output_root_dir,"model_savedmodel")
#printinfolog
log_util.info("model_output_dir:%s"%model_full_output_dir)
#重置keras的状况
tf.keras.backend.clear_session()
log_util.info("starttrain...")
train_date_list=DateHelper.get_date_range(DateHelper.get_date(-1,args.cur_date),
DateHelper.get_date(0,args.cur_date))
train_date_list.reverse()
print("train_date_list:"+",".join(train_date_list))
#loaddatafromtf.data,兼容csv和tf_record
train_set,test_set=data_consumer.get_dataset(args.train_data_dir,train_date_list,
get_feature_column_map().values())
#train_x,train_y=train_set
log_util.info("gettraindatafinish...")
emb_map,feature_inputs_list=build_embeding()
log_util.info("buildembedingfinish...")
#构建模型
model=build_model(emb_map,feature_inputs_list)
log_util.info("buildmodelfinish...")
defmy_sparse_categorical_crossentropy(y_true,y_pred):
returntf.keras.sparse_categorical_crossentropy(y_true,y_pred,from_logits=True)
opt=tf.keras.optimizers.Adam(params_conf.LEARNING_RATE)
#留意这儿设定了2个丢失别离对应[ctr_pred,ctcvr_pred]这两个使命
#loss_weights=[1.0,1.0]这种方法能够固定的调整2个使命的loss权重。
model.compile(
optimizer=opt,
loss={'task_0':'binary_crossentropy','task_1':'binary_crossentropy'},
loss_weights=[1.0,1.0],
metrics=[
tf.keras.metrics.AUC(),
tf.keras.metrics.BinaryAccuracy(),
tf.keras.metrics.Recall(),
tf.keras.metrics.Precision()]
)
model.summary()
#tf.keras.utils.plot_model(model,'multi_input_and_output_model.png',show_shapes=True,dpi=150)
print("starttraining")
#需求设置profile_batch=0,tensorboard页面才会一直坚持更新
tensorboard_callback=tf.keras.callbacks.TensorBoard(
log_dir=args.log,
histogram_freq=1,
write_graph=True,
update_freq=params_conf.BATCH_SIZE*200,
embeddings_freq=1,
profile_batch=0)
#界说衰减式学习率
classLearningRateExponentialDecay:
def__init__(self,initial_learning_rate,decay_epochs,decay_rate):
self.initial_learning_rate=initial_learning_rate
self.decay_epochs=decay_epochs
self.decay_rate=decay_rate
def__call__(self,epoch):
dtype=type(self.initial_learning_rate)
decay_epochs=np.array(self.decay_epochs).astype(dtype)
decay_rate=np.array(self.decay_rate).astype(dtype)
epoch=np.array(epoch).astype(dtype)
p=epoch/decay_epochs
lr=self.initial_learning_rate*np.power(decay_rate,p)
returnlr
lr_schedule=LearningRateExponentialDecay(
params_conf.INIT_LR,params_conf.LR_DECAY_EPOCHS,params_conf.LR_DECAY_RATE)
#该回调函数是学习率调度器
lr_schedule_callback=tf.keras.callbacks.LearningRateScheduler(lr_schedule,verbose=1)
#练习
#留意这儿的train_set能够运用for循环迭代,tf2.0默认支撑eager形式
#这儿的train_set包括两部分,第一部分是feature,第二部分是label(click,click&conversion)
#留意这儿是feature,(click,click&conversion),第二项是tuple,不能是数组或列表[],否则报数据维度不对,坑死爹了。
model.fit(
train_set,
#train_set["labels"],
#validation_data=test_set,
epochs=params_conf.NUM_EPOCHS,#NUM_EPOCHS=10
steps_per_epoch=params_conf.STEPS_PER_EPHCH,
#validation_steps=params_conf.VALIDATION_STEPS,
#
#callbacks=[tensorboard_callback,lr_schedule_callback]
)
#模型保存
tf.keras.models.save_model(model,model_full_output_dir)
#tf.saved_model.save(model,model_full_output_dir)
print("savesaved_modelsuccess")
if__name__=="__main__":
print(tf.__version__)
tf.compat.v1.disable_eager_execution()
#runtensorboard:
#tensorboard--port=8008--host=localhost--logdir=../log
args=init_args()
ifargs.mode=="train":
train()

到这儿,多使命学习之mmoe理论详解与实践就写完成了,欢迎留言沟通 ~


宅男民工码字不易,你的重视是我持续输出的最大动力!!!

接下来作者会继续共享学习与工作中一些有用的、有意思的内容,点点手指头支撑一下吧~

欢迎扫码重视作者的大众号: 算法全栈之路

多任务学习之mmoe理论详解与实践

  • END –