1. 引言
深度学习因其较高的复杂性和众多的参数,我们很不容易训练出好的模型,模型也缺乏可解释性,因此深度学习从业者一般也自嘲自己是“炼丹师”。
为了快速训练出好的模型,过去的学者也总结出来很多技巧进行辅助训练。在前人的基础上,我们可以学习、理解各种影响训练过程和结果的问题及其对应的解决方案。
需要注意的是,根据可用数据的规模和质量、模型的类型和复杂度、任务的类型和应用要求、可用设备的性能等,我们在具体训练时需要解决的问题也不同,应当因地制宜,活学活用。
2. 当梯度≈0时
2.1 判断critical point的类型
2.1.1 理论分析
梯度近似为0的时候,训练损失几乎不再改变,此时损失可能达到了全局最小值,也有可能停在了局部最小值(Local Minima)和马鞍点(Saddle Point)。
我们需要判断梯度近似为0的原因,才能针对性地进行处理,而判断的前提是知道 loss function 的形状。然而,深度学习的网络非常复杂,我们无法得知完整的 loss function,但可以使用泰勒公式近似局部的 loss function来帮助我们稳定点(critical point)的位置。
如上图所示,可以使用点的值来近似的值,其中绿线是一阶导数项的贡献,红线是二阶导数项的贡献。
当处于critical point时,一阶导数为0,此时需要通过二阶导数项来判断具体类型。
我们可以发现,不同critical point周边点的大小是不同的,可以直观判断出所处的类型,但问题在于我们实际上无法绘制出类似的图形来进行判断。正确方式是基于 Hessian 矩阵的奇异值来判断。
2.1.2 举例说明
为方便理解,我们构建了一个最简单的神经网络,而且仅用一个样本训练,然后计算损失分布。
在上图中,颜色越亮,说明损失值就越大。critical point有七个,分为 Minima 和 saddle 两种。
在已知损失函数的情况下,我们可以直接计算出 critical point 的一阶和二阶微分(以(0,0)点为例),然后根据二阶微分的值判断出该点的类型为 saddle point。
类似地,我们也可以判断其他6个 critical point 的类型。
2.2 应对方案
2.2.1 Saddle Point
Hessian
如果遇到了 saddle point,不用担心,可以通过 Hessian 计算出可行的方向进行参数更新。
按照之前的示例,我们可以计算得出更新的方向可以为(1,1)。
但实际上,这种寻找参数更新方向的方法计算量天大,实际还有其他高效的计算方法。
Momentum
Momentum 将现实世界中的惯性概念引入到了模型训练中,是一项对抗 saddle point 和 local Minima 的技术。它的主要思想是,让每次的更新步伐在本次梯度的基础上加上之前梯度的值,这样可以避免本次梯度为0时模型不再更新的问题。
更具体的运算过程如下图所示:
要注意的是,Momentum 不仅考虑了过去的梯度的值,还包括梯度的方向。
2.2.2 Local Minima
另外要注意的是,在高维空间中,Local Minima几乎不会出现,因为不大可能所有的维度的梯度都近似为0。低维空间中找不到的路,在高维空间可能很容易找到。
实证研究也证实了这一点,如下图所示。因此,我们也不必担心 Local Minima 的问题。
3. 当梯度≠0但训练损失不下降
3.1 原因分析
除了 critical point 的问题外,我们经常遇到的问题是损失函数不变,梯度却来回振荡。
我们在实际训练的时候,即使是一个很简单的深度学习模型,我们都可能 train 不起来。
背后的原因在于,不同的参数使用的学习率的相同的,A参数合适的学习率对于B参数可能就会过小,也可能过大导致无法收敛。因此,我们需要为不同的参数设置不同的学习率。
3.2 应对方案
根据调整学习率的不同方式,有不同的方法可以使用来解决梯度振荡的问题。
3.2.1 Adagrad
Adagrad 使用 Root Mean Square 作为参数依赖的 ,考虑过去梯度调整当前学习率。
3.2.2 RMSProp
即使是同一个参数,在更新过程中可能会经历梯度逐渐下降和上升的不同过程,需要快速反应。
以下是具体的算法过程,其中我们需要设置权重 来表示过去梯度的重要性。
下图可以形象表达 RMSProp 的工作特点,相比 Adagrad,它给予不同时期梯度以不同的权重。
3.2.3 Adam
Adam 是目前最常用的模型训练方法,需要设置超参数。但是默认的超参数设置已经很好了。
3.2.4 Learning Rate Scheduling
当使用 Adagrad 训练模型时,若某个维度一直未能更新,则 会变得很小导致很大的学习率,该维度会经历一次很大的剧变,然后逐渐收敛回来,周而复始。
解决这个问题的方法是动态调整 learning Rate 的数值,大方向是随着时间而降低。
经过实验发现,Warm up在训练 Bert 模型时很好用,一种解释是刚开始的时候因为数据量不足、统计值不具备代表性所以先降低学习率避免风险。
3.2.5 总结
4. 权衡模型训练的速度和质量
4.1 Batch 的一般理解
Batch 是深度学习模型训练时经常使用的方法,它不使用所有样本的损失总和去做微分,而是使用部分(batch size)样本的损失做微分。一般认为,small Batch 的使用会加快参数更新的速度,但会降低看完整个数据集(epoch)的速度,以及每次参数更新的质量不够高。
然而,实际上我们可以使用GPU平行计算加快大batch的运算速度(非倍增),但存在极限。
另一方面,当batch较小时,运算完一个epoch所需要的时间会很大。
也就是说,当使用了GPU后,Full batch 的参数更新速度快且稳定,优于 batch,是这样吗?
4.2 Small Batch 的出乎意料
反直觉的是,实验发现 smaller batch 反而增强了模型训练后的效果。noisy 反而是有价值的?
不稳定有助于跳出 critical point
不稳定有助于收敛到平稳 Minima
当处于平稳 Minima 时,测试集与训练集的细微变化对结果的影响不会很大,避免overfiting。
4.3 Batch 使用总结
目前也有很多学者探讨如何结合 small batch 和 large batch的优势,达到鱼与熊掌兼得的目的。
5. 重置模型的损失函数及影响
损失函数会影响我们训练模型的难度以及最终模型的性能。因此,我们可以尝试修改损失函数。
5.1 更改损失函数
回归和分类是机器学习中常见的两个任务,对于任务完成的好坏也有不同的损失函数,如果错用可能很难训练出满意的模型。例如,如果将一个分类任务当做回归任务来处理,可能会出现问题。
与回归不同的是,在处理分类任务时,我们需要使用 one-hot vector 来处理类别变量。即,在神经网络模型中,回归任务的输出只有一项,而分类任务会有很多项。
为了更方便的进行分类,一般会使用softmax函数将连续值映射为0-1的值。
注意:Softmax和 Sigmoid 本质上是一样的,后者用于二分类任务中。
分类任务的损失函数一般使用 cross-entropy,要比使用 MSE 要好得多。
从下图中可以直观发现使用 MSE 训练分类模型的不足:很容易陷入 critical point。
5.2 更改输入变量
除了更改损失函数外,我们还可以更改输入的变量尺度,来间接降低模型训练的难度。
从上图可知,改变两个参数相同大小,参数对应的变量范围大小决定了影响模型性能的程度。反之,如果将变量的范围归一化,那么不同参数的敏感性就相同,可以使用相同的学习率。
在实际操作中,可以对 或 做标准化,结果差异并不大,任意选择一个即可。
要注意在每一层网络中都要进行标准化的操作,而不仅仅是一开始做一次而已。另外,均值和方法的计算需要考虑所有样本,但这样计算效率就会很低,可以考虑使用Batch Normalization技术。
Batch 需要设置足够大,以便计算有效的均值和方差(保证代表性)。 和 是引入是为了防止均值为0带来的负面影响,具体值由学习得到。
再具体实施时,我们不能等到样本达到一个 Batch 才开始计算。当达不到时,我们使用移动平均来考虑之前的数据进行均值和方差的计算。
实验表明,Batch normalization 可加快收敛速度,但未必提高收敛质量。目前没有很好的解释。
参考资料