0 前言
最近Multi-task learning在学术界和业界受到大家广泛的研究和关注。很多试验证明了Multi-task learning有利于单任务学习,当然,这也符合我们人类学习特性,同时, Multi-task learning通过共享底层模型来实现多个任务,在预测速度和资源利用方面都得到较好的改善,所以在业界也得到了广泛的应用。
那么平时在实践中,假如三个任务,怎么融合多个任务loss? l o s s = a ∗ l o s s 1 + b ∗ l o s s 2 + c ∗ l o s s 3 loss = a*loss_1 + b*loss_2 + c*loss_3 loss=a∗loss1+b∗loss2+c∗loss3其中权重a,b,c怎么设置保证每个单任务都可以较好的收敛效果?会不会因为设置不合理,导致某单个task的预测效果不理想?答案是:会的。如果单个task任务产生的loss量级相差较大,设置的不合理,会导致模型忽视对整体loss产生较小影响的某个task,导致预测的时候,效果不理想。我就踩过亢,所以做个梳理和总结,实践过如下三个方法的尝试:
1 简单粗暴,根据任务重要程度设置权重
可以根据每个任务重要程度,设置每个task权重,如果 t a s k 1 task1 task1的重要性比 t a s k 2 task_2 task2大,则可以适当让 t a s k 1 task_1 task1的权重 w 1 w_1 w1比 t a s k 2 task_2 task2的权重 w 2 w_2 w2大,简单描述如下: l o s s = w 1 ∗ l o s s t a s k 1 + w 2 ∗ l o s s t a s k 2 ( 其 中 w 1 > w 2 , e g : w 1 = 0.6 , w 2 = 0.4 ) loss =w_1* loss_{task_1}+w_2*loss_{task_2}{\ \ \\(其中w_1 > w_2, eg:w_1=0.6, w_2=0.4)} loss=w1∗losstask1+w2∗losstask2 (其中w1>w2,eg:w1=0.6,w2=0.4)
2 让模型自适应学习权重
如果希望soft点方式融合单任务 l o s s loss loss,可以把权重 w 1 w_1 w1, w 2 w_2 w2作为模型参数,让模型在训练过程中,自适应调整各单任务task的loss权重,可以参考该篇论文,Multi-Task Learning Using Uncertainty to Weigh Losses for Scene Geometry and Semantics,在我的任务中,可以work。核心公式如下: L o s s = 1 2 σ 1 2 ∗ l o s s 1 + 1 2 σ 2 2 ∗ l o s s 2 + 2 ∗ l o g σ 1 + 2 ∗ l o g σ 2 Loss = \frac {1}{2\sigma{_1}^2} *loss_1+\frac {1}{2\sigma{_2}^2}*loss_2+2*log\sigma_1+2*log\sigma_2 Loss=2σ121∗loss1+2σ221∗loss2+2∗logσ1+2∗logσ2
思想也很直接,要使得总loss最小,前两项中, σ 1 \sigma_1 σ1和 σ 2 \sigma_2 σ2要尽量大,但过大,会导致前两项为0,模型没法学习了,为了防止此现象,后两项加了约束,使得 σ 1 \sigma_1 σ1和 σ 2 \sigma_2 σ2要尽量小。从公式来看,如果某个单任务的loss比较大,则对应的 σ \sigma σ也会变大,整体的一个权重就变小,平滑了单任务之间loss量级差距较大情况。其中 σ 1 \sigma_1 σ1和 σ 2 \sigma_2 σ2是模型需要学习的参数,代码大概形式如下:
# 定义变量
sigma1 = tf.get_variable("sigma1", shape=[1], initializer=tf.initializers.random_uniform(minval=0.2, maxval=1))
sigma2 = tf.get_variable("sigma2", shape=[1], initializer=tf.initializers.random_uniform(minval=0.2, maxval=1))
...
#comput task loss
loss_1 = ...
loss_2 = ...
# 融合loss
weight_1 = tf.div(1.0, 2*sigma1*sigma1)
loss_new1 = tf.add(tf.multiply(weight_1, loss_1),2*tf.log(sigma1))
weight_2 = tf.div(1.0, 2*sigma2*sigma2)
loss_new2 = tf.add(tf.multiply(weight_2, loss_2),2*tf.log(sigma2))
# 总loss
loss_all = tf.reduce_sum(loss_new1 + loss_new2)
3 基于focal loss思想融合multi-task loss
focal loss思想(可参考上篇博文:loss在实际的应用设计)主要会根据每次预测结果调整loss权重,因为产生loss权重是指数函数,所以会大大增加预测不好的样本的权重,让模型更focus on hard sample。所以,如果对每个task使用focal loss,则task的loss融合,模型在学习过程中,一定程度上也会偏向预测不好的task任务。
在我的实际应用中,验证work。还做了一个,在使用tripe loss 的multi-task任务中使用focal loss思想融合两个task的loss,效果比简单的task任务相加base版本会好些。思路就是,分别计算anchor与正样本和负样本 loss权重,然后两部分权重相加作为单个task的triple loss的权重。
代码实现如下:
# task1 triple loss, 其中cos_ach_pos标识,代表anchor与正label的cos相似度
# cos_ach_neg标识,代表achor与负label的cos相似度, margin为triple loss根据实际应用设置的一个正负样本之间差距阈值
loss_1 = cos_ach_neg1 - cos_ach_pos1 + magin
loss_2 = cos_ach_neg2 - cos_ach_pos2 + magin
# focal loss 权重计算,两部分,正label和负label
#task 1
weight_pos_1 = tf.pow(1-cos_ach_pos1, 2)
weight_neg_1 = tf.pow(tf.maximum(cos_ach_neg1,0), 2)
#正label weight与负label weight相加
weight_1 = weight_pos_1 + weight_neg_1
#task 2
weight_pos_2 = tf.pow(1-cos_ach_pos2, 2)
weight_neg_2 = tf.pow(tf.maximum(cos_ach_neg2,0), 2)
weight_2 = weight_pos_2 + weight_neg_2
# 融合multi-task loss
loss_1 = tf.maximum(loss_1, 0)
loss_2 = tf.maximum(loss_2, 0)
loss = loss_1 * weight_1 + loss_2 * weight_2