如何计算神经网络梯度

作者:DY

导语:感谢作者 DY 的这篇教学,它适合有大学低年级以上有高等数学和线性代数基础的读者。理解神经网络的工作原理对设计使用神经网络解决工作中实际问题十分重要,而理解其梯度的计算和传播是关键一步。作者从回顾梯度计算需要的数学基础“链式法则”开始,围绕神经网络的若干关键步骤讲解其中梯度的计算方法,并给出合适的范例帮助学习,既保留了必需的细节,也把握梯度计算的主题,避免让读者陷入无谓繁复的计算中。遵循作者的思路来理解神经网络的梯度计算方法较为清晰直接,同时作者也在文中引入了张量链式法则的计算方法,这也是现在流行的深度学习软件包的通用实现方法。在阅读理解作者文章的同时,也推荐读者按照作者文中留下的深入阅读的链接探索学习相关知识。 –PHunter

0. 前言

本文将以实例推导神经网络的梯度计算(back propagation算法),计算神经网络梯度需要高等数学、线性代数等基础知识。本文后面有很多公式,但是大家不要怕,公式所用到的知识都是基本的线性代数(矩阵乘法,向量内积)和高等数学(常见函数求导)知识。大家可以用笔在纸上按照思路推导一下本文的公式,相信能够对神经网络有不同的理解。本文有几个重要概念,梯度(参考Wolfram MathWorld对Gradient的定义)、神经网络(神经网络相关细节如,激活函数、输出、损失函数等可以参考Deep Learning第六章,Deep Learning第6.5节讨论了back propagation算法,6.5.6小节讲述了通用的back propagation算法。梯度仅仅是神经网络优化的一部分,关于神经网络或深度模型优化方面的知识可以参考Deep Learning第八章)、Jacobian矩阵(参考维基百科的定义Jacobian Matrix)、向量导数(DERIVATIVES AS MATRICES; CHAIN RULE)、张量内积(参考Numpy文档numpy.tensordot())

1. 链式法则求偏导数

1.1 偏导数与梯度

在机器学习中有一个非常重要的步骤就是求梯度(Gradient),求函数梯度的目的是寻找当前点往极值点去的“最快”的方向,基于梯度的优化方法就是沿着当前“最优”方向(求极小值是梯度的反方向,求极大值是梯度方向)前进直到梯度为0。

梯度是特定方向的方向导数,是“变化最大的方向”。对于一元函数来说就是导数,对于多元函数来说就是每个分量的偏导数组合而成的向量。\(\nabla _x f(x) = [\frac{\partial f}{\partial {x _0}}, \frac{\partial f}{\partial {x _1}}, …, \frac{\partial f}{\partial {x _{n-1}}}]^T, A^{\rm T}表示A的转置\),每一个维度的偏导数按照原来位置组成。参考:梯度和方向导数的关系。例如一个简单的函数 \(f(x) = x^2\),这个函数对\(x\)导数为:
$$\frac{dx^2}{dx} = 2x$$
上式为一元函数的导数,通常在机器学习中需要优化参数都不止一个,所以需要求参数的偏导数,例如二元函数\(f(x,y)=x^2+4y+7\),该函数对\(x和y\)的偏导数分别为
$$\frac{\partial}{\partial x}(x^2+4y+7)=2x \\
\frac{\partial}{\partial y}(x^2+4y+7)=4$$
非常简单,要对一个变量求偏导数,把其他变量看作常量,按照一元函数求导即可,偏导数是其他方向不变,只有所求变量方向变化的导数。

1.2 求向量梯度

把向量每个分量的偏导数组合,即为向量的梯度。例如,线性分类器常用的函数\(f(w,x)=w^{\rm T}x+b\)求\({x}\)的梯度,这里\(w和x\)是\(n\)维向量,\(b\)是一个常量,\(w^Tx =\sum_ {i=0}^{n-1} {w _i}*{x _i}\),分别对\(x\)的每一维求导
$$\frac{\partial}{\partial x _0}{f(w,x)}=w _0 \\
\frac{\partial}{\partial x _1}{f(w,x)}=w _1 \\
… \\
\frac{\partial}{\partial x _{n-1}}{f(w,x)}=w _{n-1}$$

