【深度学习】Keras MNIST手写识别(二)—— 卷积神经网络模型(CNN)

1、 再谈 MLP

普通的神经网络,如上一节讲的 【深度学习】Keras MNIST手写识别(一)—— 多层感知器模型(MLP),它接受的输入是一个向量(即在输入层时将图片展平),然后输入到一系列的隐藏层,每一层的隐藏层都由一组神经元构成,组内每个神经元的输入都会与上一层的所有输出全部连接,就是说每个神经元都是独立运行的,并没有共享任何连接、权值、偏置,直接在最后一层输出层输出分类数。

对于 MNIST 这个数据集来说,训练集有 60000 张 28 x 28 单色的照片,那么它的 shape = (60000, 28, 28, 1),那么在第一层隐藏层中,每一个神经元需要连接的输入单元则为 28 * 28 * 1 = 784个(输入的是单张图片),就是说每一个神经元对上以及输入都有一个权值w需要进行训练,所以按照上一节的例程来看

model.add(Dense(units=512, input_shape=(784,), activation='relu'))  # 第一层隐藏层

第一层隐藏层有 512 个神经元,需要训练的数据为 784 × 512 = 401408 784 \times 512 = 401408 784×512=401408 个权值w, 看起来貌似不多,还能接受,但是如果训练接受的输入是240 x 240 三通道RGB彩色照片,那么我们可以算一下,假如保持上方的 512 个神经元不变, 240 × 240 × 3 × 512 = 88 , 473 , 600 240 \times 240 \times 3 \times 512 = 88,473,600 240×240×3×512=88,473,600 ,加上偏置,需要 88474112 个参数训练,这单单只是第一层隐藏层,深度越增加,需要训练的参数增长起来越快,而这种情况下,很明显其实有很多连接是无效的,因为一张照片中,无效的信息会很多,这种大量重复的连接,会导致浪费,而且大量的训练参数,将会使得很快过拟合。

2、CNN 小前戏

早在上世纪60年代,脑科学专家就发现大脑皮层的研究,就提出过感受野这个概念,发展到了80年代,继续提出了神经认知机的概念,将一个视觉模式分解成许多子模式,然后进入分层递阶式相连的特征平面进行处理,试图将视觉系统模型化,使得该模型就算在被识别的个体具有轻微形变,或者位移时,都能够完成识别。

讲点听的懂的,先一起来找个茬,记住这个feel~~


图1

假如我们按照 MLP 的方法,直接将整张图片进行观察,那么我们很容易忽略了差异点,而且很快就会觉得两张图是一样的(过拟合),这种现象在图像越大,信息越多的图片中越明显(你可以想象是那种高级的找茬图片,图片很大,内容复杂,你需要花费大量时间来寻找不同点,假如全局查看整张照片,你将很难发现所有差异~)

感受下在找茬的过程中,眼睛关注两张图的地方。


图2

我们大部分人的做法估计是下面这样:

  1. 从一个局部范围开始,逐一对比两张图,相同则跳过,不同则记录下来,表示找得到一个差异点。
  2. 扫完一遍局部之后,我们大概还会将我们的眼睛视野放大,再从稍微大的视野中扫描,看是否有遗漏了较大范围内的差异。
  3. OK 确认完成后,我们会把眼睛的视野放到整张图片,看看自己识别到差异点,然后跟标准答案对比,默默的检查下是否按照标题说的《震惊!据说只有 IQ160+ 才能找全,快进来挑战把》
  4. 接下来认真反复观察图片,看看哪些地方没有识别到,把图中的位置记录下来,差异的内容、颜色等都记录下来。(训练)
  5. 假如这个时候有个同学拿这张图给你,想要测试下你的IQ,由于你已经经过“反复训练”,因此你可以一下子把所有结果都标记给他(预测)。
  6. 你同学看着准确率 99.99999+,来了句握草牛皮,IQ 160。其实同学心里严重怀疑你已经背下答案(过拟合),于是乎,找了一张新的图给你…而结果当然暴露了,准确率不到70%。
  7. 接下来几周,你着了迷似的,疯狂找茬,不断循环 1 - 4 ,练就了火眼精金,只要是找茬图片,你都能很快就找到结果,而最最终稳定找到的不同点准确率稳定在 99 %。
  8. 你的同学终于相信你了~

突然灵感的想到了这个 CNN 小栗子,不知是否表达到位,欢迎评论留言,建议看下文之前,多看几遍上述的几个步骤。
如果你能理解这个小栗子在说啥,估计也大概能理解下文的 CNN 卷积神经网络了。按照网络的分层以及动作,将上述8个步骤转为 CNN 的层

  1. 卷积层 (+ 池化层)
  2. 卷积层 (+ 池化层)
  3. 展平 + 全连接 + 分类输出
  4. loss 计算,反向传播更新参数,最小化 loss
  5. 训练过程预测
  6. 评估模型
  7. 理解为表示整个训练流程,包含了1~6。
  8. 最后你将的到一个训练好的模型。预测效果不错,可以用于实际情况

3、卷积神经网络

由于这篇我姑且定位为入门文章,尽量写得相对较为简单易懂,因此打算将复杂的公式推理留到后续的 算子 专题,感兴趣的可以先三连一个? /斜眼笑

3.1 卷积核

先顺带过一下卷积的概念,我们在信号与系统课程中,学过卷积
f ( x ) ∗ g ( x ) = ∫ − ∞ ∞ f ( τ ) g ( x − τ ) d τ f(x) *g(x) = \int_{-\infty}^\infty f(\tau)g(x-\tau) \mathrm{d}\tau f(x)g(x)=f(τ)g(xτ)dτ
把(x)扩展为(x,y),可以简单的推出二维的卷积:
f ( x , y ) ∗ g ( x , y ) = ∫ τ 1 = − ∞ ∞ ∫ τ 2 = − ∞ ∞ f ( τ 1 , τ 2 ) g ( x − τ 1 , y − τ 2 ) d τ 1 d τ 2 f(x,y) *g(x,y) = \int_{\tau1=-\infty}^\infty\int_{\tau2=-\infty}^\infty f(\tau1,\tau2)g(x-\tau1,y-\tau2) \mathrm{d}\tau1\mathrm{d}\tau2 f(x,y)g(x,y)=τ1=τ2=f(τ1,τ2)g(xτ1,yτ2)dτ1dτ2
那么对于图像而言,它是一个离散的二维卷积,积分简化成求和:
f [ x , y ] ∗ g [ x , y ] = ∑ m = 0 M − 1 ∑ n = 0 N − 1 f ( m , n ) g ( x − m , y − n ) f[x,y] *g[x,y] = \sum_{m = 0}^{M-1}\sum_{n = 0}^{N-1} f(m,n)g(x-m,y-n) f[x,y]g[x,y]=m=0M1n=0N1f(m,n)g(xm,yn)
其操作步骤都是【翻转、移动、乘积、求和】


上图体现的是单个卷积的计算过程,这个移动的矩阵称之为卷积核 filter,且为 3x3的卷积核,每个卷积核分别处理一种图片的特征。
卷积核移动时是从左到右,从上到下,按照设定的 步长 strides 来移动扫描,那么1个卷积核对应的隐藏层有多少个神经元呢?MNIST 的图片为 28 x 28,假如用 3 x 3 的卷积核,步长为 1,对于每一行,卷积核共移动了25次,同理对每一列也移动了25次,因此,输出到后方的隐藏层需要 26 x 26 个神经元(shape 为(26,26,1))
到此应该就对卷积核有一个概念了。

3.2 卷积层

了解完卷积后,估计你已经猜到卷积层是干什么的了,单个卷积核,学习1种特征,输出到一层隐藏层。那么卷积就是利用多个卷积核 来对图片进行小区域的扫描,将每个卷积后的结果输出到一个新的平面上。
假设我在卷积层使用 32 个卷积核,那那么卷积层的 shape 为 (26,26,32)

这里需要注意两个概念:卷积核通道数,输出特征图通道数
卷积核通道数:等于上一层特征图通道数。对于第一层卷积层,就是输入的图片通道数。(MNIST 是单色,因此为1,彩色图片channel 为 3)
输出特征图通道数: featuremaps 等于当前层卷积核个数

keras 实现的代码如下:

model.add(Conv2D(24, kernel_size=(3, 3), activation='relu', input_shape=(28, 28, 1)))

3.3 池化层

