• 第6章 图像识别与卷积神经网络

    • 6.1 图像识别问题简介及经典数据集
    • 6.2 卷积神经网络简介
    • 6.3 卷积神经网络常用结构
      • 6.3.1 卷积层
      • 6.3.2 池化层
    • 6.4 经典卷积神经网络模型
      • 6.4.1 LeNet-5模型
      • 6.4.2 Inception模型
    • 6.5 卷积神经网络实现迁移学习
      • 6.5.1 迁移学习的介绍

第6章 图像识别与卷积神经网络

本章通过利用CNN实现图像识别的应用来说明如何使用TensorFlow实现卷积神经网络

6.1 图像识别问题简介及经典数据集

1. Cifar

Cifar-10:10种不同种类的60000张图像,像素大小为32*32的彩色图像

Cifar-100:20 个大类,大类又细分为 100 个小类别,每类包含 600 张图像。

  • Cifar相比MNIST的最大区别:彩色,且每张图包含一个种类的实体分类难度更高。
  • 无论Cifar还是MNIST,相比真实环境的图像识别,还有两个主要的问题:
    1. 现实生活的图片分辨率远远高于32*32,且分辨率不固定;
    2. 现实生活中物体类别很多,且每张图像中不会仅仅出现一种物体;

2. ImageNet

  • 由斯坦福教授李飞飞(Feifei Li)带头整理的数据库,更加贴近真实生活环境。
  • Imagenet数据集有1400多万幅图片,涵盖2万多个类别;其中有超过百万的图片有明确的类别标注和图像中物体位置的标注,
  • 相关信息:
    1)Total number of non-empty synsets: 21841

    2)Total number of images: 14,197,122

    3)Number of images with bounding box annotations: 1,034,908

    4)Number of synsets with SIFT features: 1000

    5)Number of images with SIFT features: 1.2 million

6.2 卷积神经网络简介

前面所介绍的神经网络都为全连接神经网络,本节的卷积神经网络是非全连接的神经网络,全连接与卷积神经网络的结构对比如下:

两者之间最主要的区别就在于相邻两层之间的连接方式

1. 为什么全连接不能很好的处理图像数据

最大的问题在于全连接层参数太多,使得计算速度减慢,且容易导致过拟合问题。

2. 卷积神经网络的优势

卷积神经网络的前几层都被组织成一个三维矩阵,可以看出前几层的每个节点都是和上层中的部分节点相连,卷积神经网络由以下五部分构成:

1. 输入层

输入层是整个神经网络的输入,在图像处理中,输入一般代表一张图片的像素矩阵。在上图中,最左侧的三维矩阵就代表一张图片。三维矩阵的长和宽代表图像的大小,深度代表了图像的色彩通道。从输入层开始,卷积神经网络通过不同的神经网络结构将上一层的三维矩阵转化为下一层的三维矩阵,知道最后的全连接层。

2. 卷积层

卷积层是卷积神经网络中最重要的部分。卷积层中每一个节点的输入只是上一层神经网络的一小块,这个小块常用的大小有3∗3或5∗5,卷积层试图将神经网络中的每个小块进行更加深入的分析从而抽象得到更高层次的特征,一般来说经过卷积层处理过的节点矩阵会变得更深。

3. 池化层

池化层神经网络不会改变三维矩阵的深度,但是它可以缩小矩阵的大小。池化操作可以认为是将一张分辨率较高的图片转化为分辨率较低的图片。池化层可以进一步缩小最后全连接层中节点的个数,从而达到减少整个神经网络中参数的目的。

4. 全连接层

经过多轮卷积层和池化层的处理之后,卷积神经网络的最后一般会是由1-2个全连接层来给出最后的分类结果。经过几轮卷积层和池化层处理之后,可以认为图像中的信息已经被抽象成了信息含量更高的特征。 可以将卷积和池化层看成特征提取的过程,提取完成之后,仍然需要使用全连接层来完成分类任务。

5. Softmax层(pooling层)

Softmax层主要用于分类问题,通过softmax可以得到当前样例属于不同种类的概率分布情况。

6.3 卷积神经网络常用结构

6.3.1 卷积层

TensorFlow文档中将下图的部分被称为“过滤器”或“内核”,过滤器可以将当前层神经网络上的一个子节点矩阵转化为下一层神经网络上的一个单位节点矩阵(长和宽都为1,但深度不限的节点矩阵)。

  • 过滤器:

    1. 常用的过滤器的大小为 3*3 或 5*5 的,过滤器处理的矩阵深度和当前车网络节点矩阵的深度一致。
    2. 尺寸:过滤器输入节点矩阵的大小
    3. 深度:输出节点矩阵的深度
    4. 上图中,左侧小矩阵的尺寸为过滤器的尺寸,右侧单位矩阵的深度为过滤器的深度。
    5. 前向传播过程:通过左侧小矩阵的节点计算出右侧单位矩阵中节点的过程
  • 过滤器的前向传播
    卷积层结构的前向传播过程就是通过将一个过滤器从神经网络当前层的左上角移动到右下角,并且在移动过程中计算每一个对应的单位矩阵得到的。

    传播过程:左上角→→\to右上角→→\to左下角→→\to右下角

  • 全零填充:为了避免尺寸变化,可以使用“全零填充”,可以使得前向传播后两矩阵大小不变。

  • 设置不同的步长:也可以调整卷积后矩阵的大小

  • 参数共享:每一个卷积层中使用的滤波器中的参数都是一样的(很重要的性质)

    1. 使得图像上的内容不受位置的影响,因为一幅图上的滤波器是相同的,无论“1”出现在图中的哪个位置,滤波的结果都是一样的。

    2. 很大程度上减少神经网络的参数

  • 示例:全零填充、步长为2的卷积层前向传播过程


左上角的计算方式:

ReLU(0×1+0×(−1)+0×0+1×2+1)=ReLU(3)=3ReLU(0×1+0×(−1)+0×0+1×2+1)=ReLU(3)=3ReLU(0\times1+0\times(-1)+0\times0+1\times2+1)=ReLU(3)=3

  • TensorFlow实现卷积神经网络

1.创建滤波器的权值和偏置


filter_weight=tf.get_variable('weights',[5,5,3,16],initializer=tf.truncated_initializer(stddev=0.1))#  t通过tf.get_variable的方式创建滤波器的权值和偏置#  声明一个4维矩阵,前面两个是滤波器尺寸、第三个表示当前层深度、第四个表示过滤器的深度(也就是卷积核个数)
biases=tf.get_variable('biases',[16],initializer=tf.constant_initializer(0.1))#  当前层矩阵上不同位置的偏置项也是共享的,所以偏置项个数=下一层深度,本例为16

2.实现卷积层的前向传播

conv=tf.nn.conv2d(input,filter_weight,strides=[1,1,1,1],padding='SAME')#  tf.nn.conv2提供了一个非常方便的函数来实现卷积层的前向传播#  第一个输入:当前层节点矩阵# (比如输入层,input[0,:,:,:]表示输入第一张图像,input[1,:,:,:]表示输入第二张图像#  第二个参数:卷积层的权重#  第三个参数不同维度上的步长(第一维和最后一维要求一定是1,因为步长只对矩阵的长和宽有效)#  第四个参数:填充的方法,可选'SAME'(全0填充)/'VALID'(不填充)

3.加上偏置项

bias=tf.nn.bias_add(conv,biases)#  tf.nn.bias_add提供了一个方便的函数给每个节点加上偏置项#  不直接使用加法:因为矩阵上不同位置上的节点都需要加上相同的偏置项

4.激活

actived_conv=tf.nn.relu(bias)#  将计算结果通过ReLU函数激活

6.3.2 池化层

  • 作用:

    1. 减少参数
    2. 防止过拟合
    3. 获得平移不变性
  • 常用池化类型:

    1. 最大池化
    2. 平均池化
  • 池化层的作用范围:

    1. 只影响一个深度上的节点
    2. 在长、宽、深这三个维度都要进行移动

  • TensorFlow实现最大池化层的前向传播
pool = tf.nn.max_pool(actived_conv,ksize[1,3,3,1],strides=[1,2,2,1],padding='SAME')# 第一个参数:当前层节点矩阵# 第二个参数:过滤器尺寸#             给出的是一个长度为4的一位数组,但数组的首位和末位必须为1#             意味着池化层的过滤器是不可以跨过不同样例或节点矩阵深度的# 第三个参数:步长,第一维和最后一维必须为1,即池化层不能减少节点矩阵的深度或者输入样例的个数# 第四个参数:填充方法,'SAME'表示全0填充,'VALID'表示不填充
  • TensorFlow实现平均池化层的前向传播
pool = tf.nn.avg_pool(actived_conv,ksize[1,3,3,1],strides=[1,2,2,1],padding='SAME')# 第一个参数:当前层节点矩阵# 第二个参数:过滤器尺寸#             给出的是一个长度为4的一维数组,但数组的首位和末位必须为1#             意味着池化层的过滤器是不可以跨过不同样例或节点矩阵深度的# 第三个参数:步长,第一维和最后一维必须为1,即池化层不能减少节点矩阵的深度或者输入样例的个数# 第四个参数:填充方法,'SAME'表示全0填充,'VALID'表示不填充

卷积层、池化层样例

# 《TensorFlow实战Google深度学习框架》06 图像识别与卷积神经网络
# win10 Tensorflow1.0.1 python3.5.3
# CUDA v8.0 cudnn-8.0-windows10-x64-v5.1
# filename:ts06.01.py # 卷积层、池化层样例import tensorflow as tf
import numpy as np# 1. 输入矩阵
M = np.array([[[1],[-1],[0]],[[-1],[2],[1]],[[0],[2],[-2]]])
print("Matrix shape is: ",M.shape)
# Matrix shape is:  (3, 3, 1)# 2. 定义卷积过滤器, 深度为1
filter_weight = tf.get_variable('weights', [2, 2, 1, 1], initializer = tf.constant_initializer([[1, -1],[0, 2]]))
biases = tf.get_variable('biases', [1], initializer = tf.constant_initializer(1))# 3. 调整输入的格式符合TensorFlow的要求
M = np.asarray(M, dtype='float32')
M = M.reshape(1, 3, 3, 1)# 4. 计算矩阵通过卷积层过滤器和池化层过滤器计算后的结果
x = tf.placeholder('float32', [1, None, None, 1])
conv = tf.nn.conv2d(x, filter_weight, strides=[1, 2, 2, 1], padding='SAME')
bias = tf.nn.bias_add(conv, biases)
pool = tf.nn.avg_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
with tf.Session() as sess:tf.global_variables_initializer().run()convoluted_M = sess.run(bias, feed_dict={x: M})pooled_M = sess.run(pool, feed_dict={x: M})print("convoluted_M: \n", convoluted_M)print("pooled_M: \n", pooled_M)

输出:

