1. 三种获取数据到TensorFlow程序的方法
- QueueRunner:基于队列的输入管道从TensorFlow图形开头的文件中读取数据
- Feeding:运行每一步时,Pyhon代码提供数据
- 预加载数据:TensorFlow图中的张量包含所有数据(对于小数据集)
1.1 文件读取流程
大概分为三个阶段
- 第一阶段:构造文件名队列
- 方便我们快速高效读取这组文件,先读取文件名
- 第二阶段:读取与解码
- 有读取器Reader,从上一步的文件名队列文件中以样本为单位去读取
- 第三阶段:批处理队列
- 将解码出的样本放到批处理队列中,进行后续训练
- 还需要手动开启线程使其流动起来
下面将为这三个阶段进行详细讲解与演示。
2. 构造文件名队列
将需要读取的文件的文件名放入文件名队列
- tf.train.string_input_producer(string_tensor, shuffle=True)
- string_tensor:传入含有文件名+路径的列表
- shuffle:是否打乱数据
- num_epochs:过几遍数据,默认无限过数据,直到满足设定的批处理大小为止
- return:文件队列
首先,我们先导入所需模块,本案例使用Tensorflow1.x版本进行演示,但是安装了TensorFlow2.x版本,因此,我们需要开启兼容模式,从而使其支持运行Tensorflow1.x版本的语法。
import tensorflow as tf
import tensorflow.compat.v1 as tf
tf.disable_v2_behavior()
import os
我们将想要读取的图片保存在了当前路径下的picture目录中。具体内部命名方式如下图所示:
然后,我们使用os.path.join的方法拼接一下路径与图片文件名,通过变量来接收,然后传入队列函数tf.train.string_input_producer中。
def picture_read():
"""
狗图片读取案例
"""
# 1. 构造文件名队列
file_queue = tf.train.string_input_producer(file_list)
return None
filename = os.listdir("./picture")
file_list = [os.path.join("./picture/", file) for file in filename]
#print(file_list)
picture_read()
3. 读取与解码
从队列当中读取文件内容,并进行解码操作。
3.1 读取文件内容
不同的文件类型,读取与解码的方法也会不一样。阅读器默认每次只读取一个样本。换句话说,文本文件默认一次读取一行;图片文件默认一次读取一张图片;二进制文件一次读取指定字节数(最好是一个样本的字节数)TFRecords默认一次读取一个example。
-
tf.TextLineReader:- 阅读文本文件逗号分隔值(CSV)格式,默认按行读取
- return:读取器实例
-
tf.WholeFileReader:- 以一张图片作为一个样本
- 用于读取图片文件
- return:读取器实例
-
tf.FixedLengthRecordReader(record_bytes)- 二进制文件
- 要读取每个记录是固定数量字节的二进制文件
- record_bytes:整型,指定每次读取(一个样本)的字节数,要告诉它固定字节数是多少
- return:读取器实例
-
tf.TFRecordReader:- 读取TFRecords文件
- return:读取器实例
以上所有不同类型的读取器,都有一个共同的方法
.read()。都会返回一个Tensors元组(key表示文件名,value表示读取到的这个样本内容) 由于默认只会读取一个样本,所以如果想要进行批处理,需要使用tf.train.batch或tf.train.shuffle_batch进行批处理操作,便于之后指定每批次多个样本的训练。
在构造队列代码基础上继续增加图片读取部分代码。因为我们要读取图片类型的文件,因此选择tf.WholeFileReader读取方式。
def picture_read():
"""
狗图片读取案例
"""
# 1. 构造文件名队列
file_queue = tf.train.string_input_producer(file_list)
# 2. 读取图片与解码
# 2.1 读取阶段
reader = tf.WholeFileReader()
# key是文件名 value是一张图片的原始编码形式
key, value = reader.read(file_queue)
print("key:\n", key)
print("value\n", value)
3.2 内容解码
读取不同类型的文件,也应该对读取到的不同类型的内容进行相对应的解码操作,解码成同意的Tensor格式
-
tf.decode_csv:
- 解码文本文件内容
-
tf.image.decode_jpeg(contents)
- 将JPEG编码的图像解码为uint8张量
- return:uint8张量,3-D形状[height, width, channels]
-
tf.decode_raw
- 解码二进制文件内容
- 与tf.FixedLengthRecordReader搭配使用,二进制读取为uint8类型
解码阶段,默认所有的内容都解码成tf.uint8类型,如果之后需要转换成指定类型则可使用tf.cast()进行相应转换。
使用tf.image.decode_jpeg对图片进行解码
def picture_read():
"""
狗图片读取案例
"""
# 1. 构造文件名队列
file_queue = tf.train.string_input_producer(file_list)
# 2. 读取图片与解码
# 2.1 读取阶段
reader = tf.WholeFileReader()
# key是文件名 value是一张图片的原始编码形式
key, value = reader.read(file_queue)
print("key:\n", key)
print("value\n", value)
# 2.2 解码阶段
image = tf.image.decode_jpeg(value)
print("image:\n", image)
4. 批处理
解码之后,可以直接获取默认的一个样本内容了,但是如果想要获取多个样本,需要加入到新的队列进行批处理。
-
tf.train.batch(tensors, batch_size, num_threads=1, capacity=32, name=None)- 读取指定大小(个数)的张量
- tensors:可以是包含张量的列表,批处理的内容放到列表当中传入
- batch_size:从队列中读取的批处理大小(一次要处理几个样本?)
- num_threads:进入队列的线程数(取决于电脑的物理性能)
- capacity:整数,队列中元素的最大数量
- return:tensors
-
tf.train.shuffle_batch
在批处理之前,针对本案例(狗识别)还需要添加图片resize步骤,对输入的图片的宽、高、通道数进行统一,本案例中我们将图片的宽和高都修改(resize)成200*200,通道数设定为3。此过程需要在读取阶段后进行完成,图片解码以后使用tf.image.resize_images函数进行图片尺寸修改。
- 如果只修改了图片尺寸200*200,运行程序还是会报错,我们还需要指定图片的通道数
使用.set_shape方法,对静态形状进行指定,指定图片的通道数为3,就可以了。
完成图片尺寸修改步骤后,我们就可以进行批处理阶段了,使用tf.train.batch函数进行批处理。
- 批处理量batch_size设定为100
- 线程数为1
- 容量100
- 将resize后的图片张量
image_AfterResize以列表形式传入该函数
def picture_read():
"""
狗图片读取案例
"""
# 1. 构造文件名队列
file_queue = tf.train.string_input_producer(file_list)
# 2. 读取图片与解码
# 2.1 读取阶段
reader = tf.WholeFileReader()
# key是文件名 value是一张图片的原始编码形式
key, value = reader.read(file_queue)
print("key:\n", key)
print("value\n", value)
# 2.2 解码阶段
image = tf.image.decode_jpeg(value)
print("image:\n", image)
## 2.3 统一图片形状,再放入批处理队列
# (1)图像的形状、类型修改
image_AfterResize = tf.image.resize_images(image, [200, 200]) # 先修改了宽和高
print("image_AfterResize:\n", image_AfterResize)
# (2)静态形状修改
image_AfterResize.set_shape(shape=[200,200,3]) # 再修改了维度
print("image_AfterResize:\n", image_AfterResize)
# 3. 批处理
image_batch = tf.train.batch([image_AfterResize], batch_size=100, num_threads=1, capacity=100)
print("image_batch:\n", image_batch)
最后,我们开启会话,使其能够运行,张量就能有数值了。开启会话这一步骤同样需要在我们自定义的picture_read()函数中进行。
# 4. 开启会话
with tf.Session() as sess:
# 开启线程
# 线程协调员
coord = tf.train.Coordinator()
threads = tf.train.start_queue_runners(sess=sess, coord=coord)
key_new, value_new, image_new, image_AfterResize_new, image_batch_new = sess.run([key, value, image, image_AfterResize, image_batch])
print("key_new:\n", key_new)
print("value_new:\n", value_new)
print("image_new:\n", image_new)
print("image_AfterResize_new:\n", image_AfterResize_new)
print("image_batch_new:\n", image_batch_new)
# 回收线程
coord.request_stop()
coord.join(threads)
最终调用此自定义函数,并指定好图片数据所在路径,即可运行。 部分运行结果如下图片展示:
重要张量形状展示:
尺寸修改后的图片:每一个基本单元三个元素表示一个像素点。
批处理后的图片:经过批处理,图片数据变成了四维数据,因为,100张图片组成了一个批次的新数据,而不是单张图片,因此多了一个维度。
本文正在参加「金石计划 . 瓜分6万现金大奖」