卷积神经网络基础
01 - 相关基础概念
首先,来说一下卷积的概念,从数学角度来看,可以简单的认为卷积是一种数学运算,在通信领域,卷积也被应用在滤波上,因此通常将卷积和滤波同等看待,卷积核也经常被称为滤波器。
接下来通过一个例子说明卷积运算的具体操作:
【注】:动态演示参考如下网址:http://www.shipudong.com/2020/03/24/bi-ye-she-ji-nei-rong-bu-chong/
图3.9卷积操作演示
上述图中,输入图像大小为7*7*3,卷积核大小为3*3*3,且有两个卷积核,故最终经卷积操作之后的特征图(output volume)有两个。以第一个卷积核运算为例进行讲解,且图中步长(stride=2):
各参数的计算方式(为了方便标记,用a来表示矩阵,i、j表示行和列):
计算方法就是:将输入数据(取与卷积核相同大小)与分别与第一个卷积核、第二个卷积核进行卷积运算,且步长为2在输入数据上移动,其余具体计算过程不再赘述。
02 - 局部感知野
在前述文章中,我们讨论了简单的神经网络,并且知道对于全连接神经网络,它的每个神经元都与前一层的所有神经元进行连接,这种连接方式很容易造成一个不可避免的问题就是:一旦隐藏层层数增加,会带来大量的参数,最终造成无法训练,因此在本节中引入局部感知野的概念。
引入局部感知野的目的就是为了减少参数量,对于一张图片,无论是灰度图还是彩色图,我们一般认为局部像素周围的联系比较紧密(简单理解为:某一像素点及其周围的像素点差别不大),而像素点之间距离相差越远,联系越弱。所以没有必要获取到整张图片所有的信息,只需要利用局部感知野的概念获取局部的信息,并在更高层对这些信息进行综合,也可以得到全局的信息。相比全连接神经网络的方式,采用局部感知野的概念之后,参数量大大降低。
03 - 参数共享
在第二小节中我们引入了局部感知野的概念来帮助我们减少参数量,但是经过局部感知野之后,所剩余参数量还是较多,对于模型的学习训练还是非常艰难的,因此再引入参数共享的概念,对于输入图像的每一个节点,没有必要用不同的卷积核去进行卷积运算,再通过各个局部感知野连接的区域内,我们可以使用相同的参数和权值,这就是所谓的参数共享,经过局部感知野和参数共享两大优化方法,此时神经网络中的参数就可以很容易的进行训练学习了。
04 - 多卷积核
对于复杂的输入数据,通过卷积操作想要提取的特征类肯定不止一种,因此我们必须通过多个不同功能的卷积核来进行卷积操作。通过图3.9中的例子我们可以知道,图中输入数据是RGB彩色图,其中有3个通道(channel=3),且有两个卷积核,最后经卷积操作之后所得的特征图也有2个。
经过上面的介绍,可以知道多卷积核概念的引入是为了帮助我们提取更加复杂图像的特征,本质上还是本文上述所讲内容的一个应用。
05 - 池化
首先,来讨论一个例子,对于一张500*500的图片,用100个卷积核来提取图像的不同特征,卷积核大小为3*3,步长为1,并且默认不再图像周围做填充,根据公式 计算得到(W=500,F=3,p=0,s=1)经卷积之后的图像大小为498*498,总结一下,100个卷积核可以得到100个大小为498*498的图片,即24800400(498*498*100)的卷积特征向量,对如此量级的特征个数去进行分类,我们很容易造成过拟合现象的发生,因此我们在本小节中引入池化的概念。
池化的原理就是根据相邻元素具有相似性,因此可以将相邻元素进行合并,从而大幅的减少特征个数。池化中包括平均池化和最大池化操作,在我们本论文的案例中,我们使用的是最大池化操作,其过程是:将输入数据经过池化操作,并只保留池化区域中最大的一个值,其余均被忽略掉。
06 - 多层卷积
本节关于多层卷积概念的引入等同于前述章节中对于多卷积核概念的引入,以人脸识别的例子为例,我们需要提取人脸的各种特征,包括眉毛、鼻子、嘴巴、酒窝等,这些还属于较为高级的特征,对于机器来说,我们需要继续寻找更为低级的特征来供机器进行学习,为此为了对一个物体进行特征提取我们需要多层的卷积操作,并且通常我们还会在每一个卷积层后面加上非线性函数(ReLU,本论文的例子中关于对AlexNet模型的介绍,在每一层后面都使用了ReLU)。
07 - 池化层和卷积层的反向传播
在前述小节中,我们了解过反向传播的概念,并且知道首先通过前向计算我们可以得到各个节点的激活函数的值,接着我们从最后一层逐层向前计算残差值,然后根据残差值求出对应偏导数的值,并最后更新网络中的参数(权值w和偏置项b)。
然而,全连接神经网络与卷积神经网络的区别就是:在卷积神经网络中有卷积层和池化层的存在,所以在这两层中肯定会有反向传播的概念。
首先,我们来简单介绍池化层反向传播的概念。上述内容提到过,我们本论文案例中使用的是最大池化操作,我们不讨论此时的前向传播,假设此时经过池化之后的残差值已经从最后一层的反向传播计算得到(我们假设前向计算过程中每一区域的最大值我们已经标注出来了),则池化层(最大池化层)的反向传播就是逐层算出残差值,然后将残差值传递给已标注的最大位置的神经元。
接着我们来介绍卷积层的反向传播残差值的计算,具体公式我们这里不做推导,仅以如何计算为主:
卷积之前的矩阵:
卷积核矩阵:
卷积之后的残差值:
我们现在需要计算卷积之前各个节点的残差:
其余计算过程我们不再进行计算,最终所得卷积之前各个节点的残差值为:
计算得出残差值之后,我们就可以根据公式更新卷积核的参数。
TensorFlow 2.0版本中的卷积神经网络
在本系列推文的TensorFlow 2.0 概述中,我们TensorFlow 2.0版本中的一些API及其基础知识有了一定的了解,在本小节中论文将介绍一下在本文中所涉及的两个案例中所用到的关于卷积神经网络中的一些API。
模型搭建层的核心代码如下所示:
model = tf.keras.models.Sequential([ tf.keras.layers.Conv2D(input_shape = (28,28,1),filters = 32,kernel_size = 5,strides = 1,padding = "same",activation = "relu"), # 28*28 tf.keras.layers.MaxPool2D(pool_size = 2,strides = 2,padding = 'same'), # 14*14 tf.keras.layers.Conv2D(64,5,strides=1,padding='same',activation='relu'), # 14*14 tf.keras.layers.MaxPool2D(2,2,'same'), # 7*7 tf.keras.layers.Conv2D(64,5,strides=1,padding='same',activation='relu'), # 7*7 tf.keras.layers.MaxPool2D(2,2,'same'), # 3*3 tf.keras.layers.Flatten(), # 64*3*3 tf.keras.layers.Dense(1024,activation='relu'), tf.keras.layers.Dropout(0.5), tf.keras.layers.Dense(10,activation = 'softmax'), ])
上述代码中,我们通过Sequential来顺序构建模型,通过高阶APItf.keras.layers来搭建每一层需要的网络。如下是TensorFlow 2.0版本官网对于各API 的介绍:
图3.10 相关API操作