aboutsummaryrefslogtreecommitdiffhomepage
path: root/tensorflow/cc/framework
diff options
context:
space:
mode:
authorGravatar A. Unique TensorFlower <gardener@tensorflow.org>2017-09-06 17:57:04 -0700
committerGravatar TensorFlower Gardener <gardener@tensorflow.org>2017-09-06 18:01:27 -0700
commite722358e7e96dd2aa20d7e2c56336e76845daa6a (patch)
treea74960670ce4bacad0909fc913097bcc3e27ed18 /tensorflow/cc/framework
parentf8a43f9d63ce90f10852d69e40fbb9fe849fc190 (diff)
Merge changes from github.
END_PUBLIC --- Commit 607816029 authored by Eugene Brevdo<ebrevdo@google.com> Committed by TensorFlower Gardener<gardener@tensorflow.org>: Extended ScratchSpace to expose its underlying scratch tensor object. PiperOrigin-RevId: 167649551 --- Commit db43fe68e authored by A. Unique TensorFlower<gardener@tensorflow.org> Committed by TensorFlower Gardener<gardener@tensorflow.org>: Add fast math attributes to all generated methods when fast math enabled. RELNOTES: n/a PiperOrigin-RevId: 167646637 --- Commit aebe8cc6f authored by A. Unique TensorFlower<gardener@tensorflow.org> Committed by TensorFlower Gardener<gardener@tensorflow.org>: Call HloComputation.Accept instead of HloInstruction.Accept to get all instructions profiled. RELNOTES: n/a PiperOrigin-RevId: 167640259 --- Commit 0ab137cd8 authored by A. Unique TensorFlower<gardener@tensorflow.org> Committed by TensorFlower Gardener<gardener@tensorflow.org>: BEGIN_PUBLIC Automated g4 rollback of changelist 167604306 PiperOrigin-RevId: 167800256
Diffstat (limited to 'tensorflow/cc/framework')
-rw-r--r--tensorflow/cc/framework/gradients.cc36
-rw-r--r--tensorflow/cc/framework/gradients_test.cc67
-rw-r--r--tensorflow/cc/framework/testutil.cc14
-rw-r--r--tensorflow/cc/framework/testutil.h12
4 files changed, 127 insertions, 2 deletions
diff --git a/tensorflow/cc/framework/gradients.cc b/tensorflow/cc/framework/gradients.cc
index 66a943410e..82469261e5 100644
--- a/tensorflow/cc/framework/gradients.cc
+++ b/tensorflow/cc/framework/gradients.cc
@@ -78,6 +78,10 @@ class SymbolicGradientBuilder {
const std::vector<Output>& grad_inputs,
std::vector<Output>* grad_outputs);
+ // Returns a list mapping whether each node in the graph is reachable
+ // from outputs_. Keyed by node id.
+ std::vector<bool> GetReachableNodes();
+
const Scope& scope_;
const ops::GradOpRegistry* registry_;
const std::vector<Output>& outputs_;
@@ -143,11 +147,36 @@ Status SymbolicGradientBuilder::BackpropAlongEdge(const Output& dst_grad,
return Status::OK();
}
+std::vector<bool> SymbolicGradientBuilder::GetReachableNodes() {
+ std::vector<bool> reachable_nodes(scope_.graph()->num_node_ids(), false);
+ std::deque<Node*> queue;
+ for (const Output& out : outputs_) {
+ if (!reachable_nodes[out.node()->id()]) {
+ queue.push_back(out.node());
+ reachable_nodes[out.node()->id()] = true;
+ }
+ }
+
+ while (!queue.empty()) {
+ Node* n = queue.front();
+ queue.pop_front();
+ for (const Edge* e : n->in_edges()) {
+ if (e->IsControlEdge()) continue;
+ queue.push_back(e->src());
+ reachable_nodes[e->src()->id()] = true;
+ }
+ }
+ return reachable_nodes;
+}
+
Status SymbolicGradientBuilder::Initialize() {
if (outputs_.size() != grad_inputs_.size()) {
return errors::InvalidArgument(
"Must specify a gradient input for each output.");
}
+ std::vector<bool> reachable_nodes = GetReachableNodes();
+ // TODO(theflofly) Check that inputs_ are reachable from
+ // outputs_ using reachable_nodes
grad_outputs_->clear();
grad_outputs_->resize(inputs_.size());
// Populate `output_nodes_` from node ids in `outputs_`.
@@ -188,12 +217,15 @@ Status SymbolicGradientBuilder::Initialize() {
if (output_nodes_.find(n->id()) == output_nodes_.end()) {
// Internal node: continue BFS along connected outputs.
for (const Edge* e : n->out_edges()) {
- if (e->IsControlEdge()) continue;
- ++num_expected_backprops;
+ // If a node is not reachable from outputs_,
+ // we don't expect it to receive a backpropagated gradient.
+ // It will not be counted in num_expected_backprops.
+ if (e->IsControlEdge() || !reachable_nodes[e->dst()->id()]) continue;
if (visited.find(e->dst()) == visited.end()) {
queue.push_back(e->dst());
visited.insert(e->dst());
}
+ ++num_expected_backprops;
}
} else {
// Output node: stop BFS and update `num_expected_backprops` for
diff --git a/tensorflow/cc/framework/gradients_test.cc b/tensorflow/cc/framework/gradients_test.cc
index 24af7d567b..032ab93623 100644
--- a/tensorflow/cc/framework/gradients_test.cc
+++ b/tensorflow/cc/framework/gradients_test.cc
@@ -364,6 +364,73 @@ TEST_F(GradientsTest, MultipleNodeOutputGrads) {
test::AsTensor<int>({60, 61, 62, 63, 66, 66, 66, 67}, {4, 2}));
}
+TEST_F(GradientsTest, UnreachableEdgeGradOneOutput) {
+ auto x = Variable(scope_test_, {2, 3}, DT_DOUBLE);
+ auto x_const = Const(scope_test_, {{1.0, 2.0, 3.0}, {4.0, 5.0, 6.0}});
+ auto x_assign = Assign(scope_test_, x, x_const);
+
+ auto y = Variable(scope_test_, {3, 1}, DT_DOUBLE);
+ auto y_const = Const(scope_test_, {{1.0}, {2.0}, {3.0}});
+ auto y_assign = Assign(scope_test_, y, y_const);
+
+ auto m1 = MatMul(scope_test_, x, y);
+
+ auto z = Variable(scope_test_, {1, 3}, DT_DOUBLE);
+ auto z_const = Const(scope_test_, {{9.0, 10.0, 11.0}});
+ auto z_assign = Assign(scope_test_, z, z_const);
+
+ auto m2 = MatMul(scope_test_, y, z);
+
+ auto dm1 = Const(scope_test_, {{0.5}, {0.5}});
+
+ std::vector<Output> grad_outputs;
+ TF_ASSERT_OK(
+ AddSymbolicGradients(scope_test_, {m1}, {y}, {dm1}, &grad_outputs));
+
+ std::vector<Tensor> outputs;
+ test::GetTensors(scope_test_, {x_assign, y_assign, z_assign},
+ {grad_outputs[0]}, &outputs);
+ // dz/dy = xT * dm1
+ test::ExpectTensorNear<double>(
+ outputs[0], test::AsTensor<double>({2.5, 3.5, 4.5}, {3, 1}), 1e-5);
+}
+
+TEST_F(GradientsTest, UnreachableEdgeGradTwoOutputs) {
+ auto x = Variable(scope_test_, {2, 3}, DT_DOUBLE);
+ auto x_const = Const(scope_test_, {{1.0, 2.0, 3.0}, {4.0, 5.0, 6.0}});
+ auto x_assign = Assign(scope_test_, x, x_const);
+
+ auto y = Variable(scope_test_, {3, 1}, DT_DOUBLE);
+ auto y_const = Const(scope_test_, {{1.0}, {2.0}, {3.0}});
+ auto y_assign = Assign(scope_test_, y, y_const);
+
+ auto m1 = MatMul(scope_test_, x, y);
+
+ auto z = Variable(scope_test_, {1, 3}, DT_DOUBLE);
+ auto z_const = Const(scope_test_, {{9.0, 10.0, 11.0}});
+ auto z_assign = Assign(scope_test_, z, z_const);
+
+ auto m2 = MatMul(scope_test_, y, z);
+
+ auto dm1 = Const(scope_test_, {{0.5}, {0.5}});
+ auto dm2 =
+ Const(scope_test_, {{0.5, 0.5, 0.5}, {0.6, 0.7, 0.8}, {0.6, 0.7, 0.9}});
+
+ std::vector<Output> grad_outputs;
+ TF_ASSERT_OK(AddSymbolicGradients(scope_test_, {m1, m2}, {y}, {dm1, dm2},
+ &grad_outputs));
+
+ std::vector<Tensor> outputs;
+ test::GetTensors(scope_test_, {x_assign, y_assign, z_assign},
+ {grad_outputs[0]}, &outputs);
+
+ // the gradients from m1 and m2 will be summed to compute the gradient
+ // w.r.t y
+ // dz/dy = xT * dm1 + dm2 * zT
+ test::ExpectTensorNear<double>(
+ outputs[0], test::AsTensor<double>({17.5, 24.7, 26.8}, {3, 1}), 1e-5);
+}
+
// StopGradientSingleOutputMultiEdgeTest tests combinations of valid and
// 'NoGradient' (induced by StopGradient op) returned along multiple edges from
// a single nodes output.
diff --git a/tensorflow/cc/framework/testutil.cc b/tensorflow/cc/framework/testutil.cc
index ca78f31db5..57d573e3c5 100644
--- a/tensorflow/cc/framework/testutil.cc
+++ b/tensorflow/cc/framework/testutil.cc
@@ -36,5 +36,19 @@ void GetTensor(const Scope& scope, Output tensor, Tensor* out) {
*out = outputs[0];
}
+void GetTensors(const Scope& scope, const std::vector<Output>& assign_vars,
+ const OutputList& tensors, std::vector<Tensor>* out) {
+ ClientSession session(scope);
+ TF_CHECK_OK(session.Run(assign_vars, nullptr));
+ TF_CHECK_OK(session.Run(tensors, out));
+}
+
+void GetTensor(const Scope& scope, const std::vector<Output>& assign_vars,
+ Output tensor, Tensor* out) {
+ std::vector<Tensor> outputs;
+ GetTensors(scope, assign_vars, {std::move(tensor)}, &outputs);
+ *out = outputs[0];
+}
+
} // end namespace test
} // end namespace tensorflow
diff --git a/tensorflow/cc/framework/testutil.h b/tensorflow/cc/framework/testutil.h
index d027ad3744..a3e19870ec 100644
--- a/tensorflow/cc/framework/testutil.h
+++ b/tensorflow/cc/framework/testutil.h
@@ -26,9 +26,21 @@ namespace test {
void GetTensors(const Scope& scope, OutputList tensors,
std::vector<Tensor>* out);
+// Computes the outputs listed in 'tensors', returns the tensors in 'out'.
+// assign_vars are extra outputs that should be run
+// e.g. to assign values to variables.
+void GetTensors(const Scope& scope, const std::vector<Output>& assign_vars,
+ const OutputList& tensors, std::vector<Tensor>* out);
+
/// Computes the output 'tensor', returning the resulting tensor in 'out'.
void GetTensor(const Scope& scope, Output tensor, Tensor* out);
+// Computes the output 'tensor', returning the resulting tensor in 'out'.
+// assign_vars are extra outputs that should be run
+// e.g. to assign values to variables.
+void GetTensor(const Scope& scope, const std::vector<Output>& assign_vars,
+ Output tensor, Tensor* out);
+
} // namespace test
} // namespace tensorflow