数据读取处理

1. 文件读取

以下用到的队列都为tf.train.QueueRunner对象,每个 QueueRunner 都负责一个阶段,tf.train.start_queue_runners()函数会要求图中的每个 QueueRunner 启动它的运行队列操作的线程。因此需要在会话中进行线程操作结束后需回收线程)。

tf.train.Coordinator()线程协调器,通过它来对线程进行管理和协调;创建一个线程协调器对象coord = tf.train.Coordinator()coord.request_stop()请求停止coord.join(threads= )回收线程

tf.train.start_queue_runners(sess=,coord=)收集图中所有的队列线程,默认同时启动线程。sess:所在的会话,coord:线程协调器。

1.1 构造文件名队列

将需要读取的文件的文件名(一般为相对路径)放入文件队列。

tf.train.string_input_producer(string_tensor,num_epochs=None,shuffle=True)

  • string_tensor:相对路径的一阶张量,一般用[相对路径]传入即可。

  • num_epochs:表示迭代的次数,默认为 None 无限次遍历 tensor 列表。

  • shuffle:bool 类型,表示是否打乱样本的顺序,默认为 True 即生成的样本顺序被打乱了,在批处理的时候不需要再次打乱样本,使用 tf.train.batch函数就可以了;如果shuffle=False,就需要在批处理时候使用 tf.train.shuffle_batch函数打乱样本。

将文件名列表交给tf.train.string_input_producer函数。string_input_producer来生成一个先入先出的队列,文件阅读器会需要它们来取数据。string_input_producer 提供的可配置参数来设置文件名乱序和最大的训练迭代数,QueueRunner会为每次迭代(epoch)将所有的文件名加入文件名队列中,如果shuffle=True的话,会对文件名进行乱序处理。这一过程是比较均匀的,因此它可以产生均衡的文件名队列。

这个QueueRunner工作线程是独立于文件阅读器的线程,因此乱序和将文件名推入到文件名队列这些过程不会阻塞文件阅读器运行。根据你的文件格式,选择对应的文件阅读器,然后将文件名队列提供给阅读器的read方法。阅读器的read方法会输出一个键来表征输入的文件和其中的数据,同时得到一个字符串标量,这个字符串标量可以被一个或多个解析器,或者转换操作将其解码为张量并且构造成为样本。

1.2 读取与解码

1.2.1 文件读取器

根据文件的内容格式选择相应的文件读取器

tf.TextLineReader:读取文本文件逗号分隔值(CSV)格式,默认一次读取一行。

tf.WholeFileReader:读取图片文件,默认一次读取一张图片。

tf.FixedLengthRecordReader(record_bytes):读取二进制文件(每个记录是固定数量字节的二进制文件),record_bytes指定每次读取一个样本的字节数。

tf.TFRecordReader:读取TFRecords文件。

1.2.2文件内容解码器

针对不同格式的内容进行相对应的解码操作,解码为统一Tensor格式。

tf.decode_csv(): 解码文本文件内容,将CSV转换为张量,与tf.TextLineReader搭配使用。
tf.image.decode_jpeg andtf.image.decode_png(contents即样本)
将JPEG编码(PNG编码)的图像解码为uint8张量,3-D形状:[height, width, channels],与tf.WholeFileReader搭配使用。
tf.decode_raw()︰解码二进制文件内容,与tf.FixedLengthRecordReader搭配使用,二进制读取为uint8类型。

解码阶段默认所有内容都解码为unit8类型,uint8保存节省空间,若想提高计算精度则可通过tf.cast()进行类型转换为float32。

1.3 批处理

tf.train.batch(tensors, batch_size, num_threads = 1, capacity = 32,name=None)

读取指定大小(个数)的张量, 单张图片 [height width channels] 多张图片 [batch height width channels]。

  • tensors:可以是包含张量的列表,批处理的内容放到列表当中。
  • batch_size:从队列中读取的批处理大小。
  • num_threads:进入队列的线程数。
  • capacity:整数,队列中元素的最大数量。

tf.train.shuffle_batch(tensors,batch_size,capacity,min_after_dequeue,num_threads=1) : 乱序读取指定大小(个数)的张量。

2. 图片读取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
import tensorflow as tf

def picture_read(file_list):
# 1. 构建文件名队列
file_queue = tf.train.string_input_producer(file_list)
print('file_queue:',file_queue)
# 2. 文件读取与解码
reader = tf.WholeFileReader() # 图片读取器
# key为文件名,value为一张图片的原始编码形式
key, value = reader.read(file_queue)
print('key', key)
print('value', value)
image = tf.image.decode_jpeg(value) # 读取的是什么格式,就decode什么格式
print('image:',image) # 形状不定
# 图片形状、类型修改
image_resize = tf.image.resize_images(image, [200, 200])
print('image_resize:',image_resize)
image_resize.set_shape(shape=[200, 200, 3])
print('image_resize',image_resize)

# 3. 批处理
image_batch = tf.train.batch([image_resize], batch_size=100, num_threads=1, capacity=100)
print('image_batch:',image_batch)

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_resize_new,image_batch_new = sess.run([key,value,image,image_resize,image_batch])
print('key_new:\n',key_new)
print('value_new:\n',value_new)
print('image_new:\n',image_new)
print('image_resize_new:\n',image_resize_new)
print('image_batch_new:\n',image_batch_new)

# 停止线程 回收线程
coord.request_stop()
coord.join(threads)
return None

if __name__ == '__main__':
file_name = os.listdir('./dog') # 返回指定路径下的文件和文件夹列表
# 路径 + 文件名 形成列表
file_list = [os.path.join('./dog', file) for file in file_name]
# print(file_name)
# print(file_list)
picture_read(file_list)

3. CIFAR10二进制读取

CIFAR10数据集官网: https://www.cs.toronto.edu/~kriz/cifar.html

数据集分为五个训练批次和一个测试批次,每个批次有 10000 张图像。测试批次恰好包含来自每个类别的 1000 个随机选择的图像。训练批次包含随机顺序的剩余图像,但一些训练批次可能包含来自一个类的图像多于另一个。在它们之间,训练批次恰好包含来自每个类别的 5000 张图像。

这些文件中的每一个格式如下,数据中每个样本包含了特征值和目标值:

cifar10 的特征值是 image(3072Bytes) 目标值是 label 0-9 (1Bytes)。

<1×标签><3072×像素>

<1×标签><3072×像素>
**第一个字节是第一个图像的标签,它是一个0-9范围内的数字。接下来的3072个字节是图像像素的值。前1024个字节是红色通道值,下1024个绿色,最后1024个蓝色。**值以行优先顺序存储,因此前32个字节是图像第一行的红色通道值。每个文件都包含10000个这样的3073字节的“行"图像,但没有任何分隔行的限制。因此每个文件应该完全是30730000字节长。
即一个样本的形式为: 1+1024r+1024g+1024b=3073个字节

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
import tensorflow as tf

class CIFAR10():
def __init__(self):
# 初始化操作
self.height = 32
self.width = 32
self.channels = 3
# 标签和像素 字节数
self.image_bytes = self.height * self.width * self.channels
self.label_bytes = 1
self.all_bytes = self.label_bytes + self.image_bytes

def read_and_decode(self,file_list):
# 1. 构建文件名队列
file_queue = tf.train.string_input_producer(file_list)
# 2. 读取与解码
reader = tf.FixedLengthRecordReader(self.all_bytes)
key,value = reader.read(file_queue)
print('key:',key)
print('value:',value)
decode = tf.decode_raw(value,tf.uint8)
print('decode:',decode) # 解码后为一维 标签和通道并在一起(第一位标签)

# 因此需要 将目标值(标签)和特征值(通道)切片分开
# tf.slice( tensor , 初始索引 , 个数 )
label = tf.slice(decode,[0],[self.label_bytes])
image = tf.slice(decode,[self.label_bytes],[self.image_bytes])
print('label:',label)
print('image:',image)

image_reshaped = tf.reshape(image,shape = [self.channels,self.height,self.width])
print('image_reshaped:',image_reshaped)

# 解码图像由shape为[height, width, channels] 的3 - Duint8张量表示。
image_transposed = tf.transpose(image_reshaped,[1,2,0])
print('image_transposed:',image_transposed)

# uint8转为float32 提高精度
image_cast = tf.cast(image_transposed,tf.float32)
print('image_cast:',image_cast)

# 3. 批处理
label_batch,image_batch = tf.train.batch([label,image_cast],batch_size = 100,num_threads = 1,capacity = 100)
print('label_batch:',label_batch)
print('image_batch:',image_batch)

with tf.Session() as sess:
coord = tf.train.Coordinator()
threads = tf.train.start_queue_runners(sess =sess,coord = coord)
key_new, value_new, decode_new, label_new, image_new, image_reshaped_new, image_transposed_new, label_batch_new, image_batch_new = sess.run([key, value, decode, label, image, image_reshaped, image_transposed, label_batch, image_batch])
print('key_new: ', key_new)
print('value_new: ', value_new)
print('decode_new', decode_new)
print('label_new', label_new)
print('image_new', image_new)
print('image_reshaped_new\n', image_reshaped_new)
print('image_transposed_new\n', image_transposed_new)
print('label_batch_new\n', label_batch_new)
print('image_batch_new\n', image_batch_new)

# 停止线程 回收线程
coord.request_stop()
coord.join(threads)

return label_batch_new,image_batch_new

if __name__ == '__main__':
# 返回指定路径下的文件和文件夹列表
file_name = os.listdir('./cifar-10-batches-bin')
print('file_name:',file_name)
# 路径 + 文件名 if file[-3:] == 'bin': 后缀bin的文件
file_list = [os.path.join('./cifar-10-batches-bin',file) for file in file_name if file[-3:] == 'bin']
print('file_list:',file_list)

CIFAR10 = CIFAR10()
label_batch,image_batch = CIFAR10.read_and_decode(file_list) # CIFAR10二进制数据读取

4. TFRecords

TFRecords 是 Tensorflow 设计的一种内置文件格式,是一种二进制文件,它能更好的利用内存,更方便复制和移动。将二进制数据和标签(训练的类别标签)数据存储在同一个文件中,即特征值和目标值是绑定在一起的。

4.1 TFRecords存储

  1. 建立TFRecord存储器:tf.python_io.TFRecordWriter(path)

    • path: TFRecords文件的路径
    • 方法:
      • write(record):向文件中写入一个字符串记录
      • close():关闭文件写入器(通常采用with文件管理器就无需close了)
    • 注:字符串是一个序列化的Example:Example.SerializeToString()
    • Example.SerializeToString() 将 example 序列化到本地
  2. 构造每个样本的Example协议块

    • tf.train.Example(features=None) 写入tfrecords文件

      • features:tf.train.Features 类型的特征实例
      • return:example格式协议块
    • tf.train.Features(feature=None) 构建每个样本的信息键值对

      • features:字典数据,key为要保存的名字,value为tf.train.Feature实例
      • return:Features类型
    • tf.train.Feature(**options)

      • **options:例如:
        bytes_list = tf.train. BytesList(value=[Bytes]) 字节
        int64_list = tf.train. Int64List(value=[Value]) 整型

        float_list = tf.train.FloatList(value=[value]) 浮点型

  3. 循环将数据填入Example协议内存块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
import tensorflow as tf

def write_to_tfrecords(self,label_batch,image_batch):
# 将样本的特征值和目标值写入tfrecords
# print(label_batch.size) # 100
with tf.python_io.TFRecordWriter('./cifar10.tfrecords') as writer:
# 循环样本数 构造example对象 序列化写入文件
for i in range(label_batch.size):
image = image_batch[i].tostring() # 图片序列化
label = label_batch[i][0] # 取出一维数组的值
# print('image:',image)
# print('label:',label)
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]))
}))
# 将序列化后的example 写入cifar10.tfrecords文件中
writer.write(example.SerializeToString())

return None

if __name__ == '__main__':
# 返回指定路径下的文件和文件夹列表
file_name = os.listdir('./cifar-10-batches-bin')
print('file_name:',file_name)
# 路径 + 文件名 if file[-3:] == 'bin': 后缀bin的文件
file_list = [os.path.join('./cifar-10-batches-bin',file) for file in file_name if file[-3:] == 'bin']
print('file_list:',file_list)

CIFAR10 = CIFAR10()
# label_batch,image_batch = CIFAR10.read_and_decode(file_list) # CIFAR10二进制数据读取
CIFAR10.write_to_tfrecords(label_batch,image_batch) # tfrecords文件存储

4.2 TFRecords读取

读取方法跟文件读取流程基本相同,只是在读取和解码阶段时需要有个解析TFRecords的example协议内存块的过程

  • tf.parse_single_example(serialized,features=None,name=None)
    • 解析一个单一的Example原型
    • serialized:标量字符串Tensor,一个序列化的Example
    • features:dict字典数据,键为读取的名字,值为FixedLenFeature
    • return:一个键值对组成的字典,键为读取的名字
  • tf.FixedLenFeature(shape,dtype)
    • shape:输入数据的形状,一般不指定,为空列表
    • dtype:输入数据类型,与存储进文件的类型要一致,类型只能是float32,int64,string
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
import tensorflow as tf

def read_tfrecords(self,file_list):
# 1. 构造文件名队列
file_queue = tf.train.string_input_producer(file_list)

# 2. 读取与解码
reader = tf.TFRecordReader()
key,value = reader.read(file_queue)
# 读取tfrecords文件 需要解析example
feature = tf.parse_single_example(value,features={
'image':tf.FixedLenFeature([],tf.string),
'label':tf.FixedLenFeature([],tf.int64)
})
# 取出两个键值所对应的值
image = feature['image']
label = feature['label']
print('image:',image)
print('label:',label)

# 解码
image_decoded = tf.decode_raw(image,tf.uint8)
print('image_decoded:',image_decoded)
# print(image_decoded.shape)
# 改变形状
image_reshaped = tf.reshape(image_decoded,shape = [64,64,self.channels])
print('image_reshaped:',image_reshaped)

# 批处理
image_batch,label_batch = tf.train.batch([image_reshaped,label],batch_size = 100,num_threads = 1,capacity = 100)
print('image_batch:',image_batch)
print('label_batch:',label_batch)
with tf.Session() as sess:
# 开启线程
coord = tf.train.Coordinator()
threads = tf.train.start_queue_runners(sess=sess,coord=coord)

image_new,label_new,image_decoded_new,image_reshaped_new,image_batch_new,label_batch_new= sess.run([image,label,image_decoded,image_reshaped,image_batch,label_batch])
print('image_new:',image_new)
print('label_new:',label_new)
print('image_decoded_new:',image_decoded_new)
print('image_reshaped_new:',image_reshaped_new)
print('image_batch_new:',image_batch_new)
print('label_batch_new:',label_batch_new)

# 停止线程 回收线程
coord.request_stop()
coord.join(threads)

return None

if __name__ == '__main__':
# # 返回指定路径下的文件和文件夹列表
# file_name = os.listdir('./cifar-10-batches-bin')
# print('file_name:',file_name)
# # 路径 + 文件名 if file[-3:] == 'bin': 后缀bin的文件
# file_list = [os.path.join('./cifar-10-batches-bin',file) for file in file_name if file[-3:] == 'bin']
# print('file_list:',file_list)

CIFAR10 = CIFAR10()
# label_batch,image_batch = CIFAR10.read_and_decode(file_list) # CIFAR10二进制数据读取
# CIFAR10.write_to_tfrecords(label_batch,image_batch) # tfrecords文件存储

# tfrecords文件读取
path = ['./cifar10.tfrecords']
CIFAR10.read_tfrecords(path)

参考资料

(43条消息) 黑马程序员3天带你玩转Python深度学习TensorFlow框架学习笔记_wisdom_zhe的博客-CSDN博客

【学习笔记】tensorflow图片读取 - coder-qi - 博客园 (cnblogs.com)

【学习笔记】tensorflow文件读取 - coder-qi - 博客园 (cnblogs.com)

【学习笔记】tensorflow队列和线程 - coder-qi - 博客园 (cnblogs.com)