LetNET-5神经网络--tensorflow

LetNET-5神经网络–tensorflow实现

文章目录

  • LetNET-5神经网络--tensorflow实现
      • 卷积神经网络(Convolutional Neural Network, CNN)
      • 卷积基本操作
        • 卷积过程的计算
      • 池化基本操作
        • 池化过程的计算
      • LetNet-5结构:
        • 1. INPUT层(输入层)
        • 2. C1层(卷积层)
        • 3. S2层(池化层)
        • 4. C3层(卷积层)
        • 5. S4层(池化层)
        • 6. C5层(卷积层)
        • 7.F6层(全连接层)
        • 8.OUTPUT层(输出层)
      • 选择tensorflow
          • tensorflow使用计算图和sess的目的:
      • 代码

卷积神经网络(Convolutional Neural Network, CNN)

CNN是深度学习技术中极具代表的网络结构之一,在图像处理领域取得了很大的成功,在国际标准的ImageNet数据集上,许多成功的模型都是基于CNN的。CNN相较于传统的图像处理算法的优点之一在于,避免了对图像复杂的前期预处理过程(提取人工特征等),可以直接输入原始图像。

卷积基本操作

卷积操作可以说是CNN的核心了,它也正是得名于此,也正式卷积操作,让CNN得以拥有局部连接和权值共享这两件法宝。至于卷积操作到底是什么样子的呢?先看一个图吧。

LetNET-5神经网络--tensorflow_第1张图片

卷积操作的目的其实就是为了从原始数据中提取出所谓特征。假设说我们有一张6X6黑白图片,其像素信息为:LetNET-5神经网络--tensorflow_第2张图片

然后,这里我们有两个3X3的滤波器(Filter)或者称为卷积核(kernel):LetNET-5神经网络--tensorflow_第3张图片

所谓卷积操作就是使用这些卷积核,按照上面动图的规则来进行计算,我们就可以得到下面的结果(由圆形组成的矩阵),称之为特征图(Feature Map):LetNET-5神经网络--tensorflow_第4张图片

卷积过程的计算

o u t p u t = i n p u t + 2 ∗ p a d − C O N V s t r i d e + 1 output=\frac{input+2*pad-CONV}{stride}+1 output=strideinput+2padCONV+1

output:输出维度
input:输入维度
pad:扩充边缘
CONV:卷积核(滤波器)大小
stride:步长

池化基本操作

池化操作(pooling)的本质就是采样,可以采取的操作有:最大化、平均化、加和等。最常用并且被证明效果很好的池化操作为最大池化。

所谓最大池化就是,我们定义一个空间临域(比如一个2X2的窗口),并从窗口内的特征图中取出最大的元素。

LetNET-5神经网络--tensorflow_第5张图片

池化过程的计算

例:

H = h e i g h t , W = w i d t h , D = d e p t h H = height, W = width, D = depth H=height,W=width,D=depth

输入维度是 4x4x5 (HxWxD)
空间临域(采样区域)大小 2x2 (HxW)
stride 的高和宽都是 2 (S)
新的高和宽的公式是:
n e w h e i g h t = i n p u t h e i g h t − f i l t e r h e i g h t S + 1 new_{height}=\frac{input_{height} - filter_{height}}{S}+1 newheight=Sinputheightfilterheight+1
n e w h e i g h t = i n p u t w i d t h − f i l t e r w i d t h S + 1 new_{height}=\frac{input_{width} - filter_{width}}{S}+1 newheight=Sinputwidthfilterwidth+1

最大池化滤波器的大小是 2x2。当最大池化层在输入层滑动时,输出是这个 2x2 方块的最大值

LetNet-5结构:

LetNET-5神经网络--tensorflow_第6张图片

1. INPUT层(输入层)

  • 输入层
  • 输入图像的像素:32×32

2. C1层(卷积层)

  • 输入(input)维度:32×32
  • 卷积核(CONV)维度:5×5
  • 卷积核(CONV)种类:6
  • 步长(stride):1
  • 扩充边缘(pad):无
  • 输出(output)维度:6 @ 28×28 (6个28x28的特征图)

3. S2层(池化层)

  • 输入维度:28×28
  • 采样区域大小:2×2
  • 采样种类:6
  • 输出(output)维度:6 @ 14×14 (6个14x14的特征图)

4. C3层(卷积层)

  • 输入(input)维度:16 @ 14×14
  • 卷积核(CONV)维度:5×5
  • 卷积核(CONV)种类:16
  • 步长(stride):1
  • 扩充边缘(pad):无
  • 输出(output)维度:16 @ 10×10(16个10x10的特征图)

5. S4层(池化层)

  • 输入维度:16 @ 10×10
  • 采样区域大小:2×2
  • 采样种类:16
  • 输出(output)维度:16 @ 5×5 (16个5x5的特征图)

