parent
bc15117403
commit
db157eda45
@ -1,200 +0,0 @@
|
||||
/* 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/op_registry.h"
|
||||
#include "paddle/operators/net_op.h"
|
||||
|
||||
namespace paddle {
|
||||
namespace operators {
|
||||
|
||||
class FCOp : public NetOp {
|
||||
public:
|
||||
FCOp(const std::string &type, const framework::VariableNameMap &inputs,
|
||||
const framework::VariableNameMap &outputs,
|
||||
const framework::AttributeMap &attrs)
|
||||
: NetOp(type, inputs, outputs, attrs) {
|
||||
PADDLE_ENFORCE(!Inputs("X").empty(),
|
||||
"Inputs(X) of FCOp should not be null.");
|
||||
PADDLE_ENFORCE(!Inputs("W").empty(),
|
||||
"Inputs(W) of FCOp should not be null.");
|
||||
PADDLE_ENFORCE(!Outputs("MulOut").empty(),
|
||||
"Outputs(MulOut) of FCOp should not be null.");
|
||||
PADDLE_ENFORCE_NE(Output("Out"), framework::kEmptyVarName,
|
||||
"Output(Out) of FCOp should not be null.");
|
||||
|
||||
auto x = Inputs("X");
|
||||
auto w = Inputs("W");
|
||||
auto mul_out = Outputs("MulOut");
|
||||
PADDLE_ENFORCE_EQ(
|
||||
x.size(), w.size(),
|
||||
"The size of inputs X(%d) should be the same as that of weights W(%d).",
|
||||
x.size(), w.size());
|
||||
PADDLE_ENFORCE_EQ(mul_out.size(), x.size(),
|
||||
"The size of intermediate mul_out(%d) should be the same "
|
||||
"as that of inputs X(%d).",
|
||||
mul_out.size(), x.size());
|
||||
|
||||
size_t n = x.size();
|
||||
PADDLE_ENFORCE_GE(n, static_cast<size_t>(1),
|
||||
"The size of inputs X(%d) should be no less than 1.", n);
|
||||
|
||||
auto x_num_col_dims = Attr<std::vector<int>>("xNumColDims");
|
||||
|
||||
// Set all values or set no values (use the default value)
|
||||
if (!x_num_col_dims.empty()) {
|
||||
PADDLE_ENFORCE_EQ(x_num_col_dims.size(), n,
|
||||
"The size of attribute xNumColDims(%d) should be the "
|
||||
"same as that of inputs X(%d).",
|
||||
x_num_col_dims.size(), n);
|
||||
} else {
|
||||
x_num_col_dims.resize(n);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
x_num_col_dims[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// mul_out[i] = X[i] * W[i]
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
framework::AttributeMap mul_attr;
|
||||
mul_attr["x_num_col_dims"] = static_cast<int>(x_num_col_dims[i]);
|
||||
mul_attr["y_num_col_dims"] = static_cast<int>(1);
|
||||
AppendOp(
|
||||
framework::OpRegistry::CreateOp("mul", {{"X", {x[i]}}, {"Y", {w[i]}}},
|
||||
{{"Out", {mul_out[i]}}}, mul_attr));
|
||||
}
|
||||
|
||||
// sum_out = X[0] * W[0] + ... + X[n-1] * W[n-1]
|
||||
auto sum_out = mul_out[0];
|
||||
if (n > 1) {
|
||||
PADDLE_ENFORCE_NE(Output("SumOut"), framework::kEmptyVarName,
|
||||
"Output(SumOut) of FCOp should not be null when the "
|
||||
"size of Inputs(X) > 1.");
|
||||
|
||||
sum_out = Output("SumOut");
|
||||
AppendOp(framework::OpRegistry::CreateOp("sum", {{"X", {mul_out}}},
|
||||
{{"Out", {sum_out}}}, {}));
|
||||
} else {
|
||||
if (Output("SumOut") != framework::kEmptyVarName) {
|
||||
this->Rename(Output("SumOut"), framework::kEmptyVarName);
|
||||
}
|
||||
}
|
||||
|
||||
// add_out = sum_out + b
|
||||
auto b = Input("B");
|
||||
auto add_out = sum_out;
|
||||
if (b != framework::kEmptyVarName) {
|
||||
PADDLE_ENFORCE_NE(
|
||||
Output("AddOut"), framework::kEmptyVarName,
|
||||
"Output(AddOut) of FCOp should not be null when Input(B) is set.");
|
||||
|
||||
add_out = Output("AddOut");
|
||||
AppendOp(framework::OpRegistry::CreateOp(
|
||||
"elementwise_add", {{"X", {sum_out}}, {"Y", {Input("B")}}},
|
||||
{{"Out", {add_out}}}, {}));
|
||||
} else {
|
||||
if (Output("AddOut") != framework::kEmptyVarName) {
|
||||
this->Rename(Output("AddOut"), framework::kEmptyVarName);
|
||||
}
|
||||
}
|
||||
|
||||
auto activation = Attr<std::string>("activation");
|
||||
AppendOp(framework::OpRegistry::CreateOp(activation, {{"X", {add_out}}},
|
||||
{{"Y", {Output("Out")}}}, {}));
|
||||
CompleteAddOp(false);
|
||||
}
|
||||
};
|
||||
|
||||
class FCOpMaker : public framework::OpProtoAndCheckerMaker {
|
||||
public:
|
||||
FCOpMaker(framework::OpProto *proto, framework::OpAttrChecker *op_checker)
|
||||
: OpProtoAndCheckerMaker(proto, op_checker) {
|
||||
AddInput("X",
|
||||
"(A vector of Tensors) each input Tensor can be of arbitrary "
|
||||
"dimension, and will be reshaped to a 2-D matrix of size "
|
||||
"(minibatch, number_of_input_features) according to attribute "
|
||||
"xNumColDims.")
|
||||
.AsDuplicable();
|
||||
AddInput("W",
|
||||
"(A vector of Tensors) the weights of FC operator, a "
|
||||
"vector of 2-D matrix of size "
|
||||
"(number_of_input_features, number_of_neurons).")
|
||||
.AsDuplicable();
|
||||
AddInput("B",
|
||||
"(Tensor) the bias of FC operator, a 1-D vector of size "
|
||||
"number_of_neurons.");
|
||||
|
||||
AddOutput("Out",
|
||||
"(Tensor) the activated output matrix of FC operator, a 2-D "
|
||||
"matrix of size (minibatch, number_of_neurons).");
|
||||
AddOutput("MulOut",
|
||||
"(A vector of Tensors) the intermediate outputs of FC operator, "
|
||||
"each Tensor saving the product of X_i * W_i.")
|
||||
.AsIntermediate()
|
||||
.AsDuplicable();
|
||||
AddOutput(
|
||||
"SumOut",
|
||||
"(Tensor) the intermediate output of FC operator, "
|
||||
"saving the sum of the products of X and W, that is sum{X_i * W_i}.")
|
||||
.AsIntermediate();
|
||||
AddOutput("AddOut",
|
||||
"(Tensor) the non-actived output of FC operator, "
|
||||
"saving sum{X_i * W_i} + B.")
|
||||
.AsIntermediate();
|
||||
AddAttr<std::string>(
|
||||
"activation",
|
||||
"(string, default identity) the activation type of FC operator.")
|
||||
.SetDefault("identity")
|
||||
.InEnum({"identity", "sigmoid", "softmax"});
|
||||
AddAttr<std::vector<int>>(
|
||||
"xNumColDims",
|
||||
"(std::vector<int>) The inputs Tensors of FC operator can be of "
|
||||
"more than 2 dimensions. In that case, each input Tensor `X_i` will be "
|
||||
"reshaped to a 2-D matrix. The matrix's first dimension "
|
||||
"(the length of column) will be the product of `X_i`'s last "
|
||||
"`xNumColDims_i` dimensions, that is "
|
||||
"`X_i.dims[0] x ... x X_i.dims[xNumColDims_i - 1]`. "
|
||||
"The matrix's second dimension (the length of row) will be the product "
|
||||
"of `X_i`'s first `rank - xNumColDims_i` dimensions, that is "
|
||||
"`X_i.dims[xNumColDims_i] x ... x X_i.dims[rank - 1]`)")
|
||||
.SetDefault(std::vector<int>{});
|
||||
|
||||
AddComment(R"DOC(
|
||||
Fully Connected Operator, known as Fully Connected Layer or Inner Product Layer
|
||||
in Convolutional Neural Networks. Neurons in a fully connected layer have
|
||||
full connections to all activations in the previous layer.
|
||||
It computes an inner product of a set of
|
||||
learned weights with a matrix multiplication followed by a bias offset
|
||||
(optionally).
|
||||
|
||||
Equation:
|
||||
Out = Act(sum_n{X_i * W_i} + B)
|
||||
|
||||
where X_i is Tensor that will be reshaped to a 2-D matrix of size (M x K),
|
||||
usually M is the minibatch size and K is the number of input features.
|
||||
W_i is a 2-D matrix of size (K x N), where N means the number of neurons
|
||||
in the fully connected layer. B is a 1-D vector of size N.
|
||||
Thus, the output Out is a 2-D matrix of size (M x N).
|
||||
Activation type can be set to `identity` (default), `sigmoid` or `softmax`.
|
||||
|
||||
All the inputs can carry the LoD (Level of Details) information,
|
||||
or not. But the output only shares the LoD with first input (`X[0]`).
|
||||
)DOC");
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace operators
|
||||
} // namespace paddle
|
||||
|
||||
namespace ops = paddle::operators;
|
||||
REGISTER_OP_WITHOUT_GRADIENT(fc, ops::FCOp, ops::FCOpMaker);
|
@ -1,63 +0,0 @@
|
||||
/* 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/operators/net_op.h"
|
||||
#include "paddle/operators/scale_op.h"
|
||||
|
||||
namespace paddle {
|
||||
namespace operators {
|
||||
|
||||
// The identity operator is an alias of the scale operator. This is also an
|
||||
// example for creating an alias for an existing operator.
|
||||
template <typename AttrType>
|
||||
class IdentityOpMaker : public framework::OpProtoAndCheckerMaker {
|
||||
public:
|
||||
IdentityOpMaker(framework::OpProto *proto,
|
||||
framework::OpAttrChecker *op_checker)
|
||||
: OpProtoAndCheckerMaker(proto, op_checker) {
|
||||
AddInput("X", "The input tensor of identity operator.");
|
||||
AddOutput("Y", "The output tensor of identity operator.");
|
||||
AddComment(R"DOC(
|
||||
The identity operator is an alias of the scale operator
|
||||
with the attribute scale fixed to 1.0.
|
||||
)DOC");
|
||||
}
|
||||
};
|
||||
|
||||
template <typename AttrType>
|
||||
class IdentityOp : public NetOp {
|
||||
public:
|
||||
IdentityOp(const std::string &type, const framework::VariableNameMap &inputs,
|
||||
const framework::VariableNameMap &outputs,
|
||||
const framework::AttributeMap &attrs)
|
||||
: NetOp(type, inputs, outputs, attrs) {
|
||||
PADDLE_ENFORCE_NE(Input("X"), framework::kEmptyVarName,
|
||||
"Input(X) of IdentityOp should not be null.");
|
||||
PADDLE_ENFORCE_NE(Output("Y"), framework::kEmptyVarName,
|
||||
"Output(Y) of IdentityOp should not be null.");
|
||||
|
||||
AppendOp(framework::OpRegistry::CreateOp(
|
||||
"scale", {{"X", {Input("X")}}}, {{"Out", {Output("Y")}}},
|
||||
{{"scale", static_cast<AttrType>(1)}}));
|
||||
CompleteAddOp(false);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace operators
|
||||
} // namespace paddle
|
||||
|
||||
namespace ops = paddle::operators;
|
||||
|
||||
REGISTER_OP_WITHOUT_GRADIENT(identity, ops::IdentityOp<float>,
|
||||
ops::IdentityOpMaker<float>);
|
@ -1,113 +0,0 @@
|
||||
/* 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/op_registry.h"
|
||||
#include "paddle/operators/net_op.h"
|
||||
|
||||
namespace paddle {
|
||||
namespace operators {
|
||||
|
||||
class InterpOp : public NetOp {
|
||||
public:
|
||||
InterpOp(const std::string &type, const framework::VariableNameMap &inputs,
|
||||
const framework::VariableNameMap &outputs,
|
||||
const framework::AttributeMap &attrs)
|
||||
: NetOp(type, inputs, outputs, attrs) {
|
||||
PADDLE_ENFORCE_NE(Input("X"), framework::kEmptyVarName,
|
||||
"Input(X) of InterpOp should not be null.");
|
||||
PADDLE_ENFORCE_NE(Input("Y"), framework::kEmptyVarName,
|
||||
"Input(Y) of InterpOp should not be null.");
|
||||
PADDLE_ENFORCE_NE(Input("W"), framework::kEmptyVarName,
|
||||
"Input(W) of InterpOp should not be null.");
|
||||
PADDLE_ENFORCE_NE(Output("SubOut"), framework::kEmptyVarName,
|
||||
"Output(SubOut) of InterpOp should not be null.");
|
||||
PADDLE_ENFORCE_NE(Output("MulOut"), framework::kEmptyVarName,
|
||||
"Output(MulOut) of InterpOp should not be null.");
|
||||
PADDLE_ENFORCE_NE(Output("Out"), framework::kEmptyVarName,
|
||||
"Output(Out) of InterpOp should not be null.");
|
||||
|
||||
// SubOut = X - Y
|
||||
auto x = Input("X");
|
||||
auto y = Input("Y");
|
||||
auto sub_out = Output("SubOut");
|
||||
AppendOp(framework::OpRegistry::CreateOp(
|
||||
"elementwise_sub", {{"X", {x}}, {"Y", {y}}}, {{"Out", {sub_out}}}, {}));
|
||||
|
||||
// MulOut = SubOut * W = (X - Y) * W
|
||||
auto w = Input("W");
|
||||
auto mul_out = Output("MulOut");
|
||||
AppendOp(framework::OpRegistry::CreateOp(
|
||||
"elementwise_mul", {{"X", {sub_out}}, {"Y", {w}}}, {{"Out", {mul_out}}},
|
||||
{{"axis", 0}}));
|
||||
|
||||
// Out = MulOut + Y = (X - Y) * W + Y = X * W + Y * (1 - W)
|
||||
AppendOp(framework::OpRegistry::CreateOp("elementwise_add",
|
||||
{{"X", {mul_out}}, {"Y", {y}}},
|
||||
{{"Out", {Output("Out")}}}, {}));
|
||||
|
||||
CompleteAddOp(false);
|
||||
}
|
||||
};
|
||||
|
||||
class InterpOpMaker : public framework::OpProtoAndCheckerMaker {
|
||||
public:
|
||||
InterpOpMaker(framework::OpProto *proto, framework::OpAttrChecker *op_checker)
|
||||
: OpProtoAndCheckerMaker(proto, op_checker) {
|
||||
AddInput("X",
|
||||
"(Tensor), 2-D Matrix of shape [batch_size, data_dim]"
|
||||
"containing data samples, the first input of interp_op");
|
||||
AddInput("Y",
|
||||
"(Tensor), 2-D Matrix of shape `[batch_size, data_dim]`"
|
||||
"containing data samples, the second input of interp_op");
|
||||
AddInput("W",
|
||||
"(Tensor), 1-D Vector of shape [batch_size],"
|
||||
"the interpolated values in the half-open interval [0.0, 1.0)");
|
||||
AddOutput("SubOut",
|
||||
"(Tensor), the intermediate subtraction outputs, saving X - Y.")
|
||||
.AsIntermediate();
|
||||
AddOutput("MulOut",
|
||||
"(Tensor), the intermediate multiplication outputs,"
|
||||
"saving the elementwise multiplication of (X - Y) and W.")
|
||||
.AsIntermediate();
|
||||
AddOutput("Out",
|
||||
"(Tensor), the output of interp_op, same shape with X,"
|
||||
"returns the first-dimensional piecewise linear interpolant "
|
||||
"between X and Y");
|
||||
AddComment(R"DOC(
|
||||
Linear Interpolation with two inputs, used in NEURAL TURING MACHINE.
|
||||
|
||||
Equation:
|
||||
Out.row[i] = X.row[i] * W[i] + Y.row[i] * (1 - W[i])
|
||||
= (X.row[i] - Y.row[i]) * W[i] + Y.row[i]
|
||||
|
||||
Example:
|
||||
X = [[1,2],[3,4]],
|
||||
Y = [[2,1],[4,3]],
|
||||
W = [0.3, 0.4]
|
||||
|
||||
Then, Out = [[1.7,1.3],[3.6,3.4]]
|
||||
|
||||
where 1.7 = 1*0.3+2*(1-0.3),
|
||||
1.3 = 2*0.3+1*(1-0.3),
|
||||
3.6 = 3*0.4+4*(1-0.4),
|
||||
3.4 = 4*0.4+3*(1-0.4)
|
||||
)DOC");
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace operators
|
||||
} // namespace paddle
|
||||
|
||||
namespace ops = paddle::operators;
|
||||
REGISTER_OP_WITHOUT_GRADIENT(interp, ops::InterpOp, ops::InterpOpMaker);
|
@ -1,62 +0,0 @@
|
||||
import unittest
|
||||
import numpy as np
|
||||
from op_test import OpTest
|
||||
|
||||
|
||||
class TestFCOp1(OpTest):
|
||||
def setUp(self):
|
||||
x0 = np.random.random((16, 32)).astype("float32")
|
||||
w0 = np.random.random((32, 10)).astype("float32")
|
||||
|
||||
mul_out0 = np.dot(x0, w0)
|
||||
identity_out = mul_out0
|
||||
|
||||
self.op_type = "fc"
|
||||
self.inputs = {"X": [("X0", x0)], "W": [("W0", w0)]}
|
||||
self.outputs = {"MulOut": [("MulOut0", mul_out0)], "Out": identity_out}
|
||||
|
||||
def test_check_output(self):
|
||||
self.check_output()
|
||||
|
||||
def test_check_grad(self):
|
||||
self.check_grad(["X0", "W0"], "Out", max_relative_error=0.01)
|
||||
|
||||
|
||||
# FIXME: Disable TestFCOp2 since C++ fc will be removed
|
||||
# class TestFCOp2(OpTest):
|
||||
# def setUp(self):
|
||||
# x0 = np.random.random((16, 4, 8)).astype("float32")
|
||||
# x1 = np.random.random((4, 4, 32)).astype("float32")
|
||||
# w0 = np.random.random((32, 10)).astype("float32")
|
||||
# w1 = np.random.random((32, 10)).astype("float32")
|
||||
# b = np.random.random(10).astype("float32")
|
||||
#
|
||||
# mul_out0 = np.dot(x0.reshape(16, 4 * 8), w0)
|
||||
# mul_out1 = np.dot(x1.reshape(4 * 4, 32), w1)
|
||||
# sum_out = mul_out0 + mul_out1
|
||||
# add_out = np.add(sum_out, b)
|
||||
# sigmoid_out = 1 / (1 + np.exp(-add_out))
|
||||
#
|
||||
# self.op_type = "fc"
|
||||
# self.inputs = {
|
||||
# "X": [("X0", x0), ("X1", x1)],
|
||||
# "W": [("W0", w0), ("W1", w1)],
|
||||
# "B": b
|
||||
# }
|
||||
# self.attrs = {"xNumColDims": [1, 2], "activation": "sigmoid"}
|
||||
# self.outputs = {
|
||||
# "MulOut": [("MulOut0", mul_out0), ("MulOut1", mul_out1)],
|
||||
# "SumOut": sum_out,
|
||||
# "AddOut": add_out,
|
||||
# "Out": sigmoid_out
|
||||
# }
|
||||
#
|
||||
# def test_check_output(self):
|
||||
# self.check_output()
|
||||
#
|
||||
# def test_check_grad(self):
|
||||
# self.check_grad(
|
||||
# ["X0", "X1", "W0", "W1", "B"], "Out", max_relative_error=0.01)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
@ -1,20 +0,0 @@
|
||||
import unittest
|
||||
import numpy as np
|
||||
from op_test import OpTest
|
||||
|
||||
|
||||
class TestIdentityOp(OpTest):
|
||||
def setUp(self):
|
||||
self.op_type = "identity"
|
||||
self.inputs = {'X': np.random.random((10, 10)).astype("float32")}
|
||||
self.outputs = {'Y': self.inputs['X']}
|
||||
|
||||
def test_check_output(self):
|
||||
self.check_output()
|
||||
|
||||
def test_check_grad(self):
|
||||
self.check_grad(['X'], 'Y')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
@ -1,28 +0,0 @@
|
||||
import unittest
|
||||
import numpy as np
|
||||
from op_test import OpTest
|
||||
|
||||
|
||||
class TestInterpOp(OpTest):
|
||||
def setUp(self):
|
||||
self.op_type = "interp"
|
||||
x = np.random.random((2, 3)).astype("float32")
|
||||
y = np.random.random((2, 3)).astype("float32")
|
||||
w = np.random.random(2).astype("float32")
|
||||
|
||||
sub_out = x - y
|
||||
mul_out = sub_out * w.reshape(2, 1)
|
||||
out = mul_out + y
|
||||
|
||||
self.inputs = {'X': x, 'Y': y, 'W': w}
|
||||
self.outputs = {'Out': out, 'SubOut': sub_out, 'MulOut': mul_out}
|
||||
|
||||
def test_check_output(self):
|
||||
self.check_output()
|
||||
|
||||
def test_check_grad_normal(self):
|
||||
self.check_grad(['X', 'Y'], 'Out')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
Loading…
Reference in new issue