CNN前向/反向传播原理推导——卷积网络从零实现系列(2)

发布于:2022-11-29 ⋅ 阅读:(242) ⋅ 点赞:(0)

一、前言:

        本系列文章链接:

CNN前置知识:模型的数学符号定义——卷积网络从零实现系列(1)_日拱一两卒的博客-CSDN博客https://blog.csdn.net/yangwohenmai1/article/details/126951241?spm=1001.2014.3001.5501

CNN前向/反向传播原理推导——卷积网络从零实现系列(2)_日拱一两卒的博客-CSDN博客https://blog.csdn.net/yangwohenmai1/article/details/126622703?spm=1001.2014.3001.5501

CNN前向传播源码实现——CNN数学推导及源码实现系列(3)_日拱一两卒的博客-CSDN博客icon-default.png?t=M85Bhttps://blog.csdn.net/yangwohenmai1/article/details/127230225?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22127230225%22%2C%22source%22%3A%22yangwohenmai1%22%7D

        第四篇卷积网络反向传播的代码实现,正在写作。。。

        这个CNN系列是在学习图像识别过程中得一些学习笔记,包含理论分析和源码实现两部分。本质属于进阶内容,因此神经网络的基础内容不做过多讲解,想学习基础,可看之前的神经网络入门系列文章:人工智能专题研究_日拱一两卒的博客-CSDN博客

        本系列重心放在解析CNN算法逻辑、前向和反向传播数学原理、推导过程、以及CNN模型的源码实现上。

        本文详细讲解CNN的前向传播和反向传播算法理论,数学原理以及推导过程,并在文章最后,对卷积这个容易混淆的常见的概念进行进一步的解释,神经网络中的卷积和信号系统中的卷积到底有什么关系。本文是建立在上篇文章的基础上,很多数学表达式不在重新推导,详细推导过程参见文章:

CNN前置内容:网络模型的数学符号定义——CNN数学推导及源码实现系列(1)_日拱一两卒的博客-CSDN博客https://blog.csdn.net/yangwohenmai1/article/details/126951241?spm=1001.2014.3001.5501

二、CNN的基本结构

1.卷积网络结构

        卷积神经网络(CNN)在模型中引入了新的结构:卷积层(Convolution Layer)和池化层(Pooling Layer)。网络模型由各个处理层叠加构成,一个或多个卷积层可选搭配池化层,组成Conv-Pool 单元,多个Conv-Pool单元串联(如LeNet) , 或并联(如GoogLeNet)组合在一起,一个最简单的卷积网络可以描述如下构成模式如下:

输入层 -->{{卷积层}+{池化层}}-->{全连接层}-->输出层

        {卷积层}+{池化层}这个组合可以在网络上中出现很多次,在下图示例中出现两次。而实际上这个次数是根据模型的需要而来的。当然我们也可以灵活使用使用{卷积层}+{卷积层},或者{卷积层}+{卷积层}+{池化层}的组合,在构建模型的时候没有限制。但是最常见的CNN都是若干{卷积层}+{池化层}的组合。

        在若干{卷积层}+{池化层}后面是全连接层(Fully Connected Layer, 简称FC),卷积网络中的全连接层与上篇文章DNN模型中的结构相同,只是在输出层前使用了Softmax激活函数来做分类功能。如果我们熟悉普通的DNN网络,只要把卷积层和池化层的原理搞清楚了,那么搞清楚CNN就容易很多了。

2.卷积运算规则

        卷积运算规则属于基础内容,这里不做过多讲解,可以自行查阅资料。这里找了一个比较直观的图来展示一下卷积运算规则:

        输入结构:

输入矩阵

        卷积核: 

卷积核

        运算规则: 

运算规则

三、CNN前向传播的数学原理