Matrix shape is:  (3, 3, 1)
convoluted_M: [[[[ 7.],[ 1.]][[-1.],[-1.]]]]pooled_M: [[[[ 0.25],[ 0.5 ]][[ 1.  ],[-2.  ]]]]

6.4 经典卷积神经网络模型

6.4.1 LeNet-5模型

Yann LeCun 教授于1998年提出,是第一个成功用于数字识别的卷积神经网络,在mnist数据集上,可以达到99.2%的效果,共有7层,如下图所示。

输入原始图像的大小是32×32

1. 卷积层

输入:原始图像的像素(32*32*1)

过滤器:尺寸为5*5,深度为6,不使用全0填充,步长为1

输出:尺寸为 32-5+1=28,深度为6

参数个数:5*5*1*6+6=156,

下一层节点矩阵的节点:28*28*6=4704,每个节点和5*5=25 个当前层节点相连

本层卷积层共有连接个数:4704*(25+1)=122304

2. 池化层

输入:第一层的输出,是一个28*28*6的节点矩阵

过滤器:大小为2*2,长、宽、步长都为2

输出:14*14*6

3. 卷积层

输入:14*14*6

过滤器:大小为5*5,深度为16,不使用0填充,步长为1

输出:10*10*16,按标准的卷积层,本层应该有5*5*6*16+16=2416个参数

共有:10*10*16*(25+1)=41600个连接

4. 池化层

输入:10*10*16

过滤器:大小为2*2,步长为2

输出:矩阵大小为5*5*16

5. 全连接层

输入:5*5*16,本来论文中称本层为卷积层,但是因为滤波器大小为5*5,所以和全连接层没有区别,之后就将其看成全连接层。如果将矩阵5*5*16拉成一个向量,则和第四章的无区别

输出:节点个数为120

总共参数:5*5*16*120+120个参数。

6. 全连接层

输入:节点个数为120个

输出:节点个数为84个

总共参数:120*84+84=10164个

7. 全连接层

输入:84个节点

输出:10个节点

总共参数:84*10+10=850个

代码示例:

  • LeNet_inference.py
# 《TensorFlow实战Google深度学习框架》06 图像识别与卷积神经网络
# win10 Tensorflow1.0.1 python3.5.3
# CUDA v8.0 cudnn-8.0-windows10-x64-v5.1
# filename:LeNet5_infernece.py # LeNet5前向传播import tensorflow as tf# 1. 设定神经网络的参数
INPUT_NODE = 784
OUTPUT_NODE = 10IMAGE_SIZE = 28
NUM_CHANNELS = 1
NUM_LABELS = 10CONV1_DEEP = 32
CONV1_SIZE = 5CONV2_DEEP = 64
CONV2_SIZE = 5FC_SIZE = 512# 2. 定义前向传播的过程
def inference(input_tensor, train, regularizer):with tf.variable_scope('layer1-conv1'):conv1_weights = tf.get_variable("weight", [CONV1_SIZE, CONV1_SIZE, NUM_CHANNELS, CONV1_DEEP],initializer=tf.truncated_normal_initializer(stddev=0.1))conv1_biases = tf.get_variable("bias", [CONV1_DEEP],initializer=tf.constant_initializer(0.0))conv1 = tf.nn.conv2d(input_tensor, conv1_weights, strides=[1, 1, 1, 1],padding='SAME')relu1 = tf.nn.relu(tf.nn.bias_add(conv1, conv1_biases))with tf.name_scope("layer2-pool1"):pool1 = tf.nn.max_pool(relu1, ksize = [1,2,2,1],strides=[1,2,2,1],padding="SAME")with tf.variable_scope("layer3-conv2"):conv2_weights = tf.get_variable("weight", [CONV2_SIZE, CONV2_SIZE, CONV1_DEEP, CONV2_DEEP],initializer=tf.truncated_normal_initializer(stddev=0.1))conv2_biases = tf.get_variable("bias", [CONV2_DEEP],initializer=tf.constant_initializer(0.0))conv2 = tf.nn.conv2d(pool1, conv2_weights, strides=[1, 1, 1, 1], padding='SAME')relu2 = tf.nn.relu(tf.nn.bias_add(conv2, conv2_biases))with tf.name_scope("layer4-pool2"):pool2 = tf.nn.max_pool(relu2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1],padding='SAME')pool_shape = pool2.get_shape().as_list()nodes = pool_shape[1] * pool_shape[2] * pool_shape[3]reshaped = tf.reshape(pool2, [pool_shape[0], nodes])with tf.variable_scope('layer5-fc1'):fc1_weights = tf.get_variable("weight", [nodes, FC_SIZE],initializer=tf.truncated_normal_initializer(stddev=0.1))if regularizer != None: tf.add_to_collection('losses', regularizer(fc1_weights))fc1_biases = tf.get_variable("bias", [FC_SIZE], initializer=tf.constant_initializer(0.1))fc1 = tf.nn.relu(tf.matmul(reshaped, fc1_weights) + fc1_biases)if train: fc1 = tf.nn.dropout(fc1, 0.5)with tf.variable_scope('layer6-fc2'):fc2_weights = tf.get_variable("weight", [FC_SIZE, NUM_LABELS],initializer=tf.truncated_normal_initializer(stddev=0.1))if regularizer != None: tf.add_to_collection('losses', regularizer(fc2_weights))fc2_biases = tf.get_variable("bias", [NUM_LABELS],initializer=tf.constant_initializer(0.1))logit = tf.matmul(fc1, fc2_weights) + fc2_biasesreturn logit
  • LeNet_train.py
# 《TensorFlow实战Google深度学习框架》06 图像识别与卷积神经网络
# win10 Tensorflow1.0.1 python3.5.3
# CUDA v8.0 cudnn-8.0-windows10-x64-v5.1
# filename:LeNet5_train.py # LeNet5训练import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import LeNet_inference
import os
import numpy as np# 1. 定义神经网络相关的参数
BATCH_SIZE = 100
LEARNING_RATE_BASE = 0.01
LEARNING_RATE_DECAY = 0.99
REGULARIZATION_RATE = 0.0001
TRAINING_STEPS = 55000
MOVING_AVERAGE_DECAY = 0.99
MODEL_SAVE_PATH = "LeNet5_model/" # 在当前目录下存在LeNet5_model子文件夹
MODEL_NAME = "LeNet5_model"# 2. 定义训练过程
def train(mnist):# 定义输出为4维矩阵的placeholderx = tf.placeholder(tf.float32, [BATCH_SIZE,LeNet_inference.IMAGE_SIZE,LeNet_inference.IMAGE_SIZE,LeNet_inference.NUM_CHANNELS],name='x-input')y_ = tf.placeholder(tf.float32, [None, LeNet_inference.OUTPUT_NODE], name='y-input')regularizer = tf.contrib.layers.l2_regularizer(REGULARIZATION_RATE)y = LeNet_inference.inference(x, True, regularizer)global_step = tf.Variable(0, trainable=False)# 定义损失函数、学习率、滑动平均操作以及训练过程。variable_averages = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)variables_averages_op = variable_averages.apply(tf.trainable_variables())cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y, labels=tf.argmax(y_, 1))cross_entropy_mean = tf.reduce_mean(cross_entropy)loss = cross_entropy_mean + tf.add_n(tf.get_collection('losses'))learning_rate = tf.train.exponential_decay(LEARNING_RATE_BASE,global_step,mnist.train.num_examples / BATCH_SIZE, LEARNING_RATE_DECAY,staircase=True)train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss, global_step=global_step)with tf.control_dependencies([train_step, variables_averages_op]):train_op = tf.no_op(name='train')# 初始化TensorFlow持久化类。saver = tf.train.Saver()with tf.Session() as sess:tf.global_variables_initializer().run()for i in range(TRAINING_STEPS):xs, ys = mnist.train.next_batch(BATCH_SIZE)reshaped_xs = np.reshape(xs, (BATCH_SIZE,LeNet_inference.IMAGE_SIZE,LeNet_inference.IMAGE_SIZE,LeNet_inference.NUM_CHANNELS))_, loss_value, step = sess.run([train_op, loss, global_step], feed_dict={x: reshaped_xs, y_: ys})if i % 1000 == 0:print("After %d training step(s), loss on training batch is %g." % (step, loss_value))saver.save(sess, os.path.join(MODEL_SAVE_PATH, MODEL_NAME), global_step=global_step)# 3. 主程序入口
def main(argv=None):mnist = input_data.read_data_sets("MNIST_data", one_hot=True)train(mnist)if __name__ == '__main__':tf.app.run()

输出:

Extracting MNIST_data\train-images-idx3-ubyte.gz
Extracting MNIST_data\train-labels-idx1-ubyte.gz
Extracting MNIST_data\t10k-images-idx3-ubyte.gz
Extracting MNIST_data\t10k-labels-idx1-ubyte.gzAfter 1 training step(s), loss on training batch is 6.10953.
After 1001 training step(s), loss on training batch is 0.801586.
After 2001 training step(s), loss on training batch is 0.829287.
After 3001 training step(s), loss on training batch is 0.655056.
After 4001 training step(s), loss on training batch is 0.698159.
After 5001 training step(s), loss on training batch is 0.744295.
After 6001 training step(s), loss on training batch is 0.657604.
After 7001 training step(s), loss on training batch is 0.697003.
After 8001 training step(s), loss on training batch is 0.685206.
After 9001 training step(s), loss on training batch is 0.651352.
After 10001 training step(s), loss on training batch is 0.729663.
After 11001 training step(s), loss on training batch is 0.666927.
After 12001 training step(s), loss on training batch is 0.65114.
After 13001 training step(s), loss on training batch is 0.648548.
...
  • LeNet_eval.py(测试该网络在mnist的正确率,达到99.4%,巨幅高于第五章的98.4%)
