深度学习面试笔试之深度学习的优化方法
- 1. 训练误差和泛化误差
- 2. 该如何选择模型
- 2.1 验证数据集
- 2.2 K 折交叉验证
- 3. 欠拟合和过拟合
- 训练数据集大小
- 正则化
- 4. 丢弃法(Dropout)(Dropout率越高,正规化程度越低)
- 5. 梯度消失/梯度爆炸(Vanishing / Exploding gradients)
- 解决方案
- 预训练加微调
- 梯度剪切、正则
- ReLu、leakReLu等激活函数
- Batch Normalization(归一化输入值、加快模型的收敛速度)
- 残差结构
- LSTM
- 7. 优化算法
- 7.1 动量法
- 7.2 AdaGrad算法
- 7.3 RMSProp算法
- 7.4 AdaDelta算法
- 7.5 Adam算法
- 7.6 局部最优--鞍点问题
- 8. 如何解决训练样本少的问题
- 9. 如何提升模型的稳定性?
- 10. 有哪些改善模型的思路
- 数据角度
- 模型角度
- 调参优化角度
- 训练角度
- 11. 如何提高深度学习系统的性能
- 不同数据集下使用微调:
1. 训练误差和泛化误差
机器学习模型在训练数据集和测试数据集上的表现。如果你改变过实验中的模型结构或者超参数,你也许发现了:当模型在训练数据集上更准确时,它在测试数据集上却不⼀定更准确。这是为什么呢?
因为存在着训练误差和泛化误差:
- 训练误差:模型在训练数据集上表现出的误差。
- 泛化误差:模型在任意⼀个测试数据样本上表现出的误差的期望,并常常通过测试数据集上的误差来近似。
训练误差的期望小于或等于泛化误差。也就是说,⼀般情况下,由训练数据集学到的模型参数会使模型在训练数据集上的表现优于或等于在测试数据集上的表现。由于无法从训练误差估计泛化误差,⼀味地降低训练误差并不意味着泛化误差⼀定会降低。
机器学习模型应关注降低泛化误差。
2. 该如何选择模型
在机器学习中,通常需要评估若干候选模型的表现并从中选择模型。这⼀过程称为模型选择(model selection)。可供选择的候选模型可以是有着不同超参数的同类模型。以多层感知机为例,我们可以选择隐藏层的个数,以及每个隐藏层中隐藏单元个数和激活函数。为了得到有效的模型,我们通常要在模型选择上下一番功夫。
2.1 验证数据集
从严格意义上讲,测试集只能在所有超参数和模型参数选定后使⽤⼀次。不可以使⽤测试数据选择模型,如调参。由于⽆法从训练误差估计泛化误差,因此也不应只依赖训练数据选择模型。鉴于此,我们可以预留⼀部分在训练数据集和测试数据集以外的数据来进⾏模型选择。这部分数据被称为验证数据集,简称验证集(validation set)。例如,我们可以从给定的训练集中随机选取⼀小部分作为验证集,而将剩余部分作为真正的训练集。
可以通过预留这样的验证集来进行模型选择,判断验证集在模型中的表现能力。
2.2 K 折交叉验证
由于验证数据集不参与模型训练,当训练数据不够⽤时,预留⼤量的验证数据显得太奢侈。⼀种改善的方法是K折交叉验证(K-fold cross-validation)。在K折交叉验证中,我们把原始训练数据集分割成K个不重合的子数据集,然后我们做K次模型训练和验证。每⼀次,我们使⽤⼀个⼦数据集验证模型,并使用其他K − 1个子数据集来训练模型。在这K次训练和验证中,每次用来验证模型的子数据集都不同。最后,我们对这K次训练误差和验证误差分别求平均。
3. 欠拟合和过拟合
- 欠拟合:模型无法得到较低的训练误差。
- 过拟合:是模型的训练误差远小于它在测试数据集上的误差。
应对欠拟合和过拟合的⼀个办法是针对数据集选择合适复杂度的模型。
训练数据集大小
影响欠拟合和过拟合的另⼀个重要因素是训练数据集的⼤小。⼀般来说,如果训练数据集中样本数过少,特别是比模型参数数量(按元素计)更少时,过拟合更容易发⽣。此外,泛化误差不会随训练数据集里样本数量增加而增大。因此,在计算资源允许的范围之内,我们通常希望训练数据集大⼀些,特别是在模型复杂度较⾼时,例如层数较多的深度学习模型。
正则化
应对过拟合问题的常用方法:权重衰减(weight decay),权重衰减等价于L2范数正则化(regularization)。正则化通过为模型损失函数添加惩罚项使学出的模型参数值较小,是应对过拟合的常用手段。
4. 丢弃法(Dropout)(Dropout率越高,正规化程度越低)
Dropout是指在深度网络的训练中,以一定的概率随机地“临时丢弃”一部分神经元结点。除了上面提到的权重衰减以外,深度学习模型常常使用丢弃法(dropout)来应对过拟合问题。丢弃法有⼀些不同的变体。本节中提到的丢弃法特指倒置丢弃法(inverted dropout)。
回忆⼀下,“多层感知机”描述了⼀个单隐藏层的多层感知机。其中输⼊个数为4,隐藏单元个数为5,且隐藏单元hi(i = 1, . . . , 5)的计算表达式为:
这⾥ϕ是激活函数, 是输入,隐藏单元i的权重参数为 ,偏差参数为bi。当对该隐藏层使用丢弃法时,该层的隐藏单元将有⼀定概率被丢弃掉。设丢弃概率为p,那么有p的概率hi会被清零,有1 − p的概率hi会除以1 − p做拉伸。丢弃概率是丢弃法的超参数。具体来说,设随机变量ξi为0和1的概率分别为p和1 − p。使用丢弃法时我们计算新的隐藏单元 。
由于E(ξi) = 1 − p,因此:
**即丢弃法不改变其输入的期望值。**让我们对隐藏层使用丢弃法,⼀种可能的结果如下图所示,其中h2和h5被清零。这时输出值的计算不再依赖h2和h5,在反向传播时,与这两个隐藏单元相关的权重的梯度均为0。由于在训练中隐藏层神经元的丢弃是随机的,即h1, . . . , h5都有可能被清零,输出层的计算无法过度依赖h1, . . . , h5中的任⼀个,从而在训练模型时起到正则化的作用,并可以用来应对过拟合。在测试模型时,我们为了拿到更加确定性的结果,⼀般不使用丢弃法。
加入dropout,会使神经网络的训练时间变长、dropout跟bagging操作的效果类似
5. 梯度消失/梯度爆炸(Vanishing / Exploding gradients)
训练神经网络,尤其是深度神经所面临的一个问题就是梯度消失或梯度爆炸,也就是你训练神经网络的时候,导数或坡度有时会变得非常大,或者非常小,甚至于以指数方式变小,这加大了训练的难度。
本质上,梯度消失和爆炸是一种情况。在深层网络中,由于网络过深,如果初始得到的梯度过小,或者传播途中在某一层上过小,则在之后的层上得到的梯度会越来越小,即产生了梯度消失。梯度爆炸也是同样的。一般地,不合理的初始化以及激活函数,如sigmoid等,都会导致梯度过大或者过小,从而引起消失/爆炸。
解决方案
预训练加微调
其基本思想是每次训练一层隐节点,训练时将上一层隐节点的输出作为输入,而本层隐节点的输出作为下一层隐节点的输入,此过程就是逐层“预训练”(pre-training);在预训练完成后,再对整个网络进行“微调”(fine-tunning)。
梯度剪切、正则
梯度剪切这个方案主要是针对梯度爆炸提出的,其思想是设置一个梯度剪切阈值,然后更新梯度的时候,如果梯度超过这个阈值,那么就将其强制限制在这个范围之内。这可以防止梯度爆炸。
另外一种解决梯度爆炸的手段是采用权重正则化(weithts regularization)比较常见的是L1和L2正则。
ReLu、leakReLu等激活函数
ReLu:其函数的导数在正数部分是恒等于1,这样在深层网络中,在激活函数部分就不存在导致梯度过大或者过小的问题,缓解了梯度消失或者爆炸。同时也方便计算。当然,其也存在存在一些缺点,例如过滤到了负数部分,导致部分信息的丢失,输出的数据分布不在以0为中心,改变了数据分布。
leakrelu:就是为了解决relu的0区间带来的影响,其数学表达为:leakrelu=max(k*x,0)其中k是leak系数,一般选择0.01或者0.02,或者通过学习而来。
Batch Normalization(归一化输入值、加快模型的收敛速度)
Batch Normalization是深度学习发展以来提出的最重要的成果之一了,目前已经被广泛的应用到了各大网络中,具有加速网络收敛速度,提升训练稳定性的效果,Batch Normalization本质上是解决反向传播过程中的梯度问题。Batch Normalization,简称BN,即批规范化,通过规范化操作将输出信号x规范化到均值为0,方差为1保证网络的稳定性。
- 有一些从 0 到 1 而不是从 1 到 1000 的特征值,通过归一化所有的输入特征值𝑥,以获得类似范围的值,可以加速学习。所以 Batch 归一化起的作用的原因,直观的一点就是,它在做类似的工作,但不仅仅对于这里的输入值,还有隐藏单元的值。
- 它可以使权重比你的网络更滞后或更深层,比如,第 10 层的权重更能经受得住变化。
残差结构
残差的方式,能使得深层的网络梯度通过跳级连接路径直接返回到浅层部分,使得网络无论多深都能将梯度进行有效的回传。
LSTM
LSTM全称是长短期记忆网络(long-short term memory networks),是不那么容易发生梯度消失的,主要原因在于LSTM内部复杂的“门”(gates)。在计算时,将过程中的梯度进行了抵消。
7. 优化算法
7.1 动量法
在每次迭代中,梯度下降根据自变量当前位置,沿着当前位置的梯度更新自变量。然而,如果自变量的迭代⽅向仅仅取决于自变量当前位置,这可能会带来⼀些问题。
让我们考虑⼀个输入和输出分别为⼆维向量x = [x1, x2]⊤和标量的目标函数 。这⾥将 系数从1减小到了0.1。下面实现基于这个目标函数的梯度下降,并演示使用学习率为0.4时自变量的迭代轨迹。
可以看到,同⼀位置上,目标函数在竖直方向(x2轴方向)比在水平方向(x1轴方向)的斜率的绝对值更⼤。因此,给定学习率,梯度下降迭代自变量时会使自变量在竖直方向比在水平方向移动幅度更大。那么,我们需要⼀个较小的学习率从而避免自变量在竖直方向上越过目标函数最优解。然而,这会造成自变量在水平方向上朝最优解移动变慢。
动量法的提出是为了解决梯度下降的上述问题。由于小批量随机梯度下降比梯度下降更为广义,本章后续讨论将沿⽤“小批量随机梯度下降”⼀节中时间步t的小批量随机梯度gt的定义。设时间步t的⾃变量为xt,学习率为ηt。在时间步0,动量法创建速度变量v0,并将其元素初始化成0。在时间步t > 0,动量法对每次迭代的步骤做如下修改:
其中,动量超参数γ满足0 ≤ γ < 1。当γ = 0时,动量法等价于小批量随机梯度下降。在梯度下降时候使用动量法后的迭代轨迹:
可以看到使用较小的学习率η = 0.4和动量超参数γ = 0.5时,动量法在竖直方向上的移动更加平滑,且在水平方向上更快逼近最优解。
所以,在动量法中,⾃变量在各个方向上的移动幅度不仅取决当前梯度,还取决于过去的各个梯度在各个方向上是否⼀致。在本节之前示例的优化问题中,所有梯度在水平方向上为正(向右),而在竖直方向上时正(向上)时负(向下)。这样,我们就可以使⽤较大的学习率,从而使自变量向最优解更快移动。
7.2 AdaGrad算法
优化算法中,目标函数自变量的每⼀个元素在相同时间步都使用同⼀个学习率来自我迭代。在“动量法”里我们看到当x1和x2的梯度值有较大差别时,需要选择足够小的学习率使得自变量在梯度值较大的维度上不发散。但这样会导致自变量在梯度值较小的维度上迭代过慢。动量法依赖指数加权移动平均使得自变量的更新方向更加⼀致,从而降低发散的可能。本节我们介绍AdaGrad算法,它根据自变量在每个维度的梯度值的大小来调整各个维度上的学习率,从而避免统一的学习率难以适应所有维度的问题。
AdaGrad算法会使⽤⼀个小批量随机梯度gt按元素平方的累加变量st。在时间步0,AdaGrad将s0中每个元素初始化为0。在时间步t,⾸先将小批量随机梯度gt按元素平方后累加到变量st:
其中⊙是按元素相乘。接着,我们将目标函数自变量中每个元素的学习率通过按元素运算重新调整⼀下:
其中η是学习率,ϵ是为了维持数值稳定性而添加的常数,如10的-6次方。这里开方、除法和乘法的运算都是按元素运算的。这些按元素运算使得目标函数自变量中每个元素都分别拥有自己的学习率。
需要强调的是,小批量随机梯度按元素平方的累加变量st出现在学习率的分母项中。因此,
如果目标函数有关自变量中某个元素的偏导数⼀直都较大,那么该元素的学习率将下降较快;
反之,如果⽬标函数有关自变量中某个元素的偏导数⼀直都较小,那么该元素的学习率将下降较慢。
然而,由于st⼀直在累加按元素平方的梯度,自变量中每个元素的学习率在迭代过程中⼀直在降低(或不变)。所以,当学习率在迭代早期降得较快且当前解依然不佳时,AdaGrad算法在迭代后期由于学习率过小,可能较难找到⼀个有用的解。
7.3 RMSProp算法
当学习率在迭代早期降得较快且当前解依然不佳时,AdaGrad算法在迭代后期由于学习率过小,可能较难找到⼀个有用的解。为了解决这⼀问题,RMSProp算法对AdaGrad算法做了⼀点小小的修改。
不同于AdaGrad算法里状态变量st是截至时间步t所有小批量随机梯度gt按元素平方和,RMSProp算法将这些梯度按元素平方做指数加权移动平均。具体来说,给定超参数0 ≤ γ < 1,RMSProp算法在时间步t > 0计算:
和AdaGrad算法⼀样,RMSProp算法将目标函数自变量中每个元素的学习率通过按元素运算重新调整,然后更新自变量:
其中η是学习率,ϵ是为了维持数值稳定性而添加的常数,如10的-6次方。因为RMSProp算法的状态变量st是对平方项gt ⊙ gt的指数加权移动平均,所以可以看作是最近1/(1 − γ)个时间步的小批量随机梯度平方项的加权平均。如此一来,自变量每个元素的学习率在迭代过程中就不再一直降低(或不变)。
7.4 AdaDelta算法
除了RMSProp算法以外,另⼀个常用优化算法AdaDelta算法也针对AdaGrad算法在迭代后期可能较难找到有用解的问题做了改进。有意思的是,AdaDelta算法没有学习率这⼀超参数。
AdaDelta算法也像RMSProp算法⼀样,使用了小批量随机梯度gt按元素平方的指数加权移动平均变量st。在时间步0,它的所有元素被初始化为0。给定超参数0 ≤ ρ < 1(对应RMSProp算法中的γ),在时间步t > 0,同RMSProp算法⼀样计算:
与RMSProp算法不同的是,AdaDelta算法还维护⼀个额外的状态变量∆xt,其元素同样在时间步0时被初始化为0。我们使用∆xt−1来计算自变量的变化量:
最后,我们使⽤∆xt来记录自变量变化量 按元素平方的指数加权移动平均:
可以看到,如不考虑ϵ的影响,AdaDelta算法与RMSProp算法的不同之处在于使用 来替代超参数η。
7.5 Adam算法
Adam算法在RMSProp算法基础上对小批量随机梯度也做了指数加权移动平均。
Adam算法使用了动量变量vt和RMSProp算法中小批量随机梯度按元素平方的指数加权移动平均变量st,并在时间步0将它们中每个元素初始化为0。给定超参数0 ≤ β1 < 1(算法作者建议设为0.9),时间步t的动量变量vt即小批量随机梯度gt的指数加权移动平均:
和RMSProp算法中⼀样,给定超参数0 ≤ β2 < 1(算法作者建议设为0.999),将小批量随机梯度按元素平方后的项gt ⊙ gt做指数加权移动平均得到st:
由于我们将 v0 和 s0 中的元素都初始化为 0,在时间步 t 我们得到
。将过去各时间步小批量随机梯度的权值相加,得到
。需要注意的是,当 t 较小时,过去各时间步小批量随机梯度权值之和会较小。例如,当β1 = 0.9时,v1 = 0.1g1。为了消除这样的影响,对于任意时间步 t,我们可以将 vt 再除以 ,从而使过去各时间步小批量随机梯度权值之和为1。这也叫作偏差修正。在Adam算法中,我们对变量 vt 和 st 均作偏差修正:
接下来,Adam算法使⽤以上偏差修正后的变量vˆt和sˆt,将模型参数中每个元素的学习率通过按元素运算重新调整:
其中η是学习率,ϵ是为了维持数值稳定性而添加的常数,如10的-8次方。和AdaGrad算法、RMSProp算法以及AdaDelta算法⼀样,目标函数自变量中每个元素都分别拥有自己的学习率。最后,使用 迭代自变量:
7.6 局部最优–鞍点问题
一个具有高维度空间的函数,如果梯度为 0,那么在每个方向,它可能是凸函数,也可能是凹函数。如果你在 2 万维空间中,那么想要得到局部最优,所有的 2 万个方向都需要是这样,但发生的机率也许很小,也许是2的-20000次方,你更有可能遇到有些方向的曲线会这样向上弯曲,另一些方向曲线向下弯,而不是所有的都向上弯曲,因此在高维度空间,你更可能碰到鞍点。
而不会碰到局部最优。至于为什么会把一个曲面叫做鞍点,你想象一下,就像是放在马背上的马鞍一样,如果这是马,这是马的头,这就是马的眼睛,画得不好请多包涵,然后你就是骑马的人,要坐在马鞍上,因此这里的这个点,导数为 0 的点,这个点叫做鞍点。我想那确实是你坐在马鞍上的那个点,而这里导数为 0。
鞍点中的平稳段是一个问题,这样使得学习十分缓慢,**这也是像 Momentum 或是RMSprop,Adam 这样的算法,能够加速学习算法的地方。**在这些情况下,更成熟的优化算法,如 Adam 算法,能够加快速度,让你尽早往下走出平稳段。
8. 如何解决训练样本少的问题
- 利用预训练模型进行迁移微调(fine-tuning),预训练模型通常在特征上拥有很好的语义表达。此时,只需将模型在小数据集上进行微调就能取得不错的效果。CV有ImageNet,NLP有BERT等。
- 数据集进行下采样操作,使得符合数据同分布。
- 数据集增强、正则或者半监督学习等方式来解决小样本数据集的训练问题。
9. 如何提升模型的稳定性?
- 正则化(L2, L1, dropout):模型方差大,很可能来自于过拟合。正则化能有效的降低模型的复杂度,增加对更多分布的适应性。
- 前停止训练:提前停止是指模型在验证集上取得不错的性能时停止训练。这种方式本质和正则化是一个道理,能减少方差的同时增加的偏差。目的为了平衡训练集和未知数据之间在模型的表现差异。
- 扩充训练集:正则化通过控制模型复杂度,来增加更多样本的适应性。
- 特征选择:过高的特征维度会使模型过拟合,减少特征维度和正则一样可能会处理好方差问题,但是同时会增大偏差。
10. 有哪些改善模型的思路
数据角度
增强数据集。无论是有监督还是无监督学习,数据永远是最重要的驱动力。更多的类型数据对良好的模型能带来更好的稳定性和对未知数据的可预见性。对模型来说,“看到过的总比没看到的更具有判别的信心”。
模型角度
模型的容限能力决定着模型可优化的空间。在数据量充足的前提下,对同类型的模型,增大模型规模来提升容限无疑是最直接和有效的手段。
调参优化角度
如果你知道模型的性能为什么不再提高了,那已经向提升性能跨出了一大步。 超参数调整本身是一个比较大的问题。一般可以包含模型初始化的配置,优化算法的选取、学习率的策略以及如何配置正则和损失函数等等。
训练角度
在越大规模的数据集或者模型上,诚然一个好的优化算法总能加速收敛。但你在未探索到模型的上限之前,永远不知道训练多久算训练完成。所以在改善模型上充分训练永远是最必要的过程。充分训练的含义不仅仅只是增大训练轮数。有效的学习率衰减和正则同样是充分训练中非常必要的手段。
11. 如何提高深度学习系统的性能
- 提高模型的结构。
- 改进模型的初始化方式,保证早期梯度具有某些有益的性质,或者具备大量的稀疏性,或者利用线性代数原理的优势。
- 择更强大的学习算法。
不同数据集下使用微调:
- 数据集1-数据量少,但数据相似度非常高-在这种情况下,我们所做的只是修改最后几层或最终的softmax图层的输出类别。
- 数据集2-数据量少,数据相似度低-在这种情况下,我们可以冻结预训练模型的初始层(比如k层),并再次训练剩余的(n-k)层。由于新数据集的相似度较低,因此根据新数据集对较高层进行重新训练具有重要意义。
- 数据集3-数据量大,数据相似度低-在这种情况下,由于我们有一个大的数据集,我们的神经网络训练将会很有效。但是,由于我们的数据与用于训练我们的预训练模型的数据相比有很大不同,使用预训练模型进行的预测不会有效。因此,最好根据你的数据从头开始训练神经网络(Training from scatch)。
- 数据集4-数据量大,数据相似度高-这是理想情况。在这种情况下,预训练模型应该是最有效的。使用模型的最好方法是保留模型的体系结构和模型的初始权重。然后,我们可以使用在预先训练的模型中的权重来重新训练该模型。
参考: