1.3 反向传播

目录

  • 三、反向传播
    • 3.1 反向传播计算过程[^1]
    • 3.2 基于梯度下降的优化方法[^3]
      • 3.2.1 SGD、学习率衰减及动量
      • 3.2.2 Adagrad、Adadelta、RMSprop
      • 3.2.3 Adam、Adamx、Nadam
    • 3.3 损失函数
      • 3.3.1 二次损失函数
      • 3.3.2 交叉熵损失函数
      • 学习资料

三、反向传播

通过前面内容的介绍,我们知道实际 NeoCognitron 与 CNN 在结构上基本一致,其根本区别在于 CNN 使用反向传播算法进行全局参数的更新学习,而整个过程中也不再需要人类精心设计特征提取模板,而是全流程自动由网络设计各层级的卷积核。

反向传播(Backpropagation)算法是一种与最优化方法相结合,可用来训练神经网络的常见方法。它最初起源于控制论并基于链式法则进行推导计算,早在20实际六七十年代,很多研究人员就已经提出了反向传播算法,直到 1974 年才被应用到神经网络中,但人们直到 1986 年在 David Rumelhart、Geoffrey Hinton 和 Ronald Williams 的工作1中,证明反向传播算法的训练速度更快,使得之前的一些无法计算的工作变得可能,反向传播算法才得到大家的认可。

反向传播算法可以递归的求解网络中所有参数对损失函数的梯度,而梯度值会利用如梯度下降等优化方法进行更新,以最小化损失函数。反向传播的本质是网络参数值的梯度计算以及更新过程。因其计算过程是从网络最后一层往前逐层计算,因此被称为”反向传播“或者”误差反向传播“。

反向传播算法的计算过程实际就是多元函数求解微分的过程,因此要求损失函数必须是可微的。这里实际也为网络架构学习过程提供了另一个很自然的思路,如周志华提出的 gcForest2 就是一种非神经网络架构的深度学习。

在 NeoCognitron 中采用的是一种自组织学习机制,其中的隐藏层并不是我们目前所说的隐藏层,它的内部许多连接是固定且不更新的。在反向传播中,为了计算的连贯性,我们需要将输入到输出的全流程通道打通,这实际上相当于我们需要依靠反向传播算法自定义每个隐层的实际含义。

网络的结构被定义为层级关系,最底层为输入层,最顶层为输出层,中间其他层为隐藏层。网络数据流从底层向高层传输,可以跨层连接(从此处可看出,残差网络的概念实际很早就存在),但不允许层内神经元间有数据交互也不允许数据流从高层向底层传递(虽然 RNN 网络中存在这样的连接,但在单个时间步内网络是严格的前馈网络)。

3.1 反向传播计算过程1

假设前层第 i i i 个神经元的输出为 y i y_i yi,与输出层第 j j j 个神经元相连,连接权重为 w j i w_{ji} wji(含偏置项)。则输出层第 j j j 个神经元的总输入为:

(1) x j = ∑ i y i w j i x_j = \sum_i y_i w_{ji} \tag{1} xj=iyiwji(1)

该神经元的输出激活函数采用的是非线性函数(如果采用线性函数,则整体网络依然是是线性函数),通常选择 sigmoid 激活函数,则输出层第 j j j 个神经元的输出为:

(2) y j = f ( x j ) = 1 1 + e − x j y_j=f(x_j)=\frac{1}{1+e^{-x_j}}\tag{2} yj=f(xj)=1+exj1(2)

sigmoid 激活函数在求偏导时有非常好的性质,该函数的导数为

(3) f ′ ( x ) = f ( x ) ( 1 − f ( x ) ) f'(x) = f(x)(1-f(x)) \tag{3} f(x)=f(x)(1f(x))(3)

网络训练的目标是寻找一系列参数值使得网络对特定数据向量可以产生与期望输出非常接近的结果。为了评估这种近似性,我们需要损失函数来定义这种差距,例如最常见的损失函数为二次损失函数:

(4) E = 1 2 ∑ c ∑ j ( y j , c − d j , c ) 2 E=\frac{1}{2}\sum_c\sum_j (y_{j,c}-d_{j,c})^2\tag{4} E=21cj(yj,cdj,c)2(4)