1.CNN输入层前向传播到卷积层

        输入层的前向传播是CNN前向传播算法的第一步。一般的网络结构中,输入层的下一层对应的都是卷积层,因此我们标题是输入层前向传播到卷积层。

        我们这里还是以图像识别为例。先考虑最简单的,样本都是二维的黑白图片,也就是说输入数据只有一个参数:“灰度”。这里“灰度”可以理解为一个数值,好比0是白色,100是黑色。这样输入层X就是一个矩阵,矩阵的值等于图片的各个像素位置的“灰度”值。这时和卷积层相连的卷积核W就也是矩阵。如果样本都是有RGB的彩色图片,这样输入X就是3个矩阵,分别对应红绿蓝三种颜色,即R、G、B的矩阵,或者说是一个张量。这时和卷积层相连的卷积核W就也是张量,对应的最后一维的维度为3.即每个卷积核都是3个子矩阵组成的,也称三通道。

        同样的方法,对于3D的彩色图片之类的样本,我们的输入X可以是4维,5维的张量,那么对应的卷积核W也是个高维的张量。不管维度多高,对于我们的输入,前向传播的过程可以表示为:

a^2= \sigma(z^2) = \sigma(a^1*W^2 +b^2)

        上一篇文章讲解过上式中数学符号的意义,字母的上标代表层数,而b代表偏置, \sigma为激活函数,一般是ReLU,而这里的星号\ast则代表卷积运算。和上一篇文章中DNN网络的前向传播表达式比较一下,其实形式非常的像,只是我们这儿是张量的卷积运算,而不是矩阵的乘法。同时由于W是张量,可以有多通道,那么同样的位置,W参数的个数就比DNN多很多了。

        为了简化我们的描述,本文后面如果没有特殊说明,我们都默认输入是3维的张量(3通道),即用RBG可以表示的彩色图片。后续需要我们自己定义的CNN模型参数是:

        1) 每层的卷积核个数:一般我们的卷积核不止一个,比第一个卷积层的卷积核如有K个,那么第二层卷积层的对应的输入就K个。

        2) 卷积核中每个子矩阵的的大小:一般我们都用方阵作为卷积核的子矩阵,比如FxF的子矩阵。

        3) 填充大小Padding(以下简称P):我们卷积的时候,为了可以更好的识别图像边缘,一般都会在输入矩阵的周围加上若干圈的0值后,再进行卷积,加多少圈0则P值为多少。

        4) 步幅stride(以下简称S),即在卷积过程中卷积核每次移动的像素距离大小。

2. 隐藏层前向传播到卷积层

        现在我们再来看数据从隐藏层前向传播到卷积层时的前向传播算法。假设此时和卷积层相连的不再是输入层,而是连接着隐藏层,隐藏层的输出是M个矩阵(M通道)对应的三维张量,则当前卷积层的卷积核也是M个子矩阵对应的三维张量。这时其中某一个子矩阵对应的表达式和上一节很像,也是

a^l= \sigma(z^l) = \sigma(a^{l-1}*W^l +b^l)

        上标代表层数,星号\ast代表卷积,b代表偏置, \sigma为激活函数,一般是ReLU。也可以写成M个子矩阵分别进行卷积计算后对应位置相加的形式,即:

a^l= \sigma(z^l) = \sigma(\sum\limits_{k=1}^{M}z_k^l) = \sigma(\sum\limits_{k=1}^{M}a_k^{l-1}*W_k^l +b^l)

这里和上一节唯一的区别在于,这里的输入是隐藏层产生的,而不是输入的原始图片样本形成的矩阵。需要我们定义的CNN模型参数也和上节一样,分别是卷积核个数K,卷积核子矩阵的维度F,填充大小P以及步幅S。

4. 卷积层前向传播到池化层

        卷积层后面一般链接着池化层,池化层的处理逻辑比较简单,池化层的目的是对传入的矩阵进行缩小概括。比如输入的若干矩阵是NxN维的,而我们的池化大小是kxk的区域,则经过池化层后输出的矩阵都是\frac{N}{k} \times \frac{N}{k}维的。这里需要定义的CNN模型参数是:

        1)池化区域的大小k

        2)池化的标准,一般是最大池化MAX或者平均池化Average。

最大池化
最大池化

5. 池化层前向传播到全连接层

        池化层后面一般连接着全连接层,有时中间会多一个BN层,由于全连接层就是普通的DNN模型结构,因此我们可以直接使用DNN的前向传播算法逻辑,这里的激活函数一般是sigmoid或者tanh。即:

a^l = \sigma(z^l) = \sigma(W^la^{l-1} + b^l)

        经过了若干全连接层之后,假如我们处理的是一个分类任务,最后一层是Softmax输出层。此时Softmax输出层和普通的全连接层唯一的区别是,激活函数是softmax函数。这里需要需要我们定义的模型参数是:

        1)全连接层的激活函数

        2)全连接层各层神经元的个数

