最近学习tf.data这一部分内容,赶紧查漏补缺了不会的知识点,现记录一下,以备不时之需。
内容来自(https://www.sohu.com/a/148245200_115128
) 和自己仿写的代码的整理。
首先,了解一下TensorFlow读取机制:
在TensorFlow中,为了方便管理,在内存队列前又添加了一层所谓的“文件名队列”。
为什么要添加这一层文件名队列?我们首先得了解机器学习中的一个概念:epoch。对于一个数据集来讲,运行一个epoch就是将这个数据集中的图片全部计算一遍。如一个数据集中有三张图片A.jpg、B.jpg、C.jpg,那么跑一个epoch就是指对A、B、C三张图片都计算了一遍。两个epoch就是指先对A、B、C各计算一遍,然后再全部计算一遍,也就是说每张图片都计算了两遍。
TensorFlow使用文件名队列+内存队列双队列的形式读入文件,可以很好地管理epoch。下面我们用图片的形式来说明这个机制的运行方式。如下图,还是以数据集A.jpg, B.jpg, C.jpg为例,假定我们要跑一个epoch,那么我们就在文件名队列中把A、B、C各放入一次,并在之后标注队列结束。
程序运行后,内存队列首先读入A(此时A从文件名队列中出队):
再依次读入B和C:
此时,如果再尝试读入,系统由于检测到了“结束”,就会自动抛出一个异常(OutOfRange)。外部捕捉到这个异常后就可以结束程序了。这就是TensorFlow中读取数据的基本机制。如果我们要跑2个epoch而不是1个epoch,那只要在文件名队列中将A、B、C依次放入两次再标记结束就可以了。
其次,了解一下TensorFlow读取数据机制的对应函数:
如何在TensorFlow中创建上述的两个队列呢?
对于文件名队列,我们使用tf.train.string_input_producer函数。这个函数需要传入一个文件名list,系统会自动将它转为一个文件名队列。
此外tf.train.string_input_producer还有两个重要的参数,一个是num_epochs,它就是我们上文中提到的epoch数。另外一个就是shuffle,shuffle是指在一个epoch内文件的顺序是否被打乱。若设置shuffle=False,如下图,每个epoch内,数据还是按照A、B、C的顺序进入文件名队列,这个顺序不会改变:
如果设置shuffle=True,那么在一个epoch内,数据的前后顺序就会被打乱,如下图所示:
在TensorFlow中,内存队列不需要我们自己建立,我们只需要使用reader对象从文件名队列中读取数据就可以了,具体实现可以参考下面的实战代码。
除了tf.train.string_input_producer外,我们还要额外介绍一个函数:tf.train.start_queue_runners。初学者会经常在代码中看到这个函数,但往往很难理解它的用处,在这里,有了上面的铺垫后,我们就可以解释这个函数的作用了。
在我们使用tf.train.string_input_producer创建文件名队列后,整个系统其实还是处于“停滞状态”的,也就是说,我们文件名并没有真正被加入到队列中(如下图所示)。此时如果我们开始计算,因为内存队列中什么也没有,计算单元就会一直等待,导致整个系统被阻塞。
而使用tf.train.start_queue_runners之后,才会启动填充队列的线程,这时系统就不再“停滞”。此后计算单元就可以拿到数据并进行计算,整个程序也就跑起来了,这就是函数tf.train.start_queue_runners的用处。
import tensorflow as tf
def read_data(file_queue):
reader = tf.TextLineReader(skip_header_lines=1)
key, value = reader.read(file_queue)
defaults = [[0], [0.], [0.], [0.], [0.], [0]]
cvscolumn = tf.decode_csv(value, defaults)
featurecolumn = [i for i in cvscolumn[1: -1]]
labelcolumn = cvscolumn[-1]
return tf.stack(featurecolumn), labelcolumn
def creat_pipline(file_name, batch_size, num_epochs=None):
# 传入一个list,系统会自动将它转为一个文件名队列,
# num_epochs对于一个数据集来讲,运行一个epoch
# 就是将这个数据集中的图片全部计算一遍,
# shuffle是指在一个epoch内文件的顺序是否被打乱
file_queue = tf.train.string_input_producer([file_name], num_epochs=num_epochs)
feature, label = read_data(file_queue)
min_after_dequeue = 1000
capacity = min_after_dequeue + batch_size
feature_batch, label_batch = tf.train.shuffle_batch(
[feature, label], batch_size=batch_size,
capacity=capacity, min_after_dequeue=min_after_dequeue)
return feature_batch, label_batch
x_train_batch, y_train_batch = creat_pipline('./data/iris_training.csv', 32, 1)
print('------->', x_train_batch.shape, y_train_batch.shape)
x_test, y_test = creat_pipline('./data/iris_test.csv', 32)
with tf.Session() as sess:
init_op = tf.global_variables_initializer()
local_init_op = tf.local_variables_initializer()
sess.run(init_op)
sess.run(local_init_op)
coord = tf.train.Coordinator()
threads = tf.train.start_queue_runners(coord=coord)
try:
while True:
if coord.should_stop():
break
example, label = sess.run([x_train_batch, y_train_batch])
print('训练数据:', example)
print('训练标签:', label)
print('训练数据个数:', example.shape)
print('训练标签个数:', label.shape)
except tf.errors.OutOfRangeError:
print('finish reading')
example, label = sess.run([x_test, y_test])
print('测试数据:', example)
print('测试标签:', label)
except KeyboardInterrupt:
print('程序终止运行......')
finally:
coord.request_stop()
coord.join(threads)
sess.close()