多任务学习与深度学习

作者:chen_h
微信号 & QQ:862251340
微信公众号:coderpai


多任务学习是机器学习的一个子领域,学习的目标是同事执行多个相关任务。比如,系统会同时执行学习两项任务,以便这两项任务都有助于别的任务的学习。这是模仿人类智能的一种方式,即人类如何同时执行多项任务。例如,当你看到一条狗的时候,你可以立即区分出这是一只狗,而不是别的动物。如果你知道狗的品种,你也可以马上说出这只狗的品种来。我们再来换一个例子,假设我们看到一辆车子,我们可以马上识别出这是一辆汽车,三轮车,还是一辆自行车,并且识别出这辆车的牌子是什么,而这些识别都不用借助外在的事物。而这就是多任务学习。我们平时写的模型都是只能去识别一类事物,比如只能识别出这是一辆车,但是模型并不能告诉你这辆车是什么牌子的。在多任务中,我们可以同时去学习识别当前事物是不是一辆车,还能同时识别这辆车是什么牌子的。

这一切识别都是发生在我们复杂的大脑中,数十亿个神经元相互连在一起,互相作用并一起激活,从而执行这种非常复杂的分类和识别任务。很早以前,脑科学研究人员一直试图通过模拟这种方式,在计算机视觉方面取得成功,指导最近深度学习的兴起,才有了一点点的突破。随着神经网络研究工作的进步和在单任务上面取得的卓越成果,使得研究人员也开始大力在多任务上面进行研究和学习。因为多任务学习是一个非常有泛化性质的工作,比如我们想同时来预测股市的波动和舆情政策的关系,那么多任务学习就会更有帮助,因为它有助于跨任务的资源和参数共享,并且还可以减少单独训练两个模型的训练时间。

在这篇文章中,我们将分享如何执行 “深度学习中任务学习的步骤”。我们在这里将使用 TensorFlow 的 Slim API 执行这个任务。如果你不知道 TensorFlow ,那么你可以去网上先简单的了解以下这个框架的使用方法。

执行多任务学习的步骤

  1. 创建数据集
  2. 创建网络架构
  3. 定义多任务的损失函数
  4. 训练模型

问题描述

为了来更清楚的说明这个多任务学习,让我们先来假设一个简单的问题陈述。假设你想要预测一朵花的类型(玫瑰或者雏菊)以及它的颜色(红色,粉色或者白色)。这个问题完全符合多任务学习的问题,因为我们想要使用同一个网络模型来同时执行两件事情(当然,如果你想要让问题更加复杂,你还可以去预测花的形状之类的问题)。当我们训练好这个模型的时候,我们就可以得到两个类别,一个是花的类型(灭鬼或者雏菊)和它的颜色(红色,粉色或者白色)。

接下来,让我们开始吧!

1)创建数据集

对于任何的训练任务,首先的任务都是去创建一个数据集。我们需要数据来训练我们的神经网络。由于这是一个有监督的学习任务,我们的数据还会包含每张图片的正确标签。

对于每一张图片,我们需要具有两个任务的标签,花的类型和花的颜色。我们从谷歌图片上面下载了很多的图片数据,花的类型是玫瑰和雏菊,花的颜色是红色,粉色和白色。最后,我有三种不同颜色的玫瑰和雏菊的 3200 张图片数据。

在这一步骤之后,我们需要将这个数据集分成训练集,验证集和测试集。我们选择将 60% 的图像作为我们的训练集合,25% 的图像作为我们的验证集,最后的 15% 的图像作为我们的测试集。我们可以创建三个独立的文件来存储这三个信息(图像的地址,花的类型标签,花的颜色标签)。比如,在花的类型中,我们设置玫瑰的标签是 0,设置雏菊的标签是 1,在花的颜色类型中,我们设置红色的标签是 0,设置粉色的标签是 1,设置白色的标签是 2。

所以最后我们的数据文件 train.txt,val.txt 和 test.txt 看起来就是下面这个样子:

/data/img1.jpg, 0, 1
/data/img2.jpg, 1, 2
/data/img3.jpg, 1, 2
/data/img4.jpg, 0, 0
/data/img5.jpg, 0, 1
/data/img6.jpg, 1, 1
.
.
.

一旦我们有了这些数据文件,我们就可以转向下一步骤了,来创建我们的网络架构。

