梯度下降思想
Gradient Descent 的核心思想非常的简单。针对一个模型,我们最终希望它的(非负的)损失函数最小:𝜽 = argmin J(𝜽)
。
根据 Gradient Descent on Wikipedia,GD 秉持一个最朴素的思想:J'(𝜽)
指向哪里,沿着它的反方向 -J'(𝜽)
走(即 𝜽_next = 𝜽_now - J'(𝜽)
),就能让 J(𝜽)
降低。
梯度下降公式:
1 | 𝜽_next = 𝜽_now - 𝜺*J'(𝜽) |
BGD
每次计算整个训练集损失函数的均值,然后求导进行更新。
由于这种方法是在一次更新中,就对整个数据集计算梯度。
存在两个问题:
- 计算速度慢
- 计算占用内存大
所以几乎无法投入实际使用
SGD
每次对单个样本计算损失函数和梯度,然后进行更新。
存在问题:
- 存在较大的噪声,损失函数存在较大的震荡。
BGD可能收敛到局部最优点,SGD震荡可以跳出局部最优点,到达更好的最优点。
Mini-batch SGD
结合BGD和SGD的特点,每次利用训练集的一部分样本更新模型。
存在问题:
但是每次使用小部分数据更新模型,这小部分数据的梯度与整体数据的梯度可能存在偏差,还是会存在震荡。
Momentum
单批样本更新存在偏差,那么就不要全速更新,一定程度上保留之前的梯度。颇有引入惯性定理的味道。
1 | v_t+1 = 𝝁v_t - 𝜺J'(𝜽_t) |
Nesterov Momentum
Momentum算法的改进版
1 | v_t+1 = 𝝁v_t - 𝜺J'(𝜽_t + 𝝁v_t) |
区别在于,将梯度计算放在施加当前速度之后。相当于计算梯度时,按照全局趋势先走半步,进一步减小偏差。
以上算法在各个维度上的下降速度是一致的。然而在实际情况中,损失函数对不同的维度的敏感度是不一样的,因此对不同维度设置不同的学习率是一种比较好的方式。以下自适应学习率的方法即是这种思想的体现。
Delta-bar-delta
基于一种简单的思想:
- 如果过去两次更新的梯度符号相同,代表方向正确,增大更新时的步长
- 如果过去两次更新的梯度符号相反,代表方向不定,缩小更新时的步长
这种方法只能用在全批量优化中,在 Mini-Batch 上却不好。每个 Mini-Batch 之间的差别会使得每一次更新本身的步长就会有很大的差异,这一点和全量数据更新时有比较明显的差异。
Adagrad
累计所有参数的历史梯度平方和,参数学习率与梯度平方和2次方根成反比。达到的效果是:
在陡坡,学习率变小
在平地,学习率变大
1 | r = r + g^2 |
存在问题:
- 学习率过度减小
适合凸函数优化
- 优化器需要要对每个参数保存一个中间值,占用内存参数的两倍。
RMSProp
Hinton改进Adagrad算法适用于非凸优化场景。
加入指数衰减,丢弃遥远过去的历史:
1 | r = p*r + (1-p)g^2 |
这种算法被证明是深度学习模型中非常有效的一种方法。
- 优化器需要要对每个参数保存一个中间值,占用内存参数的两倍。
Adadelta
1 | r = p*r + (1-p)g^2 |
不用设定学习率。
- 优化器需要要对每个参数保存2个中间值,占用内存参数的三倍。
Adam
集合RMSProp和动量算法
1 | m_t = 𝜷_1*m_t-1 + (1 - 𝜷_1)*J'(𝜽) |
- 优化器需要要对每个参数保存2个中间值,占用内存参数的三倍。
优化器效果对比
如果数据是稀疏的,就用自适用方法,即 Adagrad, Adadelta, RMSprop, Adam。
RMSprop, Adadelta, Adam 在很多情况下的效果是相似的。
Adam 就是在 RMSprop 的基础上加了 bias-correction 和 momentum,
随着梯度变的稀疏,Adam 比 RMSprop 效果会好。
整体来讲,Adam 是最好的选择。
很多论文里都会用 SGD,没有 momentum 等。SGD 虽然能达到极小值,但是比其它算法用的时间长,而且可能会被困在鞍点。
如果需要更快的收敛,或者是训练更深更复杂的神经网络,需要用一种自适应的算法。