From db157eda4583b1ea575cc7a0f8e3fed6d8264153 Mon Sep 17 00:00:00 2001 From: "Yang Yang(Tony)" Date: Mon, 23 Oct 2017 14:16:14 -0700 Subject: [PATCH] New Op Test framework. (#4962) Pass all forward op test --- paddle/operators/crop_op.cc | 3 +- paddle/operators/fc_op.cc | 200 ------------------ paddle/operators/gru_unit_op.cc | 6 +- paddle/operators/identity_op.cc | 63 ------ paddle/operators/interp_op.cc | 113 ---------- paddle/operators/reduce_op.cc | 62 ------ paddle/operators/smooth_l1_loss_op.cc | 6 +- python/paddle/v2/framework/framework.py | 55 ++--- python/paddle/v2/framework/tests/op_test.py | 127 +++++++++-- .../v2/framework/tests/test_accuracy_op.py | 4 +- .../v2/framework/tests/test_activation_op.py | 8 +- .../paddle/v2/framework/tests/test_clip_op.py | 6 +- .../paddle/v2/framework/tests/test_fc_op.py | 62 ------ .../v2/framework/tests/test_identity_op.py | 20 -- .../v2/framework/tests/test_interp_op.py | 28 --- .../paddle/v2/framework/tests/test_pad_op.py | 4 +- .../v2/framework/tests/test_reduce_op.py | 28 --- 17 files changed, 157 insertions(+), 638 deletions(-) delete mode 100644 paddle/operators/fc_op.cc delete mode 100644 paddle/operators/identity_op.cc delete mode 100644 paddle/operators/interp_op.cc delete mode 100644 python/paddle/v2/framework/tests/test_fc_op.py delete mode 100644 python/paddle/v2/framework/tests/test_identity_op.py delete mode 100644 python/paddle/v2/framework/tests/test_interp_op.py diff --git a/paddle/operators/crop_op.cc b/paddle/operators/crop_op.cc index a994d91676..ed78e9e3a3 100644 --- a/paddle/operators/crop_op.cc +++ b/paddle/operators/crop_op.cc @@ -59,7 +59,8 @@ class CropOpMaker : public framework::OpProtoAndCheckerMaker { "The input should be a k-D tensor(k > 0 and k < 7)"); AddInput("Y", "The input used as reference for cropping" - " with the same dimension as X. "); + " with the same dimension as X. ") + .AsDispensable(); AddOutput("Out", "The output of crop op " "with the same dimension as X."); diff --git a/paddle/operators/fc_op.cc b/paddle/operators/fc_op.cc deleted file mode 100644 index 7c422c81fc..0000000000 --- a/paddle/operators/fc_op.cc +++ /dev/null @@ -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(1), - "The size of inputs X(%d) should be no less than 1.", n); - - auto x_num_col_dims = Attr>("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(x_num_col_dims[i]); - mul_attr["y_num_col_dims"] = static_cast(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("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( - "activation", - "(string, default identity) the activation type of FC operator.") - .SetDefault("identity") - .InEnum({"identity", "sigmoid", "softmax"}); - AddAttr>( - "xNumColDims", - "(std::vector) 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{}); - - 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); diff --git a/paddle/operators/gru_unit_op.cc b/paddle/operators/gru_unit_op.cc index 72dd841c85..a596f93769 100644 --- a/paddle/operators/gru_unit_op.cc +++ b/paddle/operators/gru_unit_op.cc @@ -54,8 +54,7 @@ class GRUUnitOp : public framework::OperatorWithKernel { PADDLE_ENFORCE_EQ( weight_width, frame_size * 3, "The shape of Weight matrix must be [frame_size, frame_size * 3]."); - auto bias = Input("Bias"); - if (bias != framework::kEmptyVarName) { + if (ctx->HasInput("Bias")) { auto bias_dims = ctx->GetInputDim("Bias"); int bias_height = bias_dims[0]; int bias_width = bias_dims[1]; @@ -89,7 +88,8 @@ class GRUUnitOpMaker : public framework::OpProtoAndCheckerMaker { "weights of output candidate with shape [frame_size, frame_size]"); AddInput("Bias", "(Tensor) Bias vector with shape [1, frame_size * 3] concating " - "bias of the update gate, reset gate and output candidate."); + "bias of the update gate, reset gate and output candidate.") + .AsDispensable(); AddOutput("Gate", "(Tensor) Matrix with shape [batch_size, frame_size * 3] for the " "output of update gate, reset gate and output candidate") diff --git a/paddle/operators/identity_op.cc b/paddle/operators/identity_op.cc deleted file mode 100644 index 2cc632205e..0000000000 --- a/paddle/operators/identity_op.cc +++ /dev/null @@ -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 -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 -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(1)}})); - CompleteAddOp(false); - } -}; - -} // namespace operators -} // namespace paddle - -namespace ops = paddle::operators; - -REGISTER_OP_WITHOUT_GRADIENT(identity, ops::IdentityOp, - ops::IdentityOpMaker); diff --git a/paddle/operators/interp_op.cc b/paddle/operators/interp_op.cc deleted file mode 100644 index d02b01c3f3..0000000000 --- a/paddle/operators/interp_op.cc +++ /dev/null @@ -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); diff --git a/paddle/operators/reduce_op.cc b/paddle/operators/reduce_op.cc index 46f66a1370..0599daa768 100644 --- a/paddle/operators/reduce_op.cc +++ b/paddle/operators/reduce_op.cc @@ -160,66 +160,6 @@ class ReduceMinOpMaker : public ReduceOpMaker { } }; -class NormOp : public NetOp { - public: - NormOp(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 NormOp should not be null."); - PADDLE_ENFORCE_NE(Output("AbsOut"), framework::kEmptyVarName, - "Output(AbsOut) of NormOp should not be null."); - PADDLE_ENFORCE_NE(Output("PowOut"), framework::kEmptyVarName, - "Output(PowOut) of NormOp should not be null."); - PADDLE_ENFORCE_NE(Output("SumOut"), framework::kEmptyVarName, - "Output(SumOut) of NormOp should not be null."); - PADDLE_ENFORCE_NE(Output("Out"), framework::kEmptyVarName, - "Output(Out) of NormOp should not be null."); - auto dim = Attr("dim"); - auto keep_dim = Attr("keep_dim"); - auto p = Attr("p"); - PADDLE_ENFORCE_GT(p, 0, "Order of the norm should be positive."); - AppendOp(framework::OpRegistry::CreateOp("abs", {{"X", {Input("X")}}}, - {{"Y", {Output("AbsOut")}}}, {})); - AppendOp(framework::OpRegistry::CreateOp("pow", {{"X", {Output("AbsOut")}}}, - {{"Y", {Output("PowOut")}}}, - {{"factor", p}})); - framework::AttributeMap sum_attr; - sum_attr["dim"] = dim; - sum_attr["keep_dim"] = keep_dim; - AppendOp(framework::OpRegistry::CreateOp( - "reduce_sum", {{"X", {Output("PowOut")}}}, - {{"Out", {Output("SumOut")}}}, sum_attr)); - AppendOp(framework::OpRegistry::CreateOp( - "pow", {{"X", {Output("SumOut")}}}, {{"Y", {Output("Out")}}}, - {{"factor", static_cast(1. / p)}})); - CompleteAddOp(false); - } -}; - -class NormOpMaker : public ReduceOpMaker { - public: - NormOpMaker(framework::OpProto *proto, framework::OpAttrChecker *op_checker) - : ReduceOpMaker(proto, op_checker) { - AddOutput("AbsOut", - "(Tensor) The intermediate output of Norm operator, " - "saving the absolute value of the input tensor X.") - .AsIntermediate(); - AddOutput("PowOut", - "(Tensor) The intermediate output of Norm operator, " - "saving the p-th power of the output tensor AbsOut.") - .AsIntermediate(); - AddOutput("SumOut", - "(Tensor) the intermediate output of Norm operator, " - "saving the sum of PowOut reduced on the given dimension.") - .AsIntermediate(); - AddAttr("p", "(float, default 2) The order of Norm.").SetDefault(2); - SetComment("Norm", "vector p-norm"); - AddComment(comment_); - } -}; - } // namespace operators } // namespace paddle @@ -237,8 +177,6 @@ REGISTER_OP(reduce_max, ops::ReduceOp, ops::ReduceMaxOpMaker, reduce_max_grad, REGISTER_OP(reduce_min, ops::ReduceOp, ops::ReduceMinOpMaker, reduce_min_grad, ops::ReduceGradOp); -REGISTER_OP_WITHOUT_GRADIENT(norm, ops::NormOp, ops::NormOpMaker); - #define REGISTER_REDUCE_CPU_KERNEL(reduce_type, functor, grad_functor) \ REGISTER_OP_CPU_KERNEL( \ reduce_type, \ diff --git a/paddle/operators/smooth_l1_loss_op.cc b/paddle/operators/smooth_l1_loss_op.cc index a4f0f37764..758481943d 100644 --- a/paddle/operators/smooth_l1_loss_op.cc +++ b/paddle/operators/smooth_l1_loss_op.cc @@ -62,11 +62,13 @@ class SmoothL1LossOpMaker : public framework::OpProtoAndCheckerMaker { AddInput("InsideWeight", "Optional input tensor of smooth l1 loss op with the same shape " "as X. If provided, the result of (X - Y) will be multiplied " - "by this tensor element by element."); + "by this tensor element by element.") + .AsDispensable(); AddInput("OutsideWeight", "Optinal input of smooth l1 loss op with the same shape as X." "If provided, the output smooth l1 loss will be multiplied by " - "this tensor element by element."); + "this tensor element by element.") + .AsDispensable(); AddOutput("Diff", "Intermediate variable to cache InsideWeight*(X-Y).") .AsIntermediate(); AddOutput("Out", "Smooth l1 loss."); diff --git a/python/paddle/v2/framework/framework.py b/python/paddle/v2/framework/framework.py index 1a42de3a9b..813e25816d 100644 --- a/python/paddle/v2/framework/framework.py +++ b/python/paddle/v2/framework/framework.py @@ -191,32 +191,33 @@ class Operator(object): "`type` to initilized an Operator can not be None.") self.desc.set_type(type) proto = OpProtoHolder.instance().get_op_proto(type) - if inputs is not None: - given = set() - need = set() - for n in inputs: - given.add(n) - for m in proto.inputs: - need.add(m.name) - if not given == need: - raise ValueError( - "Incorrect setting for input(s) of operator \"%s\". Need: [%s] Given: [%s]" - % (type, ", ".join(str(e) for e in need), ", ".join( - str(e) for e in given))) - for in_proto in proto.inputs: + def find_name(var_list, name): + for var_name in var_list: + if var_name == name: + return True + return False - in_argus = inputs[in_proto.name] - if not isinstance(in_argus, list): - in_argus = [in_argus] - if not in_proto.duplicable and len(in_argus) > 1: - raise ValueError( - "Input %s expects only one input, but %d are given." % - (in_proto.name, len(in_argus))) - in_argu_names = [] - for argu in in_argus: - in_argu_names.append(argu.name) - self.desc.set_input(in_proto.name, in_argu_names) + if inputs is not None: + for in_proto in proto.inputs: + found = find_name(inputs, in_proto.name) + assert found or in_proto.dispensable, "Input {} not found".format( + in_proto.name) + + if found: + in_argus = inputs[in_proto.name] + if not isinstance(in_argus, list): + in_argus = [in_argus] + if not in_proto.duplicable and len(in_argus) > 1: + raise ValueError( + "Input %s expects only one input, but %d are given." + % (in_proto.name, len(in_argus))) + in_argu_names = [] + for argu in in_argus: + in_argu_names.append(argu.name) + self.desc.set_input(in_proto.name, in_argu_names) + else: + self.desc.set_input(in_proto.name, []) if outputs is not None: given = set() @@ -250,10 +251,10 @@ class Operator(object): attr_name = attr.name if (not attr_name in attrs) or (attrs[attr_name] is None): continue - if not isinstance(attrs[attr_name], Block): - self.desc.set_attr(attr_name, attrs[attr_name]) - else: + if isinstance(attrs[attr_name], Block): self.desc.set_block_attr(attr_name, attrs[attr_name].desc) + else: + self.desc.set_attr(attr_name, attrs[attr_name]) self.desc.check_attrs() if type not in {'feed', 'fetch'}: diff --git a/python/paddle/v2/framework/tests/op_test.py b/python/paddle/v2/framework/tests/op_test.py index 169052fe41..1c6dce9634 100644 --- a/python/paddle/v2/framework/tests/op_test.py +++ b/python/paddle/v2/framework/tests/op_test.py @@ -4,6 +4,8 @@ import random import itertools import paddle.v2.framework.core as core from paddle.v2.framework.op import Operator +from paddle.v2.framework.executor import Executor +from paddle.v2.framework.framework import Program, OpProtoHolder def grad_var_name(var_name): @@ -197,6 +199,48 @@ def get_gradient(scope, op, inputs, outputs, grad_name, place, return out +def append_input_output(block, op_proto, np_list, is_input): + '''Insert VarDesc and generate Python variable instance''' + proto_list = op_proto.inputs if is_input else op_proto.outputs + + def create_var(block, name, np_list, var_proto): + if name not in np_list: + assert var_proto.intermediate, "{} not found".format(name) + shape = None + lod_level = None + else: + np_value = np_list[name] + if isinstance(np_value, tuple): + shape = list(np_value[0].shape) + lod_level = len(np_value[1]) + else: + shape = list(np_value.shape) + lod_level = 0 + return block.create_var( + dtype="float32", shape=shape, lod_level=lod_level, name=name) + + var_dict = {} + for var_proto in proto_list: + var_name = str(var_proto.name) + if is_input: + if (var_name not in np_list) and var_proto.dispensable: + continue + assert (var_name in np_list) or (var_proto.dispensable), \ + "Missing {} as input".format(var_name) + if var_proto.duplicable: + assert isinstance(np_list[var_name], list), \ + "Duplicable {} should be set as list".format(var_name) + var_list = [] + for (name, np_value) in np_list[var_name]: + var_list.append( + create_var(block, name, {name: np_value}, var_proto)) + var_dict[var_name] = var_list + else: + var_dict[var_name] = create_var(block, var_name, np_list, var_proto) + + return var_dict + + class OpTest(unittest.TestCase): @classmethod def setUpClass(cls): @@ -213,40 +257,85 @@ class OpTest(unittest.TestCase): np.random.set_state(cls._np_rand_state) random.setstate(cls._py_rand_state) + def feed_var(self, input_vars, place): + feed_map = {} + for var_name in input_vars: + if isinstance(input_vars[var_name], list): + for name, np_value in self.inputs[var_name]: + tensor = core.LoDTensor() + tensor.set(np_value, place) + feed_map[name] = tensor + else: + tensor = core.LoDTensor() + if isinstance(self.inputs[var_name], tuple): + tensor.set(self.inputs[var_name][0], place) + tensor.set_lod(self.inputs[var_name][1]) + else: + tensor.set(self.inputs[var_name], place) + feed_map[var_name] = tensor + + return feed_map + def check_output_with_place(self, place, atol): - self.scope = core.Scope() - op_inputs = self.inputs if hasattr(self, "inputs") else dict() - op_outputs = self.outputs if hasattr(self, "outputs") else dict() - op_attrs = self.attrs if hasattr(self, "attrs") else dict() - self.op = create_op(self.scope, self.op_type, op_inputs, op_outputs, - op_attrs) - if isinstance(place, core.GPUPlace) and not self.op.support_gpu(): - return - set_input(self.scope, self.op, self.inputs, place) - ctx = core.DeviceContext.create(place) - self.op.run(self.scope, ctx) + op_proto = OpProtoHolder.instance().get_op_proto(self.op_type) + + program = Program() + block = program.global_block() + + inputs = append_input_output(block, op_proto, self.inputs, True) + outputs = append_input_output(block, op_proto, self.outputs, False) + + op = block.append_op( + type=self.op_type, + inputs=inputs, + outputs=outputs, + attrs=self.attrs if hasattr(self, "attrs") else dict()) + + fetch_list = [] + for var_name, var in outputs.iteritems(): + if var_name in self.outputs: + if isinstance(var, list): + for v in var: + fetch_list.append(v) + else: + fetch_list.append(var) - for out_name, out_dup in Operator.get_op_outputs(self.op.type()): + feed_map = self.feed_var(inputs, place) + + exe = Executor(place) + outs = exe.run(program, feed=feed_map, fetch_list=fetch_list) + + for out_name, out_dup in Operator.get_op_outputs(self.op_type): if out_name not in self.outputs: continue + def find_actual(target_name, fetch_list): + found = [ + i for i, var in enumerate(fetch_list) + if var.name == target_name + ] + self.assertTrue( + len(found) == 1, "Found {} {}".format( + len(found), target_name)) + return found[0] + if out_dup: sub_out = self.outputs[out_name] if not isinstance(sub_out, list): raise AssertionError("sub_out type %s is not list", type(sub_out)) - for sub_out_name, expect in sub_out: - actual = np.array( - self.scope.find_var(sub_out_name).get_tensor()) + idx = find_actual(sub_out_name, fetch_list) + actual = outs[idx] self.assertTrue( np.allclose( actual, expect, atol=atol), - "Output (" + out_name + ") has diff at " + str(place)) + "Output (" + sub_out_name + ") has diff at " + + str(place)) else: - actual = np.array(self.scope.find_var(out_name).get_tensor()) + idx = find_actual(out_name, fetch_list) + actual = outs[idx] expect = self.outputs[out_name] - self.assertTrue( np.allclose( actual, expect, atol=atol), @@ -254,7 +343,7 @@ class OpTest(unittest.TestCase): def check_output(self, atol=1e-5): places = [core.CPUPlace()] - if core.is_compile_gpu(): + if core.is_compile_gpu() and core.op_support_gpu(self.op_type): places.append(core.GPUPlace(0)) for place in places: self.check_output_with_place(place, atol) diff --git a/python/paddle/v2/framework/tests/test_accuracy_op.py b/python/paddle/v2/framework/tests/test_accuracy_op.py index b6f3a35d6f..02be9a0291 100644 --- a/python/paddle/v2/framework/tests/test_accuracy_op.py +++ b/python/paddle/v2/framework/tests/test_accuracy_op.py @@ -16,7 +16,9 @@ class TestAccuracyOp(OpTest): if ele == label[rowid]: num_correct += 1 break - self.outputs = {'Accuracy': [num_correct / float(n)]} + self.outputs = { + 'Accuracy': np.array([num_correct / float(n)]).astype("float32") + } def test_check_output(self): self.check_output() diff --git a/python/paddle/v2/framework/tests/test_activation_op.py b/python/paddle/v2/framework/tests/test_activation_op.py index 5831b880e4..c1668cd00f 100644 --- a/python/paddle/v2/framework/tests/test_activation_op.py +++ b/python/paddle/v2/framework/tests/test_activation_op.py @@ -172,8 +172,8 @@ class TestBRelu(OpTest): def setUp(self): self.op_type = "brelu" x = np.random.uniform(-1, 1, [4, 4]).astype("float32") - t_min = 1 - t_max = 4 + t_min = 1.0 + t_max = 4.0 # The same with TestAbs x[np.abs(x - t_min) < 0.005] = t_min + 0.02 x[np.abs(x - t_max) < 0.005] = t_max + 0.02 @@ -218,7 +218,7 @@ class TestSoftRelu(OpTest): def setUp(self): self.op_type = "soft_relu" x = np.random.uniform(-3, 3, [4, 4]).astype("float32") - threshold = 2 + threshold = 2.0 # The same reason with TestAbs x[np.abs(x - threshold) < 0.005] = threshold + 0.02 x[np.abs(x + threshold) < 0.005] = -threshold + 0.02 @@ -303,7 +303,7 @@ class TestPow(OpTest): def setUp(self): self.op_type = "pow" self.inputs = {'X': np.random.uniform(1, 2, [11, 17]).astype("float32")} - self.attrs = {'factor': 3} + self.attrs = {'factor': 3.0} self.outputs = {'Y': np.power(self.inputs['X'], 3)} def test_check_output(self): diff --git a/python/paddle/v2/framework/tests/test_clip_op.py b/python/paddle/v2/framework/tests/test_clip_op.py index 5df6a49498..a7e1bf1744 100644 --- a/python/paddle/v2/framework/tests/test_clip_op.py +++ b/python/paddle/v2/framework/tests/test_clip_op.py @@ -37,14 +37,14 @@ class TestCase1(TestClipOp): def initTestCase(self): self.shape = (8, 16, 8) self.max = 0.7 - self.min = 0 + self.min = 0.0 class TestCase2(TestClipOp): def initTestCase(self): self.shape = (8, 16) - self.max = 1 - self.min = 0 + self.max = 1.0 + self.min = 0.0 class TestCase3(TestClipOp): diff --git a/python/paddle/v2/framework/tests/test_fc_op.py b/python/paddle/v2/framework/tests/test_fc_op.py deleted file mode 100644 index ffd7024bbf..0000000000 --- a/python/paddle/v2/framework/tests/test_fc_op.py +++ /dev/null @@ -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() diff --git a/python/paddle/v2/framework/tests/test_identity_op.py b/python/paddle/v2/framework/tests/test_identity_op.py deleted file mode 100644 index 26cec1fcc3..0000000000 --- a/python/paddle/v2/framework/tests/test_identity_op.py +++ /dev/null @@ -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() diff --git a/python/paddle/v2/framework/tests/test_interp_op.py b/python/paddle/v2/framework/tests/test_interp_op.py deleted file mode 100644 index 066569b96c..0000000000 --- a/python/paddle/v2/framework/tests/test_interp_op.py +++ /dev/null @@ -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() diff --git a/python/paddle/v2/framework/tests/test_pad_op.py b/python/paddle/v2/framework/tests/test_pad_op.py index 9052e63b56..55f1774e57 100644 --- a/python/paddle/v2/framework/tests/test_pad_op.py +++ b/python/paddle/v2/framework/tests/test_pad_op.py @@ -27,7 +27,7 @@ class TestPadOp(OpTest): def initTestCase(self): self.shape = (16, 16) self.paddings = [(0, 1), (2, 3)] - self.pad_value = 0 + self.pad_value = 0.0 class TestCase1(TestPadOp): @@ -41,7 +41,7 @@ class TestCase2(TestPadOp): def initTestCase(self): self.shape = (2, 2, 2) self.paddings = [(0, 0), (0, 0), (1, 2)] - self.pad_value = 1 + self.pad_value = 1.0 class TestCase3(TestPadOp): diff --git a/python/paddle/v2/framework/tests/test_reduce_op.py b/python/paddle/v2/framework/tests/test_reduce_op.py index 0fec31c2e2..70359d60cb 100644 --- a/python/paddle/v2/framework/tests/test_reduce_op.py +++ b/python/paddle/v2/framework/tests/test_reduce_op.py @@ -85,33 +85,5 @@ class Test1DReduce(OpTest): self.check_grad(['X'], 'Out') -class TestNorm(OpTest): - def setUp(self): - # use x away from 0 to avoid errors of numerical gradient when gradient near 0 - x = np.random.random((5, 6, 10)).astype("float32") + 0.2 - p = 2 - dim = 1 - keep_dim = False - abs_out = np.absolute(x) - pow_out = np.power(x, p) - sum_out = np.sum(pow_out, axis=dim, keepdims=keep_dim) - out = np.power(sum_out, 1. / p) - self.op_type = "norm" - self.inputs = {'X': x} - self.attrs = {"p": p, "dim": dim, "keep_dim": keep_dim} - self.outputs = { - "AbsOut": abs_out, - "PowOut": pow_out, - "SumOut": sum_out, - "Out": out - } - - def test_check_output(self): - self.check_output() - - def test_check_grad(self): - self.check_grad(['X'], 'Out', max_relative_error=0.01) - - if __name__ == '__main__': unittest.main() -- GitLab