深度前馈网络、神经网络概述

以下内容由我的CSDN博客迁移。

让我们学习一下深度学习最基本的深度前馈网络(主要参考《深度学习》)

一、深度前馈网络的基本概念

深度前馈网络(deep freedforward network)也叫做前馈神经网络或者多层感知机(multilayer perception, MLP), 是最典型的深度学习模型。它的目标是近似某个函数\(f^*\)

前向(feedforward) 是指信息流通过\(x\)的函数,流经用于定义\(f\)的中间过程,最终到达输出\(y\),模型的输出和模型本身之间没有反馈(feedback) 连接。 包含反馈连接的称为循环神经网络(recurrent neural network)。

网络(network) 是指神经网络通常用许多不同函数的复合来表示。该模型与一个有向无环图关联。例如经典的链式结构:\(f(x)=f^{(3)}(f^{(2)}(f^{(1)}(x)))\)。此时\(f^{(1)}\)称为网络的第一层,以此类推。链的全长称为深度 (也就是深度学习的来源),前馈网络的最后一层称为输出层,而中间层称为隐藏层

神经网络,之所以叫“神经”,是因为它受到神经科学的启发。网络中的每个隐藏层通常都是向量值的。这些隐藏层的维数决定了模型的宽度。向量的每个元素都可以被视为类似一个神经元的作用。每一层既可以看作是向量到向量的函数,也可以把层看作许多并行操作的单元组成。每个单元是一个由向量到标量的函数。与神经元类似,这些单元接受来自其他单元的输入,并计算自己的激活值,传递给之后的单元。 但是,深度前馈网络只是为了实现统计泛化的函数近似机,并不是大脑的近似模型。 神经科学只是提供了些许灵感。

进一步理解神经网络

我们可以从线性模型出发去理解神经网络。

  1. 线性模型如逻辑回归和线性回归,无论是通过闭解形式还是使用凸优化,它们都能高效且可靠地拟合。但是线性模型只局限在线性函数中,模型的容量太小,不足以学习变量间非线性的相互作用。

  2. 为了扩展线性模型,我们可以把线性模型用于一个变换后的输入\(\phi(x)\)上,这里的映射是一个非线性变换。同样,我们还可以用核技巧,得到一个隐含使用\(\phi\)映射的非线性学习算法。

  3. 接下来我们需要考虑如何去选择\(\phi\)

  • 第一种方法是选择一个通用的\(\phi\),比如无限维的\(\phi\) ,它隐含在基于RBF核的核机器上。但是这经常会导致在测试集上的泛化不好。通用的特征映射通常只基于局部光滑的原则,并且没有足够的先验信息进行编码来解决高级问题。
  • 另一种是手动的设计\(\phi\)。在深度学习以前,这一直是主流的方法。但这种方法过于复杂,需要从业者足够擅长特定的领域才可以设计出较好的特征。并且不同领域之间很难去迁移。
  • 深度学习的策略就是学习\(\phi\),此时我们的模型\(y=f(x;\theta,w)=\phi(x;\theta)^Tw.\)因此我们有两种参数:用于学习\(\phi\)的参数\(\theta\),以及用于将\(\phi(x)\)映射到所需输出的参数\(w\)。学习\(\phi\)意味着隐藏层的存在,同时也表明了我们放弃了问题的凸性,但是利大于弊。我们可以使学习到的\(\phi\)变得高度通用,只需使用一个非常广泛的函数族\(\phi(x;\theta)\)——这反映了第一种方法的优点;另外,我们可以将一些先验知识编码进入网络来帮助泛化,只需要设计一些预期表现优异的函数族(注意:不用去寻找特定的函数)即可——这体现出了第二种方法的优点。

因此我们知道了神经网络的基本思想就是学习特征,接下来让我们学习一下部署一个网络的基本设计决策。

二、前馈神经网络的基本设计决策 —— 基于梯度的学习

设计神经网络与使用梯度下降训练其他机器学习模型并没有太大不同。但是神经网络的非线性导致了许多我们已知的代价函数变得非凸,因此我们的神经网络的训练通常使用迭代的、基于梯度的优化,且只会使得代价函数达到一个较小的值,但是用于非凸损失函数的随机梯度下降并不能保证会达到全局收敛,并且神经网络对于初始值很敏感,因此初始化的策略是很重要的。

总之,绝大多数的训练算法都是基于梯度的学习,或是对梯度下降思想的改进或提纯。当然,传统的机器学习算法也可以用梯度下降来训练,因此可以认为深度学习和其他模型本质上并没有太大区别。

1. 代价函数

与其他模型一样,我们需要选择一个代价函数来进行学习。我们可以直接使用传统的代价函数,也就是简单地使用最大似然原理,我们使用训练数据和模型预测之间的交叉熵作为代价函数。

另外,有时候我们不是预测\(y\)的完整概率分布,而是仅预测给定\(x\)的条件下\(y\)的某些统计量。有些专门的损失函数允许我们训练这些预测器。

(1). 使用最大似然学习条件分布 最大似然与训练数据和模型分布间的交叉熵等价,即 \[J(\theta)=-\mathbb{E}_{x, y\sim\hat p_{data}}\log p_{\text{model}}(y|x)\] 使用最大似然的优势就是简便,只要我们明确一个模型\(p(y|x)\)就自动确定了一个代价函数。

神经网络的代价函数的梯度必须足够大和具有足够的预测性,来为学习算法提供一个好的指引。在很多情况下,隐藏单元的激活函数会使函数变得饱和(变得非常平),从而使梯度非常小。因此负的对数似然可以在很多模型中避免这一问题。

(2). 学习条件统计量 有时我们想通过一个预测器学习概率分布的某些统计量。如果我们有一个足够强大的神经网络,即可以表示一大类函数中的任何一个函数\(f\),此时我们可以把代价函数看作一个泛函(functional),即由函数到实数的映射。我们的学习过程就是选择函数的过程,因此可以设计代价泛函使它的最小值处于特殊的函数上。对函数求解优化问题需要变分法这一工具,就不在这里详述了。我们只需知道变分法可以导出下面两个结果:

  • 对于优化问题\(f^*=\argmin_f \mathbb E_{x, y\sim p_{data}}\|y-f(x)\|^2\)

有得到 \[f^*(x)=\mathbb E_{y\sim p_{data}(y|x)}[y]\] 即最小化均方误差代价函数将得到一个函数,它可以用来对每个\(x\)的值预测出\(y\)的均值

  • 另一个结果是\(f^*=\argmin_f \mathbb E_{x, y\sim p_{data}}\|y-f(x)\|_1\)

将得到一个函数可以对每个\(x\)预测\(y\)取值的中位数。这个代价函数通常称为平均绝对误差

然而均方误差和平均绝对误差在使用基于梯度的优化方法往往成效不佳。一些饱和的输出单元会使代价函数产生很小的梯度。因此即便是没必要估计整个概率条件分布,我们也倾向于交叉熵代价函数。

2. 输出单元

代价函数的选择与输出单元的选择紧密相关。在这里,我们假设前馈网络以及提供了一组定义为\(h=f(x; \theta)\)的隐藏特征。输出层的作用就是完成整个网络必须完成的任务。

1. 用于高斯输出分布的线性单元 给定特征\(h\),线性输出单元层产生一个向量\(\hat y=W^Th+b\) 它常被用来产生条件高斯分布的均值: \[p(y|x)=\mathcal N(y; \hat y, I)\] 最大化其对数似然等价于最小化均方误差。 由于线性单元不会饱和,因此它们易于采用基于梯度的优化算法。

2. 用于 Bernoulli 输出分布的 sigmoid 单元 许多任务需要预测二值型变量\(y\)的值。此时最大似然的方法是定义\(y\)\(x\)条件下的 Bernoulli 分布。之前我们学习了sigmoid单元,它的负对数似然可以很好的防止代价函数的饱和,而使用其他代价函数就很有可能由于它的饱和而效果不明显。 但是在实现的过程中我们必须要注意代价函数不能直接使用\(\hat y =\sigma (x)\)作为自变量,因为这可能会造成数值下溢。

3. 用于 Multinoulli 输出分布的 softmax 单元 当我们想要表示一个具有n个可能取值的离散型随机变量的分布时,都可以使用 softmax 函数。它可以看作 sigmoid 的扩展。 首先,线性层预测了未归一化的对数概率:\(z=W^Th+b\). 其中\(z_i=\log \hat P(y=i|x)\) softmax然后可以对z指数化和归一化来获得需要的\(\hat y\)\[\text{softmax}(z)_i={\exp(z_i)\over \sum_j\exp(z_j)}\]

与sigmoid一样,当使用最大化对数似然时softmax不会产生饱和的错误,而使用其他目标函数可能会产生饱和。

  • 从神经科学角度看,softmax 可以认为是一种在参与其中的单元之间形成竞争的方式:softmax输出总是和为1,所以一个单元的增加必然对应其他单元值的减少。这与被认为存在于皮质中相邻神经元间的侧抑制类似。在极端情况下,变成了赢家通吃(其中一个输出接近1,其它接近0)

4. 其他输出类型

  • 如果我们对于输出的方差感兴趣,那我们可以简单地将方差作为分布的其中一个属性。若希望模型对不同的x值预测出y不同的方差,这被称为异方差模型,此时我们简单地把方差指定为网络其中一个输出。
  • 若我们想要执行多峰回归,此时高斯混合是输出的自然表示,将高斯混合作为其输出的神经网络称为混合密度网络,具有 n 个分量的高斯混合输出由下面的条件分布定义: \[p(y|x)=\sum_{i=1}^np(c=i|x)\mathcal N(\boldsymbol{y; \mu^{(i)}(x), \Sigma^{(i)}(x)})\] 神经网络必须给出 3 个输出:定义\(p(c=i|x)\)的向量,对所有的 i 给出\(\mu^{(i)}(x)\)的矩阵,以及对所有的 i 给出\(\Sigma^{(i)}(x)\)的张量。高斯混合模型在语音生成模型和物理运动中特别有效。
  • 另外,我们也有可能希望输出包含更多变量、更大并且具有更多更丰富的结构。例如,我们可能希望神经网络输出字符序列来组成一个句子。此时我们仍然可以应用最大似然原理到我们的模型。

2. 隐藏单元

任何可以用作输出的神经网络单元,都可以用作隐藏单元。但隐藏单元的设计是深度学习的独有领域,目前还没有许多明确的指导性理论原则。

  • 我们并不能说哪种隐藏单元工作得更好,但我们可以先用它进行训练,用测试集评估它的性能。
  • 一些隐藏单元可能并不是在所有的输入点上是可微的,比如RELU:\(g(z)=\max\{0, z\}\). 但梯度下降对它们的表现依然很好。一部分原因是神经网络训练算法通常不会达到代价函数的局部最小值,而是显著减少它的值,因此代价函数的最小值对应于梯度未定义是可以接受的。不可微的隐藏单元通常只在少数点不可微。

大多数的隐藏单元都可以描述为接受输入向量 \(x\) ,计算仿射变换\(z=W^Tx+b\), 然后使用一个逐元素的非线性函数\(g(z)\)。大多的区别仅在这一非线性函数。

1. 整流线性单元及其扩展

整流线性单元(rectified linear unit, ReLU)使用激活函数\(g(z)=\max\{0, z\}\)

它易于优化,因为它与线性单元非常类似。这使得只要它处于激活状态,它的导数就能保持很大且一致。因此它的梯度方向对于学习来说更加有用。

当初始化仿射变换的参数时,可以将 b 的所有元素设置成一个小正值,以使单元处于激 活状态,且允许导数通过。

有许多整流线性单元的扩展存在,大多数的表现都比得上它,有时会更好。整流线性单元的一个缺陷是它不能用基于梯度的方法学习使它激活为0的点。

  • 绝对值整流:\(g(z)=|z|\)
  • 渗漏整流线性单元(Leaky ReLU):\(g(z)=\max\{0, z\}+0.01\min\{0, z\}\)
  • 参数化整流线性单元(parametric ReLU, PReLU):将0.01变为一个参数\(\alpha\)去学习。
  • maxout 单元进一步扩展了ReLU。它将向量\(z\)划分为每组具有 k 个值的组。而不是逐元素的函数: \[g(z)_i=\max_{j\in\mathbb G^{(i)}}z_j\] 这里\(\mathbb G^{(i)}\)是其中一组。maxout可以学习多达 k 段的分段线性的凸函数,因此 maxout 单元可以被视为学习激活函数本身,而不仅仅是单元之间的关系。只要 k 足够大,它就可以以任意精度去近似任何凸函数。

通常 maxout 单元由 k 个权重向量来参数化,因此它需要更多的正则化。

maxout 具有一些冗余来帮助它去抵抗一种称为灾难遗忘的现象,这个现象指神经网络忘记了如何去执行它们过去训练的任务。

总之,整流线性单元及其扩展都遵循着尽可能接近线性以使模型更容易优化的原则。

2. logistic sigmoid 与双曲正切函数

这两个激活函数紧密相关: \[\tanh(z)=2\sigma(2z)-1\]

sigmoid 由于它极容易饱和会使得梯度学习变得十分困难,因此一般不提倡使用它来作为隐藏单元。

当不得不使用时,tanh通常表现得要比sigmoid更好。因为 tanh 函数在 0 附近表现得接近线性,因此只要激活函数保持的很小,tanh的训练较为容易。

循环网络、许多概率模型以及一些自编码器由于一些额外的要求而必须使用sigmoid类函数。

3. 其他隐藏单元

  1. 线性隐藏单元:也可以认为激活函数是单位函数,尽管神经网络的每一层都是线性是不行的,而一部分层的线性是可以的,它可以一定程度上减少参数数量。
  2. softmax 单元:它可以用作一个开关,用于操作内存的高级结构中。
  3. ……

人们对于隐藏单元的研究一直不断,但通常只有当隐藏单元的表现足够好是才会被发表。

三、架构设计

架构(architecture) 是指网络的整体结构:它应具有多少单元,应该如何连接。大多数神经网络将一层层的单元组布置成链式结构,因此我们需要考虑选择网络的深度和每一层的宽度。

1. 万能近似性质和深度

万能近似定理表明,一个前馈神经网络如果具有线性输出层和至少一层具有任何一种"挤压"性质的激活函数的隐藏层,只要给于网络足够数量的隐藏单元,它可以以任意精度去近似任何从一个有限维空间到另一个有限维空间的 Borel 可测函数。前馈网络的导数也可以以任意的精度去近似函数的导数。

Borel 可测这里不讨论,但我们只需知道定义在\(\R^n\)上的任意连续函数是 Borel 可测的,因此可以用神经网络去近似。神经网络还能近似任何有限维离散空间映射到另一个的任意函数。

但是这一定理仅仅说明了我们的神经网络可以近似,却不能保证我们能够学得这个函数。一是因为优化算法可能找不到用于期望函数的参数值,二是因为存在过拟合而选择了错误的函数。

同时定理也说明了仅仅一个隐藏层的神经网络也有可能近似任意函数,但它的宽度会大的不可估计。总之,在很多情况下,使用更深的模型可以减少表示期望函数所需的单元的数量,并且可以减少泛化误差。浅层模型所需的隐藏单元的数量是n的指数级。

实际上,选择深度模型包含了我们的先验信念:我们想要学习的函数需要包含若干更加简单的函数的组合,也就是说,我们想要学习的函数是包含多个步骤的计算机程序,每个步骤使用前一步的输出。

2. 其他架构

除了简单地链式结构,在实践中,神经网络的结构有很多。 许多架构被开发用来处理特定任务:用于视觉任务的卷积神经网络;用于处理序列的循环神经网络等。

同时,一些层也不需要连接在链中,可以构建一个主链,并添加额外的架构特性,比如从层 i 到层 i+2 或更高层的跳跃连接,这使得梯度更容易从输出层流向接近输入的层。

当然,层与层之间也可以不必是全连接,可以减少连接的数量形成稀疏连接以减少参数量,但这通常高度依赖于特定问题。

四、反向传播和其他的微分算法

反向传播(back propagation) 算法,通常简称为 backprop 允许来自代价函数的信息通过网络向后流动,以便计算梯度。它能够使用简单且廉价的程序去实现计算梯度。

事实上,反向传播不是用于MLP的整个学习算法,它仅指用于计算梯度的方法。同样,它也不仅适用于MLP,原则上它可以计算任何函数的导数。

1. 计算图

