在《手写数字识别——利用Keras高层API快速搭建并优化网络模型》一文中,我们搭建了全连接层网络,准确率达到0.98,但是这种网络的参数量达到了近24万个。本文将搭建LeNet-5网络,参数仅有6万左右,该网络是由Yann LeCun在1998年提出,是历史上第一代卷积神经网络。关于其历史可阅读另一篇博客《冬日曙光——回溯CNN的诞生》

模型结构

  LeNet-5提出至今过去了很久,因此其中的很多算法都已经被代替,因此本文所建网络与LeNet-5会有所差异。LeNet-5结构如下图所示,原结构可简单概括为:

    input(32*32*1)->Conv(28*28*6)->tanh->AvgPool(14*14*6)->tanh->(局部通道连接)Conv(10*10*16)->tanh->AvgPool(5*5*16)->tanh->Conv(1*1*120)->tanh->fc(84)->RBF->out(10)

  

  本文采用的结构为:

    input(28*28*1)->Conv(28*28*6)->ReLu->MaxPool(14*14*6)->Conv(10*10*16)->ReLu->MaxPool(5*5*16)->Conv(1*1*120)->ReLu->fc(84)->out(10)

数据导入与预处理

  跟全连接网络的预处理不同,这里的预处理有以下三个变化:

    1.输入采用了z-score 标准化,使均值约为0,标准差约为1。过程类似于将正态分布进行标准化一样。实际证明,这种方法比之前直接除以255收敛更快、准确度更高。

    2.不需要将输入打平,而且还得填充1个维度作为通道,目的是使shape符合输入层维度。

    3.标签值编成独热码后,还需扩充两个维度,可以跟输出层维度数相同。

  标准化需要用到均值函数np.mean(a,axis=None,dtype=None)和标准差函数np.std(a,axis=None,dtype=None)数据导入和预处理代码如下:

import tensorflow as tf
import numpy as np
from tensorflow.keras import datasets,optimizers,Sequential,layers,metrics #数据预处理
def preprocess(x,y):
#z-score 标准化
x = (tf.cast(x,dtype=tf.float32)-mean)/std
x = tf.reshape(x,(-1,28,28,1))
y = tf.one_hot(y,depth=10,dtype=tf.int32)
y = tf.reshape(y,(-1,1,1,10))
return x,y #加载mnist数据
#trian_x -> shape(60k,28,28) val_x -> shape(10k,28,28)
#trian_y -> shape(60k,10) val_y -> shape(10k,10)
(train_x,train_y),(val_x,val_y) = datasets.mnist.load_data()
mean = np.mean(train_x)
std = np.std(train_x) #生成Dataset对象
#bacthx -> shape(128,28,28) dtype=tf.uint8
#bacthy -> shape(128,10) dtype=tf.uint8
train_db = tf.data.Dataset.from_tensor_slices((train_x,train_y)).shuffle(1000).batch(128)
val_db = tf.data.Dataset.from_tensor_slices((val_x,val_y)).shuffle(1000).batch(128) #特征缩放、独热码处理
train_db = train_db.map(preprocess)
val_db = val_db.map(preprocess)

模型构建

  生成卷积层需要使用layers.Conv2D(filters,kernel_size,strides=(1,1),padding="valid",activation),其中filters表示输出通道数(滤波器数量),kernel_size为滤波器大小,strides为卷积步长,padding为边界处理方式(valid表示不进行边界扩展,same表示进行边界扩展),activation一般选择ReLu。

  生成池化层需要使用layers.MaxPool2D(pool_size,strides,padding="valid"),一般习惯把MaxPool的padding设为“same”,通过步长来控制缩小倍数。在这里步长为2,每次池化都会将大小减半。

  MNIST数据集图片大小为28*28,为了跟LeNet保持一致,第一层卷积没有改变大小,因此padding设为"same"。我把所有全连接层用卷积形式代替了,这样可以避免卷积层输出和全连接层输入之间维度的差异(卷积输出需要被打平),从而可以用一个Sequential容器装下所有神经层。

  模型构建代码如下:

model = Sequential([
#input -> shape(128,28,28,1)
#C1 -> shape(128,28,28,6)
layers.Conv2D(6,kernel_size=5,strides=1,padding='same',activation=tf.nn.relu),
#S2 -> shape(128,14,14,6)
layers.MaxPool2D(pool_size=(2,2),strides=2,padding='same'), #C3 -> shape(128,10,10,16)
layers.Conv2D(16,kernel_size=5,strides=1,activation=tf.nn.relu),
#S4 -> shape(128,5,5,16)
layers.MaxPool2D(pool_size=(2,2),strides=2,padding='same'), #C5 -> shape(128,1,1,120)
layers.Conv2D(120,kernel_size=5,strides=1,activation=tf.nn.relu), #卷积转到全连接维数不对应导致不能训练
# #F6 ->shape(128,84)
# layers.Dense(84,activation=tf.nn.relu),
# #output ->shape(128,10)
# layers.Dense(10) #F6 ->shape(128,1,1,84)
layers.Conv2D(84,kernel_size=1,strides=1,activation=tf.nn.relu),
#output ->shape(128,1,1,10)
layers.Conv2D(10,kernel_size=1,strides=1) ]) model.build(input_shape=(None,28,28,1))
model.summary()

