由于导师要求自己从头到尾自己实现卷积神经网络,所以最近看了关于神经网络的反向传播内容。内容大部分都是从博客和书上摘录下来。整理完后方便自己以后复习!不喜勿喷。看完真的是清爽明朗!!!知道算法后写程序就简单多啦哈哈。
梯度下降法
我们训练神经网络的目的是找到能最小化代价函数C(w,b)的权重和偏置。
假设我们要最小化某些函数,C(v)。它可以是任意的多元实值函数,v=v1,v2,…。
为了最小化 C(v),我们先想象C是一个只有两个变量v1和v2的函数。
为了更精确地描述这个问题,让我们思考一下,当我们在v1和v2方向分别移动一个很小的量,即Δv1和Δv2时,C(v)将会发生什么情况。微积分告诉我们C将会有如下变化:
ΔC≈∂v1∂CΔv1+∂v2∂CΔv2......(1)
我们要寻找一种选择Δv1和Δv2的方法使得ΔC为负。为了弄明白如何选择,需要定义Δv为v变化的向量,Δv≡(Δv1,Δv2)T,T是转置符号。
我们也定义C的梯度为偏导数的向量,(∂v1∂C,∂v2∂C)T。我们用∇C来表示梯度向量,即:
∇C≡(∂v1∂C,∂v2∂C)T
有了这些定义,(1)的表达式可以被重写为:
ΔC≈∇C⋅Δv......(2)
这个方程真正让我们兴奋的是它让我们看到了如何选取Δv 才能让ΔC为负数。假设我们选取:
Δv=−η∇C......(3)
其中的η是个很小的正数(称为学习率)。方程(2)告诉我们ΔC≈−η∇C⋅∇C=−η∥∇C∥2。由于∥∇C∥2≥0,这保证了ΔC≤0,即,如果我们按照方程(3)的规则去改变v,那么C 会一直减小,不会增加。
我们用方程(3)计算Δv,来移动位置v:
v→v′=v−η∇C
然后我们用它再次更新规则来计算下一次移动。如果我们反复持续这样做,我们将持续减小C —— 获得一个全局的最小值。
总结一下,梯度下降算法工作的方式就是重复计算梯度∇C,然后沿着相反的方向移动。
为了使梯度下降能够正确地运行,我们需要选择足够小的学习速率η使得方程(2) 能得到很好的近似。如果不这样,我们会以ΔC>0结束,这显然不好。同时,我们也不想η 太小,因为这会使Δv的变化极小,梯度下降算法就会运行得非常缓慢。在真正的实现中,η 通常是变化的,以至方程(2) 能保持很好的近似度,但算法又不会太慢。
前面已经解释了具有两个变量的函数C的梯度下降。但事实上,即使C是一个具有更多变量的函数也能很好地工作。我们假设C是一个有m个变量v1,…,vm的多元函数。那么对C 中自变量的变化Δv=(Δv1,…,Δvm)T,ΔC将会变为:
ΔC≈∇C⋅Δv这里的梯度∇C是向量
∇C≡(∂v1∂C,…,∂vm∂C)T
正如两个变量的情况,我们可以选取
Δv=−η∇C
而且ΔC的(近似)表达式(2)保证是负数。这给了我们一种方式从梯度中去取得最小值,即使C是任意的多元函数,我们也能重复运用更新规则
v→v′=v−η∇C
可以把这个更新规则看做定义梯度下降算法。这给我们提供了一种方式去通过重复改变v来找到函数C的最小值。这个规则并不总是有效的 —— 有一件事能导致错误,让我们无法从梯度下降来求得函数C的全局最小值。但在实践中,梯度下降算法通常工作地非常好,在神经网络中这是一种非常有效的方式去求代价函数的最小值,进而促进网络自身的学习。
我们怎么在神经网络中用梯度下降算法去学习呢?其思想就是利用梯度下降算法去寻找能使得代价函数代价取得最小值的权重wk和偏置bl。为了清楚这是如何工作的,我们将用权重和偏置代替变量vj。也就是说,现在“位置”变量有两个分量组成:wk和bl,而梯度向量 ∇C 则有相应的分量∂C/∂wk和∂C/∂bl。 用这些分量来写梯度下降的更新规则,我们得到:
wk→wk′bl→bl′=wk−η∂wk∂C=bl−η∂bl∂C
通过重复应用这一更新规则我们就能有望能找到代价函数的最小值。换句话说,这是一个能让神经网络学习的规则。
回顾二次代价函数。注意这个代价函数有着这样的形式C=n1∑xCx即,它是遍及每个训练样本代价Cx≡2∥y(x)−a∥2的平均值。在实践中,为了计算梯度∇C,我们需要为每个训练输入x单独地计算梯度值∇Cx,然后求平均值,∇C=n1∑x∇Cx。不幸的是,当训练输入的数量过多时会花费很多时间,这样会使学习变得相当缓慢。
有种叫做\textbf{随机梯度下降}的算法能够加速学习。其思想就是通过随机选取少量训练输入样本来计算 ∇Cx,进行估算梯度∇C。通过计算少量样本的平均值我们可以快速得到一个对于实际梯度∇C的很好的估算,这有助于加速梯度下降,进而加速学习过程。
更准确地说,随机梯度下降通过随机选取少量的m个训练输入来工作。我们将这些随机的训练输入标记为X1,X2,…,Xm,并把它们称为一个小批量数据(mini-batch)。假设样本数量m 足够大,我们期望∇CXj的平均值大致相等于整个∇CX的平均值,即,
m∑j=1m∇CXj≈n∑x∇Cx=∇C
这里的第二个求和符号是在整个训练数据上进行的。交换两边我们得到
∇C≈m1j=1∑m∇CXj
证实了我们可以通过仅仅计算随机选取的小批量数据的梯度来估算整体的梯度。
为了将其明确地和神经网络的学习联系起来,假设wk和bl表示我们神经网络中权重和偏置。随机梯度下降通过随机地选取并训练输入的小批量数据来工作,
wk→wk′=wk−mηj∑∂wk∂CXj
bl→bl′=bl−mηj∑∂bl∂CXj
其中两个求和符号是在当前小批量数据中的所有训练样本Xj上进行的。然后我们再挑选另一随机选定的小批量数据去训练。直到我们用完了所有的训练输入,这被称为完成了一个训练迭代期(epoch)。然后我们就会开始一个新的训练迭代期。
神经网络的反向传播
反向传播算法最初在 1970 年代被提及,但是人们直到 David Rumelhart、Geoffrey Hinton 和Ronald Williams 的著名的 1986 年的论文中才认识到这个算法的重要性。这篇论文描述了对一些神经网络反向传播要比传统的方法更快,这使得使用神经网络来解决之前无法完成的问题变得可行。现在,反向传播算法已经是神经网络学习的重要组成部分。
反向传播预热知识
在讨论反向传播前,我们先熟悉一下基于矩阵的算法来计算网络的输出。
我们首先给出网络中权重的清晰定义。我们使用wjkl表示从(l−1)th层的 kth个神经元到lth层的jth 个神经元的链接上的\textbf{权重}。

我们对网络的偏置和**值也会使用类似的表示。显式地,我们使用bjl表示在lth层第jth个神经元的偏置,使用ajl表示lth层第jth个神经元的**值。

有了这些表示lth层的第jth个神经元的**值ajl就和(l−1)th层的**值通过方程关联起来了:
ajl=σ(k∑wjklakl−1+bjl)......(4)
其中求和是在(l−1) th 层的所有k个神经元上进行的。为了用矩阵的形式重写这个表达式,我们对每一层l都定义一个权重矩阵wljth 在第kth 列的元素是wjkl。类似的,对每一层l,定义一个偏置向量bl。偏置向量的每个元素其实就是前面给出的bjl,每个元素对应于lth层的每个神经元。最后,我们定义**向量al,其元素是那些**值ajl。
最后我们需要引入向量化函数(如σ)来按照矩阵形式重写公式(4})。其含义就是作用函数(如σ)到向量v中的每个元素。我们使用σ(v)表示这种按元素进行的函数作用。所以,σ(v)的每个元素其实满足σ(v)j=σ(vj)。
了解了这些表示,方程(4)就可以写成下面这种简洁的向量形式:
al=σ(wlal−1+bl)
在使用上述方程计算al的过程中,我们计算了中间量zl≡wlal−1+bl。 这个量其实是非常有用的:我们称zl 为l层神经元的带权输入。zl 的每个元素是zjl=∑kwjklakl−1+bjl,其实zjl就是第l层第j 个神经元的**函数的带权输入。
反向传播算法基于常规的线性代数运算 —— 诸如向量加法,向量矩阵乘法等。但是有一个运算不大常见。特别地,假设s和t是两个同样维度的向量。那么我们使用s⊙t来表示按元素的乘积。所以s⊙t的元素就是(s⊙t)j=sjtj。这种类型的按元素乘法有时候被称为 Hadamard 乘积,或者 Schur 乘积。
反向传播的四个基本方程
反向传播其实是对权重和偏置变化影响代价函数过程的理解。最终极的含义其实就是计算偏导数∂C/∂wjkl和∂C/∂bjl。但是为了计算这些值,我们首先引入一个中间量,δjl,这个我们称为在lth层第jth个神经元上的误差。
反向传播将给出计算误差 δjl的流程,然后将其关联到计算∂C/∂wjkl和∂C/∂bjl。
我们定义l层的第jth个神经元上的误差δjl:
δjl≡∂zjl∂C
∂zjl∂C是神经元的误差的度量。我们使用δl表示关联于l层的误差向量。反向传播会提供给我们一种计算每层的δl的方法,然后将这些误差和最终我们需要的量∂C/∂wjkl和∂C/∂bjl联系起来。
输出层误差的方程,δL:每个元素的定义如下:
δjL=∂ajL∂Cσ′(zjL)......(BP1)
右式第一个项∂C/∂ajL表示代价随着jth输出**值的变化而变化的速度。假如C不太依赖一个特定的输出神经元j,那么δjL就会很小,这也是我们想要的效果。右式第二项σ′(zjL)刻画了在zjL处**函数σ变化的速度。
在 (BP1) 中的每个部分都是很好计算的。其中∂C/∂ajL依赖于代价函数的形式。如果我们使用二次函数,那么C=21∑j(yj−aj)2,则∂C/∂ajL=(aj−yj)。
方程 (BP1) 对δL来说是个按分量构成的表达式。以矩阵形式重写方程如下:
δL=∇aC⊙σ′(zL)
这里∇aC被定义成一个向量,其元素是偏导数∂C/∂ajL。 在二次代价函数时,我们有∇aC=(aL−y),所以 (BP1) 的整个矩阵形式就变成
δL=(aL−y)⊙σ′(zL)
使用下一层的误差δl+1来表示当前层的误差δl: 特别地,
δl=((wl+1)Tδl+1)⊙σ′(zl)......(BP2)
其中(wl+1)T是(l+1)th层权重矩阵wl+1 的转置。假设我们知道l+1th层的误差 δl+1。当我们应用转置的权重矩阵(wl+1)T,我们可以凭直觉地把它看作是在沿着网络反向移动误差,给了我们度量在lth层输出的误差方法。然后,我们进行 Hadamard 乘积运算⊙σ′(zl)。这会让误差通过l层的**函数反向传递回来并给出在第 l 层的带权输入的误差δ。
通过组合 (BP1) 和 (BP2),我们可以计算任何层的误差δl。首先使用 (BP1) 计算δL,然后应用方程 (BP2) 来计算δL−1,然后再次用方程 (BP2) 来计算δL−2,如此一步一步地反向传播完整个网络。
代价函数关于网络中任意偏置的改变率: 就是
∂bjl∂C=δjl......(BP3)
这其实是,误差δjl和偏导数值∂C/∂bjl完全一致。这是很好的性质,因为 (BP1) 和 (BP2) 已经告诉我们如何计算δjl。所以就可以将(BP3) 简记为
∂b∂C=δ
其中δ和偏置b都是针对同一个神经元。
代价函数关于任何一个权重的改变率:特别地,
∂wjkl∂C=akl−1δjl......(BP4)
这告诉我们如何计算偏导数∂C/∂wjkl,其中δl和 al−1这些量我们都已经知道如何计算了。方程也可以写成下面用更少下标的表示:
∂w∂C=ainδout
其中ain是输入给权重w的神经元的**值,δ out 是输出自权重w的神经元的误差。

