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

程序员大白

共 3032字,需浏览 7分钟

 ·

2021-11-14 03:25

点击上方“程序员大白”,选择“星标”公众号

重磅干货,第一时间送达

作者 | hahakity@知乎(已授权)
来源 | https://www.zhihu.com/question/359962155 
编辑 | 极市平台

导读

 

多任务学习中,会存在多个网络部分,对应多个loss。看论文通常是把各loss统一到同一个数量级上,请问这么做的原理是什么?为什么一般不考虑不同网络部分梯度的数量级呢,另外如果不同网络部分单独优化的话,学习率该如何设计比较合理呢? 

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

这两项 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 configurationopt = Adam(lr=0.0002, beta_1=0.5)# compile model with weighting of least squares loss and L1 lossmodel.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开始,结果就变成了这样,

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

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

不过到了第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:本文仅代表作者个人想法,没有在多个任务上验证过这个想法的合理性。大家可以结合实际情况具体判断。


13个你一定要知道的PyTorch特性

解读:为什么要做特征归一化/标准化?

一文搞懂 PyTorch 内部机制

张一鸣:每个逆袭的年轻人,都具备的底层能力




西[]


浏览 29
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报