0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

如何利用我们拥有的共享资源来高效快速地训练深度学习模型

Tensorflowers 来源:未知 作者:李倩 2018-10-04 14:51 次阅读

与大多数 AI 研究部门一样,Zalando Research 也意识到了对创意进行尝试和快速原型设计的重要性。随着数据集变得越来越庞大,了解如何利用我们拥有的共享资源来高效快速地训练深度学习模型变得大有用处。

TensorFlow 的估算器API 对于在分布式环境中使用多个 GPU 来训练模型非常有用。本文将主要介绍这一工作流程。我们先使用 Fashion-MNIST 小数据集训练一个用tf.keras编写的自定义估算器,然后在文末介绍一个较实际的用例。

请注意:TensorFlow 团队一直在开发另一项很酷的新功能(在我写这篇文章时,该功能仍处于 Master 阶段),使用这项新功能,您只需多输入几行代码即可训练tf.keras模型,而无需先将该模型转化为估算器!其工作流程也很赞。下面我着重讲讲估算器 API。选择哪一个由您自己决定!

注:功能链接

https://github.com/tensorflow/tensorflow/blob/master/tensorflow/contrib/distribute/python/examples/keras_mnist.py#L106-L114

TL; DR:基本上,我们需要记住,对于 tf.keras. 模型,我们只要通过 tf.keras.estimator.model_to_estimator 方法将其转化为 tf.estimator.Estimator 对象,即可使用 tf.estimator API 来进行训练。转化完成后,我们可以使用估算器提供的机制用不同的硬件配置训练模型。

您可以从此笔记本下载本文中的代码并亲自运行。

注:笔记本链接

https://github.com/kashif/tf-keras-tutorial/blob/master/7-estimators-multi-gpus.ipynb

import osimport time

#!pip install -q -U tensorflow-gpuimport tensorflow as tf

import numpy as np

导入 Fashion-MNIST 数据集

我们用Fashion-MNIST数据集随手替换一下 MNIST,这里面包含几千张Zalando时尚文章的灰度图像。获取训练和测试数据非常简单,如下所示:

(train_images, train_labels), (test_images, test_labels) = tf.keras.datasets.fashion_mnist.load_data()

我们想把这些图像的像素值从 0 到 255 之间的一个数字转换为 0 到 1 之间的一个数字,并将该数据集转换为 [B, H, W ,C] 格式,其中 B 代表批处理的图像数,H 和 W 分别是高度和宽度,C 是我们数据集的通道数(灰度为 1):

TRAINING_SIZE = len(train_images)TEST_SIZE = len(test_images)

train_images = np.asarray(train_images, dtype=np.float32) / 255# Convert the train images and add channelstrain_images = train_images.reshape((TRAINING_SIZE, 28, 28, 1))

test_images = np.asarray(test_images, dtype=np.float32) / 255# Convert the test images and add channelstest_images = test_images.reshape((TEST_SIZE, 28, 28, 1))

接下来,我们想将标签从整数编号(例如,2 或套衫)转换为独热编码(例如,0,0,1,0,0,0,0,0,0,0)。为此,我们要使用 tf.keras.utils.to_categorical函数:

# How many categories we are predicting from (0-9)LABEL_DIMENSIONS = 10

train_labels = tf.keras.utils.to_categorical(train_labels, LABEL_DIMENSIONS)

test_labels = tf.keras.utils.to_categorical(test_labels, LABEL_DIMENSIONS)

# Cast the labels to floats, needed latertrain_labels = train_labels.astype(np.float32)test_labels = test_labels.astype(np.float32)

构建 tf.keras 模型

我们会使用Keras 功能 API来创建神经网络。Keras 是一个高级 API,可用于构建和训练深度学习模型,其采用模块化设计,使用方便,易于扩展。tf.keras 是 TensorFlow 对这个 API 的实现,其支持Eager Execution、tf.data 管道和估算器等。

在架构方面,我们会使用 ConvNet。一个非常笼统的说法是,ConvNet 是卷积层 (Conv2D) 和池化层 (MaxPooling2D) 的堆栈。但最重要的是,ConvNet 将每个训练示例当作一个 3D 形状张量(高度、宽度、通道),对于灰度图像,张量从通道 = 1 开始,然后返回一个 3D 张量。