这里的 c c c 是第 c c c 个样本(输入-输出对), y y y 是网络的输出值, d d d 是期望值(desired value),即整体损失是基于输出层所有神经元在所有样本上的损失总和。

通过梯度下降算法最小化 E E E。我们需要对每一个权重求偏微分值。首先数据从输入层到输出层逐层前向计算,在输出层与期望值对比就算误差,然后将误差反向逐层传递到输出层。

首先计算 E E E 对输出层每个神经元输出值的偏微分:

(5) ∂ E ∂ y j = y j − d j \frac{\partial E}{\partial y_j}=y_j - d_j \tag{5} yjE=yjdj(5)

接下来,我们可以应用链式法则求解:

(6) ∂ E ∂ x j = ∂ E ∂ y j ⋅ ∂ y j ∂ x j = ( y j − d j ) ⋅ f ′ ( x j ) = ( y j − d j ) ⋅ y j ( 1 − y j ) \frac{\partial E}{\partial x_j}=\frac{\partial E}{\partial y_j} \cdot \frac{\partial y_j}{\partial x_j}=(y_j-d_j) \cdot f'(x_j)= (y_j-d_j) \cdot y_j(1-y_j) \tag{6} xjE=yjExjyj=(yjdj)f(xj)=(yjdj)yj(1yj)(6)

同样对于权重的计算也很简单:

(7) ∂ E ∂ w j i = ∂ E ∂ x j ⋅ ∂ x j ∂ w j i = ( y j − d j ) ⋅ f ′ ( x j ) ⋅ y i = ( y j − d j ) ⋅ y j ( 1 − y j ) ⋅ y i \frac{\partial E}{\partial w_{ji}}=\frac{\partial E}{\partial x_j} \cdot \frac{\partial x_j}{\partial w_{ji}}=(y_j-d_j) \cdot f'(x_j) \cdot y_i=(y_j-d_j) \cdot y_j(1-y_j) \cdot y_i \tag{7} wjiE=xjEwjixj=(yjdj)f(xj)yi=(yjdj)yj(1yj)yi(7)

对于前层第 i i i 个神经元输出值 y i y_i yi 的偏导计算为:

(8) ∂ E ∂ y i = ∂ E ∂ x j ⋅ ∂ x j ∂ y i = ( y j − d j ) ⋅ f ′ ( x j ) ⋅ w j i = ( y j − d j ) ⋅ y j ( 1 − y j ) ⋅ w j i \frac{\partial E}{\partial y_{i}}=\frac{\partial E}{\partial x_j} \cdot \frac{\partial x_j}{\partial y_{i}}=(y_j-d_j) \cdot f'(x_j) \cdot w_{ji}=(y_j-d_j) \cdot y_j(1-y_j) \cdot w_{ji} \tag{8} yiE=xjEyixj=(yjdj)f(xj)wji=(yjdj)yj(1yj)wji(8)

因此,当我们给定最后一层所有神经元上的 ∂ E ∂ y L \frac{\partial E}{\partial y^L} yLE ,我们就可以求解倒数第二层所有神经元上的 ∂ E ∂ y L − 1 \frac{\partial E}{\partial y^{L-1}} yL1E。如此递归计算,我们就可以对整个网络的所有参数进行梯度计算。因此有了损失值对参数的梯度计算 ∂ E ∂ w \frac{\partial E}{\partial w} wE,我们就可以利用梯度下降等优化方法更新参数,下面介绍几种常用的优化方法。

3.2 基于梯度下降的优化方法3

3.2.1 SGD、学习率衰减及动量

我们目前所说的随机梯度下降,更多的时候是指小批量梯度下降,其参数更新公式为:

(9) θ t = θ t − 1 − η ⋅ ∇ θ J ( θ ) \theta_t = \theta_{t-1} - \eta \cdot \nabla_\theta J(\theta) \tag{9} θt=θt1ηθJ(θ)(9)

其中 θ \theta θ 是网络参数, η \eta η 是学习率。 ∇ θ \nabla_\theta θ 是参数的梯度值,即通过上文反向传播算法求解到的。小批量梯度下降是指,在对小批量内所有样本计算得到总损失后执行反向传播更新一次参数值。即梯度值的计算是基于小批量内所有样本计算的。

随机梯度下降算法通常速度比较快,然而却并不保证较好的收敛性,尤其是随着网络参数的增多、复杂度的提高,其损失函数是高度非凸的,存在大量的极小值点及鞍点等,给优化算法带来很大的挑战。另外学习率是一个非常重要的超参数,选择合适的学习率是非常困难的,如果学习率过大,则可能造成不收敛,参数波动不稳定,如果学习率过小,则会降低学习速率。在默认的 SGD 算法下,学习率固定不变,即衰减系数为 0。当衰减系数大于 0 ,在每次参数更新后,学习率衰减为:

(10) η = η ⋅ 1 1 + d ⋅ T \eta = \eta \cdot \frac{1}{1+d \cdot T} \tag{10} η=η1+dT1(10)

这里的 d d d 为衰减系数( d ≥ 0 d \geq 0 d0 ), T T T 为参数更新次数,初始为 0。

动量项是通过累计梯度方法,将当前梯度梯度用于修改点在权重空间中的速度而不是其位置。事实证明,这种方法可以加速学习速度,加入动量项的梯度下降公式为:

(11) v t = γ v t − 1 − η ⋅ ∇ θ J ( θ ) v_t = \gamma v_{t-1} - \eta \cdot \nabla_\theta J( \theta) \tag{11} vt=γvt1ηθJ(θ)(11)

(12) θ t = θ t − 1 + v t \theta_t = \theta_{t-1} + v_t \tag{12} θt=θt1+vt(12)

其中, γ \gamma γ 是动量项。

Nesterov 动量首先在之前累计的梯度上跃进一大步,然后在跃进点处计算其梯度并对之进行修正,计算公式为:

(13) v t = γ v t − 1 − η ∇ θ J ( θ + γ v t − 1 ) v_t = \gamma v_{t-1} - \eta \nabla_\theta J( \theta+\gamma v_{t-1})\tag{13} vt=γvt1ηθJ(θ+γvt1)(13)

(14) θ t = θ t − 1 + v t \theta_t = \theta_{t-1} + v_t \tag{14} θt=θt1+vt(14)

3.2.2 Adagrad、Adadelta、RMSprop

Adagrad 是一种具有特定参数学习率的优化方法,在随机梯度下降算法中,所有的参数具有同样大小的学习率,但是在 Adagrad 方法中,每一个参数拥有自己特定的学习率。在训练期间,更新频率更频繁的参数其学习率越小,反之越大。其更新公式为:

(15) G t = G t − 1 + g 2 G_t = G_{t-1} + g^2 \tag{15} Gt=Gt1+g2(15)

(16) θ t = θ t − 1 − η ∗ g / ( G t + ϵ ) \theta_t = \theta_{t-1} - \eta * g / (\sqrt{G_t} + \epsilon) \tag{16} θt=θt1ηg/(Gt +ϵ)(16)

其中 G G G 是对角矩阵包含每个参数的历史梯度的平方和, g g g 为梯度值, ϵ \epsilon ϵ 为平滑项,防止除数为 0.

Adagrad 的主要优点是可以自适应学习率,但是由于参数更新过程中,存在 G t \sqrt{G_t} Gt ,参数梯度累加值是非减的,这会导致分母越来越大,而整体更新量则趋近于 0.

Adadelta 是 Adagrad 的一种改进,主要解决了学习率单调递减的问题,该算法不需要手动选择全局学习率。其更新方法中首次引入了参数平方的更新,而不仅仅是累加梯度平方的更新。具体更新公式如下:

(17) E [ g 2 ] t = ρ ∗ E [ g 2 ] t − 1 + ( 1 − ρ ) ∗ g 2 E[g^2]_t = \rho * E[g^2]_{t-1} + (1 - \rho) * g^2 \tag{17} E[g2]t=ρE[g2]t1+(1ρ)g2(17)

