LeNet-5​基础介绍及代码测试

1. LeNet-5​ 基础介绍

通过详解卷积神经网络CNN一文,我们对卷积神经网络的有了很多认识,接下来我们将通过几个经典的卷积神经网络,加深对卷积神经网络的理解和认识。

1998 1998 1998年, Y a n n   L e C u n Yann \ LeCun Yann LeCun等人在论文 < < G r a d i e n t B a s e d   L e a r n i n g   A p p l i e d   t o   D o c u m e n t   R e c o g n i t i o n > > <<GradientBased \ Learning \ Applied \ to \ Document \ Recognition>> <<GradientBased Learning Applied to Document Recognition>>中提出并详细介绍了LeNet-5​神经网络结构,并用于手写数字识别问题,能达到很高的识别率。LeNET-5包含卷积层,池化层,全连接层,成为了卷积神经网络的经典结构,被誉为是卷积神经网络的"Hello Word"。

该网络现在来看十分简单,十分适合入门,其结构如下

Lenet-5架构

该网络共包含 7 7 7层(不含输入层),我们根据以下公式计算卷积或者池化之后的特征图大小

O w = { f l o o r ( W − 1 S w ) + 1 , p a d d i n g = " S A M E " f l o o r ( W − F w S w ) + 1 , p a d d i n g = " V A L I D " O h = { f l o o r ( H − 1 S h ) + 1 , p a d d i n g = " S A M E " f l o o r ( H − F h S h ) + 1 , p a d d i n g = " V A L I D " \begin{aligned}O_w &= \begin{cases} floor(\frac{W-1}{S_w})+1 ,padding="SAME"\\ \\ floor\bigg(\frac{W-F_w}{S_w} \bigg)+1 ,padding="VALID"\end{cases}\\\\ O_h &= \begin{cases}floor(\frac{H-1}{S_h})+1,padding="SAME"\\\\ floor\bigg(\frac{H-F_h}{S_h} \bigg)+1,padding="VALID" \end{cases}\end{aligned} OwOh=floor(SwW1)+1,padding="SAME"floor(SwWFw)+1,padding="VALID"=floor(ShH1)+1,padding="SAME"floor(ShHFh)+1,padding="VALID"

  • C 1 C1 C1

    该层是一个卷积层,使用 6 6 6 5 ∗ 5 5*5 55的卷积核,步长为 1 1 1

    每个卷积核有 5 ∗ 5 + 1 ( B i a s ) 5*5+1(Bias) 55+1(Bias)个参数,共有 ( 5 ∗ 5 + 1 ) ∗ 6 = 156 (5*5+1)*6=156 (55+1)6=156个参数。

  • S 2 S2 S2

    该层是一个Pooling层,Pooling大小为 2 ∗ 2 2*2 22,步长为 2 2 2.

    该层没有要学习的参数

  • C 3 C3 C3

    该层是一个卷积层,使用 16 16 16 5 ∗ 5 ∗ 6 5*5*6 556的卷积核,步长为 1 1 1

    共有 ( 5 ∗ 5 ∗ 6 + 1 ) ∗ 16 = 2416 (5*5*6+1)*16=2416 (556+1)16=2416个参数。

  • S 4 S4 S4

    该层是一个Pooling层,Pooling大小为 2 ∗ 2 2*2 22,步长为 2 2 2.

    该层没有要学习的参数

  • C 5 C5 C5

    论文描述的是一个卷积层,使用 120 120 120 5 ∗ 5 ∗ 16 5*5*16 5516的卷积核,步长为 1 1 1.

    共有 ( 5 ∗ 5 ∗ 16 + 1 ) ∗ 120 = 48120 (5*5*16+1)*120=48120 (5516+1)120=48120个参数

    其实这层也可以看做是一个全连接层的一个隐藏层,神经元个数为 120 120 120个,输入的数据来自 S 4 S4 S4层特征数据拉直。

  • F 6 F6 F6

    该层是一个全连接层。

    共有 120 ∗ 84 + 84 = 10164 120*84+84=10164 12084+84=10164个参数。

  • O U T P U T OUTPUT OUTPUT

    该层是一个全连接层。

    共有 10 ∗ 84 + 10 = 850 10*84+10=850 1084+10=850个参数。

我们用以下表格更加直观的看下每一层都做了什么

L a y e r L a y e r   T y p e I n p u t   S i z e K e r n e l   S i z e   o r   P o l l i n g   S i z e F i l t e r s   C o u n t S t r i d e s O u t p u t   S i z e P a r a m s   C o u n t C 1 卷 积 层 32 ∗ 32 ∗ 1 5 ∗ 5 6 1 28 ∗ 28 ∗ 6 156 S 2 池 化 层 28 ∗ 28 ∗ 6 2 ∗ 2 − 2 14 ∗ 14 ∗ 6 0 C 3 卷 积 层 14 ∗ 14 ∗ 6 5 ∗ 5 ∗ 6 16 1 10 ∗ 10 ∗ 16 2416 S 4 池 化 层 10 ∗ 10 ∗ 16 2 ∗ 2 − 2 5 ∗ 5 ∗ 16 0 C 5 卷 积 层 5 ∗ 5 ∗ 16 5 ∗ 5 ∗ 16 120 1 1 ∗ 1 ∗ 120 48120 F 6 全 连 接 层 120 − − − 84 10164 O U T P U T 输 出 层 84 − − − 10 850 \begin{array}{|c|c|c|c|c|c|c|c|} Layer &Layer \ Type &Input\ Size & Kernel\ Size \ or \ Polling\ Size & Filters\ Count & Strides & Output\ Size &Params \ Count \\ \hline C1 & 卷积层 & 32*32*1 & 5*5 & 6 & 1 & 28*28*6 & 156 \\ \hline \\S2 & 池化层 & 28*28*6 & 2*2 & - & 2 & 14*14*6 & 0 \\ \hline \\ C3 & 卷积层 & 14*14*6 & 5*5*6 & 16&1&10*10*16&2416 \\ \hline \\S4&池化层&10*10*16& 2*2 &-&2&5*5*16&0\\ \hline \\ C5&卷积层&5*5*16&5*5*16 &120&1&1*1*120&48120 \\ \hline \\ F6&全连接层& 120&-&-&-&84&10164 \\ \hline \\ OUTPUT &输出层&84&-&-&-&10&850\end{array} LayerC1S2C3S4C5F6OUTPUTLayer TypeInput Size323212828614146101016551612084Kernel Size or Polling Size5522556225516Filters Count616120Strides12121Output Size28286141461010165516111208410Params Count1560241604812010164850

这里重点介绍其网络结构,部分细节可能和论文不一致,需要原论文的可联系我。

2. 利用TesorFlow + LeNet-5识别mnist手写数字

#!/usr/bin/python3

# @Time    : 2021/2/23 9:56
# @Author  : 
# @File    : Lenet5
# @Software: PyCharm
# @Description : Python==3.7.3、tensorflow==2.4.1

import tensorflow as tf
import numpy as np
from matplotlib import pyplot as plt
from tensorflow.keras.layers import Conv2D,BatchNormalization,Activation,MaxPool2D,Dropout,Flatten,Dense,AveragePooling2D
from tensorflow.keras import Model
import os

# 设置超过多长省略显示,这里设置np.inf表示无限长
np.set_printoptions(threshold=np.inf)

# 准备数据集合
# cifar10 = tf.keras.datasets.cifar10
cifar10 = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
x_train,x_test = x_train/255.0,x_test/255.0

# 给数据增加一个维度,使数据和网络结构匹配
x_train = x_train.reshape(x_train.shape[0], 28, 28, 1)
x_test = x_test.reshape(x_test.shape[0], 28, 28, 1)


# 自定义网络模型
class Letnet5Model(Model,):
    # 定义网络结构
    def __init__(self):
        super(Letnet5Model,self).__init__()
        # 定义6个 5*5的卷积核
        self.c1 = Conv2D(filters=6,kernel_size=(5,5),activation='sigmoid',padding='same')
        # 平均池化,池化大小 2*2,步长2
        self.s2 = AveragePooling2D(pool_size=(2,2),strides=2)
        # 定义16个5*5的卷积核
        self.c3 = Conv2D(filters=16,kernel_size=(5,5),activation='sigmoid')
        self.s4 = AveragePooling2D(pool_size=(2,2),strides=2)

        # 将卷积得到的特征数据拉直
        self.f = Flatten()
        # 定个一个120个神经元的全连接隐藏层
        self.c5 = Dense(units=120, activation='sigmoid')
        # 定个一个80个神经元的全连接隐藏层
        self.f6 = Dense(units=84,activation='sigmoid')
        # 定个一个10个神经元的全连接隐藏层
        self.o7 = Dense(units=10,activation='softmax')

    # 调用网络结构,实现前向传播
    def call(self, inputs, training=None, mask=None):
        inputs = self.c1(inputs)
        inputs = self.s2(inputs)
        inputs = self.c3(inputs)
        inputs = self.s4(inputs)
        inputs = self.f(inputs)
        inputs = self.c5(inputs)
        inputs = self.f6(inputs)
        y = self.o7(inputs)
        return y

def acc_loss_analyse(robot,):
    # 绘制训练集和验证集的acc和loss曲线
    acc = robot.history['sparse_categorical_accuracy']
    val_acc = robot.history['val_sparse_categorical_accuracy']
    loss = robot.history['loss']
    val_loss = robot.history['val_loss']

    plt.subplot(1, 2, 1)
    plt.plot(acc, label='Training Accuracy')
    plt.plot(val_acc, label='Validation Accuracy')
    plt.title("Training and Validation Accuracy")
    plt.legend()

    plt.subplot(1, 2, 2)
    plt.plot(loss, label='Training Loss')
    plt.plot(val_loss, label='Validation Loss')
    plt.title('Training and Validation Loss')
    plt.legend()
    plt.show()

def main():
    model = Letnet5Model()

    # 配置训练项,设定optimizer(优化器),loss(损失函数),metrics(网络评判标准)
    model.compile(optimizer='adam', loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
                  metrics=['sparse_categorical_accuracy'])

    breakpoint_sava_path = './checkpoint/LeNet5.ckpt'
    if os.path.exists(breakpoint_sava_path+'.index'):
        print('-------------load the model-----------------')
        # 读取模型
        model.load_weights(breakpoint_sava_path)

    # 保存模型
    cp_callback = tf.keras.callbacks.ModelCheckpoint(filepath=breakpoint_sava_path, save_weights_only=True,save_best_only=True)
    # 开始训练
    robot = model.fit(x_train,y_train,batch_size=32,epochs=5,validation_data=(x_test,y_test),validation_freq=1,callbacks=[cp_callback])
    # 查看网络结构基本信息
    model.summary()
    acc_loss_analyse(robot, )

if __name__ == '__main__':
    main()

运行代码,可以看到 T e n s o r F l o w TensorFlow TensorFlow打印的每层参数个数和之前的分析保持一致

在这里插入图片描述
训练了两百轮,最好验证集准确率为 99.07 % 99.07\% 99.07%

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
LeNet-5神经网络 C源代码,这个写的比较好,可以用gcc编译去跑,结合理论可以对深度学习有更深刻的了解 根据YANN LECUN的论文《Gradient-based Learning Applied To Document Recognition》设计的LeNet-5神经网络,C语言写成,不依赖任何第三方库。 MNIST手写字符集初代训练识别率97%,多代训练识别率98%。 DEMO main.c文件为MNIST数据集的识别DEMO,直接编译即可运行,训练集60000张,测试集10000张。 项目环境 该项目为VISUAL STUDIO 2015项目,用VISUAL STUDIO 2015 UPDATE1及以上直接打开即可编译。采用ANSI C编写,因此源码无须修改即可在其它平台上编译。 如果因缺少openmp无法编译,请将lenet.c中的#include和#pragma omp parallel for删除掉即可。 API #####批量训练 lenet: LeNet5的权值的指针,LeNet5神经网络的核心 inputs: 要训练的多个图片对应unsigned char二维数组的数组,指向的二维数组的batchSize倍大小内存空间指针。在MNIST测试DEMO中二维数组为28x28,每个二维数组数值分别为对应位置图像像素灰度值 resMat:结果向量矩阵 labels:要训练的多个图片分别对应的标签数组。大小为batchSize batchSize:批量训练输入图像(二维数组)的数量 void TrainBatch(LeNet5 *lenet, image *inputs, const char(*resMat)[OUTPUT],uint8 *labels, int batchSize); #####单个训练 lenet: LeNet5的权值的指针,LeNet5神经网络的核心 input: 要训练的图片对应二维数组 resMat:结果向量矩阵 label: 要训练的图片对应的标签 void Train(LeNet5 *lenet, image input, const char(*resMat)[OUTPUT],uint8 label); #####预测 lenet: LeNet5的权值的指针,LeNet5神经网络的核心 input: 输入的图像的数据 labels: 结果向量矩阵指针 count: 结果向量个数 return 返回值为预测的结果 int Predict(LeNet5 *lenet, image input, const char(*labels)[LAYER6], int count); #####初始化 lenet: LeNet5的权值的指针,LeNet5神经网络的核心
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

积跬步以至千里。

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值