Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
f35c8c4
remove simple_op_design.md
JiayiFeng Jul 2, 2017
7dc53ea
renew simple_op_design.md
JiayiFeng Jul 3, 2017
015ccd4
Merge branch 'develop' of https://github.com/PaddlePaddle/Paddle into…
JiayiFeng Jul 3, 2017
816b4c8
"add backward Op"
dzhwinter Jul 18, 2017
9890b23
fix confilct
dzhwinter Jul 18, 2017
8b80cf8
"add net op testing"
dzhwinter Jul 18, 2017
7f1533f
test collaborating
JiayiFeng Jul 18, 2017
855cae6
move unused file
dzhwinter Jul 18, 2017
e786746
Merge branch 'backward' of https://github.com/dzhwinter/Paddle into b…
JiayiFeng Jul 18, 2017
bf4da3d
Refactor Rigistry::CreateGradOp()
JiayiFeng Jul 19, 2017
cb95587
"ignore some gradient of specific op"
dzhwinter Jul 19, 2017
94a6b1f
rename a macro
JiayiFeng Jul 19, 2017
8bc4892
"fix comment "
dzhwinter Jul 19, 2017
3dc70ff
Merge branch 'backward' of https://github.com/dzhwinter/Paddle into b…
JiayiFeng Jul 19, 2017
73f4779
Merge remote-tracking branch 'origin/develop' into backward2
dzhwinter Jul 19, 2017
4876f35
"make plainNet shared"
dzhwinter Jul 19, 2017
e192d0f
Refactor the implementation of gradient Op creating
JiayiFeng Jul 19, 2017
14424f3
"use built-in operator"
dzhwinter Jul 20, 2017
81a352a
"test fc without gradient"
dzhwinter Jul 20, 2017
6f05392
Merge remote-tracking branch 'origin/develop' into backward2
dzhwinter Jul 20, 2017
8a5ee46
Fix some compile errors
JiayiFeng Jul 20, 2017
b635af7
Fix some compile error
JiayiFeng Jul 20, 2017
45452ac
Merge branch 'backward' of https://github.com/dzhwinter/Paddle into b…
JiayiFeng Jul 20, 2017
088e220
"remove unused fake fc op"
dzhwinter Jul 20, 2017
99a5904
Merge branch 'backward_dev' into backward
JiayiFeng Jul 20, 2017
f41fcd4
Merge branch 'backward' of https://github.com/dzhwinter/Paddle into b…
JiayiFeng Jul 20, 2017
9418717
Fix compile errors
JiayiFeng Jul 20, 2017
4736b23
Add a simple test for grad_op_creator
JiayiFeng Jul 21, 2017
f85ccdd
Renew CMakeList dependence
JiayiFeng Jul 24, 2017
0ab8f52
Merge branch 'backward' of https://github.com/Canpio/Paddle into back…
JiayiFeng Jul 24, 2017
380227b
Renew CMakeList dependence
JiayiFeng Jul 24, 2017
5f3bc2a
Merge branch 'develop' of https://github.com/PaddlePaddle/Paddle into…
JiayiFeng Jul 24, 2017
f4e2555
Fix compile error
JiayiFeng Jul 24, 2017
81df39f
fix compile errer
JiayiFeng Jul 24, 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
1 change: 1 addition & 0 deletions doc/design/simple_op_design.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ message AttrProto {
message VarProto {
required string name = 1;
required string comment = 2;
required bool is_tensor = 3;
};

message OpProto {
Expand Down
9 changes: 6 additions & 3 deletions paddle/framework/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,17 @@ cc_test(op_desc_test SRCS op_desc_test.cc DEPS op_desc protobuf)
cc_library(operator SRCS operator.cc DEPS op_desc device_context tensor)
cc_test(operator_test SRCS operator_test.cc DEPS operator op_registry)

cc_library(op_registry SRCS op_registry.cc DEPS op_proto op_desc)
cc_test(op_registry_test SRCS op_registry_test.cc DEPS op_registry operator)
cc_library(grad_op_creator SRCS grad_op_creator.cc DEPS op_proto operator)
cc_library(op_registry SRCS op_registry.cc DEPS op_desc grad_op_creator)
cc_test(op_registry_test SRCS op_registry_test.cc DEPS op_registry)
cc_test(grad_op_creator_test SRCS grad_op_creator_test.cc DEPS grad_op_creator op_registry add_op)

py_proto_compile(framework_py_proto SRCS attr_type.proto op_proto.proto op_desc.proto)
# 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)

proto_library(net_proto SRCS net_proto.proto DEPS op_proto)
# cc_library(net SRCS net.cc DEPS operator net_proto op_registry fc_op)
cc_library(net SRCS net.cc DEPS operator net_proto op_registry)
cc_test(net_op_test SRCS net_op_test.cc DEPS net)
cc_test(net_op_test SRCS net_op_test.cc DEPS net add_op mul_op sigmoid_op softmax_op fc_op)
115 changes: 115 additions & 0 deletions paddle/framework/grad_op_creator.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/* 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/grad_op_creator.h"
#include "paddle/framework/op_registry.h"

namespace paddle {
namespace framework {

OperatorBase* GradOpCreator::Create() {
BuildOpInOutArgList();
OperatorBase* grad_op = OpRegistry::grad_creators().at(op_->type_)();
CompleteGradOp(grad_op);
return grad_op;
}

OpInOutArg* GradOpCreator::BuildArg(const VarProto& var,
const VarIndexMap& var_map,
const std::vector<int>& format,
InOutType type) {
int idx = var_map.at(var.name());
int begin_idx = format.empty() ? idx : format.at(idx);
int end_idx = format.empty() ? idx + 1 : format.at(idx + 1);
return new OpInOutArg(var.name(), type, !var.ignore_gradient(), begin_idx,
end_idx);
}

void GradOpCreator::BuildOpInOutArgList() {
const OpProto& op_proto = OpRegistry::protos().at(op_->type_);
const auto& var_map = *(OpRegistry::VarIndexMaps().at(op_->type_));
const std::vector<int>& in_format =
op_->attrs_.count("input_format")
? op_->GetAttr<std::vector<int>>("input_format")
: std::vector<int>();
const std::vector<int>& out_format =
op_->attrs_.count("output_format")
? op_->GetAttr<std::vector<int>>("output_format")
: std::vector<int>();
for (const auto& var : op_proto.inputs()) {
arg_list_.emplace_back(
std::shared_ptr<OpInOutArg>(BuildArg(var, var_map, in_format, IN)));
}
for (const auto& var : op_proto.outputs()) {
arg_list_.emplace_back(
std::shared_ptr<OpInOutArg>(BuildArg(var, var_map, out_format, OUT)));
}
}

void GradOpCreator::AddArgIntoGradOp(const OpInOutArg* arg,
std::vector<std::string>& in_out,
std::vector<int>& format,
VarIndexMap* varmap, int& idx,
bool is_grad) const {
std::string var_name = arg->proto_name_;
if (is_grad) {
var_name += OperatorBase::GRAD_VAR_SUFFIX();
}
(*varmap)[var_name] = idx++;
size_t pre_sz = in_out.size();
auto base_it =
arg->type_ == IN ? op_->inputs_.begin() : op_->outputs_.begin();
std::copy(base_it + arg->begin_idx_, base_it + arg->end_idx_,
std::back_inserter(in_out));
if (is_grad) {
for (size_t i = pre_sz; i < in_out.size(); ++i) {
in_out[i] += OperatorBase::GRAD_VAR_SUFFIX();
}
}
format.push_back(in_out.size());
}

void GradOpCreator::CompleteGradOp(OperatorBase* grad_op) const {
grad_op->type_ = op_->type_ + "@GRAD"; // not necessary
grad_op->attrs_ = op_->attrs_;
grad_op->attrs_.erase("input_format");
grad_op->attrs_.erase("output_format");
VarIndexMap* grad_varmap = new VarIndexMap();
int in_idx = 0;
int out_idx = 0;
std::vector<int> in_format({0});
std::vector<int> out_format({0});
for (const auto& arg : arg_list_) {
// op_'s inputs_ and outputs_
if (arg->needed_in_grad_) {
AddArgIntoGradOp(arg.get(), grad_op->inputs_, in_format, grad_varmap,
in_idx, false);
}
if (arg->type_ == IN) {
// gradients of op_'s inputs_
AddArgIntoGradOp(arg.get(), grad_op->outputs_, out_format, grad_varmap,
out_idx, true);
} else {
// gradients of op_'s outputs_
AddArgIntoGradOp(arg.get(), grad_op->inputs_, in_format, grad_varmap,
in_idx, true);
}
}
grad_op->attrs_["input_format"] = in_format;
grad_op->attrs_["output_format"] = out_format;
grad_op->in_out_idxs_.reset(grad_varmap);
}

} // namespace framework
} // namespace paddle
48 changes: 48 additions & 0 deletions paddle/framework/grad_op_creator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#pragma once

#include "paddle/framework/op_proto.pb.h"
#include "paddle/framework/operator.h"

namespace paddle {
namespace framework {
class OpRegistry;

enum InOutType { IN, OUT };

struct OpInOutArg {
OpInOutArg(const std::string& proto_name, const InOutType& type,
bool needed_in_grad, size_t begin_idx, size_t end_idx)
: proto_name_(proto_name),
type_(type),
needed_in_grad_(needed_in_grad),
begin_idx_(begin_idx),
end_idx_(end_idx) {}

std::string proto_name_;
InOutType type_;
bool needed_in_grad_;
size_t begin_idx_;
size_t end_idx_;
};

class GradOpCreator {
using VarIndexMap = std::unordered_map<std::string, int>;

public:
GradOpCreator(const OperatorBase* op) : op_(op) {}
OperatorBase* Create();

private:
OpInOutArg* BuildArg(const VarProto& var, const VarIndexMap& var_map,
const std::vector<int>& format, InOutType type);
void BuildOpInOutArgList();
void AddArgIntoGradOp(const OpInOutArg* arg, std::vector<std::string>& in_out,
std::vector<int>& format, VarIndexMap* varmap, int& idx,
bool is_grad) const;
void CompleteGradOp(OperatorBase* grad_op) const;
const OperatorBase* op_;
std::vector<std::shared_ptr<OpInOutArg>> arg_list_;
};

} // namespace framework
} // namespace paddle
26 changes: 26 additions & 0 deletions paddle/framework/grad_op_creator_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#include "paddle/framework/grad_op_creator.h"
#include <gtest/gtest.h>
#include "paddle/framework/op_registry.h"
#include "paddle/framework/operator.h"

USE_OP(add_two);

namespace paddle {
namespace framework {

TEST(GradOpCreator, AddTwo) {
std::shared_ptr<OperatorBase> add_op(
OpRegistry::CreateOp("add_two", {"x", "y"}, {"out"}, {}));
std::shared_ptr<OperatorBase> grad_add_op = OpRegistry::CreateGradOp(add_op);
EXPECT_EQ(static_cast<int>(grad_add_op->inputs_.size()), 4);
EXPECT_EQ(static_cast<int>(grad_add_op->outputs_.size()), 2);
EXPECT_EQ(grad_add_op->Input("X"), "x");
EXPECT_EQ(grad_add_op->Input("Y"), "y");
EXPECT_EQ(grad_add_op->Input("Out"), "out");
EXPECT_EQ(grad_add_op->Input("Out@GRAD"), "out@GRAD");
EXPECT_EQ(grad_add_op->Output("X@GRAD"), "x@GRAD");
EXPECT_EQ(grad_add_op->Output("Y@GRAD"), "y@GRAD");
}

} // namespace framework
} // namespace paddle
12 changes: 11 additions & 1 deletion paddle/framework/net.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,24 @@
*/

#include "paddle/framework/net.h"
#include "paddle/framework/op_registry.h"

namespace paddle {
namespace framework {

std::shared_ptr<PlainNet> AddBackwardOp(std::shared_ptr<PlainNet> ForwardOps) {
auto grad_ops = std::make_shared<PlainNet>();
for (auto& op : ForwardOps->ops_) {
auto op_grad = OpRegistry::CreateGradOp(op);
grad_ops->AddOp(op_grad);
}
grad_ops->CompleteAddOp();
return grad_ops;
}

void PlainNet::CompleteAddOp(bool calc) {
add_op_done_ = true;
if (!calc) return;

std::unordered_set<std::string> input_set;
std::unordered_set<std::string> output_set;
std::unordered_set<std::string> temp_output;
Expand Down
2 changes: 2 additions & 0 deletions paddle/framework/net.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,5 +100,7 @@ class PlainNet : public Net {
}
};

std::shared_ptr<PlainNet> AddBackwardOp(std::shared_ptr<PlainNet> ForwardOps);
Copy link
Collaborator

@reyoung reyoung Jul 19, 2017

Choose a reason for hiding this comment

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

I think this method might be useless, we can directly invoke OpRegistry::CreateGradOp(...) even that Op is a net.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's true. I remembered that AddBackwardOp is an interface for PlainNet, which generate the backward ops, then it can be used in Python. Remove it straightly is fine to me.


} // namespace framework
} // namespace paddle
47 changes: 39 additions & 8 deletions paddle/framework/net_op_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,24 @@
#include <paddle/framework/op_registry.h>
#include <paddle/framework/operator.h>

namespace pd = paddle::framework;
USE_OP(add_two);
USE_OP(mul);
USE_OP(sigmoid);
USE_OP(softmax);

namespace paddle {
namespace framework {

static int infer_shape_cnt = 0;
static int run_cnt = 0;

class TestOp : public pd::OperatorBase {
class TestOp : public OperatorBase {
public:
void InferShape(const std::shared_ptr<pd::Scope>& scope) const override {
void InferShape(
const std::shared_ptr<framework::Scope>& scope) const override {
++infer_shape_cnt;
}
void Run(const std::shared_ptr<pd::Scope>& scope,
void Run(const std::shared_ptr<framework::Scope>& scope,
const paddle::platform::DeviceContext& dev_ctx) const override {
++run_cnt;
}
Expand All @@ -33,7 +40,7 @@ void AssertSameVectorWithoutOrder(const std::vector<T>& expected,
}

TEST(OpKernel, all) {
auto net = std::make_shared<paddle::framework::PlainNet>();
auto net = std::make_shared<PlainNet>();
ASSERT_NE(net, nullptr);

auto op1 = std::make_shared<TestOp>();
Expand All @@ -55,13 +62,37 @@ TEST(OpKernel, all) {
ASSERT_EQ(1UL, tmp_idx.size());
ASSERT_EQ("y", net->outputs_[tmp_idx[0]]);

auto scope = std::make_shared<pd::Scope>();
paddle::platform::CPUDeviceContext dev_ctx;
auto scope = std::make_shared<Scope>();
platform::CPUDeviceContext dev_ctx;

net->InferShape(scope);
net->Run(scope, dev_ctx);
ASSERT_EQ(2, infer_shape_cnt);
ASSERT_EQ(2, run_cnt);

ASSERT_THROW(net->AddOp(op2), std::runtime_error);
}
TEST(AddBackwardOp, TestGradOp) {
auto net = std::make_shared<PlainNet>();
ASSERT_NE(net, nullptr);
net->AddOp(framework::OpRegistry::CreateOp("mul", {"X", "Y"}, {"Out"}, {}));
net->AddOp(
framework::OpRegistry::CreateOp("add_two", {"X", "Y"}, {"Out"}, {}));
net->AddOp(framework::OpRegistry::CreateOp("add_two", {"X", "Y"}, {""}, {}));
auto grad_ops = AddBackwardOp(net);
for (auto& op : grad_ops->ops_) {
op->DebugString();
}
}

// TODO(zhihong): add fc grad without registering.
// TEST(AddBackwardOp, TestNoGradOp) {
// auto net = std::make_shared<PlainNet>();
// ASSERT_NE(net, nullptr);
// net->AddOp(framework::OpRegistry::CreateOp("fc", {"X", "W", "b"}, {"Y"},
// {})); auto grad_ops = AddBackwardOp(net); for (auto& op : grad_ops->ops_) {
// op->DebugString();
// }
// }

} // namespace framework
} // namespace paddle
24 changes: 0 additions & 24 deletions paddle/framework/net_test.cc

This file was deleted.

6 changes: 6 additions & 0 deletions paddle/framework/op_proto.proto
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ message VarProto {
// "temporary_index": [1]
// }
optional bool temporary = 4 [default=false];

// The gradient of operator can be ignored immediately
// e.g. operator AddOp, y = x1 + x2, the gradient of dy/dx1, dy/dx2
// can be ignored for the future optimized on graph.
optional bool ignore_gradient = 6;
}

// Op protocol message for 3rd-party language binding.
Expand All @@ -105,4 +110,5 @@ message OpProto {

// The type of that Op.
required string type = 5;

}
Loading