Skip to content

Latest commit

 

History

History
357 lines (199 loc) · 46.9 KB

深度学习中的优化.md

File metadata and controls

357 lines (199 loc) · 46.9 KB

深度学习中的优化

引言

   机器学习中的算法涉及诸多的优化问题,典型的就是利用梯度下降法(gradient descent)求使损失函数$J(\theta)$下降的模型参数 $\theta$。在深度学习,尤其是深度神经网络的训练和预测中,大的模型往往要花上数天甚至是数月的训练时间,因此虽然模型的优化费事费力,仍然是一个高回报的步骤, 因为好的模型和优化方法可以极大的加速深度学习模型的训练。在本章中,将主要介绍神经网络优化这一特定问题:寻找神经网络的参数 $\theta$,可以显著的降低由训练集误差项和正则化项组成的代价函数 $J(\theta)$

本章结构

  • 深度学习优化与纯优化的异同
  • 优化神经网络的挑战
  • 神经网络优化的基本算法
  • 优化参数初始化
  • 自适应学习率优化
  • 二阶近似的利用
  • 优化策略和元算法

1. 深度学习优化与纯优化的异同

   在机器学习的问题中,我们关心的是算法的性能度量 $P$, 它通常定义于测试集之上而且往往是不可解的,因此,我们采取的策略是间接地优化 $P$ :我们希望通过降低代价函数 $J(\theta)$ 的方式来提高 $P$。而传统的纯优化就是单纯的优化目标 $J$ 本身。深度学习中,代价函数通常用训练集上损失函数的期望或者平均(eqn 1.1):

$$J(\theta)=E_{(x,y)\sim \hat{p}_{data}} L(f(\boldsymbol{x}; \boldsymbol{\theta}), y) $$

其中,$L$ 是每个样本的损失函数(loss function),$f(\boldsymbol{x}; \boldsymbol{\theta})$ 是输入$x$时候的预测的输出,$\hat{p}_{data}$ 是样本的经验分布,在监督学习中,$y$ 是目标输出。上式定义了训练集上的目标函数,而我们真正希望最小化的是基于数据生成分布 $ p_{data}$ 的期望(eqn 1.2):

$$J^*(\theta)=E_{(x,y)\sim p_{data}} L(f(\boldsymbol{x}; \boldsymbol{\theta}), y) $$

1.1 经验风险的最小化

   机器学习算法真正目标是想降低 (eqn 1.2) 方程表示的期望误差,这样的一个量也叫做风险(risk)。需要注意的是,该式所计算的值是取自数据的真实潜在分布 $p_{data}$ 。如果我们知道了真实的数据分布,那么这将变成一个可以被优化算法优化的问题。但是,通常我们只有训练集的样本。因此,机器学习优化问题在这里可以转化为求最小化训练集上的误差期望,也就是说用已知的经验分布 $\hat{p}_{data}$ 代替 未知的真实分布 $p_{data}$。这样一来,我们转为最小化经验风险(emperical risk)(eqn 1.3):

$$ E_{(x,y) \sim \hat{p}{data} } L(f(\boldsymbol{x}; \boldsymbol{\theta}), y)=\frac{1}{m}\sum{i=1}^{m} L(f(\boldsymbol{x^{(i)}}; \boldsymbol{\theta}), y^{(i)})$$

上式中 $m$ 表示训练样本的数目。基于这种评价训练误差的训练过程被称为经验风险最小化(empirical risk minimization)。总结起来,我们并没有直接优化风险,而是优化经验风险,希望能够很大程度上的降低风险。单纯依靠最小化经验风险可能导致过拟合现象,而且在很多的情形下,减小经验风险并不可行,所以在深度学习中,我们很少使用经验风险最小化,而使用另外一不同的方法。

1.2 代理损失函数和提前终止

   有时候我们的真正损失函数,比如 0-1 分类误差并无法被有效的优化,此时我们会使用代理损失函数(surrogate loss function)来作为原来目标的替代,而且会带来好处。比如,正确分类类别的负对数似然通常用作 0-1 损失的替代,。负对数似然允许模型估计给定样本的类别的条件概率,能够输出期望最小分类误差所对应的类型。有些情况下,代理损失函数可以比原损失函数学到更多的东西,比如对数似然代替 0-1 分类误差函数时,当训练集上的误差达到0之后,测试集上的误差还可以持续下降,也就是说此时模型可以继续学习以拉开不同类别直接的距离以提高分类的鲁棒性。也就是说,代理损失函数从训练数据中学到了更多的东西。

   另外一个一般优化算法和机器学习训练算法不同的地方是,训练算法不会停在局部极小值点,而且通常机器学习的算法会提前设置终止条件,当条件满足时(通常在过拟合之前)算法就停止,虽然此时代理损失函数仍然有较大的导数。对于纯优化来说,终止时导数非常小。

1.3 批量算法和小批量算法

   机器学习算法的目标函数通常可以分解为训练样本上的求和。机器学习中的优化算法在计算参数的每一次更新时,通常仅仅使用整个代价函数的一部分项来估计代价函数的期望值。例如(eqn 1.4):

$$\theta_{ML}=\underset{\theta} {argmax} \sum_{1}^{m} L(f(\boldsymbol{x^{(i)}}; \boldsymbol{\theta}), y^{(i)})$$

最大化这个总和等价于最大化训练集在经验分布上的期望(eqn 1.5):

$$J(\theta)=E_{(x,y)\sim \hat{p}{data} } log{p_{model}}(\boldsymbol{x}, y; \boldsymbol{\theta}) $$

优化算法用到的目标函数 $J$ 中用到的大多数属性也是训练集上的期望。例如常用的梯度期望(eqn 1.6):

$$ \nabla_{\theta} J(\theta)=E_{(x,y)\sim \hat{p}{data}} \nabla{\theta} log_{p_{model}} (\boldsymbol{x}, y; \boldsymbol{\theta}) $$

   准确的计算这个期望的代价非常大,因为需要在训练集的每个数据上进行以上的计算,计算量非常大。在真正的实践中,我们通常会随机采样少量的样本,然后计算这些样本上的平均值。简单的论证如下:$n$ 个样本的平均值的方差是 $\frac{\sigma}{\sqrt{n}}$,其中 $\sigma$ 是样本真实的标准差。这个公式表明,当样本量增大100倍时,相应地只能得到10倍的误差减小,也就是说回报是低于线性的。如果能够快速的计算出梯度的估计值,而不是缓慢的计算所有梯度的准确值,大多数算法会收敛的更快。另外,训练集的冗余也使得我们考虑使用小数目的部分样本进行模型训练。

   使用整个训练集的优化方法被称为批量(batch) 或确定性(deterministic)梯度算法,他们会在每次更新参数时计算所有样本。通常,“批量梯度下降”指使用全部训练集,而“批量”单独出现时,指一组样本。每次只使用部分样本的方法被称为随机(stochastic)或者在线(online)算法。在线通常是指从连续产生的数据流(stream)中提取样本,而不是从一个固定大小的样本中遍历多次采样的情形。大多数深度学习算法介于两者之间,使用一个以上但不是全部的训练样本,传统上称这种方法为小批量(minibatch)或者小批量随机(minibatch stochastic)方法,现在统称为随机(stochastic)方法。

  小批量大小由以下因素决定:

  • 更大批量产生更精准梯度,但是回报低于线性
  • 太小的批量无法充分利用多核架构
  • 如果可并行处理,那么内存消耗和批量大小成正比
  • 特定大小数组运行在某些硬件上,运行更快
  • 小批量引入噪声,具有正则化的效果

  不同的算法使用不同的方法从小批量中提取信息,有些表现好,有些表现不好,原因可能是无法在小批量上面获取有用信息,或者是放大了小批量上面的误差噪声。

  小批量是随机抽取的这一点非常重要,以防连续遍历时数据之间的相关性导致梯度估计的相关性。从一组样本中计算出梯度期望的无偏估计要求这些样本之间是互相独立的。通常的做法是在训练之前,将数据随机打乱一次后,再按照顺序一直往下取 minibatch 就可以了。每个独立的模型每次遍历数据时都会使用我们已经提前打乱的数据,这种理论上偏离随机采样的方法并没有给模型训练带来怎样的影响。 小批量随机梯度下降的一个有趣的事实是,只要没有重复使用样本,它将遵循着真实泛化误差的梯度。在线学习最好体现了随机梯度下降是最小泛化误差的原因:所有数据都是新的独立产生的,而不是原来固定大小的训练集,这种情况下每次更新的样本是直接从分布 $p_{data}$ 中采样获得的无偏样本。

  除非数据量非常之大,通常最好是多次遍历数据集,如果只在第一次随机打乱数据顺序后不再改变顺序,只有第一次遍历数据符合泛化误差梯度的无偏估计,额外的训练仍然可以继续减小训练误差而获得好处。随着数据集的不断增大,往往一个数据只会使用一次,甚至只是使用部分训练集,这时过拟合不再是问题,欠拟合和计算效率成为我们需要考虑的问题。

2. 优化神经网络的挑战

   优化通常是一个非常困难的任务,传统的机器学习会想办法设计目标函数和约束,使得目标函数是凸函数,从而避免优化过程中出现非凸函数的问题。然而,即使是凸函数,也会遇到优化的问题,本节将主要探讨深度学习优化问题中会遇到的挑战。

2.1 病态情况

   在优化凸函数的时候,会遇到Hessian矩阵 $\boldsymbol{\textit{H}}$ 病态的情况。病态情况一般被认为广泛存在于神经网络的训练过程中,体现在随机梯度下降会“卡”在某些特殊的情况,此时即使很小的更新步长也会增加代价函数。回顾之前的代价函数的二阶泰勒展开预测梯度下降的 $-\epsilon\boldsymbol{\textit{g}}$ 会增加:

$$\frac{1}{2}\epsilon^2\boldsymbol{\textit{g}}^{\top}\boldsymbol{\textit{H}}\boldsymbol{\textit{g}}-\epsilon\boldsymbol{\textit{g}}^{\top}\boldsymbol{\textit{g}}$$

$\frac{1}{2}\epsilon^2\boldsymbol{\textit{g}}^{\top}\boldsymbol{\textit{H}}\boldsymbol{\textit{g}}$ 超过 $\epsilon\boldsymbol{\textit{g}}^{\top}\boldsymbol{\textit{g}}$时,梯度的病态会成为问题,很多情况下,梯度的范数不会再训练中显著的减小,但是$\boldsymbol{\textit{g}}^{\top}\boldsymbol{\textit{H}}\boldsymbol{\textit{g}}$ 的增长会超过一个数量级。结果是,尽管梯度很强,但是学习率必须收缩以弥补更强的曲率,因此学习变得非常缓慢。 有些适合于解决其他情况中的病态的技术并不适用于深度神经网络。比如牛顿法在解决带有病态条件的Hessian矩阵的凸优化问题时,是有效的方法,但是运用到神经网络时需要很大的改动。

2.2 局部极小值

   在凸优化问题中,优化问题可以简化为寻找一个局部极小值点,因为任何的局部极小值就是全局最小值。虽然有些凸函数底部是一个很大的平坦区域,并非单一的极值点,但是应用过程中实际上该区域中每一个极小值点都是一个可以接受的点。所以说,对于凸优化问题来说,找到任何形式的临界点,就是找到了一个不错的可行解。而对于非凸函数问题,比如神经网络问题,可能会存在很多的局部极小值点。

  如果一个足够大的模型可以唯一确定一组模型的参数,那么我们说该模型是可以辨认的。带有潜变量的模型往往是不可辨认的,因为互相交换潜变量,可以得到等价的模型。神经网络代价函数具有非常多甚至是无限多的局部极小值点,而且,由于不可辨识性问题而产生的局部极小值都有相同的代价函数,因此局部极小值点并非是非凸带来的问题。如果局部极小值点相比全局最小值点有很大的代价,那么局部极小值点会带来很大的问题。对于实际使用的神经网络,是否存在很多代价很大的局部极小值点,优化算法是否会碰到这些极小值点都是尚未解决的公开问题。学者们现在猜想,对于足够大的神经网络而言,大部分局部极小值都具有很小的代价函数,我们能不能找到全局最小点并不重要,重要的是能够在参数空间里找到一个代价很小的可以接受的点。

2.3 高原。鞍点和平坦区域

  很多高维非凸函数而言,局部极值远少于另一类梯度为零的点:鞍点。在一个鞍点附近,有些方向有更大的代价函数,有些方向有更小的代价函数。在鞍点处,Hessian矩阵同时具有正负特征值,位于正特征值对应的特征向量方向的点比鞍点有更大的代价,负特征值对应的特征向量方向的点比鞍点有更小的代价。鞍点是某个方向的横截面的极大值点,也是另一个方向截面的极小值点。

saddle example

(图片截取自Wikipedia

   在低维空间中,局部极值很长常见,而在高维空间中,鞍点则很常见。理论上已经证明,不具有非线性的浅层自编码器只有全局极小值和鞍点,没有代价比全局极小值更大的局部极小值点。试验中发现,梯度下降在很多情况下可以逃离鞍点。对于牛顿法而言,鞍点是一个问题,因为梯度下降旨在朝着“下坡”方向移动,而非明确寻找梯度为0的点。如果不经修改,牛顿法就会跳进一个鞍点。而在高维空间中,鞍点激增,所以以牛顿法为代表的二阶方法无法成功取代梯度下降。

2.4 悬崖和梯度爆炸

   多层神经网络因为有大量的因子相乘,典型的比如循环神经网络,因为长的时间序列会有大量因子相乘,这中情况下存在像悬崖一样的斜率较大的区域,当遇到这种悬崖结构时,梯度更新会很大程度的改变参数的值,进而跳过这样的区域。不管是从上还是从下接近悬崖,都会产生不好的结果。如果采用启发式梯度截断可以避免严重的后果。基本想法在于梯度只是指明了移动的最佳方向,并没有指明最佳步长,因此启发式梯度截断会减小步长,使得梯度下降不太可能一步走出最陡下降方向的悬崖区域。

gradient cliff

(图片由英文原书内基于Pascanu等人2013论文《On the difficulty of training recurrent neural networks 》修改 )

2.5 长期依赖

   在一些算中,当计算图变得非常深的时候,会面临一个长期依赖的问题。深层的计算图不仅存在于前馈网络,也存在于循环神经网络中,因为循环神经网络要在很长的时间序列的各个时刻重复应用相同操作来构建非常深的计算图。举个例子,假如某个计算图包含一条反复与矩阵 $\boldsymbol{\textit{W}}$ 相乘的路径,那么 $t$ 步之后,相当于乘以 $\boldsymbol{\textit{W}^t}$。假设$\boldsymbol{\textit{W}}$ 有特征分解:

$$\boldsymbol{\textit{W}}=(\boldsymbol{\textit{V}} diag(\lambda)\boldsymbol{\textit{V}}^{-1})^t=\boldsymbol{\textit{V}} diag(\lambda)^t\boldsymbol{\textit{V}}^{-1}$$

当特征值 $\lambda_i$ 不在 1 附近时,若大于1,则会爆炸,若小于1,则会消失。梯度消失与爆炸问题(vanishing and exploding gradient problem)是指该计算图上面的梯度会因 $diag(\lambda)^t$ 发生大幅度的变化。梯度消失使得我们不知道参数朝那个方向移动可以快速改进代价函数,而梯度爆炸会使得学习不稳定。循环网络中使用的相同的矩阵$\boldsymbol{\textit{W}}$并没有在前馈网络中使用,因此即使使用非常深的前馈网络,也能避免梯度消失于爆炸问题。

2.6 非精确梯度

   大多数优化算法先决条件是我们知道精确的梯度或者是Hessian矩阵,然而在实践中,往往都是有偏的估计,几乎所有的深度学习算法都需要基于采样的估计,比如小批量数据计算更新梯度。有些情况下,我们希望最小化的目标函数实际上是难以处理的,所以此时我们只能使用近似梯度。大多数神经网络算法的设计都考虑到了梯度估计的缺陷,所以选择比真实损失函数更容易估计的代理损失函数来避免这个问题。

2.7 局部与全局的弱对应

   以上讨论的大都是单点的性质,如果利用梯度下降在某个方向上损失函数改进很大,但是并没有指向全局代价更低的遥远区域,那么单点处表现很好,但是全局表现不佳。有学者认为大部分训练的运行时间取决于到达最终解的路径长度,大多数优化研究难点集中于训练是否找到了全局最小点,局部最小点,鞍点,但是在实践中神经网络并不会达到其中任何一种。

  梯度下降和几乎所有可以有效训练神经网络的方法,都是基于局部较小更新。以上的内容都是集中于为何这些局部范围更新的正确方向难以计算,但是难以确定局部下降是否定义通向有效解的足够短的路径。目标函数可能有诸如病态条件或不连续梯度的问题,使得梯度为目标函数提供较好近似的区间非常小。有些情况下,局部下降或许可以定义通向解的路径,但是该路径包含很多次更新,因此遵循该路径会带来很大的计算代价;还有些情况下,局部下降完全无法定义通向解的路径;还有些情况下,局部移动太过贪心,朝着下坡的方向移动,但是却和所以可行解越来越远。

local vs all

   许多现有的研究方法在研究求解具有困难全局结构的问题时,着力于寻找良好的初始点,而不是在局部范围内更新的算法,因为前者实现目标更加可行。

2.8 优化的理论限制

   一些理论研究表明,我们为任何神经网络设计的任何优化算法都有性能限制,但是,这样的理论上的性能限制并不影响神经网络在实践中的应用。寻找一个给定规模的网络的可行解是困难的,但在现实情况中,我们可以通过选择更大的网络,设置更多的参数,轻松找到可以接受的解。另外,在神经网络中实践中,我们不关注某个函数的精确的极小值点,只要求损失减到足够小以获得可以接受的泛化误差即可。理论研究优化算法的性能上界需要学术界更多的努力。

3. 神经网络优化的基本算法

   以上内容已经讲解了神经网络优化的理论指导思想,使用梯度下降和随机梯度下降,可以很大程度上加速模型的训练,代价函数会沿着随机挑选的小批量数据的梯度方向下降。

3.1 随机梯度下降

  随机梯度下降(SGD)及其变种是一般机器学习中应用最多的优化算法,尤其是在深度学习中。按照数据生成分布 $p_{data}$ 随机抽取 $m$ 个小批量(独立同分布,iid)的样本,通过计算它们梯度的均值,得到梯度的无偏估计。下图展示了这个算法的过程:

标SGD算法

(算法图片截取自中文翻译

  SGD算法中的一个关键参数是学习率 $\epsilon_k$,在此之前我们介SGD都是使用的固定的学习率,在实践中,随着梯度的降低,有必要逐步减小学习率。因此以上伪代码中将第$k$步的学习率用 $\epsilon_k$ 来表示。由于SGD中随机采样 minibatch 会引入噪声源,因此在极小点处梯度并不会消失。而批量梯度下降使用全量数据更新梯度,在接近极小值点时,梯度很小并逐步变为0,因此,批量梯度下降可以使用固定学习率。与之对比,SGD收敛的一个充分条件是:

$$\sum_{k=1}^{\infty}\epsilon_k = \infty$$

$$\sum_{k=1}^{\infty}\epsilon_k^2 < \infty$$

在实践中,一般会选择线性衰减学习率知道第 $\tau$ 次迭代:

$$\epsilon_k = (1-\alpha)\epsilon_0 + \alpha\epsilon_{\tau}$$

其中 $\alpha= \frac{k}{\tau}$。在第 $\tau$次迭代之后,一般使 $\epsilon$ 保持常数。学习率可以通过实验误差的演变曲线来选取,如果初始 $\epsilon_0$选取的过大,则会出现损失上升和震荡,而如果$\epsilon_0$选取的过小,学习过程会过于缓慢。

   SGD及其相关的小批量,或者是更广义的基于梯度优化的在线学习算法,一个重要的性质是每一步更新的计算时间不依赖与总的训练样本的多少。及时总的数据集很大,它也能收敛,而且SGD往往在处理完整个训练集之前就收敛到可接受的误差范围之内。    研究算法的收敛率,一般会衡量额外误差(excess error) $J(\theta)-min_{\theta}J(\theta)$,也即当前代价函数超出最低可能代价的量。SGD应用于凸问题时,$k$ 步迭代之后误差的量级是$O(\frac{1}{\sqrt{k}})$,在强凸情况下误差量级是$O(\frac{1}{k})$。在没有额外假设和辅助信息之下,以上的界限不能进一步改进。批量梯度下降理论上SGD有更好的收敛率,然而有学者研究指出,泛化误差的下降速度不会快于$O(\frac{1}{k})$,因此对于机器学习算法而言,不值得探索收敛快于$O(\frac{1}{k})$的优化算法,因为往往过快的收敛对应着过拟合。对于大数据集,SGD只需要少量的样本计算梯度从而实现初始快速更新。但是由SGD损失了常数倍$O(\frac{1}{k})$的渐进分析,我们可以在学习中逐渐增大小批量的batch大小,以此权衡并充分利用批量梯度下降和随机梯度下降两者的优点。

3.2 动量

   SGD是最受欢迎的优化算法,但是其学习过程有时会有点缓慢,动量方法旨在加速学习过程,特别是处理高曲率,小但一致的梯度,或者是带有噪声的梯度。动量算法积累了之前梯度指数级衰减的移动平均,并且继续沿该方向移动。从形式上看,动量算法引入了变量 $v$ 充当速度角色-代表参数在参数空间移动的方向和速率,速度被认为是负梯度的指数衰减平均。动量这个名词的引入也是为了类比物理当中的动力概念。超参数 $\alpha$ 决定了之前的梯度贡献衰减得有多快,更新规则如下:

$$\boldsymbol{\textit{v}} = \alpha \boldsymbol{\textit{v}} - \epsilon \nabla_{\theta}(\frac{1}{m}\sum_{i=1}^{m}L(f(\boldsymbol{x^{(i)}}; \boldsymbol{\theta}), y^{(i)}))$$

$$\boldsymbol{\theta} = \boldsymbol{\theta} + \boldsymbol{\textit{v}}$$

速度$v$ 累积了梯度元素 $\nabla_{\theta}(\frac{1}{m}\sum_{i=1}^{m}L(f(\boldsymbol{x^{(i)}}; \boldsymbol{\theta}), y^{(i)}))$,相对于$\epsilon$,$\alpha$越大,之前梯度对现在方向的影响也越大。

sgd with momentum

带动量的SGD算法的伪代码如下:

标SGD算法

(算法图片截取自中文翻译

在之前的SGD或者批量梯度下降中,步长只是梯度范数乘以学习率,现在,步长取决于梯度序列的大小和排列,当许多连续的梯度指向相同的方向时,步长最大,如果动量算法始终观察到梯度 $\boldsymbol{\textit{g}}$,那么它会在 $-\boldsymbol{\textit{g}}$ 的方向上不断的加速。其中,步长大小为:

$$\frac{\epsilon \left | \boldsymbol{\textit{g}} \right |} {1-\alpha}$$

因此将动量方法的超参数视为 $\frac{\epsilon}{1-\alpha}$ 有助于SGD类比理解。比如,$\alpha=0.9$ 对应着最大速度10倍于梯度下降算法。在实践中,一般 $\alpha$ 会取值为0.5, 0.9和0.99,和学习率一样,$\alpha$ 也会随着时间不断地调整。一开始取比较小的值,逐渐变大,随着训练的深入,调整 $\alpha$ 没有收缩 $\epsilon$重要。

3.3 Nesterov 动量

  受Nesterov加速梯度算法的启发,Sutskever等人提出了动量算法的一个变种,更新规则如下:

$$\boldsymbol{\textit{v}} = \alpha \boldsymbol{\textit{v}} - \epsilon \nabla_{\theta}(\frac{1}{m}\sum_{i=1}^{m}L(f(\boldsymbol{x^{(i)}}; \boldsymbol{\theta}+\alpha \boldsymbol{\textit{v}}), y^{(i)}))$$

$$\boldsymbol{\theta} = \boldsymbol{\theta} + \boldsymbol{\textit{v}}$$

参数 $\alpha$$\epsilon$ 发挥了和标准动量方法中类似的作用,Nesterov动量和标准动量之间的区别在于梯度的计算上。Nesterov动量中,梯度计算在施加当前速度之后,可以理解为Nesterov 动量往标准动量方法中添加了一个校正因子。在凸优化问题使用批量梯度下降的情况下,Nesterov 动量将 $k$ 步之后额外误差收敛率从$O(\frac{1}{k})$ 提高到$O(\frac{1}{k^2})$,对SGD没有改进收敛率。完整的 Nesterov动量算法如下所示:

标SGD算法

(算法图片截取自中文翻译

4. 优化参数初始化

   有些算法本质上是非迭代的,只是求解一个点。而有些其他优化算法本质上是迭代的,应用这类优化问题时,能在可接受的时间内收敛到可接受的解,并且收敛值与初始值无关。深度学习的模型通常是迭代的,因此要求使用者制定一些开始迭代的初始点。另外,深度学习模型又是一个很复杂的问题,以至于大部分算法的结果收到初始值的影响。初始点能决定算法是否收敛,有些初始点十分不稳定,使得算法会遭遇数值困难,并完全失败。在收敛的情形下,初始点可以决定学习收敛的有多快,以及是否收敛到一个代价高或者低的点。另外,差不多代价的点可以导致区别极大的泛化误差,初始点可以影响泛化。

  现代机器学习乃至深度学习和神经网络的初始化策略是简单和启发式的,改进初始化是一项困难的任务。神经网络的优化到目前都没有被很好的理解。有些初始化策略在神经网络初始化时具有很好的效果,然而我们并没有理解哪些性质可以在初始化之后得意保持。进一步,有些初始化从优化的观点是有利的,但是从泛化的角度看是不利的。

  目前完全确信的唯一特性是初始参数需要在不同的单元之间“破坏对称性”。比如,如果具有相同激活函数的两个隐藏单元连接到相同的输入,那么这些单元必须具有不同的初始化参数。如果他们具有相同的参数,那么应用到确定性损失和模型的确定性学习算法之上时将会一直以相同的方式更新这两个单元。通常来说,最好还是初始化每个单元使其和其他单元计算不同的函数,这或许有助于确保没有输入模式丢失在前向传播的零空间中,也没有梯度丢失在反向传播的零空间中。每个单元计算不同的函数的目标促使了参数的随机初始化。

  我们几乎总是初始化模型的权重为高斯或者均匀分布中随机抽取的值,两者似乎没有很大的区别,然而初始分布的大小确实对优化过程的结果和网络的泛化能力都有很大的影响。

  更大的初始权重具有更强的破坏对称性的作用,有助于避免冗余的单元,也有助于避免在每层线性成分的前向或反向传播中丢失信号。如果权重初始太大,那么会在前向或者反向中产生爆炸的值。对于初始化网络,正则化和优化有着不同的观点:优化观点建议权重应该足够大以成功传播信息,正则化则希望参数小一点以降低模型复杂度。

  很多启发式的方法可用于选择权重的初始大小。一种初始化 $m$ 个输入和 $n$ 个输出的全连接层的权重启发式方法是从分布 $U(-\frac{1}{\sqrt{m}},\frac{1}{\sqrt{m}})$ 中采样权重,而 Glorot 和 Bengio 建议使用标准初始化(normalized initialization):

$$W_{i, j} \sim U(-\sqrt{\frac{6}{m+n}},\sqrt{\frac{6}{m+n}})$$

后一种启发式方法初始化所有的层,目的在于使其处于具有相同激活方差和使其具有相同的梯度方差之间。虽然这种假设网络是不含有非线性的链式矩阵乘法,现实的神经网络会违反这个假设,但是很多设计给线性模型的算法在非线性模型上面都有很好的效果。

   如果计算资源允许,将每层权重的初始值范围设定为一个超参数通常是一个好主意,使用后面11章 实践方法论中介绍的额超参数搜索算法,比如随机搜索来进行参数挑选。是否选择使用密集或者稀疏初始化也可以设置为一个超参数,当然我们也可以选择手动搜索最优初始范围。以上都是关注权重的初始化,其他参数的初始化通常是更加容易的。初始化偏置的方法必须和初始化权重的方法相协调,通常大部分情况下将偏置设置为0是可行的方案。当然,存在以下这些需要将初始化偏置为非0的情形:

  • 偏置作为输出单元,初始化偏置以获得正确的输出边缘统计通常是有利的
  • 有时需要选择偏置以避免初始化引起太大饱和
  • 有时一个单元会控制其他单元能否参与到等式中

  上面这些初始化模型参数为常数或者随机算法,实践中还可以利用机器学习来初始化网络参数。在深度学习这本书的第三部分,一个常用的策略是使用无监督模型训练出来的参数来初始化监督模型。 即使是在一个不相关的问题上运行监督训练,往往也会得到一个比随机初始化更快收敛的初始值(类似于迁移学习)。这些新式的初始化策略有时能够得到更好的泛化误差和更快的收敛速度,因为它们编码了模型初始参数的分布信息。之前的其他初始化策略效果也不错的原因主要在于设置了正确的参数范围,或者是设置不同单元计算互相不同的参数。

5. 自适应学习率优化

   学习速率对神经网络的性能有着显著的影响,损失通常高度敏感于参数空间的某些方向,而对其他因素不敏感。动量算法可以一定程度上缓解这个问题,但代价是引入了另一个超参数。如果我们相信方向敏感度在某种程度是轴对齐的,那么给每个参数设置不同的学习率,在模型学习训练过程中自动适应这些学习率是有道理的。早期的一个模型训练时候的启发式算法 Delta-bar-delta算法,基于简单的想法:如果损失对于某个给定模型参数的偏导符号保持不变,那么学习率应该增大,如果对于该参数的偏导的方向发生了变化,那么学习率应该减小。这种方法只适用于全批量优化中。本节将介绍最近提出的几种基于小批量的算法来自适应模型参数的学习率。

5.1 AdaGrad 算法

   AdaGrad算法,如下图所示,独立地适应所有模型参数的学习率,缩放每个参数反比于其所有梯度历史平方值总和和平方根,具有损失最大偏导的参数相应有一个快速下降的学习率,而具有小偏导的参数在学习率上有相对较小的下降。总的效果是在参数空间中更为平缓的倾斜方向会取得更大的进步。对于训练深度神经网络而言,从训练开始积累梯度平方会导致有效学习率过早和过量的减小。

标SGD算法

(算法图片截取自中文翻译

5.2 RMSProp 算法

   RMSProp由Hinton于2012年提出,用于修改AdaGrad以在非凸设定下效果更好,将梯度积累改变为指数加权的移动平均。AdaGrad设计以让凸问题能够快速的收敛。当应用于非凸函数训练神经网络时,学习轨迹可能穿过了很多不同的结构,最终到达一个局部是凸碗的结构。AdaGrad根据平方梯度的整个历史来收缩学习率,学习率很可能在到达这样的凸碗结构之前就变得太小。而RMSProp使用指数衰减平均,丢弃遥远过去的历史,使其能够在找到凸碗结构后快速收敛,该算法等效于一个初始化与该碗状结构的AdaGrad算法。实践中和经验上,RMSProp已经被证明是是一种有效而且实用的深度神经网络优化算法,目前是深度学习从业者经常采用的优化方法之一。

   RMSProp的标准形式和结合Nesterov动量的形式如下图所示,相比AdaGrad,引入了一个新的超参数 $\rho$,用来控制移动平均的长度范围。

标SGD算法 标SGD算法

(算法图片截取自中文翻译

5.3 Adam 算法

   Adam (adaptive moments),在早期算法的背景下,最好被看成结合RMSProp和具有一些重要区别的动量的变种。首先,在Adam中动量直接并入了梯度一阶矩(指数加权)的估计。将动量加入RMSProp最直观的方法是将动量应用于缩放后的梯度。其次,Adam包括偏置修正,修正从原点初始化的一阶矩(动量项)和二阶矩(非中心项)。Adam通常被认为对超参数的选择相当鲁棒,尽管学习率有时需要从建议的默认值修改。

标SGD算法

(算法图片截取自中文翻译

5.4 选择正确的优化算法

  本节以上部分讨论了一系列通过自适应每个模型参数的学习率以解决优化深度模型中的难题,究竟在实践中该如何选择并没有定论。目前来说,最流行并且使用率很高的优化算法包括SGD,有动量的SGD,RMSProp,有动量的RMSProp,AdaDelta 和 Adam,选择哪一个算法主要取决于使用者对特定算法的熟悉程度以便调节超参数。

  算法总结:基于动量和基于自适应学习率的优化算法都是从梯度下降SGD演化而来,算法的细节之处对比如下图:

标SGD算法

(图片截取自blog

   为比较和各个算法的特点和更好的理解基于梯度下降算法的工作原理,以下两张动图可供参考。左图:损失函数的等高图和不同算法的演化迭代收敛时间。右图:鞍点附近的各个算法的工作图,注意到红色的SGD路径在鞍点处花了很长时间才找到下降的方向,而RMSProp很快就找到了更快下降的方向。

(图片来自作者Alec Radford

6. 二阶近似的利用

   本节会讨论用于训练深度神经网络的二阶方法。为简单起见,只讨论目标函数为经验风险(暂时不考虑正则化项)(eqn. 25):

$$J(\theta) = E_{(x,y)\sim \hat{p}{data} } L(f(\boldsymbol{x}; \boldsymbol{\theta}), y)=\frac{1}{m} \sum{1}^{m} L(f(\boldsymbol{x^{(i)}}; \boldsymbol{\theta}), y^{(i)})$$

6.1 牛顿法

  与一阶方法相比,二阶方法使用二阶导数改进了优化,最广泛使用的是牛顿法。牛顿法是基于二阶泰勒级数展开在某点 $\theta_0$ 附近来近似 $J(\theta)$的方法,忽略了更高级的导数(eqn. 26):

$$J(\theta) \approx J(\theta_0) + (\theta-\theta_0)^{\top}\nabla_{\theta}J(\theta_0)+\frac{1}{2}(\theta-\theta_0)^{\top}\boldsymbol{\textit{H}}(\theta-\theta_0)$$

其中 $\boldsymbol{\textit{H}}$$J$ 相对于 $\theta$ 的Hessian矩阵在 $\theta_0$处的估计。如果我们基于上式求解这个函数的临界点,得到牛顿参数的更新规则(eqn. 27):

$$\theta^* = \theta_0 - \boldsymbol{\textit{H}}^{-1}\nabla_{\theta}J(\theta_0)$$

因此,对于局部的具有正定 $\boldsymbol{\textit{H}}$ 的二次函数,用 $\boldsymbol{\textit{H}}^{-1}$调整梯度,牛顿法会直接跳到极小值,如果目标函数是凸的但非二次,该更新将是迭代的,得到的相关算法如下。对于非二次的表面,只要Hessian矩阵保持正定,牛顿法就能够迭代应用。

标SGD算法

(算法图片截取自中文翻译

   牛顿法只适用于Hessian矩阵是正定的情况,而在深度学习中,目标函数的表面通常是非凸的,因此使用牛顿法是有问题的,这种情况下可以通过正则化Hessian矩阵来避免,常用的方法是在Hessian矩阵对角线上增加常数 $\alpha$

$$\theta^* = \theta_0 - [\boldsymbol{\textit{H}(f(\theta_0))+\alpha \boldsymbol{\textit{I}}} ]^{-1}\nabla_{\theta}J(\theta_0)$$

   这个正则化策略用于牛顿法的近似,只要Hessian矩阵的负特征值仍然相对接近0,效果就会很好。在曲率方向更极端的情况下,$\alpha$ 的值必须足够大,以抵消负特征。但是如果 $\alpha$ 变得太大,Hessian矩阵会变成由对角矩阵 $\alpha \boldsymbol{\textit{I}}$ 为主导,通过牛顿法选择的方向会收敛到普通梯度除以 $\alpha$。如果有很强的负数曲率存在时,$\alpha$ 需要特别大,导致牛顿法比选择合适学习率的梯度下降的步长更小。

   除了目标函数带来的挑战,牛顿法在大型神经网络训练中还受限制于庞大的计算负担。如果有 $k$ 个参数,那么牛顿法需要计算 $k\times k$的矩阵的逆,计算的额复杂度是 $O(k^3)$。另外由于每次训练迭代都要计算Hessian矩阵及其逆矩阵,所以只有参数很少的网络才能在实际中使用牛顿法。至于用于更大规模的网络训练,本节以下将讨论一些保持牛顿法优点,同时减小计算量的替代算法。

6.2 共轭梯度

  共轭梯度是一种通过迭代下降的共轭方向,来有效避免Hessian矩阵求逆的算法。这种方法来源于对最速下降法弱点的研究和改进。在共轭梯度法中,我们寻求一个和先前线搜索方向共轭(conjugate)的搜索方向,也即它不会撤销该方向上的进展,在训练迭代第 $t$ 步时,下一步的搜索方向 $\boldsymbol{\textit{d}}_t$的形式如下:

$${\boldsymbol{\textit{d}}}t = \nabla{\theta} J(\theta) + {\beta}t {\boldsymbol{\textit{d}}}{t-1}$$

其中 $\beta_t$的大小控制着我们应该沿方向 $\boldsymbol{\textit{d}}_{t-1}$ 上加回多少道当前搜索方向上。在这里,如果:

$${\boldsymbol{\textit{d}}}t^{\top} {\boldsymbol{\textit{H}}} {\boldsymbol{\textit{d}}}{t-1}$$ = 0

那么我们说两个方向 $\boldsymbol{\textit{d}}_t$$\boldsymbol{\textit{d}}_{t-1}$ 是共轭的。有两种计算 $\beta_t$ 的方法,一种是 Fletcher-Reeves方法,另一种是 Polak-Ribiere 方法。对于二次曲面而言,共轭方向确保梯度沿着前一个方向大小不变。

标SGD算法

(算法图片截取自中文翻译

6.3 BFGS

   BFGS算法(Broyden-Fletcher-Goldfarb-Shanno)算法具有牛顿法的有点,但是没有牛顿法的计算负担。BFGS算法使用了一个更直接的方法近似牛顿更新,回顾公式 eqn. 27,BFGS算法使用矩阵 $\boldsymbol{\textit{M}_t}$近似逆,迭代地低秩更新精度以更好地近似 $\boldsymbol{\textit{H}^{-1}}$。更多关于BFGS算法的内容,请参见相关参考资料。

  当Hessian逆近似 $\boldsymbol{\textit{M}_t}$ 更新时,下降方向 $\rho_t$$\rho_t = \textit{M}_t \textit{g}_t$,在该方向上的线搜索用于决定该方向上的步长 $\epsilon^*$,参数更行为:

$$\theta_{t+1}=\theta_t + \epsilon^* \rho_t$$

  和共轭梯度法类似BFGS算法迭代一系列线搜索,其方向含有二阶信息。而和共轭梯度不同的是,该方法的成功并不依赖于线搜索寻找该方向上和真正极小值很近的一点。优点方面,相比于共轭梯度,BFGS花费较少时间改进每个线搜索。缺点方面,BFGS算法必须存储Hessian逆矩阵$\boldsymbol{\textit{M}}$,需要 $O(n^2)$的存储空间,使得BFGS不适用于具有百万级参数的大规模现代深度学习模型。作为改进,存储受限的BFGS(或者称为L-BFGS)通过避免存储完整的Hessian逆近似$\boldsymbol{\textit{M}}$,使得存储代价显著降低。

7. 优化策略和元算法

   有很多的优化技术并不是真正具体的算法,而是更为一般化的模板和思想,它们既可以产生特定的算法,也可以并入到很多的算法之中。

7.1 批标准化

  批标准化是优化深度神经网络的重要创新之一。实际上它并不是一个优化算法,而是一个自适应的重参数的方法,以期解决训练非常深的模型的困难。重参数化显著减少了多层之间协调更新的问题,可以用于网络的任何输入层或者是隐藏层。设 $\boldsymbol{\textit{H}}$ 是需要标准化的某层的小批量激活函数,排布为设计矩阵,每个样本的激活层出现在矩阵的每一行中。为了标准化 $\boldsymbol{\textit{H}}$,我们将其替换为:

$$\boldsymbol{\textit{H}}'= \frac{\boldsymbol{\textit{H}}-\boldsymbol{\mu}}{\boldsymbol{\sigma}}$$

其中 $\mu$ 是包含每个单元均值的向量,$\sigma$ 是包含每个单元标准差的向量。在训练的阶段:

$$\mu = \frac{1}{m} \sum_{i} {\boldsymbol{\textit{H}}}_i $$

$${\boldsymbol{\sigma}} = \sqrt{\delta+\frac{1}{m} \sum_{i} (\boldsymbol{\textit{H}}-{\boldsymbol{\mu})}_i^2}$$

其中 $\delta$是一个很小的正值,比如 $10^{-8}$。我们反向传播这些操作,计算均值和标准差,并应用它们来标准化 $\boldsymbol{\textit{H}}$。在测试阶段,$\mu$ 和 $\sigma$ 可以被替换为训练阶段收集的运行均值,使得模型可以对单一样本进行评估。批标准化使得模型更容易学习。

7.2 坐标下降

  在某些情况下,将一个优化问题分解成几个部分,可以更快速地解决原问题。比如,如果我们相对于某个单一变量 $x_i$ 最小化 $f(\boldsymbol{\textit{x}})$,然后相对于另一个变量 $x_j$ 等等,这样反复循环所有变量,会保证达到局部极小值点。这种做法叫做坐标下降(coordinate descent),因为我们一次优化一个坐标。进一步地,块坐标下降(block coordinate descent)指对某个子集的变量同时最小化。

7.3 Polyak 平均

   Polyak 平均会平均优化算法在参数空间访问轨迹中的几个点。如果 $t$ 次迭代梯度下降访问了点 $\boldsymbol{\theta}^{(1)}... \theta^{(t)}$,那么Polyak平均算法输出的是:

$$\hat{\theta}^{(t)} = \frac{1}{t}\sum_i \theta^{(i)}$$

在梯度下降应用于某些问题,比如凸问题时,这种方法具有较强的收敛保证。当Polyak平均于非凸问题时,通常会用指数衰减计算平均值:

$$\hat{\theta}^{(t)} = \alpha \hat{\theta}^{(t-1)} + (1-\alpha) \theta^t$$

7.4 监督预训练

  有时如果模型太过复杂难以优化或者任务非常困难,直接训练模型的挑战非常之大,有时训练一个较为简单的问题,然后使得模型逐渐复杂会更有效。训练模型先求解一个简化的问题,然后转移到最后的问题,有时也会更有效些。这种在训练最终模型之前训练简单模型求解简化问题的方法统称为预训练(pretraining)。

  贪心算法(greedy algorithm)将问题分解为许多部分,然后独立地在每个部分求解最优值,往往结合各个最佳部分并不能保证得到一个最优解,但是这种贪心算法计算比求解联合最优解的算法高效很多,并且贪心算法的结果即使不是最优解,往往也是可以接受的。贪心算法之后可以紧随一个精调(fine-tunning)阶段,联合优化算法搜索全问题的最优解。所以,使用贪心算法初始化联合优化算法,可以极大地加速算法,并提高寻找到的解的质量。

  预训练算法,特别是贪心预训练,将监督学习问题分解成其他简化的监督学习问题的预训练算法,叫做贪心监督预训练(greedy supervised pretraining)。这种方法有助于更好的指导深层结构的中间层的学习,且在一般情况下,预训练对于优化和泛化都是有帮助的,它实际上扩展了迁移学习的想法。

7.5 设计有助于优化的模型

  改进优化的最好方法并不总是改进优化算法,相反,在深度学习中的许多改进来自设计易于优化的模型。在实践中,选择一族容易优化的模型比使用一个强大的优化算法更重要。神经网络学习在过去30年的大多数进步主要来自改变模型族,而并非改变优化过程。

  现代神经网络的设计选择体现在层之间的线性变换,几乎处处可导的激活函数,和大部分定义域都有明显的梯度,特别是创新的模型,比如LSTM,整流线性单元和maxout单元都比先前的模型,比如sigmoid单元的深度网络,使用更多的线性函数,使得这些模型都有简化优化的作用。现代神经网络的设计方案旨在使其局部梯度信息合理地对应着移动向一个遥远的解。

7.6 延拓法和课程学习

  许多优化的挑战都来自于代价函数的全局结构,不能仅仅通过局部更新方向上更好的估计来解决。解决这个问题的主要方法是尝试初始化参数到某种区域内,该区域可以通过局部下降很快连接到参数空间中的解。

  延拓法(continuation method)是一族通过挑选初始点使得优化更容易的方法,以确保局部优化花费大部分时间在表现良好的区域。延拓法的基本思想是构造一系列具有相同参数的目标函数,这些函数难度逐渐提高。传统上,延拓法主要被用来客服局部极小值的问题,它被设计用来在有很多局部极小值的情况下,求解一个全局最小点。这些连续的方法会通过“模糊”原来的代价函数来构造更容易的代价函数,有些非凸函数在模糊之后就变成了近似凸函数,而且这种模糊保留了关于全局极小值的足够信息以供算法学习。尽管局部极小值问题已不再是神经网络优化的主要问题了,延拓法仍然有所帮助。

  Bengio 提出被称为课程学习(curriculum learning)或者塑造(shaping)的方法也可以被解释为延拓法。课程学习基于规划学习过程的想法,首先学习简单的概念,然后逐步学习依赖于这些简单概念的复杂概念。比如:教师通常会先展示更容易更典型的实例给学生,然后慢慢过渡到复杂的实例,在人类教学上,课程学习的策略比基于样本均匀采样的策略更为有效。