CIFAR-10与ImageNet图像识别

        这次的实验仍旧是图像识别,使用的数据集是CIFAR-10,其中包含10个类别的RGB彩色图片:飞机、汽车、鸟类、猫、鹿、狗、蛙类、马、船和卡车,其中训练图片共50000张,测试图片共10000张。实验来自于《21个项目玩转深度学习:基于TensorFlow的实践详解》一书中的实验,在这里做一篇学习笔记,笔者不才,欢迎斧正!

         项目文件如下:  。其中,

一、下载CIFAR-10数据

         运行cifar10_download.py代码,成功将数据集下载到目录中。

        可以看到数据集中数据文件名及用途:

二、Tensorflow的数据读取机制

读取数据集必须考虑的一个问题: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字节。

  • 第一步,用tf.train.string_input_producer建立队列。
  • 第二步,用reader.read读取数据。注意:读取以文件格式存放的图片是tf.WholeFileReader(),但是Cifar数据是一个文件中包含多个以字节存储的样本,因此使用tf.FixedLengthRecordReaer()
  • 第三步,调用tf.train.start_queue_runners
  • 最后通过sess.run()取出图片结果

三、利用TensorFlow训练CIFAR-10识别模型

(壹)数据增强

原理部分:       

        一般来说在深度学习中,数据的总量越多,训练得到的模型效果会越好。在图像任务中,对输入的图像进行简单的平移、

缩放、颜色变换,并不会影响图像类别。因此可以依据此增强训练样本的个数。

        数据增强(Data Augmentation)方法是指利用平移、缩放、颜色变换,人工增大训练集样本个数,使得模型训练的效果更好。

        常见的图像数据增强方法:

  • 平移
  • 旋转
  • 翻转:水平翻转或上下翻转图像
  • 裁剪:在原有图像上裁剪出一块
  • 缩放
  • 颜色变换:对图像的RGB颜色空间进行一些变换
  • 噪声扰动:给图像加入一些人工生成的噪声

         这些数据增强方法不会改变图像原有标签。

TensorFlow中数据增强的实现

        代码为:

# 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)

        进行的操作分别是:

  1. 随机裁剪
  2. 对裁剪后的小块进行水平反转
  3. 对得到的图片进行亮度和对比度的随机训练

(贰)Cifar-10识别模型

     该模型通过三个模块来构造训练图,最大限度得提高代码复用率:

  1. 模型输入:读取数据集中的图像并进行预处理
  2. 模型预测:进行统计计算
  3. 模型训练:计算损失、计算梯度、进行变量更新、保存最终结果

  (一)模型输入

      在模型输入部分将图像的加载和变换过程放入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)
  • tf.random_crop:为图片随机裁剪
  • tf.image.random_flip_left_right:随机左右翻转
  • tf.image.random_brightness:随机亮度变化
  • tf.image.random_contrast:随机对比度变化
  • tf.image.per_image_standardization:减去均值像素,并除以像素方差(图片标准化)

(二)模型预测

        预测流程由inference() 构造。这个函数会添加必要的操作步骤用于计算预测值的logits。

     1、构建第一个卷积层

        卷积层:

  •  输入:images
  • 卷积核:宽度为5×5 、通道为3、共64个
  • 步长:1×1
  • padding:SAME
  • 偏置项:通过函数生成的初始化为全0的64维向量
  • W*x+b:通过函数tf.nn.bias_add(conv,biases)实现
  • relu:使用ReLu激活函数完成修正线性激活

        池化层:

  • 输入:conv1
  • 池化窗口大小:3×3
  • 滑动步长:2×2
  • padding:SAME

       局部响应归一化

  • 原因:

  •   公式

        i:代表下标,你要计算像素值的下标,从0计算起

        j:平方累加索引,代表从j~i的像素值平方求和

        x,y:像素的位置,公式中用不到

        a:代表feature map里面的 i 对应像素的具体值

        N:每个feature map里面最内层向量的列数

        k:超参数,由原型中的bias指定

        α:超参数,由原型中的alpha指定

        n/2:超参数,由原型中的deepth_radius指定

        β:超参数,由原型中的belta指定

        2、构建第二个卷积层

        卷积层

  • 输入:norm1
  • 卷积核:宽度为5×5、通道数为64、共64个
  • 步长:1×1
  • padding:SAME
  • 偏置项:初始化全为0.1的64维向量
  • W*A+b:通过函数tf.nn.bias_add(conv,biases)实现
  • 修正线性激活函数:ReLu

        池化层:

  • 输入:norm2
  • 池化窗口大小:3×3
  • 滑动步长:2×2
  • padding:SAME

        3、基于修正线性激活的全连接层

  • 原因:前面的卷积和池化相当于做了特征工程,而全连接层则负责特征加权,简单来说全连接的目的即对特征高度提纯,方便交给最后的分类器或者回归。
  • 本质: 由一个特征空间变换到另一个特征空间
  • 为了提高CNN网络性能,在这里全连接层每个神经元的激励函数都采用ReLu函数

        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个模型和它们的步数。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值