Python_ML-Day5: TensorFlow的线程队列与IO操作、TFRecords文件的存取
1.TensorFlow 队列
- 在训练样本的时候,希望读入的训练样本是有序的
- 考虑使用队列机制: 先进先出
- tf.FIFOQueue(capacity, dtypes, name='fifo_queue')
1.先进先出队列
2.参数
- capacity:整数。可能存储在此队列中的元素数量的上限
- dtypes:DType对象列表。长度dtypes必须等于每个队列元
素中的张量数,dtype的类型形状,决定了后面进队列元素形状
4.方法
- dequeue(name=None)
- enqueue(vals, name=None):
- enqueue_many(vals, name=None):vals列表或者元组
返回一个进队列操作
- size(name=None): 查看队列的元素个数
- tf.RandomShuffleQueue 随机出队列
2.队列管理器
- 创建一个QueueRunner:tf.train.QueueRunner(queue, enqueue_ops=None)
- 参数说明:
1. queue:A Queue
2. enqueue_ops:添加线程的队列操作列表,[queue1, queue2...]*2,指定两个线程
- 创建好队列管理器之后,在sess中开启
1. create_threads(sess, coord=None,start=False)
- start:布尔值,如果True启动线程;如果为False调用者
必须调用start()启动线程
- coord:线程协调器,后面线程管理需要用到
3.线程协调器
- 线程协调员,实现一个简单的机制来协调一组线程的终止
- tf.train.Coordinator()
- 方法:
1. request_stop(): 强制停止
2. should_stop() 检查是否要求停止
3. join(threads=None, stop_grace_period_secs=120) 等待线程终止
4.return:线程协调员实例
4.文件读取
- 流程
1. 构造一个文件队列
- 将输出字符串(例如文件名)输入到管道队列
- tf.train.string_input_producer(string_tensor,,shuffle=True)
- 参数说明:
1.string_tensor 含有文件名的1阶张量
2.num_epochs:过几遍数据,默认无限过数据
3.return:具有输出字符串(文件名)的队列
2. 读取队列的中的文件的内容,解码[默认只读取一个样本]
- 读取read:
1. csv文件: 读取一行
2. 二进制文件: 读取指定一个样本的bytes的字节
3. 图片文件: 按一张一张的读取
- 根据文件格式选择合适的文件阅读器,并返回一个阅读器Reader:
1. tf.TextLineReader
- 阅读文本文件。逗号分割 csv文件。默认按行读取
2. tf.FixedLengthRecordReader(record_bytes)
- 读取二进制文件,每次读取固定字节数量
- record_bytes: 整型,指定每次读取的字节数
3. tf.TFRecordReader
- 读取TfRecords文件
- Reader.read(file_queue)
1. 读取文件队列中的文件内容
2. 返回一个Tensors元组,key文件名字,value为文件内容
- 解码
1. 由于从文件中读取的是字符串,需要函数去解析这些字符串到张量
2. 将CSV转换为张量,与tf.TextLineReader搭配使用
- tf.decode_csv(records,record_defaults=None,field_delim = None,name = None)
- records:tensor型字符串,每个字符串是csv中的记录行,Reader中的Value
- field_delim:默认分割符”,”
- record_defaults:参数决定了所得张量的类型. 逗号分割的每一列的类型可能都不同,通过此参数指定. 当值为空时,也可以通过此参数指定默认值
3.将字节转换为一个数字向量表示,字节为一字符串类型的张量,与函数tf.FixedLengthRecordReader搭配使用,二进制读取为uint8格式
- tf.decode_raw(bytes,out_type,little_endian = None,name = None)
3. 批处理
- 每次读取一个样本,然后加入到一个批次
- tf.train.batch(tensors,batch_size,num_threads = 1,capacity = 32,name=None)
1. 读取指定大小(个数)的张量
2. tensors:可以是包含张量的列表
3. batch_size:从队列中读取的批处理大小 - 决定每批次取的数据条数
4. num_threads:进入队列的线程数
5. capacity:整数,队列中元素的最大数量
6. return:tensors
- tf.train.shuffle_batch(tensors,batch_size,capacity,min_after_dequeue, num_threads=1,)
1. 乱序读取指定大小(个数)的张量
2. min_after_dequeue:留下队列里的张量个数,能够保持随机打乱
4. 子线程读取文件形成批次,主线程从批次中取数据去训练模型
- 开启线程的另外一种操作
1. 收集所有图中的队列线程,并启动线程
2. tf.train.start_queue_runners(sess=None,coord=None)
- sess:所在的会话中
- coord:线程协调器
- return:返回所有线程队列
5.图片的读取
- 图片三要素: 长度,宽度,通道数
1. 长度,宽度 - 像素个数
2. 单通道 - 灰度值 / 三通道 - RGB值
- 三要素与张量的关系
1. [height,width,channels]
- 图片的基本操作
1. 每一个样本必须保持特征值数量一致
2. 所有的图片,要统一特征的数量。像素一样。改变长宽
3. 缩小图片 - tf.image.resize_image(images, size)
- images:4-D形状[batch, height, width, channels]或3-D形状的张量[height, width, channels]的图片数据
- size:1-D int32张量:new_height, new_width,图像的新尺寸
- 返回4-D格式或者3-D格式图片
- 图片读取器 tf.WholeFileReader
1. 将文件的全部内容作为值输出的读取器
2. rerurn 读取器实例
3. 方法:
- read(file_queue)
- 输出 key value
- 图片解码器
1. tf.image.decode_jpeg(contents)
- 将JPEG编码的图像解码为uint8张量
- return:uint8张量,3-D形状[height, width, channels]
2. tf.image.decode_png(contents)
- 将PNG编码的图像解码为uint8或uint16张量
- return:张量类型,3-D形状[height, width, channels]
- 图片批处理案例流程
1. 构造图片文件队列
2. 构造图片阅读器
3. 读取图片数据,解码
4. 处理图片数据,大小
6. 二进制文件读取
- 二进制数据下载地址:http://www.cs.toronto.edu/~kriz/cifar.html
- 二进制文件读取器
1. reader = tf.FixedLengthRecordReader(record_bytes)
- record_bytes: 每条记录的字节数
- 二进制文件解码
1. label_image = tf.decode_raw(value, tf.uint8)
- 案例流程
1. 构造文件队列
2. 新建二进制文件阅读器,读取文件
3. 新建二进制文件解码,解码成utf-8文件[每个utf-8的值,对应一个字节]
4. 图片切分
- 前1个字节,表示label
- 后32*32*3个字节,表示image
- tf.slice()
5.批处理
6.多线程处理
7.TFRecords文件
- TFRecords是Tensorflow设计的一种内置文件格式,是一种二进制文件,它能更好的利用内存,更方便复制和移动
- 为了将二进制数据和标签(训练的类别标签)数据存储在同一个文件中
- 文件格式:*.tfrecords
- 写入文件内容:Example协议块,类字典格式
- 存储tfrecords文件
1. 建立存储器,写入器
- tf.python_io.TFRecordWriter(path)
- path: tf文件存储路径
- return 写入器
- 方法:
1. write(record): 向文件中写入一个字符串记录record
- 此字符串record为一个序列化的example
- 使用 Example.SerializeToString()
2. close(): 关闭写入器
2. 为每个样本构造协议块Example
- 构建单个特征
1.tf.train.Feature(**options)
- **options:一个指定格式的列表
- 指定格式有Int64,Bytes,Float:
1.tf.train. Int64List(value=[int])
2.tf.train. BytesList(value=[string])
3.tf.train. FloatList(value=[float])
- value: 为具体的数值类型的值
- 注:
1. 比如图片信息:value = [img_tensor.eval().tostring()]
2. 比如label int值:value= [int(label_tensor.eval())]
- 为每个样本构建信息键值对
1. tf.train.Features(feature=None)
2. 参数说明:
- feature:字典数据,key为要保存的名字,value为tf.train.Feature实例
3. 返回值:
- return:Features类型
- 构造每个样本的Example协议块,提供给写入器写入record
1. tf.train.Example(features=None)
2. 参数说明:
- features:tf.train.Features类型的特征实例
3. 返回值:
- return:example格式协议块
3. 序列化Example
- example.SerializeToString()
- 返回可存储的序列化的Example
4. 写入
writer.write(example.SerializeToString())
- 读取tfrecords文件
1. 构建文件队列
2. 构造文件阅读器,读取example
tf.TFRecordReader()
3. 解析TFRecords的example协议内存块
- 解析一个单一的Example原型
1. tf.parse_single_example(serialized,features=None,name=None)
2. 参数说明:
- serialized:标量字符串Tensor,一个序列化的Example
- features模板:dict字典数据,键为读取的名字,值为FixedLenFeature
- return:一个键值对组成的字典,键为读取的名字
- tf.FixedLenFeature(shape,dtype)
- shape:输入数据的形状,一般不指定,为空列表
- dtype:输入数据类型,与存储进文件的类型要一致,类型只能是float32,int64,string
4. 解码: 如果读取的string,需要解码。其他不需要解码
tf.decode_raw(value,tf.unit8)
8.源码
import os
import tensorflow as tf
def fifo():
"""
模拟队列操作
:return:
"""
# 1. 定义队列
Q = tf.FIFOQueue(3, tf.float32)
en_Q = Q.enqueue_many([[0.1, 0.2, 0.3]])
# 2. 定义读取数据过程
out_dt = Q.dequeue()
de_Q = Q.enqueue(out_dt + 1)
# 3. 定义数据处理过程
# 4. 处理数据再次入队列
with tf.Session() as sess:
# 执行队列初始化
sess.run(en_Q)
# 执行队列进出操作
for i in range(100):
sess.run(de_Q)
# 训练数据
for i in range(Q.size().eval()):
print(sess.run(Q.dequeue()))
def queueRunner():
"""
队列管理器, 异步处理数据
:return:
"""
# 定义一个队列 1000
Q = tf.FIFOQueue(1000, tf.float32)
# 定义一个变量
var = tf.Variable(0.0, tf.float32)
# 变量自增 + 1
data = var.assign_add(1)
# 入队列
en_q = Q.enqueue(data)
# 定义一个队列管理器,去运行上述队列过程
qr = tf.train.QueueRunner(Q, enqueue_ops=[en_q] * 2)
# 初始化init_op
init_op = tf.global_variables_initializer()
with tf.Session() as sess:
# 初始化op
sess.run(init_op)
# 创建并开启子线程,并制定协调员 -- 开启队列管理器
coord = tf.train.Coordinator()
threads = qr.create_threads(sess, coord=coord, start=True)
# 主线程去读取数据,然后进行训练等操作
for i in range(300):
print(sess.run(Q.dequeue()))
# 协调器向你发送停止请求
coord.request_stop()
# 协调器回收资源
coord.join(threads)
def readCSVFile():
"""
读取csv文件
:return:
"""
# 1.找到文件目录,构造一个文件列表
file_name = os.listdir("./data")
file_path = [os.path.join('./data', file) for file in file_name]
file = tf.constant(file_path)
# 2.构造文件队列
queue = tf.train.string_input_producer(file, shuffle=True)
# 3.构造阅读器
reader = tf.TextLineReader()
key, value = reader.read(queue)
# 4.解码
# 每行记录分为两列,都是字符串格式,并且默认值分别为None和default
records = [["None"], ["default"]]
line1, line2 = tf.decode_csv(value, record_defaults=records)
# 5.批处理
line1_batch, line2_batch = tf.train.batch(tensors=[line1, line2], batch_size=9, num_threads=1, capacity=9)
init_op = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init_op)
coord = tf.train.Coordinator()
threads = tf.train.start_queue_runners(sess, coord=coord)
print(sess.run([line1_batch]))
coord.request_stop()
coord.join(threads)
def readImags():
"""
读取图片文件
:return:
"""
# 1.找到文件目录,构造一个文件列表
file_name = os.listdir("./image")
file_path = [os.path.join('./image', file) for file in file_name]
file = tf.constant(file_path)
# 2.构造文件队列
queue = tf.train.string_input_producer(file, shuffle=True)
# 3.构造阅读器
reader = tf.WholeFileReader()
key, value = reader.read(queue)
# 4.解码
img = tf.image.decode_jpeg(value)
# 处理图片的大小
img_resize = tf.image.resize(img, [200, 200])
# 设置形状固定[200,200,3]
img_resize.set_shape([200, 200, 3])
# 5.批处理
img_batch = tf.train.batch(tensors=[img_resize], batch_size=10, num_threads=1, capacity=10)
print(img_batch)
init_op = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init_op)
coord = tf.train.Coordinator()
threads = tf.train.start_queue_runners(sess, coord=coord)
print(sess.run([img_batch]))
coord.request_stop()
coord.join(threads)
# 定义命令行参数
FLAGS = tf.app.flags.FLAGS
tf.app.flags.DEFINE_string("cifar_dir", "./bin", "文件的目录")
tf.app.flags.DEFINE_string("tfrecords_dir", "./tfrecords/a.tfrecords", "tfrecords文件的目录")
class CifarRead(object):
"""
完成读取二级制文件,写进tfRecords
"""
def __init__(self, pathlist):
self.path_list = pathlist
self.bytes = 32 * 32 * 3 + 1
def getRecord(self):
# 创建队列
queue = tf.train.string_input_producer(self.path_list)
# 读取数据
reader = tf.FixedLengthRecordReader(record_bytes=self.bytes)
key, value = reader.read(queue)
# 解码器
label_image = tf.decode_raw(value, tf.uint8)
print(label_image)
# 切分图片和目标值
label = tf.cast(tf.slice(label_image, [0], [1]), tf.int32)
image = tf.slice(label_image, [1], [self.bytes - 1])
print(label)
print(image)
reshape = tf.reshape(image, [32, 32, 3])
print(reshape)
# 批处理数据
img_batch, label_batch = tf.train.batch([reshape, label], batch_size=10, num_threads=1, capacity=10)
print(img_batch)
print(label_batch)
return img_batch, label_batch
def write_to_tfrecords(self, img_batch, label_batch, batch_size=10):
"""
将批量获取的数据,存储为tfrecords文件
:param img_batch: 批量的图片信息
:param label_batch: 批量的目标值信息
:return: 返回要存储的tftrcord信息
"""
# 1. 创建一个写入器
writer = tf.python_io.TFRecordWriter(FLAGS.tfrecords_dir)
# 2. 创建要写入的example实例
# 2.1 因为是批次数据,所以要分割[因为有eval所以要在sess中运行]
for i in range(batch_size):
image = img_batch[i].eval().tostring()
label = int(label_batch[i].eval()[0])
example = tf.train.Example(features=tf.train.Features(
feature={
"image": tf.train.Feature(bytes_list=tf.train.BytesList(value=[image])),
"label": tf.train.Feature(int64_list=tf.train.Int64List(value=[label]))
}
))
# 3. 序列化,写入
writer.write(example.SerializeToString())
# 写完关闭
writer.close()
def readTfrecords(self):
# 构造文件队列
queue = tf.train.string_input_producer([FLAGS.tfrecords_dir])
# 创建阅读器
reader = tf.TFRecordReader()
# 读取文件
key, value = reader.read(queue)
# 读取的value 是string, example.SerializeToString(),所以需要反序列化parse_single_example
features = tf.parse_single_example(value, features={
"image": tf.FixedLenFeature(shape=[], dtype=tf.string),
"label": tf.FixedLenFeature(shape=[], dtype=tf.int64)
})
# 读取的image是string, 所以,需要解码成utf-8
image = tf.decode_raw(features["image"], tf.uint8)
label = features["label"]
print(image, label)
# 固定图片的形状,方便后续的处理
image_reshape = tf.reshape(image, [32, 32, 3])
# 进行批处理
img_batch, label_batch = tf.train.batch([image_reshape, label], batch_size=5, num_threads=1, capacity=10)
return img_batch, label_batch
if __name__ == '__main__':
# 1. 找到文件,放入列表
file_list = os.listdir(FLAGS.cifar_dir)
path_list = [os.path.join(FLAGS.cifar_dir, file) for file in file_list if file[:5] == "data_"]
print(path_list)
cr = CifarRead(path_list)
img_batch, label_batch = cr.readTfrecords()
with tf.Session() as sess:
coord = tf.train.Coordinator()
threads = tf.train.start_queue_runners(sess, coord=coord)
#
print(sess.run([label_batch]))
print(sess.run([label_batch]))
print(sess.run([label_batch]))
coord.request_stop()
coord.join(threads)