Zohar Komarovsky作者Tianci LIU 路编译

如何利用深度学习模型实现多任务学习?这里有三点经验

Taboola 算法开发者 Zohar Komarovsky 介绍了他们在利用深度学习模型实现多任务学习(MTL)时遇到的几个典型问题及解决方案。

在过去的一年里,我和我的团队一直致力于为 Taboola feed 提供个性化用户体验。我们运用多任务学习(Multi-Task Learning,MTL),在相同的输入特征集上预测多个关键性能指标(Key Performance Indicator,KPI),然后使用 TensorFlow 实现深度学习模型。回想最初的时候,我们感觉(上手)MTL 比现在要困难很多,所以我希望在这里分享一些经验总结。

现在已经有很多关于利用深度学习模型实现 MTL 的文章。在本文中,我准备分享一些利用神经网络实现 MTL 时需要考虑的具体问题,同时也会展示一些基于 TensorFlow 的简单解决方案。

共享即关怀

我们准备从参数硬共享(hard parameter sharing)的基础方法开始。硬共享表示我们使用一个共享的子网络,下接各个任务特定的子网络。

TensorFlow 中,实现这样一个模型的简单方法是使用带有 multi_head 的 Estimator。这个模型和其他神经网络架构相比没什么不同,所以你可以自己想想,有哪些可能出错的地方?

第一点:整合损失

我们的 MTL 模型所遇到的第一个挑战是为多个任务定义一个损失函数。既然每个任务都有一个定义良好的损失函数,那么多任务就会有多个损失。

我们尝试的第一个方法是将不同损失简单相加。很快我们就发现,虽然某一个任务会收敛得到不错的结果,其他的却表现很差。进一步研究后,可以很容易地明白原因:不同任务损失的尺度差异非常大,导致整体损失被某一个任务所主导,最终导致其他任务的损失无法影响网络共享层的学习过程。

一个简单的解决方案是,将损失简单相加替换为加权和,以使所有任务损失的尺度接近。但是,这引入了另一个可能需要不时进行调节的超参数

幸运的是,我们发现了一篇非常棒的论文《Multi-Task Learning Using Uncertainty to Weigh Losses for Scene Geometry and Semantics》,提出引入不确定性来确定 MTL 中损失的权重:在每个任务的损失函数中学习另一个噪声参数(noise parameter)。此方法可以接受多任务(可以是回归和分类),并统一所有损失的尺度。这样,我们就能像一开始那样,直接相加得到总损失了。

与损失加权求和相比,该方法不仅得到了更好的结果,而且还可以不再理会额外的权重参数。论文作者提供的 Keras 实现参见:https://github.com/yaringal/multi-task-learning-example/blob/master/multi-task-learning-example.ipynb

第二点:调节学习速率

调节神经网络有一个通用约定:学习速率是最重要的超参数之一。所以我们尝试调节学习速率。我们发现,对于某一个任务 A 而言,存在一个特别适合的学习速率,而对于另一个任务 B,则有不同的适合学习速率。如果选择较高的学习速率,可能在某个任务上出现神经元死亡(由于大的负梯度,导致 Relu 函数永久关闭,即 dying ReLU),而使用较低的学习速率,则会导致其他任务收敛缓慢。应该怎么做呢?我们可以在各个「头部」(见上图,即各任务的子网络)分别调节各自的学习速率,而在共享网络部分,使用另一个学习速率。

虽然听上去很复杂,但其实非常简单。通常,在利用 TensorFlow 训练神经网络时,使用的是:

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

AdamOptimizer 定义如何应用梯度,而 minimize 则完成具体的计算和应用。我们可以将 minimize 替换为我们自己的实现方案,在应用梯度时,为计算图中的各变量使用各自适合的学习速率。

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)    

友情提醒:这个技巧其实在单任务网络中也很实用。

第三点:将估计作为特征

当我们完成了第一阶段的工作,为预测多任务创建好神经网络后,我们可能希望将某一个任务得到的估计(estimate)作为另一个任务的特征。在前向传递(forward-pass)中,这非常简单。估计是一个张量,可以像任意一个神经层的输出一样进行传递。但在反向传播中呢?

假设将任务 A 的估计作为特征输入给 B,我们可能并不希望将梯度从任务 B 传回任务 A,因为我们已经有了任务 A 的标签。对此不用担心,TensorFlow 的 API 所提供的 tf.stop_gradient 会有所帮助。在计算梯度时,它允许你传入一个希望作为常数的张量列表,这正是我们所需要的。

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

和上面一样,这个方法对 MTL 网络很有用,但不止如此。该技术可用在任何你希望利用 TensorFlow 计算某个值并将其作为常数的场景。例如,在训练生成对抗网络(Generative Adversarial Network,GAN)时,你不希望将对抗示例反向传播到生成过程中。

下一步

我们的模型已经上线运行,Taboola feed 也已经是个性化的了。但是,还有很多可以提升改进的空间,以及许多有趣的结构可以探索。在我们的应用场景中,预测多任务也意味着基于多个 KPI 完成决策。这可能比基于单个 KPI 的更复杂……不过这就是另一个全新的问题了。


原文链接:https://engineering.taboola.com/deep-multi-task-learning-3-lessons-learned/

工程多任务学习
6
相关数据
深度学习技术

