diff --git a/paddle/framework/CMakeLists.txt b/paddle/framework/CMakeLists.txt index 6e32a1c99ba6b8bfac12c227e0cf66e0a9f16557..774c7b021754be607cd895ca910583e992ed26a0 100644 --- a/paddle/framework/CMakeLists.txt +++ b/paddle/framework/CMakeLists.txt @@ -43,7 +43,7 @@ add_custom_command(TARGET framework_py_proto POST_BUILD cc_library(backward SRCS backward.cc DEPS net_op) cc_test(backward_test SRCS backward_test.cc DEPS backward recurrent_op device_context) -cc_library(executor SRCS executor.cc DEPS op_registry device_context scope framework_proto backward) +cc_library(executor SRCS executor.cc DEPS op_registry device_context scope framework_proto backward glog) cc_library(prune SRCS prune.cc DEPS framework_proto) cc_test(prune_test SRCS prune_test.cc DEPS op_info prune recurrent_op device_context) diff --git a/paddle/framework/executor.cc b/paddle/framework/executor.cc index 00caa6e1d53a4bcfae56c4459413bc1622321960..d50f0da03245783f8f0de481d7be0699fd10feac 100644 --- a/paddle/framework/executor.cc +++ b/paddle/framework/executor.cc @@ -68,9 +68,13 @@ void Executor::Run(const ProgramDesc& pdesc, Scope* scope, int block_id) { for (auto& var : block.vars()) { if (var.persistable()) { - scope->Var(var.name()); + auto* ptr = scope->Var(var.name()); + VLOG(3) << "Create Variable " << var.name() + << " global, which pointer is " << ptr; } else { - local_scope.Var(var.name()); + auto* ptr = local_scope.Var(var.name()); + VLOG(3) << "Create Variable " << var.name() + << " locally, which pointer is " << ptr; } } diff --git a/paddle/framework/feed_fetch_method.h b/paddle/framework/feed_fetch_method.h index 826d180bfc5445224a8d9292f06eeb58d9a46b29..9b23ad271cb3782794f624cb17eaf28fd3ca801a 100644 --- a/paddle/framework/feed_fetch_method.h +++ b/paddle/framework/feed_fetch_method.h @@ -13,6 +13,8 @@ See the License for the specific language governing permissions and limitations under the License. */ #pragma once +#include "glog/logging.h" +#include "paddle/framework/feed_fetch_type.h" #include "paddle/framework/scope.h" #include "paddle/framework/variable.h" @@ -24,6 +26,7 @@ void SetFeedVariable(const LoDTensor& input, const std::string& var_name, size_t index) { // If var_name Variable is not found in GlobalScope, a new variable will // be created. + VLOG(3) << "SetFeedVariable name=" << var_name << " index=" << index; Variable* g_feed_value = GetGlobalScope().Var(var_name); auto& feed_inputs = *(g_feed_value->GetMutable>()); @@ -40,10 +43,15 @@ LoDTensor& GetFetchVariable(const std::string& var_name, size_t index) { // Since we want to fetch LodTensor from a variable, the variable must // be created alreadly. Variable* g_fetch_value = GetGlobalScope().FindVar(var_name); - auto& fetch_outputs = - *(g_fetch_value->GetMutable>()); + PADDLE_ENFORCE(g_fetch_value->IsType(), + "Only %s can be invoked by GetFetchVariable", + typeid(FeedFetchList).name()); + auto& fetch_outputs = *g_fetch_value->GetMutable(); + auto& tensor = fetch_outputs[index]; + VLOG(3) << "Fetch " << var_name << " with index " << index + << " shape= " << tensor.dims(); PADDLE_ENFORCE_LT(index, fetch_outputs.size()); - return fetch_outputs[index]; + return tensor; } } // namespace framework diff --git a/paddle/framework/framework.proto b/paddle/framework/framework.proto index 008fb45fb7bcb2f9b3d02376b15d2f88515f86d9..2aa961f1407c44fb4d4a149c40b3dad5b243c354 100644 --- a/paddle/framework/framework.proto +++ b/paddle/framework/framework.proto @@ -112,6 +112,8 @@ message VarDesc { enum VarType { LOD_TENSOR = 1; SELECTED_ROWS = 2; + FEED_MINIBATCH = 3; + FETCH_LIST = 4; } required string name = 1; required VarType type = 2; diff --git a/paddle/framework/program_desc_test.cc b/paddle/framework/program_desc_test.cc index 32ee275429687ae079ae8e15b3133428c6ff01b9..c9709a2d3f1d9e0be2bda1e8e9e7835ca49141b1 100644 --- a/paddle/framework/program_desc_test.cc +++ b/paddle/framework/program_desc_test.cc @@ -80,4 +80,4 @@ TEST(ProgramDesc, copy_ctor) { // different and it is correct. } } // namespace framework -} // namespace paddle \ No newline at end of file +} // namespace paddle diff --git a/paddle/framework/variable.h b/paddle/framework/variable.h index 38fc2720a3023039aa113b32a394bda9c5def4c0..a80f0e66b5a59bf95efc200d159ad5dd9cf4111a 100644 --- a/paddle/framework/variable.h +++ b/paddle/framework/variable.h @@ -25,7 +25,10 @@ class Variable { public: template const T& Get() const { - PADDLE_ENFORCE(IsType(), "Variable must be type %s", typeid(T).name()); + PADDLE_ENFORCE(holder_ != nullptr, "Variable must hold some thing"); + PADDLE_ENFORCE(IsType(), + "Variable must be type %s, the holding type is %s", + typeid(T).name(), holder_->Type().name()); return *static_cast(holder_->Ptr()); } diff --git a/paddle/operators/feed_op.cc b/paddle/operators/feed_op.cc index d742bbe51b678fcdaf54826947d29060bf3e4e0d..bf453c85966848d492606644a380a57196ab9869 100644 --- a/paddle/operators/feed_op.cc +++ b/paddle/operators/feed_op.cc @@ -26,8 +26,9 @@ class FeedOp : public framework::OperatorBase { : OperatorBase(type, inputs, outputs, attrs) {} void Run(const framework::Scope &scope, const platform::DeviceContext &dev_ctx) const override { - auto feed_var_name = Input("Input"); + auto feed_var_name = Input("X"); auto *feed_var = scope.FindVar(feed_var_name); + PADDLE_ENFORCE(feed_var != nullptr, "Cannot find feed_var in scope, feed_var_name is %s", feed_var_name); @@ -40,6 +41,9 @@ class FeedOp : public framework::OperatorBase { auto col = Attr("col"); + VLOG(3) << "Feed Var " << feed_var_name << "'s " << col << " column to var" + << out_name; + auto &feed_list = feed_var->Get(); auto &feed_item = feed_list.at(static_cast(col)); auto *out_item = out_var->GetMutable(); @@ -48,10 +52,21 @@ class FeedOp : public framework::OperatorBase { } }; +class FeedOpInfoMaker : public framework::OpProtoAndCheckerMaker { + public: + FeedOpInfoMaker(framework::OpProto *proto, + framework::OpAttrChecker *op_checker) + : OpProtoAndCheckerMaker(proto, op_checker) { + AddInput("X", "The input of feed op"); + AddOutput("Out", "The output of feed op"); + AddComment("feed op, it should not be configured by users directly"); + AddAttr("col", "column of feed"); + } +}; + } // namespace operators } // namespace paddle -// We do not need to register OpInfoMaker, -// since feed operator will not be used by end users directly REGISTER_OPERATOR(feed, paddle::operators::FeedOp, - paddle::framework::EmptyGradOpMaker); + paddle::framework::EmptyGradOpMaker, + paddle::operators::FeedOpInfoMaker); diff --git a/paddle/operators/fetch_op.cc b/paddle/operators/fetch_op.cc index 55d6ac093959a6e1c11457085a8ebdd8a14adaf3..524e77d6ad3a1c7a96e104405827205f704f8a59 100644 --- a/paddle/operators/fetch_op.cc +++ b/paddle/operators/fetch_op.cc @@ -27,7 +27,7 @@ class FetchOp : public framework::OperatorBase { void Run(const framework::Scope &scope, const platform::DeviceContext &dev_ctx) const override { - auto fetch_var_name = Input("Input"); + auto fetch_var_name = Input("X"); auto *fetch_var = scope.FindVar(fetch_var_name); PADDLE_ENFORCE(fetch_var != nullptr, "Cannot find fetch variable in scope, fetch_var_name is %s", @@ -52,13 +52,25 @@ class FetchOp : public framework::OperatorBase { // FIXME(yuyang18): Should we assume the fetch operator always generate // CPU outputs? dst_item.CopyFromTensor(src_item, platform::CPUPlace(), dev_ctx); + + VLOG(3) << "Fetch variable " << fetch_var_name << " to " << out_name; } }; +class FetchOpInfoMaker : public framework::OpProtoAndCheckerMaker { + public: + FetchOpInfoMaker(framework::OpProto *proto, + framework::OpAttrChecker *op_checker) + : OpProtoAndCheckerMaker(proto, op_checker) { + AddInput("X", "The input of fetch op"); + AddOutput("Out", "The output of fetch op"); + AddComment("fetch op, it should not be configured by users directly"); + AddAttr("col", "column of fetch"); + } +}; } // namespace operators } // namespace paddle -// We do not need to register OpInfoMaker, -// since fetch operator will not be used by end users directly REGISTER_OPERATOR(fetch, paddle::operators::FetchOp, - paddle::framework::EmptyGradOpMaker); + paddle::framework::EmptyGradOpMaker, + paddle::operators::FetchOpInfoMaker); diff --git a/paddle/pybind/protobuf.cc b/paddle/pybind/protobuf.cc index 58739d888ac537222fdfeff0c52bd90ab58362c1..405ac544e10f19a33399a649f76699fefc3d49b9 100644 --- a/paddle/pybind/protobuf.cc +++ b/paddle/pybind/protobuf.cc @@ -222,7 +222,9 @@ void BindVarDsec(py::module &m) { py::enum_(var_desc, "VarType", "") .value("LOD_TENSOR", VarDesc::LOD_TENSOR) - .value("SELECTED_ROWS", VarDesc::SELECTED_ROWS); + .value("SELECTED_ROWS", VarDesc::SELECTED_ROWS) + .value("FEED_MINIBATCH", VarDesc::FEED_MINIBATCH) + .value("FETCH_LIST", VarDesc::FETCH_LIST); } void BindOpDesc(py::module &m) { diff --git a/paddle/pybind/pybind.cc b/paddle/pybind/pybind.cc index 16661b93e56da30ecd3848d28a0f4667b710e80c..84ebe3c2b84a5b4fd3fb5d49494a19dea873b9c4 100644 --- a/paddle/pybind/pybind.cc +++ b/paddle/pybind/pybind.cc @@ -111,6 +111,7 @@ PYBIND11_PLUGIN(core) { new (&instance) LoDTensor(new_lod); #endif }) + .def("__init__", [](LoDTensor &instance) { new (&instance) LoDTensor(); }) .def("set_lod", [](LoDTensor &self, const std::vector> &lod) { #ifndef PADDLE_WITH_CUDA @@ -216,7 +217,8 @@ All parameter, weight, gradient are variables in Paddle. .def(py::init<>()) .def("new_scope", [](Scope &self) -> Scope * { return &self.NewScope(); }, py::return_value_policy::reference) - .def("drop_kids", &Scope::DropKids); + .def("drop_kids", &Scope::DropKids) + .def_static("global_scope", &GetGlobalScope); //! @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. @@ -264,6 +266,17 @@ All parameter, weight, gradient are variables in Paddle. .def(py::init<>()) .def("__str__", string::to_string); + py::class_(m, "Place") + .def(py::init<>()) + .def("set_place", + [](platform::Place &self, const platform::CPUPlace &cpu_place) { + self = cpu_place; + }) + .def("set_place", + [](platform::Place &self, const platform::GPUPlace &gpu_place) { + self = gpu_place; + }); + py::class_(m, "Operator") .def_static("create", [](py::bytes protobin) { @@ -437,14 +450,15 @@ All parameter, weight, gradient are variables in Paddle. py::class_(m, "Executor") .def(py::init &>()) .def("run", - [](Executor &self, const ProgramDesc &program_desc, int block_id) { + [](Executor &self, ProgramDescBind *program_bind, int block_id) { framework::Scope &global_scope = GetGlobalScope(); - self.Run(program_desc, &global_scope, block_id); + self.Run(*program_bind->Proto(), &global_scope, block_id); }); m.def("unique_integer", UniqueIntegerGenerator); m.def("is_compile_gpu", IsCompileGPU); + //! FIXME: it is no need to `set_xxx_float/double/int` m.def("set_feed_variable_float", framework::SetFeedVariable); m.def("set_feed_variable_double", framework::SetFeedVariable); m.def("set_feed_variable_int", framework::SetFeedVariable); diff --git a/python/paddle/v2/framework/executor.py b/python/paddle/v2/framework/executor.py new file mode 100644 index 0000000000000000000000000000000000000000..8da5daad993e9ceaff93b5271c30a3b260b7abcc --- /dev/null +++ b/python/paddle/v2/framework/executor.py @@ -0,0 +1,59 @@ +import paddle.v2.framework.core as core +from paddle.v2.framework.framework import Block, Program + + +class Executor(object): + def __init__(self, places): + if not isinstance(places, list) and not isinstance(places, tuple): + places = [places] + + act_places = [] + for each in places: + p = core.Place() + p.set_place(each) + act_places.append(p) + + self.executor = core.Executor(act_places) + + def run(self, + program, + feed, + fetch_list, + feed_var_name='feed', + fetch_var_name='fetch'): + if not isinstance(program, Program): + raise TypeError() + + program = program.clone() + global_block = program.global_block() + feed_var = global_block.create_var( + name=feed_var_name, + type=core.VarDesc.VarType.FEED_MINIBATCH, + persistable=True) + + for i, name in enumerate(feed): + out = global_block.var(name) + global_block.prepend_op( + 'feed', + inputs={'X': [feed_var]}, + outputs={'Out': [out]}, + attrs={'col': i}) + # FIXME + core.set_feed_variable_float(feed[name], feed_var.name, i) + + fetch_var = global_block.create_var( + name=fetch_var_name, + type=core.VarDesc.VarType.FETCH_LIST, + persistable=True) + for i, var in enumerate(fetch_list): + global_block.append_op( + type='fetch', + inputs={'X': [var]}, + outputs={'Out': [fetch_var]}, + attrs={'col': i}) + + self.executor.run(program.desc, 0) + return [ + core.get_fetch_variable(fetch_var_name, i) + for i in xrange(len(fetch_list)) + ] diff --git a/python/paddle/v2/framework/framework.py b/python/paddle/v2/framework/framework.py index a24c78171e07249beeb8131d2d338b01c30e5c1f..a68f2afcfa86fcfea57fc2d07f4e7951773d3bac 100644 --- a/python/paddle/v2/framework/framework.py +++ b/python/paddle/v2/framework/framework.py @@ -256,7 +256,8 @@ class Operator(object): self.desc.set_block_attr(attr_name, attrs[attr_name].desc) self.desc.check_attrs() - self.desc.infer_shape(self.block.desc) + if type not in {'feed', 'fetch'}: + self.desc.infer_shape(self.block.desc) def __str__(self): protostr = self.desc.serialize_to_string() @@ -323,9 +324,12 @@ class Block(object): return self.desc.id def var(self, name): - if name not in self.vars: + if not isinstance(name, basestring): + raise TypeError() + v = self.vars.get(name, None) + if v is None: raise ValueError("var %s not in this block" % name) - return self.vars[name] + return v def all_parameters(self): return {v for k, v in self.vars.iteritems() if isinstance(v, Parameter)} diff --git a/python/paddle/v2/framework/layers.py b/python/paddle/v2/framework/layers.py index c7397716c47dd7088d840edb00d96dda2fe88f1d..329a6830b6a94b12a522f7b99feb97babb843ccc 100644 --- a/python/paddle/v2/framework/layers.py +++ b/python/paddle/v2/framework/layers.py @@ -55,9 +55,11 @@ def data(name, shape, data_type='float32', type=core.VarDesc.VarType.LOD_TENSOR, + append_batch_size=True, program=None): helper = LayerHelper('data', **locals()) - shape = [-1] + shape # append batch size as -1 + if append_batch_size: + shape = [-1] + shape # append batch size as -1 return helper.create_global_variable( name=name, shape=shape, dtype=data_type, type=type) @@ -112,6 +114,7 @@ def _create_op_func_(op_type): _create_op_func_('mean') +_create_op_func_('mul') _create_op_func_('pool2d') diff --git a/python/paddle/v2/framework/tests/test_executor_and_mul.py b/python/paddle/v2/framework/tests/test_executor_and_mul.py new file mode 100644 index 0000000000000000000000000000000000000000..35f775711167ce0d210044ab4cb382db802f39a5 --- /dev/null +++ b/python/paddle/v2/framework/tests/test_executor_and_mul.py @@ -0,0 +1,36 @@ +import unittest +from paddle.v2.framework.layers import mul, data +import paddle.v2.framework.core as core +from paddle.v2.framework.executor import Executor +from paddle.v2.framework.framework import g_program +import numpy + + +class TestExecutor(unittest.TestCase): + def test_mul(self): + a = data(name='a', shape=[784], data_type='float32') + b = data( + name='b', + shape=[784, 100], + data_type='float32', + append_batch_size=False) + out = mul(x=a, y=b) + place = core.CPUPlace() + a_np = numpy.random.random((100, 784)).astype('float32') + tensor_a = core.LoDTensor() + tensor_a.set(a_np, place) + b_np = numpy.random.random((784, 100)).astype('float32') + tensor_b = core.LoDTensor() + tensor_b.set(b_np, place) + exe = Executor(place) + outs = exe.run(g_program, + feed={'a': tensor_a, + 'b': tensor_b}, + fetch_list=[out]) + out = numpy.array(outs[0]) + self.assertEqual((100, 100), out.shape) + self.assertTrue(numpy.allclose(out, numpy.dot(a_np, b_np))) + + +if __name__ == '__main__': + unittest.main()