我们之前已经知道了神经网络的架构,现在引入计算图语言来精确的描述它,其中图中的每一个节点是一个变量。这个变量可能是标量、向量、矩阵、张量等等。

如图,这是一个简单的逻辑回归的计算图。 如果变量y是变量x通过一个操作计算得到的,那么我们画一条从x到y的有向边,有时我们会把操作的名称作为注释。

2. 微积分中的链式法则

微积分中的链式法则用于计算复合函数的导数,反向传播是一种计算链式法则的算法。

设x是实数,f 和 g 是从实数到实数的映射,假设\(y=g(x)\)\(z=f(g(x))=f(y)\), 链式法则是指 \[\frac{dz}{dx}=\frac{dz}{dy}\frac{dy}{dx}\] 我们可以对标量进行扩展,假设\(\boldsymbol x\in\R^m, \boldsymbol y\in\R^n\), \(\boldsymbol y=g(\boldsymbol x), z=f(\boldsymbol y)\)那么 \[\nabla_\boldsymbol x z=\left (\dfrac{\partial \boldsymbol y}{\partial \boldsymbol x}\right)^T\nabla_\boldsymbol y z\] 这里\(\frac{\partial \boldsymbol y}{\partial \boldsymbol x}\)是 g 的 \(n\times m\)的 Jacobian 矩阵

通常我们会将反向传播应用于任意维度的张量,但这与使用向量并没有多大区别,我们会将每个张量变平为一个向量,计算向量值梯度,然后重新构造成张量。

3. 递归地使用链式法则来实现反向传播

使用计算图,我们可以直接写出某个标量关于计算图中任何产生该标量的节点的梯度的代数表达式。

然而在实际计算时,许多子表达式可能在梯度的整个表达式中重复若干次。在某些情况下,计算两次相同的子表达式纯粹是浪费,在复杂图中,可能存在指数多的这种计算上的浪费,使得链式法则不可实现。

如图,链中的每一步使用相同的操作函数\(f\)为了计算\(\frac{\partial z}{\partial w}\), 有 \[\frac{\partial z}{\partial w}=\frac{\partial z}{\partial y}\frac{\partial y}{\partial x}\frac{\partial x}{\partial w}=f'(y)f'(x)f'(w)=f'(f(f(w)))f'(f(w))f'(w)\] 显然第三个式子更加方便,它也是反向传播所采用的方法,它仅计算f(w)的值一次并将它存储在变量x中。

首先我们先考虑描述如何计算单个标量\(u^{(n)}\)的计算图. 我们希望对所有 \(i\in \{1, 2, \dots.n_i\}\)计算\(\dfrac{\partial u^{(n)}}{\partial u^{(i)}}\).

在计算图中,每个节点与操作\(f^{(i)}\)相关联,有 \[u^{(i)}=f(\mathbb A^{(i)})\] 其中,\(\mathbb A^{(i)}\)\(u^{(i)}\)的所有父节点的集合。这说明了前向传播的计算。我们将其放入图\(\mathcal G\)中。为了执行反向传播,我们可以构造一个依赖于\(\mathcal G\)并添加额外一组节点的计算图。这形成了子图\(\mathcal B\),它的每个节点都是\(\mathcal G\)的节点,但计算顺序完全相反。 \[\dfrac{\partial u^{(n)}}{\partial u^{(j)}}=\sum_{i:j\in Pa(u^{(i)})}\dfrac{\partial u^{(n)}}{\partial u^{(i)}}\dfrac{\partial u^{(i)}}{\partial u^{(j)}}\] 具体实现时的操作

反向传播算法被设计为减少公共子表达式的数量而不考虑存储的开销。而其他算法可能通过对计算图进行简化来避免更多的子表达式,或者也可能通过重新计算而不是存储来节省内存。

  • 一些反向传播的方法采用计算图以及添加一些额外的节点到计算图中。这些额外的节点提供了我们所需的倒数的符号描述。(Theano,Tensorflow采用)
  • 另一些采用计算图和一组用于图的输入的数值,然后返回在这些输入值处梯度的一组数值。这种方法叫做符号到数值的微分.(Torch、Caffe采用)

这里推荐一个视频 比较清楚地介绍了反向传播。


以上内容如有谬误还请告知。