6. C5层(卷积层)

  • 输入(input)维度:16 @ 5×5
  • 卷积核(CONV)维度:5×5
  • 卷积核(CONV)种类:120
  • 步长(stride):1
  • 扩充边缘(pad):无
  • 输出(output)维度:120 @ 1×1(120个1x1的特征图)

7.F6层(全连接层)

一个简单的全连接层,它由84个神经元构成。和传统的全连接一样每个神经元将C5层中的特征图的值乘上相应的权重并相加,再加上对应的偏置再经过sigmoid激活函数。

8.OUTPUT层(输出层)

输出层由10个函数核构成,每个核对应0-9中的一个类别。输出值最小的那个核对应的i就是这个模型识别出来的数字。

选择tensorflow

tensorflow一词可拆解为tensor(张量)与flow(流动),顾名思义,张量在流动。

LetNET-5神经网络--tensorflow_第7张图片

tensorflow使用计算图和sess的目的:

实现回归模型中,为了用python实现高效的数值计算,我们通常会使用函数库,比如NumPy,会把类似矩阵乘法这样的复杂运算使用其他外部语言实现。不幸的是,从外部计算切换回Python的每一个操作,仍然是一个很大的开销。如果你用GPU来进行外部计算,这样的开销会更大。用分布式的计算方式,也会花费更多的资源用来传输数据。
TensorFlow也把复杂的计算放在python之外完成,但是为了避免前面说的那些开销,它做了进一步完善。Tensorflow不单独地运行单一的复杂计算,而是让我们可以先用图描述一系列可交互的计算操作,然后全部一起在Python之外运行。(这样类似的运行方式,可以在不少的机器学习库中看到。)

代码

代码来源

import tensorflow as tf
import numpy as np
from tensorflow.examples.tutorials.mnist import input_data
# input_data.read_data_sets函数生成的类会自动将MNIST数据集划分为train, validation和test三个数据集
mnist = input_data.read_data_sets("./MNIST_data", one_hot=True)

batch_size = 100
learning_rate = 0.01
learning_rate_decay = 0.99
max_steps = 30000


# 输入网络的尺寸为32×32×1
def hidden_layer(input_tensor,regularizer,avg_class,resuse):
    # 创建第一个卷积层,得到特征图大小为32@28x28
    # 这行代码指定了第一个卷积层作用域为C1-conv,在这个作用域下有两个变量conv1_weights和conv1_biases
    with tf.variable_scope("C1-conv",reuse=resuse):
    	# tf.get_variable共享变量
    	# [5, 5, 1, 32]卷积核大小为5×5×1,有32个
    	# stddev正太分布的标准差
        conv1_weights = tf.get_variable("weight", [5, 5, 1, 32],
                             initializer=tf.truncated_normal_initializer(stddev=0.1))
        # tf.constant_initializer初始化为常数,这个非常有用,通常偏置项就是用它初始化的
        conv1_biases = tf.get_variable("bias", [32], initializer=tf.constant_initializer(0.0))
        # strides:卷积时在图像每一维的步长,这是一个一维的向量,长度4
        # padding=’SAME’,表示padding后卷积的图与原图尺寸一致,激活函数relu()
        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))

    # 创建第一个池化层,池化后的结果为32@14x14
    # tf.name_scope的主要目的是为了更加方便地管理参数命名。
    # 与 tf.Variable() 结合使用。简化了命名
    with tf.name_scope("S2-max_pool",):
    	# ksize:池化窗口的大小,取一个四维向量,一般是[1, height, width, 1],
    	# 因为我们不想在batch和channels上做池化,所以这两个维度设为了1
    	# strides:窗口在每一个维度上滑动的步长,一般也是[1, stride,stride, 1]
        pool1 = tf.nn.max_pool(relu1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding="SAME")

    # 创建第二个卷积层,得到特征图大小为64@14x14。注意,第一个池化层之后得到了32个
    # 特征图,所以这里设输入的深度为32,我们在这一层选择的卷积核数量为64,所以输出
    # 的深度是64,也就是说有64个特征图
    with tf.variable_scope("C3-conv",reuse=resuse):
        conv2_weights = tf.get_variable("weight", [5, 5, 32, 64],
                                     initializer=tf.truncated_normal_initializer(stddev=0.1))
        conv2_biases = tf.get_variable("bias", [64], 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))

    # 创建第二个池化层,池化后结果为64@7x7
    with tf.name_scope("S4-max_pool",):
        pool2 = tf.nn.max_pool(relu2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding="SAME")
        # get_shape()函数可以得到这一层维度信息,由于每一层网络的输入输出都是一个batch的矩阵,
        # 所以通过get_shape()函数得到的维度信息会包含这个batch中数据的个数信息
        # shape[1]是长度方向,shape[2]是宽度方向,shape[3]是深度方向
        # shape[0]是一个batch中数据的个数,reshape()函数原型reshape(tensor,shape,name)
        shape = pool2.get_shape().as_list()
        nodes = shape[1] * shape[2] * shape[3]    # nodes=3136
        reshaped = tf.reshape(pool2, [shape[0], nodes])

    # 创建第一个全连层
    with tf.variable_scope("layer5-full1",reuse=resuse):
        Full_connection1_weights = tf.get_variable("weight", [nodes, 512],
                                      initializer=tf.truncated_normal_initializer(stddev=0.1))
        # if regularizer != None:
        tf.add_to_collection("losses", regularizer(Full_connection1_weights))
        Full_connection1_biases = tf.get_variable("bias", [512],
                                                     initializer=tf.constant_initializer(0.1))
        if avg_class ==None:
            Full_1 = tf.nn.relu(tf.matmul(reshaped, Full_connection1_weights) + \
                                                                   Full_connection1_biases)
        else:
            Full_1 = tf.nn.relu(tf.matmul(reshaped, avg_class.average(Full_connection1_weights))
                                                   + avg_class.average(Full_connection1_biases))

    # 创建第二个全连层
    with tf.variable_scope("layer6-full2",reuse=resuse):
        Full_connection2_weights = tf.get_variable("weight", [512, 10],
                                      initializer=tf.truncated_normal_initializer(stddev=0.1))
        # if regularizer != None:
        tf.add_to_collection("losses", regularizer(Full_connection2_weights))
        Full_connection2_biases = tf.get_variable("bias", [10],
                                                   initializer=tf.constant_initializer(0.1))
        if avg_class == None:
            result = tf.matmul(Full_1, Full_connection2_weights) + Full_connection2_biases
        else:
            result = tf.matmul(Full_1, avg_class.average(Full_connection2_weights)) + \
                                                  avg_class.average(Full_connection2_biases)
    return result



