31

多任务学习中各loss权重应该如何设计?

 3 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzI5MDUyMDIxNA%3D%3D&%3Bmid=2247495778&%3Bidx=2&%3Bsn=5d978b69ac0de682ca9c26c32f8c6409
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

加入极市专业CV交流群,与  1 0000+来自港科大、北大、清华、中科院、CMU、腾讯、百度  等名校名企视觉开发者互动交流!

同时提供每月大咖直播分享、真实项目需求对接、干货资讯汇总,行业技术交流。关注  极市平台  公众号  , 回复  加群, 立刻申请入群~

来源|hahakity@知乎,https://www.zhihu.com/question/359962155

本文已获得作者授权,未经作者许可,不得二次转载。

个人感觉这是一个非常有意思的问题。之前在训练多任务神经网络的时候遇到过类似的问题,在我的问题中,损失函数有两项贡献,

这两项 f 和 g,分别对应着分类损失和分割损失。随着学习的进行,这两个损失函数减小的速度很不一致。往往是一项减小的非常快,另一项减小的超级慢。

看到这个问题的时候,我回想,应该可以对不同的损失项使用不同的学习率,即 Adaptive learning rate。其实 Adaptive Learning Rate 不是新东西,在 Adagrad, RMSProp, Adam 等等优化算法中,都有这个适配学习率的贡献。但那里的适配学习率,是对高维参数空间不同的方向,使用不同的学习率。

对更新快的方向,使用小一点的学习率,对更新慢的方向,使用大一点的学习率。

比如在 Adagrad 算法中,适配学习率的梯度下降算法公式为,

其中是学习率, 是损失函数对网络参数的梯度,普通的 SGD 应该是 。这里将学习率除以一个适配的常数 ,其中 是一个很小的正数,防止除零发散, 是个对角矩阵,其第 i 个矩阵元对应沿第i个方向梯度 的平方,从时间0到时间 t 的累加。

对于多任务学习,上面的适配学习率并没有起到用处,根据

展开的话, 传统的适配学习率用在多任务学习上有如下形式,

如果和 的数量级不一致,上述公式不能为不同的损失函数项提供适配的学习率。按照同样的思路,我们可以要求,

对更新快的任务,使用小一点的学习率,对更新慢的任务,使用大一点的学习率。

其中和 分别是不同任务项梯度平方的时间累积。这种方法可以简单推广到 RMSProp 和 Adam。

在Keras库中,没有对不同的任务使用 Adaptive 的学习率。贴一段 CycleGAN 的代码,里面有一个判别误差,一个重建误差和有两个 Cycle 重建误差,所以最终的误差函数有4项,分别是 mse, mae, mae, mae。但优化算法使用的是同一个 Adam。

model = Model([input_gen, input_id],

[output_d, output_id, output_f, output_b])

# define optimization algorithm configuration

opt = Adam(lr=0.0002, beta_1=0.5)

# compile model with weighting of least squares loss and L1 loss

model.compile(loss=['mse', 'mae', 'mae', 'mae'],

loss_weights=[1, 5, 10, 10],

optimizer=opt)

loss_weights 确实可以在一开始将4个任务的学习率增大 1,5,10 和 10 倍。但并没有对不同的任务使用不同的优化算法,除非最后一个选项 optimizer=opt 改成,

optimizers=[opt, opt, opt, opt]

根据 Keras 文档中对 compile 函数的描述,optimizer 表示一个优化函数,而不是一个列表。而 loss_weights 是要用在加权求和中,得到一个整体的 loss,然后用同一个 optimizer 对神经网络的参数进行更新。

(更新)最近尝试训练一个字体风格转换程序,对不同的任务使用不同的 optimizer,发现在初期确实有效。这个程序意图将楷书 A 风格转换为行书 B。定义了两种损失,一种是自编码损失,另一种是辅助损失,即使用第三种字体--宋体,保证对同样的字,A编码后的潜变量等于A_ c 编码的潜变量;对同样的字,B编码后的潜变量等于 B_c 编码的潜变量。这样,将行书翻译回正常体,让神经网络更好的映射。

如果使用传统的多任务方法,使用一个优化器,损失函数差不多定义为

前两项是自编码损失,后两项是翻译到第三方字体时的编码自洽性损失。结果发现,就算将后两项的损失权重调小1000倍,它们还是起主导,很快让编码器学会将所有的字都编码成常数,后两项损失直接变为0。前两项自编码项不起作用。从第一个Epoch开始,结果就变成了这样,

BRnIZjB.jpg!web

如果对前两项使用一个优化器,对后两项使用另一个优化器,

至少在前十个 Epoch,训练是正常进行的。结果如下,

MBBbI3v.jpg!web

不过到了第20个Epoch的时候,后两项又开始起决定性作用,结果变回到使用一个优化器的情形。

还有一个技巧,不知道有没有人尝试过。即先对一个任务做训练,训练到一定阶段,再启动第2个任务。

其他:

在下面这篇知乎文章中见到分别对两个任务的共享参数,两个任务的私有参数,使用3个 Adam 独立训练的小技巧。实现方法是 Tensorflow, 应该可以很快扩展到其他机器学习库。

mountain blue: Multi-task Learning的三个小知识

另外,下面回答中游凯超提到的 《Multi-Task Learning Using Uncertainty to Weigh Losses for Scene Geometry and Semantics》很有启发。那篇文章对不同任务的损失函数项按照方差进行归一化。感觉与 Glorot/Xavier/He 权重初始化,以及 Batch/Layer/Instance/Group normalization 的思路都很像。也就是说,如果不同任务的私有参数(甚至是最后一层的参数或loss 本身),满足的分布方差不一样,也会出现有的任务梯度消失,有的任务梯度爆炸的问题。

如果神经网络参数初始化的时候,已经使用了 Glorot/Xavier/He 的方法,那么每一层所有参数满足的分布是归一化的。但没有保证每个任务的私有参数,都单独进行了归一化。

将不同任务的参数分组初始化,正规化以及优化,可能是个好玩的研究方向。

PS:本文仅代表作者个人想法,没有在多个任务上验证过这个想法的合理性。大家可以结合实际情况具体判断。

推荐阅读:

vQrmqqN.jpg!web

添加极市小助手微信 (ID : cv-mart) ,备注: 研究方向-姓名-学校/公司-城市 (如:AI移动应用-小极-北大-深圳),即可申请加入 AI移动应用极市技术交流群 ,更有 每月大咖直播分享、真实项目需求对接、求职内推、算法竞赛、 干货资讯汇总、行业技术交流 一起来让思想之光照的更远吧~

RrYj22I.jpg!web

△长按添加极市小助手

Yjqyyiq.jpg!web

△长按关注极市平台,获取 最新CV干货

觉得有用麻烦给个在看啦~   


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK