TensorFlow入门教程(10)两层CNN对CIFAR-10图像识别

#
#作者:韦访
#博客:https://blog.csdn.net/rookie_wei
#微信:1007895847
#添加微信的备注一下是CSDN的
#欢迎大家一起学习
#

1、概述

上一讲我们已经对官方的CIFAR10图像识别代码进行分析,但如果只做到这一步感觉还是不够,没能做到举一反三以及对之前学的知识的巩固,所以这一讲,我打算结合之前学的双层卷积神经网络和上一讲的知识自己写一个demo。

环境配置:

操作系统:Win10 64位

显卡:GTX 1080ti

Python:Python3.7

TensorFlow:1.15.0

 

2、代码解析

下载数据集

第一步,还是先下载数据集,使用上一讲下载数据集的maybe_download_and_extract方法,代码如下,

# 查看CIFAR-10数据是否存在,如果不存在则下载并解压
def maybe_download_and_extract(dir):
    DATA_URL = 'https://www.cs.toronto.edu/~kriz/cifar-10-binary.tar.gz'
    """Download and extract the tarball from Alex's website."""
    if not os.path.exists(dir):
        os.makedirs(dir)
    filename = DATA_URL.split('/')[-1]
    filepath = os.path.join(dir, filename)
    if not os.path.exists(filepath):
        def _progress(count, block_size, total_size):
            sys.stdout.write('\r>> Downloading %s %.1f%%' % (filename,
                                                             float(count * block_size) / float(total_size) * 100.0))
            sys.stdout.flush()

        filepath, _ = urllib.request.urlretrieve(DATA_URL, filepath, _progress)
        print()
        statinfo = os.stat(filepath)
        print('Successfully downloaded', filename, statinfo.st_size, 'bytes.')
    extracted_dir_path = os.path.join(dir, 'cifar-10-batches-bin')
    if not os.path.exists(extracted_dir_path):
        tarfile.open(filepath, 'r:gz').extractall(dir)

获取图片和标签

接着,就是获取图片和标签的代码,跟上一讲的内容一样,代码如下,

#获取每个样本数据,样本由一个标签+一张图片数据组成
def get_record(queue):
    print('get_record')
    #定义label大小,图片宽度、高度、深度,图片大小、样本大小
    label_bytes = 1
    image_width = 32
    image_height = 32
    image_depth = 3
    image_bytes = image_width * image_height * image_depth
    record_bytes = label_bytes + image_bytes

    #根据样本大小读取数据
    reader = tf.FixedLengthRecordReader(record_bytes)
    key, value = reader.read(queue)

    #将获取的数据转变成一维数组
    #例如
    # source = 'abcde'
    # record_bytes = tf.decode_raw(source, tf.uint8)
    #运行结果为[ 97  98  99 100 101]
    record_bytes = tf.decode_raw(value, tf.uint8)

    #获取label,label数据在每个样本的第一个字节
    label_data = tf.cast(tf.strided_slice(record_bytes, [0], [label_bytes]), tf.int32)

    #获取图片数据,label后到样本末尾的数据即图片数据,
    # 再用tf.reshape函数将图片数据变成一个三维数组
    depth_major = tf.reshape(
        tf.strided_slice(record_bytes, [label_bytes],[label_bytes + image_bytes]),
        [3, 32, 32])

    #矩阵转置,上面得到的矩阵形式是[depth, height, width],即红、绿、蓝分别属于一个维度的,
    #假设只有3个像素,上面的格式就是RRRGGGBBB
    #但是我们图片数据一般是RGBRGBRGB,所以这里要进行一下转置
    #注:上面注释都是我个人的理解,不知道对不对
    image_data = tf.transpose(depth_major, [1, 2, 0])

    return label_data, image_data

def _generate_image_and_label_batch(image, label, min_queue_examples,
                                    batch_size, shuffle):
  num_preprocess_threads = 1
  if shuffle:
    images, label_batch = tf.train.shuffle_batch(
        [image, label],
        batch_size=batch_size,
        num_threads=num_preprocess_threads,
        capacity=min_queue_examples + 3 * batch_size,
        min_after_dequeue=min_queue_examples)
  else:
    images, label_batch = tf.train.batch(
        [image, label],
        batch_size=batch_size,
        num_threads=num_preprocess_threads,
        capacity=min_queue_examples + 3 * batch_size)

  # Display the training images in the visualizer.
  tf.summary.image('images', images)

  return images, tf.reshape(label_batch, [batch_size])

#检测CIFAR-10数据是否存在,如果不存在则返回False
def check_cifar10_data_files(filenames):
    for file in filenames:
        if os.path.exists(file) == False:
            print('Not found cifar10 data.')
            return False
    return True

#获取图片前的预处理,检测CIFAR10数据是否存在,如果不存在直接退出
#如果存在,用string_input_producer函数创建文件名队列,
# 并且通过get_record函数获取图片标签和图片数据,并返回
def get_image(data_path):
    filenames = [os.path.join(data_path, "data_batch_%d.bin" % i) for i in range(1, 6)]
    print(filenames)
    if check_cifar10_data_files(filenames) == False:
        exit()

    #创建文件名队列
    queue = tf.train.string_input_producer(filenames)
    # 获取图像标签和图像数据
    label, image = get_record(queue)
    #将图像数据转成float32类型
    reshaped_image = tf.cast(image, tf.float32)

    #下面是数据增强操作
    #将图片随机裁剪成24*24像素
    distorted_image = tf.random_crop(reshaped_image, [height, width, 3])

    # 将图片随机左右翻转
    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)

    # 对图片标准化处理
    float_image = tf.image.per_image_standardization(distorted_image)

    # Set the shapes of tensors.
    float_image.set_shape([height, width, 3])
    label.set_shape([1])

    min_fraction_of_examples_in_queue = 0.4
    min_queue_examples = int(NUM_EXAMPLES_PER_EPOCH_FOR_TRAIN *
                             min_fraction_of_examples_in_queue)
    return _generate_image_and_label_batch(float_image, label,
                                           min_queue_examples, batch_size,
                                           shuffle=True)

函数封装

接下来就是卷积层、全连接层、损失函数等等的函数封装,需要注意的是,我们这里设置学习率时,使用指数衰减法,代码如下,

NUM_EXAMPLES_PER_EPOCH_FOR_TRAIN = 50000
batch_size = 128
height = 24
width = 24

# Constants describing the training process.
MOVING_AVERAGE_DECAY = 0.9999     # The decay to use for the moving average.
NUM_EPOCHS_PER_DECAY = 350.0      # Epochs after which learning rate decays.
LEARNING_RATE_DECAY_FACTOR = 0.1  # Learning rate decay factor.
INITIAL_LEARNING_RATE = 0.0001       # Initial learning rate.

# 初始化过滤器
def weight_variable(shape):
    return tf.Variable(tf.truncated_normal(shape, stddev=0.1))

# 初始化偏置
def bias_variable(shape):
    return tf.Variable(tf.constant(0.1, shape=shape))

# 卷积运算
def conv2d(x, W):
    # strides表示每一维度滑动的步长,一般strides[0] = strides[3] = 1
    # 第四个参数可选"Same"或"VALID",“Same”表示边距使用全0填充
    return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding="SAME")

# 池化运算
def max_pool_2x2(x):
    return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding="SAME")

# 卷积层
def conv_layer(input, filter_shape, bias_shape):
    W = weight_variable(filter_shape)
    b = bias_variable(bias_shape)
    # 使用conv2d函数进行卷积计算,然后再用ReLU作为激活函数
    h = tf.nn.relu(conv2d(input, W) + b)
    # 卷积以后再经过池化操作
    return max_pool_2x2(h)

# 全连接层
def dense(input, weight_shape, bias_shape, reshape):
    W = weight_variable(weight_shape)
    b = bias_variable(bias_shape)
    # 将输入数据还原成向量的形式
    h = tf.reshape(input, reshape)
    # 使用ReLU作为激活函数
    return tf.nn.relu(tf.matmul(h, W) + b)

# dropout
def dropout(input):
    # 为了防止过拟合,使用dropout正则化
    keep_prob = tf.placeholder(tf.float32)
    return keep_prob, tf.nn.dropout(input, keep_prob)

# Softmax输出
def softmax(input, weight_shape, bias_shape):
    W = weight_variable(weight_shape)
    b = bias_variable(bias_shape)
    # 最后都要经过Softmax函数将输出转化为概率问题
    return tf.nn.softmax(tf.matmul(input, W) + b)