6. CNN前向传播算法流程

        有了上面的基础,我们现在总结下CNN的前向传播算法。

        输入:图片样本可以是单通道的二维矩阵,也可以是多通道的三维张量,CNN模型的层数L和所有隐藏层的类型,对于卷积层,要定义卷积核的大小K,卷积核子矩阵的维度F(通道数),图像边缘填充大小P,步幅S。对于池化层,要定义池化区域大小k和池化标准(MAX或Average),对于全连接层,要定义全连接层的激活函数和各层的神经元个数。

        输出:CNN模型的输出a^L

        1) 根据输入层的填充大小P,填充原始图片的边缘,得到输入张量a^1

        2)初始化所有隐藏层的参数W,b  

        3)for l=2 to L-1:

                a) 如果第l层是卷积层,则输出为

a^l= ReLU(z^l) = ReLU(a^{l-1}*W^l +b^l)

           b) 如果第l层是池化层,则输出为a^l= pool(a^{l-1}), 这里的pool指按照池化区域大小k和池化标准将输入张量缩小的过程。

                c) 如果第l层是全连接层,则输出为

a^l= \sigma(z^l) = \sigma(W^la^{l-1} +b^l)

        4)对于输出层softmax层:

a^L= softmax(z^L) = softmax(W^La^{L-1} +b^L)

        以上就是CNN前向传播算法的过程总结。有了前向传播算法的基础,我们后面再理解CNN的反向传播算法就简单多了。

四、CNN反向传播的数学原理

1. 回顾DNN的反向传播算法思想

        我们首先回顾DNN的反向传播算法。在DNN中,我们是首先计算出输出层的\delta^L:

\delta^L = \frac{\partial J(W,b)}{\partial z^L} = \frac{\partial J(W,b)}{\partial a^L}\odot \sigma^{'}(z^L)

        利用数学归纳法,用\delta^{l+1}的值一步步的向前求出第l层的\delta^l,表达式为:

\delta^{l} = (\frac{\partial z^{l+1}}{\partial z^{l}})^T\delta^{l+1} = (W^{l+1})^T\delta^{l+1}\odot \sigma^{'}(z^l)

        有了\delta^l的表达式,从而求出W,b的梯度表达式:

\frac{\partial J(W,b)}{\partial W^l} = \delta^{l}(a^{l-1})^T

\frac{\partial J(W,b,x,y)}{\partial b^l} = \delta^{l}

        有了W,b梯度表达式,就可以用梯度下降法来优化W,b,求出最终的所有W,b的值。现在我们想把同样的思想用到CNN中,显然有些不同的地方要进行修改。

2. 对比CNN的反向传播算法思想

        要套用DNN的反向传播算法到CNN,有几个问题需要解决:

        1)池化层没有激活函数,这个问题倒比较好解决,我们可以令池化层的激活函数为\sigma(z) = z,即激活后就是自己本身。这样池化层激活函数的导数为1.

        2)池化层在前向传播的时候,对输入进行了压缩(最大池化或平均池化),那么我们现在需要向前反向推导\delta^{l-1}时,推导方法和DNN完全不同。

        3) 卷积层的卷积计算时张量计算,或者说是若干个子矩阵分别卷积而得到的输出结果,这和DNN很不相同,DNN的全连接层是直接进行矩阵乘法得到当前层的输出。所以卷积层在计算反向传播时,上一层的\delta^{l-1}递推计算方法肯定不同。

        4)对于卷积层,由于W使用的运算是卷积,那么从\delta^l推导出该层的所有卷积核的W,b的方式也不同。

        上面这些问题中,问题1比较好解决,但是问题2,3,4比较困难,而问题2,3,4也是解决CNN反向传播算法的关键所在。另外大家要注意到的是,DNN中的a_l,z_l都只是一个向量,而我们CNN中的a^l,z^l都是三维的张量,即由若干个子矩阵组成的“大矩阵”。

        下面就针对问题2,3,4来一步步研究CNN的反向传播算法。在研究过程中,需要注意由于一个卷积层可以有多个卷积核,而各个卷积核的处理方法是完全相同且独立的,为了简化算法公式的复杂度,我们下面提到卷积核都是卷积层中若干卷积核中的一个。