# tf.placeholder(dtype, shape=None, name=None)
x = tf.placeholder(tf.float32, [batch_size ,28,28,1],name="x-input")
y_ = tf.placeholder(tf.float32, [None, 10], name="y-input")


# L2正则化是一种减少过拟合的方法
regularizer = tf.contrib.layers.l2_regularizer(0.0001)

# 调用定义的CNN的函数
y = hidden_layer(x,regularizer,avg_class=None,resuse=False)
# 定义存储训练轮数的变量
training_step = tf.Variable(0, trainable=False)
# tf.train.ExponentialMovingAverage是指数加权平均的求法
# 可以加快训练早期变量的更新速度。
variable_averages = tf.train.ExponentialMovingAverage(0.99, training_step)
variables_averages_op = variable_averages.apply(tf.trainable_variables())

average_y = hidden_layer(x,regularizer,variable_averages,resuse=True)


# 使用交叉熵作为损失函数。这里使用
# sparse_softmax_cross_entropy_with_logits函数来计算交叉熵。因为手写体是一个长度为
# 10的一维数组,而该函数需要提供的是一个正确答案的数字,所以需要使用tf.argmax函数来
# 得到正确答案对应的类别编号
cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y, labels=tf.argmax(y_, 1))
# 计算在当前batch中所有样例的交叉熵平均值
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,# 基础的学习率,随着迭代的进行,更新变量时使用的学习率
                                 training_step, mnist.train.num_examples /batch_size ,
                                 learning_rate_decay, staircase=True)

# 使用tf.train.GradientDescentOptimizer优化算法来优化损失函数
train_step = tf.train.GradientDescentOptimizer(learning_rate). \
    minimize(loss, global_step=training_step)

with tf.control_dependencies([train_step, variables_averages_op]):
    train_op = tf.no_op(name='train')
crorent_predicition = tf.equal(tf.arg_max(average_y,1),tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(crorent_predicition,tf.float32))


# 初始化会话并开始训练过程
with tf.Session() as sess:
    tf.global_variables_initializer().run()
    for i in range(max_steps):
        if i %1000==0:
            x_val, y_val = mnist.validation.next_batch(batch_size)
            reshaped_x2 = np.reshape(x_val, (batch_size,28,28, 1))
            validate_feed = {x: reshaped_x2, y_: y_val}

            validate_accuracy = sess.run(accuracy, feed_dict=validate_feed)
            print("After %d trainging step(s) ,validation accuracy"
                  "using average model is %g%%" % (i, validate_accuracy * 100))

        x_train, y_train = mnist.train.next_batch(batch_size)

        reshaped_xs = np.reshape(x_train, (batch_size ,28,28,1))
        sess.run(train_op, feed_dict={x: reshaped_xs, y_: y_train})

参考来源:
1.https://blog.csdn.net/LLyj_/article/details/88933773#INPUT_39
2.https://www.charleychai.com/blogs/2018/ai/NN/lenet.html

你可能感兴趣的:(计算机视觉)