把\(x\)的每一维的偏导数组合成向量就称为\(x\)的梯度
$$ \begin{align}
\nabla _x f(w,x) &= \frac{\partial}{\partial x}{f(w,x)} \\ &=\left[
\begin{array}{ccc}
\frac{\partial f}{\partial x _0}&
\frac{\partial f}{\partial x _1}&
\frac{\partial f}{\partial x _2}&
…&
\frac{\partial f}{\partial x _{n-1}}
\end{array}
\right]^{\rm T} \\
&=\left[
\begin{array}{ccc}
w _0&
w _1&
w _2&
…& w _{n-1}
\end{array}
\right]^{\rm T} \\
&=w \\
&A^{\rm T}表示A的转置
\end{align}
$$

同理(\(w\)和\(x\)的内积可交换,即\(w^{\rm T}x=x^{\rm T}w\),\(b\)是常量)

$$\frac{\partial f(w,x)}{\partial w} = x, \quad
\frac{\partial f(w,x)}{\partial b} = 1
$$

1.3 常用的向量导数

$$\frac{\partial \left(u^{\rm T}v\right)}{\partial u} = v \\
\frac{\partial \left(u^{\rm T}v\right)}{\partial v} = u \\
\frac{\partial \left(x^{\rm T}Ax\right)}{\partial x} = 2Ax \\
\frac{\partial \left(x^Tx\right)}{\partial x} = 2x$$
注意1:\(\frac{\partial \left(x^{\rm T}x\right)}{\partial x}\)和\(\frac{\partial \left(u^{\rm T}v\right)}{\partial v}\)不一样,前者两个向量都是变元,后者只有一个向量是变元。\(\frac{\partial \left(x^{\rm T}x\right)}{\partial x}\)和\(\frac{\partial \left(x^{\rm T}Ax\right)}{\partial x}\)形式一致,\(x^{\rm T}x\)可以看成是\(x^{\rm T}Ix\),中间省略了单位矩阵,梯度的结果也省略了单位矩阵。

注意2:上面所有的函数都是把一个向量或者矩阵映射到一个实数值,不能够求\(Ax\)对\(x\)的梯度,这类向量对向量‘求导’结果为Jacobian矩阵,第二节推导神经网络梯度反向传播算法会用到Jacobian矩阵。

1.4 导数的链式法则(Chain Rule)


$$y=f(g(x)) \\
y=f(u) \\
u=g(x) \\
\frac{\partial y}{\partial x} = \frac{\partial y}{\partial u} * \frac{\partial u}{\partial x}$$
链式法则就是寻找变量\(x\)到最终函数值\(y\)的所有路径,累加每条路径求偏导的乘积。上面的例子,首先\(x\)经过函数\(g\)变换得到\(u\),\(u\)再经过函数\(f\)变换得到\(y\),\(y\)对\(x\)的导数为\(y\)对\(u\)乘以\(u\)对\(x\)的导数,如上图所示。

导数链式法找所有路径并累加,例如计算
$$y=f(u,v)=u^2+4v \\
u=g(x)=2x \\
v=h(x)=3x \\
\frac{\partial y}{\partial x} = \frac{\partial y}{\partial u} * \frac{\partial u}{\partial x} + \frac{\partial y}{\partial v} * \frac{\partial v}{\partial x}$$

\(x\)有两条路径到\(y\),因此需要对两条路径求导并累加,这是神经网络求梯度的基础,神经网络的参数到最终的损失函数有多条路径,路径数是“指数级”(每一层的维度相乘),不可能手动一条路径一条路径求导再累加,需要找到一定的规律快速求解。

2. 计算神经网络的梯度

本节只讨论经典神经网络连接方式(只有相邻层全连接,非激活层到激活层一一对应连接,除此之外无其他方式的连接)的梯度,不具体讨论其他不规则网络,例如间隔层之间有连接,卷积神经网络和循环神经网络,这类网络可以用2.4.3节中张量导数链式法则计算剃度,问题转化为计算张量到张量的导数。

2.1 神经网络结构

举例说明神经网络的梯度计算,定义一个三层的神经网络:第零层,输入层,维度为5;第一层,隐藏层,维度为6;第二层,输出层,维度为3。隐藏层的激活函数为 \({\rm sigmoid}\),隐藏层到输出层为线性相乘再加\({\rm softmax}\)。损失函数为交叉熵。这个三层的神经网络输入是5维特征,输出为概率分布,把输入样本分为三个类别中的一个。训练样本\(\{ \langle x _1, y _1 \rangle, \langle x _2, y _2 \rangle, …, \langle x _N, y _N \rangle \}\),\(x _i\)为5维列向量,\(y _i\)为3维的one-hot向量,对应类别的位置为1,其余位置为0。例如,\(y = [0,1,0]^{\rm T}\)表示第二类样本。

\(W1\)表示输入层到隐藏层的参数,\(b1\)表示对应的偏置量,\(W2\)表示隐藏层到输出层的参数,\(b2\)表示对应的偏置量。根据神经网络的定义\(W1\)的“形状”shape为[6, 5],\(b1\)为[6, 1],\(W2\)的shape为[3, 6],\(b2\)为[3, 1]用\(L0\)表示输入的维度,\(L1\)表示第一层的维度,\(L2\)表示第二层的维度。

2.2 神经网络前向传播计算方法

神经网络前向计算比较直观,对特定样本\(\langle x, y \rangle\),神经网络前向计算最后一层(预测结果)与损失函数的值:
$$z1 = W1*x + b1 \\
a1 = {\rm sigmoid}(z1) \\
z2 = W2 * a1 + b2 \\
\hat{y} = {\rm softmax}(z2)$$
\({\rm softmax}\)定义为
$${\hat y} _i = \frac{\exp(z2 _i)}{\sum _{j=0}^{K-1} \exp(z2 _j)}$$
\({\rm softmax}\)函数的分母是归一化因子,为所有分子项之和。\({\rm softmax}\)函数输入是一个向量,输出是向量各维度的概率分布。

2.3 损失函数定义

对于单个样本的损失函数\(J(\theta) = -\sum_ {i=0}^{K-1} {y _i}*\log({{\widehat y}_i})\),这里假设有\(K\)个类别,\(\theta\)包括神经网络所有的参数。其损失函数由真实标签\(y\)和预测标签\(\widehat y\)计算得到,这种损失函数叫做交叉熵损失(Cross Entropy Loss),除了交叉熵还有其他类别的损失函数,例如均方误差(Mean Square Loss),Ian Goodfellow和Yoshua Bengio等人的巨作Deep Learning的第六章讨论了输出(例如,\({\rm softmax}\))形式与损失函数(例如交叉熵)的关系,文中提到\({\rm softmax}\)输出适合选用交叉熵损失函数。本文以交叉熵作为损失函数为例讨论梯度计算方法。

2.4 梯度的反向传播

现在假设已知神经网络最后一层的梯度,那么如何根据最后一层网络的梯度计算上一层网络与网络参数的梯度呢?

2.4.1 求网络参数\(W2\)和\(b2\)的梯度

假设已知神经网络最后一层的梯度为\(\frac{\partial J(\theta)}{\partial z2}\),往前求\(W2\)和\(b2\)的梯度,\(W2\)和\(b2\)与\(z2\)直接相关。根据神经网络前向传播公式\(z2 = W1 * a1 + b2\),拆开b2的每一个分量
$$
z2 _0 = W2[0] * a1 + b2 _0\\
z2 _1 = W2[1] * a1 + b2 _1\\
…\\
z2 _{L2-1} = W2[L2-1] * a1 + b2 _{L2-1}
$$
其中\(W2[l]\)表示矩阵\(W2\)的第\(l\)行,\(L2\)表示向量\(z2\)的维度,从上面展开的前向传播公式可以看出,\(b2\)的每一个分量到最终的损失\(J(\theta)\)都只有一条路径,经由对应维度的\(z2\)传播到损失函数。因此
$$\begin{align}
\frac{\partial J(\theta)}{\partial b2 _l}&=\frac{\partial J(\theta)}{\partial z2 _l} * \frac{\partial z2 _l)}{\partial b2 _l}\\
&=\frac{\partial J(\theta)}{\partial z2 _l} \quad (\frac{\partial z2 _l)}{\partial b2 _l} = 1)
\end{align}$$
$$\frac{\partial J(\theta)}{\partial b2}=\frac{\partial J(\theta)}{\partial z2} $$

对\(W2\)求偏导数稍微复杂一点,把其拆分成每一行看待
$$
W2[0] * a1 + b2 _0 = z2 _0\\
W2[1] * a1 + b2 _1 = z2 _1\\
…\\
W2[L2-1] * a1 + b2 _{L2-1} = z2 _{L2-1}
$$
对其每一行求偏导数,然后再组合偏导数得到偏导数的矩阵,对\(W2\)的任意一行\(W2[t]\)只有一条路径传播到损失函数\(J(\theta)\),经由对应位置的\(z2\)传播到损失函数。因此
$$\begin{align}
\frac{\partial J(\theta)}{\partial W2[t]} &= \frac{\partial J(\theta)}{\partial z2 _t} * \frac{\partial z2 _t}{\partial W2[t]} \\
&=\frac{\partial J(\theta)}{\partial z2 _t} * a1^{\rm T} \quad (\frac{\partial z2 _t)}{\partial W2[t]} = a1^{\rm T}, W2[t]为行向量)\\
&=(\frac{\partial J(\theta)}{\partial z2 _t}) * a1^{\rm T}
\end{align}$$
最后把\(W2\)的每一行的梯度上下“拼接”组成矩阵即可得到向量相乘的形式,即
$$\frac{\partial J(\theta)}{\partial W2}=\frac{\partial J(\theta)}{\partial z2} * a1^{\rm T}$$

2.4.2 梯度的反向传播到前一层

假设已知神经网络最后一层\(z2\)的梯度\(\frac{\partial J(\theta)}{\partial z2}\),现在想通过\(z2\)的梯度计算前一层网络z1的梯度。
上一小节已经计算出离\(z2\)最近的神经网络参数\(W2\)和\(b2\)的梯度。要计算前一层的网络参数\(W1\)和\(b1\)的梯度,如果链式法则找所有路径那么路径将会非常多,计算非常繁琐。如果我们知道z1的梯度,根据前面一小节的计算公式,网络参数\(W1和b1\)的梯度很容易计算。那如何根据\(z2\)的梯度,如何计算\(z1\)的梯度?这一步是backpropagation算法的关键步骤。

三层神经网络结构

由于\(a1\)直接经过变换得到\(z2\),首先分析如何求\(a1\)的梯度。根据神经网络结构,对\(a1\)的每一维分量进行分析,发现\(a1 _t\)都会经过\(z2\)的每一项才能够完整地传播到损失函数\(J(\theta)\),\(a1 _t\)有\(L2\)条路径到达损失函数。累加所有路径的偏导数,\(\frac{\partial J(\theta)}{\partial a1 _t} = \sum _{i = 0}^{L2 -1}\frac{\partial J(\theta)}{\partial z2 _i} * \frac{\partial z2 _i}{\partial a1 _t}\)

$$\begin{align}
\frac{\partial J(\theta)}{\partial a1 _t} &= \sum _{i = 0}^{L2 -1}\frac{\partial J(\theta)}{\partial z2 _i} * \frac{\partial z2 _i}{\partial a1 _t}\\
&=\sum _{i = 0}^{L2 – 1}\frac{\partial J(\theta)}{\partial z2 _i} * W2[i][t]
\end{align}$$

第一个等式根据导数的链式法则,累加所有偏导数,第二个等式代入\(\frac{\partial z2 _i}{\partial a1 _t}=W1[i][t]\),根据矩阵(W1)与向量(a1)的乘法有:$$z2 _i = \sum _{k=0}^{}W1[i][k] * a1 _k $$ 所以$$\frac{\partial z2 _i}{\partial a1 _t} = W2[i][t]$$

把a1每一维分量偏导数上下“拼接”组成向量:
$$\begin{align}
\frac{\partial J(\theta)}{\partial a1} &= \pmatrix{\sum _{i = 0}^{L2 – 1} (\frac{\partial J(\theta)}{\partial z2 _i}) * W2[i][0] \cr \sum _{i = 0}^{L2 – 1} (\frac{\partial J(\theta)}{\partial z2 _i}) * W2[i][1] \cr … \cr … \cr … \cr \sum _{i = 0}^{L2 – 1} (\frac{\partial J(\theta)}{\partial z2 _i}) * W2[i][L1-1]} \\
&=W2^{\rm T} * (\frac{\partial J(\theta)}{\partial z2})
\end{align}$$

上面等式从第一步到第二步的原因是\(\sum _{i = 0}^{L2 – 1} (\frac{\partial J(\theta)}{\partial z2 _i}) * W2[i][t]\)表示\(W2\)的第\(t\)列和\(\frac{\partial J(\theta)}{\partial z2}\)的内积,内积的值为\(a1\)的第\(t\)维分量的偏导数。把a1的梯度写成矩阵相乘的形式为
$$
\frac{\partial J(\theta)}{\partial a1}=W2^{\rm T} * (\frac{\partial J(\theta)}{\partial z2})
$$
上面推导反向梯度传播公式逐元素求导,这里同样可以用导数的链式法则,但是这里的链式法则需要有一点的调整,中间变量不是对单变量求导,而是向量对向量求导。\(\frac{\partial J(\theta)}{\partial a1} = (\frac{\partial z2}{\partial a1})^T * \frac{\partial J(\theta)}{\partial z1}\),\(\frac{\partial z2}{\partial a1}\)这类“向量对向量的导数”称为Jacobian矩阵,其定义为
$$\frac{\partial z}{\partial a} = \pmatrix{
\frac{\partial z _0}{\partial a _0} & \frac{\partial z _0}{\partial a _1} & … & \frac{\partial z _{0}}{\partial a _{n-1}} \cr
\frac{\partial z _1}{\partial a _0} & \frac{\partial z _1}{\partial a _1} & … & \frac{\partial z _{1}}{\partial a _{n-1}} \cr
…&…&…& \cr
\frac{\partial z _{m-1}}{\partial a _0} & \frac{\partial z _{m-1}}{\partial a _1} & … & \frac{\partial z _{m-1}}{\partial a _{n-1}} \cr
}$$
如果把\(\frac{\partial J(\theta)}{\partial z2}\)也用Jacobian的方式去看待,\(J(\theta)\)是标量,\(z2\)为一维向量,长度是\(L2\),如果按照Jacobian方式求导\(\frac{\partial J(\theta)}{\partial z2}\)应该是shape为\((1, L2)\)的矩阵,记为\(M _{(1, L2)}\)。1.3节定义向量梯度时为了和向量表示为相同维度,把向量导数表示成shape为\((L2, 1)\)矩阵,把梯度定义成了\(M _{(1, L1)}\)的转置。\(\frac{\partial J(\theta)}{\partial a1}\)也表示为Jacobian形式,shape为\((1, L1)\)的矩阵,即为\(M _{(1, L1)}\)。
$$\begin{align}
\frac{\partial J(\theta)}{\partial a1} = \frac{\partial J(\theta)}{\partial z2} * \frac{\partial z2}{\partial a1}
\\
M _{(1, L1)} = M _{(1, L2)} * M _{(L2, L1)}
\end{align}
$$
第二个公式符合矩阵乘法规律,把第二个公式进行转置就是本节向量导数的表示方式:
$$(M _{(1, L1)})^{\rm T} = (M _{(1, L2)} * M _{(L2, L1)}) ^{\rm T}$$
$$(M _{(1, L1)})^{\rm T} = (M _{(L2, L1)}) ^{\rm T} * M _{(1, L2)}^{\rm T}$$
人工神经网络是向量到向量的映射,如果是矩阵到矩阵的映射呢?例如,卷积神经网络(Convolutional Neural Network),加上图像颜色深度就是张量(Tensor)到张量的映射,这样的参数如何使用链式法则求导呢?

2.4.3 张量求导的链式法则

矩阵可看作是shape为\((m, n)\)的张量,记为\(T _{(m,n)}\),包含颜色深度的图像可以表示成shape为\((d, w, h)\)的张量,记为\(T _{(d, w, h)}\),\(d\)是颜色深度,\(w\)是图像宽度,\(h\)为图像高度。矩阵和向量是特殊类型的张量。\(T _{(m, n)}\)为\(m\)行,\(n\)列的矩阵,\(T _{(n)}\)为长度为\(n\)的向量。

有两个函数,\(g\)和\(f\),分别把张量\(x\)映射到\(u\)和把\(u\)到\(y\),\(x\)是\(T _{(d, w, h)}\),\(u\)是\(T _{(p, q)}\),\(y\)是\(T _{(n)}\)
$$x = g(u)\\y = f(u)$$

\(x\)是包含颜色深度的图像,\(u\)经过卷积之后“消掉”颜色深度的图像,函数\(y\)把图像经过一个变换生成向量。有点类似卷积神经网络,但是并不完全一样的结构,这里并没有指定\(g\)和\(f\)具体怎么变换。要计算\(\frac{\partial y}{\partial x}\),扩展向量对向量导数的Jacobian矩阵,向量对向量的求导,两两求偏导之后构成Jacobian矩阵,张量对张量求导,两个张量元素分别求偏导组成张量,导数张量的shape为因变量张量和自变量张量shape的“拼接”(可想象为笛卡尔积)。例如,张量\(y\)对张量\(x\)求导,导数张量的shape为\((n, d, w, h)\),记为\(\frac{\partial y}{\partial x}=T _{(n, d, w, h)}^{y \rightarrow x}\),下标为\([i,j,k,l]\)的值\(T _{(n, d, w, h)}^{y \rightarrow x}[i,j,k,l]\),表示\(y[i]\)对\(x[j,k,l]\)的偏导数。

$$\frac{\partial y}{\partial x}=T _{(n, d, m, n)}^{y \rightarrow x} \\
\frac{\partial y}{\partial u}=T _{(n, p, q)}^{y \rightarrow u}\\
\frac{\partial u}{\partial x}=T _{(p, q, d, w, h)}^{u \rightarrow x}$$

与标量的求导链式法则一样,张量求导的链式法则同样适用,标量是连乘(标量只有一条路径),张量链式求导法则则是张量的内积(张量的中间路径不止一条,内积会累加所有路径),(张量内积参考:numpy tensordot)。两个张量内积需要指定所需累加(sum over)的维度,矩阵与矩阵相乘(内积),仅累加左边矩阵最后一个维度和右边矩阵的第一个维度。张量导数的链式法则中的内积,累加不止一个维度,需要累加中间变量所有的维度。
$$\begin{align}
\frac{\partial y}{\partial x} &= \frac{\partial y}{\partial u} * \frac{\partial u}{\partial x} \\
&={\rm tensordot}(T _{(n, p, q)}^{y \rightarrow u}, \quad T _{(p, q, d, w, h)}^{u \rightarrow x}, \quad 2)
\end{align}$$

\({\rm tensordot}(T1, T2, {\rm axes})\)表示两个张量的内积,axes参数指定所需累加的维度,本例中axes为2。axes参数指定了所需累加第一个张量的最后两个维度\((p,q)\)和第二个张量的前面两个维度,同样也是\((p,q)\)。这里第一个张量的最后两个维度和第二个张量的前两个维度必须相同,否则不能进行张量的内积。(axes参数的作用参考:numpy tensordot)。最后得到的张量维度为\((n, d, w, h)\),符合最开始定义的\(y\)对\(x\)导数张量的shape。张量内积累加中间变量的维度,和最开始定义的链式法则寻找所有路径然后累加有异曲同工之处,只是标量链式法则手动寻找所有路径,张量链式法则使用张量内积累加指定维度,表达更为简洁。

2.4.4 激活层到非激活层的导数

现在利用Jacobian矩阵从a1的梯度推导z1的梯度。已经知道\(\frac{\partial J(\theta)}{\partial a1}\)利用Jacobian矩阵,计算\(\frac{\partial J(\theta)}{\partial z1}\),从神经网络的结构图可以看到,只有下标相同的\(z1\)和\(a1\)相连,相连的方式是做非线性变换操作。Jacobian矩阵除了对角线以外的所有元素都为0,\(\frac{\partial a1}{\partial z1}\)为对角矩阵,对角元素为\(\frac{\partial}{\partial z1 _t}{\rm sigmoid}(z1 _t)\),因此\(\frac{\partial J(\theta)}{\partial z1} = (\frac{\partial a1}{\partial z1} ) ^ {\rm T} * \frac{\partial J(\theta)}{\partial a1}\)。由于Jacobian矩阵为对角矩阵,对角矩阵与向量的相乘等于对角线元素组成一个向量与向量逐元素相乘。对角线向量为\(sigmoid ^{\prime} (z1)\)。因此
$$
\frac{\partial J(\theta)}{\partial z1} =
{sigmoid^{\prime} (z1)} \circ \frac{\partial J(\theta)}{\partial a1}
$$
根据2.4.2节,已知\(\frac{\partial J(\theta)}{\partial z1}\),即可求前一层神经网络参数\(W1\)和\(b1\)的梯度:

$$
\frac{\partial J(\theta)}{\partial b1}=\frac{\partial J(\theta)}{\partial z1}\\\frac{\partial J(\theta)}{\partial W1}=\frac{\partial J(\theta)}{\partial z1} * x^{\rm T}$$

如果神经网络还有更多层次,按照前面的两个步骤依次递推计算即可得到神经网络所有参数的梯度。

2.4.5 梯度反传计算方法总结

1、已知未使用激活函数的网络层\(z\)的梯度,计算该层神经网络的参数\(W\)和\(b\)的梯度

2、已知未使用激活函数的网络层\(z\)的梯度,计算前一网络层使用激活函数的网络层\(a\)的梯度

3、已知使用激活函数的网络层\(a\)的梯度,计算当前网络层未使用激活函数层\(z\)的梯度

对于经典的神经网络连接方式(只有相邻层全连接,非激活层到激活层一一对应连接,除此之外无其他方式的连接),可以用以上三条规则计算出神经网络所有参数的梯度。对于更复杂的网络连接,例如循环神经网络、卷积神经网络同样可以用张量导数的链式法则计算各层参数的梯度。back propagation算法其实就是利用导数链式法则计算神经网络参数的梯度。

2.5 求最后一层z的梯度

2.4节说明了梯度的反向传播算法,那么神经网络的最后一层的梯度如何计算呢?答案依然是链式法则求导。
神经网络最后一层离损失函数最近,其参数到最终损失值的路径最短(路径数也是最少)。为了描述简单,考虑单个样本\(\langle x, y \rangle\),假设这个样本标签为\(k\),即\(y\)的第\(k\)维为1,其余维度为0,因此损失函数为:
$$\begin{align}
J(\theta) &= -{y _k}*\log({\widehat y _k}) \\
&=-\log\frac{\exp(z2 _k)}{\sum _{j=0}^{K-1} \exp(z2 _j)}
\end{align}
$$

损失函数\(J(\theta)\)对向量\(z2\)的每一个维度计算偏导数,\(z2\)的分量分为两种情况,分别是分量下标等于\(k\)和不等于\(k\)两种。首先计算\(\frac{\partial J(\theta)}{\partial z2 _k}\),\(k\)与样本的标签相同

$$\begin{align}
\frac {\partial J(\theta)} {\partial z2 _k} &= \frac {\partial} {\partial z2 _k} ({-\log\frac{\exp(z2 _k)}{\sum _{j=0}^{K-1} \exp(z2 _j)}}) \\
&=-\frac{\sum _{j=0}^{K-1} \exp(z2 _j)}{\exp(z2 _k)} * \frac {\partial} {\partial z2 _k} (\frac{\exp(z2 _k)}{\sum _{j=0}^{K-1} \exp(z2 _j)})\\
&=-\frac{\sum _{j=0}^{K-1} \exp(z2 _j)}{\exp(z2 _k)} * \frac{exp(z2) * (\sum _{j=0}^{K-1} \exp(z2 _j)) – \exp(z2 _k) * \exp(z2 _k)}{(\sum _{j=0}^{K-1} \exp(z2 _j))^2} \\
&=-(1-\frac{\exp(z2 _k)}{\sum _{j=0}^{K-1} \exp(z2 _j)}) \\
&=\widehat y _k – 1 \\
&=\widehat y _k – y _k
\end{align}
$$

求偏导说明:
第一个等号到第二个等号利用了求导的链式法则,\(\log(u)\),\(u=\frac{\exp(z2 _k)}{\sum _{j=0}^{K-1} \exp(z2 _j)}\)

第二个等号到第三个等号是对\(\frac {\partial} {\partial z2 _k}(\frac{\exp(z2 _k)}{\sum _{j=0}^{K-1} \exp(z2 _j)})\)进行求导,对“分数类型函数”求导,对\(\exp(u)\)求导。

第三个等号到第四个等号约分化简等操作。

第四个等号到第五个等号,根据\({\rm softmax}\)的定义。

第五个等号到第六个等号,根据\(y _k\) = 1,样本的标签为\(k\)。

对\(z2 _l\)求导,其中\(l \ne k\)

$$\begin{align}
\frac {\partial J(\theta)} {\partial z2 _l} &= \frac {\partial} {\partial z2 _l} ({-\log\frac{\exp(z2 _l)}{\sum _{j=0}^{K-1} \exp(z2 _j)}}) \\
&=-\frac{\sum _{j=0}^{K-1} \exp(z2 _j)}{\exp(z2 _k)} * \frac {\partial} {\partial z2 _l} (\frac{\exp(z2 _k)}{\sum _{j=0}^{K-1} \exp(z2 _j)})\\
&=-\frac{\sum _{j=0}^{K-1} \exp(z2 _j)}{\exp(z2 _k)} * \frac{0 * (\sum _{j=0}^{K-1} \exp(z2 _j)) – \exp(z2 _k) * \exp(z2 _l)}{(\sum _{j=0}^{K-1} \exp(z2 _j))^2} \\
&=\widehat y _l\\
&=\widehat y _l – y _l \qquad (y _l = 0)
\end{align}
$$

对\(z2 _l\)求导与对\(z2 _k\)求导的唯一区别在对“分数类型”求导那步,前者分子不是一个关于\(z2 _l\)的函数;后者是关于\(z2 _k\)的函数,因此对\(z2 _l\)求导时为0。

$$\begin{align}
\frac {\partial J(\theta)} {\partial z2} &= [\frac {\partial J(\theta)} {\partial z2 _0}, \frac {\partial J(\theta)} {\partial z2 _1}, ……,\frac {\partial J(\theta)} {\partial z2 _{K-1}}]^{\rm T}\\
&=[\widehat y _0 – y _0, \widehat y _1 – y _1, ……, \widehat y _{K-1} – y _{K-1}]^{\rm T} \\
&=\widehat y – y
\end{align}
$$
计算最后神经网络的最后一层的梯度之后,能够根据2.4节所述的梯度反向传播算法,计算神经网络的所有梯度。计算梯度时本文以交叉熵作为损失函数,实际应用中损失函数种类可能不同,因此计算神经网络最后一层的梯度时方法可能有些差异,但是本质上还是求导数的链式法则。

2.6 思考

1、经典神经网络(只有相邻层全连接,非激活层到激活层一一对应连接,除此之外无其他方式的连接)计算参数梯度的时间复杂度是多少?
2、当神经网络层数增加时,通常情况下前面层次的梯度会变得越来越小(Gradient Vanish Problem),试从梯度计算方法与sigmoid函数角度解释这个问题?(提示,sigmoid的导数的取值范围(0, 0.25])
3、1.1节中,为什么说梯度方向(反方向)是函数当前点前往极大值(极小值)点的“最优”方向?(“最优”是指学习率确定的情况下,仅改变前进方向,函数值向极值变化最大的方向。这里“最优”方向,并不是真正意义的最优方向,对于凸函数,牛顿法比基于梯度的方法迭代方向更优,但是牛顿法并不适用于神经网络的优化,有两方面原因,1)牛顿法在凸优化能够得到理论上最优解,但是在非凸函数上效果并不理想,神经网络几乎所有情况都不是凸优化;2)牛顿法需要计算Hessian Matrix的逆,矩阵逆算法是三次方的时间复杂度,尽管有近似的方法计算牛顿法的逆矩阵,但是时间复杂度,收敛速度等因素综合起来并不一定比基于梯度的方法更好,关于神经网络和深度模型优化方面的问题建议深入阅读Deep Learning第八章

本文版权由作者DY和Kakapo共同拥有,转载须联系kakapo项目组并征得同意,kakapo项目组对任何形式的侵权行为保留追究的权利。详情请咨询help@paperpo.ml。

本次2.6部分的思考题列入有奖问答活动,可以通过评论回答参加。我们会选出部分答案寄送一本由主办方精心挑选等价60元左右的技术书籍,其他类型的精华评论也会被列入奖励范围,本次活动自本篇文章发布一周后截至,欢迎大家踊跃参加。

“如何计算神经网络梯度”的1,530个回复

  1. 1. 假设一层包括一个非激活层和一个激活层,假设每层有N个神经元,总共有L层,那么时间复杂度是O(L*N*N)
    2. 前面层次的梯度是从该层到末层梯度的累乘,如果用sigmoid函数作为激活层,其梯度在0-0.25之间,所以多次乘积下来前面层次的梯度会趋近于0.(所以越来越多人放弃sigmoid函数作为激活层,而选择ReLU激活函数)
    3. 梯度的定义就是:在该位置,自变量单位变化的情况下,函数值变化最大的方向。基于这个定义,梯度指出了在每个位置下,最快逼近函数极值的方向,因为学习率(步长)确定,所以它是“最优”方向。

发表评论

电子邮件地址不会被公开。 必填项已用*标注