因此,在 ConvNet 部分之后,我们需要将张量平面化,并添加密集层,其中最后一个返回 LABEL_DIMENSIONS 大小的向量,并附带 tf.nn.softmax 激活:

inputs = tf.keras.Input(shape=(28,28,1)) # Returns a placeholder

x = tf.keras.layers.Conv2D(filters=32, kernel_size=(3, 3), activation=tf.nn.relu)(inputs)

x = tf.keras.layers.MaxPooling2D(pool_size=(2, 2), strides=2)(x)

x = tf.keras.layers.Conv2D(filters=64, kernel_size=(3, 3), activation=tf.nn.relu)(x)

x = tf.keras.layers.MaxPooling2D(pool_size=(2, 2), strides=2)(x)

x = tf.keras.layers.Conv2D(filters=64, kernel_size=(3, 3), activation=tf.nn.relu)(x)

x = tf.keras.layers.Flatten()(x)

x = tf.keras.layers.Dense(64, activation=tf.nn.relu)(x)predictions = tf.keras.layers.Dense(LABEL_DIMENSIONS, activation=tf.nn.softmax)(x)

现在,我们可以定义学习模型,请选择优化器(我们从 TensorFlow 中选择一个,而不使用来自 tf.keras. optimizers 的优化器)并进行编译:

model = tf.keras.Model(inputs=inputs, outputs=predictions)

optimizer = tf.train.AdamOptimizer(learning_rate=0.001)

model.compile(loss='categorical_crossentropy', optimizer=optimizer, metrics=['accuracy'])

创建估算器

使用已编译的 Keras 模型创建估算器,也就是我们所说的 model_to_estimator 方法。请注意,Keras 模型的初始模型状态保存在创建的估算器中。

那估算器有哪些优点呢?首先要提以下几点:

您可以在本地主机或分布式多 GPU 环境中运行基于估算器的模型,而无需更改您的模型;

估算器能够简化模型开发者之间的共享实现;

估算器能够为您构建图形,所以有点像 Eager Execution,没有明确的会话。

那么我们要如何训练简单的 tf.keras 模型来使用多 GPU?我们可以使用 tf.contrib.distribute.MirroredStrategy 范式,通过同步训练进行图形内复制。如需了解更多关于此策略的信息,请观看分布式 TensorFlow 训练讲座。

注:分布式 TensorFlow 链接

https://www.youtube.com/watch?v=bRMGoPqsn20

基本上,每个工作器 GPU 都有一个网络拷贝,并会获取一个数据子集,据以计算本地梯度,然后等待所有工作器以同步方式结束。然后,工作器通过 Ring All-reduce 运算互相传递其本地梯度,这通常要进行优化,以减少网络带宽并增加吞吐量。在所有梯度到达后,每个工作器会计算其平均值并更新参数,然后开始下一步。理想情况下,您在单个节点上有多个高速互联的 GPU。

要使用此策略,我们首先要用已编译的 tf.keras 模型创建一个估算器,然后通过 RunConfig config 赋予其 MirroredStrategy 配置。默认情况下,该配置会使用全部 GPU,但您也可以赋予其一个 num_gpus 选项,以使用特定数量的 GPU:

NUM_GPUS = 2

strategy = tf.contrib.distribute.MirroredStrategy(num_gpus=NUM_GPUS)config = tf.estimator.RunConfig(train_distribute=strategy)

estimator = tf.keras.estimator.model_to_estimator(model, config=config)

创建估算器输入函数

要通过管道将数据传递到估算器,我们需要定义一个数据导入函数,该函数返回批量数据的 tf.data 数据集(图像、标签)。下面的函数接收 numpy 数组,并通过ETL过程返回数据集。

请注意,最后我们还调用了预读取方法,该方法会在训练时将数据缓冲到 GPU,以便下一批数据准备就绪并等待 GPU,而不是在每次迭代时让 GPU 等待数据。GPU 可能仍然没有得到充分利用,要改善这一点,我们可以使用融合版转换运算(如 shuffle_and_repeat),而不是两个单独的运算。不过,我在这里选用的是简单用例。

def input_fn(images, labels, epochs, batch_size):

# Convert the inputs to a Dataset. (E)ds = tf.data.Dataset.from_tensor_slices((images, labels))

# Shuffle, repeat, and batch the examples. (T)SHUFFLE_SIZE = 5000ds = ds.shuffle(SHUFFLE_SIZE).repeat(epochs).batch(batch_size)ds = ds.prefetch(2)

