参考文章:
TensorFlow全新的数据读取方式:Dataset API入门教程
TensorFlow 利用Dataset读取和构建数据
【Tensorflow】Dataset 中的 Iterator
一、基本概念
1.DataSet

简单来说,Dataset可以看作是相同类型“元素”的有序列表,其中,单个“元素”可以是向量,也可以是字符串、图片,甚至是tuple或者dict。Dataset具有三个重要的子类TextLineDataset、TFRecordDataset、FixedLengthRecordDataset
2.Iterator

Iterator可以看作一个对应的数据集迭代器实例,用于实现从Dataset中取出数据。
常见的Iterator主要有oneshot Iterator、initializable iterator、reinitializable iterator
feedable iterator

3…Eager模式与非Eager模式
Eager模式,主要是从动态图的角度考虑,为了更加灵活的使用。
具体可以参考:Eager Execution
二、Dataset创建
1.Dataset简单创建方法
#range():
dataset = tf.data.Dataset.range(5)
#from_tensor_slices():
dataset = tf.data.Dataset.from_tensor_slices(np.array([1.0, 2.0, 3.0, 4.0, 5.0]))
from_tensor_slices真正作用是切分传入Tensor的第一个维度,生成相应的dataset,例如:
dataset = tf.data.Dataset.from_tensor_slices(np.random.uniform(size=(5, 2)))
传入的数值是一个矩阵,它的形状为(5, 2),tf.data.Dataset.from_tensor_slices就会切分它形状上的第一个维度,最后生成的dataset中一个含有5个元素,每个元素的形状是(2, ),即每个元素是矩阵的一行。

在实际使用中,我们可能还希望Dataset中的每个元素具有更复杂的形式,如每个元素是一个Python中的元组,或是Python中的词典。
我们可以让每一个元素是一个词典:
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”中的数值以及”b”中的数值,最终dataset中的一个元素就是类似于{“a”: 1.0, “b”: [0.9, 0.1]}的形式。
在图像识别问题中,一个元素可以是{“image”: image_tensor, “label”: label_tensor}的形式,这样处理起来更方便。同样,利用tf.data.Dataset.from_tensor_slices创建每个元素是一个tuple的dataset也是可以的。
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)))
)
2.从TFRecord文件创建
dataset = tf.data.TFRecordDataset(filenames)
filenames可以是一个TFRecord文件,也可以是一个文件LIST。
当然,从filename中解析出数据,需要对应的构造一个解析器函数(与原有的TFRecords文件结构对应解析)。
示例代码:

def parser(record): features=tf.parse_single_example(record, features={ “index”: tf.FixedLenFeature([], tf.int64), “col1”: tf.FixedLenFeature([], tf.int64), “col2”: tf.FixedLenFeature([], tf.int64) } ) return features[“index”],features[‘col1’],features[‘col2’]training_filenames = [“file-0.tfrecords”,”file-1.tfrecords”,”file-2.tfrecords”]dataset = tf.data.TFRecordDataset(training_filenames)dataset = dataset.map(parser)

三、Iterator
1.一次性one_shot
单次的迭代器,不支持动态的数据集,不支持参数化。

import tensorflow as tfdataset = tf.data.Dataset.range(5)iterator = dataset.make_one_shot_iterator()with tf.Session() as sess: for i in range(5): try: print(sess.run(iterator.get_next())) except tf.errors.OutOfRangeError: print(‘errors.OutOfRangeError’) ##output:0,1,2,3,4

2.initializable_iterator
我们都知道 Tensorflow 中有一种 feeding 机制,它允许我们在程序运行时再真正决定我们需要的数据,很遗憾,单次的 Iterator 不能满足这要的要求。它的创建方式是:
iterator = dataset.make_initializable_iterator()
在每次重新初始化的时候都需要调用:
iterator = dataset.make_initializable_iterator()

def initialable_test(): numbers = tf.placeholder(tf.int64,shape=[]) dataset = tf.data.Dataset.range(numbers) iterator = dataset.make_initializable_iterator() with tf.Session() as sess: for i in range(5): try: print(sess.run(iterator.get_next())) except tf.errors.OutOfRangeError: print(‘errors.OutOfRangeError’)

这里的Iterator的数据来源不变,还是原来的Dataset,只是可以参数化的使用原来的数据集,如指定输出数据的数量。
3.reinitialable_iterator
当需要一个 Iterator 从不同的 Dataset 对象中读取数值,TensorFlow提供了一个Iterator,但要求是不同数据源的结构一致(利用from_structure来统一数据源的结构)。

示例代码:

def reinitialable_iterator_test(): training_data = tf.data.Dataset.range(10) validation_data = tf.data.Dataset.range(5) iterator = tf.data.Iterator.from_structure(training_data.output_types,training_data.output_shapes) train_op = iterator.make_initializer(training_data) validation_op = iterator.make_initializer(validation_data) next_element = iterator.get_next() with tf.Session() as sess: sess.run(train_op) for _ in range(8): print(sess.run(next_element)) sess.run(validation_op) for _ in range(3): print(sess.run(next_element))

4.feedableiterator

可以看到,feedableiterator可以看作是一个转换器,它可以将不同的数据集的不同的管道Iterator连在一起,随心所欲的切换数据的源。这一点,它和reinitialable_iterator相似,但是,不同之处在于——feedableiterator 在不同的 Iterator 切换的时候,可以做到不从头开始。
从具体实现来看,它是通过一个 string 类型的 handle 实现的,需要注意的一点是,string_handle() 方法返回的是一个 Tensor,只有运行一个 Tensor 才会返回 string 类型的 handle。不然,程序会报错。
先来看一下from_string_handle()官网API:

#Creates a new, uninitialized Iterator based on the given handle@staticmethodfrom_string_handle(string_handle,output_types, output_shapes=None, output_classes=None)Args:string_handle: A scalar tf.Tensor of type tf.string that evaluates to a handle produced by the Iterator.string_handle() method.output_types: A nested structure of tf.DType objects corresponding to each component of an element of this dataset.output_shapes: (Optional.) A nested structure of tf.TensorShape objects corresponding to each component of an element of this dataset. If omitted, each component will have an unconstrainted shape.output_classes: (Optional.) A nested structure of Python type objects corresponding to each component of an element of this iterator. If omitted, each component is assumed to be of type tf.Tensor.

示例代码:

def feeding_iterator_test(): train_data = tf.data.Dataset.range(100,200,5) val_data = tf.data.Dataset.range(0,100,10).repeat(2) handle = tf.placeholder(tf.string,shape=[]) iterator = tf.data.Iterator.from_string_handle( handle,train_data.output_types,train_data.output_shapes) next_element = iterator.get_next() train_op = train_data.make_one_shot_iterator() validation_op = val_data.make_initializable_iterator() with tf.Session() as sess: train_iterator_handle = sess.run(train_op.string_handle()) val_iterator_handle = sess.run(validation_op.string_handle()) sess.run(validation_op.initializer) for _ in range(5): for _ in range(2): print(sess.run(next_element,feed_dict={handle:train_iterator_handle})) print(‘======’) for _ in range(3): print(sess.run(next_element,feed_dict={handle:val_iterator_handle})) print(‘======’) return

四、其他
主要总结了非Eager模式下Iterator和Dataset的常规用法,相关的其他用法之后再做详细补充。