目录
1.随机梯度下降算法问题及解决
1.1 随机梯度下降算法SGD的问题
1.2 具有动量的梯度下降算法SGD+Momentum
1.3 Nesterov加速梯度法
1.4 AdaGrad
1.5 RMSProp
1.6 融合!Adam算法
2. 学习率的选取
3. 正则化
3.1 dropout正则化
4. 迁移学习
1.随机梯度下降算法问题及解决
1.1 随机梯度下降算法SGD的问题
回顾之前所学习的内容,训练一个神经网络的核心是一个优化问题,我们写下损失函数,定义网络权重的每一个值,损失函数告诉我们这些权重值在解决问题的时候是好是坏,我们设想在当前权重下,损失函数给了我们漂亮的等高线图,如下:
对应了两个参数,等高线图对应着损失值,目标是找到红色最深的区域,其对应了损失函数最小值的权重值。
我们之前用了最简单的随机梯度下降法完成了这一步骤。但是这个方法在实际使用中会出现诸多问题:
随机梯度下降算法的问题之一是我们改变在水平方向改变值损失函数变化的非常慢,在垂直方向改变值损失函数变化的非常快。
运行随机梯度下降算法效果如下图:
原因正是因为这类目标函数梯度的方向并不是与最小值成一个直线,因此当我们计算梯度并沿着前进时可能一遍遍跨过这些等高线。这在高维参数中更常见,因为我们的参数可能包含百万,千万甚至上亿个。
随机梯度下降法的另外一个问题是会出现局部极小值或鞍点:如果损失函数如下图所示,随机梯度下降法得到的最终结果会卡在中间。因为这里是局部最小值,梯度为0。
关于鞍点,如下图,你可以设想在该点上往一个方向是向上往另一个方向是向下,在当前的位置梯度为0,在这种情况下函数将被卡在鞍点因为这里梯度为0。
1.2 具有动量的梯度下降算法SGD+Momentum
解决局部极小值或鞍点的一个方法是加入“动量”项。
这个思想就是保持一个不随时间变化的速度,并且我们将梯度估计添加到这个速度上。然后在这个速度的方向上步进而不是在梯度的方向上步进。
通俗的来讲,加入惯性/速度,使得优化不容易落入局部最优解或停留在鞍点。
同时这里有一个超参数(rho),现在在每一步我们采用当前的速度然后用摩擦系数来对其衰减,摩擦系数通常取值0.9,我们采用当前的速度然后使用摩擦系数进行衰减之后加到梯度上,现在我们在速度向量的方向上步进而不是在原始的梯度向量。
我们想象一下在鞍点或局部最小值点发生了什么,这就好像一个小球下山,它被赋予了速度,即使在鞍点或局部最小值点时梯度比较小,但他因为被赋予了速度速度,还是可以穿越这个局部最小值点然后继续下降。同时对于上图所述这种情况,一旦我们使用动量,这些Z字型的曲线曲折也会很快互相抵消,这会有效的降低我们朝敏感方向步进的步伐,而在水平方向上,我们的速度只会不断增加,实际上还会加快在水平方向上的梯度下降。
1.3 Nesterov加速梯度法
同样还有一种叫做Nesterov加速梯度的梯度下降计算方法,有时也称之为Nesterov动量,相对于我们所说的Momentum取“混合”,它把这个顺序改变了。在普通的SGD动量中,我们估算当前位置的梯度,然后取速度和梯度的混合。但在Nesterov加速梯度中,要做的事情有一点不同,我们从红色点开始,在取得的速度的方向上进行步进(向量叠加),之后计算这个位置的梯度,随后回到初始位置将这两者混合起来。 它的公式如下:
1.4 AdaGrad
还有一种是AdaGrad方式的梯度下降:
核心思想是在优化的过程中需要保持一个在训练过程中的每一步的梯度的平方和的持续估计,与速度项不同的是我们现在有了一个梯度的平方和,在训练时我们会一直累加当前梯度的平方到这个梯度平方项,当我们在更新我们的参数向量时我们会除以这个梯度平方项。
这样的放缩对如上图所述场景有什么改进呢?沿其中一个轴我们有很高的梯度而另一个轴方向却有很小的梯度,那么随着我们累加小梯度的平方我们会在最后更新参数向量时除以一个很小的数字从而加速了在小梯度维度上的学习速率;然后在另一个维度方向上由于梯度变得特别大我们会除以一个非常大的数所以我们会降低这个维度方向上的训练进度。
不过当t(时间)越来越大的时候在训练的过程中使用AdaGrad会发生什么?
步幅会越来越小,因为我们一直在随时间更新梯度平方的估计值,所以这个估计值随着训练过程中一直随着时间单调递增,这会导致我们的步长随着时间单调递减。在学习目标是一个凸函数的情况下这个方法效果很好,因为接近极值点时会逐渐慢下来最后到收敛,这点是(AdaGrad)在凸函数情况下的一个很好的特性。但是在非凸函数的情况下,因为我们一旦到达一个局部极值点时,使用AdaGrad会让梯度下降在这里被困住从而使得训练过程无法再进行下去。
1.5 RMSProp
因此我们对AdaGrad有一个变体叫做RMSProp。在RMSProp中我们仍然计算梯度的平方,但是我们不累计梯度的平方,而是让平方梯度按照一定比率下降。我们看一下效果:
1.6 融合!Adam算法
我们将这些好的效果融合到一起,得到Adam算法的雏形:
使用Adam,我们更新第一动量和第二动量的估计值,在红框里我们让第一动量的估计值等于我们梯度的加权和(上一次迭代和这次迭代)。我们在次就有一个第二动量的动态估计值,像AdaGrad和RMSProp一样就是一个梯度平方的动态近似值。
现在我们来看看怎么更新它们,我们使用第一动量,有点类似于速度,除以第二动量的平方根,这样的话Adam最后看起来有点像RMSProp加上动量或者看起来像动量加上第二个梯度平方,就像是合并了两者各自好的性质。但是这也有一个问题,在我们算法运行最开始的时候会发生什么?
beta2一个默认值是0.99。
开始时,我们将第二动量初始化为0,第二动量经过一步更新后,第二动量仍然非常接近0,我们在更新时除以一个接近0的数字我们在一开始得到一个很大的步长,但是这个很大很大的步长并不是因为这一步的梯度很大,只是因为我们人为地将第二动量初始化成了0。这样我们的初始化工作就彻底搞砸了。
因此Adam算法也增加了偏置校正项来避免出现开始时得到很大步长的问题出现:在我们更新了第一和第二动量之后,我们构造了第一和第二动量的无偏估计,通过使用当前时间不长t,现在我们实际上在使用无偏估计来做每一步更新,而不是初始的第一和第二动量的估计值。
Adam算法一般是我们的首选!
2. 学习率的选取
那么怎么选择这些算法的学习率呢?
当我们选择学习率时?有时候太高了就会导致(损失函数)爆炸,如黄色的曲线。如果学习率很小,如蓝色的曲线,它的收敛很慢。
一个方法是我们不必在训练时都采用一个学习率,有时候人们会把学习率沿着时间衰减
,有点像是结合了上图中不同曲线的效果:在训练开始时选择一些相对较大的学习率然后在训练过程中逐渐衰减地越来越小。一个衰减的策略是步长衰减,比如在第10万次迭代时可以衰减一个因子然后继续训练:
还有指数衰减,这是在训练过程中持续衰减:
我们可以看到这样的损失曲线,这就是因为学习率的持续衰减引起的优化:
学习率下降这种思想很常见,但是像Adam的优化算法就很少用。另外学习率衰减是一种二阶的超参数,通常不应该一开始就用上。通常你想要让神经网络开始工作,你想要挑选一个不带学习率衰减的不错的学习率来作为开始,尝试在交叉验证中同时调学习率衰减和初始学习率等等宴他的事情你会一头雾水的。设置学习率的方法是先尝试不用衰减看看会发生什么,然后仔细观察损失曲线看看你希望在哪个地方开始衰减。
另外,此节我们所讲的所有算法都是一阶优化算法。在这个一维的图中,我们有一个目标函数曲线,当前点是这个红色的点,我们在这个点上求一个梯度,我们用梯度信息来计算这个函数的线性逼近,这个相当于是对我们的函数进行的一阶泰勒逼近。我们想要找到逼近的最小值,但是这个逼近在稍大的区间内并不成立所以我们不能朝那个方向一下走太多。
事实上,这里梯度的想法用上了函数的一阶偏导,我们其实可以用二阶逼近,如下图:
这里我们同时考虑一阶和二阶偏导信息,现在我们对函数做一个二阶泰勒逼近,就是用一个二次函数来局部逼近我们的函数,因为是二阶函数我们可以直接逼近到我们的最小值点。这就是二阶优化的思想。
推广到多阶,就会得到一个叫做牛顿步长的东西,计算这个海森矩阵即二阶偏导矩阵,接着求这个海森矩阵的逆以便直接走到对你的函数用二次逼近后的最小值的地方。与之前的方法相比,这种方式不用考虑学习率。但是这对于深度学习来说有点不切实际,因为海森矩阵的计算成本太大了,内存无法完成,因此,我们常用拟牛顿法来替代牛顿法,不是直接地去求完整的Hessian矩阵的逆,而是去逼近这个矩阵的逆,常见的是低阶逼近。
L-BFGS就是一个二阶优化器,用Hessian矩阵来逼近,但很多深度学习的并不适应这个算法,因为这些逼近对随机的情况处理的不是很多。而且在非凸问题上表现得不是很好。
因此在实际中Adam已经是一个很好的选择了,但如果你的计算能力足够强的话(能够承受整个批次的更新),而且你的问题中没有很多的随机性,那么L-BFGS是一个很好的选择。
目前我们讲过的所有策略都是在减少训练误差,这写优化算法都是在通过减少训练误差来减少损失函数,但是我们并不在意训练误差,我们更在意在我们没见过的数据集的表现。我们很在意减少训练误差和测试误差之前的差距。现在的问题是如果我们已经很擅长优化目标函数,要怎么做来减少训练和测试之间的误差差距,以使得我们在没有讲过的数据中表现得更好呢?
一个简单的方法就是模型集成:我们选择从不同的随机初始值上训练10个不同的模型,到了测试时,我们就会在10个模型上运行测试数据然后平均10个模型的预测结果。把这些模型加在一起能够有效避免过拟合问题。其实有时候不用独立地训练不同的模型,我们可以在训练过程中保留多个模型的快照,然后用这些模型来做集成学习,在测试阶段,我们仍然需要把这些多个快照的预测结果做平均,但我们可以在训练过程中收集这些快照。
3. 正则化
3.1 dropout正则化
我们如何能提高单一模型的效果?正则化
我们在模型中加入一些成分来防止训练集上的过拟合,从而使测试集上的效果得到提升。 L2正则化在神经网络中的表现不是很好,一个很好的选择是dropout正则化。
它是这样运行的:每次在网络中正向传播时,我们在每一层随机将一部分神经元置零,每次正向传播时随机被置0的神经元都不是相同的:
每次处理网络中的一层,我们经过一层网络算出这一层的值,随机将其中一些(激活值)置为0,然后在网络中前进。如果我们把左边这个全连接网络和右边经过dropout的版本进行对比,我们发现dropout的网络像是同样的网络变小了一号,因为我们只用到了一部分的神经元。并且每次正向传递都是不同的部分。
这个方法为什么可取?一个解释是人们觉得dropout避免了特征间的相互适应,假设我们要分类判断是不是猫?可能有一个神经元学习到了有一只耳朵,一个学习到了尾巴,一个学到了输入图像有毛
,将这些条件组合在一起来判断是不是猫。但现在加入drop之后我们判断是否是猫的时候,就是靠零散的特征来判断,这也许某种程度上抑制了过拟合。另一个解释是这是在单一模型中运行集成学习。
那dropout是如何在测试集上面表现的呢?
当我们使用了dropout后,我们把神经网络基本的运算都改变了,之前我们的神经网络里有权重w的函数f,输入x得到y,但我们选择有了一个输入z表示dropout中被置零的项(z是随机的),测试时引入一些随机性可能不是一个好主意。
我们考虑单个神经元输出是a,然后在测试时我们得到a的值是,在训练期间我们使用了dropout丢弃神经网络单元的概率是0.5,现在这个例子中训练期间的期望值可以算出解析解,我们将通过这四个掩码得到的值进行平均:
我们在测试时没有任何的随机性,而是用dropout的概率乘以这个输出现在这些期望值是一样的。
总结起来dropout在正向传播中非常简单,只需要随机添加几行代码随机对一些节点置0。在测试时的预测函数内仅仅增加了一点点乘法。
我们也可以采用反转dropout的方法让我们更快的运行在测试中:
还有一种dropout相关的算法叫做dropconnect,它不是在每次正向传播中将激活函数置零而是随机将权重矩阵的一些值置零,它们有一样的效果。
4. 迁移学习
加入不同正则策略可以帮助减小训练误差和测试误差的间隙,过拟合另外的一个原因是由于数据不够导致的。我们希望得到一个大的模型,但小数据集合时很容易过拟合,正则化是一种很好的方法,另一种方法就是迁移学习。
迁移学习使我们不再需要超大的样本集。
它的思想很简单,首先找到一些卷积神经网络在一个非常大的数据集训练例如ImageNet(可以解决100种物体的分类),现在你想尝试的想法是把从这个数据集训练出的提取特征的能力用到我们更感兴趣的小的数据集上(比如狗的分类),接着我们的做法是修改从最后一层的特征到最后的分类输出之间的全连接层,我们要重新随机初始化这部分矩阵,对于ImageNet它是4096乘以1000,对于我们新的分类,矩阵大小变为4096乘以C例如10或者任何一个数,重新随机初始化最后的矩阵并冻结前面层的权重,现在我们只需要训练一个线性分类器(最后一层)让它在我们的数据上收敛,当你只处理一个小的数据集的时候这会让我们的工作很完美!
当我们的时间数据更长一些的话,我们可以试着将学习率调低(最初的网络参数可能是在ImageNet上收敛的,其泛化能力已经很强了,我们只需要它有很小的调整来适应我们的训练集)并训练更多层(自上而下)。
当我们使用迁移学习时,可以想成是一个2乘以2的情景网格: