Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add rnn op interfaces #2775

Closed
wants to merge 64 commits into from
Closed
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
c418dac
add rnn op interfaces
Superjomn Jul 7, 2017
6042795
add Run
Superjomn Jul 7, 2017
13d8ca9
rename state -> memory
Superjomn Jul 7, 2017
a645ae6
change state -> memory
Superjomn Jul 7, 2017
8640f96
make compilable
Superjomn Jul 8, 2017
d4cde51
add .cc
Superjomn Jul 8, 2017
6e99289
init test
Superjomn Jul 8, 2017
63b5841
Merge branch 'develop' of https://github.com/PaddlePaddle/Paddle into…
qingqing01 Jul 8, 2017
08f69f6
Merge branch 'develop' of github.com:PaddlePaddle/Paddle into rnnimpl
Superjomn Jul 10, 2017
007ca1e
add op fake implementation
Superjomn Jul 10, 2017
2538b2f
add CreateStepNet and CreateScopes implementation.
qingqing01 Jul 10, 2017
5eb87f0
add TODO list
luotao1 Jul 10, 2017
4dcb02e
Merge branch 'rnnimpl' of github.com:Superjom/Paddle into rnnimpl
Superjomn Jul 10, 2017
ca53f3a
init memory attributes.
qingqing01 Jul 10, 2017
671cc26
Merge branch 'rnnimpl' of https://github.com/Superjom/Paddle into fea…
qingqing01 Jul 10, 2017
1e48cc8
add LinkMemories
Superjomn Jul 10, 2017
e0cbcd0
Merge branch 'rnnimpl' of github.com:Superjom/Paddle into rnnimpl
Superjomn Jul 10, 2017
f7916a6
add PlainNet fake implementation
Superjomn Jul 10, 2017
089c448
Use std::shared_ptr<Scope> in the OpRunContext.
qingqing01 Jul 10, 2017
bffd11e
add test
Superjomn Jul 10, 2017
c7947de
disable mutable_data
Superjomn Jul 10, 2017
94766b6
Merge branch 'rnnimpl' of github.com:Superjom/Paddle into rnnimpl
Superjomn Jul 10, 2017
6dca711
finist segmentInput function
luotao1 Jul 10, 2017
eabf1bf
Merge branch 'develop' of github.com:PaddlePaddle/Paddle into rnnimpl
Superjomn Jul 11, 2017
d210b0b
enable mutable_data with a trick
Superjomn Jul 11, 2017
6674fee
RNNOp test.
qingqing01 Jul 11, 2017
778ebb4
enable LinkMemories with mutable_data
Superjomn Jul 11, 2017
c60ed35
update
qingqing01 Jul 11, 2017
8642b27
update SegmentInput function with comments
luotao1 Jul 11, 2017
b0938ed
Merge branch 'develop' of github.com:PaddlePaddle/Paddle into rnnimpl
Superjomn Jul 11, 2017
3921fbb
Merge branch 'rnnimpl' of github.com:Superjom/Paddle into rnnimpl
Superjomn Jul 11, 2017
244fe51
create rnn op and step net in unit test.
qingqing01 Jul 11, 2017
020c189
Merge branch 'rnnimpl' of https://github.com/Superjom/Paddle into rnn…
luotao1 Jul 11, 2017
8e70b37
finish ConcatOutput function
luotao1 Jul 11, 2017
4150fa7
Merge branch 'rnnimpl' of github.com:Superjom/Paddle into rnnimpl
Superjomn Jul 12, 2017
1584414
Merge branch 'develop' of github.com:PaddlePaddle/Paddle into rnnimpl
Superjomn Jul 12, 2017
ce802c0
reformat inputs and attributes
Superjomn Jul 12, 2017
a883b4c
Refine unit test.
qingqing01 Jul 12, 2017
b98cae4
Merge branch 'rnnimpl' of https://github.com/Superjom/Paddle into fea…
qingqing01 Jul 12, 2017
a81be58
Refine unit test.
qingqing01 Jul 12, 2017
acde9b7
modify inlinks.
qingqing01 Jul 12, 2017
638384e
update from develop branch.
qingqing01 Jul 12, 2017
82464f5
add OpDesc to Net
Superjomn Jul 12, 2017
bbcc149
Merge branch 'netimpl' into rnnimpl
Superjomn Jul 12, 2017
c92ce74
Merge branch 'develop' into rnnimpl
luotao1 Jul 12, 2017
5c5d890
fix bug and update unit test.
qingqing01 Jul 12, 2017
522445b
resolve conflict.
qingqing01 Jul 12, 2017
01f20be
Merge branch 'rnnimpl' of github.com:Superjom/Paddle into rnnimpl
Superjomn Jul 12, 2017
08003de
Merge branch 'rnnimpl' of github.com:Superjom/Paddle into rnnimpl
Superjomn Jul 12, 2017
a6483e8
move step scopes from inputs to outputs
Superjomn Jul 12, 2017
7b1d123
Merge branch 'develop' of https://github.com/PaddlePaddle/Paddle into…
qingqing01 Jul 12, 2017
bcd03bf
fix merge conflict, update SegmentInput function
luotao1 Jul 13, 2017
de319bb
Merge branch 'develop' of https://github.com/PaddlePaddle/Paddle into…
qingqing01 Jul 14, 2017
0a4a502
Merge branch 'develop' into rnnimpl
luotao1 Jul 14, 2017
e64b5d3
add RecurrentOpProtoAndCheckerMaker.
qingqing01 Jul 14, 2017
e700bf6
Merge branch 'rnnimpl' of https://github.com/Superjom/Paddle into fea…
qingqing01 Jul 14, 2017
f525390
clean the codes
luotao1 Jul 14, 2017
3a27b02
Abstract GetStepScopes and GetMaxSeqLen function
luotao1 Jul 14, 2017
aede869
refine LinkMemories
luotao1 Jul 14, 2017
45682d2
Refine code and add some comments.
qingqing01 Jul 15, 2017
497c7ff
Merge branch 'develop' of https://github.com/PaddlePaddle/Paddle into…
qingqing01 Jul 15, 2017
fc5acee
add backward core
Superjomn Jul 15, 2017
14dd843
update for develop branch.
qingqing01 Jul 15, 2017
3c15641
Merge branch 'rnnimpl' of https://github.com/Superjom/Paddle into fea…
qingqing01 Jul 15, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions paddle/framework/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ py_proto_compile(framework_py_proto SRCS attr_type.proto op_proto.proto op_desc.
# Generate an empty __init__.py to make framework_py_proto as a valid python module.
add_custom_target(framework_py_proto_init ALL COMMAND ${CMAKE_COMMAND} -E touch __init__.py)
add_dependencies(framework_py_proto framework_py_proto_init)
add_library(recurrent_network_op recurrent_network_op.cc)
cc_test(recurrent_network_op_test SRCS recurrent_network_op_test.cc)
103 changes: 103 additions & 0 deletions paddle/framework/recurrent_network_op.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#include "paddle/framework/recurrent_network_op.h"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the corresponding .h file of this .cc file should be the first include.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

#include "paddle/framework/tensor.h"

namespace paddle {
namespace framework {

void RecurrentOp::Run(OpRunContext* contex) const {
auto scope = contex->scope;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

contex => context or ctx?


if (!scope->HasVariable(net_name_)) {
CreateStepNet(scope);
}
Variable* net = scope->GetVariable(net_name_);
PADDLE_ENFORCE(net, "failed to get step net");

CreateScopes(scope);
SegmentInputs(scope);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

32和34行和53行的LOG可以都打在函数里面。

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

测试完后会全部删掉。

CreateMemories(scope);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

觉得没有必要在step的循环执行前在scope里创建所有memory的variable,下面前后帧的memory连接里,可以调用scopes[step_id - 1]->CreateVariable(attr.var),这个函数会判断,如果name已经在scope里,直接返回,否则会create一个。

https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/framework/scope.h#L52

Copy link
Contributor Author

@Superjomn Superjomn Jul 10, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get it.


Variable* step_scopes = scope->GetVariable(step_scopes_name_);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename step_scopes.

PADDLE_ENFORCE(step_scopes, "failed to get step scopes");
// forward
auto dims = Input(scope, 0)->GetMutable<Tensor>()->dims();
size_t seq_len = dims[1];
auto& scopes = *step_scopes->GetMutable<std::vector<Scope*>>();
for (size_t step_id = 0; step_id < seq_len; step_id++) {
Scope* step_scope = scopes[step_id];
// TODO replace memorys' copy with reference
// copy pre-memory
for (const auto& attr : memory_attrs_) {
Variable* pre_memory_var = step_scope->CreateVariable(attr.pre_var);
// copy boot_var to current memory in first step
if (step_id == 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

觉得最好把memory的初始化,提出一个单独的函数,放在26行之前。

Variable* boot_var = step_scope->GetVariable(attr.boot_var);
*pre_memory_var->GetMutable<Tensor>() = *boot_var->GetMutable<Tensor>();
// copy varible of memory in previous scope to current pre-memory
} else {
Variable* pre_state_var = scopes[step_id - 1]->GetVariable(attr.var);
*pre_memory_var->GetMutable<Tensor>() =
*pre_state_var->GetMutable<Tensor>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

觉得memory的连接最好也提出一个单独的函数,放在LinkMemories里,这样for循环里逻辑会清晰一些。

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

我把if else合并了一下,会觉得清晰一点么

}
}

net->GetMutable<PlainNet>()->Run(step_scope);
}

// prepare outputs
ConcateOutputs(scope);
}

void RecurrentOp::CreateScopes(Scope* scope) const {
auto dims = Input(scope, 0)->GetMutable<Tensor>()->dims();
size_t seq_len = dims[1];
Variable* scopes_var = scope->GetVariable(step_scopes_name_);
// auto step_scopes =
// scopes_var->GetMutable<std::vector<std::shared_ptr<Scope>>>();
auto step_scopes = scopes_var->GetMutable<std::vector<Scope*>>();
// TODO Only two scopes are needed for inference, this case will be supported
// later.
if (seq_len > step_scopes->size()) {
for (size_t i = step_scopes->size(); i < seq_len; ++i) {
// step_scopes->push_back(std::make_shared<Scope>(
// std::shared_ptr<Scope>(scope)));
step_scopes->push_back(new Scope(std::shared_ptr<Scope>(scope)));
Copy link
Contributor Author

@Superjomn Superjomn Jul 10, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unique_ptr is better ?

scopes' type:

vector<std::unqiue_ptr<Scope>>

@qingqing01

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The shared_ptr is used for the scope constructor. I created an issue.

}
}
}

void RecurrentOp::CreateStepNet(Scope* scope) const {
Variable* var = scope->CreateVariable(net_name_);
auto step_net = GetAttr<std::string>("step_net");
// get the step net proto from the string.
// PADDLE_ENFORCE(
// google::protobuf::TextFormat::ParseFromString(step_net,
// &step_net_desc_));
// this is a fake net, it will be rewrite after the network has been merged.
var->Reset<PlainNet>(new PlainNet(step_net));
}

void RecurrentOp::CreateMemories(Scope* scope) const {
Variable* scopes_var = scope->CreateVariable(step_scopes_name_);
auto scopes = scopes_var->GetMutable<std::vector<Scope*>>();
PADDLE_ENFORCE(!scopes->empty(), "step scopes should be created before.");

PADDLE_ENFORCE(!memory_attrs_.empty(),
"memory attributes should be provided.");
for (size_t i = 0; i < scopes->size(); i++) {
for (const auto& attr : memory_attrs_) {
// check boot var exists
PADDLE_ENFORCE(scope->HasVariable(attr.boot_var),
"boot var %s not in context scope", attr.boot_var);
// create the memory in this scope
scope->CreateVariable(attr.var);
// create pre-memory in this scope
scope->CreateVariable(attr.pre_var);
// TODO reference pre-memory to the memory in previous scope if Variance
// supports reference
}
}
}

} // namespace framework
} // namespace paddle
160 changes: 160 additions & 0 deletions paddle/framework/recurrent_network_op.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve.

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. */

#pragma once

#include <google/protobuf/text_format.h>
#include "paddle/framework/attr_checker.h"
#include "paddle/framework/enforce.h"
#include "paddle/framework/scope.h"
#include "paddle/framework/variable.h"

namespace paddle {
namespace framework {

// --------------------------------------------------------------------
// fake interfaces that has not be implemented by other modules.
// TODO keep updating according to other modules' designs.
struct OpRunContext {
Scope* scope;
};

// TODO replace this with Net's proto.
struct NetDesc {
std::string name_;
};

class PlainNet {
public:
PlainNet() {}
PlainNet(const NetDesc& desc) {}
PlainNet(const std::string desc) {}
void Run(Scope* scope) {}
};

class OperatorBase {
public:
virtual ~OperatorBase() {}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looks to me that the constructor needs a parameter paddle::framework::proto::OperatorDesc so could it possble to call InferShape, which saves sizes of inputs/outputs into the desc. Only if so, we could have all necessary information for calling OperatorBase::Run:

class OperatorBase {
 public:
  OperatorBase(const proto::OperatorDesc& desc) : desc_(desc) {}
  virtual void Run(OpRunContext* context) const = 0;

 protected:
  virtual void InferShape(const Scope* scope) const = 0; // needs to read from and write to desc_

  proto::OperatorDesc desc_;
};

So the information in proto::OperatorDesc propagates along the path:

Operator's constructor 
  ① ↓
OperatorBase::desc_  → Operator's Run
  ②↓ ↑③            ④
Operator's InferShape

@Superjom @reyoung @jacquesqiao

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the new design of Operator, OpDesc will store in Op, and InferShape can get the information from scope, but it seems that it need not store the shape into the desc

Copy link
Collaborator

@wangkuiyi wangkuiyi Jul 7, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jacquesqiao You are right.

The first clue about input/output sizes is in training data instances, and we get the instance when we do training, i.e,. call operator's Run.

Should we just remove InferShape and let each operator defines its own shape inference methods, i.e., one method for an output, so to shorten code in its Run method like this:

template <typename Context> class MyOperator;

template <>
class MyOperator<GPUContext> : public OperatorBase {
 public:
  MyOperator(const proto::OperatorDesc& desc) : OperatorBase(desc) {}
  
  virtual void Run(OpRunContext* ctx) const {
    cudnnGemm(
      ctx->cudnnHandle,
      Output(0, ctx)->GetMutable<Tensor>(Output0Size(ctx))->mutable_data(),
      Input(0, ctx)->Get<Tensor>()->data(),
      Input(1, ctx)->Get<Tensor>()->data(),
    );
  }

 private:
  DDim Output0Size(OpRunContext* ctx) const { ...}
  DDim Output1Size(OpRunContext* ctx) const { ...}
};

virtual void Run(OpRunContext* context) const = 0;
virtual void InferShape(const Scope* scope) const = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does InferShape do?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the purpose of InferShape is to inference the size of inputs/outputs from some of them that we already know the size.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

InferShape will set the output variable dim according to the input variable dim.

Copy link
Contributor Author

@Superjomn Superjomn Jul 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RNNOp.InferShape will just call its step net's InferShape, and will

  • check input variable/tensors' shape, raise an error if wrong
  • update all outputs' variable/tensors' shape according to this mini-batch of input

It is offered as a public method because we want to keep checking dynamically during user adding operators.

inline Variable* Input(Scope* scope, int index) const {
return scope->GetVariable(inputs_[index]);
};

template <typename T>
inline const T GetAttr(const std::string& name) const {
return boost::get<T>(attrs_.at(name));
}

protected:
std::vector<std::string> inputs_;
std::vector<std::string> outputs_;
Copy link
Contributor Author

@Superjomn Superjomn Jul 7, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add attributes

AttributeMap attrs_;
};
// fake interfaces end
// --------------------------------------------------------------------

class RecurrentOp : public OperatorBase {
public:
RecurrentOp(NetDesc& net_desc)
: name_(net_desc.name_),
net_name_(net_desc.name_ + "__net__"),
step_scopes_name_(net_desc.name_ + "__step_scopes_") {}

virtual void InferShape(const Scope* scope) const override;

/*
* Forward run the RNN.
*
* NOTE the context's scope is not given until `Run` called, so step scopes'
* father should be set/updated in this method.
*/
virtual void Run(OpRunContext* contex) const override;

protected:
/*
* Prepare inputs for each stepnet.
*/
void SegmentInputs(Scope* scope) const;

/*
* Process outputs of stepnets and merge to variables.
*/
void ConcateOutputs(Scope* scope) const;

/*
* Create a `Net` which is shared across all steps.
*/
void CreateStepNet(Scope* scope) const;

/*
* Create a scope for each step, the context's scope is shared across all
* the step scopes as the father scope. The step scopes will be stored in
* the father scope as a variable whose name is specified by
* `step_scopes_name_`.
*
* NOTE the scopes are reused by both the `Forward` and `Backward`, so just
* create once and expand its size if more steps need.
*/
void CreateScopes(Scope* scope) const;

/*
* Create memories in each step scope.
*/
void CreateMemories(Scope* scope) const;

/*
* Link memory in previous step scope to current scope.
*/
// void LinkMemories(Scope* scope) const;

private:
/*
* these are defined in BaseOperator
*
* std::vector<std::string> inputs_;
* std::vector<std::string> outputs_;
*/

// Memory of a RNN (same as the role of `Momory` in PaddlePaddle)
struct MemoryAttr {
// name of current state variable
std::string var;
// name of previous step's state variable
std::string pre_var;
// name of the variables to init this memory (same role of `boot_layer` in
// PaddlePaddle), which is store in father's scope.
std::string boot_var;
};

std::vector<MemoryAttr> memory_attrs_;

// this op's name, used as a unique key in father scope.
// TODO repace it with OpBase's interface if supported.
std::string name_;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this name.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

// name of rnn op's step net, the step net will be shared by both `Forward`
// and `Backward`, so we store it as a variable in father's scope, with a
// unique key specified by `net_name_`.
const std::string net_name_;
// name of steps' scopes which is stored in father scope with a unique key
// specified by `step_scopes_name_`.
const std::string step_scopes_name_;

const NetDesc step_net_desc_;
};

class RecurrentGradientOp;

} // namespace framework
} // namespace paddle
20 changes: 20 additions & 0 deletions paddle/framework/recurrent_network_op_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve.
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 "paddle/framework/recurrent_network_op.h"
#include "gtest/gtest.h"

namespace paddle {
namespace framework {} // namespace framework

} // namespace paddle
4 changes: 4 additions & 0 deletions paddle/framework/tensor.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class Tensor {
|| holder_->Size() < product(dims) * sizeof(T)) {
holder_.reset(new PlaceholderImpl<T>(place, product(dims) * sizeof(T)));
}
dims_ = dims;
return static_cast<T*>(holder_->Ptr());
}

Expand All @@ -51,6 +52,8 @@ class Tensor {
return mutable_data<T>(dims, paddle::platform::get_place());
}

const DDim& dims() const { return dims_; }

private:
// Placeholder hides type T, so it doesn't appear as a template
// parameter of Variable.
Expand Down Expand Up @@ -91,6 +94,7 @@ class Tensor {
size_t size_; // size of the memory block.
};

DDim dims_;
std::shared_ptr<Placeholder> holder_; // holds the memory block if allocated.
};

Expand Down
5 changes: 5 additions & 0 deletions paddle/framework/variable.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ class Variable {
return *static_cast<const T*>(holder_->Ptr());
}

template <typename T>
void Reset(T* p) {
holder_.reset(new PlaceholderImpl<T>(p));
}

template <typename T>
T* GetMutable() {
if (!IsType<T>()) {
Expand Down