深度学习(deep learning)是机器学习的分支,是一种试图使用包含复杂结构或由多重非线性变换构成的多个处理层对数据进行高层抽象的算法。 深度学习是机器学习中一种基于对数据进行表征学习的算法,至今已有数种深度学习框架,如卷积神经网络和深度置信网络和递归神经网络等已被应用在计算机视觉、语音识别、自然语言处理、音频识别与生物信息学等领域并获取了极好的效果。

权重技术

线性模型中特征的系数,或深度网络中的边。训练线性模型的目标是确定每个特征的理想权重。如果权重为 0,则相应的特征对模型来说没有任何贡献。

参数技术

在数学和统计学裡,参数(英语:parameter)是使用通用变量来建立函数和变量之间关系(当这种关系很难用方程来阐述时)的一个数量。

收敛技术

在数学,计算机科学和逻辑学中,收敛指的是不同的变换序列在有限的时间内达到一个结论(变换终止),并且得出的结论是独立于达到它的路径(他们是融合的)。 通俗来说,收敛通常是指在训练期间达到的一种状态,即经过一定次数的迭代之后,训练损失和验证损失在每次迭代中的变化都非常小或根本没有变化。也就是说,如果采用当前数据进行额外的训练将无法改进模型,模型即达到收敛状态。在深度学习中,损失值有时会在最终下降之前的多次迭代中保持不变或几乎保持不变,暂时形成收敛的假象。

损失函数技术

在数学优化,统计学,计量经济学,决策理论,机器学习和计算神经科学等领域,损失函数或成本函数是将一或多个变量的一个事件或值映射为可以直观地表示某种与之相关“成本”的实数的函数。

超参数技术

在机器学习中,超参数是在学习过程开始之前设置其值的参数。 相反,其他参数的值是通过训练得出的。 不同的模型训练算法需要不同的超参数,一些简单的算法(如普通最小二乘回归)不需要。 给定这些超参数,训练算法从数据中学习参数。相同种类的机器学习模型可能需要不同的超参数来适应不同的数据模式,并且必须对其进行调整以便模型能够最优地解决机器学习问题。 在实际应用中一般需要对超参数进行优化,以找到一个超参数元组(tuple),由这些超参数元组形成一个最优化模型,该模型可以将在给定的独立数据上预定义的损失函数最小化。

TensorFlow技术

TensorFlow是一个开源软件库,用于各种感知和语言理解任务的机器学习。目前被50个团队用于研究和生产许多Google商业产品,如语音识别、Gmail、Google 相册和搜索,其中许多产品曾使用过其前任软件DistBelief。

张量技术

张量是一个可用来表示在一些矢量、标量和其他张量之间的线性关系的多线性函数,这些线性关系的基本例子有内积、外积、线性映射以及笛卡儿积。其坐标在 维空间内,有 个分量的一种量,其中每个分量都是坐标的函数,而在坐标变换时,这些分量也依照某些规则作线性变换。称为该张量的秩或阶(与矩阵的秩和阶均无关系)。 在数学里,张量是一种几何实体,或者说广义上的“数量”。张量概念包括标量、矢量和线性算子。张量可以用坐标系统来表达,记作标量的数组,但它是定义为“不依赖于参照系的选择的”。张量在物理和工程学中很重要。例如在扩散张量成像中,表达器官对于水的在各个方向的微分透性的张量可以用来产生大脑的扫描图。工程上最重要的例子可能就是应力张量和应变张量了,它们都是二阶张量,对于一般线性材料他们之间的关系由一个四阶弹性张量来决定。

神经网络技术

(人工)神经网络是一种起源于 20 世纪 50 年代的监督式机器学习模型,那时候研究者构想了「感知器(perceptron)」的想法。这一领域的研究者通常被称为「联结主义者(Connectionist)」,因为这种模型模拟了人脑的功能。神经网络模型通常是通过反向传播算法应用梯度下降训练的。目前神经网络有两大主要类型,它们都是前馈神经网络:卷积神经网络(CNN)和循环神经网络(RNN),其中 RNN 又包含长短期记忆(LSTM)、门控循环单元(GRU)等等。深度学习是一种主要应用于神经网络帮助其取得更好结果的技术。尽管神经网络主要用于监督学习,但也有一些为无监督学习设计的变体,比如自动编码器和生成对抗网络(GAN)。

神经元技术

(人工)神经元是一个类比于生物神经元的数学计算模型,是神经网络的基本组成单元。 对于生物神经网络,每个神经元与其他神经元相连,当它“兴奋”时会向相连的神经元发送化学物质,从而改变这些神经元的电位;神经元的“兴奋”由其电位决定,当它的电位超过一个“阈值”(threshold)便会被激活,亦即“兴奋”。 目前最常见的神经元模型是基于1943年 Warren McCulloch 和 Walter Pitts提出的“M-P 神经元模型”。 在这个模型中,神经元通过带权重的连接接处理来自n个其他神经元的输入信号,其总输入值将与神经元的阈值进行比较,最后通过“激活函数”(activation function)产生神经元的输出。

生成对抗网络技术

生成对抗网络是一种无监督学习方法,是一种通过用对抗网络来训练生成模型的架构。它由两个网络组成:用来拟合数据分布的生成网络G,和用来判断输入是否“真实”的判别网络D。在训练过程中,生成网络-G通过接受一个随机的噪声来尽量模仿训练集中的真实图片去“欺骗”D,而D则尽可能的分辨真实数据和生成网络的输出,从而形成两个网络的博弈过程。理想的情况下,博弈的结果会得到一个可以“以假乱真”的生成模型。

多任务学习技术

推荐文章
暂无评论
暂无评论~