Tensorflow:使用不同的表达式进行前向和后向传递

时间:2018-04-10 15:16:23

标签: python tensorflow keras autodiff

我有一个张量流表达式,我想根据我是否计算前向或后向(渐变)通道来使用不同的表达式。具体来说,我想忽略在向后传递期间添加到网络中的一些随机性(噪声)的影响。

这是一个简化的例子

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,它将成为一个非常长的计算图的一部分。)

1 个答案:

答案 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)的渐变。现在,因为 \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.]