CNN
核心特点是卷积操作,它可以在图像上进行滑动窗口的计算,通过滤波器(又称卷积核)和池化层(Max Pooling)来提取出图像的特征。
卷积操作可以有效地减少权重数量,降低计算量,同时也能够保留图像的空间结构信息。
池化层则可以在不改变特征图维度的前提下,减少计算量,提高模型的鲁棒性。
总览
卷积神经网络主要由两部分组成,一部分是特征提取(卷积、激活函数、池化),另一部分是分类识别(全连接层),下图便是著名的手写文字识别卷积神经网络结构图:
一个“卷积神经网络”(CNN)结构,如下图所示:
一. 卷积
二. 激活
常用的激活函数有sigmoid、tanh、relu等等,前两者sigmoid/tanh比较常见于全连接层,后者ReLU常见于卷积层。
激活函数的作用是用来加入非线性因素,把卷积层输出结果做非线性映射。
在卷积神经网络中,激活函数一般使用ReLU(The Rectified Linear Unit,修正线性单元),它的特点是收敛快,求梯度简单。计算公式也很简单,max(0,T),即对于输入的负值,输出全为0,对于正值,则原样输出。
三. 池化
为了有效地减少计算量,CNN使用的另一个有效的工具被称为“池化(Pooling)”。池化就是将输入图像进行缩小,减少像素信息,只保留重要信息。池化的操作也很简单,通常情况下,池化区域是2*2大小,然后按一定规则转换成相应的值。
最大池化(max-pooling)保留了每一小块内的最大值,也就是相当于保留了这一块最佳的匹配结果(因为值越接近1表示匹配越好)。也就是说,它不会具体关注窗口内到底是哪一个地方匹配了,而只关注是不是有某个地方匹配上了。
四. 全连接层
全连接层在整个卷积神经网络中起到“分类器”的作用,即通过卷积、激活函数、池化等深度网络后,再经过全连接层对结果进行识别分类。
- 首先将经过卷积、激活函数、池化的深度网络后的结果串起来,如下图所示:
-
由于神经网络是属于监督学习,在模型训练时,根据训练样本对模型进行训练,从而得到全连接层的权重(如预测字母X的所有连接的权重):
-
在利用该模型进行结果识别时,根据刚才提到的模型训练得出来的权重,以及经过前面的卷积、激活函数、池化等深度网络计算出来的结果,进行加权求和,得到各个结果的预测值,然后取值最大的作为识别的结果(如下图,最后计算出来字母X的识别值为0.92,字母O的识别值为0.51,则结果判定为X)
五. 特征提取实例
使用 TensorFlow 构建一个简单的卷积神经网络模型,并利用该模型对 MNIST 数据集的图像进行卷积操作,然后绘制出每个卷积核生成的特征图。
import tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np
# 加载mnist数据集
mnist = tf.keras.datasets.mnist
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
# 取10张训练集图片
images = train_images[:10]
# 将图片转换为float32类型,并进行归一化(范围在0到1之间)
images = images.astype('float32') / 255.0
# 将图片reshape成4D张量,大小为(10, 28, 28, 1)
# 也就是第一个维度表示有10张图像,每张图像由28行、28列和1个通道(灰度)组成
images = np.expand_dims(images, axis=3)
# 定义卷积核数量
num_filters = 10
# 定义卷积层
model = tf.keras.models.Sequential([
tf.keras.layers.Conv2D(num_filters, (3, 3), activation='relu', input_shape=(28, 28, 1)),
])
# 计算卷积后的特征图
features = model.predict(images)
# 绘制卷积后的特征图
fig, axs = plt.subplots(nrows=num_filters, ncols=10, figsize=(10, num_filters))
for i in range(num_filters):
for j in range(10):
# 在子图中绘制第 j 张图像的第 i 个卷积核的特征图,使用灰度色彩映射。
axs[i][j].imshow(features[j, :, :, i], cmap='gray')
axs[i][j].axis('off')
plt.show()
提问
- 关于数据集:
- 数据集是 MNIST,它包含手写数字的灰度图像。
- MNIST 数据集中的图像是单通道的灰度图像,每个图像都是28x28像素,像素值介于0到255之间,表示不同灰度级别。
- 为什么要将图像数据转换为float32类型?
- 数值精度和范围:在深度学习中,使用
float32
类型可以提供更高的数值精度,相比于整数类型(如uint8
),float32
可以存储更大范围的数值,同时具有足够的精度来处理复杂的数学运算和模型训练。 - 计算准确性:深度学习模型在训练和推断过程中涉及大量的数值计算,包括梯度计算、权重更新等。使用
float32
类型可以减少数值计算中的舍入误差,从而提高模型的计算准确性和稳定性。 - 硬件优化:现代的 GPU 和其他加速器通常更有效地处理
float32
类型的数据。因此,使用float32
类型可以利用硬件加速器的优化,加快模型的训练速度。 - 模型兼容性:大多数深度学习框架和模型都默认使用
float32
类型作为输入数据的标准类型。保持数据类型的一致性有助于避免类型转换错误,并确保模型能够正常运行和收敛。
- 定义卷积层:
model = tf.keras.models.Sequential([
tf.keras.layers.Conv2D(num_filters, (3, 3), activation='relu', input_shape=(28, 28, 1)),
])
tf.keras.models.Sequential([])
:使用 Keras 中的 Sequential 模型,这是一种简单的模型堆叠方式,适用于顺序的层堆叠结构。tf.keras.layers.Conv2D
:这是一个 2D 卷积层的类,用于处理二维图像数据。num_filters
:指定卷积核(滤波器)的数量,即输出的通道数。(3, 3)
:指定每个卷积核的大小为 3x3 像素。activation='relu'
:激活函数使用 ReLU(修正线性单元),在卷积层后应用非线性。input_shape=(28, 28, 1)
:指定输入图像的形状。这里是高度为28像素、宽度为28像素、通道数为1(灰度图像)的图像。
结果呈现:
六. LeNet——数字识别实例
下面来介绍一下 CNN 的经典模型:手写字体识别模型 LeNet5。LeNet5 诞生于 1994 年,是最早的卷积神经网络之一, 由 Yann LeCun 完成,推动了深度学习领域的发展。在那时候,没有 GPU 帮助训练模型,甚至 CPU 的速度也很慢,因此,LeNet5 通过巧妙的设计,利用卷积、参数共享、池化等操作提取特征,避免了大量的计算成本,最后再使用全连接神经网络进行分类识别,这个网络也是最近大量神经网络架构的起点,给这个领域带来了许多灵感。
LeNet5 的网络结构示意图如下所示:
LeNet5 由 7 层 CNN(不包含输入层)组成,上图中输入的原始图像大小是 32×32 像素,卷积层用 Ci 表示,子采样层(pooling,池化)用 Si 表示,全连接层用 Fi 表示。下面逐层介绍其作用和示意图上方的数字含义。
C1 层(卷积层):6@28×28
该层使用了 6 个卷积核,每个卷积核的大小为 5×5,这样就得到了 6 个 feature map(特征图)。
-
特征图大小:
每个卷积核(5×5)与原始的输入图像(32×32)进行卷积,这样得到的 feature map(特征图)大小为(32-5+1)×(32-5+1)=
28×28
-
参数个数:
由于参数(权值)共享的原因,对于同个卷积核每个神经元均使用相同的参数,因此,参数个数为
(5×5+1)×6= 156
,其中 5×5 为卷积核参数
,1 为偏置参数
-
连接数:
卷积后的图像大小为 28×28,因此每个特征图有 28×28 个神经元,卷积核参数为(5×5+1)×6,因此,该层的连接数为(5×5+1)×6×28×28=122304
S2 层(下采样层,也称池化层):6@14×14
- 特征图大小:
池化单元为 2×2,因此,6 个特征图的大小经池化后即变为 14×14。
- 参数个数:
S2 层由于每个特征图都共享相同的 w 和 b 这两个参数,因此需要 2×6=12 个参数
- 连接数:
下采样之后的图像大小为 14×14,因此 S2 层的每个特征图有 14×14 个神经元,每个池化单元连 接数为 2×2+1(1 为偏置量),因此,该层的连接数为(2×2+1)×14×14×6 = 5880
C3 层(卷积层):16@10×10
C3 层有 16 个卷积核,卷积模板大小为 5×5。
- 特征图大小:
与 C1 层的分析类似,C3 层的特征图大小为(14-5+1)×(14-5+1)= 10×10
-
参数个数:
需要注意的是,C3 与 S2 并不是全连接而是部分连接,有些是 C3 连接到 S2 三层、有些四层、甚至达到 6 层,通过这种方式提取更多特征,连接的规则如下表所示:
- 连接数:
卷积后的特征图大小为 10×10,参数数量为 1516,因此连接数为 1516×10×10= 151600
S4(下采样层,也称池化层):16@5×5
- 特征图大小:
与 S2 的分析类似,池化单元大小为 2×2,因此,该层与 C3 一样共有 16 个特征图,每个特征图的大小为 5×5
- 参数个数:
与 S2 的计算类似,所需要参数个数为 16×2 = 32
- 连接数:
连接数为(2×2+1)×5×5×16 = 2000
C5 层(卷积层):120
-
特征图大小:
该层有 120 个卷积核,每个卷积核的大小仍为 5×5,因此有 120 个特征图。由于 S4 层的大小为 5×5,而该层的卷积核大小也是 5×5,因此特征图大小为(5-5+1)×(5-5+1)= 1×1。这样该层就刚好变成了全连接,这只是巧合,如果原始输入的图像比较大,则该层就不是全连接了。
-
参数个数:
与前面的分析类似,本层的参数数目为 120×(5×5×16+1) = 48120
- 连接数:
由于该层的特征图大小刚好为 1×1,因此连接数为 48120×1×1=48120
F6 层(全连接层):84
-
特征图大小:
F6 层有 84 个单元,之所以选这个数字的原因是来自于输出层的设计,对应于一个 7×12 的比特图,如下图所示,-1 表示白色,1 表示黑色,这样每个符号的比特图的黑白色就对应于一个编码。
该层有 84 个特征图,特征图大小与 C5 一样都是 1×1,与 C5 层全连接。 -
参数个数:
由于是全连接,参数数量为(120+1)×84=10164。跟经典神经网络一样,F6 层计算输入向量和权重向量之间的点积,再加上一个偏置,然后将其传递给 sigmoid 函数得出结果。
-
连接数:
由于是全连接,连接数与参数数量一样,也是 10164。
OUTPUT 层(输出层):10
Output 层也是全连接层,共有 10 个节点,分别代表数字 0 到 9。如果第 i 个节点的值为 0,则 表示网络识别的结果是数字 i。
- 特征图大小:
该层采用径向基函数(RBF)的网络连接方式,假设 x 是上一层的输入,y 是 RBF 的输出,则 RBF 输出的计算方式是:
上式中的 Wij 的值由 i 的比特图编码确定,i 从 0 到 9,j 取值从 0 到 7×12-1。RBF 输出的值越接 近于 0,表示当前网络输入的识别结果与字符 i 越接近。
- 参数个数:
由于是全连接,参数个数为 84×10=840
- 连接数:
由于是全连接,连接数与参数个数一样,也是 840
通过以上介绍,已经了解了 LeNet 各层网络的结构、特征图大小、参数数量、连接数量等信息, 下图是识别数字 3 的过程,可对照上面介绍各个层的功能进行一一回顾: