tensorflow卷积神经网络例子

本文是对tensorflow教程中的cifar数据集卷积神经网络例子进行学习后的笔记,主要记录了一些相关方法的使用。

模型输入

模型的输入包括input()、distorted_inputs等一些操作,分别用于读取CIFAR图像并进行预处理。
文件处理流程:

  • 统一裁剪到24 * 24像素大小,裁剪中央区域
  • 近似白化处理,使得模型对动态范围变化不敏感
distorted_image = tf.random_crop(reshaped_image, [height, width,3])

float_image = tf.image.per_image_standardization(distorted_image)

对于训练集,可以采取一系列的随机变换方法来人为增加数据集的大小:

  • 对图像进行左右翻转
  • 随机变换图像的亮度
  • 随机变换突袭那个的对比度
distorted_image = tf.image.random_flip_left_right(distorted_image)

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)

文件读取机制

  在tensorflow中有一种文件读取是通过里那个队列的完成,一个是内存队列,一个是文件名队列。使用tf.string_input_producer(num_pochs, shuffle=False)来产生文件名队列,在tensorflow中内存队列不需要我们自己建立,我们只需要使用reader对象从文件名队列中读取数据就可以。需要注意的是,在我们使用tf.string_input_producer(num_pochs, shuffle=False)创建文件名队列的时候,整个系统还是处于停滞的状态,也就是说,我们的文件名还未真正加入到队列中,我们需要使用tf.train.start_queue_runners()之后才真正启动填充队列的线程。

可以参考以下一段简单的代码:

# --*- coding:utf-8 -*--
import os
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
train_dir = 'your path'

def get_files(file_dir):
    cats = []
    label_cats = []
    dogs = []
    label_dogs = []
    for file in os.listdir(file_dir + '/cat'):
        cats.append(file_dir + 'cat' + '/' + file)
        label_cats.append(0)

    for file in os.listdir(file_dir + '/dog'):
        dogs.append(file_dir + 'dog' + '/' + file)
        label_dogs.append(1)



    #把cat和dog合起来组成一个list(img和lab)
    image_list = np.hstack((cats, dogs))
    label_list = np.hstack((label_cats, label_dogs))

    #利用shuffle打乱顺序
    temp = np.array([image_list, label_list])
    temp = temp.transpose()
    np.random.shuffle(temp)

    #从打乱的temp中再取出list(img和lab)
    image_list = list(temp[:, 0])
    label_list = list(temp[:, 1])
    label_list = [int(i) for i in label_list]

    return image_list, label_list

BATCH_SIZE = 2
CAPACITY = 256
IMG_W = 208
IMG_H = 208

def get_batch(image, label, image_W, image_H, batch_size, capacity):
    image = tf.cast(image, tf.string)
    label = tf.cast(label, tf.int32)

    #产生一个输入队列queue,因为img和lab是分开的,所以使用tf.train.slice_input_producer(),然后用tf.read_file()从队列中读取图像
    input_queue = tf.train.slice_input_producer([image, label])
    label = input_queue[1]
    image_contents = tf.read_file(input_queue[0])  # read img from a queue
    image = tf.image.decode_jpeg(image_contents, channels=3)
    image = tf.image.resize_image_with_crop_or_pad(image, image_W, image_H)
    #image = tf.image.per_image_standardization(image)
    image = tf.image.random_flip_left_right(image)
    image = tf.image.random_contrast(image,
                                               lower=0.2, upper=1.8)
    image_batch, label_batch = tf.train.batch([image, label],
                                              batch_size=batch_size,
                                              num_threads=32,
                                              capacity=capacity)
    # 重新排列label,行数为[batch_size]
    label_batch = tf.reshape(label_batch, [batch_size])
    #image_batch = tf.cast(image_batch, tf.float32)

    return image_batch, label_batch

image_list, label_list = get_files(train_dir)
image_batch, label_batch = get_batch(image_list, label_list,IMG_W, IMG_H, BATCH_SIZE, CAPACITY)

with tf.Session() as sess:
    i = 0
    coord = tf.train.Coordinator()
    threads = tf.train.start_queue_runners(coord=coord)

    try:
        while not coord.should_stop() and i < 20:
            img, label = sess.run([image_batch, label_batch])

            for j in np.arange(BATCH_SIZE):

                print('label: %d' %label[j])
                plt.imshow(img[j,:,:,:])
                plt.show()
                i += 1
    except tf.errors.OutOfRangeError:
        print('Done')

    finally:
        coord.request_stop()
    coord.join(threads)