(18) Δ θ t = − R M S [ Δ θ ] t − 1 ∗ g t / R M S [ g ] t \Delta \theta_t = -RMS[\Delta \theta]_{t-1} * g_t / RMS[g]_t \tag{18} Δθt=RMS[Δθ]t1gt/RMS[g]t(18)

(19) E [ Δ θ 2 ] t = ρ ∗ E [ Δ θ 2 ] t − 1 + ( 1 − ρ ) ∗ Δ θ t 2 E[\Delta \theta^2]_t = \rho * E[\Delta \theta^2]_{t-1} + (1 - \rho) * \Delta \theta_t^2\tag{19} E[Δθ2]t=ρE[Δθ2]t1+(1ρ)Δθt2(19)

(20) θ t = θ t − 1 + Δ θ t \theta_t = \theta_{t-1} + \Delta \theta_{t} \tag{20} θt=θt1+Δθt(20)

这里的 ρ \rho ρ 是衰减率,类似于动量项。 Adadelta 是 Adagrad 的一个更强大的扩展,它基于梯度更新的移动窗口调整学习率,而不是积累所有历史的梯度。这样,即使在多轮参数更新后,Adadelta 也会继续学习。与 Adagrad 相比,在 Adadata 的原始版本中,我们不必设置初始学习率。

RMSprop 是 Hinton 在他教授的 CSC321 课程中提出的一种新的优化方法。参数更新公式为:

(21) E [ g 2 ] t = ρ ∗ E [ g 2 ] t − 1 + ( 1 − ρ ) g t 2 E[g^2]_t = \rho * E[g^2]_{t-1} + (1-\rho)g^2_t \tag{21} E[g2]t=ρE[g2]t1+(1ρ)gt2(21)

(22) θ t = θ t − 1 − η ∗ g / ( E [ g 2 ] t + ϵ ) \theta_t = \theta_{t-1} - \eta * g / (\sqrt{E[g^2]_t} + \epsilon) \tag{22} θt=θt1ηg/(E[g2]t +ϵ)(22)

3.2.3 Adam、Adamx、Nadam

Adam 是基于低阶自适应矩估计的随机目标函数一阶梯度优化算法。该方法易于实现,计算效率高,内存需求小,对渐变的对角线重新缩放不变,非常适合于数据参数较大的问题。该方法也适用于非平稳目标和具有非常嘈杂或稀疏梯度的问题。超参数具有直观的解释,通常不需要调整。参数更新公式为:

(23) η t = η ∗ 1 − β 2 t / ( 1 − β 1 t ) \eta_t = \eta * \sqrt{1 - \beta_2^t}/(1 - \beta_1^t) \tag{23} ηt=η1β2t /(1β1t)(23)

(24) m t = β 1 m t − 1 + ( 1 − β 1 ) g t m_t = \beta_1 m_{t-1} + (1 - \beta_1)g_t \tag{24} mt=β1mt1+(1β1)gt(24)

(25) v t = β 2 ∗ v t − 1 + ( 1 − β 2 ) g t 2 v_t = \beta_2 * v_{t-1} + (1 - \beta_2)g^2_t \tag{25} vt=β2vt1+(1β2)gt2(25)

(26) θ t + 1 = θ t − η t ∗ m t / ( v t + ϵ ) \theta_{t+1}= \theta_t - \eta_t * m_t / (\sqrt{v_t} + \epsilon) \tag{26} θt+1=θtηtmt/(vt +ϵ)(26)

Adamax 是 Adam 的变体,有时性能会超越 Adam,尤其是在嵌入模型中,其更新公式为:

(27) m t = β 1 ∗ m t − 1 + ( 1 − β 1 ) ∗ g t m_t = \beta_1 * m_{t-1} + (1 - \beta_1) * g_t \tag{27} mt=β1mt1+(1β1)gt(27)

(28) v t = max ⁡ ( β 2 ∗ v t − 1 , ∣ g t ∣ ) ) v_t = \max(\beta_2 * v_{t-1}, |g_t|)) \tag{28} vt=max(β2vt1,gt))(28)

(29) θ t + 1 = θ t − η / ( 1 − β 1 t ) ∗ m t / ( v t + ϵ ) \theta_{t+1} = \theta_t - \eta / (1 - \beta_1^t) * m_t / (v_t + \epsilon) \tag{29} θt+1=θtη/(1β1t)mt/(vt+ϵ)(29)

Nadam 更像是有动量的 RMSprop,它是具有 Nesterov 动量的 Adam。其更新公式为:

(30) μ t = β 1 ∗ ( 1 − 0.5 ∗ 0.9 6 0.004 ∗ t ) \mu_t = \beta_1 * (1 - 0.5 * 0.96^{0.004 * t}) \tag{30} μt=β1(10.50.960.004t)(30)

(31) g ′ = g / ( 1 − ∏ i = 1 t μ i ) g' = g / (1 - \prod_{i=1}^{t}{\mu_i}) \tag{31} g=g/(1i=1tμi)(31)

(32) m t = β 1 ∗ m t − 1 + ( 1 − β 1 ) ∗ g m_t = \beta_1 * m_{t-1} + (1 - \beta_1) * g \tag{32} mt=β1mt1+(1β1)g(32)

(33) m ′ = m t / ( 1 − ∏ i = 1 t + 1 μ i ) m' = m_t / (1 - \prod_{i=1}^{t+1}{\mu_i}) \tag{33} m=mt/(1i=1t+1μi)(33)

(34) v t = β 2 ∗ v t − 1 + ( 1 − β 2 ) ∗ g ∗ g v_t = \beta_2 * v_{t-1} + (1 - \beta_2) * g * g \tag{34} vt=β2vt1+(1β2)gg(34)

(35) v ′ = v t / ( 1 − β 2 t ) v' = v_t / (1 - \beta_2^t) \tag{35} v=vt/(1β2t)(35)

(36) m ˉ = ( 1 − μ t ) ∗ g ′ + μ t + 1 ∗ m ′ \bar{m} = (1 - \mu_t) * g' + \mu_{t+1} * m' \tag{36} mˉ=(1μt)g+μt+1m(36)

