(全连接)前馈神经网络
简介
前馈神经网络(Feedforward Neural Network, FNN)作为神经网络的一种,把每个神经元按接收信息的先后分为不同的组,每一组可以看作是一个神经层。每一层的神经元接收前一层神经元的输出,并输出到下一层神经元。整个网络中的信息是朝着一个方向传播的,它可以看作是一个函数,通过简单非线性函数的多次复合,实现输入空间到输出空间的复杂映射
前馈神经网络可以用一个有向无环图表示:

用下面的记号来描述一个前馈神经网络

前馈神经网络通过不断迭代下面的公式进行信息的传播
::: align-center
\begin{aligned}
&z^{(l)}=W^{(l)}a^{(l-1)}+b^{(l)}\\
&a^{(l)}=f_l(z_l)
\end{aligned}
:::
- 第一个公式输入第l-1层的活性值a^{(l-1)},计算第l层的净活性值z^{(l)}
- 之后经过一个激活函数f_l获得第l层的活性值a^{(l)}
- 可以把每一个神经层看作一个仿射变换(线性变换+平移变换)和一个非线性变换
通过逐层的信息传递,得到网络最后的输出a^{(L)},如此,可以将整个网络看作一个复合函数\phi(x,W,b),将向量x作为第一层输入a^{(0)},第L层的输出a^{(L)}作为整个函数的输出
::: align-center
x=a^{(0)}\rightarrow z^{(1)}\rightarrow a^{(1)}\rightarrow z^{(2)}\rightarrow a^{(2)}\rightarrow\cdots\rightarrow a^{(L-1)}\rightarrow z^{(L)}\rightarrow a^{(L)}=\phi(x,W,b)
:::
神经网络具有极强的数据拟合能力,常见的连续非线性函数均可使用前馈神经网络来进行近似。这里需要引用通用近似定理
定理(通用近似定理) 令\varphi(\cdot)是一个非常数,有界,单调递增的连续函数,\mathcal{I}_{d}是一个d维的单位超立方体[0,1]^d,C(\mathcal{I}_d)是一个定义在\mathcal{I}_d上的连续函数集合。对于任何一个函数f\in C(\mathcal{I}_d),存在一个整数m和一组实数v_i,b_i\in\mathbb{R}以及实数向量\mathbf{w}_i\in \mathbb{R}^d,i=1,2,\cdots,m,以至于我们可以定义函数
::: align-center
F(\mathbf{x})=\sum_{i=1}^m v_i\varphi(\mathbf{w}_ix+b_i)
:::
作为函数f的近似实现,即
::: align-center
|F(\mathbf{x})-f(\mathbf{x})|<\epsilon,\forall\mathbf{x}\in\mathcal{I}_d
:::
其中\epsilon是一个很小的正数
所以,神经网络在某种程度上可以作为一个“万能函数”,用于进行复杂的特征转换或逼近一个复杂的条件分布,例如多层前馈神经网络可以作为一种特征转换的方法,其输出\varphi(\mathbf{x})可以作为分类器的输入进行分类。
::: align-center
\hat{\mathbf{y}}=g(\varphi(\mathbf{x}),\theta)
:::
其中g(\cdot)是分类器函数
如果分类器为逻辑回归分类器等,那么g(\cdot)也可以直接作为神经网络的最后一层,即神经网络直接输出不同类别的条件概率p(\mathbf{y}|\mathbf{x})