# Return the dataset. (L)return ds

训练估算器

首先,我们定义一个 SessionRunHook 类,用于记录随机梯度下降法每次迭代的次数:

class TimeHistory(tf.train.SessionRunHook):def begin(self): self.times = []

def before_run(self, run_context): self.iter_time_start = time.time()

def after_run(self, run_context, run_values): self.times.append(time.time() - self.iter_time_start)

亮点在这里!我们可以对估算器调用 train 函数,并通过 hooks 参数,向其赋予我们定义的 input_fn (包含批次大小和我们希望的训练回合次数)和 TimeHistory 实例:

time_hist = TimeHistory()

BATCH_SIZE = 512EPOCHS = 5

estimator.train(lambda:input_fn(train_images, train_labels, epochs=EPOCHS, batch_size=BATCH_SIZE), hooks=[time_hist])

性能

现在,我们可以使用时间钩子来计算训练的总时间和平均每秒训练的图像数量(平均吞吐量):

total_time = sum(time_hist.times)print(f"total time with {NUM_GPUS} GPU(s): {total_time} seconds")

avg_time_per_batch = np.mean(time_hist.times)print(f"{BATCH_SIZE*NUM_GPUS/avg_time_per_batch} images/second with {NUM_GPUS} GPU(s)")

使用两块 K80 GPU 进行训练时的 Fashion-MNIST 训练吞吐量和总时间,采用不同 NUM_GPUS,显示缩放不良

评估估算器

为了检验模型的性能,我们要对估算器调用评估方法:

estimator.evaluate(lambda:input_fn(test_images, test_labels, epochs=1, batch_size=BATCH_SIZE))

视网膜 OCT (光学相干断层成像术)图像示例

为了测试模型在处理较大数据集时的扩展性能,我们使用视网膜 OCT 图像数据集,这是Kaggle众多大型数据集中的一个。该数据集由活人视网膜的横截面 X 光图像组成,分为四个类别:NORMAL、CNV、DME和DRUSEN:

光学相干断层成像术的代表图像,选自 Kermany 等人所著的《通过基于图像的深度学习技术确定医学诊断和可治疗疾病》(Identifying Medical Diagnoses and Treatable Diseases by Image-Based Deep Learning)

该数据集共有 84,495 张 JPEG 格式的 X 光图像,尺寸多为 512x496,可以通过 KaggleCLI下载:

注:CLI 链接

https://github.com/Kaggle/kaggle-api

#!pip install kaggle#!kaggle datasets download -d paultimothymooney/kermany2018

下载完成后,训练集和测试集图像类位于各自的文件夹内,因此我们可以将模式定义为:

labels = ['CNV', 'DME', 'DRUSEN', 'NORMAL']

train_folder = os.path.join('OCT2017', 'train', '**', '*.jpeg')test_folder = os.path.join('OCT2017', 'test', '**', '*.jpeg')

接下来,我们要编写估算器的输入函数,该函数可以提取任何文件模式,并返回已缩放图像和独热编码标签作为 tf.data.Dataset。这次,我们遵循输入管道性能指南中的最佳实践。请特别注意,如果 prefetch 的 buffer_size 为 None,则 TensorFlow 会自动使用最优的预读取缓冲区大小:

注:输入管道性能指南链接

https://www.tensorflow.org/performance/datasets_performance

1def input_fn(file_pattern, labels,

2image_size=(224,224),

3shuffle=False,

4batch_size=64,

5num_epochs=None,

6buffer_size=4096,

7prefetch_buffer_size=None):

8

9table = tf.contrib.lookup.index_table_from_tensor(mapping=tf.constant(labels))

10num_classes = len(labels)

11

12def _map_func(filename):

13label = tf.string_split([filename], delimiter=os.sep).values[-2]

14image = tf.image.decode_jpeg(tf.read_file(filename), channels=3)

15image = tf.image.convert_image_dtype(image, dtype=tf.float32)

16image = tf.image.resize_images(image, size=image_size)

17return (image, tf.one_hot(table.lookup(label), num_classes))

18

19dataset = tf.data.Dataset.list_files(file_pattern, shuffle=shuffle)

20

21if num_epochs is not None and shuffle:

22dataset = dataset.apply(

23tf.contrib.data.shuffle_and_repeat(buffer_size, num_epochs))

