赞
踩
这次的实验仍旧是图像识别,使用的数据集是CIFAR-10,其中包含10个类别的RGB彩色图片:飞机、汽车、鸟类、猫、鹿、狗、蛙类、马、船和卡车,其中训练图片共50000张,测试图片共10000张。实验来自于《21个项目玩转深度学习:基于TensorFlow的实践详解》一书中的实验,在这里做一篇学习笔记,笔者不才,欢迎斧正!
项目文件如下: 。其中,
运行cifar10_download.py代码,成功将数据集下载到目录中。
可以看到数据集中数据文件名及用途:
读取数据集必须考虑的一个问题:GPU/CPU因为I/O而空闲的问题
简单地将图片数据集从内存中读进CPU或GPU中进行计算,读取的时间便意味在降低内存的效率。为了解决这个问题,方法即将读入数据和计算分别放在两个线程中,先将数据读入内存队列中。读取线程负责读入数据到内存队列,另一个线程则负责计算,计算需要数据时直接从内存队列中取即可。
Tensorflow使用“文件名队列+内存队列”双队列的形式读入文件,更好的管理epoch。
对于一个数据集来讲,运行一个epoch就是将这个数据集中的图片全部计算一遍。运行一个epoch,就在文件名队列中把A、B、C各放入一次,并在之后标注队列结束。
程序运行后,内存先读取A,再依次读取B、C。
由于系统检测到“结束”,则自动抛出异常,外部捕捉到这个异常后就可以结束程序了。这就是Tensorflow中读取数据的基本机制。如果要运行两个epoch,那么只要在文件名队列中将A、B、C依次放入两次再标记结束就可以了。
在Tensorflow中创建文件名队列和内存队列
使用tf.train.string_input_producer函数。这个函数有两个参数,一个是num_epoch是,即epoch数目;另一个是shuffle,指在一个epoch内文件的顺序是否被打乱。若设置shuffle=False,则每个epoch内,数据仍然按照A、B、C的顺序进入文件名队列,顺序不变。反之,则在一个epoch内数据的前后顺序就会被打乱。在Tensorflow中内存队列不需要自己建立,只需要使用reader读取数据就可以了。
在使用tf.train.string_input_producer创建文件名队列之后,整个系统还处于停滞状态,就是说文件名其实并没有真正加入到队列中。此时若开始计算,内存队列为空,计算单元就会一直阻塞。使用tf.train.start_queue_runners之后,才会启动填充队列的线程,这时系统就不在停滞了。
在下面的代码中,我们读取三张照片的5个epoch,并把读取的结果重新存到read文件夹中。
- # coding:utf-8
- import os
- if not os.path.exists('read'):
- os.makedirs('read/')
-
- # 导入TensorFlow
- import tensorflow as tf
-
- # 新建一个Session
- with tf.Session() as sess:
- # 我们要读三幅图片A.jpg, B.jpg, C.jpg
- filename = ['A.jpg', 'B.jpg', 'C.jpg']
- # string_input_producer会产生一个文件名队列
- filename_queue = tf.train.string_input_producer(filename, shuffle=False, num_epochs=5)
- # reader从文件名队列中读数据。对应的方法是reader.read
- reader = tf.WholeFileReader()
- key, value = reader.read(filename_queue)
- # tf.train.string_input_producer定义了一个epoch变量,要对它进行初始化
- tf.local_variables_initializer().run()
- # 使用start_queue_runners之后,才会开始填充队列
- threads = tf.train.start_queue_runners(sess=sess)
- i = 0
- while True:
- i += 1
- # 获取图片数据并保存
- image_data = sess.run(value)
- with open('read/test_%d.jpg' % i, 'wb') as f:
- f.write(image_data)
- # 程序最后会抛出一个OutOfRangeError,这是epoch跑完,队列关闭的标志
其中,我们使用“filename_queue = tf.train.string_input_producer(filename, shuffle=False, num_epochs=5)” 来建立一个运行5个epoch的文件名队列,并使用reader读取,reader每次读取一张照片并保存。
运行代码后,程序最后会抛出OutOfRangeError异常,这就是epoch跑完队列关闭的标志,得到read文件夹中的图片正好是按顺序的5个epoch。下图是shuffle=False时的结果:
下面我们再来将CIFAR-10数据集保存成图片形式。运行的代码是cifar10_extract.py。Cifar数据集包括10000个样本,每个样本有3073个字节,第一个字节标签,剩下的3072个字节是图像数据。样本和样本之间无多余的字节分割,因此这几个二进制文件的大小是30730000字节。
一般来说在深度学习中,数据的总量越多,训练得到的模型效果会越好。在图像任务中,对输入的图像进行简单的平移、
缩放、颜色变换,并不会影响图像类别。因此可以依据此增强训练样本的个数。
数据增强(Data Augmentation)方法是指利用平移、缩放、颜色变换,人工增大训练集样本个数,使得模型训练的效果更好。
常见的图像数据增强方法:
这些数据增强方法不会改变图像原有标签。
代码为:
- # Randomly crop a [height, width] section of the image.
- distorted_image = tf.random_crop(reshaped_image, [height, width, 3])
-
- # Randomly flip the image horizontally.
- distorted_image = tf.image.random_flip_left_right(distorted_image)
-
- # Because these operations are not commutative, consider randomizing
- # the order their operation.
- distorted_image = tf.image.random_brightness(distorted_image,max_delta=63)
- distorted_image = tf.image.random_contrast(distorted_image,lower=0.2, upper=1.8)
进行的操作分别是:
该模型通过三个模块来构造训练图,最大限度得提高代码复用率:
在模型输入部分将图像的加载和变换过程放入16个线程中,以减慢训练过程。
其中,在distorted_inputs()函数中采取对图像随机左右翻转、变换图像亮度、变换图像对比度等操作。该函数的核心代码为:
- # Randomly crop a [height, width] section of the image.
- distorted_image = tf.random_crop(reshaped_image, [height, width, 3])
- # Randomly flip the image horizontally.
- distorted_image = tf.image.random_flip_left_right(distorted_image)
- # Because these operations are not commutative, consider randomizing
- # the order their operation.
- distorted_image = tf.image.random_brightness(distorted_image,
- max_delta=63)
- distorted_image = tf.image.random_contrast(distorted_image,
- lower=0.2, upper=1.8)
- # Subtract off the mean and divide by the variance of the pixels.
- float_image = tf.image.per_image_standardization(distorted_image)
预测流程由inference() 构造。这个函数会添加必要的操作步骤用于计算预测值的logits。
1、构建第一个卷积层
卷积层:
池化层:
局部响应归一化
i:代表下标,你要计算像素值的下标,从0计算起
j:平方累加索引,代表从j~i的像素值平方求和
x,y:像素的位置,公式中用不到
a:代表feature map里面的 i 对应像素的具体值
N:每个feature map里面最内层向量的列数
k:超参数,由原型中的bias指定
α:超参数,由原型中的alpha指定
n/2:超参数,由原型中的deepth_radius指定
β:超参数,由原型中的belta指定
2、构建第二个卷积层
卷积层
池化层:
3、基于修正线性激活的全连接层
4、基于修正线性激活的全连接层
5、softmax逻辑回归输出分类结果
现在避免全连接的方法是全局平均值法,即将最后一层卷积的输出结果(featuremap)求平均值。
训练可进行N维分类的网络常用多项式逻辑回归,即softmax回归。Softmax 回归在网络的输出层上附加了一个softmax nonlinearity,并且计算归一化的预测值和label的1-hot encoding的交叉熵。在正则化过程中,我们会对所有学习变量应用权重衰减损失。模型的目标函数是求交叉熵损失和所有权重衰减项的和,loss()
函数的返回值就是这个值。
在TensorBoard中可以查看这个值的变化:
使用标准的梯度下降算法训练的模型,其学习率随时间以指数形式衰减(本实验只让服务器跑了十万多次,衰减程度不是很明显):
训练速度的变化:
本实验跑了3000个训练样本,用记事本打开checkpoint发现:
说明model_checkpoint_path表示最新的模型是model.ckpt-112632。112632即为第112632步的模型。后面5个all_model_checkpoint_paths表示所有存储下来的5个模型和它们的步数。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。