从零构建LeNet(一)附代码详解

代码引用:https://github.com/ganyc717/LeNet

讲真,研一开始了这么久,我还从未自己从头到尾理解和构建一个神经网络,虽然总是跟别人讨论cnn什么的。

事情总要自己来干,那就从lenet开始吧!

先放上lenet的大图;我们从零开始构建网络。

首先,原来我以为构建一个神经网络就是这个神经网络的问题,但是后来发现,不仅仅是构建网络的问题,首先,你要去适应Tensorflow的规则和机制。这就好像,你进入一个新的领域,不是马上就可以施展手脚的,你要先明白这个地方做事有什么规则,不要觉得禁锢,在规则内行事也并不一定不能实现自己~

封装的思想

哎,代码量真的太少,封装的思想太差了。首先基本的封装思想就是,训练一个文件,模型一个文件。

这个思想贯穿整个编码过程。一定要从一开始就有分割的想法。

训练文件

训练文件其实就是整个训练的一个过程。我们先读取数据,然后在进行训练,进行参数的更替。最后保存训练的参数。

读取文件的方法暂且不表,待会再说。

先说一下tensorflow的运行机制,通过session进行运行。这也就是为什么大家都说tf是通过计算图来构建的。

刚开始接触tf的时候一直有一个疑问,为什么我们不能直接通过python中的机制来进行运算呢?其实这就是我上面所说的规则。

引用一段话:

TensorFlow的运行机制属于"定义"与”运行“相分离。从操作层面可以抽象成两种:构造模型和模型运行。

在讲解构建模型之前,需要讲解几个概念。在一个叫做"图"的容器中包括:

  • 张量(tensor):TensorFlow程序使用tensor数据结构来代表所有的数据,计算图中,操作间传递的数据都是tensor,你可以把TensorFlow tensor看做一个n维的数组或者列表。
  • 变量(Variable):常用于定义模型中的参数,是通过不断训练得到的值。比如权重和偏置。
  • 占位符(placeholder):输入变量的载体。也可以理解成定义函数时的参数。
  • 图中的节点操作(op):一个op获得0个或者多个Tensor,执行计算,产生0个或者多个Tensor。op是描述张量中的运算关系,是网络中真正结构。

一个TensorFlow图描述了计算的过程,为了进行计算,图必须在会话里启动,会话将图的op分发到诸如CPU或者GPU的设备上,同时提供执行op的方法,这些方法执行后,将产生的tensor返回,在python语言中,返回的tensor是numpy array对象,在C或者C++语言中,返回的tensor是tensorflow:Tensor实例。

 session与图的交互过程中定义了以下两种数据的流向机制。

  • 注入机制(feed):通过占位符向模式中传入数据。
  • 取回机制(fetch):从模式中取得结果。

来源:https://www.cnblogs.com/zyly/p/8869763.html

理解一下,通常学习的语言中都是写代码的过程即运行的的过程。但tensorflow不是,你写的很多代码,如果不喊开始,数据是不会进入这个框架的;你定义了参数,定义了张量,只是说了要做这样的操作,但,并没有去做,要通过session来进行调用,将所有的参数包裹进来,也就是使用global_variables_intializer()来开始整个过程。

训练的batch

当我们看理论知识的时候,神经网络的更新是一次次更新的;

但训练中我们往往使用mini-batch进行训练,其训练方法是什么呢?

我们看图吧:

t 代表第几个子集,从 1 到 5000,因为划分后,一共有 5000 个子集,

1. 对每个子集,先进行前向计算,从第一层网络到最后一层输出层

因为 batch 梯度下降是对整个数据集进行处理,所以不需要角标,而 mini batch 这里需要对 x 加上角标,代表的是第几个子集。

2. 接下来计算当前子集的损失函数,因为子集中一共有 1000 个样本,所以这里要除以 1000。损失函数也是有上角标,和第几个子集相对应。

3. 然后进行反向传播,计算损失函数 J 的梯度。

4. 最后更新参数。

将 5000 个子集都计算完时,就是进行了一个 epoch 处理 ,一个 epoch 意思是遍历整个数据集,即 5000 个子数据集一次,也就是做了 5000 个梯度下降,
如果需要做多次遍历,就需要对 epoch 进行循环。当数据集很大的时候,这个方法是经常被使用的。
作者:Alice嘟嘟
链接:https://www.imooc.com/article/48566
来源:慕课网
本文原创发布于慕课网 ,转载请注明出处,谢谢合作

以上把batch的整个计算过程讲的很清楚了。开始学习的时候我们是一个样本进行更新,用batch的时候,我们把这些数据的loss相加求均值,当然也有一种方法,对这些loss取不同的权重。然后根据整体损失函数的值进行反向传播,更新参数。

注入机制和占位符

tensorflow有很多种建立参数的形式,其中一种是placeholder;它被称为占位符,就是我定义一个变量,定义它的数据类型,但是不赋值,等到用的时候再进行赋值。这就是注入机制,随时用随时注入。

'''
4.注入机制
'''
a = tf.placeholder(dtype=tf.float32)
b = tf.placeholder(dtype=tf.float32)
add = a + b
product = a*b
with tf.Session() as sess:
    #启动图后,变量必须先经过'初始化' op 
    sess.run(tf.global_variables_initializer())
    print(' a + b =  {0}'.format(sess.run(add,feed_dict={a:3,b:4})))
    print(' a * b =  {0}'.format(sess.run(product,feed_dict={a:3,b:4})))
   #一次取出两个节点值
    print(' {0}'.format(sess.run([add,product],feed_dict={a:3,b:4})))

来源:https://www.cnblogs.com/zyly/p/8869763.html

那在我们的代码中使用的原因,是因为每一次的batch不同,所以要进行注入。

模型保存和读取

通过tf.tran.Saver()实现。

本次文章分析的是Train.py文件:


import tensorflow.examples.tutorials.mnist.input_data as input_data
import tensorflow as tf
import config as cfg
import os
import lenet
from lenet import Lenet


def main():
    #读取文件
    mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
    #session机制
    sess = tf.Session()
    #参数读取
    batch_size = cfg.BATCH_SIZE
    parameter_path = cfg.PARAMETER_FILE
    max_iter = cfg.MAX_ITER

    #初始化网络
    lenet = Lenet()
    
    #参数的保存,使用train.saver
    saver = tf.train.Saver()
    #如果存在参数,就读取参数
    if os.path.exists(parameter_path):
        saver.restore(parameter_path)
    else:
        #建立所有随机变量之后,开始训练要先进行初始化;通过global_variables_intializer()
        sess.run(tf.initialize_all_variables())
    #经过50000次迭代,每次迭代50个数据
    for i in range(max_iter):
        # gain the batch
        batch = mnist.train.next_batch(50)
        # train the batch;其中train.op是训练函数;通过adamoptimizer实现 
        sess.run(lenet.train_op,feed_dict={lenet.raw_input_image: batch[0],lenet.raw_input_label: batch[1]})
        if i % 100 == 0:
            train_accuracy = sess.run(lenet.train_accuracy,feed_dict={
                lenet.raw_input_image: batch[0],lenet.raw_input_label: batch[1]
            })
            print("step %d, training accuracy %g" % (i, train_accuracy))
        
    save_path = saver.save(sess, parameter_path)
    sess.close()

if __name__ == '__main__':
    main()

五点半了,准备准备去吃饭饭咯~回来继续下一个文件的学习~

  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
LeNet是由Yann LeCun在1998年提出的经典卷积神经网络模型,被广泛应用于手写数字识别、文本识别等领域。LeNet5是LeNet的一种改进版本,主要的改进在于增加了卷积层和池化层,并使用了Sigmoid和Tanh等激活函数。 下面是LeNetLeNet5的详细结构和代码实现。 LeNet结构: 输入层(32x32的图像) => 卷积层1(6个5x5的卷积核)=> 池化层1(2x2的最大池化)=> 卷积层2(16个5x5的卷积核)=> 池化层2(2x2的最大池化)=> 全连接层1(120个神经元)=> 全连接层2(84个神经元)=> 输出层(10个神经元) LeNet5结构: 输入层(32x32的图像)=> 卷积层1(6个5x5的卷积核)=> 池化层1(2x2的最大池化)=> 卷积层2(16个5x5的卷积核)=> 池化层2(2x2的最大池化)=> 卷积层3(120个5x5的卷积核)=> 全连接层1(84个神经元)=> 输出层(10个神经元) 完整代码实现: ```python import torch import torch.nn as nn import torch.optim as optim import torch.nn.functional as F class LeNet(nn.Module): def __init__(self): super(LeNet, self).__init__() self.conv1 = nn.Conv2d(1, 6, kernel_size=5) self.pool1 = nn.MaxPool2d(kernel_size=2) self.conv2 = nn.Conv2d(6, 16, kernel_size=5) self.pool2 = nn.MaxPool2d(kernel_size=2) self.fc1 = nn.Linear(16*5*5, 120) self.fc2 = nn.Linear(120, 84) self.fc3 = nn.Linear(84, 10) def forward(self, x): x = F.relu(self.conv1(x)) x = self.pool1(x) x = F.relu(self.conv2(x)) x = self.pool2(x) x = x.view(-1, 16*5*5) x = F.relu(self.fc1(x)) x = F.relu(self.fc2(x)) x = self.fc3(x) return x class LeNet5(nn.Module): def __init__(self): super(LeNet5, self).__init__() self.conv1 = nn.Conv2d(1, 6, kernel_size=5) self.pool1 = nn.MaxPool2d(kernel_size=2) self.conv2 = nn.Conv2d(6, 16, kernel_size=5) self.pool2 = nn.MaxPool2d(kernel_size=2) self.conv3 = nn.Conv2d(16, 120, kernel_size=5) self.fc1 = nn.Linear(120, 84) self.fc2 = nn.Linear(84, 10) def forward(self, x): x = F.relu(self.conv1(x)) x = self.pool1(x) x = F.relu(self.conv2(x)) x = self.pool2(x) x = F.relu(self.conv3(x)) x = x.view(-1, 120) x = F.relu(self.fc1(x)) x = self.fc2(x) return x ``` 以上就是LeNetLeNet5的结构详解和完整代码实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值