多任务学习之mmoe理论详解与实践
多任务学习之mmoe理论详解与实践
书接上文,在前一篇文章快看esmm模型理论与实践中,我们讲到MTL任务通常可以把可以分为两种:串行与并行。多个任务之间有较强关联的,例如点击率与转化率,这一种通常我们可以使用ESMM这种串行的任务进行关系递进性与相关性建模。而对于多个任务之间相对比较独立的,例如点击率与用户是否给出评论的评论率,通常可以选择MMOE这种并行的任务进行相关性与冲突性的建模。
MMOE模型全称是MultigateMixtureofExperts,该模型由Google在2018年KDD上发表的文章ModelingTaskRelationshipsinMultitaskLearningwithMultigateMixtureofExperts中提出的。
MMOE模型本身在结构上借鉴了以前的MOE模型,但又有一定的创新,它可以说是提出了一种新的MTL(MultiTaskLearning)架构,对每个顶层任务均使用了一个gate网络去学习融合多个专家对当前Task的权重影响,在很大程度上调节缓解多目标任务相关性低导致的准确率低的问题。
本文,我们主要对mmoe模型进行理论与实践过程的阐述。。。(1)mmoe和esmm模型的对比
说到mmoe模型,我们一般总是会把它横向的和阿里巴巴提出的esmm模型进行对比,而纵向的和最初sharedbottom、moe进行对比,这里我们分别也从横向和纵向对比展开。
首先是esmm模型,和mmoe一样,它也是一种(MultiTaskLearning,MTL)模型。esmm模型相关内容,可以去这里快看esmm模型理论与实践查看。但是上文我们也说了,esmm我们建模的是一种串行的任务,任务关系之间有递进的性质。而mmoe则不要求两个任务有递进的关系。
在这里,我们之所以一直强调esmm模型适合建模递进任务,我们可以从esmm的损失函数可以看到:
,我们可以重点关注下他的CTCVR损失。
而建模的pCTCVR的概率建模逻辑又可以这样表示:
读过上面介绍esmm文章的同学就可以看到,pCTCVR任务的核心是:我们站在曝光的前提下,不知道点击与否,不知道转化与否,这个时候去预估点击并且转化的概率。
如上所述,CTR任务和CVR任务都是进行分类模型的01概率预估,用到的特征相似,场景也相似,任务相关性非常高。并且,该模型使用的业务场景也要求了在曝光后,item先被点击才会有转化,没有被点击就压根谈不上转化了,这是一种严格的递进关系。同时CTR与CVR两者的概率相乘有着明显且理论正确的业务意义。
我们从上篇文章里也了解到:esmm模型从任务的根本损失函数上就保证了这种曝光前就去预估点击且转化的概率建模逻辑。如果任务本身没有严格递进且高度相似相关,那我们这里的损失建模就没有意义了。
或则,我们也可以改变esmm模型的损失函数(去掉严格递进且高度相似相关的关系逻辑),用它去建模一些不那么相关的multitask任务,毕竟是DNN模型,只要你能把跑起来,总是可以predict出一个结果的。但是如果损失函数有了大的本质上的改变,esmm模型就失去了它的精华,那这个模型还叫esmm模型吗?
而对于本文介绍的MMOE模型,我们对他的样本就没有那么多的要求,它可以进行并行与冲突性建模。当然,没有那么多的要求不是没有要求,至少两个任务的样本和特征是能共享的吧,业务能够有交叉的吧,最重要的是多个任务的损失融合后能找到一定的优化意义的吧。
例如:视频推荐中,我们有多个不同甚至可能发生冲突的目标,就像多个task是去预估用户是否会观看的同时还希望去预估用户的观看时长,同时预估用户对于视频的评分。这中间就同时有分类任务和回归任务了。
这里我们引入一些任务相关性衡量的简单介绍:
我们知道:Spearman相关系数仅评估单调关系,Pearson相关系数仅评估线性关系。例如:如果关系是一个变量在另一个变量增加时增加,但数量不一致,则Pearson相关系数为正但小于1。在这种情况下,Spearman系数仍然等于1。
我们这里说的两个任务的相关性,可以通过两个任务的label之间的皮尔逊相关系数来表示。假设模型中包含两个回归任务,而数据通过采样生成,并且规定输入相同,输出label不同,求得他们的皮尔逊相关系数,相关系数越大,表示任务之间越相关,相关系数越大,表示任务之间越相关。(2)mmoe模型详解
说到mmoe,因其一脉相承血浓于水,不得不提到sharedbottom与moe这两个模型。这里,我们通过对比三种模型的异同来逐渐引出mmoe模型的特点。闲言少叙,上图:
从上面图中,我们可以看出:在三个图(a,b,c)中,从下往上分别是模型的输入到输出的流程。
如上图a所示,假设我们的模型中有N个任务,则在上层会有K个塔(图中K2)。其中,上层的每个塔对应一个特定任务的个性化知识学习,而底层的sharedbottom层作为共享层,可以进行多个任务的知识迁移。
注意:输入的input在我们的理解里,已经是各个sparse或dense特征得到embeding并且拼接之后的结果,理论上是一个〔batchsize,embedingconcatsize〕的tensor。(2。1)原始的sharedbottom模型
如上图所示:图a是原始的sharedbottom结构,2个task共享隐藏层的输出结果,输入到各自任务的tower,一般的tower也就是几层全连接网络,最后一层就是分类层或回归层,和常规的分类回归任务没有什么区别。(2。2)moe网络
图b就是最初版本的moe网络,我们从图中可以看到有个gate网络,并且只有一个gate门控网络,同时图b中有三个专家网络。
我们可以看到:gate门控网络的输出维度和专家个数相同,起到了融合多个专家知识的作用。融合完了之后会有一个公共的输出,该相同的输出分别输入到上面2个tower中。
常规网络中,gate网络和tower网络均是几层全连接网络,只是最后的输出看情况考虑维度以及是否需要添加relu与softmax函数等。
这里要注意一点就是:我们的input是分别作为各个专家和gate门控网络的输入,各个专家和门控网络分别独自初始化以及训练。gate网络的输出的各个维度和各个专家的输出进行加权求和,得到一个综合专家维度的输出,然后相同的输入分别输入到上面两个不同的任务中。
这里我们从网络结构上可以明显看到MOE和初始网络的区别,多了一个多专家加权融合的门控网络,使得各个专家学习到的知识进行平滑的融合,可以让模型训练的更好。(2。3)mmoe网络的提出
图c就是我们本文要重点介绍的mmoe网络了。mmoe说白了,就是一种新的MTL网络架构的创新。mmoe实际上就是多个门的moe网络。输入多个专家的过程和moe无任何区别,这里唯一的不同是对每一个任务有一个门控网络。
mmoe和moe最大的差别就在于输入上面任务的输入。moe的任务tower输入的是经过同一个门控网络加权过的多个专家的输出,是相同的一个embeding。而mmoe的任务tower输入的则是经过自己任务特有的门控网络加权过的多个专家的输出,对于不同任务是不同的。没有明显增加参数,却对网络的学习起到了重要的影响作用。
通俗理解:我们网络中的每个专家都是可以学到一些关于多个任务的各自的专业知识,而我们用多个门控网络,就相当于起到了一个Attention的作用。就例如:我们使用一个多目标任务网络去预估一个人分别得感冒和高血压的概率,我们现在有多个专家都会相同的这个病人进行会诊,但是每个专家各有所长又各有所短,这个时候,我们就通过一个门控网络去自动的学习对于某种病情应该多听从哪个专家的意见,最后对各个专家的意见进行加权求和之后来综合评定这个人患某种病的概率。
让每个专家发挥出各自的特长,是不是更有利于我们实际的情况呢?
而开篇所提到的mmoe网络的冲突性建模能力也就来自于这多个门控网络对于多个任务可学习的调控能力。多个专家加上多个门控网络,不同任务对应的门控网络可以学习到不同的Experts组合模式,模型更容易捕捉到子任务间的相关性和差异性,能够使得我们多个任务融合的更加平滑,最终打分得出的结果也更加能够动态综合多个专家的特长与能力,得出一个更有益于我们业务目标的结果。
前文我们已经介绍过,mmoe网络的提出主要就是提出了一个新的MTL架构。所以上文中,我就没有在引入一些晦涩难懂的公式,而是全部采用了文字说明的形式来下进行阐述,希望能似的读者看起来更丝滑一些(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作为一种复杂的DNNlayer,我们可以在网络中叠加多个mmoelayer,实现一些比较复杂的网络来学习一些比较复杂的多目标任务。(4)代码时光
talkischeap,showmethecode!!!
哎,终于再次写到代码时光了!
对于mmoe模型,才开始看源码到最后理解花了挺长时间,中间主要的时间都花在了实现的时候专家网络和多门控网络的输入输出维度对应上。下面的代码注释均写的比较详细,看的过程中,如有任何问题欢迎公众号留言讨论欢迎关注微信公众号:算法全栈之路coding:utf8importnumpyasnpimportosimportargparseimporttensorflowastfimportlogutilimportparamsconffromdatehelperimportDateHelperimportdataconsumerfrommmoeimportMMoEfromtensorflow。kerasimportlayers,Modelfromtensorflow。keras。optimizersimportAdamfromtensorflow。keras。callbacksimportCallbackfromtensorflow。keras。initializersimportVarianceScalingos。environ〔TFCPPMINLOGLEVEL〕2os。environ〔CUDAVISIBLEDEVICES〕1importtensorflowastffromtensorflowimporttensordot,expanddimsfromtensorflow。kerasimportlayers,Model,initializers,regularizers,activations,constraints,Inputfromtensorflow。keras。backendimportexpanddims,repeatelements,sumclassMMoE(layers。Layer):MultigateMixtureofExpertsmodel。definit(self,units,numexperts,numtasks,useexpertbiasTrue,usegatebiasTrue,expertactivationrelu,gateactivationsoftmax,expertbiasinitializerzeros,gatebiasinitializerzeros,expertbiasregularizerNone,gatebiasregularizerNone,expertbiasconstraintNone,gatebiasconstraintNone,expertkernelinitializerVarianceScaling,gatekernelinitializerVarianceScaling,expertkernelregularizerNone,gatekernelregularizerNone,expertkernelconstraintNone,gatekernelconstraintNone,activityregularizerNone,kwargs):MethodforinstantiatingMMoElayer。:paramunits:Numberofhiddenunits隐藏单元:paramnumexperts:Numberofexperts专家个数,可以有共享专家,也可以有每个任务独立的专家:paramnumtasks:Numberoftasks任务个数,和tower个数一致:paramuseexpertbias:Booleantoindicatetheusageofbiasintheexpertweights。专家的权重是否添加偏置:paramusegatebias:Booleantoindicatetheusageofbiasinthegateweights。门控的权重是否添加偏置:paramexpertactivation:Activationfunctionoftheexpertweights。专家激活函数:paramgateactivation:Activationfunctionofthegateweights。门控激活函数:paramexpertbiasinitializer:Initializerfortheexpertbias。专家偏置初始化:paramgatebiasinitializer:Initializerforthegatebias。门控偏置初始化:paramexpertbiasregularizer:Regularizerfortheexpertbias。专家正则化:paramgatebiasregularizer:Regularizerforthegatebias。门控正则化:paramexpertbiasconstraint:Constraintfortheexpertbias。专家偏置:paramgatebiasconstraint:Constraintforthegatebias。门控偏置:paramexpertkernelinitializer:Initializerfortheexpertweights:paramgatekernelinitializer:Initializerforthegateweights:paramexpertkernelregularizer:Regularizerfortheexpertweights:paramgatekernelregularizer:Regularizerforthegateweights:paramexpertkernelconstraint:Constraintfortheexpertweights:paramgatekernelconstraint:Constraintforthegateweights:paramactivityregularizer:Regularizerfortheactivity:paramkwargs:AdditionalkeywordargumentsfortheLayerclass附属参数若干super(MMoE,self)。init(kwargs)Hiddennodesparameterself。unitsunitsself。numexpertsnumexpertsself。numtasksnumtasksWeightparameterself。expertkernelsNoneself。gatekernelsNoneself。expertkernelinitializerinitializers。get(expertkernelinitializer)self。gatekernelinitializerinitializers。get(gatekernelinitializer)self。expertkernelregularizerregularizers。get(expertkernelregularizer)self。gatekernelregularizerregularizers。get(gatekernelregularizer)self。expertkernelconstraintconstraints。get(expertkernelconstraint)self。gatekernelconstraintconstraints。get(gatekernelconstraint)Activationparameterself。expertactivationactivations。get(expertactivation)self。expertactivationexpertactivationself。gateactivationgateactivationBiasparameterself。expertbiasNoneself。gatebiasNoneself。useexpertbiasuseexpertbiasself。usegatebiasusegatebiasself。expertbiasinitializerinitializers。get(expertbiasinitializer)self。gatebiasinitializerinitializers。get(gatebiasinitializer)self。expertbiasregularizerregularizers。get(expertbiasregularizer)self。gatebiasregularizerregularizers。get(gatebiasregularizer)self。expertbiasconstraintconstraints。get(expertbiasconstraint)self。gatebiasconstraintconstraints。get(gatebiasconstraint)Activityparameterself。activityregularizerregularizers。get(activityregularizer)self。expertlayers〔〕self。gatelayers〔〕在初始化的过程中,先构建好网络结构foriinrange(self。numexperts):有几个专家,这里就添加几个dense层,dense层的输入为上面传入,当前层的输出维度为units的值,隐藏单元个数self。expertlayers。append(layers。Dense(self。units,activationself。expertactivation,usebiasself。useexpertbias,kernelinitializerself。expertkernelinitializer,biasinitializerself。expertbiasinitializer,kernelregularizerself。expertkernelregularizer,biasregularizerself。expertbiasregularizer,activityregularizerNone,kernelconstraintself。expertkernelconstraint,biasconstraintself。expertbiasconstraint))门控网络,门控网络的个数等于任务数目,但是取值数据的维度等于专家个数,mmoe对每个任务都要融合各个专家的意见。有几个任务,foriinrange(self。numtasks):numtasks个门控,numexperts维数据self。gatelayers。append(layers。Dense(self。numexperts,activationself。gateactivation,usebiasself。usegatebias,kernelinitializerself。gatekernelinitializer,biasinitializerself。gatebiasinitializer,kernelregularizerself。gatekernelregularizer,biasregularizerself。gatebiasregularizer,activityregularizerNone,kernelconstraintself。gatekernelconstraint,biasconstraintself。gatebiasconstraint))defcall(self,inputs):Methodfortheforwardfunctionofthelayer。:paraminputs:Inputtensor:paramkwargs:Additionalkeywordargumentsforthebasemethod:return:AtensorassertinputshapeisnotNoneandlen(inputshape)2三个输出的网络expertoutputs,gateoutputs,finaloutputs〔〕,〔〕,〔〕专家网络有几个专家循环几次forexpertlayerinself。expertlayers:注意这里是当前专家的变化输入的元素元素应该是整体embedingcontact之后的一堆浮点数维度数据。(batchsize,embeddingsize,1)expertoutputexpanddims(expertlayer(inputs),axis2)numsexpert(batchsize,unit,1)expertoutputs。append(expertoutput)同batch的数据,既然是沿着第一个维度对接,那根本就不用看第二个维度,那个axis的维度数目相加numsexpert(batchsize,unit,1)这里contact之后,列表里numexperts个tensor在最后一个维度concat到一起,则最后维度变成了(batchsize,unit,numsexpert),只有最后一个维度的维度值改变了。expertoutputstf。concat(expertoutputs,2)门控网络,每个门对每个专家均有一个分布函数。forgatelayerinself。gatelayers:对于当前门控,〔batchsize,numunits〕〔numsexpert,batchsize,numunits〕有多少个任务,就有多少个gatenumtask(batchsize,numexperts),这里对每个专家只有一个数值,和专家的输出维度unit相乘需要拓展维度gateoutputs。append(gatelayer(inputs))这里每个门控对所有的专家进行加权求和forgateoutputingateoutputs:对当前gate,忽略numtask维度,为(batchsize,1,numexperts)expandedgateoutputexpanddims(gateoutput,axis1)每个专家的输出和gate的数据维度相乘(batchsize,unit,numsexpert)(batchsize,1units,numexperts),因此1unitsIfxhasshape(s1,s2,s3)andaxisis1,theoutputwillhaveshape(s1,s2rep,s3)。这里的本质是门控和专家的输出相乘维度不对,如上面所说,门控维度1和需要拓展到各个专家的输出维度unit,方便相乘。算子在tensorflow中表示elementwiseproduct,即哈达马积,即两个向量按元素一个一个相乘,组成一个新的向量,结果向量与原向量尺寸相同。weightedexpertoutputexpertoutputsrepeatelements(expandedgateoutput,self。units,axis1)上面输出的维度是(batchsize,unit,numsexpert),对第二维numsexpert求和则该维度就变成一个数值(batchsize,unit)这里对各个专家的结果聚合之后,返回的是一个综合专家对应的输出单元unit维度。最终有多个门控,上面多个塔,这里返回的是numtasksbatchunits这个维度。finaloutputs。append(sum(weightedexpertoutput,axis2))返回的矩阵维度numtasksbatchunits返回多个门控,每个门控有综合多个专家返回的维度units这里finaloutputs返回的是个list,元素个数等于门控个数也等于任务个数returnfinaloutputsdefinitargs():parserargparse。ArgumentParser(descriptiondnndemo)parser。addargument(mode,defaulttrain)parser。addargument(traindatadir)parser。addargument(modeloutputdir)parser。addargument(curdate)parser。addargument(log,default。。logtensorboard)parser。addargument(usegpu,defaultFalse,typebool)argsparser。parseargs()returnargsdefgetfeaturecolumnmap():keyhashsizemap{adid:10000,siteid:10000,sitedomain:10000,sitecategory:10000,appid:10000,appdomain:10000,appcategory:1000,deviceid:1000,deviceip:10000,devicetype:10,deviceconntype:10,}featurecolumnmapdict()forkey,valueinkeyhashsizemap。items():featurecolumnmap。update({key:tf。featurecolumn。categoricalcolumnwithhashbucket(key,hashbucketsizevalue,dtypetf。string)})returnfeaturecolumnmapdefbuildembeding():featuremapgetfeaturecolumnmap()featureinputslist〔〕defgetfieldemb(categoricalcolkey,embsize16,inputshape(1,)):print(categoricalcolkey)embedcoltf。featurecolumn。embeddingcolumn(featuremap〔categoricalcolkey〕,embsize)层名字不可以相同,不然会报错densefeaturelayertf。keras。layers。DenseFeatures(embedcol,namecategoricalcolkeyemb2dense)featurelayerinputsdict()input和DenseFeatures必须要用dict来存和联合使用,深坑啊!!featurelayerinputs〔categoricalcolkey〕tf。keras。Input(shape(1,),dtypetf。dtypes。string,namecategoricalcolkey)保存供modelinput使用。featureinputslist。append(featurelayerinputs〔categoricalcolkey〕)returndensefeaturelayer(featurelayerinputs)embedingmap{}forkey,valueinfeaturemap。items():print(key:key)embedingmap。update({key:getfieldemb(key)})returnembedingmap,featureinputslistdefbuilddnnnet(net,paramsconf,namectr):可以在下面接入残差网络fori,dnnhiddensizeinenumerate(paramsconf。DNNHIDDENSIZES):DNNHIDDENSIZES〔512,128,64〕nettf。keras。layers。Dense(dnnhiddensize,activationrelu,nameoveralldensess(i,name))(net)returnnetdefbuildmodel(embmap,inputslist):需要特殊处理和交叉的特征,以及需要短接残差的特征,可以单独拿出来definelist〔〕adidembembmap〔adid〕deviceidemdembmap〔deviceid〕adxdevicetf。multiply(adidemb,deviceidemd)definelist。append(adxdevice)直接可以拼接的特征commonlist〔〕forkey,valueinembmap。items():commonlist。append(value)embedingcontactnettf。keras。layers。concatenate(definelistcommonlist)SetupMMoElayer返回的矩阵维度numtasksbatchunits返回多个门控,每个门控有综合多个专家返回的维度units这里finaloutputs返回的是个list,元素个数等于门控个数也等于任务个数mmoelayersMMoE(units4,numexperts8,numtasks2)(net)outputlayers〔〕BuildtowerlayerfromMMoElayer对每个mmoelayer,后面均接着2层dense到输出,list,元素个数等于门控个数也等于任务个数forindex,tasklayerinenumerate(mmoelayers):对当前task,batchunits维度的数据,介入隐藏层towerlayerlayers。Dense(units8,activationrelu,kernelinitializerVarianceScaling())(tasklayer)这里unit为1,当前任务为2分类outputlayerlayers。Dense(units1,nametasks(index),activationsigmoid,kernelinitializerVarianceScaling())(towerlayer)outputlayers。append(outputlayer)Compilemodel这里定义了模型骨架,input为模型输入参数,而outputlayers是一个列表,列表里返回了2个任务各自的logit其实分别返回了每个task的logit,logit这里为分类数目维度的数组,2维过softmaxmodelModel(inputs〔inputslist〕,outputsoutputlayers)returnmodeldeftrain():outputrootdir{}{}{}。format(paramsconf。BASEDIR,args。modeloutputdir,args。curdate)os。mkdir(outputrootdir)modelfulloutputdiros。path。join(outputrootdir,modelsavedmodel)printinfologlogutil。info(modeloutputdir:smodelfulloutputdir)重置keras的状态tf。keras。backend。clearsession()logutil。info(starttrain。。。)traindatelistDateHelper。getdaterange(DateHelper。getdate(1,args。curdate),DateHelper。getdate(0,args。curdate))traindatelist。reverse()print(traindatelist:,。join(traindatelist))loaddatafromtf。data,兼容csv和tfrecordtrainset,testsetdataconsumer。getdataset(args。traindatadir,traindatelist,getfeaturecolumnmap()。values())trainx,trainytrainsetlogutil。info(gettraindatafinish。。。)embmap,featureinputslistbuildembeding()logutil。info(buildembedingfinish。。。)构建模型modelbuildmodel(embmap,featureinputslist)logutil。info(buildmodelfinish。。。)defmysparsecategoricalcrossentropy(ytrue,ypred):returntf。keras。sparsecategoricalcrossentropy(ytrue,ypred,fromlogitsTrue)opttf。keras。optimizers。Adam(paramsconf。LEARNINGRATE)注意这里设定了2个损失分别对应〔ctrpred,ctcvrpred〕这两个任务lossweights〔1。0,1。0〕这种方式可以固定的调整2个任务的loss权重。model。compile(optimizeropt,loss{task0:binarycrossentropy,task1:binarycrossentropy},lossweights〔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。plotmodel(model,multiinputandoutputmodel。png,showshapesTrue,dpi150)print(starttraining)需要设置profilebatch0,tensorboard页面才会一直保持更新tensorboardcallbacktf。keras。callbacks。TensorBoard(logdirargs。log,histogramfreq1,writegraphTrue,updatefreqparamsconf。BATCHSIZE200,embeddingsfreq1,profilebatch0)定义衰减式学习率classLearningRateExponentialDecay:definit(self,initiallearningrate,decayepochs,decayrate):self。initiallearningrateinitiallearningrateself。decayepochsdecayepochsself。decayratedecayratedefcall(self,epoch):dtypetype(self。initiallearningrate)decayepochsnp。array(self。decayepochs)。astype(dtype)decayratenp。array(self。decayrate)。astype(dtype)epochnp。array(epoch)。astype(dtype)pepochdecayepochslrself。initiallearningratenp。power(decayrate,p)returnlrlrscheduleLearningRateExponentialDecay(paramsconf。INITLR,paramsconf。LRDECAYEPOCHS,paramsconf。LRDECAYRATE)该回调函数是学习率调度器lrschedulecallbacktf。keras。callbacks。LearningRateScheduler(lrschedule,verbose1)训练注意这里的trainset可以使用for循环迭代,tf2。0默认支持eager模式这里的trainset包含两部分,第一部分是feature,第二部分是label(click,clickconversion)注意这里是feature,(click,clickconversion),第二项是tuple,不能是数组或列表〔〕,不然报数据维度不对,坑死爹了。model。fit(trainset,trainset〔labels〕,validationdatatestset,epochsparamsconf。NUMEPOCHS,NUMEPOCHS10stepsperepochparamsconf。STEPSPEREPHCH,validationstepsparamsconf。VALIDATIONSTEPS,callbacks〔tensorboardcallback,lrschedulecallback〕)模型保存tf。keras。models。savemodel(model,modelfulloutputdir)tf。savedmodel。save(model,modelfulloutputdir)print(savesavedmodelsuccess)ifnamemain:print(tf。version)tf。compat。v1。disableeagerexecution()runtensorboard:tensorboardport8008hostlocalhostlogdir。。logargsinitargs()ifargs。modetrain:train()
到这里,多任务学习之mmoe理论详解与实践就写完成了,欢迎留言交流
宅男民工码字不易,你的关注是我持续输出的最大动力!!!
接下来作者会继续分享学习与工作中一些有用的、有意思的内容,点点手指头支持一下吧
欢迎扫码关注作者的公众号:算法全栈之路
END
江疏影自曝与胡歌分手的原因,这一点也是女生最不能接受说起江疏影,不知道你们对她了解多少呢?江疏影1986年9月1日出生于上海,是中国内地影视女演员!她出演过一仆二主致我们终将逝去的青春和好先生等等,给观众们留下了非常深刻的印象!虽然
瞭望丨中国科学院院士赵红卫重大科技基础设施造福人类依托兰州重离子加速器大科学装置,近代物理所在世界上首次合成了30多种新核素,首次精确测量了26种短寿命原子核的质量重大科技基础设施已成了培养和吸纳优秀科技人才的蓄水池文瞭望新闻周刊
姚明身高科比技术?状元热门再砍3454帽,波波维奇眼睛看直了简单对篮球运动进行概括,其实无非就是更高更快更强,在NBA里,身体素质几乎等于一切,当然也不乏有身高如普通人的超级巨星,但那毕竟只是少数,不过有时候身体素质太极端也不是一件好事情,
男人的这些模样,只会展现给他最爱的女人文森屿鹿林每个人都是多面性的,面对不同的人,会展现出不同的模样。生活中,我们每个人都身兼多个角色,在不同的场合,面对不同的人时,会展现出不一样的自己。有些人不需要知道太多,有些人不
山北江南一望中,雁飞不到楚天空山北江南一望中,雁飞不到楚天空,衡阳路远无消息,何处潇湘有信鸿。昔曾南去随胡雁,今又北来伴塞鸿,莫道衡阳夜雨梦魂中。路远意如何,风雨潇湘家山千里外,南飞雁字几行多。万里程,凭栏独立
我也知道自己嘴笨写在最后。大家好,我是泽鹏。第一次看到仙贝这篇稿子时,我其实挺受触动。我也属于是大人眼里的不太会来事的年轻人,无法夸夸其谈,甚至每次一想到要去长辈家里做客,都会有些头疼。和仙贝不同
风碎倒影相知如镜群山水面上绵亘一座浮雕黛青色养眼,林木繁茂白房子错落有致,浮云从凹陷处溢出,散漫飘远血管那么多条,运煤车深入腹地树上野果,被飞鸟纷纷啄食向着对岸呼唤一声,回音充满岁月柔情大片茶园,
害群之马,错的不仅仅是一条马人们反思的时候,第一反应就是痛恨敌人,谴责他们的残忍狠毒,毫无人性,接下来会分析下当时的历史环境。终究还是避重就轻的多。伟人一针见血落后就要挨打!我们反思的重点其实是为什么落后,为
9月19号,尝试写读书笔记的第6天!告诉自己别轻易放弃忙碌的一天结束了!今天是尝试写读书笔记的第6天,确实不知道应该写点什么?放弃实在是太容易了,理由也可以有很多。但想坚持下去,只要告诉自己写就完了,哪怕随便写点什么。我就是想试试逼自
那些当下让你很容易上瘾的东西,未来一定会让你很痛单子语录读书心得生活随感1特斯拉,作为一家不盈利但却拥有近万亿美元(9480多亿)市值的公司,最值钱的资产可能只是一个去火星的伟大梦想。因为这年头,支撑股票价格的,不完全是价值,还
父母爱情转眼间,父母一起离开我们已经五十天了,犹豫中最终我还是决定写一篇纪念的文章,尽管我实在不敢打开想的记忆那个阀门。刚刚做在阳台上,我又突然想起那崩溃的日子,随即泪流满面,所以有了这个