(37) θ t = θ t − 1 − l r ∗ m ˉ / ( v ′ + ϵ ) \theta_t = \theta_{t-1} - lr * \bar{m} / (\sqrt{v'} + \epsilon)\tag{37} θt=θt1lrmˉ/(v +ϵ)(37)

对各类优化学习算法的详细解释,超出本系列教程的讲解范围,我们会在学习资料中给出部分参考资料,方便大家对各种优化算法算法有更深刻的认识,以便在使用过程中可以灵活选择。

3.3 损失函数

3.3.1 二次损失函数

在前文中我们介绍过,损失函数的目的是评估模型预测值与样本期望输出值之间的差距。因此,最直观的损失函数是 0-1 损失函数。

(38) L ( Y , f ( X ) ) = { 1 , Y ≠ f ( X ) 0 , Y = f ( X ) L(Y, f(X))= \begin{cases} 1, & Y \neq f(X) \\ 0, & Y=f(X) \end{cases} \tag{38} L(Y,f(X))={1,0,Y̸=f(X)Y=f(X)(38)

然而,该损失函数与网络模型参数无关,是不可微分函数,因此无法用于反向传播算法中训练网络。

而在上文中,我们给出的是二次损失函数,又称为均方误差损失函数。然而,在早期的神经网络中,非线性激活函数通常选择 sigmoid 函数,该函数及导数图像如下所示:

【图 1】

我们可以看到,当函数输入值趋近于 0 或趋近于 1 时,函数曲线非常平缓,其导数接近于 0。通过公式 (7) ,我们知道这将使参数更新非常缓慢。而 sigmoid 在值取 0 时具有最大梯度为 0.25 < 1 0.25 < 1 0.25<1。当反向传播逐层向前传递误差,会积累多项 f ′ ( x j ) f'(x_j) f(xj)。则前面几层的参数更新量几乎为 0。这就是臭名昭著的梯度消失问题。曾在一段时间内,人们普通认为反向传播算法不能解决多层网络更新问题。

3.3.2 交叉熵损失函数

交叉熵源于信息论,主要用于测量两个概率分布之间的差异性信息。对于离散分布的 p p p(真实分布) 和 q q q(预测分布),其度量公式为:

(39) H ( p , q ) = − ∑ x p ( x ) log ⁡ q ( x ) H(p,q)=-\sum_x p(x)\log q(x)\tag{39} H(p,q)=xp(x)logq(x)(39)

对于二分类交叉熵损失函数,我们定义为:

(40) C = − 1 N ∑ x [ y log ⁡ f ( x ) + ( 1 − y ) log ⁡ ( 1 − f ( x ) ) ] C = -\frac{1}{N} \sum_x \left[y \log f(x) + (1-y ) \log (1-f(x)) \right] \tag{40} C=N1x[ylogf(x)+(1y)log(1f(x))](40)

它具有良好的性质,当网络输出值与期望值约接近时,其交叉熵越接近于 0。与二次函数相比,交叉熵损失函数避免了学习速率下降的问题。为了说明问题,我们看一下,当损失函数为二分类交叉熵损失函数时,其参数的梯度计算为:

(41) ∂ E ∂ w j i = ∂ E ∂ y j ⋅ ∂ y j ∂ x j ⋅ ∂ x j ∂ w j i = ( y ⋅ 1 y j + ( 1 − y ) ⋅ − 1 1 − y j ) ⋅ y j ⋅ ( 1 − y j ) ⋅ y i = ( y j − y ) ⋅ y i \frac{\partial E}{\partial w_{ji}}=\frac{\partial E}{\partial y_j} \cdot \frac{\partial y_j}{\partial x_j} \cdot \frac{\partial x_j}{\partial w_{ji}}=(y\cdot \frac{1}{y_j}+(1-y)\cdot \frac{-1}{1-y_j}) \cdot y_j \cdot (1-y_j) \cdot y_i=(y_j-y)\cdot y_i\tag{41} wjiE=yjExjyjwjixj=(yyj1+(1y)1yj1)yj(1yj)yi=(yjy)yi(41)

这是一个非常完美的公式,去除了影响梯度更新的 f ′ ( x j ) f'(x_j) f(xj) 项,其梯度更新速度直接取决于 y j y_j yj y y y 之间的误差。因此,在目前的神经网络训练中,交叉熵损失函数的选择更常见一些,二分类交叉熵损失函数主要用于求解二分类问题,当拓展到多分类任务中,输出层激活函数采用的是 Softmax,其表达式为:

(42) s o f t m a x ( x ) i = e x i ∑ j = 1 K e x j softmax(x)_i = \frac{e^{x_i}}{\sum^K_{j=1}e^{x_j}}\tag{42} softmax(x)i=j=1Kexjexi(42)

多分类问题交叉熵损失函数4为:

(43) − 1 N ∑ i = 1 N ∑ k = 1 K 1 y i ∈ K k log ⁡ p m o d e l [ y i ∈ K k ] -\frac{1}{N}\sum^N_{i=1}\sum^K_{k=1}1_{y_i \in K_k}\log p_{model}[y_i \in K_k] \tag{43} N1i=1Nk=1K1yiKklogpmodel[yiKk](43)

其中 1 y i ∈ K k 1_{y_i \in K_k} 1yiKk 是指示函数,表明第 i i i 个样本属于第 k k k 个样本。 p m o d e l [ y i ∈ K k ] p_{model}[y_i \in K_k] pmodel[yiKk] 是模型预测第 $i $ 个样本属于第 k k k 个分类的概率。

学习资料

1、http://neuralnetworksanddeeplearning.com/ 第二章及第三章内容(极力推荐)

1、An overview of gradient descent optimization algorithms 主要介绍常见的各种优化算法

2、Why Momentum Really Works 可视化讲解动量工作原理


  1. Rumelhart, David E.; Hinton, Geoffrey E.; Williams, Ronald J. Learning representations by back-propagating errors. Nature. 8 October 1986, 323 (6088): 533–536. doi:10.1038/323533a0. ↩︎ ↩︎

  2. Deep Forest ↩︎

  3. TensorFlow API ↩︎

  4. https://github.com/keras-team/keras/issues/6444 ↩︎

你可能感兴趣的:(CNN与图像分类,反向传播,梯度下降,交叉熵)