点击上方“AI公园”,关注公众号,选择加“星标“或“置顶”
导读作者:Francesco Zuppichini
编译:ronghuaiyang
你在写TensorFlow代码的时候还在用占位符吗,别再用了,用Dataset吧。
内置的输入管道。永远不要再使用 feed-dict
。
你应该知道, feed-dict
是传递信息给TensorFlow的最慢的方法,必须避免。将数据输入模型的正确方法是使用输入管道,以确保GPU永远不会等待新东西的到来。
幸运的是,TensorFlow有一个内置的API,名为Dataset,以便更容易地完成这项任务。在本教程中,我们将看到如何创建输入管道,以及如何高效地将数据输入模型。
本文将解释数据集的基本机制,包括最常见的用例。
你可以在这里找到所有代码:
https://github.com/FrancescoSaverioZuppichini/Tensorflow-Dataset-Tutorial/blob/master/dataset_tutorial.ipynb
通用的概述
为了使用数据集,我们需要三个步骤:
导入数据。从一些数据创建数据集实例
创建一个迭代器。通过使用创建的数据集创建迭代器实例来遍历数据集
消费数据。通过使用创建的迭代器,我们可以从数据集中获取元素来提供给模型
导入数据
我们首先需要把一些数据放入数据集中
来自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
当然,我们可以用一些张量初始化我们的数据集
# using a tensor
dataset = tf.data.Dataset.from_tensor_slices(tf.random_uniform([100, 2]))
来自占位符
当我们想要动态地改变数据集中的数据时,这是很有用的,我们将在后面看到如何做。
x = tf.placeholder(tf.float32, shape=[None,2])
dataset = tf.data.Dataset.from_tensor_slices(x)
来自生成器
我们也可以从生成器初始化数据集,当我们有一个不同元素长度的数组(比如一个序列)的时候:
# from generator
sequence = np.array([[[1]],[[2],[3]],[[3],[4],[5]]])
def generator():
for el in sequence:
yield el
dataset = tf.data.Dataset().batch(1).from_generator(generator,
output_types= tf.int64,
output_shapes=(tf.TensorShape([None, 1])))
iter = dataset.make_initializable_iterator()
el = iter.get_next()
with tf.Session() as sess:
sess.run(iter.initializer)
print(sess.run(el))
print(sess.run(el))
print(sess.run(el))
Ouputs:
[[1]]
[[2]
[3]]
[[3]
[4]
[5]]
在本例中,还需要指定用于创建正确张量的数据的类型和形状。
来自csv文件
你可以直接将csv文件读入数据集中。例如,我有一个csv文件,其中包含推文和对应的情绪。
我现在可以通过调用 tf.contrib.data.make_csv_dataset
轻松地从它创建 Dataset
。注意,迭代器将创建一个字典,其中键作为列名,值作为张量,并具有正确的行值。
# load a csv
CSV_PATH = './tweets.csv'
dataset = tf.contrib.data.make_csv_dataset(CSV_PATH, batch_size=32)
iter = dataset.make_one_shot_iterator()
next = iter.get_next()
print(next) # next is a dict with key=columns names and value=column data
inputs, labels = next['text'], next['sentiment']
with tf.Session() as sess:
sess.run([inputs, labels])
next
的值为:
{'sentiment': <tf.Tensor 'IteratorGetNext_15:0' shape=(?,) dtype=int32>, 'text': <tf.Tensor 'IteratorGetNext_15:1' shape=(?,) dtype=string>}
创建迭代器
我们已经看到了如何创建数据集,但是如何取回数据呢?我们必须使用“迭代器”,这将使我们能够遍历数据集并检索数据的实际值。有四种类型的迭代器。
One shot. 它可以迭代一次数据集,你不能向它提供任何值。
Initializable 你可以动态更改调用它的
initializer
操作,并使用feed_dict
传递新数据。它基本上是一个可以装满东西的桶。Reinitializable 它可以从不同的
Dataset.
初始化。当你有一个需要额外变换的训练数据集时,比如说shuffle和测试集,这非常有用。这就像使用塔式起重机来选择不同的容器。Feedable 使用迭代器进行选择。按照前面的示例,它就像塔式起重机一样,选择要使用哪个塔式起重机来选择要取哪个容器。在我看来是没有用的。
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]
Initializable迭代器
如果我们想要构建一个动态数据集,我们可以在运行时更改数据源,我们可以创建一个带有占位符的数据集。然后,我们可以使用常见的 feed-dict
机制初始化占位符。这是通过一个initializable迭代器完成的。使用上一节的示例3
# 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
操作,以便传递我们的数据,在本例中是一个随机的numpy数组。
假设现在我们有一个训练集和一个测试集,一个真实的常见场景:
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]))
Reinitializable迭代器
这个概念与以前类似,我们希望在数据之间进行动态切换。但是,我们不是将新数据提供给相同的数据集,而是切换数据集。和以前一样,我们希望有一个训练数据集和一个测试数据集
# 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)
然后是两个初始化操作:
# 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()
现在,我们可以使用会话直接运行两个初始化操作。把所有这些放在一起,我们得到:
# 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]))
Feedable迭代器
这与 reinitializable
迭代器非常相似,但是它不是在数据集之间切换,而是在迭代器之间切换。在我们创建两个数据集之后
train_dataset = tf.data.Dataset.from_tensor_slices((x,y))
test_dataset = tf.data.Dataset.from_tensor_slices((x,y))
一个用于训练,一个用于测试。然后,我们可以创建我们的迭代器,在本例中,我们使用 initializable
迭代器,但也可以使用 one shot
迭代器
train_iterator = train_dataset.make_initializable_iterator()
test_iterator = test_dataset.make_initializable_iterator()
现在,我们需要定义一个 handle
,这将是一个可以动态更改的占位符。
handle = tf.placeholder(tf.string, shape=[])
然后,与前面类似,我们使用数据集的形状定义一个通用迭代器
iter = tf.data.Iterator.from_string_handle(
handle, train_dataset.output_types, train_dataset.output_shapes)
然后,我们得到下一个元素
next_elements = iter.get_next()
为了在迭代器之间切换,我们只需要调用 next_elemenents
操作,并在feed_dict中传递正确的 handle
。例如,要从训练集中得到一个元素:
sess.run(next_elements, feed_dict = {handle: train_handle})
如果您正在使用 initializable
迭代器,就像我们正在做的一样,请记住在开始之前初始化它们
sess.run(train_iterator.initializer, feed_dict={ x: train_data[0], y: train_data[1]})
sess.run(test_iterator.initializer, feed_dict={ x: test_data[0], y: test_data[1]})
把所有这些放在一起,我们得到:
# feedable iterator to switch between iterators
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 placeholder
x, y = tf.placeholder(tf.float32, shape=[None,2]), tf.placeholder(tf.float32, shape=[None,1])
# create two datasets, one for training and one for test
train_dataset = tf.data.Dataset.from_tensor_slices((x,y))
test_dataset = tf.data.Dataset.from_tensor_slices((x,y))
# create the iterators from the dataset
train_iterator = train_dataset.make_initializable_iterator()
test_iterator = test_dataset.make_initializable_iterator()
# same as in the doc https://www.tensorflow.org/programmers_guide/datasets#creating_an_iterator
handle = tf.placeholder(tf.string, shape=[])
iter = tf.data.Iterator.from_string_handle(
handle, train_dataset.output_types, train_dataset.output_shapes)
next_elements = iter.get_next()
with tf.Session() as sess:
train_handle = sess.run(train_iterator.string_handle())
test_handle = sess.run(test_iterator.string_handle())
# initialise iterators.
sess.run(train_iterator.initializer, feed_dict={ x: train_data[0], y: train_data[1]})
sess.run(test_iterator.initializer, feed_dict={ x: test_data[0], y: test_data[1]})
for _ in range(EPOCHS):
x,y = sess.run(next_elements, feed_dict = {handle: train_handle})
print(x, y)
x,y = sess.run(next_elements, feed_dict = {handle: test_handle})
print(x,y)
消费数据
在前面的示例中,我们使用会话打印数据集中 next
元素的值。
...
next_el = iter.get_next()
...
print(sess.run(next_el)) # will output the current element
为了将数据传递给模型,我们必须传递由 get_next()
生成的张量
在下面的代码片段中,我们有一个包含两个numpy数组的数据集,使用了第一部分中的相同示例。注意,我们需要在另一个numpy数组中封装 .random.sample
,以添加一个维度,组成我们需要的批数据。
# 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()
中的张量作为第一层的输入,并作为loss函数的标签。包装在一起:
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))
Output:
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
有用的东西
Batch
通常,批处理数据是一件令人头痛的事情,使用 Dataset
API,我们可以使用 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))
Output:
[[ 0.65686128 0.99373963]
[ 0.69690451 0.32446826]
[ 0.57148422 0.68688242]
[ 0.20335116 0.82473219]]
Repeat
使用 .repeat()
可以指定希望迭代数据集的次数。如果没有传递参数,它将永远循环,通常最好是永远循环,并直接用标准循环控制周期的数量。
Shuffle
我们可以使用 shuffle()
方法来打乱数据集,该方法在每个epoch开始的时候默认打乱数据集。
请记住:打乱数据集是非常重要的,以避免过度拟合
我们还可以设置参数 buffer_size
,这是一个固定大小的缓冲区,下一个元素将从其中均匀的选择。例子:
# 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
方法将自定义函数应用于数据集的每个成员。在下面的例子中,我们将每个元素乘以2:
# 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))
Output:
[2]
[4]
[6]
[8]
完整的例子
Initializable iterator
在下面的示例中,我们使用批处理来训练一个简单的模型,并使用Initializable iterator在训练和测试数据集之间切换
# Wrapping all together -> Switch between train and test set using Initializable iterator
EPOCHS = 10
# create a placeholder to dynamically switch between batch sizes
batch_size = tf.placeholder(tf.int64)
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)).batch(batch_size).repeat()
# using two numpy arrays
train_data = (np.random.sample((100,2)), np.random.sample((100,1)))
test_data = (np.random.sample((20,2)), np.random.sample((20,1)))
iter = dataset.make_initializable_iterator()
features, labels = iter.get_next()
# make a simple model
net = tf.layers.dense(features, 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, labels) # 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())
# initialise iterator with train data
sess.run(iter.initializer, feed_dict={ x: train_data[0], y: train_data[1], batch_size: BATCH_SIZE})
print('Training...')
for i in range(EPOCHS):
tot_loss = 0
for _ in range(n_batches):
_, loss_value = sess.run([train_op, loss])
tot_loss += loss_value
print("Iter: {}, Loss: {:.4f}".format(i, tot_loss / n_batches))
# initialise iterator with test data
sess.run(iter.initializer, feed_dict={ x: test_data[0], y: test_data[1], batch_size: test_data[0].shape[0]})
print('Test Loss: {:4f}'.format(sess.run(loss)))
注意,我们为批大小使用占位符,以便训练之后动态切换它
Output
Training...
Iter: 0, Loss: 0.2977
Iter: 1, Loss: 0.2152
Iter: 2, Loss: 0.1787
Iter: 3, Loss: 0.1597
Iter: 4, Loss: 0.1277
Iter: 5, Loss: 0.1334
Iter: 6, Loss: 0.1000
Iter: 7, Loss: 0.1154
Iter: 8, Loss: 0.0989
Iter: 9, Loss: 0.0948
Test Loss: 0.082150
Reinitializable Iterator
在下面的示例中,我们使用批处理来训练一个简单的模型,并使用Reinitializable Iterator在训练和测试数据集之间切换
# Wrapping all together -> Switch between train and test set using Reinitializable iterator
EPOCHS = 10
# create a placeholder to dynamically switch between batch sizes
batch_size = tf.placeholder(tf.int64)
x, y = tf.placeholder(tf.float32, shape=[None,2]), tf.placeholder(tf.float32, shape=[None,1])
train_dataset = tf.data.Dataset.from_tensor_slices((x,y)).batch(batch_size).repeat()
test_dataset = tf.data.Dataset.from_tensor_slices((x,y)).batch(batch_size) # always batch even if you want to one shot it
# using two numpy arrays
train_data = (np.random.sample((100,2)), np.random.sample((100,1)))
test_data = (np.random.sample((20,2)), np.random.sample((20,1)))
# 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)
# make a simple model
net = tf.layers.dense(features, 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, labels) # 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())
# initialise iterator with train data
sess.run(train_init_op, feed_dict = {x : train_data[0], y: train_data[1], batch_size: 16})
print('Training...')
for i in range(EPOCHS):
tot_loss = 0
for _ in range(n_batches):
_, loss_value = sess.run([train_op, loss])
tot_loss += loss_value
print("Iter: {}, Loss: {:.4f}".format(i, tot_loss / n_batches))
# initialise iterator with test data
sess.run(test_init_op, feed_dict = {x : test_data[0], y: test_data[1], batch_size:len(test_data[0])})
print('Test Loss: {:4f}'.format(sess.run(loss)))
其他的资源
TensorFlow Dataset教程: https://www.tensorflow.org/programmers_guide/datasets
Dataset文档:https://www.tensorflow.org/api_docs/python/tf/data/Dataset
总结
Dataset
API为我们提供了一种快速而健壮的方法来创建优化的输入管道,来训练、评估和测试我们的模型。在本文中,我们已经看到了可以使用它们进行的大多数常见操作。
![640?wx_fmt=png](https://i-blog.csdnimg.cn/blog_migrate/f26db2441c55d286cc8f35e567ed66f7.png)
英文原文:https://towardsdatascience.com/how-to-use-dataset-in-tensorflow-c758ef9e4428
请长按或扫描二维码关注本公众号
喜欢的话,请给我个好看吧!