四个基本方程的证明
我们现在证明这四个基本的方程 (BP1)–(BP4)。所有这些都是多元微积分的链式法则的推论。
从方程 (BP1) 开始,它给出了输出误差δL的表达式:
δjL=∂zjL∂C
应用链式法则,我们可以就输出**值的偏导数的形式重新表示上面的偏导数:
δjL=k∑∂akL∂C∂zjL∂akL
这里求和是在输出层的所有神经元k上运行的。当然,第k th 个神经元的输出**值akL只依赖于当k=j时第j th 个神经元的输入权重zjL。所以当 k̸=j时∂akL/∂zjL消失了。结果我们可以简化上一个方程为:
δjL=∂ajL∂C∂zjL∂ajL
又因为ajL=σ(zjL),右边的第二项可以写为σ′(zjL),方程变成:
δjL=∂ajL∂Cσ′(zjL)
这正是分量形式的 (BP1)。
下一步,我们将证明 (BP2),它给出了以下一层误差δl+1的形式表示误差 δl。为此,我们想要以δkl+1=∂C/∂zkl+1的形式重写δjl=∂C/∂zjl。我们可以用链式法则:
δjl=∂zjl∂C=k∑∂zkl+1∂C∂zjl∂zkl+1=k∑∂zjl∂zkl+1δkl+1
这里最后一行我们交换了右边的两项,并用δkl+1的定义代入。为了对最后一行的第一项求值,注意:
zkl+1=j∑wkjl+1ajl+bkl+1=j∑wkjl+1σ(zjl)+bkl+1
做微分,我们得到
∂zjl∂zkl+1=wkjl+1σ′(zjl)
把它代入 (42) 我们得到
δjl=k∑wkjl+1δkl+1σ′(zjl)
这正是以分量形式写的 (BP2)。
最后两个方程(BP3) 和 (BP4)的证明它们同样遵循链式法则,和前面两个方程的证明相似。
反向传播算法
反向传播方程给出了一种计算代价函数梯度的方法。让我们显式地用算法描述出来:
-
输入x: 为输入层设置对应的**值a1
-
前向传播: 对每个l=2,3,…,L计算相应的zl=wlal−1+bl 和al=σ(zl)
-
输出层误差 δL: 计算向量δL=∇aC⊙σ′(zL)
-
反向误差传播: 对每个l=L−1,L−2,…,2,计算δl=((wl+1)Tδl+1)⊙σ′(zl)
-
输出: 代价函数的梯度由∂wjkl∂C=akl−1δjl和∂bjl∂C=δjl得出
卷积神经网络的反向传播
由于CNN的运算过程与DNN不一样,所以CNN的反向传播过程也有所不同。
1.池化层在前向传播的时候,对输入进行了压缩,那么我们现在需要向前反向推导
δl−1,这个推导方法和DNN完全不同。
2.卷积层是通过张量卷积,或者说若干个矩阵卷积求和而得的当前层的输出,这和DNN很不相同,DNN的全连接层是直接进行矩阵乘法得到当前层的输出。这样在卷积层反向传播的时候,上一层的δl−1 递推计算方法肯定有所不同。
3.对于卷积层,由于W使用的运算是卷积,那么从推导出该层的所有卷积核的W,b的梯度方式也不同。
对于这三个问题,我们一个一个来解决。
已知池化层的δl推导上一层的δl−1,这个过程一般称为upsample。
假设池化的size为2*2,δl:
δl=[2435]
由于池化size为2*2,首先将size还原:
⎣⎢⎢⎡0000024003500000⎦⎥⎥⎤
假设是最大池化,并且之前记录的最大值的位置为左上,右下,右上,左下。那么
δl−1:
δl−1=⎣⎢⎢⎡2000004000050300⎦⎥⎥⎤
解释下为什么要这么做,在正向传播的时候,池化之前的四个最大值位置左上,右下,右上,左下,都以比例为1的系数传递到下一层。而其他位置对输出的贡献都为0,也就是说对池化输出没有影响,因此比例系数可以理解为0。所以在正向传播的过程中,最大值所在位置可以理解为通过函数f(x)=x传递到下一层,而其他位置则通过f(x)=0传递到下一层,并且把这些值相加构成下一层的输出,虽然f(x)=0并没有作用,但这样也就不难理解反向传播时,把的各个值移到最大值所在位置,而其他位置为0了。因为由f(x)=x,最大值位置的偏导数为1,而f(x)=0的偏导数为0。
如果是平均池化,那么δl−1:
δl−1=⎣⎢⎢⎡0.50.5110.50.5110.750.751.251.250.750.751.251.25⎦⎥⎥⎤
平均池化的话,池化操作的四个位置传递到下一层的作用可以等价为f(x)=x/4,所以在方向传播过程中就相当于把δl每一个位置的值乘1/4再还原回去。
所以由δl推导δl−1可以总结为:
δl−1=upsample(δl)⊙σ′(zl−1)
等式右边第一项表示上采样,第二项是**函数的导数,在池化中可以理解为常数1(因为池化过程的正向传播过程中没有**函数)。
已知卷积层的δl推导上一层的δl−1:
δl−1=δl∂zl−1∂zl=δl∗rot180(Wl)⊙σ′(zl−1)
首先由链式法则:
δl−1=∂zl∂C∂al−1∂zl⊙σ′(zl−1)
rot180(wl)代表对卷积核进行翻转180°的操作,σ′(zl−1)为**函数的导数。这里比较难理解的是为什么要对卷积核进行180°的翻转。
假设我们l−1层的输出al−1是一个3x3矩阵,第l层的卷积核W是一个2x2矩阵,采用1像素的步幅,则输出zl是一个2x2的矩阵。这里暂时不考虑偏置项b的影响。
那么可得:
al−1∗Wl=zl
即:
⎝⎛a11a21a31a12a22a32a13a23a33⎠⎞∗(w11w21w12w22)=(z11z21z12z22)
展开后得到:
z11=a11w11+a12w12+a21w21+a22w22z12=a12w11+a13w12+a22w21+a23w22z21=a21w11+a22w12+a31w21+a32w22z22=a22w11+a23w12+a32w21+a33w22
求al的梯度:
∇al−1=∂al−1∂J(W,b)=∂zl∂J(W,b)∂al−1∂zl=δl∂al−1∂zl
又由展开式可知:
a11只与z11有关,并且系数为w11,所以有:
∇a11=δ11w11
a12只与z11和z12有关,并且系数分别为w11,w12所以:
∇a12=δ11w12+δ12w11
同理可以得到:
∇a13=δ12w12∇a21=δ11w21+δ21w11∇a22=δ11w22+δ12w21+δ21w12+δ22w11∇a23=δ12w22+δ22w12∇a31=δ21w21∇a32=δ21w22+δ22w21∇a33=δ22w22
使用矩阵形式表示就是:
⎝⎜⎜⎛00000δ11δ2100δ12δ2200000⎠⎟⎟⎞∗(w22w12w21w11)=⎝⎛∇a11∇a21∇a31∇a12∇a22∇a32∇a13∇a23∇a33⎠⎞
这就解释了为什么在反向传播时需要将卷积核进行180°的翻转操作了。
已知卷积层的δl推导w,b的梯度:
全连接层中的w,b的梯度与DNN中的推导一致,池化层没有w,b参数,所以不用进行w,b 梯度的推导。
对于卷积层正向传播过程:
zl=al−1∗Wl+b
所以参数w的梯度:
∂Wl∂J(W,b)=∂zl∂J(W,b)∂Wl∂zl=al−1∗δl
注意到这里我们并没有使用翻转180°的操作。因为由之前的展开式:
z11=a11w11+a12w12+a21w21+a22w22z12=a12w11+a13w12+a22w21+a23w22z21=a21w11+a22w12+a31w21+a32w22z22=a22w11+a23w12+a32w21+a33w22
∂W11l∂J(W,b)=a11δ11+a12δ12+a21δ21+a22δ22∂W12l∂J(W,b)=a12δ11+a13δ12+a22δ21+a23δ22∂W21l∂J(W,b)=a13δ11+a14δ12+a23δ21+a24δ22∂W22l∂J(W,b)=a21δ11+a22δ12+a31δ21+a32δ22
所以w的梯度:
∂Wl∂J(W,b)=⎝⎜⎜⎛a11a21a31a41a12a22a32a42a13a23a33a43a14a24a34a44⎠⎟⎟⎞∗(δ11δ21δ12δ22)
这也就是没有进行翻转的原因。
b的梯度:
这里假设w=0,那么z=b,梯度δl是三维张量,而b只是一个向量,不能像普通网络中那样直接和δl相等。通常的做法是将误差δ的各个子矩阵的项分别求和,得到一个误差向量所以这里b的梯度就是δl 的各个通道对应位置求和:
∂bl∂J(W,b)=u,v∑(δl)u,v
得到的是一个误差向量。
总结一下CNN的反向传播过程:
1 池化层的误差反向传播:
δl−1=upsample(δl)⊙σ′(zl−1)
2 卷积层的误差反向传播:
δl−1=δl∂zl−1∂zl=δl∗rot180(Wl)⊙σ′(zl−1)
3 参数更新:
∂Wl∂J(W,b)=∂zl∂J(W,b)∂Wl∂zl=al−1∗δl
∂bl∂J(W,b)=u,v∑(δl)u,v
参考文献
[1]https://blog.****.net/u013093426/article/details/81086396 (手把手教你搭建卷积神经网络(CNN))
[2]https://www.cnblogs.com/charlotte77/p/7783261.htm (【深度学习系列】卷积神经网络详解(二)——自己手写一个卷积神经网络)
[3]https://www.cnblogs.com/pinard/p/6494810.htm (卷积神经网络(CNN)反向传播算法)
[4]https://www.jianshu.com/p/8ad58a170fd (反向传播算法推导-卷积神经)
[5]https://blog.****.net/weixin_40446651/article/details/81516944 (深层神经网络和卷积神经网络的反向传播过程推导)
[6]神经网络与深度学习中文版