策略和算法
我们考虑使用交叉熵损失函数(也可以使用平方损失。这里以交叉熵损失为例,它被广泛应用于分类神经网络)
::: align-center
\mathcal{L}(\mathbf{y},\hat{\mathbf{y}})=-\mathbf{y}^T\log\hat{\mathbf{y}}
:::
这里的\mathbf{y}是标签的向量形式(比如one-hot编码)
注:这里要区分交叉熵与前面最大熵模型中的信息熵(香农熵),最大熵模型通过优化使香农熵最大从而保证模型对未知数据保持最大的不确定性(即最公平的假设),从而达到对未知数据的无偏估计。交叉熵是监督学习中用于衡量预测分布与真实分布的一种损失计算方法,通过优化交叉熵损失函数,我们使预测分布尽可能的对齐真实分布。
给定训练集为D=\{\mathbf{x}^{(n)},\mathbf{y}^{(n)}\}^N,将每个样本\mathbf{x}^{(n)}输入给前馈神经网络,得到的网络输出为\hat{\mathbf{y}}^{(n)},其在数据集D上的结构化风险函数为
::: align-center
\mathcal{R}(\mathbf{W},b)=\frac{1}{N}\sum_{n=1}^{N}\mathcal{L}(\mathbf{y}^{(n)},\hat{\mathbf{y}}^{(n)})+\frac{\lambda}{2}||\mathbf{W}||_F^2
:::
其中\frac{\lambda}{2}||\mathbf{W}||_F^2为正则化项,用于防止过拟合,一般使用Frobenius范数
::: align-center
||\mathbf{W}||_F^2=\sum_{l=1}^L\sum_{i=1}^{M_l}\sum_{j=1}^{M_{l-1}}(w_{ij})^2
:::
若考虑直接使用全局梯度下降法,有
::: align-center
\begin{aligned}
&\mathbf{W}^{(l)}\leftarrow\mathbf{W}^{(l)}-\alpha\frac{\partial \mathcal{R}(\mathbf{W},b)}{\partial\mathbf{W}^{(l)}}=\mathbf{W}^{(l)}-\alpha(\frac{1}{N}\sum_{n=1}^N\frac{\partial \mathcal{L}(\mathbf{y}^{(n)},\hat{\mathbf{y}}^{(n)})}{\partial\mathbf{W}^{(l)}}+\lambda \mathbf{W}^{(l)})\\
&\mathbf{b}^{(l)}\leftarrow\mathbf{b}^{(l)}-\alpha\frac{\partial\mathcal{R}(\mathbf{W},b)}{\partial\mathbf{W}^{(l)}}=\mathbf{b}^{(l)}-\alpha(\frac{1}{N}\sum_{n=1}^N\frac{\partial\mathcal{L}(\mathbf{y}^{(n)},\hat{\mathbf{y}}^{(n)})}{\partial\mathbf{b}^{(l)}}
\end{aligned}
:::
但是,正向的梯度下降法需要计算损失函数对参数的偏导数,如果链式法则逐一对每个参数求偏导比较低效,在神经网络层数过多时会出现组合爆炸问题,在神经网络的训练中经常使用反向传播算法高效地计算梯度。
算法
反向传播算法
反向传播算法(BackPropagation, BP)是“误差反向传播”的简称,它适合于多层神经元网络,建立在梯度下降法的基础上。
BP算法的学习过程由正向传播过程和反向传播过程组成
- 在正向传播过程中,输入信息通过输入层经隐含层,逐层处理并传向输出层。如果与预测值与真实值不一样,则取输出与期望的某种误差 作为损失函数\mathcal{L}(比如上面的交叉熵损失函数+正则化项)
- 将正向传播中的损失函数传入反向传播过程,逐层求出损失函数对各神经元权重的偏导数,作为目标函数对权重的梯度。根据这个计算出来的梯度来修改权重。当误差达到期望值时,学习结束
(1)计算误差项
首先定义误差项,每一层的误差可以定义为损失函数\mathcal{L}对于该层净输入z的偏导数
::: align-center
\delta^{(l)}=\frac{\partial \mathcal{L}}{\partial z^{(l)}}
:::
从偏导数的几何直观,表示“损失函数”在z^{(l)}上的“变化程度”的大小,也就是说,z^{(l)}层对最终损失的影响程度。\delta越大,说明该层对最终误差的贡献越大
已知神经网络最后的预测值\hat{\mathbf{y}}和真实值\mathbf{y}的损失函数是\mathcal{L},我们接下来就应该考虑将前面每一层神经元节点对损失函数的影响计算出来,以供我们依照不同层对最终损失的影响来决定如何按照不同权重更新每一层的参数
所以考虑反向,从最后一层开始,把“误差的总影响”逐一分摊传播给前一层的神经元结点。注意传播时需要考虑到前一个神经元的权重系数(因为不同神经元的重要性毕竟是不同的)和激活函数的导数(不同神经元的激活函数对于输出值的“改变程度”也是不同的)
考虑神经网络中的第l+1层到第l层误差的反向传递
正向传播时,由l层到l+1层的路径为
::: align-center
z^{(l)}\rightarrow a^{(l)}\rightarrow z^{(l+1)}
:::
在已经获得了后一层的误差\delta^{(l+1)}=\frac{\partial \mathcal{L}}{\partial z^{(l+1)}},现考虑获得前一层的误差\delta^{(l)}=\frac{\partial \mathcal{L}}{\partial z^{(l)}},由链式法得
::: align-center
\delta^{(l)}=\frac{\partial \mathcal{L}}{\partial z^{(l)}}=\frac{\partial\mathcal{L}}{\partial z^{(l+1)}}\frac{\partial z^{(l+1)}}{\partial a^{(l)}}\frac{\partial a^{(l)}}{\partial z^{(l)}}
:::
求导过程参考矩阵微分笔记
等式右侧:
- 第一项为已知量\delta^{(l+1)}
- 第二项为z+1层加权后的净活性值向量z^{(l+1)}对该层加权前的输入值,上一层的活性值向量a^{(l)}的偏导数,故可知偏导得到的是l到l+1层的权重矩阵W^{(l+1)}
- 第三项为第l层经激活函数的活性值向量a^{(l)}对经激活函数前的加权净活性值向量z^{(l)}的偏导数,故可知偏导得到的结果是第l层的激活函数的偏导f'(z^{(l)})
故综上得,误差反向传播的公式为
::: align-center
\delta^{(l)}=(W^{(l+1)})^T\delta^{(l+1)}\odot f'(z^{(l)})
:::
其中\odot表示逐项乘积
对于最后一层,误差的计算方式为
::: align-center
\delta^{(L)}=\frac{\partial \mathcal{L}}{\partial a^{(L)}}\frac{\partial a^{(L)}}{\partial z^{L}}
:::
- 第一项是损失函数对神经网络输出的偏导数
- 第二项为最后一层激活函数的偏导数f'(z^{(L)})
(2)梯度下降
计算完每层的误差项后,我们准备进行梯度下降,考虑上一节中的梯度下降公式,我们要获得其中损失函数对权重项和偏置项的偏导数,即考虑损失函数对某个神经元参数(第l层第i个神经元的第j个w_{ij}^{(l)}和该层的偏置项b^{(l)})的偏导
::: align-center
\begin{aligned}
&\frac{\partial\mathcal{L}}{\partial w_{ij}^{(l)}}=\frac{\partial\mathcal{L}}{\partial z^{(l)}}\frac{\partial z^{(l)}}{\partial w_{ij}^{(l)}}\\
&\frac{\partial\mathcal{L}}{\partial b^{(l)}}=\frac{\partial \mathcal{L}}{\partial z^{(l)}}\frac{\partial z^{(l)}}{\partial b^{(l)}}
\end{aligned}
:::
考虑第一个式子,其中\frac{\partial\mathcal{L}}{\partial z^{(l)}}=\delta^{(l)},考虑第二项,它是一个向量对标量的微分
::: align-center
\begin{aligned}
&\frac{\partial z^{(l)}}{\partial w_{ij}^{(l)}}\\
&=[\frac{\partial z_1}{\partial w_{ij}^{(l)}},\cdots,\frac{\partial z_i}{\partial w_{ij}^{(l)}},\cdots,\frac{\partial z_{m^{(l)}}}{\partial w_{ij}^{(l)}}]\\
&=[0,\cdots,\frac{\partial (w_{i:})a^{(l-1)}+b_i^{(l)}}{\partial w_{ij}^{(l)}},\cdots,0]\\
&=a_j^{(l-1)}
\end{aligned}
:::
故结果为
::: align-center
\frac{\partial\mathcal{L}}{\partial w_{ij}^{(l)}}=\delta^{(l)}(a^{(l-1)})^T
:::
第二项容易得第二项为单位矩阵I,故最后得结果为
::: align-center
\frac{\partial\mathcal{L}}{\partial b^{(l)}}=\delta^{(l)}
:::
最后给出伪代码:

梯度计算
数值微分
计算机内一般使用数值方法实现,如前向差分计算导数
::: align-center
\lim_{\Delta x\to\infty}\frac{f(x+\Delta x)-f(x)}{\Delta x}
:::
或中心差分计算导数
::: align-center
\lim_{\Delta x\to\infty}\frac{f(x+\Delta x)-f(x-\Delta x)}{2\Delta x}
:::
前向差分很少使用,因为由泰勒公式展开后,前向差分的误差是\mathcal{O}(\Delta x),中心差分的误差是\mathcal{O}(\Delta x^2)
直接使用数值微分计算神经网络中损失函数的梯度是不现实的,一方面参数是非常多的,运算量非常大。另一方面数值微分的准确度可能不够高,考虑缩小步长又会进一步增加计算量。
符号微分
符号微分实际上类似于人的手工计算,它是计算机根据微分计算规则进行微分计算的方式,但是直接使用符号微分计算神经网络的梯度容易产生表达式膨胀,造成组合爆炸
自动微分
自动微分(Automatic Differentiation, AD)是将一个复杂的数学运算过程分解为一系列简单的基本运算,形成计算图,之后每一项的基本运算都可以直接查表得出。由自动微分形成的自动梯度计算方法被广泛地应用于神经网络框架的梯度计算过程中
自动微分形成的计算图分为静态计算图和动态计算图
- 静态计算图在编译期构建,编译时间较长且运行期不能改变,灵活性差,但是因为编译期可优化所以其计算能力通常更强。代表框架早期的Tensorflow
- 动态计算图在运行期动态构建,灵活性好,但是与此同时牺牲了部分优化。代表框架PyTorch,现在的Tensorflow
这里我们介绍常用的反向模式
下面部分参考《老饼讲解-深度学习》
(1)函数拆解
首先把待计算梯度的函数拆分成多个基本初等函数,并记录各个基本函数的关系,形成计算图,函数直接的关系有两种:
- 复合关系:在复合关系中,求导为串行关系
- 基本运算关系:在基本运算之中,求导为并行关系

(2)前向计算节点值
在计算图中进行一次前向传播,得到每一个节点的值

(3)反向传播梯度值
根据复合关系的前后依赖性,由后向前对每个节点求偏导,传播梯度
由于每一个节点都是简单的初等函数,所以可以使用符号微分获得每个节点的偏导表达式,之后代入数据

(4)叠加获得梯度
最后叠加各个叶节点的梯度获得总梯度,对于上面的计算图来说,梯度为
::: align-center
\frac{\partial z}{\partial x^{(1)}}+\frac{\partial z}{\partial x^{(2)}}+\frac{\partial z}{\partial x^{(3)}}=-1.4436
:::
全部评论 (0)
暂无评论,快来抢沙发吧~