tf.train.MomentumOptimizer
的文档提供了一个use_nesterov
参数,用于启用Nesterov加速梯度(NAG)方法。
然而,NAG需要计算当前变量位置之外的梯度,而apply_gradients
接口只允许传递当前梯度。因此,我不太明白如何使用这个接口来实现NAG算法。
文档中关于实现的描述如下:
use_nesterov
:如果为True,则使用Nesterov动量。详见Sutskever等人,2013。此实现始终在传递给优化器的变量值上计算梯度。使用Nesterov动量使得变量跟踪论文中称为theta_t + mu*v_t
的值。
阅读了链接中的论文后,我不太确定这个描述是否回答了我的问题。当接口不需要提供梯度函数时,如何实现NAG算法?
回答:
简而言之
TensorFlow对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
(在TensorFlow中进行此更改的动机是,现在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
。这就是为什么在返回之前我们从ψt+1
中减去μ.λ.at+1
——为了恢复ψ
,在下一次调用时首先会再次加上它。