由于一张图片中,有很多冗余的信息,这些冗余的信息其实对特征提取的贡献并不大,而且增加了计算开销,因此需要一层池化层来减少冗余信息。因此池化也称为欠采样或下采样。主要用于特征降维,压缩数据和参数的数量,减小过拟合,同时提高模型的容错性。利用图像局部相关性的原理,对图像进行子抽样,可以减少数据处理量同时保留有用信息

上图展示的是一个常用的 max pooling 的动图,可以看见,通过池化层,使得原本4x4的特征图压缩成了2x2,从而降低了特征维度。
同样的还有另一个常用的 average pooling,用于对池化窗口进行求和。

特征提取的误差主要来自两个方面:
(1)卷积层权值参数误差;
(2)邻域大小受限。

目前主流上对于average pooling 和max-pooling 的主要区别在于
max-pooling能减小(1),更多的保留纹理信
average -pooling能减小(2),更多的保留图像的背景信

但是不管用那种方法,pooling 层的确是人为的对信息进行过滤,仍然属于有损采样,因此在计算性能足够的情况下,某些网络也不一定在卷积层后立马跟一个池化层

keras 实现的代码如下:

model.add(MaxPool2D(pool_size=(2, 2), strides=(2, 2)))

3.4 感受野

卷积神经网络有一个重要的概念,就是感受野。
概念:在卷积神经网络CNN中,决定某一层输出结果中一个元素所对应的输入层的区域大小,被称作感受野 receptive field。
通俗一点的理解,就是映射,把当前层的特征,映射回原图上的具体位置。
打个比如,MNIST 的图片大小是 28 x 28,第一层卷积层使用 3 x 3 的卷积核,那么其中一个神经元对应的感受野就等于原图的 3 x 3 的大小,那么如果第二层卷积层继续使用3 x 3 的卷积核,按照比例映射回原图,就是 5 x 5 大小的感受野,就意味着二层的感受范围比第一层的大。

3.5 特征展平

在经过卷积层与池化层后,数据的长度、宽度、深度,都已经发生了明显的改变,我们最后的结果是要进行分类,此时就需要将数据展平成1维向量,输出给全连接层。

keras 实现的代码如下:

model.add(Flatten())

3.6 全连接层

卷积层将图片的特征提取出来,这种利用卷积层特征提取往往比人工提取特征或直接将整张图片送进全连接网络要来的高效,全连接网络在上一节有讲到,可以来这里查看。【深度学习】Keras MNIST手写识别(一)—— 多层感知器模型(MLP),最终仍然是是需要对手写数字进行分类。

4 实现代码汇总

MLP 训练参数:
在这里插入图片描述
CNN 训练参数:
在这里插入图片描述

实际跑的结果,识别率达到 99%
在这里插入图片描述
代码汇总:

from keras import Sequential
from keras.layers import Dense, Conv2D, MaxPool2D, Flatten
from keras.datasets import mnist
from keras.utils import np_utils

# 定义全连接网络模型
model = Sequential()
model.add(Conv2D(24, kernel_size=(3, 3), activation='relu', input_shape=(28, 28, 1)))
model.add(MaxPool2D(pool_size=(2, 2), strides=(2, 2)))
model.add(Conv2D(24, kernel_size=(3, 3), activation='relu'))
model.add(MaxPool2D(pool_size=(2, 2), strides=(2, 2)))
model.add(Flatten())
model.add(Dense(units=512, activation='relu'))
model.add(Dense(units=10, activation='softmax'))
model.summary()

# 编译静态图
model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics='accuracy')

# 加载并处理 mnist 数据集,Conv2D 接受的是 NHWC 格式数据,需要将数据集 reshape 成该格式
(train_x, train_y), (test_x, test_y) = mnist.load_data()
X_train = train_x.reshape(train_x.shape[0], train_x.shape[1], train_x.shape[2], 1).astype('float32') / 255
Y_train = np_utils.to_categorical(train_y, num_classes=10)
X_test = test_x.reshape(test_x.shape[0], test_x.shape[1], test_x.shape[2], 1).astype('float32') / 255
Y_test = np_utils.to_categorical(test_y, num_classes=10)

# 模型训练
model.fit(X_train, Y_train, epochs=5, batch_size=32)

# 精度验证
loss, accuracy = model.evaluate(X_test, Y_test)
print('Test loss:', loss)
print('Accuracy:', accuracy)
predict = model.predict(X_test)
print('预测值:', predict.argmax())
print('实际值:', test_y[0])

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值