参考引用:
Blog: An overview of gradient descent optimization algorithms
review paper: An overview of gradient descent optimization algorithms
在搭积木的时候往往面临要选择 optimizer, 虽然说经验指导下选来选去都是那几个(玄学炼丹),但是作为一个有 wei 点 le 志 zhuang 向 bi 的科研菜鸟,还是想自己整理学习一下各个 optimizer 的特点,留下此篇。
Gradient Descent 梯度下降
Gradient descent is one of the most popular algorithms to perform optimization in Neural network. Nearly all of the state-of-the -art Deep Learning Library contains implementations of various algorithms to optimize gradient descent.
在台湾大学李宏毅教授的《机器学习》课程中,李宏毅教授把机器学习总结为三个 step:
- step 1 : define a set of function as model
- step 2 : find a function evaluate the goodness of function
- step3: pick the best function (optimization)
Gradient descent 是用于 minimize the object function (or say, the loss function) 的一种推广方法。我们知道在很多机器学习的课程中,介绍 gradient descent 都从多项式函数的优化开始讲的,也就是类似 这样的函数求极值问题。学过一点数值计算的话我们就知道可以用最小二乘法来求解这种问题,但是最小二乘一般来说是针对于线性方程组的求解方法,所以为了推广到非线性的问题,使用 gradient descent 是不错的选择。
gradient descent 算法详解:http://cs231n.github.io/optimization-1/
Gradient descent variants
在处理很多很多 samples,或者说在训练数据集 dataset 的时候,我们就要对最原始介绍的 gradient descent 做变动(其实就是把算法搬到数据集上应用)。
数据集的大小会影响运行 gradient descent 的速度,因为 gradient descent 的求导/求偏导 运算是很花时间的,求一次导数更新一次参数值。如果一次性处理太多 sample 的话,那更新一次参数也未免太慢了;所以就有了设置 batch 的想法:一次就计算一批,由 batch 的 loss function 梯度来决定(近似)整个 dataset 的 loss function 梯度。
根据数据集的大小,我们需要 make a trade-off between the accuracy of the parameter update and the time it takes to perform an update. 这里有三种 variants:
- Batch gradient descent ( vanilla gradient descent )
- Stocastic gradient descent
- Mini-batch gradient descent
下面一个一个讲:
Batch gradient descent ( vanilla gradient descent )
Traning set 不管有多少 sample,全都拿去计算 gradient. 训练集在 500 以下都可以直接用。
Batch gradient descent can be very slow and is intractable for datasets that don't fit in memory. Batch gradient descent also doesn't allow us to update our model online .
Batch gradient descent is guaranteed to converge to the global minimum for convex error surfaces and to a local minimum for non-convex surfaces.
伪代码如下
for i in range( number_epoch ):
parameters_gradient = evaluate_gradient(loss_function, full_data, parameters)
parameters = parameters - learning_rate * parameters_gradient
缺点: Batch gradient descent performs redundant computations for large datasets, as it recomputes gradients for similar examples before each parameter update
Stochastic gradient descent (SGD)
Stochastic gradient descent performs a parameter update for each training example and laybel . 来一个 sample 算一次 gradient。
SGD does away with redundancy of recomputes gradient by performing one update at a time. It is therefore usually much faster and can also be used to learn online.
不过每来一个 example 就 update 一次参数未免也太频繁了,而且每次来的 example 其实个体差异性挺大的,这样导致最后 object function 波动很厉害:SGD's fluctuation enables it to jump to new and potentially better local minima. On the other hand, this ultimately complicates convergence to the exact minimum, as SGD will keep overshooting.
不过,只要我们缓慢的减小 learning rate,SGD 就可以表现出和 Batch GD一样的性能 —— 陷入全局最优 or 局部最优。
伪代码如下:
for i in range(number_epochs):
np.random.shuffle(data)
for example in full_data:
parameters_gradient = evaluate_gradient(loss_function, example, parameters)
parameters = parameters - learning_rate * parameters_gradient
Mini-batch gradient descent
结合上述两种方法的利弊(其实上面两种方法对应两个极端情况),我们取一个 Batch (批次),一批数据算一次 loss 然后算偏导进行参数更新。假设一个 batch 有 n 个 example, 也就是 batch_size = n, 有:
设置 batch 的好处在于:
- reduces the variance of the parameter updates, which can lead to more stable convergence
- can make use of highly optimized matrix optimizations common to state-of-the-art deep learning libraries that make computing the gradient w.r.t. a mini-batch very efficient
常用的 batch 大小: 50、100、128、256.... 有的观点认为用 2 的幂次会更快。
伪代码如下:
for i in range(number_epochs):
np.random.shuffle(data)
for batch in get_batch(full_data, batch_size=128):
parameter_gradient = evaluate_gradient(loss_function, batch, parameter)
parameter = parameter - learning_rate * parameter_gradient
Challenge 困难与挑战
照这么看,似乎 Gradient Descent 已经很好的满足了我们的使用需求了。然而进一步实践可以发现即使是 mini_batch GD 也存在很多问题: 我们只解决了由单次运算数据量引起的梯度下降准确性 accuracy 问题与运算时间(效率)的问题,还有其他困难和挑战:
- How to choose a proper learning rate ?
- If we use 'Learning Rate Schedules', i.e. adjusting (or say, reducing) the learning rate according to a pre-defined schedule or when the change in objective between epochs falls below a threshold. These schedules and thresholds have to be defined in advance and are thus unable to adapt to a dataset's characteristics.
- Additionally, the same learning rate applies to all parameter updates. If our data is sparse and our features have very different frequencies, we might not want to update all of them to the same extent, but perform a larger update for rarely occurring features.
- Another key challenge of minimizing highly non-convex error functions common for neural networks is avoiding getting trapped in their numerous suboptimal local minima & saddle points. For saddle points, they are usually surrounded by a plateau of the same error, which makes it notoriously hard for SGD to escape, as the gradient is close to zero in all dimensions.
面对以上这些问题与挑战,有许多改进的方法不断涌现:
Momentum 动量
讨论一个二维的优化问题:假设两个维度上的梯度分布是不同步的,比方说梯度曲线为椭圆形(这样的情况经常出现在 local minima 附近),如下图所示。图中最中间的小椭圆为梯度引导的最低处,可以把这个图想象成一个峡谷,SGD 的优化目标就是要优化到最中间的低处去。
在这种情况下,SGD 会在峡谷的两侧表面频繁的震荡,使得整个优化过程十分缓慢。
Momentum 就是帮助 SGD 在这种条件下进行加速的一种方法: 在每次对 parameter vector 进行更新时,加上一个动量项,即上一次梯度更新的步长 很小的常量 :
Vanilla_SGD:
SGD_with_ Momentum :
常常取 0.9、0.8 等等,而初速度其实自己定义就行,初速度为0就可以,初始梯度会赋予它更新的速度值
在使用 Momentum 的时候,由于动量的不断累积,在 参数 update 过程中相同方向的 update 会越来越快,可以冲过一些 saddle point, 使得算法更快收敛、震荡次数更少。
Nesterov accelerated gradient
重新想回 Momentum,我们设想假如一个 minima 位于两座“山”之间,由于 Momentum 的存在,小球总是要往两边滚来滚去往复很久才能最终停在中间位置。有没有一种方法能够让小球能够在小球准备冲上下一座“山”之前就减速呢?
Nesterov accelerated gradient (NAG) 就是这样一种方法。
回想一下我们在 Momentum 方法中用到的动量项 代表了上一次梯度更新的影响, 表示参数减去动量项的影响后,下一次参数应该更新的 “大概位置”。这里说大概位置是因为真实的下一次参数应该更新的位置其实是 . 如果我们知道了下一次位置的近似值,根据下一次的位置求偏导求更新的步长来指导当下的运动,那么算法就有了 “预见性 prescience”, 这样就可以让在参数准备到达 minima 的时候提前减速,而不用过多受 Momentum term 的影响 push 到更远的地方。
SGD with Momentum :
Nesterov accelerated gradient :
更多更具体的数学解释,可以参照:http://cs231n.github.io/neural-networks-3/
上面的方法已经可以让 optimizer 根据 Loss function surface 自适应的进行加速/减速了,但科学家还不满足,之前计算 gradient 都是计算 ,各个分量(维度)在统一的 learning rate 下进行更新。我们还希望根据每个单独的参数进行调整更新,以便根据它们的重要性执行较大或较小的 update。
Adagrad
Adagrad 的好处在于:It adapts the learning rate to the parameters.
所谓的 “Adapts 自适应” 就是说,在 frequently occurring features 时能够减小 learning rate;在 infrequently occurring features 时能够增大 learning rate.什么意思呢?就是当优化过程中一些 feature 频繁出现的时候(例如说 loss function 的 error 不太怎么改变的时候),表示算法接近收敛了,这个时候就要减小 learning rate,也就是说减少步长去逐步接近那个 minima.
For this reason, Adagrad is well-suited for dealing with sparse data.