# 定义损失函数和优化器
def optimizer(label, y):
    global_step = tf.train.get_or_create_global_step()
    num_batches_per_epoch = NUM_EXAMPLES_PER_EPOCH_FOR_TRAIN / batch_size
    decay_steps = int(num_batches_per_epoch * NUM_EPOCHS_PER_DECAY)

    # Decay the learning rate exponentially based on the number of steps.
    lr = tf.train.exponential_decay(INITIAL_LEARNING_RATE,
                                    global_step,
                                    decay_steps,
                                    LEARNING_RATE_DECAY_FACTOR,
                                    staircase=True)
    loss = tf.reduce_mean(-tf.reduce_sum(label * tf.log(y)))
    return tf.train.AdamOptimizer(lr).minimize(loss), loss

# 计算模型预测准确率
def accuracy(label, y):
    pred = tf.equal(tf.argmax(y, 1), tf.argmax(label, 1))
    return tf.reduce_mean(tf.cast(pred, tf.float32))

网络结构

网络结构跟以前一样,代码如下,

# 网络结构
def net(input, label):

    # 第一层卷积
    # 将过滤器设置成5×5×1的矩阵,
    # 其中5×5表示过滤器大小,1表示深度,因为MNIST是黑白图片只有一层。所以深度为1
    # 32表示我们要创建32个大小5×5×1的过滤器,经过卷积后算出32个特征图(每个过滤器得到一个特征图)
    c1 = conv_layer(input, [5, 5, 3, 32], [32])

    # 第二层卷积
    # 因为经过第一层卷积运算后,输出的深度为32,所以过滤器深度也为32,64是指经过第二层卷积运算以后的深度
    c2 = conv_layer(c1, [5, 5, 32, 64], [64])

    # 全连接层
    # 经过两层卷积后,图片的大小为6×6(第一层池化后输出为(24/2)×(24/2),
    # 第二层池化后输出为(12/2)×(12/2)),深度为64,
    # 我们在这里加入一个有1024个神经元的全连接层,所以权重W的尺寸为[6 * 6 * 64, 1024]
    f1 = dense(c2, [6 * 6 * 64, 1024], [1024], [-1, 6 * 6 * 64])

    # dropout
    keep_prob, h = dropout(f1)

    # Softmax
    y = softmax(h, [1024, 10], [10])

    # 定义损失函数和优化器
    op, loss = optimizer(label, y)

    # 计算预测准确率
    acc = accuracy(label, y)

    return acc, op, keep_prob, loss

main函数

main函数代码如下,

def main(argv=None):
    dir = "./cifar10_dataset/"
    #查看CIFAR-10数据是否存在,如果不存在则下载并解压
    maybe_download_and_extract(dir)

    #获取图片数据
    value, key = get_image(os.path.join(dir, 'cifar-10-batches-bin/'))

    # 创建x占位符,用于临时存放CIFAR10图片的数据,
    # [None, height , width , 3]中的None表示不限长度
    x = tf.placeholder(tf.float32, [None, height , width , 3])
    # y_存的是实际图像的标签,即对应于每张输入图片实际的值,
    # 为了方便对比,我们获得标签后,将起转成one-hot格式
    label = tf.placeholder(tf.float32, [None, 10])

    # 搭建神经网络结构
    acc, op, keep_prob, loss = net(x, label)


    with tf.Session() as sess:
        # 初始化变量
        sess.run(tf.global_variables_initializer())
        coord = tf.train.Coordinator()
        try:
            # 这里才真的启动队列
            threads = tf.train.start_queue_runners(sess=sess, coord=coord)
            for i in range(190000):
                labels, images = sess.run([key, value])
                # print(labels)
                # 下面的方法可以很方便的将label转换成one-hot的形式
                labels = np.eye(10, dtype=float)[labels]

                # 将数据传入神经网络,开始训练
                sess.run(op, feed_dict={x: images, label: labels, keep_prob: 1.0})
                if i % 100 == 0:
                    train_accuracy = sess.run(acc, feed_dict={x: images, label: labels, keep_prob: 1.0})
                    print("step %d, training accuracy %g" % (i, train_accuracy))
        except:
            print('Done..')


        coord.request_stop()
        coord.join()

首先是下载数据集,然后获取图片数据和标签,接着创建占位符,值得注意的是,这里的占位符形状跟以前是不一样的。运行结果如下,

可以看到运行到18万多步的时候,准确率能到百分之九十多。可以看到,模型或数据集越复杂,需要训练的步数就越多。

3、完整代码

完整代码链接如下,

https://mianbaoduo.com/o/bread/YpeVlZ8=

 

 

下一讲,我们来学习带“记忆”的循环神经网络RNN。

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值