TensorFlow 利用Dataset读取和构建数据

参考链接:
Dataset官方链接
TensorFlow全新的数据读取方式:Dataset API入门教程
TensorFlow数据读取方式:
- 利用placeholder读取内存数据
- 利用queue读取硬盘中的数据
参考链接:https://zhuanlan.zhihu.com/p/27238630 - Dataset API同时支持从内存和硬盘的读取,相比之前的两种方法在语法上更加简洁易懂
Dataset创建和读取数据集
Google官方给出的Dataset API中的类图如下所示:
Dataset可以看作是相同类型元素的有序列表。在实际使用时,单个元素可以是向量,也可以是字符串、图片,元组tuple或者字典dict
简单的创建和读取
以单个元素为数字为例创建Dataset,创建的数据集包含1~5 5个元素
dataset = tf.data.Dataset.from_tensor_slices(np.array([1.0, 2.0, 3.0, 4.0, 5.0]))
使用类iterator读取数据集中的值如下所示:
#实例化make_one_shot_iterator对象,该对象只能读取一次
iterator = dataset.make_one_shot_iterator()
# 从iterator里取出一个元素
one_element = iterator.get_next()
with tf.Session() as sess:
for i in range(5):
print(sess.run(one_element))
如果一个dataset中元素被读取完了,再尝试sess.run(one_element)的话,就会抛出tf.errors.OutOfRangeError异常,这个行为与使用队列方式读取数据的行为是一致的。在实际程序中,可以在外界捕捉这个异常以判断数据是否读取完,请参考下面的代码:
iterator = dataset.make_one_shot_iterator()
one_element = iterator.get_next()
with tf.Session() as sess:
try:
while True:
print(sess.run(one_element))
except tf.errors.OutOfRangeError:
print("end!")
创建较为复杂的Dataset
tf.data.Dataset.from_tensor_slices会切分传入Tensor的第0个维度,生成相应的dataset。
dataset = tf.data.Dataset.from_tensor_slices(np.random.uniform(size=(5, 2)))
传入的数值是一个矩阵,它的形状为(5, 2),tf.data.Dataset.from_tensor_slices
就会切分它形状上的第0个维度,最后生成的dataset中一个含有5个元素,每个元素的形状是(2, ),即每个元素是矩阵的一行,相当于一个一维的array。
实际使用中,Dataset中的元素可能是元组或者字典。在图像识别问题中,一个元素可以是{“image”: image_tensor, “label”: label_tensor}
形式的字典。
例如:(字典)
dataset = tf.data.Dataset.from_tensor_slices(
{
"a": np.array([1.0, 2.0, 3.0, 4.0, 5.0]),
"b": np.random.uniform(size=(5, 2))
}
)
输出:
{'a': 1.0, 'b': array([0.26454147, 0.78977893])}
{'a': 2.0, 'b': array([0.19478178, 0.37884041])}
{'a': 3.0, 'b': array([0.45655924, 0.46979661])}
{'a': 4.0, 'b': array([0.08724776, 0.26343558])}
{'a': 5.0, 'b': array([0.63206763, 0.02796295])}
例如(元组):
dataset = tf.data.Dataset.from_tensor_slices(
(np.array([1.0, 2.0, 3.0, 4.0, 5.0]), np.random.uniform(size=(5, 2)))
)
输出:
(1.0, array([0.48643253, 0.69208099]))
(2.0, array([0.96668577, 0.39036077]))
(3.0, array([0.54854508, 0.54833064]))
(4.0, array([0.30970788, 0.29545166]))
(5.0, array([0.0290356 , 0.75689468]))
对Dataset中的数据进行转换transformation
一个Dataset通过Transformation变成一个新的Dataset。通常我们可以通过Transformation完成数据变换,打乱,组成batch,生成epoch等一系列操作
常用的Transformation有:
- map
- batch
- shuffle
repeat
(1)map
map接收一个函数对象,Dataset中的每个元素都会被当作这个函数的输入,并将函数返回值作为新的Dataset,如我们可以对dataset中每个元素的值加1:
dataset = tf.data.Dataset.from_tensor_slices(np.array([1.0, 2.0, 3.0, 4.0, 5.0]))
dataset = dataset.map(lambda x: x + 1) # 2.0, 3.0, 4.0, 5.0, 6.0
(2)batch
根据接收的整数值将该数个元素组合成batch,如下面的程序将dataset中的元素组成了大小为32的batch
dataset = dataset.batch(32)
(3)shuffle
打乱dataset中的元素,它有一个参数buffersize,表示打乱时使用的buffer的大小
dataset = dataset.shuffle(buffer_size=10000)
(4)repeat
整个序列重复多次,主要用来处理机器学习中的epoch,假设原先的数据是一个epoch,使用repeat(5)就可以将之变成5个epoch:
dataset = dataset.repeat(5)
实例:读入磁盘图片和label创建Dataset
读入磁盘中的图片和图片相应的label,并将其打乱,组成batch_size=32的训练样本。在训练时重复10个epoch。
# 将filename对应的图片文件读入,并缩放到统一的大小
def _parse_function(filename, label):
# 读取图像文件内容编码为字符串
image_contents = tf.read_file(filename)
# 根据图像编码后的字符串解码为uint8的tensor
image_decoded = tf.image.decode_image(image_contents)
# 修改图像尺寸
image_resized = tf.image.resize_images(image_decoded, [28, 28])
return image_resized, label
# 图片文件的列表
filenames = tf.constant(["/var/data/image1.jpg", "/var/data/image2.jpg", ...])
# label[i]就是图片filenames[i]的label
labels = tf.constant([0, 37, ...])
# 此时dataset中的一个元素是(filename, label)
dataset = tf.data.Dataset.from_tensor_slices((filenames, labels))
# 此时dataset中的一个元素是(image_resized, label)
dataset = dataset.map(_parse_function)
# 此时dataset中的一个元素是(image_resized_batch, label_batch)
# dataset中的元素是(image_resized_batch, label_batch),image_resized_batch的形状为(32, 28, 28, 3),而label_batch的形状为(32, )
dataset = dataset.shuffle(buffersize=1000).batch(32).repeat(10)
Dataset其他创建方法
Dataset API还提供了另外三种创建Dataset的方式:
- tf.data.TextLineDataset():这个函数的输入是一个文件的列表,输出是一个dataset。dataset中的每一个元素就对应了文件中的一行。可以使用这个函数来读入CSV文件。
- tf.data.FixedLengthRecordDataset():这个函数的输入是一个文件的列表和一个record_bytes,之后dataset的每一个元素就是文件中固定字节数record_bytes的内容。通常用来读取以二进制形式保存的文件,如CIFAR10数据集就是这种形式。
- tf.data.TFRecordDataset():顾名思义,这个函数是用来读TFRecord文件的,dataset中的每一个元素就是一个TFExample。
其他的Iterator
除了这种one shot iterator外,还有三个更复杂的Iterator,即:
- initializable iterator
- reinitializable iterator
- feedable iterator
initializable iterator
initializable iterator必须要在使用前通过sess.run()来初始化。使用initializable iterator,可以将placeholder代入Iterator中,这可以方便我们通过参数快速定义新的Iterator。一个简单的initializable iterator使用示例:
limit = tf.placeholder(dtype=tf.int32, shape=[])
dataset = tf.data.Dataset.from_tensor_slices(tf.range(start=0, limit=limit))
iterator = dataset.make_initializable_iterator()
next_element = iterator.get_next()
with tf.Session() as sess:
sess.run(iterator.initializer, feed_dict={limit: 10})
for i in range(10):
value = sess.run(next_element)
使用initializable iterator读入大规模数据时更为方便。在使用tf.data.Dataset.from_tensor_slices(array)时,实际上发生的事情是将array作为一个tf.constants保存到了计算图中。当array很大时,会导致计算图变得很大,给传输、保存带来不便。这时,我们可以用一个placeholder取代这里的array,并使用initializable iterator,只在需要时将array传进去,这样就可以避免把大数组保存在图里,示例代码为(来自官方例程):
with np.load("/var/data/training_data.npy") as data:
features = data["features"]
labels = data["labels"]
features_placeholder = tf.placeholder(features.dtype, features.shape)
labels_placeholder = tf.placeholder(labels.dtype, labels.shape)
dataset = tf.data.Dataset.from_tensor_slices((features_placeholder, labels_placeholder))
iterator = dataset.make_initializable_iterator()
sess.run(iterator.initializer, feed_dict={features_placeholder: features,
labels_placeholder: labels})
reinitializable iterator
此iterator可以被不同的数据集重用
# Creates a new, uninitialized Iterator with the given structure
iterator = tf.data.Iterator.from_structure(tf.int64, tf.TensorShape([]))
# The returned iterator is not bound to a particular dataset,
# and it has no initializer. To initialize the iterator, run the
# operation returned by Iterator.make_initializer(dataset).
dataset_range = tf.data.Dataset.range(2)
range_initializer = iterator.make_initializer(dataset_range)
dataset_evens = tf.data.Dataset.range(3)
evens_initializer = iterator.make_initializer(dataset_evens)
next_batch = iterator.get_next()
sess = tf.Session()
# Initialize the iterator to `dataset_range`
sess.run(range_initializer)
while True:
try:
val = sess.run(next_batch)
print(val)
except tf.errors.OutOfRangeError:
break
print('-------------------')
# Initialize the iterator to `dataset_evens`
sess.run(evens_initializer)
while True:
try:
val1 = sess.run(next_batch)
print(val1)
except tf.errors.OutOfRangeError:
break
输出:
0
1
-------------------
0
1
2




更多推荐
所有评论(0)