LeNet-5学习记录

LeNet-5学习记录

根据学长讲课的笔记https://blog.youkuaiyun.com/LLyj_/article/details/88933773
img

首先上一张图,如图所示这是LeNet-5模型的全部流程,我们这里输入的数据集是tensorflow自带的MNIST数据集,因此输入都是一张32 * 32 * 1的黑白图片(经过后期验证,因为下载下来的数据集是已经处理好的,所以最后输入的图片其实是28* 28的)。

前提:

padding

因为感觉对padding的操作存在不理解的地方,课后还询问了下学长,再搜了网上的博客准备分析一下在卷积操作中的padding方便之后的理解操作(虽然学长说这个不太重要但是我还是有点不太理解)。个人认为,卷积操作中如果能够理解了padding的操作的话,应该能够更好的理解卷积的操作,我觉得卷积的操作要先从padding 开始理解。

这里秉承着没图没真相的原则找了一张图:

img

我这里算过了,如果是用same的话第一层的结果算出来会是57,所以这个第一层我估计是VALID出来的。卷积核估计有32个,所以最后出来的是32x3=96个。OK翻篇,进入正题。

通过之前的学习,我们可以知道卷积是一个提取特征的过程,因此在操作中可能图片的分辨率会变小

padding的给出的算法有两种,“SAME"和"VAILD”,这里先考究一下两种算法是怎样算出来的:

(1)SAME

先给出SAME的尺寸计算结果:
n e w _ h i g h t h = n e w _ w i d t h = ⌈ W / S ⌉ new\_highth=new\_width=⌈W/S⌉ new_highth=new_width=W/S
SAME的最后取值是向上取整(和向下取整的区别就是向上取整就是如果最后的值为2.5,输出的结果就为3)

SAME的结果就是会根据步长来填充,所以一般来说,如果步长是1的话,最后输出的结果就和原来图片的大小一样。

(2)VALID

这个就比较凶残了,这个就是舍弃掉那些多余出来的点(因此我们大部分都会使用SAME)

计算公式:
n e w _ h i g h t h = n e w _ w i d t h = ⌈ ( W − F + 1 ) / S ⌉ new\_highth=new\_width=⌈(W-F+1)/S⌉ new_highth=new_width=(WF+1)/S
VALID最后的结果是向下取整

导入数据包:

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)#one_hot队标签转换成只有0和1

C1卷积层

之前在学长的讲课记录中,给出的卷积核是5 * 5 * 1 的六个卷积核,之后再代码实现中又用了5 * 5 * 1 * 32 的卷积,个人猜测可能是因为卷积核的数量太少的话,最后提取的特征不够精度会不够高。不过问题不大,这里不影响理解。

通过流程图,因为这个图片的输入是28 * 28 * 1 因为我们选择的padding是SAME,所以我们输出的图片还是28 * 28 图中的卷积结果的深度(通道)是6,因为我们最后的代码里用的是32个卷积核,所以我们最后输出的是28 * 28 * 32。然后这32个卷积出来的特征流向下一个池化层。

在这里插入图片描述

我们打印一下这个,确实如同上面得出的结果。

S2池化层

输入数据:28 * 28 的32个卷积特征图

池化的区域大小: 2 x 2

池化的步长:2x2(长度和宽度都为2)

在这里插入图片描述
这里两个维度都设置成了1.

经过池化层以后,因为我们这里用的是最大值池化,所以最后出来的结果根据步长和池化区域大小,出来的图片为:14 * 14 * 32(图片上写的是6,我们按照代码来,其实问题不大)。然后这些图片流向下一层的卷积层。

池化层结果就不打印了(略)

C3卷积层

输入数据:14 * 14 * 32(卷积核个数为64)

卷积核大小:5 * 5

卷积核种类(个数):64(图中为16)

我们这里的padding还是SAME因此根据上面的公式,我们计算得出,输出的特征大小为:14 * 14 * 64(总共64个的卷积核全部变成深度),转入下一个池化层。

在这里插入图片描述

S4池化层

输入数据:14 * 14 * 64

池化区域大小:2x2

池化步长:2x2

同S2池化层,这里的池化也是最大值池化,其实和上面的池化层没什么区别,就不具体说了,最后得出的64张特征图的大小为:7x7x64

C5全连接层(卷积层)

在这里插入图片描述

我们可以看到在S4之后有这样一段的话。nodes其实是结点数的意思,解释一下nodes=3136怎么来的,这个是7x7x64的结果,因为要全连接,我们需要的结点数就要刚刚好。并且卷积核的大小也和之前的一模一样,这样,我们最后才能得出那个一维的数据。(这个长条状的C5层)

在这里插入图片描述

这一层说是全连接层,其实他应该说也是一个卷积层!只不过这个卷积层的卷积核大小和之前的卷积核大小是

输入数据:S4层输出的所有的特征图

卷积核的大小:7x7

卷积核的个数:512

模型依照图中的全连接层,我们做了512个3136的一维向量,最后我们每一个结点都和他们相乘相加

F6全连接层

这一层在代码里面并没有实现,但是没有关系,这并不影响我们实现这个模型,这里按照模型进行分析。

输入:C5的512维向量

计算方式:计算输入向量和权重向量之间的点积,再加上一个偏置,结果通过sigmoid函数输出。(我复制的)

有84个神经元(如果我们也是用84个的话)

可训练参数:84×(512+1)=43092 (代码中)

	 84×(120+1)=10164 (图中)

OUTPUT层

Output层也是全连接层,共有10个神经元,分别代表数字0到9,且如果节点i的值为0,则网络识别的结果是数字i(复制的)

附上完整代码

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)#one_hot队标签转换成只有0和1

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))
        print(conv1)

    # 创建第一个池化层,池化后的结果为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))
        print(conv2)

    # 创建第二个池化层,池化后结果为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})

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值