作者丨Ai 来源丨宅码 编辑丨极市平台 本文分享如何从loss和gradient方面,优化多任务模型,缓解负迁移或跷跷板的问题。不足之处,还望批评指正。背景 最近工作中,有使用到多任务模型,但实际使用时,会面临负迁移、跷跷板等现象。 除了从模型角度优化,这里介绍从loss和gradient方面的优化策略。阿里云云栖号的一篇文章〔1〕总结的很好,多目标优化策略主要关注三个问题: 1。Magnitude(Loss量级):Loss值有大有小,出现取值大的Loss主导的现象,怎么办? 2。Velocity(Loss学习速度):任务有难有易,Loss学习速度有快有慢,怎么办? 3。Direction(Loss梯度冲突):多个Loss的反向梯度,更新方向冲突,出现翘翘板、负迁移现象,怎么办? 为了解决以上问题,本文分享以下8种方法: 1。UncertaintyWeighting 论文:Kendall,A。,Gal,Y。,Cipolla,R。(2018)。Multitasklearningusinguncertaintytoweighlossesforscenegeometryandsemantics。InProceedingsoftheIEEEconferenceoncomputervisionandpatternrecognition 引用量:2047 defUWLoss(labels,preds,logvars,devicegpu:1):UncertaintyWeightingKendall,A。,Gal,Y。,Cipolla,R。(2018)。Multitasklearningusinguncertaintytoweighlossesforscenegeometryandsemantics。InProceedingsoftheIEEEconferenceoncomputervisionandpatternrecognition(pp。74827491)。assertlen(preds)labels。shape〔1〕1assertlen(lossweights)labels。shape〔1〕1labelslabels。to(device)计算各环节的ce〔click,cart,order〕loss1nn。functional。binarycrossentropy(preds〔0〕,labels〔:,0〕)loss2nn。functional。binarycrossentropy(preds〔1〕,labels〔:,1〕)loss3nn。functional。binarycrossentropy(preds〔2〕,labels〔:,2〕)losses〔loss1,loss2,loss3〕fori,logvarinenumerate(logvars):logvarlogvar。to(device)losses〔i〕(12)(torch。exp(logvar〔0〕)2)losses〔i〕torch。log(torch。exp(logvar〔0〕)1)losssum(losses)returnloss,losses2。MGDA 论文:Sener,O。,Koltun,V。(2018)。Multitasklearningasmultiobjectiveoptimization。Advancesinneuralinformationprocessingsystems,31。 引用量:638 代码:https:github。comislorgMultiObjectiveOptimization 据〔2〕解释,作者将MTL看作一个带约束优化问题,求解过程相当于寻找帕累托最优过程。假定固有有一群任务和可分配的任务损失权重,从一种分配状态到另一种状态的变化中,在没有使任何任务境况变坏的前提下,使得至少一个任务变得更好,这就达到了帕累托最优化。 MTL的优化目标函数: 文中,作者总结一个方法:多重梯度下降算法(multiplegradientdescentalgorithm,MGDA),该算法针对共享参数和任务独立参数,声明KKT(KarushKuhnTucker)条件: 第二个条件,是让每个任务独立的参数梯度为0,直接对每个任务独立分支的部分上,各自做梯度下降即可。而第一个条件是要找到一个帕累托最优点(即最好的alpha组合),使得共享层参数梯度为0。这边作者使用了FrankWolfe算法。 虽然FrankWolfe求解器有效率和质量,但我们需要对每个任务t计算其在共享层的参数梯度,用于反向传播,因此要T次反向传播后,才能前向传播1次。考虑到后向传播比前向传播耗时,本文提出一个更有效的办法,只需要1次反向传播。先把任务t的预测函数做个变换:从x为自变量,deltash和deltat为参数,变换到以下,以表征函数作为自变量,deltat为参数。而表征函数内部又是由x为自变量,deltash为参数。 如果将表征函数g下样本xi的表征,表达为zi,则我们可以得到共享层部分的帕累托最优的上界: 简单来说就是共享层的表征向量取代了原来的x。 源码上看,先看:MGDA先对共享层和任务独立层,获取梯度和反向传播。 而MGDAUB是冻结共享层的反向传播,先通过共享层获取表征向量,再对任务独立层算梯度和反向传播,最后基于任务独立层的梯度,算出alpha后,计算共享层的损失,进而反向传播。 3。GradNorm 论文:Chen,Z。,Badrinarayanan,V。,Lee,C。Y。,Rabinovich,A。(2018,July)。Gradnorm:Gradientnormalizationforadaptivelossbalancingindeepmultitasknetworks。InInternationalconferenceonmachinelearning(pp。794803)。PMLR。 引用量:670 代码:https:github。comhav4ikHydrablobmastersrcapplicationstrainersgradnorm。py 该文章〔3〕写的很详细,建议直接阅读它。GradNorm是基于不同任务的学习速度调整任务权重,学习速度越快,权重越小。 每个任务的学习速度反值r为: 得到学习速率后,便可以更新任务权重: 其中alpha为调整力度,它越大,调整力度越大。GradNorm整个训练流程为: forstep,batchinenumerate(trainloader):confeatsbatch〔features〕。float()。to(device)targetsbatch〔targets〕。float()。to(device)predsmodel(confeats)模型训练loss,taskloss,multilossesmodel。loss(targets,preds,lossweightslossweights,devicedevice)计算损失获得第一轮的任务损失ifepoch0andstep0:initialtasklosstaskloss。data。cpu()。detach()。numpy()optimizer。zerograd()梯度清空loss。backward(retaingraphTrue)计算梯度重置Lgrad对w的梯度model。weights。grad。datamodel。weights。grad。data0。0获取共享层最后一层网络权重WWmodel。getlastsharedlayer()计算每个任务的GiW(t)norms〔〕foriinrange(len(taskloss)):Loss对W求导wGradnormgntorch。autograd。grad(taskloss〔i〕,W。parameters(),retaingraphTrue)norms。append(torch。norm(torch。mul(model。weights〔i〕,gn〔0〕)))normstorch。stack(norms)计算均值Gradnormmeannormnp。mean(norms。data。cpu()。detach()。numpy())计算学习速度反值rlossratiotaskloss。data。cpu()。detach()。numpy()initialtasklossinvtrainratelossrationp。mean(lossratio)计算Lgradconsttorch。tensor(meannorm(invtrainratealpha),requiresgradFalse)gnlosstorch。sum(torch。abs(normsconst。to(device)))计算lgrad对w的梯度(即GradNormLoss的梯度)model。weights。gradtorch。autograd。grad(gnloss,model。weights)〔0〕共同反向传播更新lgrad对w的梯度和网络参数optimizer。step()epochtrnlossloss。cpu()。detach()。numpy()foriinrange(len(labels)1):epochtrnmultiloss〔i〕np。round(multilosses〔i〕。cpu()。detach()。numpy(),4)trainhistory〔i〕。append(multilosses〔i〕。cpu()。detach()。numpy())收集个任务的w、Gradnorm和GradNormTargetweighthistory〔i〕。append(model。weights。data〔i〕。clone())gnhistory〔i〕。append(norms〔i〕)consthistory〔i〕。append(const〔i〕)epochtrnlossround(epochtrnloss(step1),4)renormalizethelossweightsaftereachepochnormcoeff(len(labels)1)torch。sum(model。weights。data,dim0)model。weights。datamodel。weights。datanormcoeff4。DWA 论文:Liu,S。,Johns,E。,Davison,A。J。(2019)。Endtoendmultitasklearningwithattention。InProceedingsoftheIEEECVFconferenceoncomputervisionandpatternrecognition(pp。18711880)。 引用量:571 DWA全称是DynamicWeightAverage,受到GradNorm的启发,它也通过考虑每个任务的损失改变,去学习平均不同训练轮数下各任务的权重。GradNorm需要接触网络内部梯度,而DWA提出只要任务的损失数值,所以实施起来更简单。作者把任务k的权重lambdak定义如下: wk计算损失相对衰减率,越小,说明学习速度越快。我们要减少其重要度,为此,喂入softmax获取各任务的权重,这样wk越小,权重越小,即近期训练过程中学习速度快的任务,要给低的重要度。T越大,权重越趋近于1,因此任务权重更平均,所以T调大,有利于让各任务的权重更均匀。乘上K是为了确保各任务的权重之和为K,保证权重在同一量纲下缩放。DWA的缺点是容易受损失量级大的任务主导〔3〕。另外,前2轮wk初始化为1。dynamicweightaveraginglossweightsnp。ones((modelparams〔totalepoch〕,len(labels)))avgtasklossnp。zeros((modelparams〔totalepoch〕,len(labels)))forepochinrange(modelparams〔totalepoch〕):前2轮wk初始化为1ifepoch0orepoch1:pass算权重else:sumw〔〕foriinrange(len(labels)):wavgtaskloss〔epoch1,i〕avgtaskloss〔epoch2,i〕sumw。append(np。exp(wT))lossweights〔epoch〕〔len(labels)wnp。sum(sumw)forwinsumw〕model。train()forstep,batchinenumerate(trainloader):modeltrainingcodesavelossineachepoch每epoch结束追加lossavgtaskloss〔epoch〕〔tasklosslen(trainloader)fortasklossinepochtrnmultiloss〕 5。PELTR 论文:Lin,X。,Chen,H。,Pei,C。,Sun,F。,Xiao,X。,Sun,H。,。。。Jiang,P。(2019,September)。Aparetoefficientalgorithmformultipleobjectiveoptimizationinecommercerecommendation。InProceedingsofthe13thACMConferenceonrecommendersystems(pp。2028)。 引用量:64 代码:https:github。comweberrrPELTRblobmasterPELTR。py〕(https:github。comweberrrPELTRblobmasterPELTR。py 本文是在阿里电商LTR(LearingtoRank)场景下,提出用帕累托最优算法找到各任务适合的权重,跟前面提到的MultiObjectiveOptimization的思路有些相似。直接看训练过程: 绿色部分:定义输入。初始化各任务权重为1K,各任务目标的值约束边界。橙色部分:定义输出。首轮先用初始权重加权求和多任务损失。红色部分:批次更新。随机梯度下降更新模型参数,用PECsolver求解各任务的权重,加权求和多任务损失。 先定义损失函数: 为了求解该问题,作者提出了PECsolver: 简单来说,利用KKT条件来进行求解,放松条件求解w,然后考虑原约束调节进一步收紧解集。〔4〕,涉及数学细节,感兴趣可阅读原文。defparetostep(w,c,G):ref:http:ofey。mepapersPareto。pdfK:thenumberoftaskM:thedimofNNsparams:paramW:(K,1):paramC:(K,1):paramG:(K,M):return:GGTnp。matmul(G,np。transpose(G))(K,K)enp。mat(np。ones(np。shape(w)))(K,1)mupnp。hstack((GGT,e))(K,K1)mdownnp。hstack((np。transpose(e),np。mat(np。zeros((1,1)))))(1,K1)Mnp。vstack((mup,mdown))(K1,K1)znp。vstack((np。matmul(GGT,c),1np。sum(c)))(K1,1)hatwnp。matmul(np。matmul(np。linalg。inv(np。matmul(np。transpose(M),M)),M),z)(K1,1)hatwhatw〔:1〕(K,1)hatwnp。reshape(np。array(hatw),(hatw。shape〔0〕,))(K,)cnp。reshape(np。array(c),(c。shape〔0〕,))(K,)newwASM(hatw,c)returnnewwdefASM(hatw,c):ref:http:ofey。mepapersPareto。pdf,https:stackoverflow。comquestions33385898howtoincludeconstrainttoscipynnlsfunctionsolutionsothatitsumsto1:paramhatw:(K,):paramc:(K,):return:Anp。array(〔〔0ifi!jelse1foriinrange(len(c))〕forjinrange(len(c))〕)bhatwx0,nnls(A,b)deffn(x,A,b):returnnp。linalg。norm(A。dot(x)b)cons{type:eq,fun:lambdax:np。sum(x)np。sum(c)1}bounds〔〔0。,None〕forinrange(len(hatw))〕minoutminimize(fn,x0,args(A,b),methodSLSQP,boundsbounds,constraintscons)newwminout。xcreturnnewwlossweightsnp。full(len(labels)1,1(len(labels)1))(K,)wconstraintnp。full((len(labels)1,1),0。)(K,1)forepochinrange(modelparams〔totalepoch〕):model。train()forstep,batchinenumerate(trainloader):confeatsbatch〔features〕。float()。to(device)targetsbatch〔targets〕。float()。to(device)predsmodel(confeats)模型训练loss,taskloss,multilossesmodel。loss(targets,preds,lossweightslossweights,devicedevice)计算损失optimizer。zerograd()梯度清空loss。backward(retaingraphTrue)计算梯度G:(K,m)grads〔〕forlintaskloss:grad〔〕optimizer。zerograd()l。backward(retaingraphTrue)forparaminmodel。parameters():ifparam。gradisnotNone:grad。append(param。grad。view(1)。cpu()。detach()。numpy())grads。append(np。hstack(grad))Gnp。vstack(grads)lossweightsparetostep(len(labels)1,wconstraint,G)optimizer。step()梯度回传6。HTW 论文:Kongyoung,S。,Macdonald,C。,Ounis,I。(2020)。Multitasklearningusingdynamictaskweightingforconversationalquestionanswering。 引用量:5 在现有的MTL权重分配方法中,对所有任务都是一视同仁,但其实存在一些业务场景是有主次任务之分。HTW(HybridTaskWeighting)则是分别对主任务和辅助任务分别使用:主任务:AbridgedLinearSchedule。设置一个step阈值,ttaoT10,即前10的训练step是用于主任务的warmup(任务权重当前step数t总step数T),后90的steps,主任务的权重为1。这里warmup的目的应该是让模型训练初期能从辅助任务中学习到一些有利于主任务的信息,然后后期再专心学习主任务。这里stepepoch总数trainloader的循环step数;辅助任务:LossBalancedTaskWeighting(LBTW)。对于每个batch,任务的权重lambda是stept下的loss除以首轮下的loss,即某任务学习速度越快,它的权重越趋向于0。另外,引入alpha0。5用于平衡任务权重。 整个训练流程如下: 记录首轮lossifstep0:initialtasklosstasklossloss0mutillosses〔〕对辅助任务算权重,并加权辅助任务的lossforiinrange(len(taskloss)1):lossweights〔i〕(taskloss〔i〕initialtaskloss〔i〕)alphalosslossweights〔i〕taskloss〔i〕mutillosses。append(lossweights〔i〕taskloss〔i〕)对主任务算权重,并加权主任务的lossifstepcountstepthreshold:lossweights〔1〕stepcounttotalstepstepcount1else:lossweights〔1〕1。0losslossweights〔1〕taskloss〔1〕mutillosses。append(lossweights〔1〕taskloss〔1〕)7。PCGrad 论文:Yu,T。,Kumar,S。,Gupta,A。,Levine,S。,Hausman,K。,Finn,C。(2020)。Gradientsurgeryformultitasklearning。AdvancesinNeuralInformationProcessingSystems,33,58245836。 引用量:353 代码:https:github。comchenllliangGradientVaccineblob17fa758fdd4f87475ee2847db6fc0a013631fee3fairseqfairseqoptimpcgrad。py 如果两个梯度在方向上存在冲突,就把任务i的梯度投影到具有冲突梯度的任何其他任务j的梯度的法向量平面上。如下图所示,若任务i和任务j的余弦相似度是正值,如图(d),不相互冲突,那任务i和j保持各自原有梯度,做更新。若如图(a),梯度方向有相互冲突,对于任务i的梯度,就把它投影在任务j梯度的法向量上,如图(b),作为任务i的新梯度去更新。对于任务j就反之,如图(c)。 投影计算方式如下〔5〕: 如上图所示,gi和gj有冲突,所以要把gi投影在gj的法向量平面上作为任务i的新梯度(即蓝色虚线)。假设绿色向量为agj,基于向量的加法三角形法则,可以得到蓝色虚线xgiagj。之后做以下计算,便可得到a和蓝色虚线向量(即任务i的新梯度): 效果如下: 整体训练流程如下: Computegradientprojections。defprojgrad(gradtask):计算投影梯度forkinrange(numtasks):innerproducttf。reducesum(gradtaskgradstask〔k〕)projdirectioninnerproducttf。reducesum(gradstask〔k〕gradstask〔k〕)gradtaskgradtasktf。minimum(projdirection,0。)gradstask〔k〕returngradtask8。GradVac 论文:Wang,Z。,Tsvetkov,Y。,Firat,O。,Cao,Y。(2020)。Gradientvaccine:Investigatingandimprovingmultitaskoptimizationinmassivelymultilingualmodels。arXivpreprintarXiv:2010。05874。 引用量:57 代码:https:github。comchenllliangGradientVaccine PCGrad只在梯度之间的余弦相似度为负值时生效,这导致PCGrad在训练过程中的表现是非常稀疏的,如下面的左图,而真实情况,存在不少任务梯度之间是正余弦相似度,但它们却没被PCGrad考虑,于是作者提出GradVac。 论文附录E有详细推导过程: 整个训练过程如下: 总结 总结如下: 参考资料 〔1〕多任务多目标CTR预估技术阿里云云栖号,文章:https:baijiahao。baidu。coms?id1713545722047735100wfrspiderforpc 〔2〕深度学习的多个loss如何平衡?陈瀚清的回答知乎:https:www。zhihu。comquestion375794498answer2657267272 〔3〕多目标样本权重GradNorm和DWA原理详解和实现知乎:https:zhuanlan。zhihu。comp542296680 〔4〕阿里多目标优化:PELTR知乎:https:zhuanlan。zhihu。comp159459480 〔5〕多任务学习【ICLR2020】PCGrad知乎:https:zhuanlan。zhihu。comp39