aboutsummaryrefslogtreecommitdiffhomepage
path: root/tensorflow/cc
diff options
context:
space:
mode:
authorGravatar Yan Facai (颜发才) <facai.yan@gmail.com>2018-08-07 14:53:52 +0800
committerGravatar Yan Facai (颜发才) <facai.yan@gmail.com>2018-08-07 14:53:52 +0800
commit7613b773e03987c89fe5e5883c411588bce59673 (patch)
treef8ecec45355696cb95936ad9b06b7d020ce95f50 /tensorflow/cc
parent2f56b4a39f8eb519457feea3132a11f3716f2445 (diff)
parent7585a44b0164e234ad6b6c3b99632fb28b7023e8 (diff)
Merge branch 'master' into ENH/unsafe_div
Diffstat (limited to 'tensorflow/cc')
-rw-r--r--tensorflow/cc/BUILD31
-rw-r--r--tensorflow/cc/client/client_session.cc18
-rw-r--r--tensorflow/cc/client/client_session.h28
-rw-r--r--tensorflow/cc/client/client_session_test.cc21
-rw-r--r--tensorflow/cc/framework/gradient_checker.cc12
-rw-r--r--tensorflow/cc/framework/gradient_checker_test.cc16
-rw-r--r--tensorflow/cc/gradients/image_grad.cc74
-rw-r--r--tensorflow/cc/gradients/image_grad_test.cc157
8 files changed, 354 insertions, 3 deletions
diff --git a/tensorflow/cc/BUILD b/tensorflow/cc/BUILD
index a98f0b00b2..588a45ea43 100644
--- a/tensorflow/cc/BUILD
+++ b/tensorflow/cc/BUILD
@@ -121,6 +121,7 @@ cc_library(
deps = [
":array_grad",
":data_flow_grad",
+ ":image_grad",
":math_grad",
":nn_grad",
],
@@ -332,6 +333,36 @@ tf_cc_test(
)
cc_library(
+ name = "image_grad",
+ srcs = ["gradients/image_grad.cc"],
+ deps = [
+ ":cc_ops",
+ ":cc_ops_internal",
+ ":grad_op_registry",
+ ":gradients",
+ ],
+ alwayslink = 1,
+)
+
+tf_cc_test(
+ name = "gradients_image_grad_test",
+ srcs = ["gradients/image_grad_test.cc"],
+ deps = [
+ ":cc_ops",
+ ":client_session",
+ ":grad_op_registry",
+ ":grad_testutil",
+ ":gradient_checker",
+ ":image_grad",
+ ":testutil",
+ "//tensorflow/core:lib_internal",
+ "//tensorflow/core:test",
+ "//tensorflow/core:test_main",
+ "//tensorflow/core:testlib",
+ ],
+)
+
+cc_library(
name = "math_grad",
srcs = ["gradients/math_grad.cc"],
deps = [
diff --git a/tensorflow/cc/client/client_session.cc b/tensorflow/cc/client/client_session.cc
index ba056a8f3a..0e61089a59 100644
--- a/tensorflow/cc/client/client_session.cc
+++ b/tensorflow/cc/client/client_session.cc
@@ -127,4 +127,22 @@ Status ClientSession::Run(const RunOptions& run_options, const FeedType& inputs,
target_node_names, outputs, run_metadata);
}
+Status ClientSession::MakeCallable(const CallableOptions& callable_options,
+ CallableHandle* out_handle) {
+ TF_RETURN_IF_ERROR(impl()->MaybeExtendGraph());
+ return impl()->session_->MakeCallable(callable_options, out_handle);
+}
+
+Status ClientSession::RunCallable(CallableHandle handle,
+ const std::vector<Tensor>& feed_tensors,
+ std::vector<Tensor>* fetch_tensors,
+ RunMetadata* run_metadata) {
+ return impl()->session_->RunCallable(handle, feed_tensors, fetch_tensors,
+ run_metadata);
+}
+
+Status ClientSession::ReleaseCallable(CallableHandle handle) {
+ return impl()->session_->ReleaseCallable(handle);
+}
+
} // end namespace tensorflow
diff --git a/tensorflow/cc/client/client_session.h b/tensorflow/cc/client/client_session.h
index 5fb4109f7d..7dd653eec4 100644
--- a/tensorflow/cc/client/client_session.h
+++ b/tensorflow/cc/client/client_session.h
@@ -87,7 +87,33 @@ class ClientSession {
const std::vector<Operation>& run_outputs,
std::vector<Tensor>* outputs, RunMetadata* run_metadata) const;
- // TODO(keveman): Add support for partial run.
+ /// \brief A handle to a subgraph, created with
+ /// `ClientSession::MakeCallable()`.
+ typedef int64 CallableHandle;
+
+ /// \brief Creates a `handle` for invoking the subgraph defined by
+ /// `callable_options`.
+ /// NOTE: This API is still experimental and may change.
+ Status MakeCallable(const CallableOptions& callable_options,
+ CallableHandle* out_handle);
+
+ /// \brief Invokes the subgraph named by `handle` with the given options and
+ /// input tensors.
+ ///
+ /// The order of tensors in `feed_tensors` must match the order of names in
+ /// `CallableOptions::feed()` and the order of tensors in `fetch_tensors` will
+ /// match the order of names in `CallableOptions::fetch()` when this subgraph
+ /// was created.
+ /// NOTE: This API is still experimental and may change.
+ Status RunCallable(CallableHandle handle,
+ const std::vector<Tensor>& feed_tensors,
+ std::vector<Tensor>* fetch_tensors,
+ RunMetadata* run_metadata);
+
+ /// \brief Releases resources associated with the given `handle` in this
+ /// session.
+ /// NOTE: This API is still experimental and may change.
+ Status ReleaseCallable(CallableHandle handle);
private:
class Impl;
diff --git a/tensorflow/cc/client/client_session_test.cc b/tensorflow/cc/client/client_session_test.cc
index ea5cf5a1f1..559ffea7e8 100644
--- a/tensorflow/cc/client/client_session_test.cc
+++ b/tensorflow/cc/client/client_session_test.cc
@@ -95,5 +95,26 @@ TEST(ClientSessionTest, MultiThreaded) {
test::ExpectTensorEqual<int>(outputs[0], test::AsTensor<int>({-1, 2}, {2}));
}
+TEST(ClientSessionTest, Callable) {
+ Scope root = Scope::NewRootScope();
+ auto a = Placeholder(root, DT_INT32);
+ auto b = Placeholder(root, DT_INT32);
+ auto c = Add(root, a, b);
+ ClientSession session(root);
+ std::vector<Tensor> outputs;
+
+ CallableOptions options;
+ options.add_feed(a.node()->name());
+ options.add_feed(b.node()->name());
+ options.add_fetch(c.node()->name());
+ ClientSession::CallableHandle callable;
+ TF_CHECK_OK(session.MakeCallable(options, &callable));
+ TF_EXPECT_OK(session.RunCallable(
+ callable, {test::AsTensor<int>({1}, {}), test::AsTensor<int>({41}, {})},
+ &outputs, nullptr));
+ test::ExpectTensorEqual<int>(outputs[0], test::AsTensor<int>({42}, {}));
+ TF_EXPECT_OK(session.ReleaseCallable(callable));
+}
+
} // namespace
} // namespace tensorflow
diff --git a/tensorflow/cc/framework/gradient_checker.cc b/tensorflow/cc/framework/gradient_checker.cc
index de2645cb44..e9f9c59e3a 100644
--- a/tensorflow/cc/framework/gradient_checker.cc
+++ b/tensorflow/cc/framework/gradient_checker.cc
@@ -247,7 +247,7 @@ Status ComputeNumericJacobianTranspose(const Scope& scope, const OutputList& xs,
auto y_pos_flat = y_pos[y_idx].flat<Y_T>();
auto y_neg_flat = y_neg[y_idx].flat<Y_T>();
const int64 y_size = y_shapes[y_idx].num_elements();
- const Y_T scale = Y_T{2 * delta};
+ const Y_T scale = 2 * delta;
auto jacobian = (*jacobian_ts)[x_idx * y_num + y_idx].matrix<JAC_T>();
for (int c = 0; c < y_size; ++c) {
SetJacobian<Y_T, JAC_T>(&jacobian, r * x_stride + unit_dimension,
@@ -351,7 +351,14 @@ Status ComputeGradientErrorInternal(const Scope& scope, const OutputList& xs,
auto jac_n = jacobian_ns[i].matrix<JAC_T>();
for (int r = 0; r < jacobian_ts[i].dim_size(0); ++r) {
for (int c = 0; c < jacobian_ts[i].dim_size(1); ++c) {
- *max_error = std::max(*max_error, std::fabs(jac_t(r, c) - jac_n(r, c)));
+ auto cur_error = std::fabs(jac_t(r, c) - jac_n(r, c));
+ // Treat any NaN as max_error and immediately return.
+ // (Note that std::max may ignore NaN arguments.)
+ if (std::isnan(cur_error)) {
+ *max_error = cur_error;
+ return Status::OK();
+ }
+ *max_error = std::max(*max_error, cur_error);
}
}
}
@@ -409,6 +416,7 @@ Status ComputeGradientError(const Scope& scope, const Output& x,
const Output& y, const TensorShape& y_shape, JAC_T* max_error);
INSTANTIATE_GRAD_ERR_TYPE(float, float, float);
+INSTANTIATE_GRAD_ERR_TYPE(double, float, double);
INSTANTIATE_GRAD_ERR_TYPE(double, double, double);
INSTANTIATE_GRAD_ERR_TYPE(complex64, float, float);
INSTANTIATE_GRAD_ERR_TYPE(float, complex64, float);
diff --git a/tensorflow/cc/framework/gradient_checker_test.cc b/tensorflow/cc/framework/gradient_checker_test.cc
index d4f0a7f5ab..8dd762c282 100644
--- a/tensorflow/cc/framework/gradient_checker_test.cc
+++ b/tensorflow/cc/framework/gradient_checker_test.cc
@@ -28,12 +28,14 @@ namespace {
using ops::Complex;
using ops::Const;
+using ops::Div;
using ops::MatMul;
using ops::Placeholder;
using ops::Real;
using ops::Split;
using ops::Square;
using ops::Stack;
+using ops::Sub;
using ops::Unstack;
TEST(GradientCheckerTest, BasicFloat) {
@@ -104,6 +106,20 @@ TEST(GradientCheckerTest, Complex64ToFloat) {
EXPECT_LT(max_error, 1e-4);
}
+// When calculating gradients that are undefined, test we get NaN
+// as the computed error rather than 0.
+TEST(GradientCheckerTest, BasicNan) {
+ Scope scope = Scope::NewRootScope();
+ TensorShape shape({2, 4, 3});
+ auto x = Placeholder(scope, DT_FLOAT, Placeholder::Shape(shape));
+ // y = x/(x-x) should always return NaN
+ auto y = Div(scope, x, Sub(scope, x, x));
+ float max_error;
+ TF_ASSERT_OK((ComputeGradientError<float, float, float>(
+ scope, {x}, {shape}, {y}, {shape}, &max_error)));
+ EXPECT_TRUE(std::isnan(max_error));
+}
+
TEST(GradientCheckerTest, MatMulGrad) {
Scope scope = Scope::NewRootScope();
diff --git a/tensorflow/cc/gradients/image_grad.cc b/tensorflow/cc/gradients/image_grad.cc
new file mode 100644
index 0000000000..882709e1e2
--- /dev/null
+++ b/tensorflow/cc/gradients/image_grad.cc
@@ -0,0 +1,74 @@
+/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+==============================================================================*/
+
+#include <vector>
+#include "tensorflow/cc/framework/grad_op_registry.h"
+#include "tensorflow/cc/framework/gradients.h"
+#include "tensorflow/cc/ops/image_ops_internal.h"
+#include "tensorflow/cc/ops/standard_ops.h"
+
+namespace tensorflow {
+namespace ops {
+namespace {
+
+Status ResizeNearestNeighborGradHelper(const Scope& scope, const Operation& op,
+ const std::vector<Output>& grad_inputs,
+ std::vector<Output>* grad_outputs) {
+ bool align_corners;
+ TF_RETURN_IF_ERROR(
+ GetNodeAttr(op.node()->attrs(), "align_corners", &align_corners));
+ // The internal gradient implementation needs the shape of the input image.
+ // x_shape = shape(x)[1:3]
+ // = slice(shape(x), {1}, {3 - 1})
+ auto x_shape = Slice(scope, Shape(scope, op.input(0)), {1}, {2});
+ grad_outputs->push_back(internal::ResizeNearestNeighborGrad(
+ scope, grad_inputs[0], x_shape,
+ internal::ResizeNearestNeighborGrad::AlignCorners(align_corners)));
+ grad_outputs->push_back(NoGradient());
+ return scope.status();
+}
+REGISTER_GRADIENT_OP("ResizeNearestNeighbor", ResizeNearestNeighborGradHelper);
+
+Status ResizeBilinearGradHelper(const Scope& scope, const Operation& op,
+ const std::vector<Output>& grad_inputs,
+ std::vector<Output>* grad_outputs) {
+ bool align_corners;
+ TF_RETURN_IF_ERROR(
+ GetNodeAttr(op.node()->attrs(), "align_corners", &align_corners));
+ grad_outputs->push_back(internal::ResizeBilinearGrad(
+ scope, grad_inputs[0], op.input(0),
+ internal::ResizeBilinearGrad::AlignCorners(align_corners)));
+ grad_outputs->push_back(NoGradient());
+ return scope.status();
+}
+REGISTER_GRADIENT_OP("ResizeBilinear", ResizeBilinearGradHelper);
+
+Status ResizeBicubicGradHelper(const Scope& scope, const Operation& op,
+ const std::vector<Output>& grad_inputs,
+ std::vector<Output>* grad_outputs) {
+ bool align_corners;
+ TF_RETURN_IF_ERROR(
+ GetNodeAttr(op.node()->attrs(), "align_corners", &align_corners));
+ grad_outputs->push_back(internal::ResizeBicubicGrad(
+ scope, grad_inputs[0], op.input(0),
+ internal::ResizeBicubicGrad::AlignCorners(align_corners)));
+ grad_outputs->push_back(NoGradient());
+ return scope.status();
+}
+REGISTER_GRADIENT_OP("ResizeBicubic", ResizeBicubicGradHelper);
+
+} // anonymous namespace
+} // namespace ops
+} // namespace tensorflow
diff --git a/tensorflow/cc/gradients/image_grad_test.cc b/tensorflow/cc/gradients/image_grad_test.cc
new file mode 100644
index 0000000000..2e55c7561b
--- /dev/null
+++ b/tensorflow/cc/gradients/image_grad_test.cc
@@ -0,0 +1,157 @@
+/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+==============================================================================*/
+
+#include "tensorflow/cc/client/client_session.h"
+#include "tensorflow/cc/framework/grad_op_registry.h"
+#include "tensorflow/cc/framework/gradient_checker.h"
+#include "tensorflow/cc/framework/testutil.h"
+#include "tensorflow/cc/gradients/grad_testutil.h"
+#include "tensorflow/cc/ops/image_ops.h"
+#include "tensorflow/cc/ops/standard_ops.h"
+#include "tensorflow/core/framework/tensor_testutil.h"
+#include "tensorflow/core/lib/core/status_test_util.h"
+
+namespace tensorflow {
+namespace {
+
+using ops::Const;
+using ops::ResizeBicubic;
+using ops::ResizeBilinear;
+using ops::ResizeNearestNeighbor;
+
+class ImageGradTest : public ::testing::Test {
+ protected:
+ ImageGradTest() : scope_(Scope::NewRootScope()) {}
+
+ enum OpType { RESIZE_NEAREST, RESIZE_BILINEAR, RESIZE_BICUBIC };
+
+ template <typename T>
+ Tensor MakeData(const TensorShape& data_shape) {
+ DataType data_type = DataTypeToEnum<T>::v();
+ Tensor data(data_type, data_shape);
+ auto data_flat = data.flat<T>();
+ for (int i = 0; i < data_flat.size(); ++i) {
+ data_flat(i) = T(i);
+ }
+ return data;
+ }
+
+ template <typename T>
+ void MakeOp(const OpType op_type, const Tensor& x_data, const Input& y_shape,
+ const bool align_corners, Output* x, Output* y) {
+ *x = Const<T>(scope_, x_data);
+ switch (op_type) {
+ case RESIZE_NEAREST:
+ *y = ResizeNearestNeighbor(
+ scope_, *x, y_shape,
+ ResizeNearestNeighbor::AlignCorners(align_corners));
+ return;
+ case RESIZE_BILINEAR:
+ *y = ResizeBilinear(scope_, *x, y_shape,
+ ResizeBilinear::AlignCorners(align_corners));
+ return;
+ case RESIZE_BICUBIC:
+ *y = ResizeBicubic(scope_, *x, y_shape,
+ ResizeBicubic::AlignCorners(align_corners));
+ return;
+ }
+ assert(false);
+ }
+
+ template <typename T>
+ void TestResizedShapeForType(const OpType op_type, const bool align_corners) {
+ TensorShape x_shape({1, 2, 2, 1});
+ Tensor x_data = MakeData<T>(x_shape);
+ Output x, y;
+ MakeOp<T>(op_type, x_data, {4, 6}, align_corners, &x, &y);
+
+ ClientSession session(scope_);
+ std::vector<Tensor> outputs;
+ TF_ASSERT_OK(session.Run({y}, &outputs));
+ EXPECT_EQ(outputs.size(), 1);
+ EXPECT_EQ(outputs[0].shape(), TensorShape({1, 4, 6, 1}));
+ }
+
+ void TestResizedShape(OpType op_type) {
+ for (const bool align_corners : {true, false}) {
+ TestResizedShapeForType<Eigen::half>(op_type, align_corners);
+ TestResizedShapeForType<float>(op_type, align_corners);
+ TestResizedShapeForType<double>(op_type, align_corners);
+ }
+ }
+
+ template <typename X_T, typename Y_T, typename JAC_T>
+ void TestResizeToSmallerAndAlign(const OpType op_type,
+ const bool align_corners) {
+ TensorShape x_shape({1, 4, 6, 1});
+ Tensor x_data = MakeData<X_T>(x_shape);
+ Output x, y;
+ MakeOp<X_T>(op_type, x_data, {2, 3}, align_corners, &x, &y);
+ JAC_T max_error;
+ TF_ASSERT_OK((ComputeGradientError<X_T, Y_T, JAC_T>(
+ scope_, x, x_data, y, {1, 2, 3, 1}, &max_error)));
+ EXPECT_LT(max_error, 1e-3);
+ }
+
+ template <typename X_T, typename Y_T, typename JAC_T>
+ void TestResizeToLargerAndAlign(const OpType op_type,
+ const bool align_corners) {
+ TensorShape x_shape({1, 2, 3, 1});
+ Tensor x_data = MakeData<X_T>(x_shape);
+ Output x, y;
+ MakeOp<X_T>(op_type, x_data, {4, 6}, align_corners, &x, &y);
+ JAC_T max_error;
+ TF_ASSERT_OK((ComputeGradientError<X_T, Y_T, JAC_T>(
+ scope_, x, x_data, y, {1, 4, 6, 1}, &max_error)));
+ EXPECT_LT(max_error, 1e-3);
+ }
+
+ template <typename X_T, typename Y_T, typename JAC_T>
+ void TestResize(OpType op_type) {
+ for (const bool align_corners : {true, false}) {
+ TestResizeToSmallerAndAlign<X_T, Y_T, JAC_T>(op_type, align_corners);
+ TestResizeToLargerAndAlign<X_T, Y_T, JAC_T>(op_type, align_corners);
+ }
+ }
+
+ Scope scope_;
+};
+
+TEST_F(ImageGradTest, TestNearestNeighbor) {
+ TestResizedShape(RESIZE_NEAREST);
+ TestResize<float, float, float>(RESIZE_NEAREST);
+ TestResize<double, double, double>(RESIZE_NEAREST);
+}
+
+TEST_F(ImageGradTest, TestBilinear) {
+ TestResizedShape(RESIZE_BILINEAR);
+ TestResize<float, float, float>(RESIZE_BILINEAR);
+ // Note that Y_T is always float for this op. We choose
+ // double for the jacobian to capture the higher precision
+ // between X_T and Y_T.
+ TestResize<double, float, double>(RESIZE_BILINEAR);
+}
+
+TEST_F(ImageGradTest, TestBicubic) {
+ TestResizedShape(RESIZE_BICUBIC);
+ TestResize<float, float, float>(RESIZE_BICUBIC);
+ // Note that Y_T is always float for this op. We choose
+ // double for the jacobian to capture the higher precision
+ // between X_T and Y_T.
+ TestResize<double, float, double>(RESIZE_BICUBIC);
+}
+
+} // namespace
+} // namespace tensorflow