深度前馈网络FNN
深度前馈网络
今天介绍一个非常基础的神经网络模型,深度前馈网络(deep feedforward network),也叫做前馈神经网络(feedforward neural network,FNN)或者多层感知机(multilayer percetron,MLP),是典型的深度学习网络模型。 FNN的学习目标是去拟合一个函数\(f^*(\mathbf{x})\),这个函数是一个符合真实数据分布情况的函数,它将输入\(\mathbf{x}\)映射到输出\(y\)上。
例如说,一个用来预测苹果种类的判别模型,它的输入就是关于苹果的各种相关特征(例如说颜色、形状、大小等),输出就是苹果的种类。那么\(f^*(\mathbf{x})\)就相对于是一个完美的判别器,它总能对任何一个苹果的输入给出正确的输出。
前馈网络模型可以定义一个映射\(f(\mathbf{x},\theta)\),能通过对学习参数\(\theta\)的调整,最终得到最佳的对真实函数的近似。
感知机
历史
感知机模型最早是上个世纪50年代提出的一种基于生物神经元工作方式的人工神经网络模型。
感知机接收多个输入,每个输入对应一个权重。输入向量\(\mathbf{x}\)与权重向量\(\mathbf{w}\)的内积:
\[ z = \mathbf{w} \cdot\mathbf{x} + b \]
其中\(b\)是偏置(bias),用于调节激活函数的输出。
感知机使用一个激活函数(通常是阶跃函数)将内积的结果转换为输出:
\[ f(z) = \begin{cases} 1 & \text{如果 } z \geq 0 \\ 0 & \text{如果 } z \lt 0 \end{cases} \]
XOR问题
单层感知机可以很好地模拟与门,或门和非门,但是它不能很好地模拟异或门。
异或门是一种二元函数,它接受两个输入\((x_1,x_2)\),输出只有两种可能的结果:0或1。当且仅当两个输入值不同时,异或门输出1,否则输出0。可以发现,无论重要性参数和偏置取值多少,都无法完成异或门的实现。(这就促使在当时,人们认为感知机连最基本的异或门逻辑电路都无法实现,认为 感知机并没有很大的作用)。
之所以无法实现异或门,是因为感知机只能处理线性可分的数据,而异或门的输入是非线性的。对于其他的逻辑门,我们都可以通过一条直线将其分割成两个子区域,而直线无法对异或门进行这样的分割。
tips:线性模型不能用\(x_1\)的值来改变\(x_2\)的值
如何实现XOR门
为了实现异或门,我们可以使用一个模型来学习一个不同的特征空间,在这个空间上线性模型能够表示解。具体来说,我们引入一个新的隐藏层,隐藏层中包含两个神经元,每个神经元都有两个输入,然后输出一个值。
在这个新的前馈神经网络中,通过函数\(f^{(1)}(\mathbf{x},\mathbf{W},c)\)计算得到隐藏单元的向量\(\mathbf{h}\),然后这些隐藏单元的值随后被用作第二层的输入,第二层即为网络的输出层。于是,我们有:
\[ \mathbf{h} = f^{(1)}(\mathbf{x},\mathbf{W},c) \]
\[ y = f^{(2)}(\mathbf{h},\mathbf{w},b) \]
完整的模型即为:
\[ f(\mathbf{x},\mathbf{W},\mathbf{w},c,b)=f^{(2)}(f^{(1)}(\mathbf{x})) \]
现在,我们还需要确定\(f^{(1)}\)的具体形式,如果我们使用线性模型,前馈网络作为一个整体依然是线性的。因此隐藏层的特征必须用非线性函数来描述,大多数神经网络通过仿射变换后紧跟一个被称为激活函数的非线性函数来实现非线性变换,其中仿射变换是由学习得到的参数来进行控制。 定义\(\mathbf{h}=g(\mathbf{W}^T\mathbf{x}+c)\),其中\(\mathbf{W}\)是线性变换的权重矩阵,\(c\)是偏置项,\(g\)是激活函数。激活函数\(g\)通常选择对向量中每个元素分别起作用的函数,以此来得到一个隐藏层的向量。一般来说,现代的激活函数维\(g(z)=max\{0,z\}\)定义的整流线性单元,即ReLU函数。最终整个网络可以定义为:
\[ f(\mathbf{x},\mathbf{W},\mathbf{w},c,b)=w^T max\{0,g(\mathbf{W}^T\mathbf{x}+c)\} + b \]
利用这个双层的前馈神经网络,我们可以指定每一层的参数来使得模型拟合异或门,并且误差为0.
现在我们可以已经可以用感知机网络来完成任何可以用布尔函数表示的任务了。
基于梯度的学习
在实际的应用中,模型的参数可能有数十亿,不能通过直接指定参数来解决问题。基于梯度的优化方法可以找到一些参数使得模型产生的误差非常小(但并不一定为最优).
梯度下降法
这里可以浅显地再次回顾一下微积分概念与优化的联系。通常,我们把要最大化或者最小化的函数称为目标函数,并使用一个带上标*的\(\mathbf{x}^*\)表示最大或最小化的\(\mathbf{x}\)的值,记作\(\mathbf{x}^*=argmin_{\mathbf{x}} f(\mathbf{x})\)或\(\mathbf{x}^*=argmax_{\mathbf{x}} f(\mathbf{x})\)。
假设有一个函数\(y = f(x)\),其中\(x\)与\(y\)是实数,这个函数的导数记作\(f'(x)\),即\(f'(x)=\frac{d f}{d x}\)。导数\(f'(x)\)代表\(f(x)\)在\(x\)点的切线斜率。换句话说,它表明如何缩放输入的小变化才能在输出获得相应的变换:\(f(x+\epsilon) \approx f(x) + \epsilon f'(x)\)。
因此导数对于最小化一个函数很有用,它告诉我们如何更改\(x\)来略微改善\(y\)。例如说,我们知道对于足够小的\(\epsilon\)来说,\(f(x-\epsilon(f'(x)))\)的值比\(f(x)\)小,因此我们可以将\(x\)往导数的反方向移动一小步来减小\(f(x)\)的值。而这种技术也就称为梯度下降法(gradient descent)。
在深度学习的背景下,我们要优化的函数可能含有许多不是最优的全局极小点,或者还有很多处于非常平坦区域内的鞍点(即不是最小点也不是最大点的临界点,一阶导数也为0)。尤其在当输入是多维的时候,所有这些都将使优化变得困难。 我们通常寻找使\(f\)非常小的点,而这在任何形式意义下都并不一定是最小的点。
多维输入函数
通常我们经常最小化的是多维输入的函数\(f:\mathbb{R}^n\rightarrow\mathbb{R}\),为了使“最小化”有意义,其输出必须是一维的标量。
针对具有多维输入的函数,我们需要用到偏导数的概念。偏导数是\(\frac{\partial}{\partial
x_i}f(\mathbf{x})\)衡量点\(\mathbf{x}\)处只有在\(x_i\)增加时,\(f(\mathbf{x})\)的变换情况。梯度则是相对于一个向量求导的导数:\(f\)的导数是包含所有偏导数的向量,记作\(\nabla_x
f(\mathbf{x})\)。梯度的第i个元素是\(f\)关于\(\mathbf{x}\)的第i个分量的偏导数。多维情况下,临界点是梯度为\(\mathbf{0}\)的点。
在\(\mathbf{u}\)单位向量方向上的方向导数是函数\(f\)在\(\mathbf{u}\)方向的斜率。方向导数是函数\(f(\mathbf{x}+\alpha\mathbf{u})\)关于\(\alpha\)的导数(在\(\alpha=0\)时取得)。使用链式法则,当\(\alpha=0\)时有: \[ \frac{\partial }{\partial \alpha}f(\mathbf{x}+\alpha\mathbf{u})=\mathbf{u}^T\nabla_x f(\mathbf{x}) \]
为了最小化\(f(\mathbf{x})\),我们希望找到一个使得\(f(\mathbf{x})\)下降最快的方向,因此计算方向导数: \[ \underset{\mathbf{u},\mathbf{u}^T\mathbf{u}=1}{min} \mathbf{u}^T\nabla_x f(\mathbf{x})\\=\underset{\mathbf{u},\mathbf{u}^T\mathbf{u}=1}{min}\Vert\mathbf{u}\Vert_2\Vert\nabla_x f(\mathbf{x})\Vert_2cos\theta \] 这个公式代表着,我们在负梯度方向上移动可以减小\(f\),这被称为最速下降法(steepest descent)。即更新的式子为: \[ \mathbf{x}'=\mathbf{x}-\eta\nabla_x f(\mathbf{x}) \] 其中\(\eta\)是学习率(learning rate),它控制着更新的步长。普遍的方式是我们选择一个小常数作为\(\eta\),但有时可以通过计算,选择使方向导数消失的步长。
最速下降在梯度的每一个元素为零时收敛(或者很接近零时),但有时可以直接通过解方程\(\nabla_x f(\mathbf{x})=0\)来直接跳到临界点。
随机梯度下降法
根据前文,我们为了度量模型在整个数据集上的质量,以MSE计算为例,需要计算在训练集n个样本上的损失均值(也等价于求和):
\[ L(\mathbf{w},b) = \frac{1}{n}\sum^n_{i=1}l^{(i)}(\mathbf{w},b) = \frac{1}{n}\sum ^n_{i=1}\frac{1}{2}(\mathbf{w}^T\mathbf{x}^{(i)}+b-y^{(i)})^2 \]
我们希望找到一组参数
\((\mathbf{w}^*,b^*)\),这组参数能最小化在所有训练样本上的总损失。
梯度下降的最简单用法是计算损失函数(数据集中所有样本的损失均值,)关于模型参数的导数(在这里也可以称为梯度)。 但实际中的执行可能会非常慢:因为在每一次更新参数之前,我们必须遍历整个数据集。 因此,我们通常会在每次需要计算更新的时候随机抽取一小批样本, 这种变体叫做小批量随机梯度下降(minibatch stochastic gradient descent)。
在每次迭代中,我们首先随机抽样一个小批量\(\mathcal{B}\), 它是由固定数量的训练样本组成的。 然后,我们计算小批量的平均损失关于模型参数的导数(也可以称为梯度)。 最后,我们将梯度乘以一个预先确定的正数\(\eta\),并从当前参数的值中减掉。即:
\[ (\mathbf{w},b) \leftarrow (\mathbf{w},b)-\frac{\eta}{|\mathcal{B}|}\sum_{i\in \mathcal{B} }\partial_{(\mathbf{w},b)}\mathbf{l}^{(i)}(\mathbf{w},b) \]
公式中的\(\mathbf{w}\)和\(\mathbf{x}\)都是向量。 \(\mathcal{B}\)表示每个小批量中的样本数,这也称为批量大小(batch size)。 \(\eta\)表示学习率(learning rate)。 批量大小和学习率的值通常是手动预先指定,而不是通过模型训练得到的。 这些可以调整但不在训练过程中更新的参数称为超参数(hyperparameter)。
调参(hyperparameter tuning)是选择超参数的过程。 超参数通常是我们根据训练迭代结果来调整的, 而训练迭代结果是在独立的验证数据集(validation dataset)上评估得到的。
⚠️之所以称为随机梯度下降,就是因为每个批次的梯度在全部数据集的视角下是随机的,并不会像计算全部数据集时那样精确下降。
通用近似定理
如前文所述,增加上隐藏层的多层感知机可以捕捉到输入之间的复杂的相互作用,这些神经元依赖于每个输入的值。我们可以很容易地设计隐藏节点来执行任意计算。 即使是网络只有一个隐藏层,给定足够的神经元和正确的权重, 我们可以对任意函数建模,尽管实际中学习该函数是很困难的。
而且,虽然一个单隐层网络能学习任何函数, 但并不意味着我们应该尝试使用单隐藏层网络来解决所有问题。 事实上,通过使用更深(而不是更广)的网络,我们可以更容易地逼近许多函数。
激活函数
ReLu
前文已经提到,使用ReLU的原因是,它求导表现得特别好:要么让参数消失,要么让参数通过。 这使得优化表现得更好,并且ReLU减轻了困扰以往神经网络的梯度消失问题。

注意,ReLU函数有许多变体,包括参数化ReLU(Parameterized ReLU,pReLU) 函数 (He et al., 2015)。 该变体为ReLU添加了一个线性项,因此即使参数是负的,某些信息仍然可以通过:
\[ pRELU(x) =max(0,x)+\alpha min(0,x) \]
sigmoid函数
对于一个定义域\(\mathbb{R}\)在中的输入, sigmoid函数将输入变换为区间(0, 1)上的输出。 因此,sigmoid通常称为挤压函数(squashing function): 它将范围(-inf, inf)中的任意输入压缩到区间(0, 1)中的某个值:
\[ sigmoid(x)=\frac{1}{1+exp(-x)} \]
sigmoid函数是一个自然的选择,因为它是一个平滑的、可微的阈值单元近似。 当我们想要将输出视作二元分类问题的概率时,sigmoid仍然被广泛用作输出单元上的激活函数 (sigmoid可以视为softmax的特例)。 然而,sigmoid在隐藏层中已经较少使用,它在大部分时候被更简单、更容易训练的ReLU所取代。

sigmoid函数的导数为:
\[ sigmoid(x)(1-sigmoid(x)) \]
tanh函数
与sigmoid函数类似, tanh(双曲正切)函数也能将其输入压缩转换到区间(-1, 1)上。 tanh函数的公式如下:
\[ tanh(x)=\frac{1-exp(-2x)}{1+exp(-2x)} \]
tanh函数的导数为:
\[ \frac{d}{d x}tanh(x)=1-tanh(x)^2 \]
正则化技术
们总是可以通过去收集更多的训练数据来缓解过拟合。但这可能成本很高,耗时颇多,或者完全超出我们的控制,因而在短期内不可能做到。
训练参数化机器学习模型时, 权重衰减(weight decay)是最广泛使用的正则化的技术之一, 它通常也被称为\(L_2\)正则化。 这项技术通过函数与零的距离来衡量函数的复杂度, 因为在所有函数\(f\)中,函数\(f=0\)(所有输入都得到值\(0\))在某种意义上是最简单的。但是我们应该如何精确地测量一个函数和零之间的距离呢? 没有一个正确的答案。
一种简单的方法是通过线性函数\(f(\mathbf{x})=\mathbf{w}^T\mathbf{x}\)中的权重向量的某个范数来度量其复杂性, 例如\(||\mathbf{w}||^2\)。 要保证权重向量比较小, 最常用方法是将其范数作为惩罚项加到最小化损失的问题中。 将原来的训练目标最小化训练标签上的预测损失, 调整为最小化预测损失和惩罚项之和。 现在,如果我们的权重向量增长的太大, 我们的学习算法可能会更集中于最小化权重范数\(||\mathbf{w}||^2\)。
通过正则化常数\(\lambda\)来平衡额外惩罚的损失,也即:
\[ L(\mathbf{w},b)+\frac{\lambda}{2}||\mathbf{w}||^2 \]
什么在这里我们使用平方范数而不是标准范数(即欧几里得距离)?这样做是为了便于计算。 通过平方\(L_2\)范数,我们去掉平方根,留下权重向量每个分量的平方和。 这使得惩罚的导数很容易计算:导数的和等于和的导数。
\(L_2\)正则化线性模型构成经典的岭回归(ridge regression)算法, \(L_1\)正则化线性回归是统计学中类似的基本模型, 通常被称为套索回归(lasso regression)。
- 使用\(L_2\)范数的一个原因是它对权重向量的大分量施加了巨大的惩罚。 这使得我们的学习算法偏向于在大量特征上均匀分布权重的模型。 在实践中,这可能使它们对单个变量中的观测误差更为稳定。
- 相比之下,\(L_1\)惩罚会导致模型将权重集中在一小部分特征上, 而将其他权重清除为零。 这称为特征选择(feature selection),这可能是其他场景下需要的。
加上正则化技术之后的小批量随机梯度下降更新公式为:
\[ \mathbf{w}\leftarrow (1-\eta\lambda)\mathbf{w}-\frac{\eta}{|\mathcal{B}|}\sum_{i\in \mathcal{B} }\mathbf{x}^{(i)}(\mathbf{w}^T\mathbf{x}^{(i)}+b-y^{(i)}) \]
Dropout
当面对更多的特征而样本不足时,线性模型往往会过拟合。 相反,当给出更多样本而不是特征,通常线性模型不会过拟合。 不幸的是,线性模型泛化的可靠性是有代价的。 简单地说,线性模型没有考虑到特征之间的交互作用。 对于每个特征,线性模型必须指定正的或负的权重,而忽略其他特征。
泛化性和灵活性之间的这种基本权衡被描述为偏差-方差权衡(bias-variance tradeoff)。 线性模型有很高的偏差:它们只能表示一小类函数。 然而,这些模型的方差很低:它们在不同的随机数据样本上可以得出相似的结果。
深度神经网络位于偏差-方差的另一端。 与线性模型不同,神经网络并不局限于单独查看每个特征,而是学习特征之间的交互。 即使我们有比特征多得多的样本,深度神经网络也有可能过拟合。
我们期待“好”的预测模型能在未知的数据上有很好的表现: 经典泛化理论认为,为了缩小训练和测试性能之间的差距,应该以简单的模型为目标。
简单性以较小维度的形式展现,
简单性的另一个角度是平滑性,即函数不应该对其输入的微小变化敏感。
在训练过程中,如果在计算后续层之前向网络的每一层注入噪声,那么当训练一个有多层的深层网络时,注入噪声会在输入-输出映射上增强平滑性。这个想法被称为暂退法(dropout)。
暂退法在前向传播过程中,计算每一内部层的同时注入噪声,这已经成为训练神经网络的常用技术。
这种方法之所以被称为暂退法,因为我们从表面上看是在训练过程中丢弃(drop
out)一些神经元。
在整个训练过程的每一次迭代中,标准暂退法包括在计算下一层之前将当前层中的一些节点置零。
:warning:需要说明的是,暂退法的原始论文提到了一个关于有性繁殖的类比: 神经网络过拟合与每一层都依赖于前一层激活值相关,称这种情况为“共适应性”。 作者认为,暂退法会破坏共适应性,就像有性生殖会破坏共适应的基因一样。
那么关键的挑战就是如何注入这种噪声。 一种想法是以一种无偏向(unbiased)的方式注入噪声。 这样在固定住其他层时,每一层的期望值等于没有噪音时的值。
在标准暂退法正则化中,通过按照保留(未丢弃)的节点的分数进行规范化来消除每一层的偏差。换言之,每个中间活性值\(h\)以暂退概率\(p\)由随机变量\(h'\)替换 \[h' = \begin{cases} 0 & 概率为0 \\ \frac{h}{1-p} & 其他情况 \end{cases} \] 其期望值保持不变,即\(E[h'] =h\)