3. 已知池化层的\delta^l,推导上一隐藏层的\delta^{l-1}   

        我们首先解决上面的问题2,如果已知池化层的\delta^l,如何推导出上一隐藏层的\delta^{l-1}

        在CNN模型前向传播时,池化层一般我们会用MAX或者Average对输入进行池化,池化的区域大小已知。现在我们反过来,要从缩小后的误差\delta^l,还原前一次较大区域对应的误差。

        在反向传播时,我们首先会把\delta^l的所有子矩阵矩阵大小还原成池化之前的大小,然后如果是MAX,则把\delta^l的所有子矩阵的各个池化局域的值放在之前做前向传播算法得到最大值的位置。如果是Average,则把\delta^l的所有子矩阵的各个池化局域的值取平均后放在还原后的子矩阵位置。这个过程一般叫做向上采样upsample。

        用一个例子可以很方便的表示,假设我们的池化区域大小是2x2,\delta^l的第k个子矩阵为:

\delta_k^l = \left( \begin{array}{ccc} 2& 8 \\ 4& 6 \end{array} \right)

        由于池化区域为2x2,我们先将\delta_k^l做还原,即变成:

\begin{pmatrix} 0& 0& 0& 0\\ 0& 2& 8& 0\\ 0& 4& 6& 0\\ 0& 0& 0& 0 \end{pmatrix}

        如果是MAX,假设我们之前在前向传播时记录下的最大值位置分别是左上,右下,右上,左下,则转换后的矩阵为:

\begin{pmatrix} 2& 0& 0& 0\\ 0& 0& 0& 8\\ 0& 4& 0& 0\\ 0& 0& 6& 0 \end{pmatrix}

        如果是Average,则进行平均分配:转换后的矩阵为:

\begin{pmatrix} 0.5& 0.5& 2& 2\\ 0.5& 0.5& 2& 2\\ 1& 1& 1.5& 1.5\\ 1& 1& 1.5& 1.5 \end{pmatrix}

        这样我们就得到了上一层 \frac{\partial J(W,b)}{\partial a_k^{l-1}}的值,要得到\delta_k^{l-1}

\delta_k^{l-1} = (\frac{\partial a_k^{l-1}}{\partial z_k^{l-1}})^T\frac{\partial J(W,b)}{\partial a_k^{l-1}} = upsample(\delta_k^l) \odot \sigma^{'}(z_k^{l-1})

        其中,upsample函数完成了池化误差矩阵放大与误差重新分配的逻辑。对于张量\delta^{l-1},我们有通项表达式:

\delta^{l-1} = upsample(\delta^l) \odot \sigma^{'}(z^{l-1})

4. 已知卷积层的\delta^l,推导上一隐藏层的\delta^{l-1}  

        对于卷积层的反向传播,我们首先回忆下卷积层的前向传播公式:

a^l= \sigma(z^l) = \sigma(a^{l-1}*W^l +b^l)

        在DNN中,我们知道\delta^{l-1}\delta^{l}的递推关系为,推导过程可参考上一篇文章第四节部分:

\delta^{l} = \frac{\partial J(W,b)}{\partial z^l} =(\frac{\partial z^{l+1}}{\partial z^{l}})^T \frac{\partial J(W,b)}{\partial z^{l+1}} =(\frac{\partial z^{l+1}}{\partial z^{l}})^T\delta^{l+1}

        因此要推导出\delta^{l-1}\delta^{l}的递推关系,必须计算\frac{\partial z^{l}}{\partial z^{l-1}}的梯度表达式。上篇文章中我们已知了z^{l}z^{l-1}的关系表达式为:

z^l = a^{l-1}*W^l +b^l =\sigma(z^{l-1})*W^l +b^l

        我们最终可以得到如下结论,这个结论后面会详细证明:

\delta^{l-1} = (\frac{\partial z^{l}}{\partial z^{l-1}})^T\delta^{l} = \delta^{l}*rot180(W^{l}) \odot \sigma^{'}(z^{l-1})

        这里的表达式其实和上一篇文章中DNN的反向传播表达式类似,区别在于对于含有卷积的式子求导时出现了一个新符号rot180(),这里rot180表示卷积核被旋转了180度,我们可以通过对矩阵进行先上下翻转一次,再左右翻转一次来实现,180度反转示例如下:

\begin{pmatrix} 1 &2 \\ 3& 4 \end{pmatrix}\Rightarrow \begin{pmatrix} 4 & 3\\ 2& 1 \end{pmatrix}

        在普通DNN反向传播求导过程中,是矩阵的转置计算,而在卷积网络反向传播求导过程中采用的是卷积核翻转180度。由于张量的直接推导过程比较复杂,下面用一个简单例子说明为何这里求导后卷积核要翻转。

        假设我们l-1层的输出a^{l-1}是一个3x3矩阵,第l层的卷积核W^l是一个2x2矩阵,采用步幅大小为1,则输出z^{l}是一个2x2的矩阵。简化起见偏置项b^l设为0,则前向传播表达式为

a^{l-1}*W^l = z^{l}

        列出a,W,z的矩阵表达式如下:

\left( \begin{array}{ccc} a_{11}&a_{12}&a_{13} \\ a_{21}&a_{22}&a_{23}\\ a_{31}&a_{32}&a_{33} \end{array} \right) * \left( \begin{array}{ccc} w_{11}&w_{12}\\ w_{21}&w_{22} \end{array} \right) = \left( \begin{array}{ccc} z_{11}&z_{12}\\ z_{21}&z_{22} \end{array} \right)

        利用第二节中提到的卷积的运算规则,可以得矩阵出卷积运算后的表达式:

z_{11} = a_{11}w_{11} + a_{12}w_{12} + a_{21}w_{21} + a_{22}w_{22}

z_{12} = a_{12}w_{11} + a_{13}w_{12} + a_{22}w_{21} + a_{23}w_{22}

z_{21} = a_{21}w_{11} + a_{22}w_{12} + a_{31}w_{21} + a_{32}w_{22}

z_{22} = a_{22}w_{11} + a_{23}w_{12} + a_{32}w_{21} + a_{33}w_{22}

        接着我们使用上篇文章中推导的反向求导公式:

\nabla a^{l-1} = \frac{\partial J(W,b)}{\partial a^{l-1}} = ( \frac{\partial z^{l}}{\partial a^{l-1}})^T\frac{\partial J(W,b)}{\partial z^{l}} =(\frac{\partial z^{l}}{\partial a^{l-1}})^T \delta^{l}

        从上式可以看出,对于a^{l-1}的梯度误差\nabla a^{l-1},等于第l层的反向传播梯度误差\delta_{nm}^l乘以偏导数的转置\left ( \frac{\partial z^{l}}{\partial a^{l-1}} \right )^{T},而偏导数\frac{\partial z^{l}}{\partial a^{l-1}}对应上面卷积计算矩阵表达式,例如:

\frac{\partial z_{11}^{l}}{\partial a_{11}^{l-1}} = (a_{11}w_{11} + a_{12}w_{12} + a_{21}w_{21} + a_{22}w_{22})^{'}=w_{11}

        所以对于\frac{\partial z^{l}}{\partial a^{l-1}},则是对z_{nm}的四个分量分别计算偏导,再求和。假设我们的z_{2\times 2}矩阵对应的反向传播梯度误差是\delta_{11}, \delta_{12}, \delta_{21}\delta_{22}组成的2x2矩阵\delta_{2\times 2},则利用上述梯度求导表达式和关于z_{nm}的4个等式,可以分别计算出\nabla a^{l-1}的9个标量的梯度。

        比如对于a_{11}的梯度,由于在4个等式中a_{11}只和z_{11}有乘积关系,从而我们有:

\nabla a_{11} = \delta_{11}w_{11}

        对于a_{12}的梯度,由于在4个等式中a_{12}z_{11}z_{12}都有乘积关系,从而我们有:

\nabla a_{12} = \delta_{11}w_{12} + \delta_{12}w_{11}

        同样的道理我们可以得到:

\nabla a_{13} = \delta_{12}w_{12}

\nabla a_{21} = \delta_{11}w_{21} + \delta_{21}w_{11}

\nabla a_{22} = \delta_{11}w_{22} + \delta_{12}w_{21} + \delta_{21}w_{12} + \delta_{22}w_{11}

\nabla a_{23} = \delta_{12}w_{22} + \delta_{22}w_{12}

\nabla a_{31} = \delta_{21}w_{21}

\nabla a_{32} = \delta_{21}w_{22} + \delta_{22}w_{21}

\nabla a_{33} = \delta_{22}w_{22}

  

        这上面9个式子每次都单独计算十分麻烦,如果是4x4矩阵则要进行16次求导,所以效率很低。其实有更便捷的计算方法,可以用两个矩阵进行卷积运算的形式表示出来。我们构建两个矩阵,分别是上一层反向传播来的误差矩阵\delta_{nm}和卷积核矩阵W。为了使表达式符合卷积的计算形式,我们在反向传播梯度误差矩阵\delta_{2\times 2}周围填充了一圈0,将其变为\delta_{4\times 4},然后将卷积核W进行180度翻转后变成rot180(W),将其与反向传播梯度误差矩阵\delta_{4\times 4}进行卷积运算即:

\begin{pmatrix} 0&0&0&0 \\ 0&\delta_{11}& \delta_{12}&0 \\ 0&\delta_{21}&\delta_{22}&0 \\ 0&0&0&0 \end{pmatrix} * \left( \begin{array}{ccc} w_{22}&w_{21}\\ w_{12}&w_{11} \end{array} \right) = \left( \begin{array}{ccc} \nabla a_{11}&\nabla a_{12}&\nabla a_{13} \\ \nabla a_{21}&\nabla a_{22}&\nabla a_{23}\\ \nabla a_{31}&\nabla a_{32}&\nabla a_{33} \end{array} \right)

        如上所述将\delta_{4\times 4}rot180(W)_{2\times 2}的矩阵进行卷积运算,得到了一个\nabla a_{3\times 3}的矩阵(此处卷积运算规则不再细说,自行查阅)。很容易看出\triangledown a_{3\times 3}矩阵中每个元素对应的结果就是a在每个分量上的求导关系表达式。示例如下:

\nabla a_{11} = 0\cdot w_{22}+0\cdot w_{21}+0\cdot w_{12} +\delta_{11}w_{11}

        所以将卷积核翻转180度,再与反向传播误差矩阵补0后进行卷积运算,就可以快速得到向下一层进行反向传播的新误差矩阵。这个例子直观的介绍了为什么对含有卷积的式子反向传播时,卷积核要翻转180度的原因,与其说是卷积层反向传播的算法规则,不如说是一种计算技巧。

        以上就是卷积层的误差反向传播过程。

5. 已知卷积层的\delta^l,推导该层的Wb的梯度    

        上面分别讲了池化层的误差传播,卷积层的误差传播,而全连接层的误差传播与普通的DNN模型一样,只是对激活函数求导即可,这里不再重复讲解。一个简单的卷积网络的误差传递原理我们基本都清楚了。误差传播本质是为了对模型进行修正,误差\delta^l能在网络中顺利传播,我们就可以根据每个周期误差的变化,来更新参数 W,b ,从而使得每个周期反向传播的误差\delta越来越小。

        对于全连接层来说,可以按照普通DNN网络的反向传播算法求该层权重参数W、偏置b的梯度;而池化层并没有W,b,所以不用求W,b的梯度;因此这里只讲解如何推导卷积层的W,b梯度。

        由之前的内容可知,卷积层zW,b的关系为:

z^l = a^{l-1}*W^l +b

        因此对卷积层权重参数W求偏导有:

\frac{\partial J(W,b)}{\partial W^{l}}=a^{l-1} *\delta^l

        可以发现,这个求导表达式中的卷积核没有进行翻转,是因为此时的求导是层内求导,而不是像误差反向传播到上一层时求导,具体过程分析如下。

        和第4节一样的一个简化的例子,这里输入是矩阵,不是张量,那么对于第l层,某个卷积核矩阵W的导数可以表示如下:

\frac{\partial J(W,b)}{\partial W_{pq}^{l}} = \sum\limits_i\sum\limits_j(\delta_{ij}^la_{i+p-1,j+q-1}^{l-1})

        假设我们输入a是3x3的矩阵,卷积核W是2x2的矩阵,输出z是2x2的矩阵,那么反向传播的z的梯度误差\delta也是2x2的矩阵。

        那么根据上面的式子,我们有:

\frac{\partial J(W,b)}{\partial W_{11}^{l}} = a_{11}\delta_{11} + a_{12}\delta_{12} + a_{21}\delta_{21} + a_{22}\delta_{22}

\frac{\partial J(W,b)}{\partial W_{12}^{l}} = a_{12}\delta_{11} + a_{13}\delta_{12} + a_{22}\delta_{21} + a_{23}\delta_{22}

\frac{\partial J(W,b)}{\partial W_{13}^{l}} = a_{13}\delta_{11} + a_{14}\delta_{12} + a_{23}\delta_{21} + a_{24}\delta_{22}

\frac{\partial J(W,b)}{\partial W_{21}^{l}} = a_{21}\delta_{11} + a_{22}\delta_{12} + a_{31}\delta_{21} + a_{32}\delta_{22}

        最终我们可以一共得到4个式子。整理成矩阵形式后可得:

\frac{\partial J(W,b)}{\partial W^{l}} =\begin{pmatrix} a_{11} & a_{12} & a_{13} \\ a_{21} & a_{22} & a_{23} \\ a_{31} & a_{32} & a_{33} \\ \end{pmatrix}* \left( \begin{array}{ccc} \delta_{11}& \delta_{12} \\ \delta_{21}&\delta_{22} \end{array} \right)

        此时我们可以清楚的看到为什么这次没有对卷积核进行反转的原因。

        而对于b,则稍微有些特殊,因为\delta^l是高维张量,而b只是一个向量,不能像DNN那样直接和\delta^l相乘。通常的做法是将\delta^l的各个子矩阵的项分别求和,得到一个误差向量,即为b的梯度:

\frac{\partial J(W,b)}{\partial b^{l}} = \sum\limits_{u,v}(\delta^l)_{u,v}

6. CNN反向传播算法完整流程

        现在我们总结下CNN的反向传播算法,以最基本的批量梯度下降法为例来描述反向传播算法。

        输入:m个图片样本,CNN模型的层数L和其他隐藏层的类型。对于卷积层,要定义卷积核大小K,卷积核通道数F,填充大小P,步幅S。对于池化层,要定义池化区域大小k和池化标准(MAX或Average)。对于全连接层,要定义全连接层的激活函数(输出层除外)和各层的神经元个数。梯度迭代参数迭代步长\alpha,最大迭代次数MAX与停止迭代阈值\epsilon

        输出:CNN模型各隐藏层与输出层的权重参数W和偏置b

        1) 初始化各隐藏层与输出层的各W,b的值为一个随机数。

        2)for iter to 1 to MAX:

        2-1) for i =1 to m:

                a) 将CNN输入a^1设置为x_i对应的张量

                b) for l=2 to L-1,根据下面3种情况进行前向传播算法计算:

                        b-1) 如果当前是全连接层:则有a^{i,l} = \sigma(z^{i,l}) = \sigma(W^la^{i,l-1} + b^{l})

                        b-2) 如果当前是卷积层:则有a^{i,l} = \sigma(z^{i,l}) = \sigma(W^l*a^{i,l-1} + b^{l})

                        b-3) 如果当前是池化层:则有a^{i,l}= pool(a^{i,l-1}), 这里的pool指按照池化区域大小k和池化标准将输入张量缩小的过程。

                c) 对于输出层第L层: a^{i,L}= softmax(z^{i,L}) = softmax(W^{L}a^{i,L-1} +b^{L}),通过损失函数计算输出层的\delta^{i,L}

                d) for l= L-1 to 2, 根据下面3种情况进行进行反向传播算法计算:

                        d-1)  如果当前是全连接层:\delta^{i,l} = (W^{l+1})^T\delta^{i,l+1}\odot \sigma^{'}(z^{i,l})

                        d-2) 如果当前是卷积层:\delta^{i,l} = \delta^{i,l+1}*rot180(W^{l+1}) \odot \sigma^{'}(z^{i,l})

                        d-3) 如果当前是池化层:\delta^{i,l} = upsample(\delta^{i,l+1}) \odot \sigma^{'}(z^{i,l})

        2-2) for l = 2 to L,根据下面2种情况更新第l层的W^l,b^l

                2-2-1) 如果是全连接层:W^l = W^l -\alpha \sum\limits_{i=1}^m \delta^{i,l}(a^{i, l-1})^T, b^l = b^l -\alpha \sum\limits_{i=1}^m \delta^{i,l}

                2-2-2) 如果是卷积层,对于每一个卷积核有:W^l = W^l -\alpha \sum\limits_{i=1}^m a^{i, l-1}*\delta^{i,l}, b^l = b^l -\alpha \sum\limits_{i=1}^m \sum\limits_{u,v}(\delta^{i,l})_{u,v}

                2-3) 如果所有Wb的变化值都小于停止迭代阈值\epsilon,则跳出迭代循环到步骤3。

        3) 输出各隐藏层与输出层的线性关系系数矩阵W和偏置向量b

