说在前面
首先给大家推荐一个可视化CNN整个过程的网站,https://poloclub.github.io/cnn-explainer/
这个是2021年IEEETRANSACTIONS ON VISUALIZATION AND COMPUTER GRAPHICS 的一篇论文,对CNN模型的每个步骤都进行了可视化,对于理解学习CNN有很大的帮助,强烈推荐配合使用。效果图如下:
1. 什么是卷积?
卷积(convolution),指的是数字图像处理的卷积操作,我总结就一句话:对应相乘再相加,下面直接上图:
image代表原始的图像,卷积一般有卷积模板(或者叫卷积核),图中黄色方块右下角组成的3×3 的卷积核,该卷积核为:
“对应相乘”指的是原始图像与卷积核对应相乘,例如左上角第一个3×3区域,计算为$1×1+1×0+1×1+0×0+1×1+1×0+0×1+0×0+1×1=4$输出的对应右边卷积特征的第一个元素4,后面以此类推。
通过上述演示,也可以看出在经过3×3 的卷积核卷积操作之后,生成的
feature map
尺寸和原图像不一样,卷积操作隐含一个参数stride
(步长),步长就是卷积核滑动的长度,这里默认stride=1
,那么就有如下公式计算输出feature map
的尺寸大小。假设原始图像尺寸为$M×N$,卷积核尺寸为$W_1×W_2$,步长$stride=S$,则输出feature map
的尺寸为:进一步,我们引出
padding
的概念,为了使输出feature map
的大小和原始图像保持一致,我们需要对原图像进行padding
,也就是扩展像素,这里设置padding=1
(即原始图像上下左右扩展1个像素,一般用0填充),padding
后的图像尺寸为$7×7$,这样再通过卷积得到的feature map
就是$5×5$,和原始图像保持一致,下面修正上述公式,假设padding=P
,则输出feature map
的尺寸为:再进一步,我们上述的输入图像默认是灰度图像,他是单通道的,但是对于三通道的彩色图像,又是如何进行卷积的呢?
假如输入的彩色图像维度是
[64,64,3]
,“3”代表“R G B”三通道,那么自然而然想到的是同样有三个通道的卷积核对“R G B”通道分别进行卷积,设一个卷积核维度是[3,3,3]
,(也是三个通道)stride=1, padding=0
,那么卷积之后得到的是3个$(64-3+1)×(64-3+1)=62×62$,正常来说输入一个图像,应该得到一个feature map
,所以应该把这三个$62×62$对应像素相加得到最终的feature map
。 这里学过机器学习的同学应该就知道在相加之后还要加上一个Bias偏执单元。所以3通道的卷积过程如下图所示:
最后一步,我们上面所使用的仅仅是一个卷积核
[3,3,3]
,那么实际的CNN中卷积核不只一个,这时我们再给卷积核加上一个维度n,代表卷积核的个数,[3,3,3,10]
(假设n=10
),那么也就是说,有10个卷积核对三通道的彩色图像分别进行上述卷积操作,那么得到的就应该有10个feature map
即[62,62,10]
,如下图所示,也就是说卷积得到的feature map
个数仅仅与卷积核个数有关(大家一定要明白这个原理)。
2. 非线性激活Relu
由上面图我们大家也可以看到卷积生成
feature map
后,又进行了激活函数ReLu处理,Relu激活函数是在CNN中经常用到的,$ReLu(x)=max(0,x)$,卷积层对原图运算多个卷积产生一组线性激活响应,而非线性激活是对之前的结果进行非线性的响应。个人理解是对卷积后的特征图进行有目的的提取,小于0的不是识别特征,故记为0,而大于0的就是与卷积模板匹配的特征保留下来。
至此,所有卷积的内容我们都已经了解完了,下面就进行CNN的下一部分池化Pooling层。
3. Pooling 池化层
通过卷积操作之后,我们得到了许多
feature map
,并且数量和卷积核的个数相同,此时的数据非常庞大,并且这仅仅是一层卷积,对于深度学习来说,卷积层有很多,那也就意味着feature map
也会变多,为了解决这个问题,Pooling层就起到了关键作用,他的目标就是减少数据量,提取重要特征。Pooling池化层一般分为两种:Max Pooling 最大池化和Average Pooling 平均池化,顾名思义,最大池化就是取最大值,平均池化就是取平均值,下面举一个例子来说明:
上述演示的就是Max Pooling 的过程,其中蓝色的是池化模板的尺寸为$2×2$,池化也有
stride
步长,一般stride=2
,最大池化就是取$2×2$特征值中的最大值,然后以stride=2
进行移动池化模板,遍历完整个feature map
。设原来feature map
尺寸为$M×N$,池化模板尺寸为$W×W$,步长为S(一般取2),padding 为P(池化也可以先padding),可以得到池化层的输出尺寸公式:平均池化也就是将上面的$2×2$特征值中取平均值,这里就不再赘述,我们在CNN中一般使用的是最大池化,因为
feature map
本身就是卷积之后并且经过Relu的结果,如果没有提取到相应特征,经过Relu就被置为0,在Pooling时选取最大的就能代表feature map
的最佳特征,从而实现降维,减少数据量,最大池化保留了每一小块的最大值,所以它相当于保留了这一块的最佳匹配结果。
4. 全连接层
上面讨论过卷积和池化之后,深度学习CNN的神秘面纱也就被揭开,Deep主要体现在了网络的深度上,也就是许多的卷积层和池化层堆叠的结果,每一层都依据上述原理进行。
下面我们“不忘初心”,我们的目的是什么,回头看我们要提取图像的高层次特征不就是为了识别出该图像是什么吗?也就是图像分类,那么经过这么多卷积和池化之后已经提取了高维特征,下面就是对其进行分类了,这里的分类就用的是全连接层,全连接层的作用就是对特征进行维度上的改变来得到每个分类类别对应的概率值。
全连接层就是基本的神经网络,可以参考我机器学习专栏的博客:https://blog.csdn.net/qq_40181592/article/details/97747125 (Machine Learning 学习笔记(七)——神经网络(Neural Networks)),里面详细说明了神经网络的工作过程,在此不在赘述。
这就就提一下全连接层最后一层输出使用的是$Softmax$分类函数:
它可以将网络输出变成一个概率分布,且n类的概率相加等于1,这就很方便的可以判断网络最终分类的结果,具体计算如下所示:
可以看到全连接层的输入是将池化后的
feature map
先进行了flatten
打平操作,然后再输出到最后一层,之后通过Softmax函数输出概率,各个类别的概率计算在图中标出。
5. 对比卷积层核全连接层
全连接层为什么叫全连接呢?
因为它的每一个神经元都与上一层的神经元进行连接,故称全连接,相邻两层神经元与神经元之间都是通过一个参数相连。
卷积层“局部连接”、“参数共享”的思想?
这时再回头来看前面的卷积层就肯定不是全连接了,他是“局部连接”的思想,每一次只有3×3的卷积核与对应的图像进行相连,只不过这个卷积核通过步长在滑动。也就是说每次只有卷积核大小的图像与卷积核进行参数连接,而未连接的部分是通过将窗口滑动起来的方法进行后续的连接,这个思想就是“参数共享”,这里的参数就是每个卷积核的值,对于同一个卷积核,在对图像进行卷积时,卷积核是不变的,所以参数可以共用。
这也就是卷积层核全连接层的最重要的区别。
6. CNN的反向传播
以上是对CNN各个部分的分析,那么我们训练的是什么呢?训练的就是那些卷积核的参数,训练的方法仍然是之前神经网络的正向传播和反向传播算法,只不过计算每一层的梯度和误差时不一样而已。
由此看来CNN的训练就变得简单起来了,我们只需要知道卷积层、Pooling层的误差和对每个参数梯度怎么求,就可以进行训练了,这里推荐一篇CNN反向传播算法详解 https://zhuanlan.zhihu.com/p/61898234 ,里面详细说明了卷积层和Pooling层的误差计算和梯度计算方法,下面我就只给出结论:
对于卷积层,原图的delta误差,等于卷积结果的delta误差经过零填充后,与卷积核旋转180度后的卷积。(具体推导可以参考上面的链接文章)
delta 误差是损失函数对于当前层未激活输出$z^l$的导数:$\delta=\sigma^{‘}(z^l) $.
当前卷积层的误差$\delta^l$为:
其中
ROT180()
表示将矩阵旋转180°,符号$\odot$为元素相乘。当获得每一层的误差项时,偏导数$\frac{\partial L}{\partial w_{ij}^{l}}$ 和$\frac{\partial L}{\partial b_{i}^{l}}$ 计算公式如下:
对于池化层,他的反向传播理解比较容易,如下图所示,池化后的数字6对应于池化前的红色区域,实际上只有红色区域中最大值数字6对池化后的结果有影响,权重为1,而其它的数字对池化后的结果影响都为0。假设池化后数字6的位置delta误差为$\delta$,误差反向传播回去时,红色区域中最大值对应的位置delta误差即等于 $\delta$,而其它3个位置对应的delta误差为0。
而对于平均池化来说所有权重都是1/4。
用公式表示为:
7. CNN的训练
最后训练的方法就是梯度下降法,基础的梯度下降可以参考我机器学习专栏的文章https://blog.csdn.net/qq_40181592/article/details/95479520 Machine Learning 学习笔记(二)——梯度下降(Gradient Descent)
这里我就补充一下随机梯度下降SGD(Stochastic Gradient Descent)和Mini-batch梯度下降:
引入随机梯度下降是因为原始的梯度下降BGD要遍历所有的数据集去计算Cost function,这样对于深度学习来说,需要大量的数据集基本都是上万数量(甚至更多),要训练一次花费太大,并且速度较慢,故引出SGD,每次只考虑一个样本,对一个样本进行参数更新,具体训练如下:
1
2
3
4
5
6
71. 随机打乱所有数据集
2.Repeat{
for i = 1, 2, ... m:{
(更新wj 参数) for j = 0,1,2,...n
}
}
外层循环是重复上述过程,一般1~10次就好了SGD收敛是无规律的,只是朝着全局最优的范围去,并不像BGD一样每次都找最优的方向。
Mini-batch梯度下降
b = mini-batch size,一般取10(2~100之间),他是介于SGD和BGD之间的,一次使用b个样本来更新参数的值,它的更新公式为:
for i = 1, 1+b, 1+2b, 1+3b ……:
区别:SGD每次只需要1个样本,而mini batch每次要b个样本,BGD一次需要全部样本。
8. 总结CNN整个训练过程
对神经网络进行初始化,定义好网络结构,设定好激活函数,对卷积层的卷积核W、偏置b进行随机初试化,对全连接层的权重矩阵W和偏置b进行随机初始化。
设置好训练的最大迭代次数,每个训练batch的大小,学习率 $\eta$.从训练数据中取出一个batch的数据
从该batch数据中取出一个数据,包括输入x以及对应的正确标注y
将输入x送入神经网络的输入端,得到神经网络各层输出参数$z^l$和$a^l$.
根据神经网络的输出和标注值y计算神经网络的损失函数Loss
计算损失函数对输出层的delta误差$\delta_L$
利用相邻层之间delta误差的递推公式求得每一层的delta误差
如果是全连接层如果是卷积层
如果是池化层
利用每一层的delta误差求出损失函数对该层参数的导数
如果是全连接层:如果是卷积层:
将求得的导数加到该batch数据求得的导数之和上(初始化为0),跳转到步骤3,直到该batch数据都训练完毕
利用一个batch数据求得的导数之和,根据梯度下降法对参数进行更新
跳转到步骤2,直到达到指定的迭代次数
——END——
结尾
最后这篇文章整个总结了CNN的结构和具体训练过程,并且一开始就给出了CNN的可视化工具,大家学完可以用那个工具看看CNN的可视化,一定会对CNN理解更深刻的,后面将进行实战,利用CNN进行分类识别,采用的是tensorflow 2.2 GPU版本 + Vs Code,如果没有配置好的可以参考上一篇文章。
如果有错误请及时在评论区指出,作者会积极改正,大家一起进步!