From 7c42aad412e634c7e8853d170c3f516fc3e6b2bf Mon Sep 17 00:00:00 2001 From: Yu Yang Date: Wed, 2 Aug 2017 16:07:51 +0800 Subject: [PATCH] Initialize Gradient Checker Add get_numeric_gradient API and its unittest. --- paddle/pybind/pybind.cc | 10 ++- .../paddle/v2/framework/tests/CMakeLists.txt | 3 +- .../v2/framework/tests/gradient_checker.py | 69 +++++++++++++++++++ 3 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 python/paddle/v2/framework/tests/gradient_checker.py diff --git a/paddle/pybind/pybind.cc b/paddle/pybind/pybind.cc index ee5f675e25..e79ad49b6d 100644 --- a/paddle/pybind/pybind.cc +++ b/paddle/pybind/pybind.cc @@ -77,8 +77,14 @@ PYBIND11_PLUGIN(core) { }) .def("set", paddle::pybind::PyTensorSetFromArray) .def("set", paddle::pybind::PyTensorSetFromArray) - .def("shape", - [](pd::Tensor& self) { return pd::vectorize(self.dims()); }); + .def("shape", [](pd::Tensor& self) { return pd::vectorize(self.dims()); }) + .def("set_float_element", + [](pd::Tensor& self, size_t offset, float f) { + self.data()[offset] = f; + }) + .def("get_float_element", [](pd::Tensor& self, size_t offset) -> float { + return self.data()[offset]; + }); py::class_(m, "Variable", R"DOC(Variable Class. diff --git a/python/paddle/v2/framework/tests/CMakeLists.txt b/python/paddle/v2/framework/tests/CMakeLists.txt index cdaaa60674..494c517a9b 100644 --- a/python/paddle/v2/framework/tests/CMakeLists.txt +++ b/python/paddle/v2/framework/tests/CMakeLists.txt @@ -13,4 +13,5 @@ add_python_test(test_framework test_sigmoid_op.py test_softmax_op.py test_rowwise_add_op.py - test_network.py) + test_network.py + gradient_checker.py) diff --git a/python/paddle/v2/framework/tests/gradient_checker.py b/python/paddle/v2/framework/tests/gradient_checker.py new file mode 100644 index 0000000000..d7e5de8252 --- /dev/null +++ b/python/paddle/v2/framework/tests/gradient_checker.py @@ -0,0 +1,69 @@ +import paddle.v2.framework.core as core +from paddle.v2.framework.create_op_creation_methods import op_creations +import numpy +import unittest + + +def get_numeric_gradient(op, + input_values, + output_name, + input_to_check, + delta=1e-5, + local_scope=None): + if local_scope is None: + local_scope = core.Scope() + for var_name in input_values: + var = local_scope.new_var(var_name) + tensor = var.get_tensor() + tensor.set_dims(input_values[var_name].shape) + tensor.alloc_float() + tensor.set(input_values[var_name]) + + for output in op.outputs(): + local_scope.new_var(output).get_tensor() + + op.infer_shape(local_scope) + + for output in op.outputs(): + local_scope.find_var(output).get_tensor().alloc_float() + + cpu_ctx = core.DeviceContext.cpu_context() + + def get_output(): + op.run(local_scope, cpu_ctx) + return numpy.array(local_scope.find_var(output_name).get_tensor()).sum() + + def product(dim): + return reduce(lambda a, b: a * b, dim, 1) + + tensor_to_check = local_scope.find_var(input_to_check).get_tensor() + tensor_size = product(tensor_to_check.get_dims()) + gradient_flat = numpy.zeros(shape=(tensor_size, ), dtype='float32') + for i in xrange(tensor_size): + origin = tensor_to_check.get_float_element(i) + x_pos = origin + delta + tensor_to_check.set_float_element(i, x_pos) + y_pos = get_output() + + x_neg = origin - delta + tensor_to_check.set_float_element(i, x_neg) + y_neg = get_output() + + tensor_to_check.set_float_element(i, origin) # restore old value + gradient_flat[i] = (y_pos - y_neg) / delta / 2 + return gradient_flat.reshape(tensor_to_check.get_dims()) + + +if __name__ == '__main__': + + class GetNumericGradientTest(unittest.TestCase): + def test_add_op(self): + add_op = op_creations.add_two(X="X", Y="Y", Out="Z") + x = numpy.random.random((10, 1)).astype("float32") + y = numpy.random.random((10, 1)).astype("float32") + + arr = get_numeric_gradient(add_op, {'X': x, "Y": y}, 'Z', 'X') + + self.assertAlmostEqual(arr.mean(), 1.0, delta=1e-2) + + unittest.main() -- GitLab