Skip to content

Commit 2bfb700

Browse files
Change gradients to be computed with respect to variable ref rather than
snapshot. Variables may create another snapshot or their ref may be exposed via public API (e.g., var.op.outputs[0] or graph.as_graph_element(var) which happens fairly often inside libraries or collection serialization). On the other hand, tf.gradients() use convert_to_tensor() which returns a snapshot, and gradients were computed with respect to this particular snapshot, which makes the gradients incorrect. Change: 147800865
1 parent ff26c39 commit 2bfb700

4 files changed

Lines changed: 39 additions & 5 deletions

File tree

‎tensorflow/contrib/imperative/imperative_graph.py‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ def __init__(self, parent_graph=None):
102102
# calling the original gradient function.
103103
def _imperative_op_grad(op, *grad):
104104
with self.replace_outputs(op):
105-
return self._gradient_function_map[op](op, *grad)
105+
return self._gradient_function_map[op.name](op, *grad)
106106

107107
ops.RegisterGradient(self._imperative_op_type)(_imperative_op_grad)
108108

@@ -166,7 +166,7 @@ def replace_outputs(self, op):
166166
"""Replaces the outputs of `op` with values recorded in `_outputs_map`."""
167167
# pylint: disable=protected-access
168168
old_outputs = op._outputs
169-
op._outputs = self._outputs_map[op]
169+
op._outputs = self._outputs_map[op.name]
170170
yield
171171
op._outputs = old_outputs
172172
# pylint: enable=protected-access
@@ -318,9 +318,9 @@ def create_op(self, *args, **kwargs):
318318

319319
for i, _ in enumerate(shapes):
320320
values[i].set_shape(shapes[i])
321-
self._outputs_map[orig_op] = values
321+
self._outputs_map[orig_op.name] = values
322322
try:
323-
self._gradient_function_map[orig_op] = ops.get_gradient_function(
323+
self._gradient_function_map[orig_op.name] = ops.get_gradient_function(
324324
orig_op)
325325
except (KeyError, LookupError):
326326
pass

‎tensorflow/contrib/imperative/imperative_test.py‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,17 @@ def testNewStep(self):
107107
b = a + random_ops.random_uniform([], minval=0.1)
108108
self.assertGreaterEqual(b.value, a.value)
109109

110+
def testGradientThroughNewStep(self):
111+
with imperative_mode.ImperativeMode(self._target) as mode:
112+
x = constant_op.constant(np.random.rand(3))
113+
y = math_ops.tanh(x)
114+
115+
with mode.new_step():
116+
z = constant_op.constant(np.random.rand(3))
117+
w = math_ops.multiply(y, z)
118+
dx = gradients_impl.gradients(w, x)
119+
self.assertAllClose(dx[0].value, z.value * (1.0 - y.value ** 2))
120+
110121
def testEscape(self):
111122
"""Makes sure that values don't escape a `new_step` scope."""
112123
with imperative_mode.ImperativeMode(self._target) as mode:

‎tensorflow/python/ops/gradients_impl.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,8 @@ def gradients(ys,
433433
xs = [x.handle if isinstance(x, resource_variable_ops.ResourceVariable)
434434
else x
435435
for x in xs]
436-
xs = ops.convert_n_to_tensor_or_indexed_slices(xs, name="x")
436+
xs = ops.internal_convert_n_to_tensor_or_indexed_slices(xs, name="x",
437+
as_ref=True)
437438
grad_ys = _DefaultGradYs(grad_ys, ys, colocate_gradients_with_ops)
438439

439440
# The approach we take here is as follows: Create a list of all ops in the

‎tensorflow/python/ops/gradients_test.py‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
from tensorflow.python.ops import state_grad # pylint: disable=unused-import
4545
from tensorflow.python.ops import tensor_array_grad # pylint: disable=unused-import
4646
from tensorflow.python.ops import tensor_array_ops
47+
from tensorflow.python.ops import variables
4748
from tensorflow.python.ops.nn_ops import bias_add
4849
from tensorflow.python.platform import googletest
4950

@@ -311,6 +312,27 @@ def _Step(i, a, ta):
311312
grad, = gradients.gradients(target, v)
312313
self.assertIsNone(grad)
313314

315+
def testVariableReadValueGradient(self):
316+
with ops.Graph().as_default():
317+
init = constant_op.constant(100.0)
318+
var = variables.Variable(init)
319+
gradient = gradients.gradients(var.read_value(), var)
320+
self.assertIsNotNone(gradient)
321+
322+
def testVariableAsGraphElementGradient(self):
323+
with ops.Graph().as_default() as graph:
324+
init = constant_op.constant(100.0)
325+
var = variables.Variable(init)
326+
gradient = gradients.gradients(graph.as_graph_element(var), var)
327+
self.assertIsNotNone(gradient)
328+
329+
def testVariableRefGradient(self):
330+
with ops.Graph().as_default():
331+
init = constant_op.constant(100.0)
332+
var = variables.Variable(init)
333+
gradient = gradients.gradients(var._ref(), var)
334+
self.assertIsNotNone(gradient)
335+
314336

315337
class FunctionGradientsTest(test_util.TensorFlowTestCase):
316338

0 commit comments

Comments
 (0)