TensorFlow笔记:数据集导出

2018/5/17更新
有问题欢迎联系邮箱 [email protected]

公布一下自己的一段读入数据的操作。
我在做端到端的任务,所以实际上我的image和label都是array。
准备工作:需要按照下文方法先生成tfrecord文件以便tf.data.Dataset操作。

def read_and_decode(serialized_example):
    '''read and decode tfrecord file, generate (image, label) batches
    Args:
        tfrecords_file: the directory of tfrecord file
        batch_size: number of images in each batch
    Returns:
        image: 4D tensor - [batch_size, width, height, channel]
        label: 1D tensor - [batch_size]
    '''
    # make an input queue from the tfrecord file
    img_features = tf.parse_single_example(
        serialized_example,
        features={
            'label_raw': tf.FixedLenFeature([], tf.string),
            'image_raw': tf.FixedLenFeature([], tf.string),
        })
    image = tf.decode_raw(img_features['image_raw'], tf.uint8)
    image = tf.reshape(image, [480, 640, 3])
    image = tf.div(tf.to_float(image), 255.0)
    label = tf.decode_raw(img_features['label_raw'], tf.uint8)
    label = tf.reshape(label, [480, 640, 1])
    label = tf.div(tf.to_float(label), 255.0)
    return image, label

#根据自己需要来修改下面路径
TRAIN_TFREC = 'data/.../train.tfrecords'
VAL_TFREC = 'data/.../test.tfrecords'

filenames = tf.placeholder(tf.string, shape=[None])
dataset = tf.data.TFRecordDataset(filenames)
dataset = dataset.map(read_and_decode,num_parallel_calls=4)
dataset = dataset.shuffle(buffer_size=100)
dataset = dataset.batch(1)
#dataset = dataset.repeat()
iterator = tf.data.Iterator.from_structure(dataset.output_types, dataset.output_shapes)
init_train = iterator.make_initializer(dataset)
images, labels = iterator.get_next()

#这里是GPU的基本操作
gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=0.9)
config = tf.ConfigProto(allow_soft_placement=True,gpu_options=gpu_options)
sess=tf.train.MonitoredTrainingSession(config=config)
# Compute for 2 epochs.
for epoch in range(2):
  sess.run(init_train, feed_dict={filenames: [TRAIN_TFREC]})
  step=0
  while True:
    step +=1
    try:
      label=sess.run(labels)
      if step%1000==0:
        print(label[0][225][90:100])
        print(epoch,step)
    except tf.errors.OutOfRangeError:
      break

更新至Tensorflow 1.4

I. 读输入数据

1. 如果数据库大小可以全部被内存读入 使用最简单的Numpy arrays格式:

1). 将npy文件转换成tf.Tensor
2). 使用Dataset.from_tensor_slices()
示例:

# Load the training data into two NumPy arrays, for example using `np.load()`.
with np.load("/var/data/training_data.npy") as data:
features = data["features"]
labels = data["labels"]
# Assume that each row of features corresponds to the same row as `labels`.
assert features.shape[0] == labels.shape[0]
dataset = tf.data.Dataset.from_tensor_slices((features, labels))

请注意,上面的代码片段会将TensorFlow graph中的featureslabels数组作为tf.constant()操作嵌入。 这适用于小数据集,但会浪费内存—因为数组的内容将被复制多次—并且可以运行到tf.GraphDef协议缓冲区(有2GB限制)。

作为替代,可以使用tf.placeholder()张量来定义数据集,并在初始化数据集上的迭代器时提供NumPy数组。

# Load the training data into two NumPy arrays, for example using `np.load()`.
with np.load("/var/data/training_data.npy") as data:
  features = data["features"]
  labels = data["labels"]

# Assume that each row of `features` corresponds to the same row as `labels`.
assert features.shape[0] == labels.shape[0]

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))
# [Other transformations on `dataset`...]
dataset = ...
iterator = dataset.make_initializable_iterator()

sess.run(iterator.initializer, feed_dict={features_placeholder: features,
                                          labels_placeholder: labels})

2. 创建TFRecord数据

Dataset API支持多种文件格式,因此可以处理不匹配现有内存的大型数据集。 例如,TFRecord文件格式是简单的面向记录(record-oriented)的二进制格式,许多TensorFlow应用TFRecord来训练数据。 tf.data.TFRecordDataset类使您可以将一个或多个TFRecord文件的内容作为input pipline的一部分进行流式处理。

# Creates a dataset that reads all of the examples from two files.
filenames = ["/var/data/file1.tfrecord", "/var/data/file2.tfrecord"]
dataset = tf.data.TFRecordDataset(filenames)

TFRecordDataset初始化程序的filenames参数可以是strings,a list of strings或tf.Tensor of strings。 因此,如果您有两组文件用于训练和验证,则可以使用tf.placeholder(tf.string)来表示文件名,并使用相应的文件名初始化迭代器:

filenames = tf.placeholder(tf.string, shape=[None])
dataset = tf.data.TFRecordDataset(filenames)
dataset = dataset.map(...)  # Parse the record into tensors.
dataset = dataset.repeat()  # Repeat the input indefinitely.
dataset = dataset.batch(32)
iterator = dataset.make_initializable_iterator()

# You can feed the initializer with the appropriate filenames for the current
# phase of execution, e.g. training vs. validation.

# Initialize `iterator` with training data.
training_filenames = ["/var/data/file1.tfrecord", "/var/data/file2.tfrecord"]
sess.run(iterator.initializer, feed_dict={filenames: training_filenames})

# Initialize `iterator` with validation data.
validation_filenames = ["/var/data/validation1.tfrecord", ...]
sess.run(iterator.initializer, feed_dict={filenames: validation_filenames})

3. 创建text数据

许多数据集分布为一个或多个text文件。tf.data.TextLineDataset提供了从一个或多个文本文件中提取行(lines)的简单方法。 给定一个或多个文件名,TextLineDataset将为这些文件的每行生成一个字符串值元素(string-valued element)。 像TFRecordDataset一样,TextLineDataset接受文件名作为tf.Tensor,所以你可以通过传递一个tf.placeholder(tf.string)来对它进行参数化。

filenames = ["/var/data/file1.txt", "/var/data/file2.txt"]
dataset = tf.data.TextLineDataset(filenames)

默认情况下,TextLineDataset产生每个文件的每一行,这可能不是所希望的,例如存在文件以标题行开头或包含注释。 这些行可以使用Dataset.skip()Dataset.filter()的转换来删除。 要将这些转换分别应用于每个文件,我们使用Dataset.flat_map()为每个文件创建一个嵌套的数据集。

filenames = ["/var/data/file1.txt", "/var/data/file2.txt"]

dataset = tf.data.Dataset.from_tensor_slices(filenames)

# Use `Dataset.flat_map()` to transform each file as a separate nested dataset,
# and then concatenate their contents sequentially into a single "flat" dataset.
# * Skip the first line (header row).
# * Filter out lines beginning with "#" (comments).
dataset = dataset.flat_map(
    lambda filename: (
        tf.data.TextLineDataset(filename)
        .skip(1)
        .filter(lambda line: tf.not_equal(tf.substr(line, 0, 1), "#"))))

有关使用数据集解析CSV文件的完整示例,可以参阅imports85.py


II. 使用Dataset.map()来预处理数据

Dataset.map(f)变换是通过将给定的函数f应用于输入数据集的每个元素来生成新的数据集。 它基于通常应用于函数式编程语言中的列表(和其他结构)的map()函数。 函数f采用代表输入中单个元素的tf.Tensor对象,并返回将表示新数据集中单个元素的tf.Tensor对象。 其实现使用标准的TensorFlow操作将一个元素转换为另一个元素。

本节介绍如何使用Dataset.map()的常见示例。

1. 解析tf.Example协议缓冲区消息(protocol buffer messages)

许多输入流水线从TFRecord格式文件中提取tf.train.Example协议缓冲区消息(如使用tf.python_io.TFRecordWriter编写)。 每个tf.train.Example记录包含一个或多个“features”,输入管道通常将这些features转换为张量(tensors)。

# Transforms a scalar string `example_proto` into a pair of a scalar string and
# a scalar integer, representing an image and its label, respectively.
def _parse_function(example_proto):
  features = {"image": tf.FixedLenFeature((), tf.string, default_value=""),
              "label": tf.FixedLenFeature((), tf.int32, default_value=0)}
  parsed_features = tf.parse_single_example(example_proto, features)
  return parsed_features["image"], parsed_features["label"]

# Creates a dataset that reads all of the examples from two files, and extracts
# the image and label features.
filenames = ["/var/data/file1.tfrecord", "/var/data/file2.tfrecord"]
dataset = tf.data.TFRecordDataset(filenames)
dataset = dataset.map(_parse_function)

2. 解码图像数据并调整大小

当在实际图像数据上训练神经网络时,经常需要将不同大小的图像转换成通用大小,以便它们可以批量化(batch into)为固定大小。

