53

多任务深度学习的三个经验教训

 5 years ago
source link: https://www.leiphone.com/news/201902/SlFB1OlWd6U8ubJj.html?amp%3Butm_medium=referral
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.

雷锋网 AI 科技评论按,AZohar Komarovsky,Taboola 算法工程师,致力于研究推荐系统相关的机器学习应用程序。不久前他分享了最近一年关于多任务深度学习的研究经验。雷锋网 (公众号:雷锋网) AI 科技评论编译整理如下:

在过去的一年里,我和我的团队一直致力于提高 Taboola Feed 的个性化用户体验。我们使用多任务学习(MTL)来预测同一组输入特性上的多个关键性能指标(KPIs),并在 TensorFlow 中实现了一个深度学习(DL)模型。但是,在我们开始着手这项研究的时候,MTL 对我们来说比现在复杂得多,所以我想分享一些经验教训。

在本文中,我将分享一些在神经网络(NN)中实现 MTL 时具体需要考虑哪些方面的问题,我还将对这些问题提出简单的 TensorFlow 解决方案。

我们想从硬参数共享(hard parameter sharing)的基本方法开始。硬共享意味着我们有一个共享子网,这个子网是特定于任务的。

nYbYvuM.png!web

在 TensorFlow 中使用这种模型时,由于它看起来与其他 NN 体系结构没有那么大的不同,您可能会觉得自己有哪里做错了。 雷锋网

经验 1-损失合并

我们在 MTL 模型中遇到的第一个挑战是为多个任务定义单个损失函数。虽然单个任务有定义明确的损失函数,但多个任务会带来多个损失。

我们最开始尝试的做法是直接将所有的损失相加。不久我们就发现,当一个任务趋同于好的结果时,其他任务看起来相当糟糕。造成这个现象的原因很简单,因为损失的规模是如此的不同,以至于一个任务主导了整个损失,而其余的任务没有机会影响共享层的学习过程。

一个快速的解决办法是用一个加权和替代损失的直接相加和,使所有的损失对共享层的影响大致相同。然而,这个解决方案涉及另一个超参数,可能需要每隔一段时间调整一次。

幸运的是,我们发现了 一篇很棒的论文 ,论文建议使用不确定性来衡量 MTL 中的损失。具体方法是学习另一个噪声参数,该参数集成在每个任务的损失函数中。这允许 MTL 中有多个任务,并使所有损失达到相同的规模。

通过这种方法,不仅可以得到比加权和更好的结果,而且不需要考虑附加的权重超参数。这篇论文的作者还提供了 一个 keras 实现方法

经验 2-调整学习速率

学习速率是调节神经网络最重要的超参数之一,这是一个常见的规律。所以我们尝试了调优,发现了对不同任务来说最优的调试速率。选择较高的学习率会导致其中一个任务的 dying Relu ,而使用较低的学习率会导致另一个任务的收敛缓慢。那我们该怎么办?我们可以让每个特定于任务的子网调整为单独的学习速率,并将共享子网调整为另一个速率。

虽然这听起来很复杂,但实际上相当简单。通常,在 TensorFlow 中训练神经网络时,您可以使用如下方法:

optimizer = tf.train.AdamOptimizer(learning_rate).minimize(loss) 

AdamOptimizer 定义了应该如何应用渐变,并最小化计算并应用它们。我们可以用自己的实现来代替最小化,该实现将对计算图中的每个变量使用适当的学习速率:

all_variables = shared_vars + a_vars + b_vars
all_gradients = tf.gradients(loss, all_variables)

shared_subnet_gradients = all_gradients[:len(shared_vars)]
a_gradients = all_gradients[len(shared_vars):len(shared_vars + a_vars)]
b_gradients = all_gradients[len(shared_vars + a_vars):]

shared_subnet_optimizer = tf.train.AdamOptimizer(shared_learning_rate)
a_optimizer = tf.train.AdamOptimizer(a_learning_rate)
b_optimizer = tf.train.AdamOptimizer(b_learning_rate)

train_shared_op = shared_subnet_optimizer.apply_gradients(zip(shared_subnet_gradients, shared_vars))
train_a_op = a_optimizer.apply_gradients(zip(a_gradients, a_vars))
train_b_op = b_optimizer.apply_gradients(zip(b_gradients, b_vars))

train_op = tf.group(train_shared_op, train_a_op, train_b_op)

另外,这个技巧实际上也可以应用于单任务网络。

经验 3-使用评估作为特征

一旦我们通过了创建预测多个任务的 NN 的第一个阶段,我们可能会将某个任务的评估作为另一个任务的结果。这个估计是张量,所以我们可以像连接其他层的输出一样连接它。但是在反向传播中会发生什么呢?

假设任务 A 的估计值作为一个特性传递给任务 B。我们可能并不想将梯度从任务 B 传回任务 A,因为我们已经给了任务 A 标签。

别担心,TensorFlow 的 API 有 tf.stop_gradient ,它正是为了解决这个问题而存在的。当计算梯度时,它可以让你传递一个张量列表,你想把它当作常数,这正是我们所需要的。

all_gradients = tf.gradients(loss, all_variables, stop_gradients=stop_tensors) 

同样地,这在 MTL 网络中很有用,但它不仅仅在 MTL 网络中有用。只要您想用 TensorFlow 计算一个值,并且需要假设该值是一个常量,就可以使用此技术。例如,当训练生成对抗网络(GANs)时,您不希望在生成对抗性网络的过程中进行反向传播。

via: https://engineering.taboola.com/deep-multi-task-learning-3-lessons-learned/

雷锋网版权文章,未经授权禁止转载。详情见 转载须知


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK