内容摘自 --《TensorFlow 实战Google深度学习框架》 第二版

  • 1.4 版本以后,数据集框架从tf.contrib.data 迁移到 tf.data,它被TensorFlow推荐作为输入数据的首选框架。

由于训练数据通常无法全部写入内存中,从数据集中读取数据时需要使用一个迭代器(iterator)按顺序进行读取。与队列相似,数据集也是计算图上的一个节点。

input_data = [1,2,3,4,5]
dataset = tf.data.Dataset.from_tensor_slices(input_data)

#定义一个迭代器用于遍历数据集,由于没有用placeholder定义数据集,此处可以使用one_shot
iterator = dataset.make_one_shot_iterator()
# get_next()返回代表一个输入数据的张量
x = iterator.get_next()
y = x*x

with tf.Session() as sess:
    for i in range(len(input_data)):
        print(sess.run(y))

上述例子表明,用数据集读取数据有三个基本步骤:

  1. 定义数据集的构造方法:
    tf.data.Dataset.from_tensor_slices() 表明数据集是从一个张量中构建的,如果数据集是从文件中构建的,则需要相应调用不同的构造方法。
  2. 定义遍历器:
  • one_shot_iterator: 使用时,数据集的所有参数必须已经确定,因此不需要特别的初始化过程
  • initializable_iterator: 可以应对placeholder来初始化数据集的情况
  1. 使用get_next() 方法从遍历器中读取数据张量,作为计算图其他部分的输入。在正式项目中,训练数据通常保存在硬盘文件上
  • 在自然语言处理的任务中,训练数据通常以每一条数据的形式存在文本文件中,此时可用 TextLineDataset 来更方便地读取数据
  • 在图像相关任务中,输入数据通常以TFRecord形式存储,这是可以用TFRecordDataset来读取数据。每一个TFRecord都有自己不同的feature格式,因此在读取时,需要提供parse函数来解析所读取的TFRecord数据格式。
def parser(record):
    features = tf.parse_single_example(
        record,
        features = {
            'feat1': tf.FixedLenFeature([], tf.int64),
            'feat2': tf.FixedLenFeature([], tf.int64),
            })
        return features['feat1'], features['feat2']
 
 input_files = ["/path/to/input_file1", "/path/to/input_file2"]
 input_files = tf.placeholder(tf.string) # 具体文件路径稍后再提供
 dataset = tf.data.TFRecordDataset(input_files)
 # 使用TFRecordDataset()读出的是二进制的数据,用过map()调用parser()对二进制数据进行解析
 dataset = dataset.map(parser)
 
 #
 iterator = dataset.make_initializable_iterator()
 feat1, feat2 = iterator.get_next()
 
 
 with tf.Session() as sess:
     # 首先初始化interator,并给出input_files的值
     sess.run(iterator.inilializer, feed_dict={input_files:["/path/to/input_file1", "/path/to/input_file2"]})
     
     # 遍历所有数据一个epoch,当遍历结束时,程序会抛出OutOfRangeError
     while True:
         try:
             sess.run([feat1, feat2])
         except tf.errors.OutOfRangeError:
             break

one_shot_iteratorinitializable_iterator 能满足大多数项目的需求。除这两种以外,还有两种更灵活的迭代器: reinitializable_iteratorfeedable_iterator

  • 数据集高层操作
dataset = dataset.map(lambda x: preprocess_for_train(x, image_size, image_size, None))
# 通过map对每一条数据调用

dataset = dataset.shuffle(buffer_size) # 随机打乱数据
# shuffle算法内部使用在缓冲区中保存buffer_size条数据,每读入一条新数据,就从buffer随机抽一条# 数据输出。 buffer_size越大 随机性越强 但占用内存越多

dataset = dataset.batch(batch_size) # 将数据组合成batch
# 若iterator.get_next()返回image,label两个张量,维度[300,300],[]; batch_size为128
# 则 ---> [128,300,300], [128]

dataset = dataset.repeat(N) #数据集重复N份,一份为一个epoch
# 若在repeat前进行了shuffle操作,输出每个epoch中随机shuffle的结果并不会相同。

此外还有很多操作:concatenate() 连接两个数据集, take(N) 读取数据集前N项, skip(N) 跳过前N项, flap_map() 从多个数据集中轮流读取 等

以下给出一个用数据集方式的数据输入流程代码:

train_files = tf.train.match_filenames_once("path-to-trainfile")
test_files = tf.train.match_filenames_once("path-to-testfile")

def parser(record):
    features = tf.parse_single_example(
        record,
        features = {
            'image': tf.FixedLenFeature([], tf.int64),
            'label': tf.FixedLenFeature([], tf.int64),
            'height': tf.FixedLenFeature([], tf.int64),
            'width': tf.FixedLenFeature([], tf.int64),
            'channels': tf.FixedLenFeature([], tf.int64),
            })
    decoded_image = tf.decode_raw(features['image'], tf.uint8)
    decoded_image.set_shape([features['height'], features['width'], features['channels']])
    label = features['label']

    return decoded_image, label
    
img_size = 299
batch_size = 100
shuffle_buffer = 10000

dataset = tf.data.TFRecordDataset(train_files)
dataset = dataset.map(parser)
# 由于上一个map得到的数据集中提供了decoded_img和label两个结果,所以这个map需要提供有两个参数的函数
dataset = dataset.map(lambda image, label: (preprocess_for_train(image, img_size, img_size, None), label))
dataset = dataset.shuffle(shuffle_buffer).batch(batch_size)
NUM_EPOCHS
dataset = dataset.repeat(NUM_EPOCHS)

# tf.train.match_filenames_once方法得到的结果与placeholder的机制类似, 也需要初始化
iterator = dataset.make_initializable_iterator()
img_batch, label_batch = iterator.get_next()


learning_rate = 0.01
logit = inference(img_batch)
loss = cal_loss(logit, label_batch)
train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss)

# 接下来定义测试用的Dataset
test_dataset = tf.data.TFRecordDataset(test_files)
test_dataset = test_dataset.map(parser).map(lambda img, label: (
               tf.image.resize_images(img, [img_size, img_size]), label))
test_dataset = test_dataset.batch(batch_size)

test_iterator = test_dataset.make_initializable_iterator()
test_image_batch, test_label_batch = test_iterator.get_next()

test_logit = inference(test_image_batch)
predictions = tf.argmax(test_logit, axis=-1, output_type=tf.int32)

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer(),
             tf.local_variables_initializer())
    sess.run(iterator.initializer)
    while True:
        try: 
            sess.run(train_step)
        except tf.errors.OutOfRangeError:
            break
    sess.run(test_iterator.initializer)
    
    test_results = []
    test_labels = []
    while True:
        try: 
            pred, label = sess.run([predictions, test_label_batch])
            test_results.extend(pred)
            test_labels.extend(label)
        except tf.errors.OutOfRangeError:
            break
correct = [float(y==y_) for (y, y_) in zip(test_results, test_labels)]
accuracy = sum(correct)/len(correct)
print("Test accuracy is: {}".format(accuracy))

未完待续