2)创建网络架构

在定义网络结构之前,必须能够将其可视化,我们的网络看起来就是如下的网络结构:

在上图中我们有隐藏层节点,我们称之为 “共享层”,因为这些层的权重对于这两个任务都是通用的。然后,我们有每个任务特定的层,称之为 “任务特定层” ,这些层之间的参数是独立计算的,不参与跨层共享。在这些特定任务层中,网络学习特定于任务的信息。每个这些独立的任务层都会生成一个单独的输出结果。具体在我们的例子中,就是来预测花的类别是玫瑰还是雏菊,花的颜色是红色,粉色还是白色。

(...previous network)
net = fire_module(net, 48, 192, scope='fire7')
net = fire_module(net, 64, 256, scope='fire8')
net = slim.max_pool2d(net, [3, 3], stride=2, scope='maxpool8')
## splitting network here
## ------------------ Network 1 - flower classification ------------
type_net = fire_module(net, 64, 256, scope='type_fire9')
type_net = slim.conv2d(type_net, 2, [1, 1], stride=1, padding=”VALID”, scope='type_conv10')
type_net = slim.avg_pool2d(type_net, [10, 10], padding="VALID", scope='type_avgpool10')
flower_type = tf.squeeze(type_net, [1, 2], name='flower_type')
## ------------------ Network 2 - color classification -------------
color_net = fire_module(net, 64, 256, scope='color_fire9')
color_net = slim.conv2d(color_net, 3, [1, 1], stride=1, padding=”VALID”, scope='color_conv10')
color_net = slim.avg_pool2d(color_net, [10, 10], scope='color_avgpool10')
flower_color = tf.squeeze(color_type, [1, 2], name='color_type')

创建多任务学习的网络是非常容易的。我们在这里使用 Squeezenet 来作为基础模型,为什么选用这个模型,是因为我已经熟悉它的架构和网络参数调试,并且非常熟悉它的性能。所以在这个模型之上,来构建我们的多任务模型是非常容易的一件事了。

3)定义多任务损失函数

在实际开始训练我们的网络之前,我们需要定义每个任务的损失函数。我们每个任务的损失函数看起来如下:

flower_type_loss = slim.losses.softmax_cross_entropy(predicted_flower_type, original_flower_type)   
flower_color_loss = slim.losses.softmax_cross_entropy(predicted_flower_color, original_flower_color)

现在,在分别为这些任务定义损失函数之后,我们需要对此进行优化以训练我们的神经网络。有两种方法可以做到这一点:

  1. 定义这两个任务的损失函数,并且分别进行优化。
  2. 定义这两个任务的损失函数,并且共同优化它们。

第一种方法适用于想要分批进行训练的数据,我们可以对任务一的数据和任务二的数据进行交替训练,然后交替优化训练器。

第二种方法是,如果你想要同事进行学习,那么联合学习是更加适合的。你只需要添加损失函数来联合优化这种损失。这保留了各种单独任务在同一时间进行训练的功能。

因为我想对不同任务进行同时训练,所以我采用了第二种方法。

total_loss = flower_type_loss + flower_color_loss
train = optimizer.minimize(total_loss)

现在,我们不是分别优化两个损失函数,而是优化单个联合损失函数。我们定义了我们的优化器函数,它负责最小化我们的联合损失值 total_loss

4)训练

一旦我们定义了我们的网络结构,那么就到了我们去训练它的时候。请记住,我们已经对我们的数据创建了三个数据文件,train.txxval.txttest.txt。因此,我们的第一个任务就是将这些数据文件读入到我们的训练模型之中,一旦读入成功之后,我们就可以开始训练我们的多任务模型了。

总结

在这篇博客稳重中,我们通过使用非常简单的问题进行了深度学习的多任务学习。它可以推广到更复杂的问题中去,比如识别面部表情和确定人脸属性。当你想要使用同一网络执行类似任务时,那么多任务学习是非常有用的。与使用良好总不同模型进行预测相比,这不仅减少了训练时间,还同事减少了需要推理的工作量。这类模型的最佳应用场景是在移动设备中,我们需要对其进行优化,以获得运行时内存,电池利用率和 CPU 利用率。


来源:Medium

猜你喜欢

转载自blog.csdn.net/CoderPai/article/details/80080455