# Reads an image from a file, decodes it into a dense tensor, and resizes it
# to a fixed shape.
def _parse_function(filename, label):
  image_string = tf.read_file(filename)
  image_decoded = tf.image.decode_image(image_string)
  image_resized = tf.image.resize_images(image_decoded, [28, 28])
  return image_resized, label

# A vector of filenames.
filenames = tf.constant(["/var/data/image1.jpg", "/var/data/image2.jpg", ...])

# `labels[i]` is the label for the image in `filenames[i].
labels = tf.constant([0, 37, ...])

dataset = tf.data.Dataset.from_tensor_slices((filenames, labels))
dataset = dataset.map(_parse_function)

3. 用tf.py_func()应用任意的Python逻辑

出于性能原因,鼓励尽可能使用TensorFlow操作来预处理数据。 但是,在解析输入数据时,调用外部Python库有时会很有用。 为此,请调用Dataset.map()转换中的tf.py_func()操作。这里用opencv的python库cv2举例。

import cv2

# Use a custom OpenCV function to read the image, instead of the standard
# TensorFlow `tf.read_file()` operation.
def _read_py_function(filename, label):
  image_decoded = cv2.imread(image_string, cv2.IMREAD_GRAYSCALE)
  return image_decoded, label

# Use standard TensorFlow operations to resize the image to a fixed shape.
def _resize_function(image_decoded, label):
  image_decoded.set_shape([None, None, None])
  image_resized = tf.image.resize_images(image_decoded, [28, 28])
  return image_resized, label

filenames = ["/var/data/image1.jpg", "/var/data/image2.jpg", ...]
labels = [0, 37, 29, 1, ...]

dataset = tf.data.Dataset.from_tensor_slices((filenames, labels))
dataset = dataset.map(
    lambda filename, label: tuple(tf.py_func(
        _read_py_function, [filename, label], [tf.uint8, label.dtype])))
dataset = dataset.map(_resize_function)

III. 批处理(Batching)数据集

1. 简单批处理

最简单的批量形式将数据集中的n个连续元素堆叠成一个元素。 Dataset.batch()转换正是这样做的,它与tf.stack()运算符的约束条件相同,应用于元素的每个元素,也就是说对于每个元素i,都必须具有完全相同形状的张量。

inc_dataset = tf.data.Dataset.range(100)
dec_dataset = tf.data.Dataset.range(0, -100, -1)
dataset = tf.data.Dataset.zip((inc_dataset, dec_dataset))
batched_dataset = dataset.batch(4)

iterator = batched_dataset.make_one_shot_iterator()
next_element = iterator.get_next()

print(sess.run(next_element))  # ==> ([0, 1, 2,   3],   [ 0, -1,  -2,  -3])
print(sess.run(next_element))  # ==> ([4, 5, 6,   7],   [-4, -5,  -6,  -7])
print(sess.run(next_element))  # ==> ([8, 9, 10, 11],   [-8, -9, -10, -11])

2. 使用padding填充来批量化张量

上述方法适用于所有尺寸相同的张量。 然而,许多模型(如序列模型)与可能具有不同大小的输入数据(例如,不同长度的序列)一起工作。 为了处理这种情况,通过Dataset.padded_batch()转换,您可以通过指定一个或多个可能被填充的维度来批量处理不同形状的张量。

dataset = tf.data.Dataset.range(100)
dataset = dataset.map(lambda x: tf.fill([tf.cast(x, tf.int32)], x))
dataset = dataset.padded_batch(4, padded_shapes=[None])

iterator = dataset.make_one_shot_iterator()
next_element = iterator.get_next()

print(sess.run(next_element))  # ==> [[0, 0, 0], [1, 0, 0], [2, 2, 0], [3, 3, 3]]
print(sess.run(next_element))  # ==> [[4, 4, 4, 4, 0, 0, 0],
                               #      [5, 5, 5, 5, 5, 0, 0],
                               #      [6, 6, 6, 6, 6, 6, 0],
                               #      [7, 7, 7, 7, 7, 7, 7]]

Dataset.padded_batch()转换允许为每个组件的每个维度设置不同的填充(padding),并且它可以是可变长度的(在上面的示例中由None表示)或恒定长度。 也可以重写填充值(默认为0)。


IV. 训练流程

1. 处理多epoches

Dataset API提供了两种主要方法来处理相同数据的多个epoches。

在多个epoches迭代数据集的最简单方法是使用Dataset.repeat()。 例如要创建一个重复10个epoches输入的数据集:

