From 0380bfb3cf693cf233a3ad5fa4382fc65a2c7a02 Mon Sep 17 00:00:00 2001 From: Yu Yang <yuyang18@baidu.com> Date: Wed, 19 Jul 2017 17:47:06 +0800 Subject: [PATCH 01/11] Expose Net to Python * Expose PlainNet to Python, make python can add_op, complete_add_op * Provide a low level api to manipulate Net * Unittest for Net::DebugString --- paddle/framework/net.cc | 5 +- paddle/pybind/pybind.cc | 71 ++++++++++++++------ python/paddle/v2/framework/tests/test_net.py | 28 ++++++++ 3 files changed, 81 insertions(+), 23 deletions(-) create mode 100644 python/paddle/v2/framework/tests/test_net.py diff --git a/paddle/framework/net.cc b/paddle/framework/net.cc index 501536657d..407a69fda6 100644 --- a/paddle/framework/net.cc +++ b/paddle/framework/net.cc @@ -61,7 +61,10 @@ std::string PlainNet::DebugString() const { std::ostringstream os; os << this->type_ << ":" << std::endl; for (auto& op : ops_) { - os << "\t" << op->DebugString() << std::endl; + std::istringstream is(op->DebugString()); + for (std::string line; std::getline(is, line);) { + os << " " << line << std::endl; + } } return os.str(); } diff --git a/paddle/pybind/pybind.cc b/paddle/pybind/pybind.cc index 7e84550f77..bd126f0e97 100644 --- a/paddle/pybind/pybind.cc +++ b/paddle/pybind/pybind.cc @@ -13,15 +13,16 @@ See the License for the specific language governing permissions and limitations under the License. */ #include <Python.h> -#include <paddle/framework/op_registry.h> -#include <paddle/framework/operator.h> -#include <paddle/framework/scope.h> -#include <paddle/pybind/tensor_bind.h> -#include <pybind11/numpy.h> -#include <pybind11/pybind11.h> -#include <pybind11/stl.h> #include <fstream> #include <vector> +#include "paddle/framework/net.h" +#include "paddle/framework/op_registry.h" +#include "paddle/framework/operator.h" +#include "paddle/framework/scope.h" +#include "paddle/pybind/tensor_bind.h" +#include "pybind11/numpy.h" +#include "pybind11/pybind11.h" +#include "pybind11/stl.h" namespace py = pybind11; namespace pd = paddle::framework; @@ -29,6 +30,17 @@ namespace pd = paddle::framework; USE_OP(add_two); USE_OP_WITHOUT_KERNEL(fc); +template <typename ClassType> +void ExposeOperator(ClassType& m) { + m.def("infer_shape", &ClassType::type::InferShape) + .def("run", &ClassType::type::Run) + .def("outputs", + [](const typename ClassType::type& op) -> std::vector<std::string> { + return op.outputs_; + }) + .def("__str__", &ClassType::type::DebugString); +} + PYBIND11_PLUGIN(core) { py::module m("core", "C++ core of Paddle Paddle"); @@ -107,21 +119,36 @@ All parameter, weight, gradient are variables in Paddle. return new paddle::platform::CPUDeviceContext(); }); - py::class_<pd::OperatorBase, pd::OperatorPtr>(m, "Operator") - .def("__str__", &pd::OperatorBase::DebugString) - .def_static("create", - [](py::bytes protobin) { - pd::OpDesc desc; - PADDLE_ENFORCE(desc.ParsePartialFromString(protobin), - "Cannot parse user input to OpDesc"); - PADDLE_ENFORCE(desc.IsInitialized(), - "User OpDesc is not initialized, reason %s", - desc.InitializationErrorString()); - return pd::OpRegistry::CreateOp(desc); - }) - .def("infer_shape", &pd::OperatorBase::InferShape) - .def("run", &pd::OperatorBase::Run) - .def("outputs", [](const pd::OperatorPtr& op) { return op->outputs_; }); + py::class_<pd::OperatorBase, pd::OperatorPtr> operator_base(m, "Operator"); + + operator_base.def_static("create", [](py::bytes protobin) -> pd::OperatorPtr { + pd::OpDesc desc; + PADDLE_ENFORCE(desc.ParsePartialFromString(protobin), + "Cannot parse user input to OpDesc"); + PADDLE_ENFORCE(desc.IsInitialized(), + "User OpDesc is not initialized, reason %s", + desc.InitializationErrorString()); + return pd::OpRegistry::CreateOp(desc); + }); + ExposeOperator(operator_base); + + using PlainNetPtr = std::shared_ptr<pd::PlainNet>; + py::class_<pd::PlainNet, PlainNetPtr> net(m, "Net"); + + net.def_static("create", + []() -> std::shared_ptr<pd::PlainNet> { + auto retv = std::make_shared<pd::PlainNet>(); + retv->type_ = "naive_net"; + return retv; + }) + .def("add_op", &pd::PlainNet::AddOp) + .def("add_op", + [](PlainNetPtr& self, const PlainNetPtr& net) -> void { + self->AddOp(std::static_pointer_cast<pd::OperatorBase>(net)); + }) + .def("complete_add_op", &pd::PlainNet::CompleteAddOp) + .def("complete_add_op", [](PlainNetPtr& self) { self->CompleteAddOp(); }); + ExposeOperator(net); return m.ptr(); } diff --git a/python/paddle/v2/framework/tests/test_net.py b/python/paddle/v2/framework/tests/test_net.py new file mode 100644 index 0000000000..6a97c24990 --- /dev/null +++ b/python/paddle/v2/framework/tests/test_net.py @@ -0,0 +1,28 @@ +import paddle.v2.framework.core as core +from paddle.v2.framework.create_op_creation_methods import op_creations +import unittest + + +class TestNet(unittest.TestCase): + def test_net_all(self): + net = core.Net.create() + op1 = op_creations.add_two(X="X", Y="Y", Out="Out") + net.add_op(op1) + + net2 = core.Net.create() + net2.add_op(op_creations.fc(X="X", W="w", Y="fc.out")) + net2.complete_add_op(True) + net.add_op(net2) + net.complete_add_op(True) + expected = '''naive_net: + Op(add_two), inputs:(X, Y), outputs:(Out). + naive_net: + fc: + Op(mul), inputs:(X, w), outputs:(@TEMP@fc@0). + Op(sigmoid), inputs:(@TEMP@fc@0), outputs:(fc.out). +''' + self.assertEqual(expected, str(net)) + + +if __name__ == '__main__': + unittest.main() From 684563660bab79408b3bf180cf0d49786bc3dd8b Mon Sep 17 00:00:00 2001 From: Yu Yang <yuyang18@baidu.com> Date: Mon, 24 Jul 2017 11:33:20 +0800 Subject: [PATCH 02/11] Init commit --- .../framework/create_op_creation_methods.py | 3 + python/paddle/v2/framework/network.py | 86 +++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 python/paddle/v2/framework/network.py diff --git a/python/paddle/v2/framework/create_op_creation_methods.py b/python/paddle/v2/framework/create_op_creation_methods.py index 7248c3f52a..b034efffb6 100644 --- a/python/paddle/v2/framework/create_op_creation_methods.py +++ b/python/paddle/v2/framework/create_op_creation_methods.py @@ -220,6 +220,9 @@ def create_op_creation_method(op_proto): __impl__.all_input_args = [var.name for var in op_proto.inputs] __impl__.all_output_args = [var.name for var in op_proto.outputs] __impl__.all_attr_args = [attr.name for attr in op_proto.attrs] + __impl__.all_not_temp_output_args = [ + var.name for var in op_proto.outputs if not var.temporary + ] return __impl__ diff --git a/python/paddle/v2/framework/network.py b/python/paddle/v2/framework/network.py new file mode 100644 index 0000000000..347e7bb5ae --- /dev/null +++ b/python/paddle/v2/framework/network.py @@ -0,0 +1,86 @@ +import paddle.v2.framework.core as core +from paddle.v2.framework.create_op_creation_methods import op_creations +from default_scope_funcs import create_var, get_var, get_cur_scope + + +class NetworkFunctor(object): + def __init__(self, func, net): + self.func = func + self.net = net + + def __call__(self, **kwargs): + inputs = self.func.all_input_args + for ipt in inputs: + if ipt in kwargs: + var = kwargs[ipt] + if isinstance(var, basestring): + var_name = var + var = create_var(var) + self.net.var_name_map[var] = var_name + if not isinstance(var, core.Variable): + raise TypeError( + "Input of op creation must be string or variable") + + kwargs[ipt] = self.net.var_name_map[var] + + notemp_outputs = self.func.all_not_temp_output_args + + for name in notemp_outputs: + if name not in kwargs: + kwargs[ + name] = self.func.__name__ + "@OUT@%d" % self.net.generate_idx + self.net.generate_idx += 1 + + outputs = self.func.all_output_args + for opt in outputs: + if opt in kwargs: + var = kwargs[opt] + if isinstance(var, basestring): + var_name = var + var = create_var(var) + self.net.var_name_map[var] = var_name + if not isinstance(var, core.Variable): + raise TypeError( + "Output of op creation must be string or variable") + kwargs[opt] = self.net.var_name_map[var] + + op = self.func(**kwargs) + + self.net.net.add_op(op) + + lst = [get_var(kwargs[opt]) for opt in notemp_outputs] + if len(lst) == 1: + return lst[0] + elif len(lst) == 0: + return None + else: + return lst + + +class Network(object): + def __init__(self): + self.net = core.Net.create() + funcs = (func_name for func_name in dir(op_creations) + if not func_name.startswith("__")) + self.generate_idx = 0 + self.var_name_map = dict() + + for func_name in funcs: + func = getattr(op_creations, func_name) + impl = NetworkFunctor(func, self) + setattr(self, func_name, impl.__call__) + self.__complete_add_op__ = False + + def infer_shape(self): + self.net.infer_shape(get_cur_scope()) + + def __str__(self): + return str(self.net) + + +if __name__ == '__main__': + net = Network() + out = net.add_two(X="a", Y="b") + fc_out = net.fc(X=out, W="fc.w", b="fc.b", activation="softmax") + + print str(net) From 9e4fac1ef42c572728e2645d506849ca1c166769 Mon Sep 17 00:00:00 2001 From: Yu Yang <yuyang18@baidu.com> Date: Mon, 24 Jul 2017 16:12:28 +0800 Subject: [PATCH 03/11] Complete Add Op --- python/paddle/v2/framework/network.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/python/paddle/v2/framework/network.py b/python/paddle/v2/framework/network.py index 347e7bb5ae..bde48851a0 100644 --- a/python/paddle/v2/framework/network.py +++ b/python/paddle/v2/framework/network.py @@ -72,11 +72,21 @@ class Network(object): self.__complete_add_op__ = False def infer_shape(self): + self.complete_add_op() self.net.infer_shape(get_cur_scope()) + def run(self, device_context): + self.complete_add_op() + self.net.run(get_cur_scope(), device_context) + def __str__(self): return str(self.net) + def complete_add_op(self): + if not self.__complete_add_op__: + self.net.complete_add_op() + self.__complete_add_op__ = True + if __name__ == '__main__': net = Network() From 0ab678e9e275a079d938333d1c536fe2766f37b1 Mon Sep 17 00:00:00 2001 From: Yu Yang <yuyang18@baidu.com> Date: Mon, 24 Jul 2017 17:38:50 +0800 Subject: [PATCH 04/11] Add unittest for network --- paddle/pybind/pybind.cc | 11 ----- python/paddle/v2/framework/network.py | 40 +++++++++++++++++-- .../paddle/v2/framework/tests/CMakeLists.txt | 3 +- .../paddle/v2/framework/tests/test_network.py | 23 +++++++++++ 4 files changed, 62 insertions(+), 15 deletions(-) create mode 100644 python/paddle/v2/framework/tests/test_network.py diff --git a/paddle/pybind/pybind.cc b/paddle/pybind/pybind.cc index 62539c1076..43c52957a1 100644 --- a/paddle/pybind/pybind.cc +++ b/paddle/pybind/pybind.cc @@ -56,17 +56,6 @@ void ExposeOperator(ClassType& m) { .def("__str__", &ClassType::type::DebugString); } -template <typename ClassType> -void ExposeOperator(ClassType& m) { - m.def("infer_shape", &ClassType::type::InferShape) - .def("run", &ClassType::type::Run) - .def("outputs", - [](const typename ClassType::type& op) -> std::vector<std::string> { - return op.outputs_; - }) - .def("__str__", &ClassType::type::DebugString); -} - PYBIND11_PLUGIN(core) { py::module m("core", "C++ core of PaddlePaddle"); diff --git a/python/paddle/v2/framework/network.py b/python/paddle/v2/framework/network.py index bde48851a0..ade7e4b85e 100644 --- a/python/paddle/v2/framework/network.py +++ b/python/paddle/v2/framework/network.py @@ -2,13 +2,28 @@ import paddle.v2.framework.core as core from paddle.v2.framework.create_op_creation_methods import op_creations from default_scope_funcs import create_var, get_var, get_cur_scope +__all__ = ['Network'] # Only expose Network + class NetworkFunctor(object): + """ + Network Op Creation Function. Used internally in this module. + It convert string input to Variable. If it is not created before, just + create in scope. + + It is a functor object. means the instances are callable. + + :param func: The op creation function which generated in Python. + :param net: The Network instance. + """ + def __init__(self, func, net): self.func = func self.net = net - def __call__(self, **kwargs): + def __call__(self, *args, **kwargs): + if len(args) != 0: + raise ValueError("Paddle must use keyword argument") inputs = self.func.all_input_args for ipt in inputs: if ipt in kwargs: @@ -58,6 +73,22 @@ class NetworkFunctor(object): class Network(object): + """ + The network concept. It avoid user to manually create operator, create + variable, and combine them into a Net. Just use Network.xxx can create the + operator, create variables in default scope, and add them into `self.net`. + + For example: + + .. code-block: python + + net = Network() + out = net.add_two(X="a", Y="b") + fc_out = net.fc(X="out", W="fc.w") + + net.run(...) + """ + def __init__(self): self.net = core.Net.create() funcs = (func_name for func_name in dir(op_creations) @@ -65,6 +96,9 @@ class Network(object): self.generate_idx = 0 self.var_name_map = dict() + # TODO(yuyang18): This code can work, but do not generate a good + # docstring, try to give a better way generate function in runtime + # later. for func_name in funcs: func = getattr(op_creations, func_name) impl = NetworkFunctor(func, self) @@ -92,5 +126,5 @@ if __name__ == '__main__': net = Network() out = net.add_two(X="a", Y="b") fc_out = net.fc(X=out, W="fc.w", b="fc.b", activation="softmax") - - print str(net) + net.complete_add_op() + print net diff --git a/python/paddle/v2/framework/tests/CMakeLists.txt b/python/paddle/v2/framework/tests/CMakeLists.txt index b3eb2ef8a8..7d1229a34c 100644 --- a/python/paddle/v2/framework/tests/CMakeLists.txt +++ b/python/paddle/v2/framework/tests/CMakeLists.txt @@ -12,4 +12,5 @@ add_python_test(test_framework test_mul_op.py test_sigmoid_op.py test_softmax_op.py - test_rowwise_add_op.py) + test_rowwise_add_op.py + test_network.py) diff --git a/python/paddle/v2/framework/tests/test_network.py b/python/paddle/v2/framework/tests/test_network.py new file mode 100644 index 0000000000..310290e34b --- /dev/null +++ b/python/paddle/v2/framework/tests/test_network.py @@ -0,0 +1,23 @@ +from paddle.v2.framework.network import Network +import paddle.v2.framework.core as core +import unittest + + +class TestNet(unittest.TestCase): + def test_net_all(self): + net = Network() + out = net.add_two(X="X", Y="Y") + fc_out = net.fc(X=out, W="w") + net.complete_add_op() + self.assertTrue(isinstance(fc_out, core.Variable)) + self.assertEqual( + '''Op(naive_net), inputs:(@EMPTY@, X, Y, w), outputs:(@TEMP@fc@0, add_two@OUT@0, fc@OUT@1). + Op(add_two), inputs:(X, Y), outputs:(add_two@OUT@0). + Op(fc), inputs:(add_two@OUT@0, w, @EMPTY@), outputs:(fc@OUT@1, @TEMP@fc@0). + Op(mul), inputs:(add_two@OUT@0, w), outputs:(@TEMP@fc@0). + Op(sigmoid), inputs:(@TEMP@fc@0), outputs:(fc@OUT@1). +''', str(net)) + + +if __name__ == '__main__': + unittest.main() From 0ceeacbe455c3e74431dfd92c4d837a52869d424 Mon Sep 17 00:00:00 2001 From: Yu Yang <yuyang18@baidu.com> Date: Mon, 24 Jul 2017 18:12:50 +0800 Subject: [PATCH 05/11] Make Scope can lookup variable name by variable * Refine unittest also --- paddle/framework/scope.h | 13 ++++++++++++- paddle/framework/scope_test.cc | 2 ++ paddle/pybind/pybind.cc | 10 +++++++++- python/paddle/v2/framework/network.py | 14 ++++---------- python/paddle/v2/framework/tests/test_network.py | 9 +++++++++ 5 files changed, 36 insertions(+), 12 deletions(-) diff --git a/paddle/framework/scope.h b/paddle/framework/scope.h index 79c9ffd1a6..cbbccf465d 100644 --- a/paddle/framework/scope.h +++ b/paddle/framework/scope.h @@ -56,7 +56,9 @@ class Scope { if (var) { return var; } else { - vars_[name] = std::unique_ptr<Variable>(new Variable()); + auto ptr = new Variable(); + vars_[name] = std::unique_ptr<Variable>(ptr); + var_names_[ptr] = name; return GetVariable(name); } } @@ -88,7 +90,16 @@ class Scope { (parent_ && parent_->HasVariable(name))); } + std::string GetVariableName(Variable* const var) const { + try { + return var_names_.at(var); + } catch (...) { + return ""; + } + } + private: + std::unordered_map<Variable*, std::string> var_names_; std::unordered_map<std::string, std::unique_ptr<Variable>> vars_; std::shared_ptr<Scope> parent_{nullptr}; }; diff --git a/paddle/framework/scope_test.cc b/paddle/framework/scope_test.cc index df1afb200c..51de74ddfe 100644 --- a/paddle/framework/scope_test.cc +++ b/paddle/framework/scope_test.cc @@ -40,6 +40,8 @@ TEST(Scope, Create) { /// already exist. Variable* var4 = scope->CreateVariable("a"); EXPECT_EQ(var4, var2); + + EXPECT_EQ("a", scope->GetVariableName(var4)); } TEST(Scope, Parent) { diff --git a/paddle/pybind/pybind.cc b/paddle/pybind/pybind.cc index 43c52957a1..3588004122 100644 --- a/paddle/pybind/pybind.cc +++ b/paddle/pybind/pybind.cc @@ -56,6 +56,11 @@ void ExposeOperator(ClassType& m) { .def("__str__", &ClassType::type::DebugString); } +static size_t UniqueIntegerGenerator() { + static std::atomic<size_t> generator; + return generator.fetch_add(1); +} + PYBIND11_PLUGIN(core) { py::module m("core", "C++ core of PaddlePaddle"); @@ -106,7 +111,8 @@ All parameter, weight, gradient are variables in Paddle. py::return_value_policy::reference) .def("create_var", &pd::Scope::CreateVariable, - py::return_value_policy::reference); + py::return_value_policy::reference) + .def("get_var_name", &pd::Scope::GetVariableName); //! @note: Be careful! PyBind will return std::string as an unicode, not //! Python str. If you want a str object, you should cast them in Python. @@ -166,5 +172,7 @@ All parameter, weight, gradient are variables in Paddle. .def("complete_add_op", [](PlainNetPtr& self) { self->CompleteAddOp(); }); ExposeOperator(net); + m.def("unique_integer", UniqueIntegerGenerator); + return m.ptr(); } diff --git a/python/paddle/v2/framework/network.py b/python/paddle/v2/framework/network.py index ade7e4b85e..c85e87413e 100644 --- a/python/paddle/v2/framework/network.py +++ b/python/paddle/v2/framework/network.py @@ -29,35 +29,31 @@ class NetworkFunctor(object): if ipt in kwargs: var = kwargs[ipt] if isinstance(var, basestring): - var_name = var var = create_var(var) - self.net.var_name_map[var] = var_name if not isinstance(var, core.Variable): raise TypeError( "Input of op creation must be string or variable") - kwargs[ipt] = self.net.var_name_map[var] + kwargs[ipt] = get_cur_scope().get_var_name(var) notemp_outputs = self.func.all_not_temp_output_args for name in notemp_outputs: if name not in kwargs: kwargs[ - name] = self.func.__name__ + "@OUT@%d" % self.net.generate_idx - self.net.generate_idx += 1 + name] = self.func.__name__ + "@OUT@%d" % core.unique_integer( + ) outputs = self.func.all_output_args for opt in outputs: if opt in kwargs: var = kwargs[opt] if isinstance(var, basestring): - var_name = var var = create_var(var) - self.net.var_name_map[var] = var_name if not isinstance(var, core.Variable): raise TypeError( "Output of op creation must be string or variable") - kwargs[opt] = self.net.var_name_map[var] + kwargs[opt] = get_cur_scope().get_var_name(var) op = self.func(**kwargs) @@ -93,8 +89,6 @@ class Network(object): self.net = core.Net.create() funcs = (func_name for func_name in dir(op_creations) if not func_name.startswith("__")) - self.generate_idx = 0 - self.var_name_map = dict() # TODO(yuyang18): This code can work, but do not generate a good # docstring, try to give a better way generate function in runtime diff --git a/python/paddle/v2/framework/tests/test_network.py b/python/paddle/v2/framework/tests/test_network.py index 310290e34b..457f8f13a6 100644 --- a/python/paddle/v2/framework/tests/test_network.py +++ b/python/paddle/v2/framework/tests/test_network.py @@ -18,6 +18,15 @@ class TestNet(unittest.TestCase): Op(sigmoid), inputs:(@TEMP@fc@0), outputs:(fc@OUT@1). ''', str(net)) + net2 = Network() + tmp = net2.add_two(X="X", Y="Y") + self.assertTrue(isinstance(tmp, core.Variable)) + net2.complete_add_op() + self.assertEqual( + '''Op(naive_net), inputs:(X, Y), outputs:(add_two@OUT@2). + Op(add_two), inputs:(X, Y), outputs:(add_two@OUT@2). +''', str(net2)) + if __name__ == '__main__': unittest.main() From b80590d70b9d5d963cbf077671b4458f46cc9713 Mon Sep 17 00:00:00 2001 From: Yu Yang <yuyang18@baidu.com> Date: Tue, 25 Jul 2017 13:33:42 +0800 Subject: [PATCH 06/11] Remove test_plain_net --- .../paddle/v2/framework/tests/CMakeLists.txt | 2 +- python/paddle/v2/framework/tests/test_net.py | 10 ++++--- .../v2/framework/tests/test_plain_net.py | 30 ------------------- 3 files changed, 7 insertions(+), 35 deletions(-) delete mode 100644 python/paddle/v2/framework/tests/test_plain_net.py diff --git a/python/paddle/v2/framework/tests/CMakeLists.txt b/python/paddle/v2/framework/tests/CMakeLists.txt index 7d1229a34c..cdaaa60674 100644 --- a/python/paddle/v2/framework/tests/CMakeLists.txt +++ b/python/paddle/v2/framework/tests/CMakeLists.txt @@ -3,7 +3,7 @@ add_python_test(test_framework test_scope.py test_default_scope_funcs.py test_op_creation_methods.py - test_plain_net.py + test_net.py test_tensor.py test_fc_op.py test_add_two_op.py diff --git a/python/paddle/v2/framework/tests/test_net.py b/python/paddle/v2/framework/tests/test_net.py index 6a97c24990..db776d6b64 100644 --- a/python/paddle/v2/framework/tests/test_net.py +++ b/python/paddle/v2/framework/tests/test_net.py @@ -14,14 +14,16 @@ class TestNet(unittest.TestCase): net2.complete_add_op(True) net.add_op(net2) net.complete_add_op(True) - expected = '''naive_net: + + expected = ''' +Op(plain_net), inputs:(@EMPTY@, X, Y, w), outputs:(@TEMP@fc@0, Out, fc.out). Op(add_two), inputs:(X, Y), outputs:(Out). - naive_net: - fc: + Op(plain_net), inputs:(@EMPTY@, X, w), outputs:(@TEMP@fc@0, fc.out). + Op(fc), inputs:(X, w, @EMPTY@), outputs:(fc.out, @TEMP@fc@0). Op(mul), inputs:(X, w), outputs:(@TEMP@fc@0). Op(sigmoid), inputs:(@TEMP@fc@0), outputs:(fc.out). ''' - self.assertEqual(expected, str(net)) + self.assertEqual(expected, "\n" + str(net)) if __name__ == '__main__': diff --git a/python/paddle/v2/framework/tests/test_plain_net.py b/python/paddle/v2/framework/tests/test_plain_net.py deleted file mode 100644 index 2b919aca28..0000000000 --- a/python/paddle/v2/framework/tests/test_plain_net.py +++ /dev/null @@ -1,30 +0,0 @@ -import paddle.v2.framework.core as core -from paddle.v2.framework.create_op_creation_methods import op_creations -import unittest - - -class TestNet(unittest.TestCase): - def test_net_all(self): - net = core.PlainNet.create() - op1 = op_creations.add_two(X="X", Y="Y", Out="Out") - net.add_op(op1) - - net2 = core.PlainNet.create() - net2.add_op(op_creations.fc(X="X", W="w", Y="fc.out")) - net2.complete_add_op(True) - net.add_op(net2) - net.complete_add_op(True) - - expected = ''' -Op(plain_net), inputs:(@EMPTY@, X, Y, w), outputs:(@TEMP@fc@0, Out, fc.out). - Op(add_two), inputs:(X, Y), outputs:(Out). - Op(plain_net), inputs:(@EMPTY@, X, w), outputs:(@TEMP@fc@0, fc.out). - Op(fc), inputs:(X, w, @EMPTY@), outputs:(fc.out, @TEMP@fc@0). - Op(mul), inputs:(X, w), outputs:(@TEMP@fc@0). - Op(sigmoid), inputs:(@TEMP@fc@0), outputs:(fc.out). -''' - self.assertEqual(expected, "\n" + str(net)) - - -if __name__ == '__main__': - unittest.main() From 754f0c68da61ae4b7a5a67cdc9d841159bd73fbe Mon Sep 17 00:00:00 2001 From: Yu Yang <yuyang18@baidu.com> Date: Tue, 25 Jul 2017 15:26:01 +0800 Subject: [PATCH 07/11] Fix unittest --- paddle/framework/scope.h | 16 ++++++++-------- paddle/framework/scope_test.cc | 3 +++ paddle/pybind/pybind.cc | 10 +--------- python/paddle/v2/framework/tests/test_network.py | 4 ++-- 4 files changed, 14 insertions(+), 19 deletions(-) diff --git a/paddle/framework/scope.h b/paddle/framework/scope.h index cbbccf465d..4faaf84144 100644 --- a/paddle/framework/scope.h +++ b/paddle/framework/scope.h @@ -57,8 +57,8 @@ class Scope { return var; } else { auto ptr = new Variable(); - vars_[name] = std::unique_ptr<Variable>(ptr); - var_names_[ptr] = name; + name_to_var_[name] = std::unique_ptr<Variable>(ptr); + var_to_name_[ptr] = name; return GetVariable(name); } } @@ -70,8 +70,8 @@ class Scope { * from it's parent scope. Return nullptr if not found. */ Variable* GetVariable(const std::string& name) const { - auto it = vars_.find(name); - if (it != vars_.end()) { + auto it = name_to_var_.find(name); + if (it != name_to_var_.end()) { return it->second.get(); } else if (parent_ != nullptr) { return parent_->GetVariable(name); @@ -86,21 +86,21 @@ class Scope { * Find if there is a Variable in this scope and it's parent scope */ bool HasVariable(const std::string& name) const { - return (vars_.find(name) != vars_.end() || + return (name_to_var_.find(name) != name_to_var_.end() || (parent_ && parent_->HasVariable(name))); } std::string GetVariableName(Variable* const var) const { try { - return var_names_.at(var); + return var_to_name_.at(var); } catch (...) { return ""; } } private: - std::unordered_map<Variable*, std::string> var_names_; - std::unordered_map<std::string, std::unique_ptr<Variable>> vars_; + std::unordered_map<Variable*, std::string> var_to_name_; + std::unordered_map<std::string, std::unique_ptr<Variable>> name_to_var_; std::shared_ptr<Scope> parent_{nullptr}; }; diff --git a/paddle/framework/scope_test.cc b/paddle/framework/scope_test.cc index 51de74ddfe..ff069c7be0 100644 --- a/paddle/framework/scope_test.cc +++ b/paddle/framework/scope_test.cc @@ -42,6 +42,9 @@ TEST(Scope, Create) { EXPECT_EQ(var4, var2); EXPECT_EQ("a", scope->GetVariableName(var4)); + Scope scope2; + auto var = scope2.CreateVariable("tmp"); + EXPECT_EQ("", scope->GetVariableName(var)); } TEST(Scope, Parent) { diff --git a/paddle/pybind/pybind.cc b/paddle/pybind/pybind.cc index 3588004122..0b152d03c0 100644 --- a/paddle/pybind/pybind.cc +++ b/paddle/pybind/pybind.cc @@ -15,14 +15,6 @@ limitations under the License. */ #include <Python.h> #include <fstream> #include <vector> -#include "paddle/framework/net.h" -#include "paddle/framework/op_registry.h" -#include "paddle/framework/operator.h" -#include "paddle/framework/scope.h" -#include "paddle/pybind/tensor_bind.h" -#include "pybind11/numpy.h" -#include "pybind11/pybind11.h" -#include "pybind11/stl.h" #include "paddle/framework/net.h" #include "paddle/framework/op_registry.h" @@ -160,7 +152,7 @@ All parameter, weight, gradient are variables in Paddle. net.def_static("create", []() -> std::shared_ptr<pd::PlainNet> { auto retv = std::make_shared<pd::PlainNet>(); - retv->type_ = "naive_net"; + retv->type_ = "plain_net"; return retv; }) .def("add_op", &pd::PlainNet::AddOp) diff --git a/python/paddle/v2/framework/tests/test_network.py b/python/paddle/v2/framework/tests/test_network.py index 457f8f13a6..6d53e233e9 100644 --- a/python/paddle/v2/framework/tests/test_network.py +++ b/python/paddle/v2/framework/tests/test_network.py @@ -11,7 +11,7 @@ class TestNet(unittest.TestCase): net.complete_add_op() self.assertTrue(isinstance(fc_out, core.Variable)) self.assertEqual( - '''Op(naive_net), inputs:(@EMPTY@, X, Y, w), outputs:(@TEMP@fc@0, add_two@OUT@0, fc@OUT@1). + '''Op(plain_net), inputs:(@EMPTY@, X, Y, w), outputs:(@TEMP@fc@0, add_two@OUT@0, fc@OUT@1). Op(add_two), inputs:(X, Y), outputs:(add_two@OUT@0). Op(fc), inputs:(add_two@OUT@0, w, @EMPTY@), outputs:(fc@OUT@1, @TEMP@fc@0). Op(mul), inputs:(add_two@OUT@0, w), outputs:(@TEMP@fc@0). @@ -23,7 +23,7 @@ class TestNet(unittest.TestCase): self.assertTrue(isinstance(tmp, core.Variable)) net2.complete_add_op() self.assertEqual( - '''Op(naive_net), inputs:(X, Y), outputs:(add_two@OUT@2). + '''Op(plain_net), inputs:(X, Y), outputs:(add_two@OUT@2). Op(add_two), inputs:(X, Y), outputs:(add_two@OUT@2). ''', str(net2)) From e3f5fdcc7a242ec8d65e20554bbc2ceb79c0c900 Mon Sep 17 00:00:00 2001 From: Yu Yang <yuyang18@baidu.com> Date: Tue, 25 Jul 2017 17:04:45 +0800 Subject: [PATCH 08/11] Make PADDLE_ENFORCE and PADDLE_THROW catchable * Use EnforceNotMet to unify all exception types. --- paddle/platform/enforce.h | 68 ++++++++++++++++++--------------- paddle/platform/enforce_test.cc | 2 +- 2 files changed, 38 insertions(+), 32 deletions(-) diff --git a/paddle/platform/enforce.h b/paddle/platform/enforce.h index b06ab8a2f1..a3a10fc07f 100644 --- a/paddle/platform/enforce.h +++ b/paddle/platform/enforce.h @@ -36,6 +36,21 @@ limitations under the License. */ namespace paddle { namespace platform { +struct EnforceNotMet : public std::exception { + std::exception_ptr exp_; + std::string err_str_; + + EnforceNotMet(std::exception_ptr e, const char* f, int l) : exp_(e) { + try { + std::rethrow_exception(exp_); + } catch (const std::exception& exp) { + err_str_ = string::Sprintf("%s at [%s:%d]", exp.what(), f, l); + } + } + + const char* what() const noexcept { return err_str_.c_str(); } +}; + // Because most enforce conditions would evaluate to true, we can use // __builtin_expect to instruct the C++ compiler to generate code that // always forces branch prediction of true. @@ -52,9 +67,7 @@ template <typename... Args> inline typename std::enable_if<sizeof...(Args) != 0, void>::type throw_on_error( int stat, const Args&... args) { if (UNLIKELY(!(stat))) { - throw std::runtime_error( - string::Sprintf(args...) + - string::Sprintf(" at [%s:%s];", __FILE__, __LINE__)); + throw std::runtime_error(string::Sprintf(args...)); } } @@ -64,12 +77,8 @@ template <typename... Args> inline typename std::enable_if<sizeof...(Args) != 0, void>::type throw_on_error( cudaError_t e, const Args&... args) { if (UNLIKELY(e)) { - // clang-format off - throw thrust::system_error( - e, thrust::cuda_category(), - string::Sprintf(args...) + - string::Sprintf(" at [%s:%s];", __FILE__, __LINE__)); - // clang-format on + throw thrust::system_error(e, thrust::cuda_category(), + string::Sprintf(args...)); } } @@ -77,12 +86,8 @@ template <typename... Args> inline typename std::enable_if<sizeof...(Args) != 0, void>::type throw_on_error( curandStatus_t stat, const Args&... args) { if (stat != CURAND_STATUS_SUCCESS) { - // clang-format off - throw thrust::system_error( - cudaErrorLaunchFailure, thrust::cuda_category(), - string::Sprintf(args...) + - string::Sprintf(" at [%s:%s];", __FILE__, __LINE__)); - // clang-format on + throw thrust::system_error(cudaErrorLaunchFailure, thrust::cuda_category(), + string::Sprintf(args...)); } } @@ -92,12 +97,8 @@ inline typename std::enable_if<sizeof...(Args) != 0, void>::type throw_on_error( if (stat == CUDNN_STATUS_SUCCESS) { return; } else { - // clang-format off - throw std::runtime_error( - platform::dynload::cudnnGetErrorString(stat) + - string::Sprintf(args...) + - string::Sprintf(" at [%s:%s];", __FILE__, __LINE__)); - // clang-format on + throw std::runtime_error(platform::dynload::cudnnGetErrorString(stat) + + string::Sprintf(args...)); } } @@ -126,22 +127,27 @@ inline typename std::enable_if<sizeof...(Args) != 0, void>::type throw_on_error( } else if (stat == CUBLAS_STATUS_LICENSE_ERROR) { err = "CUBLAS: license error, "; } - throw std::runtime_error(err + string::Sprintf(args...) + - string::Sprintf(" at [%s:%s];", __FILE__, __LINE__)); + throw std::runtime_error(err + string::Sprintf(args...)); } #endif // PADDLE_ONLY_CPU -#define PADDLE_THROW(...) \ - do { \ - throw std::runtime_error( \ - string::Sprintf(__VA_ARGS__) + \ - string::Sprintf(" at [%s:%s];", __FILE__, __LINE__)); \ +#define PADDLE_THROW(...) \ + do { \ + throw ::paddle::platform::EnforceNotMet( \ + std::make_exception_ptr( \ + std::runtime_error(string::Sprintf(__VA_ARGS__))), \ + __FILE__, __LINE__); \ } while (0) -#define PADDLE_ENFORCE(...) \ - do { \ - ::paddle::platform::throw_on_error(__VA_ARGS__); \ +#define PADDLE_ENFORCE(...) \ + do { \ + try { \ + ::paddle::platform::throw_on_error(__VA_ARGS__); \ + } catch (...) { \ + throw ::paddle::platform::EnforceNotMet(std::current_exception(), \ + __FILE__, __LINE__); \ + } \ } while (0) } // namespace platform diff --git a/paddle/platform/enforce_test.cc b/paddle/platform/enforce_test.cc index d7152f8150..2ac31812a8 100644 --- a/paddle/platform/enforce_test.cc +++ b/paddle/platform/enforce_test.cc @@ -23,7 +23,7 @@ TEST(ENFORCE, FAILED) { bool in_catch = false; try { PADDLE_ENFORCE(false, "Enforce is not ok %d at all", 123); - } catch (const std::runtime_error& error) { + } catch (paddle::platform::EnforceNotMet error) { // your error handling code here in_catch = true; std::string msg = "Enforce is not ok 123 at all"; From bc09551e8cd9ec23c538d1782bc4fdacf2dbf6a3 Mon Sep 17 00:00:00 2001 From: Yu Yang <yuyang18@baidu.com> Date: Tue, 25 Jul 2017 17:36:12 +0800 Subject: [PATCH 09/11] Fix unittest --- paddle/framework/net_op_test.cc | 2 +- paddle/framework/op_registry_test.cc | 10 +++++----- paddle/framework/tensor_test.cc | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/paddle/framework/net_op_test.cc b/paddle/framework/net_op_test.cc index 20b42cbb49..d924058624 100644 --- a/paddle/framework/net_op_test.cc +++ b/paddle/framework/net_op_test.cc @@ -69,7 +69,7 @@ TEST(OpKernel, all) { net->Run(scope, dev_ctx); ASSERT_EQ(2, infer_shape_cnt); ASSERT_EQ(2, run_cnt); - ASSERT_THROW(net->AddOp(op2), std::runtime_error); + ASSERT_THROW(net->AddOp(op2), paddle::platform::EnforceNotMet); } TEST(AddBackwardOp, TestGradOp) { auto net = std::make_shared<PlainNet>(); diff --git a/paddle/framework/op_registry_test.cc b/paddle/framework/op_registry_test.cc index 05095372d8..2ef781bf86 100644 --- a/paddle/framework/op_registry_test.cc +++ b/paddle/framework/op_registry_test.cc @@ -90,7 +90,7 @@ TEST(OpRegistry, IllegalAttr) { bool caught = false; try { paddle::framework::OpRegistry::CreateOp(op_desc); - } catch (std::runtime_error& err) { + } catch (paddle::platform::EnforceNotMet err) { caught = true; std::string msg = "larger_than check fail"; const char* err_msg = err.what(); @@ -136,7 +136,7 @@ TEST(OpRegistry, CustomChecker) { bool caught = false; try { paddle::framework::OpRegistry::CreateOp(op_desc); - } catch (std::runtime_error& err) { + } catch (paddle::platform::EnforceNotMet err) { caught = true; std::string msg = "Attribute 'test_attr' is required!"; const char* err_msg = err.what(); @@ -154,7 +154,7 @@ TEST(OpRegistry, CustomChecker) { caught = false; try { paddle::framework::OpRegistry::CreateOp(op_desc); - } catch (std::runtime_error& err) { + } catch (paddle::platform::EnforceNotMet err) { caught = true; std::string msg = "'test_attr' must be even!"; const char* err_msg = err.what(); @@ -192,7 +192,7 @@ TEST(ProtoMaker, DuplicatedAttr) { pd::OpProto op_proto; pd::OpAttrChecker op_checker; auto proto_maker = TestAttrProtoMaker(&op_proto, &op_checker); - ASSERT_THROW(proto_maker.Validate(), std::runtime_error); + ASSERT_THROW(proto_maker.Validate(), paddle::platform::EnforceNotMet); } class TestInOutProtoMaker : public pd::OpProtoAndCheckerMaker { @@ -208,5 +208,5 @@ TEST(ProtoMaker, DuplicatedInOut) { pd::OpProto op_proto; pd::OpAttrChecker op_checker; auto proto_maker = TestInOutProtoMaker(&op_proto, &op_checker); - ASSERT_THROW(proto_maker.Validate(), std::runtime_error); + ASSERT_THROW(proto_maker.Validate(), paddle::platform::EnforceNotMet); } diff --git a/paddle/framework/tensor_test.cc b/paddle/framework/tensor_test.cc index 089844dc01..530a8fb05e 100644 --- a/paddle/framework/tensor_test.cc +++ b/paddle/framework/tensor_test.cc @@ -33,7 +33,7 @@ TEST(Tensor, DataAssert) { bool caught = false; try { src_tensor.data<double>(); - } catch (std::runtime_error& err) { + } catch (paddle::platform::EnforceNotMet err) { caught = true; std::string msg = "Tenosr holds no memory. Call Tensor::mutable_data first."; @@ -107,7 +107,7 @@ TEST(Tensor, ShareDataWith) { bool caught = false; try { dst_tensor.ShareDataWith<float>(src_tensor); - } catch (std::runtime_error& err) { + } catch (paddle::platform::EnforceNotMet err) { caught = true; std::string msg = "Tenosr holds no memory. Call Tensor::mutable_data first."; From e5cb9b77b6026912a34be7bb8044ddad76450842 Mon Sep 17 00:00:00 2001 From: dongzhihong <dzhwinter@gmail.com> Date: Wed, 26 Jul 2017 09:37:36 +0800 Subject: [PATCH 10/11] "fix gen list" --- python/paddle/v2/dataset/mq2007.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/paddle/v2/dataset/mq2007.py b/python/paddle/v2/dataset/mq2007.py index cffb319ad8..b705c9109b 100644 --- a/python/paddle/v2/dataset/mq2007.py +++ b/python/paddle/v2/dataset/mq2007.py @@ -242,9 +242,9 @@ def gen_list(querylist): if not isinstance(querylist, QueryList): querylist = QueryList(querylist) querylist._correct_ranking_() - relevance_score_list = [query.relevance_score for query in querylist] + relevance_score_list = [[query.relevance_score] for query in querylist] feature_vector_list = [query.feature_vector for query in querylist] - yield np.array(relevance_score_list).T, np.array(feature_vector_list) + yield np.array(relevance_score_list), np.array(feature_vector_list) def query_filter(querylists): From ccdc26284897f39d57e6f93ba1e264e6e0473c1d Mon Sep 17 00:00:00 2001 From: caoying03 <caoying03@baidu.com> Date: Tue, 25 Jul 2017 15:29:02 +0800 Subject: [PATCH 11/11] enable v2 use cudnn batch norm automatically. --- python/paddle/trainer/config_parser.py | 3 +-- python/paddle/v2/__init__.py | 8 ++++++++ python/paddle/v2/layer.py | 3 --- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/python/paddle/trainer/config_parser.py b/python/paddle/trainer/config_parser.py index fc112f1327..5477158ecb 100644 --- a/python/paddle/trainer/config_parser.py +++ b/python/paddle/trainer/config_parser.py @@ -2055,8 +2055,7 @@ class BatchNormLayer(LayerBase): # Automatically select cudnn_batch_norm for GPU and batch_norm for CPU. # Also based on cudnn version. use_cudnn = use_gpu and batch_norm_type != "batch_norm" and \ - ((not parallel_nn) or self.config.device > -1) and \ - cudnn_version >= 4007 + ((not parallel_nn) or self.config.device > -1) self.layer_type = "cudnn_batch_norm" if use_cudnn else "batch_norm" super(BatchNormLayer, self).__init__( name, self.layer_type, 0, inputs=inputs, **xargs) diff --git a/python/paddle/v2/__init__.py b/python/paddle/v2/__init__.py index 07ab2c9b18..5bea980611 100644 --- a/python/paddle/v2/__init__.py +++ b/python/paddle/v2/__init__.py @@ -34,6 +34,7 @@ import minibatch import plot import image import model +import paddle.trainer.config_parser as cp __all__ = [ 'optimizer', @@ -58,6 +59,8 @@ __all__ = [ 'model', ] +cp.begin_parse() + def init(**kwargs): import py_paddle.swig_paddle as api @@ -73,6 +76,11 @@ def init(**kwargs): for key in args_dict.keys(): args.append('--%s=%s' % (key, str(args_dict[key]))) + if 'use_gpu' in kwargs: + cp.g_command_config_args['use_gpu'] = kwargs['use_gpu'] + assert 'parallel_nn' not in kwargs, ("currently 'parallel_nn' is not " + "supported in v2 APIs.") + api.initPaddle(*args) diff --git a/python/paddle/v2/layer.py b/python/paddle/v2/layer.py index 4ade1c6f32..6a2bb8d337 100644 --- a/python/paddle/v2/layer.py +++ b/python/paddle/v2/layer.py @@ -324,6 +324,3 @@ def parse_network(output_layers, extra_layers=None): def get_layer(name): return config_base.__layer_map__.get(name) - - -cp.begin_parse()