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
  • 6
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
TensorFlow 中,可以使用 `tf.data` API 读取本地数据集。下面是一个例子: ```python import tensorflow as tf # 定义数据集路径 data_dir = "/path/to/data" # 创建数据集对象 dataset = tf.data.TFRecordDataset(filenames=[data_dir]) # 解析数据集并进行预处理 def parse_fn(example_proto): features = {"image": tf.io.FixedLenFeature([], tf.string), "label": tf.io.FixedLenFeature([], tf.int64)} parsed_features = tf.io.parse_single_example(example_proto, features) image = tf.io.decode_jpeg(parsed_features['image'], channels=3) image = tf.image.resize(image, [224, 224]) label = parsed_features['label'] return image, label # 对数据集进行处理,包括乱序、批处理、预处理等 batch_size = 32 dataset = dataset.map(parse_fn) dataset = dataset.shuffle(buffer_size=10000) dataset = dataset.batch(batch_size) dataset = dataset.prefetch(tf.data.experimental.AUTOTUNE) # 构建迭代器并获取数据 iterator = tf.compat.v1.data.make_one_shot_iterator(dataset) images, labels = iterator.get_next() ``` 上述代码中,首先定义了数据集路径 `data_dir`,然后使用 `TFRecordDataset` 创建数据集对象。接着定义了一个解析函数 `parse_fn`,用于解析 TFRecord 数据并进行预处理。最后,对数据集进行乱序、批处理、预处理等操作,并使用迭代器获取数据。 需要注意的是,上述代码中使用了 TensorFlow 2.0 的 API,如果使用 TensorFlow 1.x 版本,需要对一些 API 进行修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值