# 《TensorFlow实战Google深度学习框架》06 图像识别与卷积神经网络
# win10 Tensorflow1.0.1 python3.5.3
# CUDA v8.0 cudnn-8.0-windows10-x64-v5.1
# filename:LeNet5_eval.py # 测试import time
import math
import tensorflow as tf
import numpy as np
from tensorflow.examples.tutorials.mnist import input_data
import LeNet5_infernece
import LeNet5_traindef evaluate(mnist):with tf.Graph().as_default() as g:# 定义输出为4维矩阵的placeholderx = tf.placeholder(tf.float32, [mnist.test.num_examples,#LeNet5_train.BATCH_SIZE,LeNet5_infernece.IMAGE_SIZE,LeNet5_infernece.IMAGE_SIZE,LeNet5_infernece.NUM_CHANNELS],name='x-input')y_ = tf.placeholder(tf.float32, [None, LeNet5_infernece.OUTPUT_NODE], name='y-input')validate_feed = {x: mnist.test.images, y_: mnist.test.labels}global_step = tf.Variable(0, trainable=False)regularizer = tf.contrib.layers.l2_regularizer(LeNet5_train.REGULARIZATION_RATE)y = LeNet5_infernece.inference(x, False, regularizer)correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))variable_averages = tf.train.ExponentialMovingAverage(LeNet5_train.MOVING_AVERAGE_DECAY)variables_to_restore = variable_averages.variables_to_restore()saver = tf.train.Saver(variables_to_restore)#n = math.ceil(mnist.test.num_examples / LeNet5_train.BATCH_SIZE)n = math.ceil(mnist.test.num_examples / mnist.test.num_examples)for i in range(n):with tf.Session() as sess:ckpt = tf.train.get_checkpoint_state(LeNet5_train.MODEL_SAVE_PATH)if ckpt and ckpt.model_checkpoint_path:saver.restore(sess, ckpt.model_checkpoint_path)global_step = ckpt.model_checkpoint_path.split('/')[-1].split('-')[-1]xs, ys = mnist.test.next_batch(mnist.test.num_examples)#xs, ys = mnist.test.next_batch(LeNet5_train.BATCH_SIZE)reshaped_xs = np.reshape(xs, (mnist.test.num_examples,#LeNet5_train.BATCH_SIZE,LeNet5_infernece.IMAGE_SIZE,LeNet5_infernece.IMAGE_SIZE,LeNet5_infernece.NUM_CHANNELS))accuracy_score = sess.run(accuracy, feed_dict={x:reshaped_xs, y_:ys})print("After %s training step(s), test accuracy = %g" % (global_step, accuracy_score))else:print('No checkpoint file found')return# 主程序
def main(argv=None):mnist = input_data.read_data_sets("../../../datasets/MNIST_data", one_hot=True)evaluate(mnist)if __name__ == '__main__':tf.app.run()

LeNet-5模型的缺点
不能很好的解决所有问题,比如类似ImageNet的复杂数据集

如何设计卷积神经网络架构
该正则表达公式总结了经典的用于图像分类问题的卷积神经网络架构

输入层→→\to(卷积层+→→\to池化层?)+→→\to全连接层+
其中:

  1. +表示一层或多层
  2. “池化层?”表示没有或一层池化层,因为有的网络是通过直接调整卷积层步长来完成的参数减少
  3. 多伦卷积核池化之后,输出层之前会有1~2个全连接层

LeNet-5的正则表达:

输入层→→\to卷积层→→\to池化层→→\to卷积层→→\to池化层→→\to全连接层→→\to全连接层→→\to输出层

从VGGNet观察正则表达式的特点:

  1. convX-Y:滤波器的边长为X,深度为Y

  2. VGGNet中的滤波器的边长基本都为3或1

  3. LeNet-5中使用了大小为5*5的滤波器,一般都不会超过5,但也有的设置为7*7,甚至11*11的

  4. 在滤波器的深度选择上,大部分都采用逐层递增的方式,VGG中,每经过一个池化层,滤波器的深度*2,

  5. 卷积层的步长一般为2,但也有例外(使用2或3)

  6. 池化层配置相对简单,用的最多的是最大池化层,边长一般为2或3,步长一般为2或3

6.4.2 Inception模型

已知滤波器的大小可选,但是没有先验知识帮助我们去选择其大小,所以Inception模型将不同尺寸的滤波器的滤波结果通过并联的方式结合在一起,即将得到的矩阵拼接起来。

Inception模型会使用不同尺寸的滤波器处理矩阵,使用全0填充和步长为1的方法获得的特征图谱大小是相同的,不会影响矩阵的拼接。

Inception-v3模型共有46层,由11个Inception模块构成,上图中方框标注的就是一个模块,Inception-v3模型有96个卷积层,直接使用上文的TensorFlow模型会非常冗长,所以此处介绍TensorFlow-Slim工具来更加简洁的实现同样结构的神经网络代码量。

1.直接使用TensorFlow原始API实现卷积层

with tf.variable_scope(scope_name):weights=tf.get_variable("weight",...)biases=tf.get_variable("bias",...)conv=tf.nn.conv2d(...)relu=tf.nn.relu(tf.nn.bias_add(conv,biases))

2.使用TensorFlow-Slim实现卷积层

tf.contrib.slim.conv2d (inputs,num_outputs,#[卷积核个数]kernel_size,#[高度,宽度]stride=1,#步长padding='SAME',#VALID

存在于tensorflow.contrib的库中,导入方法:import tensorflow.contrib.slim as slim

net=slim.conv2d(input,32,[3,3])
# 可以在一行中实现一个卷积层的前向传播算法
# 第一个参数:输入节点矩阵
# 第二个参数:当前卷积层过滤器的深度
# 第三个参数:过滤器的尺寸
# 可选步长、填充、激活函数、变量命名空间等

6.5 卷积神经网络实现迁移学习

6.5.1 迁移学习的介绍

从上表可以看出,随着模型层数及复杂度的增加,模型在ImageNet上的错误率也随之降低,然而训练神经网络需要非常多的标注数据,ImageNet数据集中有120万标注图像,所以才能使得152层的ResNet模型达到分类的96.5%的正确率,实际中很难收集到如此多的标记数据,即使收集也会花费很多的人力物力,并且海量的训练数据使得复杂的卷积神经网络的训练需要几天甚至几周,为了解决标注数据和训练时间的问题,提出了迁移学习。

迁移学习:将一个问题上训练好的模型通过简单的调整使其适用于一个新的问题。比如可以保留训练好的Inception-v3模型中所有卷积层的参数,只是替换最后一层全连接层,可以解决一个新的图像分类问题。

瓶颈层:在最后这一层全连接层之前的网络层称之为瓶颈层。

  • 将新的图像通过训练好的卷积神经网络直到瓶颈层的过程可以看成是对图像进行特征提取的过程。
  • 可以认为瓶颈层的输出节点向量可以被作为任何图像的一个更加精简且表达能力更强的特征向量,于是可以用该训练好的神经网络在新的数据集上完成对图像特征的提取。
  • 再将提取到的特征作为输入来训练一个新的单层全连接神经网络处理新的分类问题

迁移学习相比重新训练的优缺点:

  • 缺点:效果不如完全重新训练
  • 优点:需要的训练时间和训练样本数要远远小于训练完整的模型

代码实现:

# 《TensorFlow实战Google深度学习框架》06 图像识别与卷积神经网络
# win10 Tensorflow1.0.1 python3.5.3
# CUDA v8.0 cudnn-8.0-windows10-x64-v5.1
# filename:ts06.03.py # 迁移学习# 以下实验需要如下资源
# 源码及资源位置:git clone https://github.com/caicloud/tensorflow-tutorial.git
# 需要tensorflow-tutorial.git库中的flower_photos和inception_dec_2015
# tensorflow-tutorial\Deep_Learning_with_TensorFlow\datasets\flower_photos
# tensorflow-tutorial\Deep_Learning_with_TensorFlow\datasets\inception_dec_2015import glob
import os.path
import random
import numpy as np
import tensorflow as tf
from tensorflow.python.platform import gfile# 1. 模型和样本路径的设置
# inception-v3模型瓶颈层的节点数
BOTTLENECK_TENSOR_SIZE = 2048# inception-v3模型中代表瓶颈层结果的张量名称,谷歌提供的inception-v3模型中,这个张量名称就是'pool_3/_reshape:0'
# 训练过程中,可以利用tensor.name来获取张量的名称
BOTTLENECK_TENSOR_NAME = 'pool_3/_reshape:0'# 图像输入张量所对应的名称
JPEG_DATA_TENSOR_NAME = 'DecodeJpeg/contents:0'# 保存的模型文件目录
MODEL_DIR = '../../datasets/inception_dec_2015'
# 下载的谷歌训练好的inception-v3模型文件名
MODEL_FILE= 'tensorflow_inception_graph.pb'# 因为一个训练数据会被使用多次,所以可以将原始图像通过inception-v3模型计算得到的特征向量保存在文件中,
# 以免重复计算,下面是这些文件的存放地址。
CACHE_DIR = '../../datasets/bottleneck'# 图片数据文件夹
# 该文件夹中每一个子文件夹代表一个需要区分的类别,每个子文件夹中存放了对应类别的图片
INPUT_DATA = '../../datasets/flower_photos'# 验证集百分比
VALIDATION_PERCENTAGE = 10
# 测试集百分比
TEST_PERCENTAGE = 10# 2. 神经网络参数的设置
LEARNING_RATE = 0.01
STEPS = 4000
BATCH = 100# 3. 把样本中所有的图片列表并按训练、验证、测试数据分开
# testing_percentage:测试数据集的大小
# validation_percentage:验证数据集的大小
def create_image_lists(testing_percentage, validation_percentage):# 定义一个result字典,key存储类别名称, 与key对应的value也是一个字典,存储所有该类的图片名称result = {}# 获取当前目录下所有的子目录sub_dirs = [x[0] for x in os.walk(INPUT_DATA)]is_root_dir = Truefor sub_dir in sub_dirs:if is_root_dir:is_root_dir = Falsecontinueextensions = ['jpg', 'jpeg', 'JPG', 'JPEG']file_list = []dir_name = os.path.basename(sub_dir)for extension in extensions:file_glob = os.path.join(INPUT_DATA, dir_name, '*.' + extension)file_list.extend(glob.glob(file_glob))if not file_list: continuelabel_name = dir_name.lower()# 初始化training_images = []testing_images = []validation_images = []for file_name in file_list:base_name = os.path.basename(file_name)# 随机划分数据chance = np.random.randint(100)if chance < validation_percentage:validation_images.append(base_name)elif chance < (testing_percentage + validation_percentage):testing_images.append(base_name)else:training_images.append(base_name)result[label_name] = {'dir': dir_name,'training': training_images,'testing': testing_images,'validation': validation_images,}return result# 4. 定义函数通过类别名称、所属数据集和图片编号获取一张图片的地址
def get_image_path(image_lists, image_dir, label_name, index, category):label_lists = image_lists[label_name]category_list = label_lists[category]mod_index = index % len(category_list)base_name = category_list[mod_index]sub_dir = label_lists['dir']full_path = os.path.join(image_dir, sub_dir, base_name)return full_path# 5. 定义函数获取Inception-v3模型处理之后的特征向量的文件地址
def get_bottleneck_path(image_lists, label_name, index, category):return get_image_path(image_lists, CACHE_DIR, label_name, index, category) + '.txt'# 6. 定义函数使用加载的训练好的Inception-v3模型处理一张图片,得到这个图片的特征向量
# 该过程实际上就是将当前图片作为输入九三瓶颈张量的值,这个瓶颈张量的值就是该图像的新的特征向量
def run_bottleneck_on_image(sess, image_data, image_data_tensor, bottleneck_tensor):bottleneck_values = sess.run(bottleneck_tensor, {image_data_tensor: image_data})# 将四维数组压缩为一个特征向量bottleneck_values = np.squeeze(bottleneck_values)return bottleneck_values# 7. 定义函数会先试图寻找已经计算且保存下来的特征向量,如果找不到则先计算这个特征向量,然后保存到文件
def get_or_create_bottleneck(sess, image_lists, label_name, index, category, jpeg_data_tensor, bottleneck_tensor):label_lists = image_lists[label_name]sub_dir = label_lists['dir']sub_dir_path = os.path.join(CACHE_DIR, sub_dir)if not os.path.exists(sub_dir_path): os.makedirs(sub_dir_path)bottleneck_path = get_bottleneck_path(image_lists, label_name, index, category)if not os.path.exists(bottleneck_path):image_path = get_image_path(image_lists, INPUT_DATA, label_name, index, category)image_data = gfile.FastGFile(image_path, 'rb').read()bottleneck_values = run_bottleneck_on_image(sess, image_data, jpeg_data_tensor, bottleneck_tensor)bottleneck_string = ','.join(str(x) for x in bottleneck_values)with open(bottleneck_path, 'w') as bottleneck_file:bottleneck_file.write(bottleneck_string)else:with open(bottleneck_path, 'r') as bottleneck_file:bottleneck_string = bottleneck_file.read()bottleneck_values = [float(x) for x in bottleneck_string.split(',')]return bottleneck_values# 8. 这个函数随机获取一个batch的图片作为训练数据
def get_random_cached_bottlenecks(sess, n_classes, image_lists, how_many, category, jpeg_data_tensor, bottleneck_tensor):bottlenecks = []ground_truths = []for _ in range(how_many):label_index = random.randrange(n_classes)label_name = list(image_lists.keys())[label_index]image_index = random.randrange(65536)bottleneck = get_or_create_bottleneck(sess, image_lists, label_name, image_index, category, jpeg_data_tensor, bottleneck_tensor)ground_truth = np.zeros(n_classes, dtype=np.float32)ground_truth[label_index] = 1.0bottlenecks.append(bottleneck)ground_truths.append(ground_truth)return bottlenecks, ground_truths# 9. 这个函数获取全部的测试数据,并计算正确率
def get_test_bottlenecks(sess, image_lists, n_classes, jpeg_data_tensor, bottleneck_tensor):bottlenecks = []ground_truths = []label_name_list = list(image_lists.keys())for label_index, label_name in enumerate(label_name_list):category = 'testing'for index, unused_base_name in enumerate(image_lists[label_name][category]):bottleneck = get_or_create_bottleneck(sess, image_lists, label_name, index, category,jpeg_data_tensor, bottleneck_tensor)ground_truth = np.zeros(n_classes, dtype=np.float32)ground_truth[label_index] = 1.0bottlenecks.append(bottleneck)ground_truths.append(ground_truth)return bottlenecks, ground_truths# 10. 定义主函数
def main():image_lists = create_image_lists(TEST_PERCENTAGE, VALIDATION_PERCENTAGE)n_classes = len(image_lists.keys())# 读取已经训练好的Inception-v3模型。with gfile.FastGFile(os.path.join(MODEL_DIR, MODEL_FILE), 'rb') as f:graph_def = tf.GraphDef()graph_def.ParseFromString(f.read())bottleneck_tensor, jpeg_data_tensor = tf.import_graph_def(graph_def, return_elements=[BOTTLENECK_TENSOR_NAME, JPEG_DATA_TENSOR_NAME])# 定义新的神经网络输入bottleneck_input = tf.placeholder(tf.float32, [None, BOTTLENECK_TENSOR_SIZE], name='BottleneckInputPlaceholder')ground_truth_input = tf.placeholder(tf.float32, [None, n_classes], name='GroundTruthInput')# 定义一层全链接层with tf.name_scope('final_training_ops'):weights = tf.Variable(tf.truncated_normal([BOTTLENECK_TENSOR_SIZE, n_classes], stddev=0.001))biases = tf.Variable(tf.zeros([n_classes]))logits = tf.matmul(bottleneck_input, weights) + biasesfinal_tensor = tf.nn.softmax(logits)# 定义交叉熵损失函数。cross_entropy = tf.nn.softmax_cross_entropy_with_logits(logits=logits, labels=ground_truth_input)cross_entropy_mean = tf.reduce_mean(cross_entropy)train_step = tf.train.GradientDescentOptimizer(LEARNING_RATE).minimize(cross_entropy_mean)# 计算正确率。with tf.name_scope('evaluation'):correct_prediction = tf.equal(tf.argmax(final_tensor, 1), tf.argmax(ground_truth_input, 1))evaluation_step = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))with tf.Session() as sess:init = tf.global_variables_initializer()sess.run(init)# 训练过程。for i in range(STEPS):train_bottlenecks, train_ground_truth = get_random_cached_bottlenecks(sess, n_classes, image_lists, BATCH, 'training', jpeg_data_tensor, bottleneck_tensor)sess.run(train_step,feed_dict={bottleneck_input: train_bottlenecks, ground_truth_input: train_ground_truth})if i % 100 == 0 or i + 1 == STEPS:validation_bottlenecks, validation_ground_truth = get_random_cached_bottlenecks(sess, n_classes, image_lists, BATCH, 'validation', jpeg_data_tensor, bottleneck_tensor)validation_accuracy = sess.run(evaluation_step, feed_dict={bottleneck_input: validation_bottlenecks, ground_truth_input: validation_ground_truth})print('Step %d: Validation accuracy on random sampled %d examples = %.1f%%' %(i, BATCH, validation_accuracy * 100))# 在最后的测试数据上测试正确率。test_bottlenecks, test_ground_truth = get_test_bottlenecks(sess, image_lists, n_classes, jpeg_data_tensor, bottleneck_tensor)test_accuracy = sess.run(evaluation_step, feed_dict={bottleneck_input: test_bottlenecks, ground_truth_input: test_ground_truth})print('Final test accuracy = %.1f%%' % (test_accuracy * 100))if __name__ == '__main__':tf.app.run()

TensorFlow:实战Google深度学习框架(五)图像识别与卷积神经网络相关推荐

  1. 06.图像识别与卷积神经网络------《Tensorflow实战Google深度学习框架》笔记

    一.图像识别问题简介及经典数据集 图像识别问题希望借助计算机程序来处理.分析和理解图片中的内容,使得计算机可以从图片中自动识别各种不同模式的目标和对象.图像识别问题作为人工智能的一个重要领域,在最近几 ...

  2. (转)Tensorflow 实战Google深度学习框架 读书笔记

    本文大致脉络: 读书笔记的自我说明 对读书笔记的摘要 具体章节的摘要: 第一章 深度学习简介 第二章 TensorFlow环境搭建 第三章 TensorFlow入门 第四章 深层神经网络 第五章 MN ...

  3. 免费教材丨第55期:Python机器学习实践指南、Tensorflow 实战Google深度学习框架

    小编说  时间过的好快啊,小伙伴们是不是都快进入寒假啦?但是学习可不要落下哦!  本期教材  本期为大家发放的教材为:<Python机器学习实践指南>.<Tensorflow 实战G ...

  4. 《Tensorflow 实战google深度学习框架》第二版源代码

    <<Tensorflow 实战google深度学习框架–第二版>> 完整资料github地址: https://github.com/caicloud/tensorflow-t ...

  5. 学习《TensorFlow实战Google深度学习框架 (第2版) 》中文PDF和代码

    TensorFlow是谷歌2015年开源的主流深度学习框架,目前已得到广泛应用.<TensorFlow:实战Google深度学习框架(第2版)>为TensorFlow入门参考书,帮助快速. ...

  6. 说说TensorFlow实战Google深度学习框架

    说说TensorFlow实战Google深度学习框架 事情是这样的,博主买了这本书,但是碍于想在电脑上边看边码,想找找PDF版本,然后各种百度,Google,百度网盘,最后找到的都是很多200M的,百 ...

  7. TensorFlow实战Google深度学习框架

    TensorFlow是谷歌2015年开源的主流深度学习框架.科技届的聚光灯已经从"互联网+"转到了"AI+": 掌握深度学习需要较强的理论功底,用好Tensor ...

  8. TensorFlow实战Google深度学习框架5-7章学习笔记

    目录 第5章 MNIST数字识别问题 第6章 图像识别与卷积神经网络 第7章 图像数据处理 第5章 MNIST数字识别问题 MNIST是一个非常有名的手写体数字识别数据集,在很多资料中,这个数据集都会 ...

  9. TensorFlow:实战Google深度学习框架(三)深层神经网络

    第四章 深层神经网络 4.1 深度学习与深层神经网络 4.1.1 线性模型的局限性 4.1.2 激活函数实现非线性化 4.1.3 多层网络解决异或问题 4.2 损失函数 4.2.1 经典损失函数 1. ...

  10. tensorflow实战google深度学习框架在线阅读

    https://max.book118.com/html/2019/0317/7112141026002014.shtm

最新文章

  1. 同时拿到BATJMD的Offer是怎样的一种体验?
  2. php bootstraptable分页,php+bootstrap+dataTable+jquery分页列表
  3. tableau实战系列(三十五)-教你画个不一样的圆角条形图
  4. 微信(支付宝)小程序蓝牙4.0线上项目
  5. ICCV 2019 | 微软开源无监督学习的医学图像配准方法:递归级联网络
  6. java怎么中断流_JAVA代码运行中断
  7. java程序设计简明教程张晓龙_Java网络编程简明教程
  8. 图像平均池化 利用pytorch对图像进行池化
  9. Vector, ArrayList, LinkedList分析
  10. IT项目管理的三个约束条件、五个实施步骤
  11. vue 检测ie版本_vue判断当前浏览器为IE低版本,给出升级提示;IE11及其他浏览器正常使用-Go语言中文社区...
  12. 渗透工具-masscan
  13. Spring boot集成海康威视门禁设备
  14. 【正点原子FPGA连载】第三章 硬件资源详解 -摘自【正点原子】新起点之FPGA开发指南_V2.1
  15. 美敦力PB 560呼吸机设计方案(包含原理图+PCB+BOM表+CAD)
  16. 【电脑修复小知识】电脑网络正常连接,但是浏览器却无法打开网页是什么原因?三招教你修复!
  17. 计算机毕业设计ssm社区疫情防控管理系统nt4k1系统+程序+源码+lw+远程部署
  18. c语言如何判断一个带分数整数部分,2013年第四届蓝桥杯省赛C语言B组
  19. Ural1671 Anansi's Cobweb
  20. Error : connect Time out

热门文章

  1. 制造机器人的现状和发展趋势
  2. Pandas学习笔记1(序列部分)
  3. iOS 开发之获取时间到年底可能会踩到的坑
  4. 绝对定位多个字居中显示的css
  5. Derby的jar说明
  6. 《开源思索集》一黑客的胜利——读《增长黑客》有感
  7. ffplay的快捷键以及选项
  8. Shell and powershell
  9. 把有无线网卡的机子当作无线ap无需任何软件
  10. 【动画技巧】在Flash中自定义鼠标外观