百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

数据载入过慢这里有一份TensorFlow加速指南

toyiye 2024-06-21 12:38 9 浏览 0 评论

王小新 编译自 Towards Data Science

量子位 出品 | 公众号 QbitAI

机器学习算法烂熟于心,网络结构顺手拈来,但是如果数据集载入时耗费大量时间,那整个训练时间就会大大增加。

这个问题可能困扰着很多使用大型数据集训练的炼丹师们。最近,Francesco Zuppichini在medium上的一篇文章,从使用Dataset函数的三个步骤讲起,介绍了相应的解决方法。

以下内容译自他的文章。

看完这篇文章后,千万不要再用默认的输入函数feed-dict了。

本文以TensorFlow 1.5为标准函数库。根据以往经验,在TensorFlow中,feed-dict函数可能是最慢的一种数据载入方法,尽量少用。把数据输入到模型的最佳方法是使用输入流水线(input pipeline),来确保GPU无须等待新数据输入。

幸好,TensorFlow有一个内置接口,叫做Dataset。这个接口是为了更容易地实现数据输入,在1.3版本已被提出。这份教程将会介绍如何使用它来创建输入流水线,高效率地将数据输入到模型中。

本文还会解释Dataset接口的基本原理,包括最常见的一些用法。

所有代码可从这个网址获取:

https://github.com/FrancescoSaverioZuppichini/Tensorflow-Dataset-Tutorial/blob/master/dataset_tutorial.ipynb

概述

使用Dataset接口,有以下三个步骤:

1. 导入数据,从某些数据创建一个数据集实例;

2. 创建迭代器iterator,即使用已有的数据集来创建一个迭代器实例,对数据集进行迭代;

3. 消耗数据,即使用所创建的迭代器,从数据集中取出元素输入到模型。

导入数据

首先,我们需要把数据导入到数据集中,有以下几种方式。

使用Numpy

这是常用的一个方法,把一个numpy数组输入到tensorflow中:

# create a random vector of shape (100,2)
x = np.random.sample((100,2))
# make a dataset from a numpy array
dataset = tf.data.Dataset.from_tensor_slices(x)

我们也可以输入多个numpy数组。典型示例就是我们将一些数据根据特征和标签分类。

features, labels = (np.random.sample((100,2)), np.random.sample((100,1)))
dataset = tf.data.Dataset.from_tensor_slices((features,labels))

使用Tensors

当然,我们可以用Tensor来初始化数据集:

# using a tensor
dataset = tf.data.Dataset.from_tensor_slices(tf.random_uniform([100, 2]))

使用Placeholder

当我们需要多次修改Dataset函数中的数据时,这个方法是最合适的,稍后会详细介绍。

x = tf.placeholder(tf.float32, shape=[None,2])
dataset = tf.data.Dataset.from_tensor_slices(x)

使用generator

我们也可以使用生成器generator来初始化Dataset,在处理长度不同的元素(如序列)时,这种方法很有用:

sequence = np.array([[1],[2,3],[3,4]])
def generator():
 for el in sequence:
 yield el
dataset = tf.data.Dataset().from_generator(generator,
 output_types=tf.float32,
 output_shapes=[tf.float32])

在这种情况下,你还需要指定输入数据的类型和形状,来得到合适的Tensor。

创建迭代器

上面已经介绍了如何创建一个数据集,但是如何拿出里面的数据呢?这里要使用迭代器Iterator,来遍历整个数据集并取出数据的实际值,有以下四种类型。

One shot迭代器

这是最简单的一种迭代器,利用上节的示例一:

x = np.random.sample((100,2))
# make a dataset from a numpy array
dataset = tf.data.Dataset.from_tensor_slices(x)
# create the iterator
iter = dataset.make_one_shot_iterator()

接着,再调用get_next()函数来获取下一个数据张量:

...
# create the iterator
iter = dataset.make_one_shot_iterator()
el = iter.get_next()

然后,运行el函数来得到输出值:

with tf.Session() as sess:
 print(sess.run(el)) # output: [ 0.42116176 0.40666069]

可初始化迭代器

如果要构建一个动态数据集,在运行时要更改其中的源数据,则应该选择占位符placeholder来创建数据集,然后使用经典的feed-dict方法来初始化占位符。这些可以用一个可初始化迭代器来完成,利用上节“使用Placeholder”部分的示例:

# using a placeholder
x = tf.placeholder(tf.float32, shape=[None,2])
dataset = tf.data.Dataset.from_tensor_slices(x)
data = np.random.sample((100,2))
iter = dataset.make_initializable_iterator() # create the iterator
el = iter.get_next()
with tf.Session() as sess:
 # feed the placeholder with data
 sess.run(iter.initializer, feed_dict={ x: data })
 print(sess.run(el)) # output [ 0.52374458 0.71968478]

这里调用了make_initializable_iterator函数。在这个sess范围内,运行initializer函数来传递数据,这里先以随机数组为例。

到这里,我们已经构建好训练集和测试集:

train_data = (np.random.sample((100,2)), np.random.sample((100,1)))
test_data = (np.array([[1,2]]), np.array([[0]]))

接下来,读入数据来训练模型,并在测试数据集上进行评估,这可通过训练后再次初始化迭代器来完成:

# initializable iterator to switch between dataset
EPOCHS = 10
x, y = tf.placeholder(tf.float32, shape=[None,2]), tf.placeholder(tf.float32, shape=[None,1])
dataset = tf.data.Dataset.from_tensor_slices((x, y))
train_data = (np.random.sample((100,2)), np.random.sample((100,1)))
test_data = (np.array([[1,2]]), np.array([[0]]))
iter = dataset.make_initializable_iterator()
features, labels = iter.get_next()
with tf.Session() as sess:
# initialise iterator with train data
 sess.run(iter.initializer, feed_dict={ x: train_data[0], y: train_data[1]})
 for _ in range(EPOCHS):
 sess.run([features, labels])
# switch to test data
 sess.run(iter.initializer, feed_dict={ x: test_data[0], y: test_data[1]})
 print(sess.run([features, labels])

可重初始化迭代器

这个概念与上个类似,要在数据之间进行动态切换。但是,上面是将新数据输入到同一个数据集中,这里是在数据集之间切换。同样地,我们要构建一个训练数据集和一个测试数据集:

# making fake data using numpy
train_data = (np.random.sample((100,2)), np.random.sample((100,1)))
test_data = (np.random.sample((10,2)), np.random.sample((10,1)))

我们可以创建两个数据集:

# create two datasets, one for training and one for test
train_dataset = tf.data.Dataset.from_tensor_slices(train_data)
test_dataset = tf.data.Dataset.from_tensor_slices(test_data)

这里是关键,要构建一个通用型Iterator:

# create a iterator of the correct shape and type
iter = tf.data.Iterator.from_structure(train_dataset.output_types,
 train_dataset.output_shapes)

然后初始化数据集:

# create the initialisation operations
train_init_op = iter.make_initializer(train_dataset)
test_init_op = iter.make_initializer(test_dataset)

跟上面操作一样,得到下个元素:

features, labels = iter.get_next()

现在,可以使用构建的Session来直接运行这两个初始化操作,把这些程序组合起来:

# Reinitializable iterator to switch between Datasets
EPOCHS = 10
# making fake data using numpy
train_data = (np.random.sample((100,2)), np.random.sample((100,1)))
test_data = (np.random.sample((10,2)), np.random.sample((10,1)))
# create two datasets, one for training and one for test
train_dataset = tf.data.Dataset.from_tensor_slices(train_data)
test_dataset = tf.data.Dataset.from_tensor_slices(test_data)
# create a iterator of the correct shape and type
iter = tf.data.Iterator.from_structure(train_dataset.output_types,
 train_dataset.output_shapes)
features, labels = iter.get_next()
# create the initialisation operations
train_init_op = iter.make_initializer(train_dataset)
test_init_op = iter.make_initializer(test_dataset)
with tf.Session() as sess:
 sess.run(train_init_op) # switch to train dataset
 for _ in range(EPOCHS):
 sess.run([features, labels])
 sess.run(test_init_op) # switch to val dataset
 print(sess.run([features, labels]))

可馈送迭代器

在我看来,这些方法可能效果不好,它们基本上不是在数据集之间切换,而是在迭代器之间切换。你可以分别用make_one_shot_iterator函数和make_initializable_iterator函数来创建两个迭代器。

消耗数据

在前面例子中,我们使用过Session来输出数据集中元素next的值:

...
next_el = iter.get_next()
...
print(sess.run(next_el)) # will output the current element

为了将数据传递给模型,我们只要传递get_next函数生成的张量。

在下面代码段中,有一个包含两个numpy数组的数据集,这里用了第一节的例子。请注意,我们要用.random.sample函数来包装另一个numpy数组以满足数据批量化的维度要求:

# using two numpy arrays
features, labels = (np.array([np.random.sample((100,2))]),
 np.array([np.random.sample((100,1))]))
dataset = tf.data.Dataset.from_tensor_slices((features,labels)).repeat().batch(BATCH_SIZE)

接着,和上面一样,创建一个迭代器:

iter = dataset.make_one_shot_iterator()
x, y = iter.get_next()

下面,构建一个简单的神经网络模型:

# make a simple model
net = tf.layers.dense(x, 8) # pass the first value from iter.get_next() as input
net = tf.layers.dense(net, 8)
prediction = tf.layers.dense(net, 1)
loss = tf.losses.mean_squared_error(prediction, y) # pass the second value from iter.get_net() as label
train_op = tf.train.AdamOptimizer().minimize(loss)

我们直接用iter.get_next函数输出的张量作为第一层的输入和损失函数的标签,整理后得到:

EPOCHS = 10
BATCH_SIZE = 16
# using two numpy arrays
features, labels = (np.array([np.random.sample((100,2))]),
 np.array([np.random.sample((100,1))]))
dataset = tf.data.Dataset.from_tensor_slices((features,labels)).repeat().batch(BATCH_SIZE)
iter = dataset.make_one_shot_iterator()
x, y = iter.get_next()
# make a simple model
net = tf.layers.dense(x, 8, activation=tf.tanh) # pass the first value from iter.get_next() as input
net = tf.layers.dense(net, 8, activation=tf.tanh)
prediction = tf.layers.dense(net, 1, activation=tf.tanh)
loss = tf.losses.mean_squared_error(prediction, y) # pass the second value from iter.get_net() as label
train_op = tf.train.AdamOptimizer().minimize(loss)
with tf.Session() as sess:
 sess.run(tf.global_variables_initializer())
 for i in range(EPOCHS):
 _, loss_value = sess.run([train_op, loss])
 print("Iter: {}, Loss: {:.4f}".format(i, loss_value))

输出为:

Iter: 0, Loss: 0.1328
Iter: 1, Loss: 0.1312
Iter: 2, Loss: 0.1296
Iter: 3, Loss: 0.1281
Iter: 4, Loss: 0.1267
Iter: 5, Loss: 0.1254
Iter: 6, Loss: 0.1242
Iter: 7, Loss: 0.1231
Iter: 8, Loss: 0.1220
Iter: 9, Loss: 0.1210

更多内容

批处理

通常来说,批处理数据是一件麻烦的事。但是可以用Dataset函数中的批处理方法batch(BATCH_SIZE),按照设定尺寸来自动批处理数据集,其中默认值为1。在下面示例中,批尺寸设置为4:

# BATCHING
BATCH_SIZE = 4
x = np.random.sample((100,2))
# make a dataset from a numpy array
dataset = tf.data.Dataset.from_tensor_slices(x).batch(BATCH_SIZE)
iter = dataset.make_one_shot_iterator()
el = iter.get_next()
with tf.Session() as sess:
 print(sess.run(el))

输出:

[[ 0.65686128 0.99373963]
[ 0.69690451 0.32446826]
[ 0.57148422 0.68688242]
[ 0.20335116 0.82473219]]

Repeat操作

使用repeat函数,可指定数据集中的迭代次数。若没有参数传递,它会一直循环。通常在持续循环后直接用一个标准循环来控制epoch大小。

Shuffle操作

我们可使用shuffle函数来打乱数据集,该函数默认在每个epoch打乱数据集。

打乱数据集,这个操作是非常重要的,可以减弱过拟合效应。

我们也可以设置参数buffer_size,这是shuffle函数的一个内置参数,下个元素将在这个缓冲区中统一选择。下面举例:

# BATCHING
BATCH_SIZE = 4
x = np.array([[1],[2],[3],[4]])
# make a dataset from a numpy array
dataset = tf.data.Dataset.from_tensor_slices(x)
dataset = dataset.shuffle(buffer_size=100)
dataset = dataset.batch(BATCH_SIZE)
iter = dataset.make_one_shot_iterator()
el = iter.get_next()
with tf.Session() as sess:
 print(sess.run(el))

第一次输出:

[[4]
[2]
[3]
[1]]

第二次输出:

[[3]
[1]
[2]
[4]]

可以看出,数字被打乱了。当然,你也可以设置下参数seed看看效果。

Map操作

你还可以使用map方法将自定义函数应用到数据集的每个元素中。在下面示例中,我们把每个元素都乘二:

# MAP
x = np.array([[1],[2],[3],[4]])
# make a dataset from a numpy array
dataset = tf.data.Dataset.from_tensor_slices(x)
dataset = dataset.map(lambda x: x*2)
iter = dataset.make_one_shot_iterator()
el = iter.get_next()
with tf.Session() as sess:
# this will run forever
 for _ in range(len(x)):
 print(sess.run(el))

输出:

[2]
[4]
[6]
[8]

结论

本文介绍的Dataset API给我们提供了一种快速且稳定的方法来创建最佳的输入流水线,以更好地训练、评估和测试网络模型。这篇文章介绍了这个API中的大部分常见操作。更多代码可参见本文对应的jupyter-notebook。

相关链接

原文:

https://towardsdatascience.com/how-to-use-dataset-in-tensorflow-c758ef9e4428

本文对应jupyter notebook:

https://github.com/FrancescoSaverioZuppichini/Tensorflow-Dataset-Tutorial/blob/master/dataset_tutorial.ipynb

TensorFlow dataset官方教程:

https://www.tensorflow.org/programmers_guide/datasets

Dataset的API文档:

https://www.tensorflow.org/api_docs/python/tf/data/Dataset

— 完 —

诚挚招聘

量子位正在招募编辑/记者,工作地点在北京中关村。期待有才气、有热情的同学加入我们!相关细节,请在量子位公众号(QbitAI)对话界面,回复“招聘”两个字。

量子位 QbitAI · 头条号签约作者

?'?' ? 追踪AI技术和产品新动态

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码