引入
TensorFlow提供了一种统一的格式来存储数据,这个格式就是TFRecord。基于这个统一的数据格式,在处理数据的时候有一些通用的框架。这些通用的框架总结为获取文件列表、创建文件队列、图像预处理、合成Batch、设计损失函数、梯度下降算法。如图片总结如下:
获取文件列表、创建文件队列
TFRecord介绍
TFRecord数据文件是一种将图像数据和标签统一存储的二进制文件,能更好的利用内存,在tensorflow中快速的复制,移动,读取,存储等。
写入
通过将数据填入到tf.train.Example类,Example的protocol buffer包含了字段的tf.train.Features,使用数据修改Features, 实现将protocol buffer序列化成一个字符串, 再通过tf.python_io.TFRecordWriter类将序列化的字符串写入到TFRecord中.
读出
使用tf.TFRecordReader读取器, 通过tf.parse_single_example解析器解析,parse_single_example操作可以将Example protocol buffer解析为张量, 然后用解码器tf.decode_raw解码.
代码实现
将数据保存为tfrecord格式
1 | import os |
Tensorflow从TFRecord中读取数据
1 | def read_and_decode(filename): # read iris_contact.tfrecords |
将TFRecord中的数据保存为图片
1 | filename_queue = tf.train.string_input_producer(["iris_contact.tfrecords"]) |
图像预处理
图像编/解码
图像是按照jpg等格式编码并保存在文件中。
1 | #解码 |
图像大小调整
作用:在将图像的像素作为输入提供给神经网络之前,需要将图像的参数大小统一。
1 | #代码实现一:调整图片大小 |
图像翻转
1 | flipped=tf.image.flip_up_down(img_data) #上下翻转 |
图像色彩调整
1 | #调整亮度 |
处理标注框
1 | #函数tf.image.draw_bounding_boxes()的输入是一个batch的数据 |
合成Batch
TensorFlow队列与多线程的应用
实现队列
FIFOQueue():创建一个先入先出(FIFO)的队列
RandomShuffleQueue():创建一个随机出队的队列
enqueue_many():初始化队列中的元素
dequeue():出队
enqueue():入队
1 | import tensorflow as tf |
多线程协同
TensorFlow为我们提供了多线程协同操作的类—tf.Coordinator,其函数主要有:
should_stop():确定当前线程是否退出
request_stop():通知其他线程退出
join():等待所有线程终止
假设有五个线程同时在工作,每个线程自身会先判断should_stop()的值,当其返回值为True时,则退出当前线程;如果为Flase,也继续该线程。此时如果线程3发出了request_stop()通知,则其它4个线程的should_stop()将全部变为True,然后线程4自身的should_stop()也将变为True,则退出了所有线程。
1 | import tensorflow as tf |
在第一轮遍历过程中,所有进程的should_stop()都为Flase,且随机数都大于等于0.09,所以依次打印了workingfrom id:0-5,再重新回到进程0时,出现了小于0.09的随机数,即进程0发出了request_stop()请求,进程1-4的should_stop()返回值全部为True(进程退出),也就无法进入while,进程0的should_stop()返回值也将为True(退出),五个进程全部退出。
多线程操作队列
前面说到了队列的操作,多线程协同的操作,在多线程协同的代码中让每一个线程打印自己的id编号,下面我们说下如何用多线程操作一个队列。
TensorFlow提供了队列tf.QueueRunner类处理多个线程操作同一队列,启动的线程由上面提到的tf.Coordinator类统一管理,常用的操作有:
QueueRunner():启动线程,第一个参数为线程需要操作的队列,第二个参数为对队列的操作,如enqueue_op,此时的enqueue_op= queue.enqueue()
add_queue_runner():在图中的一个集合中加‘QueueRunner’,如果没有指定的合集的话,会被添加到tf.GraphKeys.QUEUE_RUNNERS合集
start_queue_runners():启动所有被添加到图中的线程
1 | import tensorflow as tf |
组合训练数据
Tensorflow读出TFRecord中的数据,然后在经过预处理操作,此时需要注意:数据还是单个,而网络的输入一般以Batch为单位,因此我们需要将单个的数据组合成一个Batch,做为神经网络的输入。
Tensorflow提供组合训练数据的函数有四个:tf.train.batch(),tf.train.shuffle_batch()与tf.train.batch_join、tf.train.shuffle_batch_join,这里为什么要用与呢?其实他们是针对两种情况。tf.train.batch和tf.train.batch_join的区别,一般来说,单一文件多线程,选用tf.train.batch(需要打乱样本,有对应的tf.train.shuffle_batch);而对于多线程多文件的情况,一般选用tf.train.batch_join来获取样本(打乱样本同样也有对应的tf.train.shuffle_batch_join使用)。下面会通过具体的例子来说明。tf.train.batch(),tf.train.shuffle_batch()这两个函数都会生成一个队列,队列的入队操作是生成单个样例的方法,也就是经过预处理之后的图像。
我们首先看看一下这两个函数的定义:
1 | def batch(tensors, batch_size, num_threads=1, capacity=32, |
这两个函数的主要参数为:
- tensors入队队列,预处理后的数据和对应的标签。
- batch_size:batch的大小。如果太大,则需要占用较多的内存资源,如果太小,那么出队操作可能会因为没有数据而被阻塞,从而导致训练效率降低。
- capacity:队列的最大容量,当队列的长度等于容量时,Tensorflow将暂停入队操作,而只是等待元素出队。当队列个数小于容量时,Tensorflow将自动启动入队操作。
- num_threads:启动多少个线程读取文件和预处理。
- allow_smaller_final_batch:如果设置True,则会允许最后一个Batch的大小比较小,当没有足够的数据输入时。
1 | # -*- coding: utf-8 -*- |
设计损失函数、梯度下降算法
经典的损失函数
交叉熵损失函数
$\mathrm{H}(\mathrm{p}, \mathrm{q})=-\sum_{x} p(x) \log q(x)$,p是真实的分布,q是模型给出的分布
刻画了两个概率分布之间的距离,一般需要加一个softmax层将输出层变为概率分布,交叉熵越小,越说明两个分布之间的距离就越小。
1 | tf.reduce.mean(y_*tf.log(tf.clip_by_value(y,1e-10,1.0))) |
均方误差
$\operatorname{MSE}\left(\mathrm{y}, y^{\prime}\right)=\frac{\sum_{i=1}^{n}\left(y_{i}-y_{i}\right)}{n}$,常用于回归问题
1 | mse=tf.reduce_mean(tf.square(y_-y)) |
交叉熵和softmax回归
1 | cross_entrop=tf.nn.softmax_cross_entrop_with_logits(labels=y_,logits=y) |
说明:labels表示训练数据的正确答案,只需要传入正确答案的数字就可以,常用tf.argmax(y_,1),logits表示训练的结果
作用:在只有一个正确答案的分类问题中,使用这个函数可以进一步加速计算过程,因为相当于只有一个数来乘以一个向量了,而不是两个向量相乘
神经网络训练过程
1 | batch_size=n |
代码总结
1 | # 创建文件列表,并通过文件列表来创建文件队列。在调用输入数据处理流程前,需要统一 |
参考资料:
《Tensorflow:实战Google深度学习框架》