filenames = ["/var/data/file1.tfrecord", "/var/data/file2.tfrecord"]
dataset = tf.data.TFRecordDataset(filenames)
dataset = dataset.map(...)
dataset = dataset.repeat(10)
dataset = dataset.batch(32)

应用不带参数的Dataset.repeat()将无限地重复输入。 Dataset.repeat() 可以不用指示一个epoch的结束和下一个epoch的开始的前提下将其参数连接(concatenate)起来。
如果想在每个epoch结束时收到一个信号,可以编写一个训练循环,捕捉数据集末尾的tf.errors.OutOfRangeError ,这样可以收集一些统计信息(例如验证错误)。

filenames = ["/var/data/file1.tfrecord", "/var/data/file2.tfrecord"]
dataset = tf.data.TFRecordDataset(filenames)
dataset = dataset.map(...)
dataset = dataset.batch(32)
iterator = dataset.make_initializable_iterator()
next_element = iterator.get_next()

# Compute for 100 epochs.
for _ in range(100):
  sess.run(iterator.initializer)
  while True:
    try:
      sess.run(next_element)
    except tf.errors.OutOfRangeError:
      break

  # [Perform end-of-epoch calculations here.]

2. 随机乱序(shuffle)输入数据

Dataset.shuffle()使用与tf.RandomShuffleQueue类似的算法对输入数据集进行随机乱序排列:保持一个固定大小的缓冲区,并从该缓冲区随机选择下一个元素。

filenames = ["/var/data/file1.tfrecord", "/var/data/file2.tfrecord"]
dataset = tf.data.TFRecordDataset(filenames)
dataset = dataset.map(...)
dataset = dataset.shuffle(buffer_size=10000)
dataset = dataset.batch(32)
dataset = dataset.repeat()

3. 使用高级别的APIs

tf.train.MonitoredTrainingSession API简化了在分布式设置中运行TensorFlow的许多方面。 MonitoredTrainingSession使用tf.errors.OutOfRangeError表示训练已完成,因此要将其与Dataset API结合使用,建议使用Dataset.make_one_shot_iterator()。 举例如下:

filenames = ["/var/data/file1.tfrecord", "/var/data/file2.tfrecord"]
dataset = tf.data.TFRecordDataset(filenames)
dataset = dataset.map(...)
dataset = dataset.shuffle(buffer_size=10000)
dataset = dataset.batch(32)
dataset = dataset.repeat(num_epochs)
iterator = dataset.make_one_shot_iterator()

next_example, next_label = iterator.get_next()
loss = model_function(next_example, next_label)

training_op = tf.train.AdagradOptimizer(...).minimize(loss)

with tf.train.MonitoredTrainingSession(...) as sess:
  while not sess.should_stop():
    sess.run(training_op)

要在tf.estimator.Estimatorinput_fn中使用Dataset,依然推荐使用Dataset.make_one_shot_iterator()。 例如:

def dataset_input_fn():
  filenames = ["/var/data/file1.tfrecord", "/var/data/file2.tfrecord"]
  dataset = tf.data.TFRecordDataset(filenames)

  # Use `tf.parse_single_example()` to extract data from a `tf.Example`
  # protocol buffer, and perform any additional per-record preprocessing.
  def parser(record):
    keys_to_features = {
        "image_data": tf.FixedLenFeature((), tf.string, default_value=""),
        "date_time": tf.FixedLenFeature((), tf.int64, default_value=""),
        "label": tf.FixedLenFeature((), tf.int64,
                                    default_value=tf.zeros([], dtype=tf.int64)),
    }
    parsed = tf.parse_single_example(record, keys_to_features)

    # Perform additional preprocessing on the parsed data.
    image = tf.decode_jpeg(parsed["image_data"])
    image = tf.reshape(image, [299, 299, 1])
    label = tf.cast(parsed["label"], tf.int32)

    return {"image_data": image, "date_time": parsed["date_time"]}, label

  # Use `Dataset.map()` to build a pair of a feature dictionary and a label
  # tensor for each example.
  dataset = dataset.map(parser)
  dataset = dataset.shuffle(buffer_size=10000)
  dataset = dataset.batch(32)
  dataset = dataset.repeat(num_epochs)
  iterator = dataset.make_one_shot_iterator()

  # `features` is a dictionary in which each value is a batch of values for
  # that feature; `labels` is a batch of labels.
  features, labels = iterator.get_next()
  return features, labels

你可能感兴趣的:(TensorFlow,Deep,Learning,数据集)