目前常用的优化器
深度学习的优化算法就是为了找到一个全局的最优参数使得loss最小的一种算法,目前一般采用梯度下降法。
梯度下降中有根据整个数据集进行更新的,还有一种是采用部分的数据进行更新。全体数据的更新对于凸函数可以
很好的收敛到全局极小值,对于非凸函数会收敛到局部极小值,而且数据很大的时候梯度更新太慢。而部分的数据进行
训练梯度更新比较快,但是容易受到噪声的影响,准确度就会降低,不是全局最优,但是当训练次数足够多的情况下
会趋向于局部最优。因此目前都是采用部分数据min_batch的模式进行训练。
BGD (batch gradient descent)
实例代码
for i in range(nb_epochs):
grad = cal_gradient(loss_fn, data, param)
param = param - lr*grad
这种方式需要对整个数据集计算梯度,所以计算比较慢,而且不能加入新的数据进行实时更新模型。
SGD(Stochastic gradient descent)
sgd每次都是对每一个样本进行梯度更新,这样更新速度就比较快。
for i in range(nb_epochs):
np.random.shuffle(data)
for val in data
grad = cal_gradient(loss_fn, val, param)
param = param - lr*grad
考虑到数据分布是有统计意义的,可能部分数据的梯度更新已经很接近总体的分布。但是sgd受到噪声的影响比较大,所以上图中的loss曲线抖动很大。
对比bgd和sgd,bgd可能会陷入局部极小值,而sgd可能到不了局部极小值,也有可能跳到更好的局部极小值。
降低学习率之后两者的收敛性差不多。
MBGD (mini-batch gradient descent)
每次采用一小批数据进行更新,这样相对于SGD收敛更稳定,同时相对于bgd而言可以保持比较好的梯度更新效率。
for i in range(nb_epochs):
np.random.shuffle(data)
for val_batch in getbatches(data, 10)
grad = cal_gradient(loss_fn, val_batch, param)
param = param - lr*grad
这里的n是一个超参数可以调节。
传统的梯度算法的问题
mbgd不能保证很好的收敛性,学习率太小就容易造成收敛速度慢,太大就会在极小值左右摆动甚至偏离。
以上三种朴素的梯度下降算法在非凸函数的时候容易陷入鞍点处,因为此时的梯度都接近0,容易卡在这里。
同时由于都是采用固定的学习率,这样学习率对于梯度没有自适应,我们一般希望在远离最优解的地方学习率大一点
而在最优解附近学习率小一点。
解决方法
可以从梯度方向和学习率来解决,在加快训练的同时,可以使得收敛更稳定。在下面的优化方法中都应用了
指数加权这一方法 https://www.cnblogs.com/guoyaohua/p/8544835.html
Momentum
利用指数加权引入了动量,这样在梯度方向不变时,速度很快,在梯度方向有所改变的维度上又会有所变慢。
这个方法都是迎着当前的趋势进行更新,不具备一些先验知识。
NAG(Nesterov Accelerated Gradient)
NAG在跟新会根据未来的梯度进行预期调节。
所以NGA在RNN上会有比较好的表现。
上面是在总体上进行梯度的调节,如果可以根据不同的参数的重要性进行不同程度的更新。
也就是在不同的参数上学习率有所不同。
Adagrad (Adaptive gradient algorithm)
对于低频的数据有较大的更新,对于高频的数据做较小的更新。
其中g(t,i)表示t时刻参数的梯度,
是一个对角矩阵,对角上面每一个值表示梯度的平方值的累积和。
Adagrad实现了不同参数学习率的自动调整。但是由于分母是梯度平方和的累积会使得学习率不断减少。
Adadelta
这个算法是adagrad的改进版本,它的梯度函数如下:
分母采用了梯度的均方根,就是将之前的梯度的平方求和转换成求平均值,用RMS简写
而E的计算又依赖之前的平均值和当前的梯度
RMSprop
RMSprop和Adadelta都是为了解决adagrad学习率急剧下降的方法。
Adam (adaptive moment estimation)
这种方式就是在学习率和梯度两个维度上进行了自适应的方式。相当于RMSprop+Momentum
除了像RMSprop存储了过去梯度平方vt的指数衰减平均值,也需要保存momentum中梯度的指数平均衰减值。
m(t)和v(t)的初始化都为0,
梯度更新规则:
实践证明Adam效果要好一点。