五、总结

         到此卷积神经网络的核心内容就讲完了,本文省略了很多前置基础知识,重点放在了推导卷积层和池化层的前向传播、反向误差传播和求解模型参数梯度上。前置的基础知识可以参考上一篇文章。下一篇文章将根据本文的数学推导过程,对卷积网络模型进行编码实现。

        小知识1:

        关于卷积的概念,最初来自于信号系统。刚开始学习卷积网络的时候,查的大多都是从信号系统角度对卷积计算的讲解,每次和卷积网络结合理解的时候总是一头雾水。后来才知道其实神经网络中的卷积和信号系统中的卷积并不是一回事。CNN网络其实使用的是一种互相关运算。

         在信号系统中常见的一阶和二阶卷积表达式如下:

S(x)=\int_{-\infty }^{+\infty }f(\tau )g(x-\tau)dx

Conv_{i,j}=\sum_{m=0}^{M-1}\sum_{n=0}^{N-1}w_{m,n}x_{i-m,j-n}

         对比一下CNN网络的互相关运算过程如下:

z_{d,i,j}=\sum_{ch=0}^{CH-1}\sum_{r=0}^{R-1}\sum_{c=0}^{C-1}w_{d,ch,r,c}x_{ch,i+r,j+c}+b_d

        输入数据与卷积核经过互相关运算后,在输出矩阵某个输出深度d上,位于第 i 行 j 列的元素,其中ch为输入通道(Channel),R和C分别为过滤器在高度和宽度上的尺寸,如果不考虑输入通道channel 这个维度跟输出深度,再拿掉偏置项 , 可以简化为:

CrossCorr_{i,j}=\sum_{r=0}^{R-1}\sum_{c=0}^{C-1}w_{r,c}x_{i+r,j+c}

        现在比较一下原始卷积(Convolution)和互相关(Cross-correlation)表达式,发现仅仅是运算符号有差异,使得输入数据 x,与卷积核权值矩阵 w 做点积运算时,x 数据块(block)中元素,与中的元素是逆序对应的,如果把 w 旋转180°作为卷积核,卷积运算就成了互相关运算。

        小知识2:

        使原始卷积运算满足结合律(associative):Fa*(Fb*G) = (Fa*Fb)*G。这在需要连续多次原始卷积的场景下非常实用:你可以先把各个卷积核做卷积运算,得到新的叠加卷积核,再与输入数据做一次卷积得到结果;如果这些卷积核的权值参数都是已知常数,则可以更进一步:预先计算叠加卷积核并存储起来,成倍提高运算效率。

        互相关运算不满足结合律,目前成熟的CNN模型,也不需要做多个过滤器直接叠加的互相关运算;互相关运算另有效果拔群的优化方法,实现部分会做具体介绍。CNN模型训练的时候,参数不是预先固定的常量,而是通过反向传播误差损失,迭代优化得到的,所以前向传播时卷积核w无论翻转与否,对模型参数训练是等效的。

参考文献:

  1.  Neural Networks and Deep Learning by By Michael Nielsen
  2.  Deep Learning, book by Ian Goodfellow, Yoshua Bengio, and Aaron Courville
  3.  UFLDL Tutorial
  4. CS231n Convolutional Neural Networks for Visual Recognition, Stanford
  5. 第8章 卷积神经网络 - 知乎 (zhihu.com)
  6. 卷积层(2)_输出 (sohu.com)
  7. 卷积神经网络前向传播和BP后向传播计算步骤 - 简书 (jianshu.com)