3

我有一个 tensorflow 表达式,我想根据我是计算前向还是后向(梯度)传递来使用不同的表达式。具体来说,我想忽略在反向传递期间添加到网络中的一些随机性(噪声)的影响。

这是一个简化的例子

import numpy as np
import tensorflow as tf

x = tf.placeholder(tf.float32)
y = x**2
u = tf.random_uniform(tf.shape(x), minval=0.9, maxval=1.1)
yu = y * u
z = tf.sqrt(yu)
g = tf.gradients(z, x)[0]

with tf.Session() as sess:
    yv, yuv, zv, gv = sess.run([y,yu,z,g], {x: [-2, -1, 1]})

print(yv)
print(yuv)
print(zv)
print(gv)

输出类似的东西

[4. 1. 1.]
[4.1626534 0.9370764 1.0806011]
[2.0402582  0.96802706 1.0395197 ]
[-1.0201291  -0.96802706  1.0395197 ]

这里的最后一个值是关于 的导zx。我希望它们不包括乘性噪声项u,即它们应该始终[-1, -1, 1]适用于 的这些输入值x

有没有办法只使用 Python 来做这样的事情?我知道我可以在 C 中创建一个自定义运算符并为它定义一个自定义渐变,但如果可能的话,我想避免这种情况。

另外,我希望将其用作 Keras 层的一部分,因此基于 Keras 的解决方案将是一种替代方案(即,如果可以为通过 Keras 层的向前和向后传递定义不同的表达式)。这确实意味着仅仅定义第二个表达式z2 = tf.sqrt(y)并调用gradients它对我来说不是一个解决方案,因为我不知道如何将它放在 Keras 中(因为在 Keras 中,它将是一个很长的计算的一部分图形)。

4

1 回答 1

3

简短的回答是,您上面提到的 Sergey Ioffe 的技巧只有在梯度计算之前应用到图的最末端时才有效。

我假设您尝试了以下方法,但它不起作用

yu_fixed = tf.stop_gradient(yu - y) + y
z = tf.sqrt(yu_fixed)

这仍然会输出随机污染的梯度。

要了解原因,让我们继续进行梯度计算。让我们s用作tf.stop_gradient. 它的工作方式是,当 TensorFlow 需要计算 时s(expr),它只返回expr,但是当它需要计算 的梯度时s(expr),它返回 0。

我们要计算 的梯度z = sqrt(s(yu - y) + y)。现在,因为 \frac{\partial \sqrt{f(x)}}{\partial x} = \frac{1}{2\sqrt{f(x)}} \frac{\partial f(x)}{\partial x },我们发现 的梯度z既包含具有 的导数的项s(),也包含包含s()自身的项。后一项不会将该s()部分归零,因此计算的导数z将取决于(以某种奇怪和不正确的方式)值yu。这就是为什么上述解决方案在梯度中仍然包含随机性。

据我所知,解决这个问题的唯一方法是将 Ioffe 的技巧作为tf.gradient. 换句话说,如果您执行以下操作,您将获得预期的结果:

x = tf.placeholder(tf.float32)
y = x**2
u = tf.random_uniform(tf.shape(x), minval=0.9, maxval=1.1)
yu = y * u
z = tf.sqrt(yu)
z_fixed = tf.stop_gradient(z - tf.sqrt(y)) + tf.sqrt(y)
g = tf.gradients(z_fixed, x)[0]

with tf.Session() as sess:
    yv, yuv, zv, gv = sess.run([y,yu,z_fixed,g], {x: [-2, -1, 1]})

print(yv)
print(yuv)
print(zv)
print(gv)

输出:

[ 4.  1.  1.]
[ 3.65438652  1.07519293  0.94398856]
[ 1.91164494  1.03691506  0.97159076]
[-1. -1.  1.]
于 2018-04-10T19:54:03.083 回答