From a278365e8848f5fcbccb42f95a3c523367c1602f Mon Sep 17 00:00:00 2001 From: "karl@kubx.ca" Date: Mon, 16 Jul 2018 00:49:06 -0400 Subject: Enforce uniqueness of custom prefixes for gradients --- tensorflow/c/c_api.cc | 17 ++++++++++++++++- tensorflow/c/c_api.h | 31 ++++++++++++++++++++++++++----- tensorflow/c/c_api_test.cc | 40 ++++++++++++++++++++++------------------ 3 files changed, 64 insertions(+), 24 deletions(-) (limited to 'tensorflow/c') diff --git a/tensorflow/c/c_api.cc b/tensorflow/c/c_api.cc index 32b0b70620..c1f4745e56 100644 --- a/tensorflow/c/c_api.cc +++ b/tensorflow/c/c_api.cc @@ -2411,9 +2411,24 @@ void TF_AddGradientsWithPrefix(TF_Graph* g, const char* prefix, TF_Output* y, const int first_new_node_id = g->graph.num_node_ids(); + const char* child_scope_name = prefix; + if (child_scope_name != nullptr) { + // The operation should fail if the provided name prefix has already been + // used in this graph + for (const auto& pair: g->name_map) { + const string& name = pair.first; + if (name.compare(0, name.find_last_of('/'), prefix) == 0) { + status->status = + InvalidArgument("Duplicate node name in graph: '", prefix, "'"); + return; + } + } + } else { + child_scope_name = "gradients"; + } tensorflow::Scope scope = NewInternalScope(&g->graph, &status->status, &g->refiner) - .NewSubScope(prefix != nullptr ? prefix : "gradients"); + .NewSubScope(child_scope_name); if (dx != nullptr) { std::vector dx_arg = OutputsFromTFOutputs(dx, ny); diff --git a/tensorflow/c/c_api.h b/tensorflow/c/c_api.h index 8e49158957..0a9fa9ddbc 100644 --- a/tensorflow/c/c_api.h +++ b/tensorflow/c/c_api.h @@ -1129,17 +1129,36 @@ TF_CAPI_EXPORT extern void TF_FinishWhile(const TF_WhileParams* params, // called after a successful TF_NewWhile() call. TF_CAPI_EXPORT extern void TF_AbortWhile(const TF_WhileParams* params); -// Adds operations to compute the partial derivatives of sum of `y`s w.r.t `x`s. +// Adds operations to compute the partial derivatives of sum of `y`s w.r.t `x`s, +// i.e., d(y_1 + y_2 + ...)/dx_1, d(y_1 + y_2 + ...)/dx_2... +// +// `dx` are used as initial gradients (which represent the symbolic partial +// derivatives of some loss function `L` w.r.t. `y`). +// `dx` must be nullptr or have size `ny`. +// If `dx` is nullptr, the implementation will use dx of `OnesLike` for all +// shapes in `y`. +// The partial derivatives are returned in `dy`. `dy` should be allocated to +// size `nx`. // -// This method is the equivalent of calling TF_AddGradientsWithPrefix with a -// nullptr prefix (which will create all gradients operations under "gradients/" -// by default). See TF_AddGradientsWithPrefix for more details. +// Gradient nodes are automatically named under the "gradients/" prefix. To +// guarantee name uniqueness, subsequent calls to the same graph will +// append an incremental tag to the prefix: "gradients_1/", "gradients_2/", ... +// See TF_AddGradientsWithPrefix, which provides a means to specify a custom +// name prefix for operations added to a graph to compute the gradients. +// +// WARNING: This function does not yet support all the gradients that python +// supports. See +// https://www.tensorflow.org/code/tensorflow/cc/gradients/README.md +// for instructions on how to add C++ more gradients. TF_CAPI_EXPORT void TF_AddGradients(TF_Graph* g, TF_Output* y, int ny, TF_Output* x, int nx, TF_Output* dx, TF_Status* status, TF_Output* dy); // Adds operations to compute the partial derivatives of sum of `y`s w.r.t `x`s, // i.e., d(y_1 + y_2 + ...)/dx_1, d(y_1 + y_2 + ...)/dx_2... +// This is a variant of TF_AddGradients that allows to caller to pass a custom +// name prefix to the operations added to a graph to compute the gradients. +// // `dx` are used as initial gradients (which represent the symbolic partial // derivatives of some loss function `L` w.r.t. `y`). // `dx` must be nullptr or have size `ny`. @@ -1148,7 +1167,9 @@ TF_CAPI_EXPORT void TF_AddGradients(TF_Graph* g, TF_Output* y, int ny, // The partial derivatives are returned in `dy`. `dy` should be allocated to // size `nx`. // `prefix` names the scope into which all gradients operations are being added. -// If `prefix` is nullptr, "gradients" is used by default. +// `prefix` must be unique within the provided graph otherwise this operation +// will fail. If `prefix` is nullptr, the default prefixing behaviour takes +// place, see TF_AddGradients for more details. // // WARNING: This function does not yet support all the gradients that python // supports. See diff --git a/tensorflow/c/c_api_test.cc b/tensorflow/c/c_api_test.cc index adcdefbaf3..7094d5d32d 100644 --- a/tensorflow/c/c_api_test.cc +++ b/tensorflow/c/c_api_test.cc @@ -1474,19 +1474,17 @@ class CApiGradientsTest : public ::testing::Test { TF_DeleteStatus(s_); } - void TestGradientsSuccess(bool grad_inputs_provided, - const char* prefix = nullptr) { + void TestGradientsSuccess(bool grad_inputs_provided) { TF_Output inputs[2]; TF_Output outputs[1]; TF_Output grad_outputs[2]; TF_Output expected_grad_outputs[2]; BuildSuccessGraph(inputs, outputs); - BuildExpectedGraph(grad_inputs_provided, prefix, expected_grad_outputs); + BuildExpectedGraph(grad_inputs_provided, expected_grad_outputs); - AddGradients(grad_inputs_provided, prefix, inputs, 2, outputs, 1, + AddGradients(grad_inputs_provided, nullptr, inputs, 2, outputs, 1, grad_outputs); - EXPECT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_); // Compare that the graphs match. @@ -1604,7 +1602,6 @@ class CApiGradientsTest : public ::testing::Test { } void BuildExpectedGraph(bool grad_inputs_provided, - const char* grad_prefix, TF_Output* expected_grad_outputs) { // The expected graph looks like this if grad_inputs_provided. // If grad_inputs_provided is false, Const_0 will be a OnesLike op. @@ -1633,10 +1630,6 @@ class CApiGradientsTest : public ::testing::Test { // const float const0_val[] = {1.0, 2.0, 3.0, 4.0}; const float const1_val[] = {1.0, 0.0, 0.0, 1.0}; - const char* prefix = grad_prefix; - if (prefix == nullptr) { - prefix = "gradients"; - } TF_Operation* const0 = FloatConst2x2(expected_graph_, s_, const0_val, "Const_0"); TF_Operation* const1 = @@ -1649,14 +1642,13 @@ class CApiGradientsTest : public ::testing::Test { const float const3_val[] = {1.0, 1.0, 1.0, 1.0}; const3 = FloatConst2x2(expected_graph_, s_, const3_val, "GradInputs"); } else { - const3 = OnesLike(expected_graph_, s_, matmul, - strings::StrCat(prefix, "/OnesLike").c_str()); + const3 = OnesLike(expected_graph_, s_, matmul, "gradients/OnesLike"); } TF_Operation* matmul1 = MatMul(expected_graph_, s_, const3, const1, - strings::StrCat(prefix, "/MatMul").c_str(), false, true); + "gradients/MatMul", false, true); TF_Operation* matmul2 = MatMul(expected_graph_, s_, const0, const3, - strings::StrCat(prefix, "/MatMul_1").c_str(), true, false); + "gradients/MatMul_1", true, false); expected_grad_outputs[0] = {matmul1, 0}; expected_grad_outputs[1] = {matmul2, 0}; } @@ -1727,10 +1719,6 @@ TEST_F(CApiGradientsTest, Gradients_NoGradInputs) { TestGradientsSuccess(false); } -TEST_F(CApiGradientsTest, Gradients_NoGradInputsWithScopeName) { - TestGradientsSuccess(false, "gradscope"); -} - TEST_F(CApiGradientsTest, OpWithNoGradientRegistered_GradInputs) { TestGradientsError(true); } @@ -1739,6 +1727,22 @@ TEST_F(CApiGradientsTest, OpWithNoGradientRegistered_NoGradInputs) { TestGradientsError(false); } +TEST_F(CApiGradientsTest, Gradients_WithPrefix) { + TF_Output inputs[2]; + TF_Output outputs[1]; + TF_Output grad_outputs[2]; + + BuildSuccessGraph(inputs, outputs); + AddGradients(false, "mygrads", inputs, 2, outputs, 1, grad_outputs); + EXPECT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_); + + AddGradients(false, "mygrads_1", inputs, 2, outputs, 1, grad_outputs); + ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_); + + AddGradients(false, "mygrads_1", inputs, 2, outputs, 1, grad_outputs); + ASSERT_EQ(TF_INVALID_ARGUMENT, TF_GetCode(s_)) << TF_Message(s_); +} + void ScalarFloatFromTensor(const TF_Tensor* t, float* f) { ASSERT_TRUE(t != nullptr); ASSERT_EQ(TF_FLOAT, TF_TensorType(t)); -- cgit v1.2.3