1、数据集生成读取文件(mnist_generateds.py)
tfrecords 文件
1)tfrecords:是一种二进制文件,可先将图片和标签制作成该格式的文件。使用 tfrecords 进行数据读取,会提高内存利用率。
2)tf.train.Example: 用来存储训练数据。训练数据的特征用键值对的形式表示。如:‘ img_raw ’ :值 ‘ label ’ :值 值是 Byteslist/FloatList/Int64List
3)SerializeToString( ):把数据序列化成字符串存储。
首先生成 tfrecords 文件 :
1)将数据集的相关路径定义好
2)读训练集和测试集;
读文件解析
a:先读入文件名,路径
b:新建一个writer,计数次数
c: 在open函数中默认为只读形式打开label_path,readlines() 方法用于读取所有行(直到结束符 EOF)并返回列表,该列表可以由 Python 的 for... in ... 结构进行处理。如果碰到结束符 EOF 则返回空字符串。
d:for 循环遍历每张图和标签
f:
example = tf.train.Example(features=tf.train.Features(feature={ 'img_raw': tf.train.Feature(bytes_list=tf.train.BytesList(value=[img_raw])), 'label': tf.train.Feature(int64_list=tf.train.Int64List(value=labels)) }))
这段代码将在tf.train.examle函数中讲解
e: writer.write(example.SerializeToString()) # 把 example 进行序列化
image_train_path='./data/mnist_data_jpg/mnist_train_jpg_60000/' label_train_path='./data/mnist_data_jpg/mnist_train_jpg_60000.txt' tfRecord_train='./data/mnist_train.tfrecords' image_test_path='./data/mnist_data_jpg/mnist_test_jpg_10000/' label_test_path='./data/mnist_data_jpg/mnist_test_jpg_10000.txt' tfRecord_test='./data/mnist_test.tfrecords' data_path='./data' resize_height = 28 resize_width = 28 #生成tfrecords文件 def write_tfRecord(tfRecordName, image_path, label_path): #新建一个writer writer = tf.python_io.TFRecordWriter(tfRecordName) num_pic = 0 f = open(label_path, 'r') contents = f.readlines() f.close() #循环遍历每张图和标签 for content in contents: value = content.split() img_path = image_path + value[0] img = Image.open(img_path) img_raw = img.tobytes() labels = [0] * 10 labels[int(value[1])] = 1 #把每张图片和标签封装到example中 example = tf.train.Example(features=tf.train.Features(feature={ 'img_raw': tf.train.Feature(bytes_list=tf.train.BytesList(value=[img_raw])), 'label': tf.train.Feature(int64_list=tf.train.Int64List(value=labels)) })) #把example进行序列化 writer.write(example.SerializeToString()) num_pic += 1 print ("the number of picture:", num_pic) #关闭writer writer.close() print("write tfrecord successful") def generate_tfRecord(): isExists = os.path.exists(data_path) if not isExists: os.makedirs(data_path) print 'The directory was created successfully' else: print 'directory already exists' write_tfRecord(tfRecord_train, image_train_path, label_train_path) write_tfRecord(tfRecord_test, image_test_path, label_test_path)
解析tfrecords文件:
先看一下对应的路径文件名:
image_train_path='./data/mnist_data_jpg/mnist_train_jpg_60000/' label_train_path='./data/mnist_data_jpg/mnist_train_jpg_60000.txt' tfRecord_train='./data/mnist_train.tfrecords' image_test_path='./data/mnist_data_jpg/mnist_test_jpg_10000/' label_test_path='./data/mnist_data_jpg/mnist_test_jpg_10000.txt' tfRecord_test='./data/mnist_test.tfrecords' data_path='./data'
def main(): generate_tfRecord() (1)
def generate_tfRecord(): (2)
isExists = os.path.exists(data_path) if not isExists: os.makedirs(data_path) print 'The directory was created successfully' else: print 'directory already exists'
def get_tfrecord(num, isTrain=True): (1)
if isTrain:
tfRecord_path = tfRecord_train
else:
tfRecord_path = tfRecord_test
img, label = read_tfRecord(tfRecord_path) (2)
def read_tfRecord(tfRecord_path): (3)
filename_queue = tf.train.string_input_producer([tfRecord_path], shuffle=True) #新建一个reader reader = tf.TFRecordReader()
上面用颜色标记了一些参数的传递情况;
下面从主函数谈起:
main函数调用了generate_tfRecord()函数,在该函数中使用write_tfRecord()写入相关数据集到tfRecord中;
get_tfrecord函数获取被写入的数据集的tfRecord的地址tfRecord_path = tfRecord_train (tfRecord_path = tfRecord_test ),使用read_tfRecord()函数读取信息。
在read_tfRecord()函数中;
1)filename_queue = tf.train.string_input_producer([tfRecord_path])
tf.train.string_input_producer( string_tensor,
num_epochs=None,
shuffle=True,
seed=None,
capacity=32,
shared_name=None,
name=None,
cancel_op=None)
该函数会生成一个先入先出的队列,文件阅读器会使用它来读取数据。
参数说明:string_tensor: 存储图像和标签信息的 TFRecord 文件名列表
num_epochs: 循环读取的轮数(可选)
shuffle:布尔值(可选),如果为 True,则在每轮随机打乱读取顺序
seed:随机读取时设置的种子(可选)
capacity:设置队列容量
shared_name:(可选) 如果设置,该队列将在多个会话中以给定名称共享。所有具有此队列的设备都可以通过 shared_name 访问它。在分布式设置中使用这种方法意味着每
个名称只能被访问此操作的其中一个会话看到。
name:操作的名称(可选)
cancel_op:取消队列(None)
2)reader = tf.TFRecordReader() #新建一个 reader
3)_, serialized_example = reader.read(filename_queue)
features = tf.parse_single_example(serialized_example,features={
'img_raw': tf.FixedLenFeature([ ], tf.string) ,
'label': tf.FixedLenFeature([10], tf.int64)})
#把读出的每个样本保存在 serialized_example 中进行解序列化,标签和图片的键名应该和制作 tfrecords 的键名相同,其中标签给出几分类。
tf.parse_single_example(serialized,
features,
name=None,
example_names=None)
该函数可以将 tf.train.Example 协议内存块(protocol buffer)解析为张量。
参数说明:serialized: 一个标量字符串张量
features: 一个字典映射功能键 FixedLenFeature 或 VarLenFeature值,也就是在协议内存块中储存的
name:操作的名称(可选)
example_names: 标量字符串联的名称(可选)
4)img = tf.decode_raw(features['img_raw'], tf.uint8)
#将 img_raw 字符串转换为 8 位无符号整型
5)img.set_shape([784]) #将形状变为一行 784 列
6)img = tf.cast(img, tf.float32) * (1. / 255) #变成 0 到 1 之间的浮点数
7)label = tf.cast(features['label'], tf.float32)#把标签列表变为浮点数
8)return image,label #返回图片和标签(跳回到 get_tfrecord)
9) tf.train.shuffle_batch( tensors,
batch_size,
capacity,
min_after_dequeue,
num_threads=1,
seed=None,
enqueue_many=False,
shapes=None,
allow_smaller_final_batch=False,
shared_name=None,
name=None)
这个函数随机读取一个 batch 的数据。
参数说明:tensors: 待乱序处理的列表中的样本(图像和标签)
batch_size: 从队列中提取的新批量大小
capacity:队列中元素的最大数量
min_after_dequeue: 出队后队列中的最小数量元素,用于确保元素的混合级别
num_threads: 排列 tensors 的线程数
seed:用于队列内的随机洗牌
enqueue_many: tensor 中的每个张量是否是一个例子
shapes: 每个示例的形状
allow_smaller_final_batch: (可选)布尔值。 如果为 True,则在队列中剩余数量不足时允许最终批次更小。
shared_name:(可选)如果设置,该队列将在多个会话中以给定名称共享。
name:操作的名称(可选)
10)return img_batch,label_batch #返回的图片和标签为随机抽取的 batch_size 组
2.反向传播文件修改图片标签获取的接口(mnist_backward.py)
关键操作:利用多线程提高图片和标签的批获取效率 方法:将批获取的操作放到线程协调器开启和关闭之间
开启线程协调器:
coord = tf.train.Coordinator( )
threads = tf.train.start_queue_runners(sess=sess, coord=coord)
关闭线程协调器:
coord.request_stop( )
coord.join(threads)
注解:
tf.train.start_queue_runners( sess=None,
coord=None,
daemon=True,
start=True,
collection=tf.GraphKeys.QUEUE_RUNNERS)
这个函数将会启动输入队列的线程,填充训练样本到队列中,以便出队操作可以从队列中拿到样本。这种情况下最好配合使用一个 tf.train.Coordinator ,这
样可以在发生错误的情况下正确地关闭这些线程。
参数说明:sess:用于运行队列操作的会话。 默认为默认会话。
coord:可选协调器,用于协调启动的线程。
daemon: 守护进程,线程是否应该标记为守护进程,这意味着它们不会阻止程序退出。
start:设置为 False 只创建线程,不启动它们。
collection:指定图集合以获取启动队列的 GraphKey。默认为GraphKeys.QUEUE_RUNNERS。
与之前学习代码的区别
1)TEST_NUM=10000
之前:用 mnist.test.num_examples 表示总样本数;
现在:要手动给出测试的总样本数,这个数是 1 万。
2)image_batch, label_batch=mnist_generateds.get_tfrecord(TEST_NUM, isTrain=False)
之前:用 mnist.test.next_batch 函数读出图片和标签喂给网络;
现在:用函数 get_tfrecord 替换读取所有测试集 1 万张图片。
isTrain:用来区分训练阶段和测试阶段,True 表示训练,False 表示测试。
3)xs,ys=sess.run([img_batch,label_batch])
之前:使用函数 xs,ys=mnist.test.next_batch(BATCH_SIZE)
现在:在 sess.run 中执行图片和标签的批获取。