基本概念
神经元
神经网络中最基本的单元是神经元模型(neuron)
由两部分组成,前一部分计算总输入值(即输入信号的加权和),后一部分先计算总输入值与该神经元阈值的差值,然后通过激活函数(activation function)的处理,产生输出从轴突传送给其它神经元。M-P神经元模型如下图所示:
激活函数
神经元模型最理想的激活函数也是阶跃函数,即将神经元输入值与阈值的差值映射为输出值1或0,若差值大于零输出1,对应兴奋;若差值小于零则输出0,对应抑制。但阶跃函数不连续,不光滑,故在M-P神经元模型中,也采用Sigmoid函数来近似, Sigmoid函数将较大范围内变化的输入值挤压到 (0,1) 输出值范围内。
神经网络
多层神经网络的拓扑结构如下图所示:
在神经网络中,输入层与输出层之间的层称为隐含层或隐层(hidden layer),隐层和输出层的神经元都是具有激活函数的功能神经元。
只需包含一个隐层便可以称为多层神经网络,常用的神经网络称为“多层前馈神经网络”(multi-layer feedforward neural network),该结构满足以下几个特点:
- 每层神经元与下一层神经元之间完全互连
- 神经元之间不存在同层连接
- 神经元之间不存在跨层连接
这里的“前馈”指的是网络拓扑结构中不存在环或回路,而不是指该网络只能向前传播而不能向后传播
感知机相关
感知机(Perceptron)是由两层神经元组成的一个简单模型,但只有输出层是M-P神经元,即只有输出层神经元进行激活函数处理,也称为功能神经元(functional neuron);输入层只是接受外界信号(样本属性)并传递给输出层(输入层的神经元个数等于样本的属性数目),而没有激活函数。通过对属性加权与另一个常数求和,再使用sigmoid函数将这个输出值压缩到0-1之间,从而解决分类问题。感知机的输出层应该可以有多个神经元,从而可以实现多分类问题。
利用TensorFlow实现简单的3层网络(激活函数sigmoid)
激活函数的种类
激活函数的作用
激活函数(activation function)层又称非线性映射 (non-linearity mapping) 层,作用是增加整个网络的非线性(即抽象能力)。
如果不用激活函数(相当于激活函数是 $f(x) = x$),这种情况下每一层节点的输入都是上层输出的线性函数,那么无论神经网络有多少层,输出都是输入的线性组合,与没有隐藏层效果相当,这种情况就是最原始的感知机(Perceptron),网络的逼近能力相当有限。
因此,需要引入非线性函数作为激励函数,这样深层神经网络表达能力就更加强大,近乎可以逼近任意函数。
常用的激活函数
$Sigmoid$函数
也叫逻辑激活函数,如图所示:
导数:$g^\prime(z) = a(1-a)$
优点:能够很好的表达“激活”的意思,未激活就是0,完全饱和的激活则是1。
缺点:
- Sigmoid容易饱和,并且当输入非常大或者非常小的时候,神经元的梯度接近于0。这使得在反向传播算法中反向传播接近于0的梯度,导致权重基本没有更新,无法递归地学习输入数据,即梯度消失。
- Sigmoid 的输出不是0均值的。这是我们不希望的,因为这会导致后层的神经元的输入是非0均值的信号,这会对梯度产生影响。假设后层神经元的输入都为正,那么对w求局部梯度则都为正,这样在反向传播的过程中w要么都往正方向更新,要么都往负方向更新,导致有一种捆绑的效果,使得收敛缓慢。
$tanh$函数
$tanh$是双曲函数中的一种,又名“双曲正切”,如图所示:
$tanh: a=\frac{e^z-e^{-z}}{e^z+e^{-z}}$
导数:$g^\prime(z) = 1-a^2$
$tan(h)$相当于把$sigmoid$平移到以原点为中心,然后再缩放到 $−1\leq a\leq 1$ 的范围。
优点:使用$tan(h)$能够中心化数据,使数据的均值接近0而不是像0.5这样的值。这会使得下一层的学习变得简单一点。
缺点:本质上依然是Sigmoid函数,还是存在梯度饱和的问题。
$Relu$函数
$Relu: a=max(0,z)$
导数:
优点:
- 使用 $ReLU$ 得到的$SGD$的收敛速度会比 $sigmoid$/$tanh$ 快很多。
- 相比于 $sigmoid$/$tanh$ 需要计算指数等,计算复杂度高,$ReLU$ 只需要一个阈值就可以得到激活值。
缺点:$ReLU$在训练的时候很”脆弱”,一不小心有可能导致神经元”坏死”。由于$ReLU$在 $x < 0$ 时梯度为$0$,这就导致负的梯度在这个$ReLU$被置零,而且这个神经元有可能再也不会被任何数据激活。这个神经元之后的梯度就永远是0了,不再对任何数据有所响应。
深度学习中的正则化
正则化
正则化是选择模型的一种方法。正则化的作用是选择经验风险与模型复杂度同时较小的模型来避免过拟合。
常见的正则化策略
L1正则化
L1正则也被称为Lasso回归。就是在原始的代价函数后面加上一个L1正则化项,即所有权重$w$的绝对值的和,乘以$\frac{\lambda}{n}$。
求导:
$sgn(w)$表示$w$的符号,$w$的更新规则为:
当$w$为正时,更新后的$w$变小。当$w$为负时,更新后的$w$变大。因此,它的效果就是让$w$往0靠,使网络中的权重尽可能为0,也就相当于减小了网络复杂度,防止过拟合。
当w等于0时,$|w|$是不可导的,所以我们只能按照原始的未经正则化的方法去更新$w$,这就相当于去掉$\frac{\eta \lambda}{n}sgn(w)$这一项,可以规定$sgn(0)=0$,这样就把$w=0$的情况也统一进来了。
L2正则化
L2正则化也被称为权重衰减或岭回归。就是在代价函数后面再加上一个正则化项。
$C_0$代表原始的代价函数,后面那一项就是L2正则化项,是所有参数$w$的平方的和,除以训练集的样本大小$n$。$\lambda$就是正则项系数,权衡正则项与$C_0$项的比重。
求导:
可以发现L2正则化项对$b$的更新没有影响,但是对于$w$的更新有影响:
在不使用L2正则化时,求导结果中$w$前系数为1,现在$w$前面系数为 $1- \frac{\eta \lambda}{n}$ ,因为$\eta$、$\lambda$、$n$都是正的,所以 $1- \frac{\eta \lambda}{n}$ 小于1,它的效果是减小$w$,这也就是权重衰减(weight decay)的由来。当然考虑到后面的导数项,$w$最终的值可能增大也可能减小。
一个所谓“显而易见”的解释就是:更小的权值$w$,从某种意义上说,表示网络的复杂度更低,对数据的拟合刚刚好(这个法则也叫做奥卡姆剃刀)。在实际应用中,L2正则化的效果往往好于未经正则化的效果。
过拟合的时候,拟合函数的系数往往非常大,需要顾忌每一个点,最终形成的拟合函数波动很大。在某些很小的区间里,函数值的变化很剧烈。这就意味着函数在某些小区间里的导数值(绝对值)非常大,由于自变量值可大可小,所以只有系数足够大,才能保证导数值很大。而正则化是通过约束参数的范数使其不要太大,所以可以在一定程度上减少过拟合情况。
噪声注入
模型容易过拟合的原因之一就是没有太好的抗噪能力,如果输入数据稍微改变一点点,就可能得到完全不一样的结果。
最简单提高网络抗噪能力的方法,就是在训练中加入随机噪声一起训练。我们可以在网络的不同位置加入噪声:输入层,隐藏层,输出层。
数据集增强在某种意义上也能看做是在输入层加入噪声,通过随机旋转、翻转,色彩变换,裁剪等操作人工扩充训练集大小。这可以使得网络对于输入更加鲁棒。Dropout方法属于在隐藏层中加入噪声的一种方式。
对于输出层可能标记不一定完全正确的情况,可以通过标签平滑,把确切的分类目标从0和1替换成$\frac{\epsilon}{k−1}$和$1−\epsilon$,正则化具有$k$个输出的$softmax$函数的模型。标签平滑的优势是能够防止模型追求确切的概率而不能学习正确分类。
提前终止
模型过拟合一般是发生在训练次数过多的情况下,那么只要在过拟合之前停止训练即可。
这是深度学习中最常用的正则化形式,主要是因为它的有效性和简单性。提前终止需要验证集损失作为观测指标,当验证集损失开始持续上升时,这时就该停止训练过程了。
稀疏表示
另一种策略是惩罚网络中的激活单元,稀疏化激活单元,从而间接地对模型参数施加了复杂惩罚。
上面第一个表达式是对参数进行惩罚的例子,第二个是对激活函数进行惩罚的例子。含有隐藏单元的模型在本质上都能变得稀疏。
Dropout
Dropout可以作为训练深度神经网络的一种trick供选择。在每个训练批次中,通过忽略一半的特征检测器(让一半的隐层节点值为0),可以明显地减少过拟合现象。这种方式可以减少特征检测器(隐层节点)间的相互作用,检测器相互作用是指某些检测器依赖其他检测器才能发挥作用。
简单点看就是:在前向传播时,让某个神经元的激活值以一定的概率$p$停止工作,这样可以使模型泛化性更强,因为它不会太依赖某些局部的特征,如图所示。
第一种理解方式是,在每次训练的时候使用dropout,每个神经元有百分之50的概率被移除,这样可以使得一个神经元的训练不依赖于另外一个神经元,同样也就使得特征之间的协同作用被减弱。Hinton认为,过拟合可以通过阻止某些特征的协同作用来缓解。
第二种理解方式是,我们可以把dropout当做一种多模型效果平均的方式。对于减少测试集中的错误,我们可以将多个不同神经网络的预测结果取平均,而因为dropout的随机性,我们每次dropout后,网络模型都可以看成是一个不同结构的神经网络,而此时要训练的参数数目却是不变的,这就解脱了训练多个独立的不同神经网络的时耗问题。在测试输出的时候,将输出权重除以二,从而达到类似平均的效果。
需要注意的是如果采用dropout,训练时间大大延长,但是对测试阶段没影响。
深度模型中的优化
参数初始化策略
为了让神经网络在训练过程中学习到有用的信息,需要参数更新时的梯度不为0。在一般的全连接网络中,参数更新的梯度和反向传播得到的状态梯度以及输入激活值有关。那么参数初始化应该满足以下两个条件:
- 初始化必要条件一:各层激活值不会出现饱和现象(对于$sigmoid$,$tanh$);
- 初始化必要条件二:各层激活值不为0。
全零初始化 (Zero Initialization)
将网络中所有参数初始化为 0 。
全零初始化方法在前向传播过程中会使得隐层神经元的激活值均未0,在反向过程中根据BP公式,不同维度的参数会得到相同的更新。
所以这是不可取的。
随机初始化 (Random Initialization)
将参数值(通过高斯分布或均匀分布)随机初始化为 接近0的 一个很小的随机数(有正有负),从而使对称失效。1
2
3
4# node_in 、 node_out 表示 输入神经元个数 、输出神经元个数
# np.random.randn(node_in, node_out) 输出 服从标准正态分布的node_in × node_out矩阵
# 控制因子:0.001 ,保证参数期望接近0
W = tf.Variable(np.random.randn(node_in, node_out)) * 0.001
一旦随机分布选择不当,就会导致网络优化陷入困境。
Xavier初始化 (Xavier Initialization)
1 | # 方差规范化: / np.sqrt(node_in) ,维持了 输入、输出数据分布方差的一致性,从而更快地收敛。 |
He初始化 (He Initialization)
1 | # 非线性映射 (relu) 函数 的影响, 将 方差规范化 的 分母 修改为 / np.sqrt(node_in/2) ,能让Relu网络更快地收敛。 |
迁移学习初始化 (Pre-train Initialization)
将 预训练模型的参数 作为新任务上的初始化参数。
数据敏感初始化
根据自身任务数据集而特别定制的参数初始化方法。
自适应学习率算法
SGD
SGD指mini-batch gradient descent,就是每一次迭代计算mini-batch的梯度,然后对参数进行更新,是最常见的优化方法。
其中,$\eta$是学习率,$g_t$是梯度。
SGD完全依赖于当前batch的梯度,所以$\eta$可理解为允许当前batch的梯度多大程度影响参数更新。
缺点:
- 选择合适的 learning rate 比较困难
- 对所有的参数更新使用同样的 learning rate。对于稀疏数据或者不经常出现的特征,想更新快一些,对于常出现的特征更新慢一些,这时候SGD就不太能满足要求。
- SGD容易收敛到局部最优,在某些情况下可能被困在鞍点
AdaGrad
AdaGrad 其实是对学习率进行了一个约束。即:
此处,对$g_t$从1到$t$进行一个递推形成一个约束项 regularizer,$-\frac{1}{\sqrt{\sum_{r-1}^t(g_t)^2+\epsilon}}$,$\epsilon$用来保证分母非0。
优点
- 前期$g_t$较小的时候,regularizer较大,能够放大梯度
- 后期$g_t$较大的时候,regularizer较小,能够约束梯度
- 适合处理稀疏梯度
缺点:
- 依赖于人工设置一个全局学习率
- $\eta$ 设置过大的话,会使 regularizer 过于敏感,对梯度的调节太大
- 中后期,分母上梯度平方的累加将会越来越大,使梯度趋向于0,使得训练提前结束
Adadelta
Adadelta 是对 Adagrad 的扩展,最初方案依然是对学习率进行自适应约束,但是进行了计算上的简化。Adagrad 会累加之前所有的梯度平方,而 Adadelta 只累加固定大小的项,并且也不直接存储这些项,仅仅是近似计算对应的平均值。即:
在此处 Adadelta 其实还是依赖于全局学习率,但是作者做了一定处理,经过近似牛顿迭代法之后:
其中,$E$代表求期望。
此时,可以看出Adadelta已经不用依赖于全局学习率了。
特点:
- 训练初中期,加速效果不错,很快
- 训练后期,反复在局部最小值附近抖动
RMSProp
RMSprop 可以算作 Adadelta 的一个特例:
当$\rho=0.5$时,$E|g^2|_t = \rhoE|g^2|_{t-1}+(1-\rho)g_t^2 $就变为了求梯度平方和的平均数。
如果再求根的话,就变成了RMS(均方根):
此时,这个RMS就可以作为学习率ηη的一个约束:
特点:
- RMSprop 依然依赖于全局学习率
- RMSprop 算是 Adagrad 的一种发展,和 Adadelta 的变体,效果趋于二者之间
- 适合处理非平稳目标
- 对于RNN效果很好
Adam
Adam(Adaptive Moment Estimation)本质上是带有动量项的RMSprop,它利用梯度的一阶矩估计和二阶矩估计动态调整每个参数的学习率。Adam的优点主要在于经过偏置校正后,每一次迭代学习率都有个确定范围,使得参数比较平稳。公式如下:
其中,$m_t$,$n_t$分别是对梯度的一阶矩估计和二阶矩估计,可以看作对期望$E|g_t|$,$E|g_t^2|$的估计;$\hat{m_t}$,$\hat{n_t}$是对$m_t$,$n_t$的校正,这样可以近似为对期望的无偏估计。
可以看出,直接对梯度的矩估计对内存没有额外的要求,而且可以根据梯度进行动态调整,而$-\frac{\hat{m_t}}{\sqrt{\hat{n_t}}+\epsilon}$对学习率形成一个动态约束,而且有明确的范围。
特点:
- 结合了 Adagrad 善于处理稀疏梯度和 RMSprop 善于处理非平稳目标的优点
- 对内存需求较小
- 为不同的参数计算不同的自适应学习率
- 也适用于大多非凸优化
- 适用于大数据集和高维空间
优化算法的选择
- 对于稀疏数据,尽量使用学习率可自适应的优化方法,不用手动调节,而且最好采用默认值
- SGD通常训练时间更长,容易陷入鞍点,但是在好的初始化和学习率调度方案的情况下,结果更可靠
- 如果在意更快的收敛,并且需要训练较深较复杂的网络时,推荐使用学习率自适应的优化方法。
- Adadelta,RMSprop,Adam是比较相近的算法,在相似的情况下表现差不多。
- 在想使用带动量的RMSprop,或者Adam的地方,大多可以使用Nadam取得更好的效果
Batch normalization
将输入值进行归一化操作之后只是加快了第一层网络的速度,并不能对后边的n层网络产生影响。
将每一层前的输入包括输入层和隐藏层均进行减去均值除以方差的操作,我们叫做Batch normalization。
因为深层神经网络在做非线性变换前的激活输入值随着网络深度加深或者在训练过程中,其分布逐渐发生偏移或者变动,之所以训练收敛慢,一般是整体分布逐渐往非线性函数的取值区间的上下限两端靠近(对于Sigmoid函数来说,意味着激活输入值是大的负值或正值),所以这导致反向传播时低层神经网络的梯度消失,这是训练深层神经网络收敛越来越慢的本质原因,而BN就是通过一定的规范化手段,把每层神经网络任意神经元这个输入值的分布强行拉回到均值为0方差为1的标准正态分布,其实就是把越来越偏的分布强制拉回比较标准的分布,这样使得激活输入值落在非线性函数对输入比较敏感的区域,这样输入的小变化就会导致损失函数较大的变化,这样让梯度变大,避免梯度消失问题产生,而且梯度变大意味着学习收敛速度快,能大大加快训练速度。
对于前馈神经网络第$l$隐层,神经元的输入为$a$, 激活函数为$f$, 激活函数输出为$h$。权值$w$通过 SGD学习得到。 如下面的公式所示:
对于每个隐层的神经元,我们在所有的训练样本上归一化该神经元的输入。
在整个训练样本上计算均值方差,然后对神经元的输入进行归一化。
优点
- 不仅仅极大提升了训练速度,收敛过程大大加快
- 增加分类效果,一种解释是这是类似于Dropout的一种防止过拟合的正则化表达方式,所以不用Dropout也能达到相当的效果
- 调参过程也简单多了,对于初始化要求不高,且可以使用大的学习率
Layer normalization
注意到一层输出的改变会产生下一层输入的高相关性改变,特别是当使用 ReLU,其输出改变很大。那么我们可以通过固定一层神经元的输入均值和方差来降低 covariate shift 的影响。
上面公式中 H 是 一层神经元的个数。这里一层网络 共享一个均值和方差,不同训练样本对应不同的均值和方差,这是和 Batch normalization 的最大区别。
- Layer normalization 对于recurrent neural networks 的帮助最大。
- Layer normalization 对于 Convolutional Networks 作用不是很大,后续研究可以提高其作用。