我有一个张量流表达式,我想根据我是否计算前向或后向(渐变)通道来使用不同的表达式。具体来说,我想忽略在向后传递期间添加到网络中的一些随机性(噪声)的影响。
这是一个简化的例子
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 ]
此处的最后一个值是z
相对于x
的导数。我希望它们不包含乘法噪声项u
,即它们对[-1, -1, 1]
的这些输入值应始终为x
。
有没有办法只使用Python做这样的事情?我知道我可以在C中创建一个自定义运算符并为其定义自定义渐变,但我想尽可能避免这种情况。
此外,我希望将此作为Keras层的一部分使用,因此基于Keras的解决方案将是另一种选择(即,如果可以为前向和后向通过Keras层定义不同的表达式)。这确实意味着只是定义第二个表达式z2 = tf.sqrt(y)
并且在那个问题上调用gradients
对我来说不是,因为我不知道如何把它放在Keras中(因为在Keras,它将成为一个非常长的计算图的一部分。)
答案 0 :(得分:2)
简短的回答是,上面提到过的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)
的渐变。现在,因为
,
我们发现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.]