训练模型

模型的训练包括loss()和train()等一些操作,用于计算损失,计算梯度、进行变量更新以及呈现最终的结果。

logits = cifar10.inference(images)

    # Calculate loss.
loss = cifar10.loss(logits=logits, labels=labels)

    # Build a Graph that trains the model with one batch of examples and
    # updates the model parameters.
train_op = cifar10.train(loss, global_step)

    # Create a saver.
saver = tf.train.Saver(tf.all_variables())
  • inference()
    这部分主要是网络的设置,包括两个卷积层,三个局部连接层,用于构造分类模型。这部分代码全部采用的是tf.get_variable(),和tf.Variable()的主要区别在于tf.get_variable()可用于共享机制,若某变量名已存在,则返回该值,而后者不管是否存在都会返回新的变量。
import tensorflow as tf

with tf.variable_scope("scope1"):
    w1 = tf.get_variable("w1", shape=[])
    w2 = tf.Variable(0.0, name="w2")
with tf.variable_scope("scope1", reuse=True):
    w1_p = tf.get_variable("w1", shape=[])
    w2_p = tf.Variable(1.0, name="w2")

print(w1 is w1_p, w2 is w2_p)
  • loss()
    此处定义的loss是交叉熵损失加上所有w的的L2正则。
tf.add_n(tf.get_collection('losses'), name='total_loss')

在tensorflow中,tensorflow的collection提供一个全局的存储机制,不会受到变量名生存空间的影响。一处保存,到处可取。也就是说,一个名称下,可以储存多个值。

  • train()
# Compute gradients.
  with tf.control_dependencies([loss_averages_op]):
    opt = tf.train.GradientDescentOptimizer(lr)
    grads = opt.compute_gradients(total_loss)

  # Apply gradients.
  apply_gradient_op = opt.apply_gradients(grads, global_step=global_step)

  # Add histograms for trainable variables.
  for var in tf.trainable_variables():
    tf.summary.histogram(var.op.name, var)

  # Add histograms for gradients.
  for grad, var in grads:
    if grad is not None:
      tf.summary.histogram(var.op.name + '/gradients', grad)

  # Track the moving averages of all trainable variables.
  variable_averages = tf.train.ExponentialMovingAverage(
      MOVING_AVERAGE_DECAY, global_step)
  variables_averages_op = variable_averages.apply(tf.trainable_variables())

  with tf.control_dependencies([apply_gradient_op, variables_averages_op]):
    train_op = tf.no_op(name='train')

  return train_op

tf.control_dependencies()设计是用来控制计算流图的,给图中的某些计算指定顺序。tf.no_op()表示什么也不做。比如模型训练的时候要执行a,b操作,可使用以下代码:

with tf.control_dependencies([a, b]):
    c= tf.no_op(name='train')#tf.no_op;什么也不做
sess.run(c)

评估

训练脚本会为所有学习变量计算其移动均值,评估脚本则直接将所有学习到的模型参数替换成对应的移动均值。这一替代方式可以在评估过程中提升模型的性能。

# Restore the moving average version of the learned variables for eval.
    variable_averages = tf.train.ExponentialMovingAverage(
        cifar10.MOVING_AVERAGE_DECAY)
    variables_to_restore = variable_averages.variables_to_restore()
    saver = tf.train.Saver(variables_to_restore)

ExponentialMovingAverage 对每一个(待更新训练学习的)变量(variable)都会维护一个影子变量(shadow variable)。影子变量的初始值就是这个变量的初始值:
shadow_variable=decay∗shadow_variable+(1−decay)∗variable
decay 控制着模型更新的速度,越大越趋于稳定。实际运用中,decay 一般会设置为十分接近 1 的常数(0.99或0.999)。为了使得模型在训练的初始阶段更新得更快,ExponentialMovingAverage 还提供了 num_updates 参数来动态设置 decay 的大小
decay=min{decay,1+num_updates10+num_updates}

就此,我们就完成了整个卷积神经网络在CIFAR数据集上的分类预测。