24elif shuffle:

25dataset = dataset.shuffle(buffer_size)

26elif num_epochs is not None:

27dataset = dataset.repeat(num_epochs)

28

29dataset = dataset.apply(

30tf.contrib.data.map_and_batch(map_func=_map_func,

31 batch_size=batch_size,

32num_parallel_calls=os.cpu_count()))

33dataset = dataset.prefetch(buffer_size=prefetch_buffer_size)

34

35return dataset

这次训练该模型时,我们将使用一个经过预训练的 VGG16,并且只重新训练其最后 5 层:

keras_vgg16 = tf.keras.applications.VGG16(input_shape=(224,224,3), include_top=False)

output = keras_vgg16.outputoutput = tf.keras.layers.Flatten()(output)prediction = tf.keras.layers.Dense(len(labels), activation=tf.nn.softmax)(output)

model = tf.keras.Model(inputs=keras_vgg16.input, outputs=prediction)

for layer in keras_vgg16.layers[:-4]: layer.trainable = False

现在,我们万事皆备,可以按照上述步骤进行,并使用 NUM_GPUS GPU 在几分钟内训练我们的模型:

model.compile(loss='categorical_crossentropy', optimizer=tf.train.AdamOptimizer(), metrics=['accuracy'])

NUM_GPUS = 2

strategy = tf.contrib.distribute.MirroredStrategy(num_gpus=NUM_GPUS)

config = tf.estimator.RunConfig(train_distribute=strategy)

estimator = tf.keras.estimator.model_to_estimator(model, config=config)

BATCH_SIZE = 64

EPOCHS = 1

estimator.train(input_fn=lambda:input_fn(train_folder, labels, shuffle=True, batch_size=BATCH_SIZE, buffer_size=2048, num_epochs=EPOCHS, prefetch_buffer_size=4), hooks=[time_hist])

训练结束后,我们可以评估测试集的准确度,应该在 95% 左右(对初始基线来说还不错):

estimator.evaluate(input_fn=lambda:input_fn(test_folder,

labels, shuffle=False, batch_size=BATCH_SIZE, buffer_size=1024, num_epochs=1))

使用两块 K80 GPU 进行训练时的 Fashion-MNIST 训练吞吐量和总时间,采用不同 NUM_GPUS,显示线性缩放

总结

我们在上文中介绍了如何使用估算器 API 在多个 GPU 上轻松训练 Keras 深度学习模型,如何编写符合最佳实践的输入管道,以充分利用我们的资源(线性缩放),以及如何通过钩子为我们的训练吞吐量计时。

请务必注意,最后我们主要关注的是测试集错误。您可能会注意到,测试集的准确度会随着 NUM_GPUS 值的增加而下降。其中一个原因可能是,使用 BATCH_SIZE*NUM_GPUS 的批量大小时,MirroredStrategy 能够有效地训练模型,而当我们增加 GPU 数量时,可能需要调整 BATCH_SIZE 或学习率。为便于制图,文中除 NUM_GPUS 之外的所有其他超参数均保持不变,但实际上我们需要调整这些超参数。

数据集和模型的大小也会影响这些方案的缩放效果。在读取或写入小数据时,GPU 的带宽较差,如果是较为老旧的 GPU(如 K80),则情形尤其如此,而且可能会造成上面 Fashion-MNIST 图中所示情况。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 深度学习
    +关注

    关注

    73

    文章

    5224

    浏览量

    119865
  • tensorflow
    +关注

    关注

    13

    文章

    313

    浏览量

    60241

原文标题:使用估算器、tf.keras 和 tf.data 进行多 GPU 训练

文章出处:【微信号:tensorflowers,微信公众号:Tensorflowers】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    如何才能高效地进行深度学习模型训练

    分布式深度学习框架中,包括数据/模型切分、本地单机优化算法训练、通信机制、和数据/模型聚合等模块。现有的
    的头像 发表于 07-09 08:48 1.4w次阅读
    如何才能<b class='flag-5'>高效</b>地进行<b class='flag-5'>深度</b><b class='flag-5'>学习</b><b class='flag-5'>模型</b><b class='flag-5'>训练</b>?

    动态分配多任务资源的移动端深度学习框架

    )。如图所示,所有派生模型的 GFLOPs 都比对应原版模型低。这表明我们的滤波器剪枝方法能够有效减少这六个应用的计算成本,且该方法可泛化至在不同数据集上训练的不同
    发表于 10-31 16:32

    ucos怎么完成共享资源

    如果想要访问共享资源,首先创建信号量OSSemCreate ((OS_SEM* )&ADC_SEM, (CPU_CHAR* )"ADC_SEM", (OS_SEM_CTR
    发表于 04-02 06:36

    labview实现深度学习,还在用python?

    算法。其编程特点是上手快,开发效率高,兼容性强,能快速调用c++,c#等平台的dll类库。如何将labview与深度学习结合起来,解决视觉行业越来越复杂的应用场景所遇到的困难。下面以
    发表于 07-23 20:33

    深度学习模型是如何创建的?

    到准备模型,然后再在边缘的嵌入式系统上运行。训练深度学习模型是过程的工作量和时间密集型部分,其中通过提供需要时间和
    发表于 10-27 06:34

    异构组网如何解决共享资源冲突?

    相信大家对HarmonyOS的“超级终端”比较熟悉了。那么,您知道超级终端场景下的多种设备在不同环境下是如何组成一个网络的吗?这些设备之间又是如何解决共享资源冲突的?本期我们就来为您揭晓~一、分布式
    发表于 12-06 18:28

    文件锁-文件共享共享资源竞争问题的解决方法

    文件锁-文件共享共享资源竞争问题的解决方法:文件锁。文件锁包括建议性锁要求每个上锁文件的进程都要检查是否有锁存在,并且尊重 已有的锁强制性锁由内核执行的锁,当一个文件被上锁进行写入操作的时候, 内核
    发表于 12-15 09:08

    TDA4对深度学习的重要性

    进行验证当深度学习遇上TDA4,模型部署变得简单的同时,模型也可以更加高效地运行。让我们开启TD
    发表于 11-03 06:53

    什么是深度学习?使用FPGA进行深度学习的好处?

    什么是深度学习为了解释深度学习,有必要了解神经网络。神经网络是一种模拟人脑的神经元和神经网络的计算模型。作为具体示例,让
    发表于 02-17 16:56

    基于令牌的共享资源分配算法_雷鸣

    基于令牌的共享资源分配算法_雷鸣
    发表于 03-16 10:53 0次下载

    基于双十字链表存储的共享资源矩阵方法特性研究

    存储;其次,针对共享资源矩阵方法建立了概率模型;最后,在该概率模型下,分析了改进算法的时间复杂度和共享资源矩阵方法的特性。理论分析和实验仿真表明:当
    发表于 12-21 11:22 0次下载
    基于双十字链表存储的<b class='flag-5'>共享资源</b>矩阵方法特性研究

    基于预训练模型和长短期记忆网络的深度学习模型

    语义槽填充是对话系统中一项非常重要的任务,旨在为输入句子的毎个单词标注正确的标签,其性能的妤坏极大地影响着后续的对话管理模块。目前,使用深度学习方法解决该任务时,一般利用随机词向量或者预训练
    发表于 04-20 14:29 19次下载
    基于预<b class='flag-5'>训练</b><b class='flag-5'>模型</b>和长短期记忆网络的<b class='flag-5'>深度</b><b class='flag-5'>学习</b><b class='flag-5'>模型</b>

    利用OpenVINO™部署HuggingFace预训练模型的方法与技巧

    作为深度学习领域的 “github”,HuggingFace 已经共享了超过 100,000 个预训练模型
    的头像 发表于 05-19 15:57 589次阅读
    <b class='flag-5'>利用</b>OpenVINO™部署HuggingFace预<b class='flag-5'>训练</b><b class='flag-5'>模型</b>的方法与技巧

    深度学习框架区分训练还是推理吗

    深度学习框架区分训练还是推理吗 深度学习框架是一个非常重要的技术,它们能够加速深度
    的头像 发表于 08-17 16:03 1022次阅读

    深度学习如何训练出好的模型

    和足够的计算资源,还需要根据任务和数据的特点进行合理的超参数调整、数据增强和模型微调。在本文中,我们将会详细介绍深度学习
    的头像 发表于 12-07 12:38 642次阅读
    <b class='flag-5'>深度</b><b class='flag-5'>学习</b>如何<b class='flag-5'>训练</b>出好的<b class='flag-5'>模型</b>