模型的训练

  LeNet-5当时使用的是随机梯度下降和分段学习率,现在更多都交给Adam了。这里给出了两种实现,一种利用fit的回调进行手动学习率调整,可能需要花点时间调参,另一种则使用Adam自动管理。在SGD下,因为是随机取样,所以Loss相对较小,需要比较大的学习率。

  当年LeNet-5迭代20次后收敛,准确率可以达到0.9905。本文SGD版在迭代12次后能达到0.9900水平,在18次时收敛,准确率稳定在0.9917;Adam版在迭代20次收敛于0.9892。也就是说,在普遍使用Adam的今天,SGD(TensorFlow里SGD经过了动量的优化)也是值得一试的。

  Adam版:

model.compile(optimizer=optimizers.Adam(),
loss=tf.losses.CategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
model.fit(train_db,epochs=15,validation_data=val_db,validation_freq=1)

  SGD+分段学习率版:

def scheduler(epoch):
if epoch < 10:
return 0.1
else:
return 0.1 * np.exp(0.2 * (10 - epoch))#新建LearningRateScheduler对象
callback = tf.keras.callbacks.LearningRateScheduler(scheduler,verbose=1) model.compile(optimizer=optimizers.SGD(),
loss=tf.losses.CategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
model.fit(train_db,epochs=20,validation_data=val_db,validation_freq=1,callback

完整代码

import tensorflow as tf
import numpy as np
from tensorflow.keras import datasets,optimizers,Sequential,layers,metrics #数据预处理
def preprocess(x,y):
#z-score 标准化
x = (tf.cast(x,dtype=tf.float32)-mean)/std
x = tf.reshape(x,(-1,28,28,1))
y = tf.one_hot(y,depth=10,dtype=tf.int32)
y = tf.reshape(y,(-1,1,1,10))
return x,y #加载mnist数据
#trian_x -> shape(60k,28,28) val_x -> shape(10k,28,28)
#trian_y -> shape(60k,10) val_y -> shape(10k,10)
(train_x,train_y),(val_x,val_y) = datasets.mnist.load_data()
mean = np.mean(train_x)
std = np.std(train_x) #生成Dataset对象
#bacthx -> shape(128,28,28) dtype=tf.uint8
#bacthy -> shape(128,10) dtype=tf.uint8
train_db = tf.data.Dataset.from_tensor_slices((train_x,train_y)).shuffle(1000).batch(128)
val_db = tf.data.Dataset.from_tensor_slices((val_x,val_y)).shuffle(1000).batch(128) #特征缩放、独热码处理
train_db = train_db.map(preprocess)
val_db = val_db.map(preprocess) model = Sequential([
#input -> shape(128,28,28,1)
#C1 -> shape(128,28,28,6)
layers.Conv2D(6,kernel_size=5,strides=1,padding='same',activation=tf.nn.relu),
#S2 -> shape(128,14,14,6)
layers.MaxPool2D(pool_size=(2,2),strides=2,padding='same'), #C3 -> shape(128,10,10,16)
layers.Conv2D(16,kernel_size=5,strides=1,activation=tf.nn.relu),
#S4 -> shape(128,5,5,16)
layers.MaxPool2D(pool_size=(2,2),strides=2,padding='same'), #C5 -> shape(128,1,1,120)
layers.Conv2D(120,kernel_size=5,strides=1,activation=tf.nn.relu), #F6 ->shape(128,1,1,84)
layers.Conv2D(84,kernel_size=1,strides=1,activation=tf.nn.relu),
#output ->shape(128,1,1,10)
layers.Conv2D(10,kernel_size=1,strides=1) ]) model.build(input_shape=(None,28,28,1))
model.summary() #Adam版
# model.compile(optimizer=optimizers.Adam(),
# loss=tf.losses.CategoricalCrossentropy(from_logits=True),
# metrics=['accuracy'])
# model.fit(train_db,epochs=20,validation_data=val_db,validation_freq=1) #SGD版
def scheduler(epoch):
if epoch < 10:
return 0.1
else:
return 0.1 * np.exp(0.2 * (10 - epoch)) # #新建LearningRateScheduler对象
callback = tf.keras.callbacks.LearningRateScheduler(scheduler,verbose=1) model.compile(optimizer=optimizers.SGD(),
loss=tf.losses.CategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
model.fit(train_db,epochs=20,validation_data=val_db,validation_freq=1,callbacks=[callback])

手写数字识别——基于LeNet-5卷积网络模型的更多相关文章

  1. MINST手写数字识别(二)—— 卷积神经网络(CNN)

    今天我们的主角是keras,其简洁性和易用性简直出乎David 9我的预期.大家都知道keras是在TensorFlow上又包装了一层,向简洁易用的深度学习又迈出了坚实的一步. 所以,今天就来带大家写 ...

  2. linux-基于tensorflow2.x的手写数字识别-基于MNIST数据集

    数据集 数据集下载MNIST 首先读取数据集, 并打印相关信息 包括 图像的数量, 形状 像素的最大, 最小值 以及看一下第一张图片 path = 'MNIST/mnist.npz' with np. ...

  3. TensorFlow 卷积神经网络手写数字识别数据集介绍

    欢迎大家关注我们的网站和系列教程:http://www.tensorflownews.com/,学习更多的机器学习.深度学习的知识! 手写数字识别 接下来将会以 MNIST 数据集为例,使用卷积层和池 ...

  4. 手写数字识别 ----卷积神经网络模型官方案例注释(基于Tensorflow,Python)

    # 手写数字识别 ----卷积神经网络模型 import os import tensorflow as tf #部分注释来源于 # http://www.cnblogs.com/rgvb178/p/ ...

  5. 【深度学习系列】手写数字识别卷积神经--卷积神经网络CNN原理详解(一)

    上篇文章我们给出了用paddlepaddle来做手写数字识别的示例,并对网络结构进行到了调整,提高了识别的精度.有的同学表示不是很理解原理,为什么传统的机器学习算法,简单的神经网络(如多层感知机)都可 ...

  6. 手写数字识别 ----在已经训练好的数据上根据28*28的图片获取识别概率(基于Tensorflow,Python)

    通过: 手写数字识别  ----卷积神经网络模型官方案例详解(基于Tensorflow,Python) 手写数字识别  ----Softmax回归模型官方案例详解(基于Tensorflow,Pytho ...

  7. 学习笔记CB009:人工神经网络模型、手写数字识别、多层卷积网络、词向量、word2vec

    人工神经网络,借鉴生物神经网络工作原理数学模型. 由n个输入特征得出与输入特征几乎相同的n个结果,训练隐藏层得到意想不到信息.信息检索领域,模型训练合理排序模型,输入特征,文档质量.文档点击历史.文档 ...

  8. 基于tensorflow的MNIST手写数字识别(二)--入门篇

    http://www.jianshu.com/p/4195577585e6 基于tensorflow的MNIST手写字识别(一)--白话卷积神经网络模型 基于tensorflow的MNIST手写数字识 ...

  9. [Python]基于CNN的MNIST手写数字识别

    目录 一.背景介绍 1.1 卷积神经网络 1.2 深度学习框架 1.3 MNIST 数据集 二.方法和原理 2.1 部署网络模型 (1)权重初始化 (2)卷积和池化 (3)搭建卷积层1 (4)搭建卷积 ...

随机推荐

  1. Linux中Hadoop的安装与配置

    一.准备 1,配通网络 ping www.baidu.com 之前安装虚拟机时配过 2,关闭防火墙 systemctl stop firewalld systemctl disable firewal ...

  2. php gettype()函数

      gettype() 会根据 参数类型返回值: boolean:表示变量为布尔类型 integer:表示变量为整数类型 double :表示变量为float类型(历史原因) string:表示变量为 ...

  3. centos7.5下yum安装mysql-5.6.43

    cd ~/ && cat /etc/redhat-release yum list installed |grep mysql #<===查看是否安装mysql,如果已经安装,使 ...

  4. 使用Webpack的代码拆分在Vue中进行懒加载

    参考学习:https://alexjover.com/blog/lazy-load-in-vue-using-webpack-s-code-splitting/ 学习文案:https://webpac ...

  5. APP图标在线生成

    在线生成安卓APP图标生成 图标在线 在线图标 安卓图标 生成图标 https://icon.wuruihong.com/ 在线png图片压缩  png压缩 https://compresspng.c ...

  6. 头部布局,搜索验证和AJAX自动搜索提示,并封装成组件,提高代码复用性

    index.html 头部区结构和样式 效果图 静态样式 index.html中的部分 <!-- 头部 --> <div class="header"> & ...

  7. Android中DatePicker日期选择器的使用和获取选择的年月日

    场景 实现效果如下 注: 博客: https://blog.csdn.net/badao_liumang_qizhi 关注公众号 霸道的程序猿 获取编程相关电子书.教程推送与免费下载. 实现 将布局改 ...

  8. AndroidStudio修改默认C盘配置文件夹(.android.gradle.AndroidStudio)以及修改后避免踩的坑

    场景 AndroidStudio下载安装教程(图文教程): https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/103672471 在上 ...

  9. Java之GUI编程

    GUI编程 组建 窗口 弹窗 面板 文本框 列表框 按钮 图片 监听事件 鼠标 键盘事件 破解工具 1.简介 GUI的核心技术:Swing AWT 为什么不流行? 界面不美观. 需要jre环境.(没必 ...

  10. 回炉重造之重读Windows核心编程-001-错误处理

    Windows处理错误靠的是API的返回值,类型不止一种种: VOID,函数不可能失败,Windows API的返回值很少是这个情况. BOOL,如果函数失败,则返回值是0,否则返回是非零值.不要测试 ...