tf.train.MomentumOptimizer
的文档提供use_nesterov
参数,以利用Nesterov的加速渐变(NAG)方法。
但是,NAG要求在不同于当前变量的位置处计算渐变,并且apply_gradients
接口仅允许传递当前渐变。因此,我不太了解如何使用此接口实现NAG算法。
文档说明了以下有关实现的内容:
use_nesterov
:如果是,请使用Nesterov Momentum。见Sutskever et al., 2013。这个 实现总是计算渐变的值 传递给优化器的变量。使用Nesterov Momentum制作 变量跟踪文章中名为theta_t + mu*v_t
的值。
通过链接阅读了论文,我对这个描述是否回答了我的问题有点不确定。当界面不需要提供梯度函数时,如何实现NAG算法?
答案 0 :(得分:2)
<强> TL; DR 强>
TF对Nesterov的实现确实是原始公式的近似值,对于高动量值有效。
<强>详情
这是一个很好的问题。在本文中,NAG更新定义为
vt+1 = μ.vt - λ.∇f(θt + μ.vt)
θt+1 = θt + vt+1
其中f
是我们的费用函数,θt
我们的参数t
,μ
动量,λ
学习率; vt
是NAG的内部累加器。
与标准动量的主要区别在于θt + μ.vt
处的渐变使用,θt
处的不。但正如您所说,tensorflow仅使用θt
处的渐变。那么诀窍是什么?
您引用的文档部分实际上提到了部分技巧:算法正在跟踪θt + μ.vt
,不是 θt
。另一部分来自对高动量值有效的近似值。
让我们从纸上略微更改符号,以便累加器坚持使用tensorflow的定义。我们来定义at = vt / λ
。更新规则稍有改动
at+1 = μ.at - ∇f(θt + μ.λ.at)
θt+1 = θt + λ.at+1
(TF的这种变化的动机是,现在a
是一个纯粹的梯度动量,与学习率无关。这使得更新过程对λ
的变化具有鲁棒性,这种可能性很常见练习,但文章没有考虑。)
如果我们注意到ψt = θt + μ.λ.at
,那么
at+1 = μ.at - ∇f(ψt)
ψt+1 = θt+1 + μ.λ.at+1
= θt + λ.at+1 + μ.λ.at+1
= ψt + λ.at+1 + μ.λ.(at+1 - at)
= ψt + λ.at+1 + μ.λ.[(μ-1)at - ∇f(ψt)]
≈ ψt + λ.at+1
最后的近似值适用于强动量值,其中μ
接近1,因此μ-1
接近于零,∇f(ψt)
与a
相比较小 - 实际上这最后的近似值更具争议性,对频繁梯度切换的方向不太有效。
我们现在有一个使用当前位置的渐变的更新,规则非常简单 - 它们实际上是标准动量的那些。
但是,我们需要θt
,而不是ψt
。这就是我们在返回之前将μ.λ.at+1
减去ψt+1
的原因 - 并且为了恢复ψ
,它会在下次调用时再次添加。{/ p>
答案 1 :(得分:1)
我在网上看不到这方面的任何信息,链接的文章肯定没有帮助,所以我看了unit tests for tf.train.MomentumOptimizer
,从中我可以看到实施的测试经典动量和NAG模式。
var = var + accum * learning_rate * momentum
accum = accum * momentum + g
var = var - learning_rate * accum
var = var - accum * learning_rate * momentum
其中accum
从0开始并在每一步都更新。以上是单元测试中配方的修改版本,我觉得有点混乱。以下是我对每个参数所代表的解释所安排的同一组方程式(虽然我可能错了):
average_grad_0 = accum # previous rolling average
average_grad_1 = accum * momentum + g # updated rolling average
grad_diff = average_grad_1 - average_grad_0
adjustment = -learning_rate * (grad_diff * momentum + average_grad_1)
var += adjustment
accum = average_grad_new
换句话说,在我看来,tensorflow
的实现尝试猜测&#34;调整后的渐变&#34;在NAG中假设新的梯度将通过当前平均梯度加上动量的乘积和平均梯度的变化来估计。我很乐意看到这方面的证据!
以下是有关如何根据测试在tensorflow
中实现经典和nesterov模式的更多细节。
对于use_nesterov=False
,基于doTestBasic
函数,我们有以下初始参数:
learning_rate = 2.0
momentum = 0.9
var_0 = 1.0 # at time 0
grad = 0.1
实际上,上面只是grads_0
和vars_0
数组的第一个元素,但我只关注单个值。对于随后的时间步,我们有
var_1 = 1.0 - (0.1 * 2.0)
var_2 = 1.0 - (0.1 * 2.0) - ((0.9 * 0.1 + 0.1) * 2.0)
我将其解释为含义;
var_1 = var_0 - (grad * learning_rate)
var_2 = var_1 - ((momentum * grad + grad) * learning_rate)
如果我们假设出于单元测试grad_0 == grad_1 == grad
的目的,那么这作为经典动力的表达是有意义的。
对于use_nesterov=True
,我查看了_update_nesterov_momentum_numpy
函数和testNesterovMomentum
测试用例。
_update_nesterov_momentum_numpy
函数具有以下定义:
def _update_nesterov_momentum_numpy(self, var, accum, g, lr, momentum):
var = var + accum * lr * momentum
accum = accum * momentum + g
var = var - lr * accum
var = var - accum * lr * momentum
return var, accum
并在单元测试中调用它:
for t in range(1, 5):
opt_op.run()
var0_np, accum0_np = self._update_nesterov_momentum_numpy(
var0_np, accum0_np, var0_np * 10, 2.0, 0.9)