我正在尝试使用Deepmind在张量流中自己实现DQN文件,并且在剪切损失函数时遇到了困难。
以下是描述损失裁剪的自然纸的摘录:
我们还发现将更新中的错误项剪切到介于-1和1之间是有帮助的。因为绝对值损失函数| x |对于x的所有正值,其导数为-1,对于x的所有正值,导数为1,将平方误差限制在-1和1之间,对应于使用绝对值损失函数( - 1,1)间隔。这种形式的错误裁剪进一步提高了算法的稳定性。
(链接到完整论文:http://www.nature.com/nature/journal/v518/n7540/full/nature14236.html)
到目前为止我尝试使用
clipped_loss_vec = tf.clip_by_value(loss, -1, 1)
修剪我在-1和+1之间计算的损失。在这种情况下,代理商没有学习正确的政策。我打印出网络的渐变,并意识到如果损失低于-1,渐变都会突然变为0!
我发生这种情况的原因是削减损失是(-inf,-1)U(1,inf)中的常数函数,这意味着它在这些区域中具有零梯度。这反过来确保整个网络中的梯度为零(想象一下,无论我提供网络的输入图像是什么,损失在本地邻域中保持为-1,因为它已被剪切)。
所以,我的问题分为两部分:
Deepmind在摘录中的确含义是什么?它们是否意味着低于-1的损失被限制为-1,高于+1的损失被限制为+1。如果是这样,他们是如何处理渐变的(即关于绝对值函数的那部分是什么?)
如何在张量流中实现损耗限幅,使得梯度在限幅范围之外不会变为零(但可能保持在+1和-1)? 谢谢!
答案 0 :(得分:8)
我怀疑他们的意思是你应该将渐变剪辑为[-1,1],而不是剪辑损失函数。因此,您像往常一样计算渐变,但随后将渐变的每个分量剪切到[-1,1]范围内(因此,如果它大于+1,则将其替换为+1;如果小于+1; -1,用-1)替换它;然后在梯度下降更新步骤中使用结果,而不是使用未修改的渐变。
等效:定义函数f
,如下所示:
f(x) = x^2 if x in [-0.5,0.5]
f(x) = |x| - 0.25 if x < -0.5 or x > 0.5
他们建议使用s^2
作为损失函数,而不是使用s
形式的某些东西作为损失函数(其中f(s)
是一些复杂的表达式)。这是平方损失和绝对值损失之间的某种混合:当s^2
很小时会表现得像s
,但当s
变大时,它会表现得像绝对值价值(|s|
)。
请注意,f
的导数具有良好的属性,其导数总是在[-1,1]范围内:
f'(x) = 2x if x in [-0.5,0.5]
f'(x) = +1 if x > +1
f'(x) = -1 if x < -1
因此,当你采用这个基于f
的损失函数的梯度时,结果将与计算平方损失的梯度然后剪切它相同。
因此,他们正在做的是用Huber loss有效地取代平方损失。对于delta = 0.5,函数f
只是Huber损失的两倍。
现在重点是以下两个选择是等价的:
使用平方损失函数。计算此损失函数的梯度,但在执行梯度下降的更新步骤之前,梯度为[-1,1]。
使用Huber损失函数代替平方损失函数。在梯度下降中直接(未更改)计算此损失函数的梯度。
前者易于实施。后者具有良好的性能(提高稳定性;它比绝对值损失更好,因为它避免了在最小值附近振荡)。因为这两者是等价的,这意味着我们得到一个易于实现的方案,具有平方损失的简单性以及Huber损失的稳定性和鲁棒性。
答案 1 :(得分:1)
没有。他们实际上谈论的是错误剪辑,而不是关于损失裁剪,但据我所知,同样的事情会引起混淆。它们并不意味着低于-1的损失被限制为-1,高于+1的损失被限制为+1,因为这会导致误差范围[-1; 1]之外的零梯度。相反,他们建议使用线性损耗而不是二次损失来表示误差值<1。 -1和错误值&gt; 1。
计算错误值(r + \ gamma \ max_ {a&#39;} Q(s&#39;,a&#39 ;; \ theta_i ^ - ) - Q(s,a; \ theta_i) )。如果该误差值在[-1; 1]范围内,则将其平方,如果误差值是&lt; -1如果误差值> 1,则乘以-1。 1保持原样。如果你使用它作为损失函数,区间[-1; 1]之外的渐变不会消失。
为了拥有一个平滑的&#34;复合损失函数你也可以用边界值-1和1的一阶泰勒近似来代替误差范围[-1; 1]之外的平方损失。在这种情况下,如果e是你的误差值,你就可以如果e&in [-1; 1],则情况e&lt;在e> 1的情况下,将其替换为-2e-1。 1,将其替换为2e-1。
答案 2 :(得分:1)
首先,论文的代码是available online,这是一个非常宝贵的参考。
如果您看一下代码,您会在nql:getQUpdate
(NeuralQLearner.lua
,第180行)中看到它们,它们会剪切Q学习函数的错误术语:
-- delta = r + (1-terminal) * gamma * max_a Q(s2, a) - Q(s, a)
if self.clip_delta then
delta[delta:ge(self.clip_delta)] = self.clip_delta
delta[delta:le(-self.clip_delta)] = -self.clip_delta
end
在TensorFlow中,假设您的神经网络的最后一层被称为self.output
,self.actions
是所有操作的一个热门编码,self.q_targets_
是一个包含目标的占位符,并且self.q
是您的计算Q:
# The loss function
one = tf.Variable(1.0)
delta = self.q - self.q_targets_
absolute_delta = tf.abs(delta)
delta = tf.where(
absolute_delta < one,
tf.square(delta),
tf.ones_like(delta) # squared error: (-1)^2 = 1
)
或者,使用tf.clip_by_value
(并使实现更接近原始版本):
delta = tf.clip_by_value(
self.q - self.q_targets_,
-1.0,
+1.0
)
答案 3 :(得分:0)
我建议实施Huber loss function。下面是一个python tensorflow实现。
def huber_loss(y_true, y_pred, max_grad=1.):
"""Calculates the huber loss.
Parameters
----------
y_true: np.array, tf.Tensor
Target value.
y_pred: np.array, tf.Tensor
Predicted value.
max_grad: float, optional
Positive floating point value. Represents the maximum possible
gradient magnitude.
Returns
-------
tf.Tensor
The huber loss.
"""
err = tf.abs(y_true - y_pred, name='abs')
mg = tf.constant(max_grad, name='max_grad')
lin = mg*(err-.5*mg)
quad=.5*err*err
return tf.where(err < mg, quad, lin)