优化器的理解与选择
Contents [hide]
讲优化器的,网上有很多资料,但是大多都是套下公式泛泛而谈,我虽然想做到把优化器的理论、算法、代码和优劣特性等,但碍于实力有限,只能当作快速学习的学习笔记参考,本文大部分内容参考魏秀参的《CNN卷积神经网络》这本书,想要深入学习我建议直接去细致看论文。
网络模型优化算法选择
深度卷积神经网络通常采用随机梯度下降类型的优化算法进行模型训练和参数求解。经过近几年深度学习的发展,也出现了一系列有效的网络训练优化新算法。在实际工程中,Pytorch
和 Keras
等框架几乎都已经封装好了最新的优化器算法,我们只需根据自身需要选择合适的优化器即可。但是理解一些典型的一阶优化算法还是很有必要的,本文将简单介绍这些算法的定义。
为了方便理解,下文的计算公式中,假设待学习的参数为 \(w\),学习率(或步长)为 \(η\),一阶梯度值为 \(g\),\(t\) 表示第 \(t\) 轮训练。
优化算法优化的是神经元参数的取值\(w、b\)。优化过程如下:假设 \(θ\) 表示神经网络中的参数, \(J(θ)\) 表示在给定的参数取值下,训练数据集上损失函数的大小,则优化过程即为寻找某一参数 \(θ\) ,使得损失函数 \(J(θ)\) 最小。
梯度下降法(Gradient Descent)
梯度下降法是最基本的一类优化器,目前主要分为三种梯度下降法:标准梯度下降法(GD, Gradient Descent),随机梯度下降法(SGD, Stochastic Gradient Descent)及批量梯度下降法(BGD, Batch Gradient Descent)。
随机梯度下降算法SGD
随机梯度下降算法(Stochastic Gradient Descent, 简称 SGD
)是神经网络训练的基本算法,其每次批处理训练时都会计算网络误差并作误差的反向传播,根据一阶梯度信息对参数进行更新,其计算公式如下:
$$w_{t} = w_{t-1} – \eta \cdot g$$
其中,一阶梯度信息 \(g\) 完全依赖于当前批数据在网络目标函数上的误差,所以可将学习率 \(η\) 理解为当前批的梯度对网络整体参数更新的影响程度。SGD
优化算法是最经典的神经网络优化方法,虽然收敛速度慢,但是收敛效果比较稳定。
基于动量的随机梯度下降算法
基于动量(momentum)的随机梯度下降算法用于改善 SGD 更新时可能产生的振荡现象,其计算公式如下:
$$\upsilon_{t} = \mu \cdot \upsilon_{t-1} – \eta \cdot g$$$$w_{t} = w_{t-1} + \upsilon_{t}$$
其中,\( \upsilon_{t} \) 为动量因子,控制动量信息对整体梯度更新的影响程度,一般设为 0.9
。基于动量的随机梯度下降算法除了可以抑制振荡,还可以在网络训练中后期趋于收敛、网络参数在局部最小值附近来回震荡时帮助其跳出局部限制,找到更优的网络参数。 值得注意的是,这里动量因子除了设定为 0.9 的静态因子,还可以设置为动态因子,常用的动态因子设定方式是将动量因子初始化值设为 0.5,之后随着训练轮数的增长逐渐变为 0.9 或 0.99。
mxnet 框架中使用 SGD
优化器函数及参数配置如下:
sgd_opt = mx.optimizer.SGD(
momentum=0.9,
learning_rate=args.base_lr,
wd=0.00001,
rescale_grad=1.0/args.batch_size,
lr_scheduler=lr_scheduler.CosineScheduler(decay_steps=args.decay_steps, stop_factor_lr=0.00001)
)
Nesterov型动量随机梯度下降法
此算法是对 Momentum 的改进算法,不是在当前位置求梯度,而是每次都向前看一步,使用未来的梯度值,在动量梯度下降法更新梯度时加入对当前梯度的校正。相比一般动量法,Nesterov 型动量法对于凸函数在收敛性证明上有更强的理论证明,计算公式如下:
$$w_{ahead} = w_{t-1} + \mu \cdot \upsilon_{t-1}$$$$\upsilon_{t} = \mu \cdot \upsilon_{t-1} – \eta \cdot \bigtriangledown_{w_{ahead}}$$$$w_{t} = w_{t-1} + \upsilon_{t}$$
其中\(\bigtriangledown_{w_{ahead}}\)表示\({w_{ahead}}\)的导数信息。
总结:无论是随机梯度下降法、基于动量的随机梯度下降算法,还是Nesterov型动量随机梯度下降法,本质上都是为了使梯度更新更加灵活,这对优化神经网络这种拥有非凸且异常复杂的函数空间的学习模型很重要。但是这三种方法都有相同的局限性,我们知道较小的学习率更加适合网络后期的优化,但是这三种方法的学习率 \(η\) 固定不变,并未将学习率的自适应性考虑进去。
自适应学习率优化算法
自适应学习率优化算法针对于机器学习模型的学习率,传统的优化算法要么将学习率设置为常数要么根据训练次数调节学习率,极大忽视了学习率其他变化的可能性。然而,学习率对模型的性能有着显著的影响,因此需要采取一些策略来想办法更新学习率,从而提高训练速度。目前的自适应学习率优化算法主要有:AdaGrad算法,RMSProp算法,Adam算法以及AdaDelta算法。
Adagrad方法
针对学习率自适应问题,Adagrad
法根据训练轮数的不同,对学习率进行动态调整,学习率动态调整公式如下:
$$\eta_{t} = \frac{\eta_{global}}{\sqrt{\sum_{t’}^{t} g^{2}_{t’}+\epsilon}} \cdot g_{t}$$
AdaGrad 是第一个自适应算法,通过每次除以根号下之前所有梯度值的平方和,从而使得步长单调递减。它根据自变量在每个维度的梯度值的大小来调整各个维度上的学习率,从而避免统一的学习率难以适应所有维度的问题。同时因为 \(\sum_{t’}^{t} g^{2}_{t’}\) 的变化与每个维度上的值有关,所以此方法可以解决各个维度梯度值相差较大的问题。不过, Adagrad
法仍需人为指定一个全局学习率 \(\eta_{global}\),同时,网络训练到一定轮数后,分母上的梯度累加过大会使得学习率为 0 而导致训练过早结束,RMSprop
就是对于 AdaGrad 的改进版本。算法解释如下:
AdaGrad 算法会使用一个小批量随机梯度 \(g_{t}\) 按元素平方的累加变量 \(r_{t}\)。在时间同步 0,AdaGrad 将 \(r_{0}\) 中每个元素初始化为 0。在时间同步 \(t\),首先将小批量随机梯度 \(g_{t}\) 按元素平方后累加到变量 \(r_{t}\):
$$r_{t} = r_{t-1} + g_{t}\odot g_{t}$$
\(\odot\)是按元素相乘,目标函数自变量 \(s_{t}\) 通过学习率动态调整公式如下:
$$s_{t} = s_{t-1} – \frac{\eta }{\sqrt{s_{t}+\epsilon }}\odot g_{t}$$
其中 \(\eta\) 是学习率, ϵ 是为了维持数值稳定性而添加的常数,如 \(10e−6\)。这⾥开⽅、除法和乘法的运算都是按元素运算的。这些按元素运算使得⽬标函数⾃变量中每个元素都分别拥有⾃⼰的学习率。
keras
中使用 adagrad
优化器代码如下:
keras.optimizers.Adagrad(lr=0.01, epsilon=None, decay=0.0)
参数解释
- lr: float >= 0. 学习率.
- epsilon: float >= 0. 若为 None, 默认为 K.epsilon().
- decay: float >= 0. 每次参数更新后学习率衰减值.
Adadelta法
AdaDelta 算法像 RMSProp、AdaGrad算法⼀样,使用了小批量随机梯度 \(g_t\) 按元素平⽅的指数加权移动平均变量 \(r_t\),但是通过引入衰减因子 ρ 消除 Adagrad 法对全局学习率的依赖。在时间步 0 ,它的所有元素被初始化为 0 ,在时间步t > 0,各变量计算公式如下:
$$r_{t} = \rho \cdot r_{t-1} + (1-\rho) \cdot g^{2}$$$$\eta_{t} = \frac{\sqrt{s_{t-1}+\epsilon }}{\sqrt{r_{t}+\epsilon }}$$$$s_{t} = \rho \cdot s_{t-1}+(1-\rho)\cdot (\eta_{t}\cdot g)^{2}$$
其中 \(\rho\) 为区间 [0,1] 间的实值:较大的 \(\rho\) 会促进网络更新,较小的 \(\rho\)会抑制网络更新。两个超参数的推荐设定为 \(\rho=0.95\),\(\epsilon=10−6\)。Keras
中使用 Adadelta
优化器代码如下:
keras.optimizers.Adadelta(lr=1.0, rho=0.95, epsilon=None, decay=0.0)
参数解释:
lr
: float >= 0. 学习率,建议保留默认值。rho
: float >= 0. Adadelta梯度平方移动均值的衰减率,对应公式中的ρ。epsilon
: float >= 0. 模糊因子. 若为 None, 默认为 K.epsilon()。decay
: float >= 0. 每次参数更新后学习率衰减值。
RMSProp法
RMSProp 法可以看作 Adadelta 法的一个特例,即使用全局学习率替换掉 Adadelta 法中的 \(s_t\),\(r_t\) 表示前 \(t\) 次的梯度平凡的均值:
$$r_{t} = \rho \cdot r_{t-1} + (1-\rho) \cdot g^{2}$$$$\eta_{t} = \frac{\eta_{global}}{\sqrt{r_{t}+\epsilon }}$$$$w_{t}=w_{t-1}-\eta_{t} \cdot g_{t}$$
RMSProp 算法和 AdaGrad 算法的不同在于, RMSProp算法使⽤了小批量随机梯度按元素平⽅的指数加权移动平均来调整学习率。Keras
中使用 RMSprop
优化器公式如下:
keras.optimizers.RMSprop(lr=0.001, rho=0.9, epsilon=None, decay=0.0)
参数
- lr: float >= 0. 学习率。
- rho: float >= 0. RMSProp梯度平方的移动均值的衰减率,对应公式中的 ρ。
- epsilon: float >= 0. 模糊因子. 若为 None, 默认为 K.epsilon()。
- decay: float >= 0. 每次参数更新后学习率衰减值。
Adam 法
Adam
法本质上是带有动量项的 RMSprop 法,计算公式如下:
$$m_{t}=\beta_{1}m_{t-1}+(1-\beta_{1})g_{t}$$$$v_{t}=\beta_{2}v_{t-1}+(1-\beta_{2})g^2_{t}$$$$\hat{m}_{t}=\frac{m_{t}}{1-\beta^{t}_{1}},\hat{v}_{t}=\frac{v_{t}}{1-\beta^{t}_{2}}$$$$w_{t+1}=w_{t}-\frac{\eta}{\sqrt{\hat{v}_{t}}+\epsilon}\hat{m}_{t}$$
这里,\(m_{t}\) 和 \(v_{t}\)分别为一阶动量项和二阶动量项。\(\beta_{1},\beta_{2}\) 为动力值大小通常分别取 0.9 和 0.999;\(\hat{m}_{t}, \hat{v}_{t}\) 分别为各自的修正值。\(m_{t}\) 表示 \(t\) 时刻即第 \(t\) 次迭代模型的参数,\(g_{t}=\Delta J(w_{t})\) 表示 \(t\) 次迭代代价函数关于 \(w\) 的梯度大小;\(\epsilon\) 是一个取值很小的数(一般为1e-8)为了避免分母为0
Adam与SGD比较
1,Adam 等自适应学习率算法对于稀疏数据具有优势,且收敛速度很快;但精调参数的 SGD(+Momentum)往往能够取得更好的最终结果。 2,用相同数量的超参数来调参,SGD 和 SGD +momentum 方法性能在测试集上的额误差好于所有的自适应优化算法,尽管有时自适应优化算法在训练集上的loss更小,但是他们在测试集上的 loss 却依然比 SGD 方法高, 3,自适应优化算法 在训练前期阶段在训练集上收敛的更快,但是在测试集上这种有点遇到了瓶颈。 4,所有方法需要的迭代次数相同,这就和约定俗成的默认自适应优化算法 需要更少的迭代次数的结论相悖!
优化算法常用tricks
首先,各大算法孰优孰劣并无定论。如果是刚入门,优先考虑 SGD+Nesterov Momentum 或者 Adam.(Standford 231n : The two recommended updates to use are either SGD+Nesterov Momentum or Adam)
选择你熟悉的算法——这样你可以更加熟练地利用你的经验进行调参。
充分了解你的数据——如果模型是非常稀疏的,那么优先考虑自适应学习率的算法。
根据你的需求来选择——在模型设计实验过程中,要快速验证新模型的效果,可以先用Adam进行快速实验优化;在模型上线或者结果发布前,可以用精调的SGD进行模型的极致优化。
先用小数据集进行实验。有论文研究指出,随机梯度下降算法的收敛速度和数据集的大小的关系不大。(The mathematics of stochastic gradient descent are amazingly independent of the training set size. In particular, the asymptotic SGD convergence rates are independent from the sample size. [2])因此可以先用一个具有代表性的小数据集进行实验,测试一下最好的优化算法,并通过参数搜索来寻找最优的训练参数。
考虑不同算法的组合。可以采用先用 Adam 进行快速下降,而后再换到 SGD 进行充分的调优的方法。切换策略可以参考网络其他资料。
数据集一定要充分的打散(shuffle)。这样在使用自适应学习率算法的时候,可以避免某些特征集中出现,而导致的有时学习过度、有时学习不足,使得下降方向出现偏差的问题。
训练过程中持续监控训练数据和验证数据上的目标函数值以及精度或者 AUC 等指标的变化情况。对训练数据的监控是要保证模型进行了充分的训练——下降方向正确,且学习率足够高;对验证数据的监控也可以避免出现过拟合。
制定一个合适的学习率衰减策略。可以使用定期衰减策略,比如每过多少个epoch就衰减一次;或者利用精度或者AUC等性能指标来监控,当测试集上的指标不变或者下跌时,就降低学习率。
参考资料
相关文章
- vue项目性能优化-前端加分项
- [C#.NET 拾遗补漏]10:理解 volatile 关键字(去除 Release 版本反向优化)
- 2021版 WordPress速度及性能优化终极指南 - WP小白
- 深入理解图片内存优化的常见方案和 AndroidBitmapMonitor 的原理
- 一文带你理解慢SQL分析与优化
- MySQL系统变量:深入理解并优化使用(mysql系统变量)
- 深入理解Oracle的优化器模式(oracle优化器模式)
- MySQL数据库存储优化探索(mysql存储优化的方法)
- 微软 Win11 Build 22000.100 更新发布:全新任务栏应用提醒,大量细节优化
- 分析Oracle数据库优化查询语句:深入理解执行计划(oracle看执行计划)
- 提升效率:MySQL查询优化器的应用(mysql的查询优化器)
- 一步步教你如何进行 MySQL 数据库分表优化(mysql数据库分表)
- 深入理解 Linux 的 {} 参数:优化脚本编程技巧!(linux参数)
- 深入理解Linux 3690:探究操作系统内部机制与优化技巧(linux3690)
- MySQL 存储过程:优化数据库操作流程(mysql使用存储过程)
- Oracle用户模式:理解和优化使用(oracle用户模式)
- 深入理解MySQL索引:分析与优化(mysql索引分析和优化)
- T3 MSSQL自动优化 让你的数据库更加安全高效(t3 mssql自动运行)
- 深入理解Linux shmall,优化你的内存管理策略(linuxshmall)
- Oracle内存优化之深入理解内存计算公式(oracle内存计算公式)
- Oracle内存构成理解和优化(oracle内存组成)
- 电商多点利用Redis优化体验(电商分布式redis)
- Oracle中表的合并优化技巧实践(oracle中进行表合并)
- 调整Redis最大链接数优化性能(修改redis最大链接数)
- Redis中一键多值优化频繁取一个键值(redis频繁取一个键值)
- 配置Redis请求优先级,优化性能(redis配置请求优先级)
- 内存Redis自动优化PHP内存管理(redis自动释放php)
- 理解Oracle中的0点表示,优化数据库性能(oracle 0点表示)
- 平安科技RPA负责人:流程优化视角下的“RPA+AI”实践
- asp.net程序优化尽量减少数据库连接操作