From aeeb77de1d40ea71df2d18de9969980aab4fc631 Mon Sep 17 00:00:00 2001 From: typhoonzero Date: Wed, 1 Nov 2017 20:53:43 +0800 Subject: [PATCH 01/67] simple pipe reader for hdfs or other service --- python/paddle/v2/reader/decorator.py | 98 ++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/python/paddle/v2/reader/decorator.py b/python/paddle/v2/reader/decorator.py index 45a4288751..0695542690 100644 --- a/python/paddle/v2/reader/decorator.py +++ b/python/paddle/v2/reader/decorator.py @@ -323,3 +323,101 @@ def xmap_readers(mapper, reader, process_num, buffer_size, order=False): yield sample return xreader + + +def _buf2lines(buf, line_break="\n"): + # FIXME: line_break should be automatically configured. + lines = buf.split(line_break) + return lines[:-1], lines[-1] + + +def pipe_reader(left_cmd, + parser, + bufsize=8192, + file_type="plain", + cut_lines=True, + line_break="\n"): + """ + pipe_reader read data by stream from a command, take it's + stdout into a pipe buffer and redirect it to the parser to + parse, then yield data as your desired format. + + You can using standard linux command or call another program + to read data, from HDFS, Ceph, URL, AWS S3 etc: + + cmd = "hadoop fs -cat /path/to/some/file" + cmd = "cat sample_file.tar.gz" + cmd = "curl http://someurl" + cmd = "python print_s3_bucket.py" + + A sample parser: + + def sample_parser(lines): + # parse each line as one sample data, + # return a list of samples as batches. + ret = [] + for l in lines: + ret.append(l.split(" ")[1:5]) + return ret + + :param left_cmd: command to excute to get stdout from. + :type left_cmd: string + :param parser: parser function to parse lines of data. + if cut_lines is True, parser will receive list + of lines. + if cut_lines is False, parser will receive a + raw buffer each time. + parser should return a list of parsed values. + :type parser: callable + :param bufsize: the buffer size used for the stdout pipe. + :type bufsize: int + :param file_type: can be plain/gzip, stream buffer data type. + :type file_type: string + :param cut_lines: whether to pass lines instead of raw buffer + to the parser + :type cut_lines: bool + :param line_break: line break of the file, like \n or \r + :type line_break: string + + :return: the reader generator. + :rtype: callable + """ + if not isinstance(left_cmd, str): + raise TypeError("left_cmd must be a string") + if not callable(parser): + raise TypeError("parser must be a callable object") + + process = subprocess.Popen( + left_cmd.split(" "), bufsize=bufsize, stdout=subprocess.PIPE) + # TODO(typhoonzero): add a thread to read stderr + + # Always init a decompress object is better than + # create in the loop. + dec = zlib.decompressobj( + 32 + zlib.MAX_WBITS) # offset 32 to skip the header + + def reader(): + remained = "" + while True: + buff = process.stdout.read(bufsize) + if buff: + if file_type == "gzip": + decomp_buff = dec.decompress(buff) + elif file_type == "plain": + decomp_buff = buff + else: + raise TypeError("file_type %s is not allowed" % file_type) + + if cut_lines: + lines, remained = _buf2lines(''.join( + [remained, decomp_buff]), line_break) + parsed_list = parser(lines) + for ret in parsed_list: + yield ret + else: + for ret in parser(decomp_buff): + yield ret + else: + break + + return reader From 3d276277df1b1f8b216cae246d5cdc4f6dd02028 Mon Sep 17 00:00:00 2001 From: wanghaoshuang Date: Wed, 8 Nov 2017 14:17:38 +0800 Subject: [PATCH 02/67] Add nce op 1. Add nce forward and backward kernel for CPU --- paddle/operators/nce_op.cc | 120 +++++++++++++++++++++ paddle/operators/nce_op.h | 210 +++++++++++++++++++++++++++++++++++++ 2 files changed, 330 insertions(+) create mode 100644 paddle/operators/nce_op.cc create mode 100644 paddle/operators/nce_op.h diff --git a/paddle/operators/nce_op.cc b/paddle/operators/nce_op.cc new file mode 100644 index 0000000000..afd61b8851 --- /dev/null +++ b/paddle/operators/nce_op.cc @@ -0,0 +1,120 @@ +/* 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/nce_op.h" + +namespace paddle { +namespace operators { + +using framework::Tensor; + +class NCEOp : public framework::OperatorWithKernel { + public: + using framework::OperatorWithKernel::OperatorWithKernel; + + protected: + void InferShape(framework::InferShapeContext* ctx) const override { + PADDLE_ENFORCE(ctx->HasInput("X")); + PADDLE_ENFORCE(ctx->HasInput("Label")); + PADDLE_ENFORCE(ctx->HasInput("W")); + PADDLE_ENFORCE(ctx->HasOutput("Out")); + PADDLE_ENFORCE(ctx->HasOutput("SampleLogits")); + PADDLE_ENFORCE(ctx->HasOutput("SampleLabels")); + + auto x_dims = ctx->GetInputDim("X"); + auto label_dims = ctx->GetInputDim("Label"); + PADDLE_ENFORCE_EQ(x_dims[0], label_dims[0]); + if (ctx->HasInput("B")) { + PADDLE_ENFORCE_EQ(ctx->GetInputDim("W")[0], ctx->GetInputDim("B")[0]); + } + int num_sampled_classes = ctx->Attrs().Get("num_sampled_classes"); + int num_classes = ctx->Attrs().Get("num_classes"); + PADDLE_ENFORCE_EQ(num_classes, ctx->GetInputDim("W")[0]); + PADDLE_ENFORCE_LT(num_sampled_classes, num_classes); + + // set dims of output(Out) + std::vector out_dims(1); + out_dims.push_back(x_dims[0]); + ctx->SetOutputDim("Out", framework::make_ddim(out_dims)); + + // set dims of output(SampleOut) + std::vector sample_out_dims(2); + sample_out_dims.push_back(x_dims[0]); + sample_out_dims.push_back(num_sampled_classes + 1); + ctx->SetOutputDim("SampleLogits", framework::make_ddim(sample_out_dims)); + ctx->SetOutputDim("SampleLabels", framework::make_ddim(sample_out_dims)); + } +}; + +class NCEOpMaker : public framework::OpProtoAndCheckerMaker { + public: + NCEOpMaker(framework::OpProto* proto, framework::OpAttrChecker* op_checker) + : OpProtoAndCheckerMaker(proto, op_checker) { + AddInput("X", ""); + AddInput("Label", ""); + AddInput("W", ""); + AddInput("B", ""); + AddInput("SampleWeight", ""); + AddOutput("Out", ""); + AddOutput("SampleLogits", ""); + AddOutput("SampleLabels", ""); + AddAttr("num_classes", ""); + AddAttr("num_sampled_classes", "").SetDefault(10); + AddComment(R"DOC( +Expand input(X) according to LOD of input(Y). + +)DOC"); + } +}; + +class NCEOpGrad : public framework::OperatorWithKernel { + public: + using framework::OperatorWithKernel::OperatorWithKernel; + + protected: + void InferShape(framework::InferShapeContext* ctx) const override { + PADDLE_ENFORCE(ctx->HasInput("X")); + PADDLE_ENFORCE(ctx->HasInput("W")); + PADDLE_ENFORCE(ctx->HasInput("Out")); + PADDLE_ENFORCE(ctx->HasInput(framework::GradVarName("Out")), + "The input(Out@GRAD) should not be null"); + + auto x_dims = ctx->GetInputDim("X"); + auto x_grad_name = framework::GradVarName("X"); + if (ctx->HasOutput(x_grad_name)) { + ctx->SetOutputDim(x_grad_name, x_dims); + } + + auto w_dims = ctx->GetInputDim("W"); + auto w_grad_name = framework::GradVarName("W"); + if (ctx->HasOutput(w_grad_name)) { + ctx->SetOutputDim(w_grad_name, w_dims); + } + + auto bias_grad_name = framework::GradVarName("B"); + if (ctx->HasOutput(bias_grad_name)) { + auto bias_dims = ctx->GetInputDim("B"); + ctx->SetOutputDim(bias_grad_name, bias_dims); + } + } +}; + +} // namespace operators +} // namespace paddle + +namespace ops = paddle::operators; +REGISTER_OP(nce, ops::NCEOp, ops::NCEOpMaker, nce_grad, ops::NCEOpGrad); +REGISTER_OP_CPU_KERNEL(nce, ops::NCEKernel); +REGISTER_OP_CPU_KERNEL(nce_grad, + ops::NCEGradKernel); diff --git a/paddle/operators/nce_op.h b/paddle/operators/nce_op.h new file mode 100644 index 0000000000..ce1717c9b0 --- /dev/null +++ b/paddle/operators/nce_op.h @@ -0,0 +1,210 @@ +/* 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 +#include "paddle/framework/eigen.h" +#include "paddle/framework/op_registry.h" +#include "paddle/memory/memcpy.h" +#include "unsupported/Eigen/CXX11/Tensor" + +namespace paddle { +namespace operators { + +using Tensor = framework::Tensor; + +template +using EigenMatrix = framework::EigenMatrix; + +template +void PrepareSamples(const framework::ExecutionContext& context) { + auto label = context.Input("Label"); + const T* label_data = label->data(); + auto label_dims = label->dims(); + int num_classes = context.Attr("num_classes"); + // random machine + std::random_device rd; + std::mt19937 rng(rd()); + std::uniform_int_distribution rand(0, num_classes - 1); + + auto sample_labels = context.Output("SampleLabels"); + auto sample_labels_dims = sample_labels->dims(); + int* sample_labels_data = + sample_labels->mutable_data(context.GetPlace()); + + int num_label = label_dims.size() == 2 ? label_dims[1] : 1; + for (size_t i = 0; i < label_dims[0]; ++i) { + int j = 0; + for (; j < num_label; ++j) { + sample_labels_data[sample_labels_dims[1] * i + j] = + label_data[i * num_label + j]; + } + for (; j < sample_labels_dims[1]; ++j) { + int id = rand(rng); + sample_labels_data[sample_labels_dims[1] * i + j] = id; + } + } +} + +template +class NCEKernel : public framework::OpKernel { + public: + void Compute(const framework::ExecutionContext& context) const override { + PrepareSamples(context); + auto sample_labels = context.Output("SampleLabels"); + const int* sample_labels_data = sample_labels->data(); + auto sample_out = context.Output("SampleLogits"); + T* sample_out_data = sample_out->mutable_data(context.GetPlace()); + auto label = context.Input("Label"); + auto sample_weight = context.Input("SampleWeight"); + const T* sample_weight_data = nullptr; + if (sample_weight != nullptr) { + sample_weight_data = sample_weight->data(); + } + auto out = context.Output("Out"); + T* out_data = out->mutable_data(context.GetPlace()); + int num_smalped_classes = context.Attr("num_sampled_classes"); + int num_classes = context.Attr("num_classes"); + int num_true_class = 1; + if (label != nullptr) { + num_true_class = label->dims()[1]; + } + T b = 1. / num_classes * num_smalped_classes; + + // forward bias + auto bias = context.Input("B"); + if (bias != nullptr) { + const T* bias_data = bias->data(); + for (size_t i = 0; i < sample_labels->numel(); ++i) { + sample_out_data[i] = bias_data[sample_labels_data[i]]; + } + } else { + for (size_t i = 0; i < sample_labels->numel(); ++i) { + sample_out_data[i] = 0; + } + } + + // forward mul + auto input_mat = EigenMatrix::From(*(context.Input("X"))); + auto weight_mat = EigenMatrix::From(*(context.Input("W"))); + for (size_t i = 0; i < sample_labels->numel(); ++i) { + // sample_out_data[i] += (input_mat.chip((int)(i / + // sample_labels->dims()[1]), 0) * weight_mat.chip(sample_labels_data[i], + // 0)).sum(); + Eigen::Tensor result = + (input_mat.chip((int)(i / sample_labels->dims()[1]), 0) * + weight_mat.chip(sample_labels_data[i], 0)) + .sum(); + sample_out_data[i] += result(0); + // activation_->forward + sample_out_data[i] = (1 / 1 + (sample_out_data[i])); + } + + // forward cost + for (size_t i = 0; i < sample_labels->dims()[0]; ++i) { + size_t j = 0; + T w = sample_weight == nullptr ? 1 : sample_weight_data[i]; + // for true classes + for (; j < num_true_class; ++j) { + T o = sample_out_data[i * sample_out->dims()[1] + j]; + T cost = -log(o / (o + b)); + out_data[i] += w * cost; + } + // for sampled neg classes + for (; j < sample_labels->dims()[1]; ++j) { + T o = sample_out_data[i * sample_out->dims()[1] + j]; + T cost = -log(b / (o + b)); + out_data[i] += w * cost; + } + } + } +}; + +template +class NCEGradKernel : public framework::OpKernel { + public: + void Compute(const framework::ExecutionContext& context) const override { + auto label = context.Input("Label"); + auto sample_out = context.Input("SampleLogits"); + const T* sample_out_data = sample_out->data(); + auto sample_labels = context.Input("SampleLabels"); + const int* sample_labels_data = sample_labels->data(); + auto sample_weight = context.Input("SampleWeight"); + const T* sample_weight_data = nullptr; + if (sample_weight != nullptr) { + sample_weight_data = sample_weight->data(); + } + int num_smalped_classes = context.Attr("num_sampled_classes"); + int num_classes = context.Attr("num_classes"); + int num_true_class = 1; + if (label != nullptr) { + num_true_class = label->dims()[1]; + } + T b = 1. / num_classes * num_smalped_classes; + + Tensor sample_grad; // tmp tensor + T* sample_grad_data = + sample_grad.mutable_data(sample_labels->dims(), context.GetPlace()); + + // backward cost + for (size_t i = 0; i < sample_labels->numel(); ++i) { + T o = sample_out_data[i]; + T w = sample_weight == nullptr + ? 1 + : sample_weight_data[i / sample_labels->dims()[1]]; + sample_grad_data[i] = (i % sample_labels->dims()[1]) < num_true_class + ? -w * b / (o * (o + b)) + : w / (o + b); + // sigmoid->backward + sample_grad_data[i] = + (o > 0) ? sample_grad_data[i] : ((o < 0) ? -sample_grad_data[i] : 0); + } + + // get d_bias + auto d_bias = context.Output(framework::GradVarName("B")); + if (d_bias != nullptr) { + T* d_bias_data = d_bias->mutable_data(context.GetPlace()); + for (size_t i = 0; i < sample_labels->numel(); ++i) { + d_bias_data[sample_labels_data[i]] += sample_grad_data[i]; + } + } + // get d_w + auto d_w = context.Output(framework::GradVarName("W")); + if (d_w != nullptr) { + auto d_w_matrix = EigenMatrix::From(*d_w); + auto x_matrix = EigenMatrix::From(*(context.Input("X"))); + for (size_t i = 0; i < sample_labels->numel(); ++i) { + d_w_matrix.chip(sample_labels_data[i], 0) = + x_matrix.chip((int)(i / sample_labels->dims()[1]), 0) * + sample_grad_data[i]; + } + } + + // get d_x + auto d_x = context.Output(framework::GradVarName("X")); + if (d_x != nullptr) { + auto d_x_matrix = EigenMatrix::From(*d_x); + auto w_matrix = EigenMatrix::From(*(context.Input("W"))); + for (size_t i = 0; i < sample_labels->numel(); ++i) { + d_x_matrix.chip((int)(i / sample_labels->dims()[1]), 0) += + w_matrix.chip(sample_labels_data[i], 0) * sample_grad_data[i]; + } + } + } +}; + +} // namespace operators +} // namespace paddle From 09d32b068cbdf65f93e98f7b357dbc7e90f11734 Mon Sep 17 00:00:00 2001 From: wanghaoshuang Date: Thu, 16 Nov 2017 00:01:55 +0800 Subject: [PATCH 03/67] Add unitest and comments. --- paddle/operators/nce_op.cc | 115 +++++++++++++------ paddle/operators/nce_op.h | 79 +++++++------ python/paddle/v2/framework/tests/test_nce.py | 96 ++++++++++++++++ 3 files changed, 212 insertions(+), 78 deletions(-) create mode 100644 python/paddle/v2/framework/tests/test_nce.py diff --git a/paddle/operators/nce_op.cc b/paddle/operators/nce_op.cc index afd61b8851..c365d5d922 100644 --- a/paddle/operators/nce_op.cc +++ b/paddle/operators/nce_op.cc @@ -23,57 +23,87 @@ class NCEOp : public framework::OperatorWithKernel { public: using framework::OperatorWithKernel::OperatorWithKernel; - protected: void InferShape(framework::InferShapeContext* ctx) const override { - PADDLE_ENFORCE(ctx->HasInput("X")); + PADDLE_ENFORCE(ctx->HasInput("Input")); PADDLE_ENFORCE(ctx->HasInput("Label")); - PADDLE_ENFORCE(ctx->HasInput("W")); - PADDLE_ENFORCE(ctx->HasOutput("Out")); + PADDLE_ENFORCE(ctx->HasInput("Weight")); + PADDLE_ENFORCE(ctx->HasOutput("Cost")); PADDLE_ENFORCE(ctx->HasOutput("SampleLogits")); PADDLE_ENFORCE(ctx->HasOutput("SampleLabels")); - auto x_dims = ctx->GetInputDim("X"); + auto x_dims = ctx->GetInputDim("Input"); auto label_dims = ctx->GetInputDim("Label"); PADDLE_ENFORCE_EQ(x_dims[0], label_dims[0]); - if (ctx->HasInput("B")) { - PADDLE_ENFORCE_EQ(ctx->GetInputDim("W")[0], ctx->GetInputDim("B")[0]); + int num_true_classes = label_dims.size() == 2 ? label_dims[1] : 1; + if (ctx->HasInput("Bias")) { + PADDLE_ENFORCE_EQ(ctx->GetInputDim("Weight")[0], + ctx->GetInputDim("Bias")[0]); } - int num_sampled_classes = ctx->Attrs().Get("num_sampled_classes"); - int num_classes = ctx->Attrs().Get("num_classes"); - PADDLE_ENFORCE_EQ(num_classes, ctx->GetInputDim("W")[0]); + auto num_sampled_classes = ctx->Attrs().Get("num_sampled_classes"); + auto num_classes = ctx->Attrs().Get("num_classes"); + std::vector sampled_labels = + ctx->Attrs().Get>("sampled_labels"); + PADDLE_ENFORCE_EQ(num_classes, ctx->GetInputDim("Weight")[0]); PADDLE_ENFORCE_LT(num_sampled_classes, num_classes); - + if (sampled_labels.size() > 0) { + PADDLE_ENFORCE_EQ(sampled_labels.size(), + static_cast(num_sampled_classes)); + } // set dims of output(Out) - std::vector out_dims(1); + std::vector out_dims; out_dims.push_back(x_dims[0]); - ctx->SetOutputDim("Out", framework::make_ddim(out_dims)); + ctx->SetOutputDim("Cost", framework::make_ddim(out_dims)); // set dims of output(SampleOut) - std::vector sample_out_dims(2); + std::vector sample_out_dims; sample_out_dims.push_back(x_dims[0]); - sample_out_dims.push_back(num_sampled_classes + 1); + sample_out_dims.push_back(num_sampled_classes + num_true_classes); ctx->SetOutputDim("SampleLogits", framework::make_ddim(sample_out_dims)); ctx->SetOutputDim("SampleLabels", framework::make_ddim(sample_out_dims)); } + + protected: + framework::OpKernelType GetKernelType( + const framework::ExecutionContext& ctx) const override { + return framework::OpKernelType( + framework::ToDataType(ctx.Input("Input")->type()), + ctx.device_context()); + } }; class NCEOpMaker : public framework::OpProtoAndCheckerMaker { public: NCEOpMaker(framework::OpProto* proto, framework::OpAttrChecker* op_checker) : OpProtoAndCheckerMaker(proto, op_checker) { - AddInput("X", ""); - AddInput("Label", ""); - AddInput("W", ""); - AddInput("B", ""); - AddInput("SampleWeight", ""); - AddOutput("Out", ""); - AddOutput("SampleLogits", ""); - AddOutput("SampleLabels", ""); - AddAttr("num_classes", ""); - AddAttr("num_sampled_classes", "").SetDefault(10); + AddInput("Input", "(Tensor) A tensor of shape [batch_size, dim]."); + AddInput("Label", + "(Tensor) A tensor of shape [batch_size, num_true_class]. " + "'num_true_class' is the number of target class in each sample."); + AddInput("Weight", + "(Tensor) A tensor of shape [num_class, dim]. 'num_class' is the " + "total number of class."); + AddInput("Bias", + "(Tensor) A tensor of shape [num_class]. 'num_class' is the total " + "number of class. It is a dispensable input.") + .AsDispensable(); + AddInput("SampleWeight", + "(Tensor) A tensor of shape [batch_size] storing a weight for " + "each sample. And it is a dispensable input. The default value of " + "sample is 1.") + .AsDispensable(); + AddOutput("Cost", + "(Tensor) A tensor of shape [batch_size]. Cost of samples."); + AddOutput("SampleLogits", "An intermediate tensor.").AsIntermediate(); + AddOutput("SampleLabels", "An intermediate tensor.").AsIntermediate(); + AddAttr("num_classes", "Total number of classes."); + AddAttr("num_sampled_classes", "The number of negative classes.") + .SetDefault(10); + AddAttr>("sampled_labels", ""); AddComment(R"DOC( -Expand input(X) according to LOD of input(Y). - +Computes and returns the noise-contrastive estimation training loss. +See [Noise-contrastive estimation: A new estimation principle for unnormalized statistical models](http://www.jmlr.org/proceedings/papers/v9/gutmann10a/gutmann10a.pdf). +By default this uses a uniform distribution for sampling. +The number of target classes per example should be same. If you have a variable number of target classes, you can pad them out to a constant number by either repeating them or by padding with an otherwise unused class. )DOC"); } }; @@ -82,32 +112,41 @@ class NCEOpGrad : public framework::OperatorWithKernel { public: using framework::OperatorWithKernel::OperatorWithKernel; - protected: void InferShape(framework::InferShapeContext* ctx) const override { - PADDLE_ENFORCE(ctx->HasInput("X")); - PADDLE_ENFORCE(ctx->HasInput("W")); - PADDLE_ENFORCE(ctx->HasInput("Out")); - PADDLE_ENFORCE(ctx->HasInput(framework::GradVarName("Out")), + PADDLE_ENFORCE(ctx->HasInput("Input")); + PADDLE_ENFORCE(ctx->HasInput("Weight")); + PADDLE_ENFORCE(ctx->HasInput("Cost")); + PADDLE_ENFORCE(ctx->HasInput("SampleLogits")); + PADDLE_ENFORCE(ctx->HasInput("SampleLabels")); + PADDLE_ENFORCE(ctx->HasInput(framework::GradVarName("Cost")), "The input(Out@GRAD) should not be null"); - auto x_dims = ctx->GetInputDim("X"); - auto x_grad_name = framework::GradVarName("X"); + auto x_dims = ctx->GetInputDim("Input"); + auto x_grad_name = framework::GradVarName("Input"); if (ctx->HasOutput(x_grad_name)) { ctx->SetOutputDim(x_grad_name, x_dims); } - auto w_dims = ctx->GetInputDim("W"); - auto w_grad_name = framework::GradVarName("W"); + auto w_dims = ctx->GetInputDim("Weight"); + auto w_grad_name = framework::GradVarName("Weight"); if (ctx->HasOutput(w_grad_name)) { ctx->SetOutputDim(w_grad_name, w_dims); } - auto bias_grad_name = framework::GradVarName("B"); + auto bias_grad_name = framework::GradVarName("Bias"); if (ctx->HasOutput(bias_grad_name)) { - auto bias_dims = ctx->GetInputDim("B"); + auto bias_dims = ctx->GetInputDim("Bias"); ctx->SetOutputDim(bias_grad_name, bias_dims); } } + + protected: + framework::OpKernelType GetKernelType( + const framework::ExecutionContext& ctx) const override { + return framework::OpKernelType( + framework::ToDataType(ctx.Input("Input")->type()), + ctx.device_context()); + } }; } // namespace operators diff --git a/paddle/operators/nce_op.h b/paddle/operators/nce_op.h index ce1717c9b0..3017bccdca 100644 --- a/paddle/operators/nce_op.h +++ b/paddle/operators/nce_op.h @@ -14,12 +14,11 @@ #pragma once +#include #include #include "paddle/framework/eigen.h" #include "paddle/framework/op_registry.h" -#include "paddle/memory/memcpy.h" #include "unsupported/Eigen/CXX11/Tensor" - namespace paddle { namespace operators { @@ -32,9 +31,12 @@ using EigenMatrix = framework::EigenMatrix; template void PrepareSamples(const framework::ExecutionContext& context) { auto label = context.Input("Label"); - const T* label_data = label->data(); + const int64_t* label_data = label->data(); auto label_dims = label->dims(); int num_classes = context.Attr("num_classes"); + // for unitest + std::vector sampled_labels = + context.Attr>("sampled_labels"); // random machine std::random_device rd; std::mt19937 rng(rd()); @@ -42,19 +44,24 @@ void PrepareSamples(const framework::ExecutionContext& context) { auto sample_labels = context.Output("SampleLabels"); auto sample_labels_dims = sample_labels->dims(); - int* sample_labels_data = - sample_labels->mutable_data(context.GetPlace()); + int64_t* sample_labels_data = + sample_labels->mutable_data(context.GetPlace()); int num_label = label_dims.size() == 2 ? label_dims[1] : 1; + int index = 0; for (size_t i = 0; i < label_dims[0]; ++i) { int j = 0; for (; j < num_label; ++j) { - sample_labels_data[sample_labels_dims[1] * i + j] = - label_data[i * num_label + j]; + sample_labels_data[index++] = label_data[i * num_label + j]; } - for (; j < sample_labels_dims[1]; ++j) { - int id = rand(rng); - sample_labels_data[sample_labels_dims[1] * i + j] = id; + if (sampled_labels.size() > 0) { + for (auto label : sampled_labels) { + sample_labels_data[index++] = label; + } + } else { + for (; j < sample_labels_dims[1]; ++j) { + sample_labels_data[index++] = rand(rng); + } } } } @@ -65,7 +72,7 @@ class NCEKernel : public framework::OpKernel { void Compute(const framework::ExecutionContext& context) const override { PrepareSamples(context); auto sample_labels = context.Output("SampleLabels"); - const int* sample_labels_data = sample_labels->data(); + const int64_t* sample_labels_data = sample_labels->data(); auto sample_out = context.Output("SampleLogits"); T* sample_out_data = sample_out->mutable_data(context.GetPlace()); auto label = context.Input("Label"); @@ -74,7 +81,7 @@ class NCEKernel : public framework::OpKernel { if (sample_weight != nullptr) { sample_weight_data = sample_weight->data(); } - auto out = context.Output("Out"); + auto out = context.Output("Cost"); T* out_data = out->mutable_data(context.GetPlace()); int num_smalped_classes = context.Attr("num_sampled_classes"); int num_classes = context.Attr("num_classes"); @@ -83,9 +90,8 @@ class NCEKernel : public framework::OpKernel { num_true_class = label->dims()[1]; } T b = 1. / num_classes * num_smalped_classes; - // forward bias - auto bias = context.Input("B"); + auto bias = context.Input("Bias"); if (bias != nullptr) { const T* bias_data = bias->data(); for (size_t i = 0; i < sample_labels->numel(); ++i) { @@ -96,27 +102,23 @@ class NCEKernel : public framework::OpKernel { sample_out_data[i] = 0; } } - // forward mul - auto input_mat = EigenMatrix::From(*(context.Input("X"))); - auto weight_mat = EigenMatrix::From(*(context.Input("W"))); + auto input_mat = EigenMatrix::From(*(context.Input("Input"))); + auto weight_mat = EigenMatrix::From(*(context.Input("Weight"))); for (size_t i = 0; i < sample_labels->numel(); ++i) { - // sample_out_data[i] += (input_mat.chip((int)(i / - // sample_labels->dims()[1]), 0) * weight_mat.chip(sample_labels_data[i], - // 0)).sum(); Eigen::Tensor result = (input_mat.chip((int)(i / sample_labels->dims()[1]), 0) * weight_mat.chip(sample_labels_data[i], 0)) .sum(); sample_out_data[i] += result(0); // activation_->forward - sample_out_data[i] = (1 / 1 + (sample_out_data[i])); + sample_out_data[i] = (1. / (1. + exp(-sample_out_data[i]))); } - // forward cost for (size_t i = 0; i < sample_labels->dims()[0]; ++i) { size_t j = 0; - T w = sample_weight == nullptr ? 1 : sample_weight_data[i]; + out_data[i] = 0; + T w = sample_weight == nullptr ? 1. : sample_weight_data[i]; // for true classes for (; j < num_true_class; ++j) { T o = sample_out_data[i * sample_out->dims()[1] + j]; @@ -137,11 +139,13 @@ template class NCEGradKernel : public framework::OpKernel { public: void Compute(const framework::ExecutionContext& context) const override { + auto d_out = context.Input(framework::GradVarName("Cost")); + const T* d_out_data = d_out->data(); auto label = context.Input("Label"); auto sample_out = context.Input("SampleLogits"); const T* sample_out_data = sample_out->data(); auto sample_labels = context.Input("SampleLabels"); - const int* sample_labels_data = sample_labels->data(); + const int64_t* sample_labels_data = sample_labels->data(); auto sample_weight = context.Input("SampleWeight"); const T* sample_weight_data = nullptr; if (sample_weight != nullptr) { @@ -154,11 +158,9 @@ class NCEGradKernel : public framework::OpKernel { num_true_class = label->dims()[1]; } T b = 1. / num_classes * num_smalped_classes; - Tensor sample_grad; // tmp tensor T* sample_grad_data = sample_grad.mutable_data(sample_labels->dims(), context.GetPlace()); - // backward cost for (size_t i = 0; i < sample_labels->numel(); ++i) { T o = sample_out_data[i]; @@ -166,15 +168,12 @@ class NCEGradKernel : public framework::OpKernel { ? 1 : sample_weight_data[i / sample_labels->dims()[1]]; sample_grad_data[i] = (i % sample_labels->dims()[1]) < num_true_class - ? -w * b / (o * (o + b)) - : w / (o + b); - // sigmoid->backward - sample_grad_data[i] = - (o > 0) ? sample_grad_data[i] : ((o < 0) ? -sample_grad_data[i] : 0); + ? w * (b / (o + b)) * (o - 1) + : w * (o * (1 - o) / (o + b)); + sample_grad_data[i] *= d_out_data[i / sample_labels->dims()[1]]; } - // get d_bias - auto d_bias = context.Output(framework::GradVarName("B")); + auto d_bias = context.Output(framework::GradVarName("Bias")); if (d_bias != nullptr) { T* d_bias_data = d_bias->mutable_data(context.GetPlace()); for (size_t i = 0; i < sample_labels->numel(); ++i) { @@ -182,22 +181,23 @@ class NCEGradKernel : public framework::OpKernel { } } // get d_w - auto d_w = context.Output(framework::GradVarName("W")); + auto d_w = context.Output(framework::GradVarName("Weight")); if (d_w != nullptr) { + d_w->mutable_data(context.GetPlace()); auto d_w_matrix = EigenMatrix::From(*d_w); - auto x_matrix = EigenMatrix::From(*(context.Input("X"))); + auto x_matrix = EigenMatrix::From(*(context.Input("Input"))); for (size_t i = 0; i < sample_labels->numel(); ++i) { - d_w_matrix.chip(sample_labels_data[i], 0) = + d_w_matrix.chip(sample_labels_data[i], 0) += x_matrix.chip((int)(i / sample_labels->dims()[1]), 0) * sample_grad_data[i]; } } - // get d_x - auto d_x = context.Output(framework::GradVarName("X")); + auto d_x = context.Output(framework::GradVarName("Input")); if (d_x != nullptr) { + d_x->mutable_data(context.GetPlace()); auto d_x_matrix = EigenMatrix::From(*d_x); - auto w_matrix = EigenMatrix::From(*(context.Input("W"))); + auto w_matrix = EigenMatrix::From(*(context.Input("Weight"))); for (size_t i = 0; i < sample_labels->numel(); ++i) { d_x_matrix.chip((int)(i / sample_labels->dims()[1]), 0) += w_matrix.chip(sample_labels_data[i], 0) * sample_grad_data[i]; @@ -205,6 +205,5 @@ class NCEGradKernel : public framework::OpKernel { } } }; - } // namespace operators } // namespace paddle diff --git a/python/paddle/v2/framework/tests/test_nce.py b/python/paddle/v2/framework/tests/test_nce.py new file mode 100644 index 0000000000..8b1e7a6bb5 --- /dev/null +++ b/python/paddle/v2/framework/tests/test_nce.py @@ -0,0 +1,96 @@ +import unittest +import numpy as np +from op_test import OpTest + + +def nce(input, weight, bias, sample_weight, labels, num_classes, + num_sample_class): + samples = [] + sample_labels = [] + batch_size = input.shape[0] + num_true_class = labels.shape[1] + for i in range(batch_size): + w = 1 if sample_weight is None else sample_weight[i] + for label in labels[i]: + samples.append((i, label, True, w)) + sample_labels.append(label) + for num in range(num_sample_class): + samples.append((i, num, False, w)) + sample_labels.append(num) + # forward bias + sampleOut = np.zeros(len(samples)).astype(np.float32) + if bias is not None: + for i in range(len(samples)): + sampleOut[i] = bias[samples[i][1]] + # forward weight + for i in range(len(samples)): + sampleOut[i] += np.dot(input[samples[i][0]], weight[samples[i][1]]) + + # forward activation + sampleOut = 1.0 / (1.0 + np.exp(-sampleOut)) + # forward cost + out = np.zeros(batch_size).astype(np.float32) + b = 1.0 / num_classes * num_sample_class + for i in range(len(samples)): + o = sampleOut[i] + cost = -np.log(o / (o + b)) if samples[i][2] else -np.log(b / (o + b)) + out[samples[i][0]] += cost * samples[i][3] + return (out, np.array(sampleOut).reshape(batch_size, + num_sample_class + num_true_class), + np.array(sample_labels).reshape(batch_size, + num_sample_class + num_true_class)) + + +class TestNCE(OpTest): + def generate_data(self, dim, batch_size, num_classes, num_true_class, + num_sampled_classes): + input = np.random.randn(batch_size, dim).astype(np.float32) + weight = np.random.randn(num_classes, dim).astype(np.float32) + bias = np.random.randn(num_classes).astype(np.float32) + sample_weight = np.random.randn(batch_size).astype(np.float32) + labels = np.random.randint(0, num_classes, (batch_size, num_true_class)) + self.attrs = { + 'num_classes': num_classes, + 'num_sampled_classes': num_sampled_classes, + 'sampled_labels': range(num_sampled_classes) + } + self.inputs = { + 'X': input, + 'Label': labels, + 'W': weight, + 'B': bias, + 'SampleWeight': sample_weight + } + + def set_data(self): + self.generate_data(5, 5, 4, 1, 2) + + def compute(self): + out = nce(self.inputs['X'], self.inputs['W'], self.inputs['B'], + self.inputs['SampleWeight'], self.inputs['Label'], + self.attrs['num_classes'], self.attrs['num_sampled_classes']) + self.outputs = { + 'Out': out[0], + 'SampleLogits': out[1], + 'SampleLabels': out[2] + } + + def setUp(self): + self.op_type = 'nce' + self.set_data() + self.compute() + + def test_check_output(self): + self.check_output() + + def test_check_grad(self): + self.check_grad(["X", "W", "B"], "Out", max_relative_error=0.02) + + +class TestNCECase1(TestNCE): + def set_data(self): + self.generate_data(10, 20, 10, 2, 5) + + +if __name__ == '__main__': + unittest.main() From e60eb1eacdac476b52cbd029660249fe709b7196 Mon Sep 17 00:00:00 2001 From: wanghaoshuang Date: Thu, 16 Nov 2017 00:45:36 +0800 Subject: [PATCH 04/67] fix unitest --- .../v2/{framework => fluid}/tests/test_nce.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) rename python/paddle/v2/{framework => fluid}/tests/test_nce.py (86%) diff --git a/python/paddle/v2/framework/tests/test_nce.py b/python/paddle/v2/fluid/tests/test_nce.py similarity index 86% rename from python/paddle/v2/framework/tests/test_nce.py rename to python/paddle/v2/fluid/tests/test_nce.py index 8b1e7a6bb5..82978f2d23 100644 --- a/python/paddle/v2/framework/tests/test_nce.py +++ b/python/paddle/v2/fluid/tests/test_nce.py @@ -55,10 +55,10 @@ class TestNCE(OpTest): 'sampled_labels': range(num_sampled_classes) } self.inputs = { - 'X': input, + 'Input': input, 'Label': labels, - 'W': weight, - 'B': bias, + 'Weight': weight, + 'Bias': bias, 'SampleWeight': sample_weight } @@ -66,11 +66,12 @@ class TestNCE(OpTest): self.generate_data(5, 5, 4, 1, 2) def compute(self): - out = nce(self.inputs['X'], self.inputs['W'], self.inputs['B'], - self.inputs['SampleWeight'], self.inputs['Label'], - self.attrs['num_classes'], self.attrs['num_sampled_classes']) + out = nce(self.inputs['Input'], self.inputs['Weight'], + self.inputs['Bias'], self.inputs['SampleWeight'], + self.inputs['Label'], self.attrs['num_classes'], + self.attrs['num_sampled_classes']) self.outputs = { - 'Out': out[0], + 'Cost': out[0], 'SampleLogits': out[1], 'SampleLabels': out[2] } @@ -84,7 +85,8 @@ class TestNCE(OpTest): self.check_output() def test_check_grad(self): - self.check_grad(["X", "W", "B"], "Out", max_relative_error=0.02) + self.check_grad( + ["Input", "Weight", "Bias"], "Cost", max_relative_error=0.02) class TestNCECase1(TestNCE): From e7cbde80c3fa3de277e74c3a7e80a2046ea9edf5 Mon Sep 17 00:00:00 2001 From: Luo Tao Date: Wed, 22 Nov 2017 13:57:28 +0800 Subject: [PATCH 05/67] simplify the CMakeLists.txt of trainer/tests --- paddle/trainer/tests/CMakeLists.txt | 51 +++++++++-------------------- 1 file changed, 15 insertions(+), 36 deletions(-) diff --git a/paddle/trainer/tests/CMakeLists.txt b/paddle/trainer/tests/CMakeLists.txt index 2739878b7f..9d33e20656 100644 --- a/paddle/trainer/tests/CMakeLists.txt +++ b/paddle/trainer/tests/CMakeLists.txt @@ -1,19 +1,17 @@ -################# test_Compare ############################ -add_unittest_without_exec(test_Compare - test_Compare.cpp) -add_test(NAME test_Compare - COMMAND ${PADDLE_SOURCE_DIR}/paddle/.set_python_path.sh -d ${PADDLE_SOURCE_DIR}/python - ${CMAKE_CURRENT_BINARY_DIR}/test_Compare - WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle/) +set(PYTHON_PATH + ${PADDLE_SOURCE_DIR}/paddle/.set_python_path.sh -d + ${PADDLE_SOURCE_DIR}/python/:${PADDLE_SOURCE_DIR}/paddle/trainer/tests) +function(trainer_test TARGET) + add_unittest_without_exec(${TARGET} ${TARGET}.cpp) + add_test(NAME ${TARGET} + COMMAND ${PYTHON_PATH} ${CMAKE_CURRENT_BINARY_DIR}/${TARGET} + WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle/) +endfunction() -################# test_Trainer ########################### -add_unittest_without_exec(test_Trainer - test_Trainer.cpp) -add_test(NAME test_Trainer - COMMAND ${PADDLE_SOURCE_DIR}/paddle/.set_python_path.sh -d ${PADDLE_SOURCE_DIR}/python/ - ${PADDLE_SOURCE_DIR}/paddle/.set_python_path.sh -d ${PADDLE_SOURCE_DIR}/python/ - ${CMAKE_CURRENT_BINARY_DIR}/test_Trainer - WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle/) +trainer_test(test_Compare) +trainer_test(test_PyDataProviderWrapper) +trainer_test(test_recurrent_machine_generation) +trainer_test(test_Trainer) ############### test_TrainerOnePass ########################## if(WITH_PYTHON) @@ -22,32 +20,13 @@ if(WITH_PYTHON) add_unittest_without_exec(test_TrainerOnePass test_TrainerOnePass.cpp) add_test(NAME test_TrainerOnePass - COMMAND ${PADDLE_SOURCE_DIR}/paddle/.set_python_path.sh -d - ${PADDLE_SOURCE_DIR}/python/:${PADDLE_SOURCE_DIR}/paddle/trainer/tests + COMMAND ${PYTHON_PATH} ${CMAKE_CURRENT_BINARY_DIR}/${TARGET} ${PADDLE_SOURCE_DIR}/paddle/.set_port.sh -p port ${CMAKE_CURRENT_BINARY_DIR}/test_TrainerOnePass WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle/) endif() -################# test_recurrent_machine_generation ############### -add_unittest_without_exec(test_recurrent_machine_generation - test_recurrent_machine_generation.cpp) -add_test(NAME test_recurrent_machine_generation - COMMAND ${PADDLE_SOURCE_DIR}/paddle/.set_python_path.sh -d ${PADDLE_SOURCE_DIR}/python/ - ${CMAKE_CURRENT_BINARY_DIR}/test_recurrent_machine_generation - WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle/) - -#################### test_PyDataProviderWrapper ######################### -add_unittest_without_exec(test_PyDataProviderWrapper - test_PyDataProviderWrapper.cpp) - -add_test(NAME test_PyDataProviderWrapper - COMMAND ${PADDLE_SOURCE_DIR}/paddle/.set_python_path.sh -d - ${PADDLE_SOURCE_DIR}/python/:${PADDLE_SOURCE_DIR}/paddle/trainer/tests - ${CMAKE_CURRENT_BINARY_DIR}/test_PyDataProviderWrapper - WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle/) - #################### test_config_parser ######################### add_test(NAME test_config_parser - COMMAND ${PADDLE_SOURCE_DIR}/paddle/.set_python_path.sh -d ${PADDLE_SOURCE_DIR}/python/ + COMMAND ${PYTHON_PATH} ${CMAKE_CURRENT_BINARY_DIR}/${TARGET} ${PYTHON_EXECUTABLE} ${PADDLE_SOURCE_DIR}/paddle/trainer/tests/config_parser_test.py WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle/) From b3e7c4bcf967f1927c6c5df9c3bf2526080df265 Mon Sep 17 00:00:00 2001 From: Luo Tao Date: Wed, 22 Nov 2017 16:29:11 +0800 Subject: [PATCH 06/67] simplify the CMakeLists.txt of gserver/tests --- paddle/gserver/tests/CMakeLists.txt | 81 ++++++++++------------------- 1 file changed, 28 insertions(+), 53 deletions(-) diff --git a/paddle/gserver/tests/CMakeLists.txt b/paddle/gserver/tests/CMakeLists.txt index c295ea19c9..f68abc1b9f 100644 --- a/paddle/gserver/tests/CMakeLists.txt +++ b/paddle/gserver/tests/CMakeLists.txt @@ -1,5 +1,4 @@ # gserver pacakge unittests - add_simple_unittest(test_LinearChainCRF) add_simple_unittest(test_RecurrentLayer) @@ -29,6 +28,26 @@ gserver_test(test_KmaxSeqScore) gserver_test(test_Expand) gserver_test(test_MaxPoolingWithMaskOutput) +set(PYTHON_PATH + ${PADDLE_SOURCE_DIR}/paddle/.set_python_path.sh -d + ${PADDLE_SOURCE_DIR}/python/:${PADDLE_SOURCE_DIR}/paddle/gserver/tests) +function(gserver_test2 TARGET) + add_unittest_without_exec(${TARGET} ${TARGET}.cpp) + add_test(NAME ${TARGET} + COMMAND ${PYTHON_PATH} ${CMAKE_CURRENT_BINARY_DIR}/${TARGET} + WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle/) +endfunction() + +gserver_test2(test_CompareTwoNets) +gserver_test2(test_PyDataProvider2) +if(WITH_PYTHON) + gserver_test2(test_PyDataProvider) +endif() +if(NOT MOBILE_INFERENCE) + # TODO(yuyang18): There is some bug in test_RecurrentGradientMachine, I will fix it. + gserver_test2(test_RecurrentGradientMachine) +endif() + ########## test_MKLDNN layers and activations ########## if(WITH_MKLDNN) add_unittest_without_exec(test_MKLDNN @@ -36,26 +55,14 @@ if(WITH_MKLDNN) MKLDNNTester.cpp LayerGradUtil.cpp) add_test(NAME test_MKLDNN - COMMAND .set_python_path.sh -d ${PADDLE_SOURCE_DIR}/python - ${CMAKE_CURRENT_BINARY_DIR}/test_MKLDNN + COMMAND ${PYTHON_PATH} ${CMAKE_CURRENT_BINARY_DIR}/test_MKLDNN WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle) endif() -############## test_PyDataProvider ######################## -if(WITH_PYTHON) - add_unittest_without_exec(test_PyDataProvider - test_PyDataProvider.cpp) - - add_test(NAME test_PyDataProvider - COMMAND .set_python_path.sh -d ./gserver/tests:${PADDLE_SOURCE_DIR}/python/ ${CMAKE_CURRENT_BINARY_DIR}/test_PyDataProvider - WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle) -endif() - ############### test_WarpCTCLayer ####################### if(NOT WITH_DOUBLE AND NOT MOBILE_INFERENCE) add_unittest_without_exec(test_WarpCTCLayer test_WarpCTCLayer.cpp) - add_test(NAME test_WarpCTCLayer COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test_WarpCTCLayer --warpctc_dir=${WARPCTC_LIB_DIR} WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle) @@ -66,57 +73,25 @@ if(NOT MOBILE_INFERENCE) add_unittest(test_Evaluator test_Evaluator.cpp) -############### test_RecurrentGradientMachine ############### - # TODO(yuyang18): There is some bug in test_RecurrentGradientMachine - # I will fix it. - add_unittest_without_exec(test_RecurrentGradientMachine - test_RecurrentGradientMachine.cpp) - add_test(NAME test_RecurrentGradientMachine - COMMAND .set_python_path.sh -d - ${PADDLE_SOURCE_DIR}/python:${PADDLE_SOURCE_DIR}/paddle/gserver/tests - ${CMAKE_CURRENT_BINARY_DIR}/test_RecurrentGradientMachine - WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle) - ############### test_NetworkCompare ############### add_unittest_without_exec(test_NetworkCompare test_NetworkCompare.cpp) if(WITH_GPU) - add_test(NAME test_NetworkCompare - COMMAND .set_python_path.sh -d ${PADDLE_SOURCE_DIR}/python ${CMAKE_CURRENT_BINARY_DIR}/test_NetworkCompare --use_gpu=true - WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle) + set(use_gpu true) else() - add_test(NAME test_NetworkCompare - COMMAND .set_python_path.sh -d ${PADDLE_SOURCE_DIR}/python ${CMAKE_CURRENT_BINARY_DIR}/test_NetworkCompare --use_gpu=false - WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle) + set(use_gpu false) endif() + add_test(NAME test_NetworkCompare + COMMAND ${PYTHON_PATH} ${CMAKE_CURRENT_BINARY_DIR}/test_NetworkCompare --use_gpu=${use_gpu} + WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle) endif() - -add_unittest_without_exec(test_PyDataProvider2 - test_PyDataProvider2.cpp) - -add_test(NAME test_PyDataProvider2 - COMMAND .set_python_path.sh -d ${PADDLE_SOURCE_DIR}/paddle/gserver/tests:${PADDLE_SOURCE_DIR}/python ${CMAKE_CURRENT_BINARY_DIR}/test_PyDataProvider2 - WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle -) - ################# test_CompareSparse ################## add_unittest_without_exec(test_CompareSparse test_CompareSparse.cpp) if(NOT ON_TRAVIS) add_test(NAME test_CompareSparse - COMMAND ${PADDLE_SOURCE_DIR}/paddle/.set_python_path.sh -d - ${PADDLE_SOURCE_DIR}/python:${PADDLE_SOURCE_DIR}/paddle/gserver/tests - ./.set_port.sh -p port -n 6 - ${CMAKE_CURRENT_BINARY_DIR}/test_CompareSparse + COMMAND ${PYTHON_PATH} ./.set_port.sh -p port -n 6 + ${CMAKE_CURRENT_BINARY_DIR}/test_CompareSparse WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle/) endif() - -################ test_CompareTwoNets ###################### -add_unittest_without_exec(test_CompareTwoNets - test_CompareTwoNets.cpp) -add_test(NAME test_CompareTwoNets - COMMAND ${PADDLE_SOURCE_DIR}/paddle/.set_python_path.sh -d - ${PADDLE_SOURCE_DIR}/python:${PADDLE_SOURCE_DIR}/paddle/gserver/tests - ${CMAKE_CURRENT_BINARY_DIR}/test_CompareTwoNets - WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle/) From 4599aea7c2fe90febb0772cda3ceeb50b1953ce3 Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Tue, 21 Nov 2017 18:06:31 +0800 Subject: [PATCH 07/67] polish mkldnn doc --- doc/design/mkldnn/README.MD | 170 +++++++++++++++++++------- doc/design/mkldnn/image/engine.png | Bin 0 -> 36180 bytes doc/design/mkldnn/image/gradients.png | Bin 0 -> 57433 bytes doc/design/mkldnn/image/layers.png | Bin 0 -> 57028 bytes doc/design/mkldnn/image/matrix.png | Bin 0 -> 19755 bytes 5 files changed, 127 insertions(+), 43 deletions(-) create mode 100644 doc/design/mkldnn/image/engine.png create mode 100644 doc/design/mkldnn/image/gradients.png create mode 100644 doc/design/mkldnn/image/layers.png create mode 100644 doc/design/mkldnn/image/matrix.png diff --git a/doc/design/mkldnn/README.MD b/doc/design/mkldnn/README.MD index ec6d468183..7c863197e7 100644 --- a/doc/design/mkldnn/README.MD +++ b/doc/design/mkldnn/README.MD @@ -1,21 +1,32 @@ # Intel® MKL-DNN on PaddlePaddle: Design Doc -我们计划将Intel深度神经网络数学库(**MKL-DNN**\[[1](#references)\])集成到PaddlePaddle,充分展现英特尔平台的优势,有效提升PaddlePaddle在英特尔架构上的性能。 +我们计划将英特尔深度神经网络数学库[Intel MKL-DNN](https://github.com/01org/mkl-dnn) +(Intel Math Kernel Library for Deep Neural Networks)集成到PaddlePaddle, +充分展现英特尔平台的优势,有效提升PaddlePaddle在英特尔架构上的性能。 -我们短期内的基本目标是: +
+
+Figure 1. PaddlePaddle on IA +
-- 完成常用layer的MKL-DNN实现。 +近期目标 + +- 完成常用Layer的MKL-DNN实现。 - 完成常见深度神经网络VGG,GoogLeNet 和 ResNet的MKL-DNN实现。 +目前的优化,主要针对PaddlePaddle在重构之前的代码框架以及V1的API。 +具体的完成状态可以参见[这里](https://github.com/PaddlePaddle/Paddle/projects/21)。 ## Contents - [Overview](#overview) - [Actions](#actions) - [CMake](#cmake) + - [Matrix](#matrix) - [Layers](#layers) - [Activations](#activations) - - [Weights](#weights) + - [Parameters](#parameters) + - [Gradients](#gradients) - [Unit Tests](#unit-tests) - [Protobuf Messages](#protobuf-messages) - [Python API](#python-api) @@ -26,42 +37,114 @@ ## Overview -我们会把MKL-DNN作为第三方库集成进PaddlePaddle,整体框架图 +我们会把MKL-DNN会作为第三方库集成进PaddlePaddle,与其他第三方库一样,会在编译PaddlePaddle的时候下载并编译MKL-DNN。 + +同时,为了进一步提升PaddlePaddle在基本数学运算的计算速度,我们也将MKLML即(MKL small library\[[1](#references)\]) +作为另一个第三方库集成进PaddlePaddle,它只会包括生成好的动态库和头文件。 +MKLML可以与MKL-DNN共同使用,以此达到最好的性能。 +
-
-Figure 1. PaddlePaddle on IA. +
+Figure 2. PaddlePaddle with MKL Engines
## Actions -我们把集成方案大致分为了如下几个方面。 + +添加的相关文件和目录结构如下: + +```txt +PaddlePaddle/Paddle +├── ... +├── cmake/ +│ ├── external/ +│ │ ├── ... +│ │ ├── mkldnn.cmake +│ │ └── mklml.cmake +└── paddle/ + ├── ... + ├── math/ + │ ├── ... + │ └── MKLDNNMatrix.* + └── gserver/ + ├── ... + ├── layers/ + │ ├── ... + │ └── MKLDNN*Layer.* + ├── activations/ + │ ├── ... + │ └── MKLDNNActivations.* + └── tests/ + ├── ... + ├── MKLDNNTester.* + └── test_MKLDNN.cpp +``` ### CMake -我们会在`CMakeLists.txt`中会给用户添加一个`WITH_MKL`的开关,他是负责`WITH_MKLML`和`WITH_MKLDNN`的总开关。 +在`CMakeLists.txt`中提供一个与MKL有关的总开关:`WITH_MKL`,它负责决定编译时是否使用MKLML和MKL-DNN -当打开`WITH_MKL`时,会开启MKLML的功能,作为PaddlePaddle的CBLAS和LAPACK库,同时会开启Intel OpenMP用于提高MKLML的性能。 如果系统支持AVX2指令集及以上,同时会开启MKL-DNN功能。 +- `WITH_MKLML` 控制是否使用MKLML库。 +当打开`WITH_MKL`时,会自动使用MKLML库作为PaddlePaddle的CBLAS和LAPACK库,同时会开启Intel OpenMP用于提高MKLML的性能。 +- `WITH_MKLDNN` 控制是否使用MKL-DNN。 +当开启`WITH_MKL`时,会自动根据硬件配置[[2](#references)]选择是否编译MKL-DNN。 -当关闭`WITH_MKL`时,MKLML和MKL-DNN功能会同时关闭。 +### Matrix +目前在PaddlePaddle中数据都是以`nchw`的格式存储,但是在MKL-DNN中的排列方式不止这一种。 +所以我们定义了一个`MKLDNNMatrix`用于管理MKL-DNN数据的不同格式以及相互之间的转换。 -所以,我们会在`cmake/external`目录新建`mkldnn.cmake`和`mklml.cmake`文件,它们会在编译PaddlePaddle的时候下载对应的软件包,并放到PaddlePaddle的third party目录中。 +
+
+Figure 3. MKLDNNMatrix +
### Layers -所有MKL-DNN相关的C++ layers,都会按照PaddlePaddle的目录结构存放在 -`paddle/gserver/layers`中,并且文件名都会一以*MKLDNN*开头。 +所有MKL-DNN的Layers都会继承于`MKLDNNLayer`,该类继承于PaddlePaddle的基类`Layer`。 +在`MKLDNNLayer`中会提供一些必要的接口和函数,并且会写好`forward`和`backward`的基本逻辑, +子类只需要使用定义好的接口,实现具体的函数功能即可。 + +
+
+Figure 4. MKLDNNLayer +
+ +每个`MKLDNNlayer`都会有`inVal_`,`inGrad_`,`outVal_`和`outGrad_`的`MKLDNNMatrix`, +分别代表input value, input gradient,output value和output gradient。 +它们会存放MKL-DNN用到的internal memory,同时还会定义以*ext*开头的`MKLDNNMatrix`(表示external的memory)。 +他们主要是当数据格式与PaddlePaddle默认的`nchw`格式不匹配时,用于转换内存的工作。 -所有MKL-DNN的layers都会继承于一个叫做`MKLDNNLayer`的父类,该父类继承于PaddlePaddle的基类`Layer`。 +必要的转换函数也会在`MKLDNNLayer`中提前定义好(具体包括reset input、output的value和grad), +这些函数会根据输入参数重新设置internal和external的memory(当然这两者也可以相等,即表示不需要转换), +每个`MKLDNNlayer`的子类只需要使用internal的memory就可以了,所有external的转换工作都会在reset函数中都准备好。 -在`MKLDNNLayer`中会提供一些必要的接口和函数,并且会写好`forward`和`backward`的基本逻辑。部分函数定义为纯虚函数,子类只需要实现这些函数即可。 +一般来说,每个`MKLDNNLayer`中的`extOutVal_`和`extOutGrad_`必须分别与`output_.value`和`output_.grad`共享内存, +因为PaddlePaddle的activation会直接使用`output_.value`和`output_.grad`, +如果不需要external的buffer用于转换,那么internal的buffer也会与它们共享内存。 ### Activations -由于在PaddlePaddle中,激活函数是独立于layer概念的,所以会在`paddle/gserver/activations`目录下添加`MKLDNNActivation.h`和`MKLDNNActivation.cpp`文件用于定义和使用MKL-DNN的接口。 +在重构前的PaddlePaddle中,激活函数是独立于`Layer`的概念,并且输入输出都是公用一块内存, +所以添加了对应的`MKLDNNActivation`来实现,方式类似于`MKLDNNLayer`。 + +### Parameters +对于有参数的层,我们会保证`MKLDNNLayer`使用的参数与PaddlePaddle申请的buffer公用一块内存。 +如果存在数据排列格式不一样的情况时,我们会在网络训练之前把格式转换为MKL-DNN希望的格式, +在训练结束的时候再保存为PaddlePaddle的格式,但是整个训练过程中不需要任何转换。 +这样既使得最终保存的参数格式与PaddlePaddle一致,又可以避免不必要的转换。 + +### Gradients +由于MKL-DNN的操作都是直接覆盖的形式,也就是说输出的结果不会在原来的数据上累加, +这样带来的好处就是不需要一直清空memory,节省了不必要的操作。 +但是注意的是,当网络出现分支且在`backward`的时候,需要累加不同Layer传过来的梯度。 +所以在`MKLDNNlayer`中实现了一个merge的方法,此时每个小分支的`Input Gradient` +会先临时保存在`MKLDNNMatrix`中,由分支处的Layer负责求和,并把结果放到当前层的`output_.grad`中。 +所以整体上,在实现每个子类的时候就不需要关心分支的事情了。 -### Weights -由于有些layer是含有参数的,我们会尽量让MKL-DNN的参数与PaddlePaddle中`parameter`共享一块内存。 -同时,由于MKL-DNN在训练时使用的参数layout可能与PaddlePaddle默认的`nchw`不一致,我们会在网络训练的开始和结束时分别转换这个layout,使得最终保存的参数格式与PaddlePaddle一致。 +
+
+Figure 5. Merge Gradients +
### Unit Tests -会在`paddle/gserver/test`目录下添加`test_MKLDNN.cpp`和`MKLDNNTester.*`用于MKL-DNN的测试。 -测试分为每个layer(或activation)的单元测试和简单网络的整体测试。 +我们会添加`test_MKLDNN.cpp`和`MKLDNNTester.*`用于MKL-DNN的测试。 +测试分为每个Layer(或Activation)的单元测试和简单网络的整体测试。 每个测试会对比PaddlePaddle中CPU算出的结果与MKL-DNN的结果,小于某个比较小的阈值认为通过。 ### Protobuf Messages @@ -80,41 +163,42 @@ if use_mkldnn self.layer_type = mkldnn_* ``` -所有MKL-DNN的layer type会以*mkldnn_*开头,以示区分。 +所有MKL-DNN的`layer_type`会以*mkldnn_*开头,这些会在`MKLDNN*Layer`注册layer的时候保证,以示区分。 -并且可能在`python/paddle/trainer_config_helper`目录下的`activations.py `和`layers.py`里面添加必要的MKL-DNN的接口。 +同时,会在`paddle/utils.Flags`中添加一个`use_mkldnn`的flag,用于选择是否使用MKL-DNN的相关功能。 ### Demos - -会在`v1_api_demo`目录下添加一个`mkldnn`的文件夹,里面放入一些用于MKL-DNN测试的demo脚本。 +可能会在`v1_api_demo`目录下添加一个`mkldnn`的文件夹,里面放入一些用于MKL-DNN测试的demo脚本。 ### Benchmarking -会添加`benchmark/paddle/image/run_mkldnn.sh`,用于测试使用MKL-DNN之后的性能。 +会添加`benchmark/paddle/image/run_mkldnn.sh`,用于测试和对比,在使用MKL-DNN前后的性能。 ### Others -1. 如果在使用MKL-DNN的情况下,会把CPU的Buffer对齐为64。 +1. 如果在使用MKL-DNN的情况下,会把CPU的Buffer对齐为4096,具体可以参考MKL-DNN中的[memory](https://github.com/01org/mkl-dnn/blob/master/include/mkldnn.hpp#L673)。 2. 深入PaddlePaddle,寻找有没有其他可以优化的可能,进一步优化。比如可能会用OpenMP改进SGD的更新性能。 ## Design Concerns -为了更好的符合PaddlePaddle的代码风格\[[2](#references)\],同时又尽可能少的牺牲MKL-DNN的性能\[[3](#references)\]。 +为了更好的符合PaddlePaddle的代码风格\[[3](#references)\],同时又尽可能少的牺牲MKL-DNN的性能\[[4](#references)\]。 我们总结出一些特别需要注意的点: -1. 使用**deviceId_**。为了尽可能少的在父类Layer中添加变量或者函数,我们决定使用已有的`deviceId_`变量来区分layer的属性,定义`-2`为`MKLDNNLayer`特有的设备ID。 -2. 重写父类Layer的**init**函数,修改`deviceId_`为`-2`,代表这个layer是用于跑在MKL-DNN的环境下。 -3. 创建`MKLDNNMatrix`,同时继承`CpuMatrix`和`mkldnn::memory`。用于管理MKL-DNN会用到的相关memory函数、接口以及会用的到格式信息。 -4. 创建`MKLDNNBase`,定义一些除了layer和memory相关的类和函数。包括MKL-DNN会用到`MKLDNNStream`和`CPUEngine`,和未来可能还会用到`FPGAEngine`等。 -5. 每个`MKLDNNlayer`都会有`inVal_`,`inGrad_`,`outVal_`和`outGrad_`,分别代表input value, input gradient,output value和output gradient。他们会存放MKL-DNN用到的internal memory。同时还会定义以*ext*开头的`MKLDNNMatrix`(表示external的memory),主要是在格式与PaddlePaddle默认的`nchw`格式不匹配时,用于转换内存的工作。必要的转换函数也会在`MKLDNNLayer`中提前定义好,每个子类只需要调用定义好的reset buffer函数即可。 -6. 每个`MKLDNNlayer`的resetbuffer相关的函数(包括reset input、output的Value和grad),他们会根据输入参数reset internal和external的memory,当然这两者也可以相等,即表示不需要转换。只需要把握一个原则,每个`MKLDNNlayer`的子类,只需要使用internal的memory就可以了,所有external的转换工作在父类的reset函数中都提前准备好了。 -7. 一般来说,external的memory会尽量与PaddlePaddle中的`value`和`grad`共享内存。同时每个`MKLDNNLayer`中的external output value和gradient(也就是`extOutVal_`和`extOutGrad_`)必须分别与`output_.value`和`output_.grad`共享内存,因为PaddlePaddle的activation会直接使用`output_.value`和`output_.grad`。如果不需要external的buffer用于转换,那么internal的buffer也会与他们共享内存。 -8. 如果MKL-DNN layer的后面接有cpu device,那么就会使`output_.value`与`extOutVal_`共享内存,同时数据格式就是`nchw`,这样下一个cpu device就能拿到正确的数据。在有cpu device的时候,external的memory的格式始终是`nchw`或者`nc`。 -9. 由于MKL-DNN的输出操作都是覆盖data的,不是在原来的数据上累加,所以当网络出现分支时,在`backward`时会需要merge不同layer的梯度。`MKLDNNlayer`中会实现merge的方法,此时每个小分支的input gradient会先临时保存在一个`MKLDNNMatrix`中,由分支处的layer负责求和,并把结果放到这个layer的`output_.grad`中。所以整体上,每个子类并不会需要关心分支的事情,也是在父类都实现好了。 -10. 在原来的`FLAGS`中添加一个`use_mkldnn`的flag,用于选择是否使用MKL-DNN的相关功能。 +1. 使用**deviceId_**。为了尽可能少的在父类Layer中添加变量或者函数, +我们决定使用已有的`deviceId_`变量来区分layer的属性,定义`-2`为`MKLDNNLayer`特有的设备ID。 +2. 重写父类Layer的**init**函数,修改`deviceId_`为`-2`,代表这个layer是用于跑在MKL-DNN的环境下。 +3. 创建`MKLDNNBase`,定义一些除了layer和memory相关的类和函数。 +包括MKL-DNN会用到`MKLDNNStream`和`CPUEngine`,和未来可能还会用到`FPGAEngine`等。 +4. 如果MKL-DNN layer的后面接有cpu device,那么就会使`output_.value`与`extOutVal_`共享内存, +同时数据格式就是`nchw`,这样下一个cpu device就能拿到正确的数据。 +在有cpu device的时候,external的memory的格式始终是`nchw`或者`nc`。 ## References - -1. [Intel Math Kernel Library for Deep Neural Networks (Intel MKL-DNN)](https://github.com/01org/mkl-dnn "Intel MKL-DNN") -2. [原来的方案](https://github.com/PaddlePaddle/Paddle/pull/3096)会引入**nextLayer**的信息。但是在PaddlePaddle中,无论是重构前的layer还是重构后的op,都不会想要知道next layer/op的信息。 -3. MKL-DNN的高性能格式与PaddlePaddle原有的`NCHW`不同(PaddlePaddle中的CUDNN部分使用的也是`NCHW`,所以不存在这个问题),所以需要引入一个转换方法,并且只需要在必要的时候转换这种格式,才能更好的发挥MKL-DNN的性能。 +1. [MKL small library](https://github.com/01org/mkl-dnn#linking-your-application)是[Intel MKL](https://software.intel.com/en-us/mkl)的一个子集。 +主要包括了深度学习相关的数学原语与操作,一般由MKL-DNN在发布[新版本](https://github.com/01org/mkl-dnn/releases)时一起更新。 +2. [MKL-DNN System Requirements](https://github.com/01org/mkl-dnn#system-requirements)。 +目前在PaddlePaddle中,仅会在支持AVX2指令集及以上的机器才使用MKL-DNN。 +3. [原来的方案](https://github.com/PaddlePaddle/Paddle/pull/3096)会引入**nextLayer**的信息。 +但是在PaddlePaddle中,无论是重构前的layer还是重构后的op,都不会想要知道next layer/op的信息。 +4. MKL-DNN的高性能格式与PaddlePaddle原有的`NCHW`不同(PaddlePaddle中的cuDNN部分使用的也是`NCHW`,所以不存在这个问题)。 +所以需要引入一个转换方法,并且只需要在必要的时候转换这种格式,才能更好的发挥MKL-DNN的性能。 diff --git a/doc/design/mkldnn/image/engine.png b/doc/design/mkldnn/image/engine.png new file mode 100644 index 0000000000000000000000000000000000000000..65bbb41fbb389ff5f7906b0284ada77ac2dc4ec9 GIT binary patch literal 36180 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 literal 0 HcmV?d00001 diff --git a/doc/design/mkldnn/image/layers.png b/doc/design/mkldnn/image/layers.png new file mode 100644 index 0000000000000000000000000000000000000000..e65e1aeca47cd2f0c5289d0bb209ef394545bd31 GIT binary patch literal 57028 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 bXvf(?U#`U2=OD4O0xZmI|Ej&>`RM-vK7}V@ literal 0 HcmV?d00001 From 6c5f928a3e099eb787111c8fe5120118ef2e5155 Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Mon, 27 Nov 2017 14:27:25 +0800 Subject: [PATCH 08/67] enable inference benchmark --- benchmark/paddle/image/googlenet.py | 2 +- benchmark/paddle/image/resnet.py | 2 +- benchmark/paddle/image/run_mkldnn.sh | 69 ++++++++++++++++++++++++++-- benchmark/paddle/image/vgg.py | 2 +- 4 files changed, 68 insertions(+), 7 deletions(-) diff --git a/benchmark/paddle/image/googlenet.py b/benchmark/paddle/image/googlenet.py index a88ecac67d..5b1f0ca006 100644 --- a/benchmark/paddle/image/googlenet.py +++ b/benchmark/paddle/image/googlenet.py @@ -9,7 +9,7 @@ use_gpu = get_config_arg('use_gpu', bool, True) args = {'height': height, 'width': width, 'color': True, 'num_class': num_class} define_py_data_sources2( - "train.list", None, module="provider", obj="process", args=args) + "train.list", "test.list", module="provider", obj="process", args=args) settings( batch_size=batch_size, diff --git a/benchmark/paddle/image/resnet.py b/benchmark/paddle/image/resnet.py index 6ae1857642..f8c1c2df88 100644 --- a/benchmark/paddle/image/resnet.py +++ b/benchmark/paddle/image/resnet.py @@ -10,7 +10,7 @@ is_test = get_config_arg("is_test", bool, False) args = {'height': height, 'width': width, 'color': True, 'num_class': num_class} define_py_data_sources2( - "train.list", None, module="provider", obj="process", args=args) + "train.list", "test.list", module="provider", obj="process", args=args) settings( batch_size=batch_size, diff --git a/benchmark/paddle/image/run_mkldnn.sh b/benchmark/paddle/image/run_mkldnn.sh index f768f6c29a..c78079fa45 100755 --- a/benchmark/paddle/image/run_mkldnn.sh +++ b/benchmark/paddle/image/run_mkldnn.sh @@ -8,13 +8,13 @@ function train() { use_mkldnn=$4 if [ $4 == "True" ]; then thread=1 - log="logs/${topology}-${layer_num}-mkldnn-${bs}.log" + log="logs/train-${topology}-${layer_num}-mkldnn-${bs}.log" elif [ $4 == "False" ]; then thread=`nproc` # each trainer_count use only 1 core to avoid conflict - log="logs/${topology}-${layer_num}-${thread}mklml-${bs}.log" + log="logs/train-${topology}-${layer_num}-${thread}mklml-${bs}.log" else - echo "Wrong input $3, use True or False." + echo "Wrong input $4, use True or False." exit 0 fi args="batch_size=${bs},layer_num=${layer_num}" @@ -30,13 +30,74 @@ function train() { 2>&1 | tee ${log} } -if [ ! -d "train.list" ]; then +function test() { + unset OMP_NUM_THREADS MKL_NUM_THREADS OMP_DYNAMIC KMP_AFFINITY + topology=$1 + layer_num=$2 + bs=$3 + use_mkldnn=$4 + if [ $4 == "True" ]; then + thread=1 + log="logs/test-${topology}-${layer_num}-mkldnn-${bs}.log" + elif [ $4 == "False" ]; then + thread=`nproc` + if [ $thread -gt $bs ]; then + thread=$bs + fi + log="logs/test-${topology}-${layer_num}-${thread}mklml-${bs}.log" + else + echo "Wrong input $4, use True or False." + exit 0 + fi + + models_in="models/${topology}-${layer_num}/pass-00000/" + if [ ! -d $models_in ]; then + echo "Training model ${topology}_${layer_num}" + paddle train --job=train \ + --config="${topology}.py" \ + --use_mkldnn=True \ + --use_gpu=False \ + --trainer_count=1 \ + --num_passes=1 \ + --save_dir="models/${topology}-${layer_num}" \ + --config_args="batch_size=128,layer_num=${layer_num}" \ + > /dev/null 2>&1 + echo "Done" + fi + paddle train --job=test \ + --config="${topology}.py" \ + --use_mkldnn=$use_mkldnn \ + --use_gpu=False \ + --trainer_count=$thread \ + --log_period=10 \ + --config_args="batch_size=${bs},layer_num=${layer_num},is_test=True" \ + --init_model_path=$models_in \ + 2>&1 | tee ${log} +} + +if [ ! -f "train.list" ]; then echo " " > train.list fi +if [ ! -f "test.list" ]; then + echo " " > test.list +fi if [ ! -d "logs" ]; then mkdir logs fi +if [ ! -d "models" ]; then + mkdir -p models +fi + +# inference benchmark +for use_mkldnn in True False; do + for batchsize in 1 2 4 8 16; do + test googlenet v1 $batchsize $use_mkldnn + test resnet 50 $batchsize $use_mkldnn + test vgg 19 $batchsize $use_mkldnn + done +done +# training benchmark for use_mkldnn in True False; do for batchsize in 64 128 256; do train vgg 19 $batchsize $use_mkldnn diff --git a/benchmark/paddle/image/vgg.py b/benchmark/paddle/image/vgg.py index 420884ed8e..97f4dbe0e1 100644 --- a/benchmark/paddle/image/vgg.py +++ b/benchmark/paddle/image/vgg.py @@ -9,7 +9,7 @@ layer_num = get_config_arg('layer_num', int, 19) args = {'height': height, 'width': width, 'color': True, 'num_class': num_class} define_py_data_sources2( - "train.list", None, module="provider", obj="process", args=args) + "train.list", "test.list", module="provider", obj="process", args=args) settings( batch_size=batch_size, From bf360c7746db9a5084fb58dc73452cc19048f54a Mon Sep 17 00:00:00 2001 From: tangwei12 Date: Mon, 27 Nov 2017 22:19:59 +0800 Subject: [PATCH 09/67] fix pipe_reader unimport packages --- python/paddle/v2/reader/decorator.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/python/paddle/v2/reader/decorator.py b/python/paddle/v2/reader/decorator.py index 0695542690..7e457f987d 100644 --- a/python/paddle/v2/reader/decorator.py +++ b/python/paddle/v2/reader/decorator.py @@ -14,13 +14,16 @@ __all__ = [ 'map_readers', 'buffered', 'compose', 'chain', 'shuffle', - 'ComposeNotAligned', 'firstn', 'xmap_readers' + 'ComposeNotAligned', 'firstn', 'xmap_readers', 'pipe_reader' ] +from threading import Thread +import subprocess + +from Queue import Queue import itertools import random -from Queue import Queue -from threading import Thread +import zlib def map_readers(func, *readers): From ea7359c60bdf6062b1296f471f50cbeaf8da243e Mon Sep 17 00:00:00 2001 From: wanghaoshuang Date: Tue, 28 Nov 2017 12:47:17 +0800 Subject: [PATCH 10/67] Refine code and comments 1. Remove checking for num_neg_samples. 2. Fix dims of Output(Cost) and Input(Bias). 3. Renamed num_sampled_classes to num_neg_samples. 4. Add TODO for add more distribution sampler. 5. Init grad_data of bias by zero. 6. Refine comments. 7. Register a kernel for type double. --- paddle/operators/nce_op.cc | 95 +++++++++++++++--------- paddle/operators/nce_op.h | 15 ++-- python/paddle/v2/fluid/tests/test_nce.py | 14 ++-- 3 files changed, 77 insertions(+), 47 deletions(-) diff --git a/paddle/operators/nce_op.cc b/paddle/operators/nce_op.cc index c365d5d922..bb9346b134 100644 --- a/paddle/operators/nce_op.cc +++ b/paddle/operators/nce_op.cc @@ -1,16 +1,16 @@ /* 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 +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 + 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. */ +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/nce_op.h" @@ -39,25 +39,25 @@ class NCEOp : public framework::OperatorWithKernel { PADDLE_ENFORCE_EQ(ctx->GetInputDim("Weight")[0], ctx->GetInputDim("Bias")[0]); } - auto num_sampled_classes = ctx->Attrs().Get("num_sampled_classes"); - auto num_classes = ctx->Attrs().Get("num_classes"); + auto num_neg_samples = ctx->Attrs().Get("num_neg_samples"); + auto num_total_classes = ctx->Attrs().Get("num_total_classes"); std::vector sampled_labels = ctx->Attrs().Get>("sampled_labels"); - PADDLE_ENFORCE_EQ(num_classes, ctx->GetInputDim("Weight")[0]); - PADDLE_ENFORCE_LT(num_sampled_classes, num_classes); + PADDLE_ENFORCE_EQ(num_total_classes, ctx->GetInputDim("Weight")[0]); if (sampled_labels.size() > 0) { PADDLE_ENFORCE_EQ(sampled_labels.size(), - static_cast(num_sampled_classes)); + static_cast(num_neg_samples)); } // set dims of output(Out) std::vector out_dims; out_dims.push_back(x_dims[0]); + out_dims.push_back(1); ctx->SetOutputDim("Cost", framework::make_ddim(out_dims)); // set dims of output(SampleOut) std::vector sample_out_dims; sample_out_dims.push_back(x_dims[0]); - sample_out_dims.push_back(num_sampled_classes + num_true_classes); + sample_out_dims.push_back(num_neg_samples + num_true_classes); ctx->SetOutputDim("SampleLogits", framework::make_ddim(sample_out_dims)); ctx->SetOutputDim("SampleLabels", framework::make_ddim(sample_out_dims)); } @@ -76,34 +76,59 @@ class NCEOpMaker : public framework::OpProtoAndCheckerMaker { NCEOpMaker(framework::OpProto* proto, framework::OpAttrChecker* op_checker) : OpProtoAndCheckerMaker(proto, op_checker) { AddInput("Input", "(Tensor) A tensor of shape [batch_size, dim]."); - AddInput("Label", - "(Tensor) A tensor of shape [batch_size, num_true_class]. " - "'num_true_class' is the number of target class in each sample."); + AddInput( + "Label", + "(Tensor) A tensor of shape [batch_size, num_true_class]. " + "'num_true_class' is the number of target classes in each sample." + "The number of target classes per sample should be same. " + "If you have a variable number of target classes, " + "you can pad them out to a constant number by either repeating them" + " or by padding with an otherwise unused class.)"); AddInput("Weight", "(Tensor) A tensor of shape [num_class, dim]. 'num_class' is the " "total number of class."); - AddInput("Bias", - "(Tensor) A tensor of shape [num_class]. 'num_class' is the total " - "number of class. It is a dispensable input.") + AddInput( + "Bias", + "(Tensor) A tensor of shape [num_class, 1]. 'num_class' is the total " + "number of class. It is a dispensable input.") .AsDispensable(); AddInput("SampleWeight", - "(Tensor) A tensor of shape [batch_size] storing a weight for " + "(Tensor) A tensor of shape [batch_size, 1] storing a weight for " "each sample. And it is a dispensable input. The default value of " "sample is 1.") .AsDispensable(); AddOutput("Cost", - "(Tensor) A tensor of shape [batch_size]. Cost of samples."); - AddOutput("SampleLogits", "An intermediate tensor.").AsIntermediate(); - AddOutput("SampleLabels", "An intermediate tensor.").AsIntermediate(); - AddAttr("num_classes", "Total number of classes."); - AddAttr("num_sampled_classes", "The number of negative classes.") + "(Tensor) A tensor of shape [batch_size, 1]. Cost of samples."); + AddOutput("SampleLogits", + "An intermediate tensor of shape[batch_size, num_neg_samples + " + "num_pos_samples]." + "This tensor is output of forward kernel and used in backward " + "kernel to compute grads." + "Given X is the dot product of input tensor and sampled labels' " + "weights." + "Then 'SampleLogits' is sigmoid(X).") + .AsIntermediate(); + AddOutput("SampleLabels", + "An intermediate tensor of shape[batch_size, num_neg_samples + " + "num_pos_samples]." + "This tensor is output of forward kernel and used in backward " + "kernel to compute grads." + "") + .AsIntermediate(); + AddAttr("num_total_classes", + "Total number of classes in all samples."); + AddAttr("num_neg_samples", + "The number of negative classes. The default value is 10.") .SetDefault(10); - AddAttr>("sampled_labels", ""); + AddAttr>("custom_neg_classes", + "This attribute only be used in unitest. Classes " + "in this list wiil be used as negative classes " + "for every samples. Under normal conditions, " + "user should avoid setting this attribute."); AddComment(R"DOC( -Computes and returns the noise-contrastive estimation training loss. +Compute and return the noise-contrastive estimation training loss. See [Noise-contrastive estimation: A new estimation principle for unnormalized statistical models](http://www.jmlr.org/proceedings/papers/v9/gutmann10a/gutmann10a.pdf). -By default this uses a uniform distribution for sampling. -The number of target classes per example should be same. If you have a variable number of target classes, you can pad them out to a constant number by either repeating them or by padding with an otherwise unused class. +By default this operator uses a uniform distribution for sampling. )DOC"); } }; @@ -119,7 +144,7 @@ class NCEOpGrad : public framework::OperatorWithKernel { PADDLE_ENFORCE(ctx->HasInput("SampleLogits")); PADDLE_ENFORCE(ctx->HasInput("SampleLabels")); PADDLE_ENFORCE(ctx->HasInput(framework::GradVarName("Cost")), - "The input(Out@GRAD) should not be null"); + "The input(Out@GRAD) should not be null."); auto x_dims = ctx->GetInputDim("Input"); auto x_grad_name = framework::GradVarName("Input"); @@ -154,6 +179,8 @@ class NCEOpGrad : public framework::OperatorWithKernel { namespace ops = paddle::operators; REGISTER_OP(nce, ops::NCEOp, ops::NCEOpMaker, nce_grad, ops::NCEOpGrad); -REGISTER_OP_CPU_KERNEL(nce, ops::NCEKernel); +REGISTER_OP_CPU_KERNEL(nce, ops::NCEKernel, + ops::NCEKernel); REGISTER_OP_CPU_KERNEL(nce_grad, - ops::NCEGradKernel); + ops::NCEGradKernel, + ops::NCEGradKernel); diff --git a/paddle/operators/nce_op.h b/paddle/operators/nce_op.h index 3017bccdca..c41393d260 100644 --- a/paddle/operators/nce_op.h +++ b/paddle/operators/nce_op.h @@ -22,7 +22,7 @@ namespace paddle { namespace operators { -using Tensor = framework::Tensor; +using framework::Tensor; template @@ -35,8 +35,8 @@ void PrepareSamples(const framework::ExecutionContext& context) { auto label_dims = label->dims(); int num_classes = context.Attr("num_classes"); // for unitest - std::vector sampled_labels = - context.Attr>("sampled_labels"); + std::vector custom_neg_classes = + context.Attr>("custom_neg_classes"); // random machine std::random_device rd; std::mt19937 rng(rd()); @@ -54,12 +54,13 @@ void PrepareSamples(const framework::ExecutionContext& context) { for (; j < num_label; ++j) { sample_labels_data[index++] = label_data[i * num_label + j]; } - if (sampled_labels.size() > 0) { - for (auto label : sampled_labels) { + if (custom_neg_classes.size() > 0) { + for (auto label : custom_neg_classes) { sample_labels_data[index++] = label; } } else { for (; j < sample_labels_dims[1]; ++j) { + // TODO: support more distribution sampling sample_labels_data[index++] = rand(rng); } } @@ -176,6 +177,7 @@ class NCEGradKernel : public framework::OpKernel { auto d_bias = context.Output(framework::GradVarName("Bias")); if (d_bias != nullptr) { T* d_bias_data = d_bias->mutable_data(context.GetPlace()); + std::fill(d_bias_data, d_bias_data + d_bias->numel(), 0.0); for (size_t i = 0; i < sample_labels->numel(); ++i) { d_bias_data[sample_labels_data[i]] += sample_grad_data[i]; } @@ -183,7 +185,8 @@ class NCEGradKernel : public framework::OpKernel { // get d_w auto d_w = context.Output(framework::GradVarName("Weight")); if (d_w != nullptr) { - d_w->mutable_data(context.GetPlace()); + auto d_w_data = d_w->mutable_data(context.GetPlace()); + std::fill(d_w_data, d_w_data + d_w->numel(), 0.0); auto d_w_matrix = EigenMatrix::From(*d_w); auto x_matrix = EigenMatrix::From(*(context.Input("Input"))); for (size_t i = 0; i < sample_labels->numel(); ++i) { diff --git a/python/paddle/v2/fluid/tests/test_nce.py b/python/paddle/v2/fluid/tests/test_nce.py index 82978f2d23..6cbf468e0a 100644 --- a/python/paddle/v2/fluid/tests/test_nce.py +++ b/python/paddle/v2/fluid/tests/test_nce.py @@ -18,25 +18,25 @@ def nce(input, weight, bias, sample_weight, labels, num_classes, samples.append((i, num, False, w)) sample_labels.append(num) # forward bias - sampleOut = np.zeros(len(samples)).astype(np.float32) + sample_out = np.zeros(len(samples)).astype(np.float32) if bias is not None: for i in range(len(samples)): - sampleOut[i] = bias[samples[i][1]] + sample_out[i] = bias[samples[i][1]] # forward weight for i in range(len(samples)): - sampleOut[i] += np.dot(input[samples[i][0]], weight[samples[i][1]]) + sample_out[i] += np.dot(input[samples[i][0]], weight[samples[i][1]]) # forward activation - sampleOut = 1.0 / (1.0 + np.exp(-sampleOut)) + sample_out = 1.0 / (1.0 + np.exp(-sample_out)) # forward cost out = np.zeros(batch_size).astype(np.float32) b = 1.0 / num_classes * num_sample_class for i in range(len(samples)): - o = sampleOut[i] + o = sample_out[i] cost = -np.log(o / (o + b)) if samples[i][2] else -np.log(b / (o + b)) out[samples[i][0]] += cost * samples[i][3] - return (out, np.array(sampleOut).reshape(batch_size, - num_sample_class + num_true_class), + return (out, np.array(sample_out).reshape( + batch_size, num_sample_class + num_true_class), np.array(sample_labels).reshape(batch_size, num_sample_class + num_true_class)) From ab9d59c5396002a1c0695075164da5109c530150 Mon Sep 17 00:00:00 2001 From: wanghaoshuang Date: Tue, 28 Nov 2017 14:45:11 +0800 Subject: [PATCH 11/67] Fix double type error while using eigen api --- paddle/operators/nce_op.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/paddle/operators/nce_op.h b/paddle/operators/nce_op.h index c41393d260..7a91070329 100644 --- a/paddle/operators/nce_op.h +++ b/paddle/operators/nce_op.h @@ -22,7 +22,7 @@ namespace paddle { namespace operators { -using framework::Tensor; +using Tensor = framework::Tensor; template @@ -107,12 +107,11 @@ class NCEKernel : public framework::OpKernel { auto input_mat = EigenMatrix::From(*(context.Input("Input"))); auto weight_mat = EigenMatrix::From(*(context.Input("Weight"))); for (size_t i = 0; i < sample_labels->numel(); ++i) { - Eigen::Tensor result = + Eigen::Tensor result = (input_mat.chip((int)(i / sample_labels->dims()[1]), 0) * weight_mat.chip(sample_labels_data[i], 0)) .sum(); sample_out_data[i] += result(0); - // activation_->forward sample_out_data[i] = (1. / (1. + exp(-sample_out_data[i]))); } // forward cost From 76a65a83a015a38bd8f6654b4dc27d6040bcd5d8 Mon Sep 17 00:00:00 2001 From: wanghaoshuang Date: Tue, 28 Nov 2017 15:54:54 +0800 Subject: [PATCH 12/67] Fix comments style --- paddle/operators/nce_op.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paddle/operators/nce_op.h b/paddle/operators/nce_op.h index 7a91070329..8df20f432d 100644 --- a/paddle/operators/nce_op.h +++ b/paddle/operators/nce_op.h @@ -60,7 +60,7 @@ void PrepareSamples(const framework::ExecutionContext& context) { } } else { for (; j < sample_labels_dims[1]; ++j) { - // TODO: support more distribution sampling + // TODO(wanghaoshuang): support more distribution sampling sample_labels_data[index++] = rand(rng); } } From 29262ab24d8675d5b50fe21dda59f4102db1bb7b Mon Sep 17 00:00:00 2001 From: wanghaoshuang Date: Wed, 29 Nov 2017 11:56:29 +0800 Subject: [PATCH 13/67] Fix unitest. --- paddle/operators/nce_op.cc | 8 ++++---- paddle/operators/nce_op.h | 16 ++++++++-------- python/paddle/v2/fluid/tests/test_nce.py | 14 +++++++------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/paddle/operators/nce_op.cc b/paddle/operators/nce_op.cc index bb9346b134..952da10434 100644 --- a/paddle/operators/nce_op.cc +++ b/paddle/operators/nce_op.cc @@ -41,11 +41,11 @@ class NCEOp : public framework::OperatorWithKernel { } auto num_neg_samples = ctx->Attrs().Get("num_neg_samples"); auto num_total_classes = ctx->Attrs().Get("num_total_classes"); - std::vector sampled_labels = - ctx->Attrs().Get>("sampled_labels"); + std::vector custom_neg_classes = + ctx->Attrs().Get>("custom_neg_classes"); PADDLE_ENFORCE_EQ(num_total_classes, ctx->GetInputDim("Weight")[0]); - if (sampled_labels.size() > 0) { - PADDLE_ENFORCE_EQ(sampled_labels.size(), + if (custom_neg_classes.size() > 0) { + PADDLE_ENFORCE_EQ(custom_neg_classes.size(), static_cast(num_neg_samples)); } // set dims of output(Out) diff --git a/paddle/operators/nce_op.h b/paddle/operators/nce_op.h index 8df20f432d..ea92a797fe 100644 --- a/paddle/operators/nce_op.h +++ b/paddle/operators/nce_op.h @@ -33,14 +33,14 @@ void PrepareSamples(const framework::ExecutionContext& context) { auto label = context.Input("Label"); const int64_t* label_data = label->data(); auto label_dims = label->dims(); - int num_classes = context.Attr("num_classes"); + int num_total_classes = context.Attr("num_total_classes"); // for unitest std::vector custom_neg_classes = context.Attr>("custom_neg_classes"); // random machine std::random_device rd; std::mt19937 rng(rd()); - std::uniform_int_distribution rand(0, num_classes - 1); + std::uniform_int_distribution rand(0, num_total_classes - 1); auto sample_labels = context.Output("SampleLabels"); auto sample_labels_dims = sample_labels->dims(); @@ -84,13 +84,13 @@ class NCEKernel : public framework::OpKernel { } auto out = context.Output("Cost"); T* out_data = out->mutable_data(context.GetPlace()); - int num_smalped_classes = context.Attr("num_sampled_classes"); - int num_classes = context.Attr("num_classes"); + int num_neg_samples = context.Attr("num_neg_samples"); + int num_total_classes = context.Attr("num_total_classes"); int num_true_class = 1; if (label != nullptr) { num_true_class = label->dims()[1]; } - T b = 1. / num_classes * num_smalped_classes; + T b = 1. / num_total_classes * num_neg_samples; // forward bias auto bias = context.Input("Bias"); if (bias != nullptr) { @@ -151,13 +151,13 @@ class NCEGradKernel : public framework::OpKernel { if (sample_weight != nullptr) { sample_weight_data = sample_weight->data(); } - int num_smalped_classes = context.Attr("num_sampled_classes"); - int num_classes = context.Attr("num_classes"); + int num_neg_samples = context.Attr("num_neg_samples"); + int num_total_classes = context.Attr("num_total_classes"); int num_true_class = 1; if (label != nullptr) { num_true_class = label->dims()[1]; } - T b = 1. / num_classes * num_smalped_classes; + T b = 1. / num_total_classes * num_neg_samples; Tensor sample_grad; // tmp tensor T* sample_grad_data = sample_grad.mutable_data(sample_labels->dims(), context.GetPlace()); diff --git a/python/paddle/v2/fluid/tests/test_nce.py b/python/paddle/v2/fluid/tests/test_nce.py index 6cbf468e0a..8aeba69769 100644 --- a/python/paddle/v2/fluid/tests/test_nce.py +++ b/python/paddle/v2/fluid/tests/test_nce.py @@ -35,7 +35,7 @@ def nce(input, weight, bias, sample_weight, labels, num_classes, o = sample_out[i] cost = -np.log(o / (o + b)) if samples[i][2] else -np.log(b / (o + b)) out[samples[i][0]] += cost * samples[i][3] - return (out, np.array(sample_out).reshape( + return (out[:, np.newaxis], np.array(sample_out).reshape( batch_size, num_sample_class + num_true_class), np.array(sample_labels).reshape(batch_size, num_sample_class + num_true_class)) @@ -43,16 +43,16 @@ def nce(input, weight, bias, sample_weight, labels, num_classes, class TestNCE(OpTest): def generate_data(self, dim, batch_size, num_classes, num_true_class, - num_sampled_classes): + num_neg_samples): input = np.random.randn(batch_size, dim).astype(np.float32) weight = np.random.randn(num_classes, dim).astype(np.float32) bias = np.random.randn(num_classes).astype(np.float32) sample_weight = np.random.randn(batch_size).astype(np.float32) labels = np.random.randint(0, num_classes, (batch_size, num_true_class)) self.attrs = { - 'num_classes': num_classes, - 'num_sampled_classes': num_sampled_classes, - 'sampled_labels': range(num_sampled_classes) + 'num_total_classes': num_classes, + 'num_neg_samples': num_neg_samples, + 'custom_neg_classes': range(num_neg_samples) } self.inputs = { 'Input': input, @@ -68,8 +68,8 @@ class TestNCE(OpTest): def compute(self): out = nce(self.inputs['Input'], self.inputs['Weight'], self.inputs['Bias'], self.inputs['SampleWeight'], - self.inputs['Label'], self.attrs['num_classes'], - self.attrs['num_sampled_classes']) + self.inputs['Label'], self.attrs['num_total_classes'], + self.attrs['num_neg_samples']) self.outputs = { 'Cost': out[0], 'SampleLogits': out[1], From a5236265b752b9dfad32ae1188798b22eaba9a22 Mon Sep 17 00:00:00 2001 From: yangyaming Date: Wed, 29 Nov 2017 15:55:21 +0800 Subject: [PATCH 14/67] Refine doc for smooth l1 loss op. --- paddle/operators/smooth_l1_loss_op.cc | 62 ++++++++++++++++----------- 1 file changed, 36 insertions(+), 26 deletions(-) diff --git a/paddle/operators/smooth_l1_loss_op.cc b/paddle/operators/smooth_l1_loss_op.cc index ebf7b43700..50543fcc14 100644 --- a/paddle/operators/smooth_l1_loss_op.cc +++ b/paddle/operators/smooth_l1_loss_op.cc @@ -22,22 +22,20 @@ class SmoothL1LossOp : public framework::OperatorWithKernel { using framework::OperatorWithKernel::OperatorWithKernel; void InferShape(framework::InferShapeContext* ctx) const override { - PADDLE_ENFORCE(ctx->HasInput("X"), "X must be initialized."); - PADDLE_ENFORCE(ctx->HasInput("Y"), "Y must be initialized."); + PADDLE_ENFORCE(ctx->HasInput("X"), "Input(X) should not be null."); + PADDLE_ENFORCE(ctx->HasInput("Y"), "Input(Y) should not be null."); auto x_dims = ctx->GetInputDim("X"); auto y_dims = ctx->GetInputDim("Y"); - PADDLE_ENFORCE_EQ(x_dims, y_dims, "The shape of X and Y must be the same."); + PADDLE_ENFORCE_EQ(x_dims, y_dims); PADDLE_ENFORCE_GE(x_dims.size(), 2, - "The tensor rank of X must be at least 2."); + "The tensor rank of Input(X) should not be less than 2."); if (ctx->HasInput("InsideWeight")) { PADDLE_ENFORCE(ctx->HasInput("OutsideWeight"), "If weights are provided, must specify both " "inside and outside weights."); - PADDLE_ENFORCE_EQ(ctx->GetInputDim("InsideWeight"), x_dims, - "The shape of InsideWeight must be same as X."); - PADDLE_ENFORCE_EQ(ctx->GetInputDim("OutsideWeight"), x_dims, - "The shape of OutsideWeight must be same as X."); + PADDLE_ENFORCE_EQ(ctx->GetInputDim("InsideWeight"), x_dims); + PADDLE_ENFORCE_EQ(ctx->GetInputDim("OutsideWeight"), x_dims); } ctx->SetOutputDim("Diff", x_dims); @@ -53,25 +51,29 @@ class SmoothL1LossOpMaker : public framework::OpProtoAndCheckerMaker { framework::OpAttrChecker* op_checker) : OpProtoAndCheckerMaker(proto, op_checker) { AddInput("X", - "The input tensor of smooth l1 loss op." - "The rank should be greater or equal to 2 with shape " - "[batch_size, value_dim1, value_dim2, ..., value_dimN]"); + "(Tensor, default Tensor) A tensor with rank at least 2. " + "The input value of smooth l1 loss op with shape " + "[batch_size, dim1, ..., dimN]."); AddInput("Y", - "The target tensor of smooth l1 loss op " - "with the same shape as X."); + "(Tensor, default Tensor) A tensor with rank at least 2. " + "The target value of smooth l1 loss op with same shape as X."); 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 " + "(Tensor, default Tensor) A tensor with rank at least 2. " + "This input is optional and should have same shape with X. " + "If provided, the result of (X - Y) will be multiplied " "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.") + "(Tensor, default Tensor) A tensor with rank at least 2. " + "This input is optional and should have same shape with X. " + "If provided, the out smooth l1 loss will be multiplied by this " + "tensor element by element.") .AsDispensable(); - AddOutput("Diff", "Intermediate variable to cache InsideWeight*(X-Y).") + AddOutput("Diff", "Intermediate variable to cache InsideWeight * (X - Y).") .AsIntermediate(); - AddOutput("Out", "Smooth l1 loss."); + AddOutput("Out", + "(Tensor, default Tensor) A tensor with rank be 2. " + "The output smooth l1 loss with shape [batch_size, 1]."); AddAttr("sigma", "Hyper parameter of smooth l1 loss op." "A float scalar with default value 3.0.") @@ -79,15 +81,23 @@ class SmoothL1LossOpMaker : public framework::OpProtoAndCheckerMaker { AddComment(R"DOC( Smooth L1 Loss Operator. -This operator computes the smooth l1 loss for input and target. -The operator takes the first dimension of input as the batch size. +This operator computes the smooth l1 loss for X and Y. +The operator takes the first dimension of X and Y as batch size. For each instance, it computes the smooth l1 loss element by element first -and then sums all the losses. So the resulting output shape -is [batch_size, 1]. +and then sums all the losses. So the shape of Out is [batch_size, 1]. The equation is: -loss = $$0.5 * (\sigma * (x-y))^2$$ if $$|x - y| < 1 /({\sigma}^2)$$ - $$\frac{|x - y| - 0.5}{{\sigma}^2}$$ otherwise +$$ +Out_{\sigma}(X, Y)_i = \begin{cases} +0.5 * (\sigma * (X_i - Y_i)) ^ 2 +\quad |X_i - Y_i| \lt \frac{1} {{\sigma} ^ 2} \\ +\frac{|X_i - Y_i| - 0.5}{{\sigma}^2}, +\quad otherwise +\end{cases} +$$ + +In the above equation, $Out_{\sigma}(X, Y)_i$, $X_i$ and $Y_i$ represent the ith +element of Out, X and Y. )DOC"); } From 8a5a8637f9342f996ee0b92ff55cbe82ecced6e5 Mon Sep 17 00:00:00 2001 From: Luo Tao Date: Wed, 29 Nov 2017 18:35:49 +0800 Subject: [PATCH 15/67] fix bug in trainer/tests/CMakeLists.txt --- paddle/trainer/tests/CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/paddle/trainer/tests/CMakeLists.txt b/paddle/trainer/tests/CMakeLists.txt index 9d33e20656..bd518d8598 100644 --- a/paddle/trainer/tests/CMakeLists.txt +++ b/paddle/trainer/tests/CMakeLists.txt @@ -20,13 +20,13 @@ if(WITH_PYTHON) add_unittest_without_exec(test_TrainerOnePass test_TrainerOnePass.cpp) add_test(NAME test_TrainerOnePass - COMMAND ${PYTHON_PATH} ${CMAKE_CURRENT_BINARY_DIR}/${TARGET} - ${PADDLE_SOURCE_DIR}/paddle/.set_port.sh -p port ${CMAKE_CURRENT_BINARY_DIR}/test_TrainerOnePass + COMMAND ${PYTHON_PATH} ${PADDLE_SOURCE_DIR}/paddle/.set_port.sh -p port + ${CMAKE_CURRENT_BINARY_DIR}/test_TrainerOnePass WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle/) endif() #################### test_config_parser ######################### add_test(NAME test_config_parser - COMMAND ${PYTHON_PATH} ${CMAKE_CURRENT_BINARY_DIR}/${TARGET} - ${PYTHON_EXECUTABLE} ${PADDLE_SOURCE_DIR}/paddle/trainer/tests/config_parser_test.py + COMMAND ${PYTHON_PATH} ${PYTHON_EXECUTABLE} + ${PADDLE_SOURCE_DIR}/paddle/trainer/tests/config_parser_test.py WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle/) From ff8a6778483dcaff32e5e0acc056cf45d12148ff Mon Sep 17 00:00:00 2001 From: Yibing Liu Date: Wed, 29 Nov 2017 13:42:42 +0000 Subject: [PATCH 16/67] Revise comments in rank_loss_op --- paddle/operators/rank_loss_op.cc | 31 ++++++++++++++++++++----------- paddle/operators/rank_loss_op.cu | 2 +- paddle/operators/rank_loss_op.h | 2 +- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/paddle/operators/rank_loss_op.cc b/paddle/operators/rank_loss_op.cc index 061e82412e..87774a56f3 100644 --- a/paddle/operators/rank_loss_op.cc +++ b/paddle/operators/rank_loss_op.cc @@ -4,7 +4,7 @@ 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 + 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, @@ -35,9 +35,10 @@ class RankLossOp : public framework::OperatorWithKernel { auto right_dims = ctx->GetInputDim("Right"); PADDLE_ENFORCE((label_dims == left_dims) && (left_dims == right_dims), - "All inputs must have the same size"); - PADDLE_ENFORCE((label_dims.size() == 2) && (label_dims[1] == 1), - "All inputs must be row vector with size batch_size x 1."); + "All inputs must have the same size."); + PADDLE_ENFORCE( + (label_dims.size() == 2) && (label_dims[1] == 1), + "All inputs must be 2-D tensors with shape [batch_size x 1]."); ctx->SetOutputDim("Out", label_dims); } }; @@ -48,10 +49,17 @@ class RankLossOpMaker : public framework::OpProtoAndCheckerMaker { framework::OpAttrChecker *op_checker) : OpProtoAndCheckerMaker(proto, op_checker) { AddInput("Label", - "The label indicating A ranked higher than B or not, row vector."); - AddInput("Left", "The output of RankNet for doc A, vector."); - AddInput("Right", "The output of RankNet for doc B, vetor."); - AddOutput("Out", "The output loss of RankLoss operator, vector."); + "(2-D Tensor with shape [batch_size x 1]) " + "The label indicating A ranked higher than B or not."); + AddInput("Left", + "(2-D Tensor with shape [batch_size x 1]) " + "The output of RankNet for doc A."); + AddInput("Right", + "(2-D Tensor with shape [batch_size x 1]) " + "The output of RankNet for doc B."); + AddOutput("Out", + "(2-D Tensor with shape [batch_size x 1]) " + "The output loss of RankLoss operator."); AddComment(R"DOC( RankLoss Operator. @@ -65,8 +73,9 @@ P = {0, 1} or {0, 0.5, 1}, where 0.5 means no information about the rank of the input pair. The RankLoss operator takes three inputs: Left (o_i), Right (o_j) and Label -(P_{i,j}), which represent the output of RankNet for the two docs and the label, -respectively, and yields the rank loss C_{i,j} using the following equation: +(P_{i,j}), which represent the output score of RankNet for the two docs and +the label respectively, and yields the rank loss C_{i,j} using the following +equation: \f$$ C_{i,j} = -\tilde{P_{ij}} * o_{i,j} + log(1 + e^{o_{i,j}}) \\ @@ -74,7 +83,7 @@ respectively, and yields the rank loss C_{i,j} using the following equation: \tilde{P_{i,j}} = \left \{0, 0.5, 1 \right \} \ or \ \left \{0, 1 \right \} \f$$ -The operator can take inputs of one sample or in batch. +The operator can take batch inputs with size batch_size (batch_size >= 1). )DOC"); } diff --git a/paddle/operators/rank_loss_op.cu b/paddle/operators/rank_loss_op.cu index 779588ff36..5382e3a629 100644 --- a/paddle/operators/rank_loss_op.cu +++ b/paddle/operators/rank_loss_op.cu @@ -4,7 +4,7 @@ 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 + 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, diff --git a/paddle/operators/rank_loss_op.h b/paddle/operators/rank_loss_op.h index f184d6efcb..703c77a0b2 100644 --- a/paddle/operators/rank_loss_op.h +++ b/paddle/operators/rank_loss_op.h @@ -4,7 +4,7 @@ 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 + 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, From 4d1ee0ff126de91d7705f5587400466926ba5907 Mon Sep 17 00:00:00 2001 From: Yibing Liu Date: Wed, 29 Nov 2017 13:56:27 +0000 Subject: [PATCH 17/67] Amend license and comments in reshape_op --- paddle/operators/reshape_op.cc | 7 +++---- paddle/operators/{reshape_op.cu.cc => reshape_op.cu} | 2 +- paddle/operators/reshape_op.h | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) rename paddle/operators/{reshape_op.cu.cc => reshape_op.cu} (94%) diff --git a/paddle/operators/reshape_op.cc b/paddle/operators/reshape_op.cc index ba774ec216..39bf2118d6 100644 --- a/paddle/operators/reshape_op.cc +++ b/paddle/operators/reshape_op.cc @@ -1,11 +1,10 @@ - /* 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 + 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, @@ -38,8 +37,8 @@ class ReshapeOp : public framework::OperatorWithKernel { // TODO(qiao) change batch_size for (size_t i = 1; i < shape.size(); ++i) { PADDLE_ENFORCE(shape[i] > 0, - "Each dimension of shape " - "must be positiv except the first."); + "Each dimension of Attr(shape) " + "must be positive except the first one."); } if (shape[0] < 0) { shape[0] = x_dims[0]; diff --git a/paddle/operators/reshape_op.cu.cc b/paddle/operators/reshape_op.cu similarity index 94% rename from paddle/operators/reshape_op.cu.cc rename to paddle/operators/reshape_op.cu index 23dbe089d3..dca6c15007 100644 --- a/paddle/operators/reshape_op.cu.cc +++ b/paddle/operators/reshape_op.cu @@ -4,7 +4,7 @@ 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 + 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, diff --git a/paddle/operators/reshape_op.h b/paddle/operators/reshape_op.h index 0e98c8b4f4..73fd1da642 100644 --- a/paddle/operators/reshape_op.h +++ b/paddle/operators/reshape_op.h @@ -4,7 +4,7 @@ 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 + 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, From 00eceea06e0b7e7771c027bac190078f6ed4e77f Mon Sep 17 00:00:00 2001 From: Yiqun Liu Date: Thu, 30 Nov 2017 10:32:03 +0800 Subject: [PATCH 18/67] Fix the problem that building for Android fails with WITH_TESTING=ON. (#6051) --- paddle/gserver/tests/CMakeLists.txt | 51 ++++++++++++++--------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/paddle/gserver/tests/CMakeLists.txt b/paddle/gserver/tests/CMakeLists.txt index c295ea19c9..24e6cae8e6 100644 --- a/paddle/gserver/tests/CMakeLists.txt +++ b/paddle/gserver/tests/CMakeLists.txt @@ -62,11 +62,11 @@ if(NOT WITH_DOUBLE AND NOT MOBILE_INFERENCE) endif() if(NOT MOBILE_INFERENCE) -################## test_Evaluator ####################### + ################## test_Evaluator ####################### add_unittest(test_Evaluator test_Evaluator.cpp) -############### test_RecurrentGradientMachine ############### + ############### test_RecurrentGradientMachine ############### # TODO(yuyang18): There is some bug in test_RecurrentGradientMachine # I will fix it. add_unittest_without_exec(test_RecurrentGradientMachine @@ -77,7 +77,7 @@ if(NOT MOBILE_INFERENCE) ${CMAKE_CURRENT_BINARY_DIR}/test_RecurrentGradientMachine WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle) -############### test_NetworkCompare ############### + ############### test_NetworkCompare ############### add_unittest_without_exec(test_NetworkCompare test_NetworkCompare.cpp) if(WITH_GPU) @@ -89,34 +89,33 @@ if(NOT MOBILE_INFERENCE) COMMAND .set_python_path.sh -d ${PADDLE_SOURCE_DIR}/python ${CMAKE_CURRENT_BINARY_DIR}/test_NetworkCompare --use_gpu=false WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle) endif() -endif() + ################# test_CompareSparse ################## + add_unittest_without_exec(test_CompareSparse + test_CompareSparse.cpp) + if(NOT ON_TRAVIS) + add_test(NAME test_CompareSparse + COMMAND ${PADDLE_SOURCE_DIR}/paddle/.set_python_path.sh -d + ${PADDLE_SOURCE_DIR}/python:${PADDLE_SOURCE_DIR}/paddle/gserver/tests + ./.set_port.sh -p port -n 6 + ${CMAKE_CURRENT_BINARY_DIR}/test_CompareSparse + WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle/) + endif() + + ################ test_CompareTwoNets ###################### + add_unittest_without_exec(test_CompareTwoNets + test_CompareTwoNets.cpp) + add_test(NAME test_CompareTwoNets + COMMAND ${PADDLE_SOURCE_DIR}/paddle/.set_python_path.sh -d + ${PADDLE_SOURCE_DIR}/python:${PADDLE_SOURCE_DIR}/paddle/gserver/tests + ${CMAKE_CURRENT_BINARY_DIR}/test_CompareTwoNets + WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle/) +endif() +################ test_PyDataProvider2 ###################### add_unittest_without_exec(test_PyDataProvider2 test_PyDataProvider2.cpp) - add_test(NAME test_PyDataProvider2 COMMAND .set_python_path.sh -d ${PADDLE_SOURCE_DIR}/paddle/gserver/tests:${PADDLE_SOURCE_DIR}/python ${CMAKE_CURRENT_BINARY_DIR}/test_PyDataProvider2 WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle ) - -################# test_CompareSparse ################## -add_unittest_without_exec(test_CompareSparse - test_CompareSparse.cpp) -if(NOT ON_TRAVIS) - add_test(NAME test_CompareSparse - COMMAND ${PADDLE_SOURCE_DIR}/paddle/.set_python_path.sh -d - ${PADDLE_SOURCE_DIR}/python:${PADDLE_SOURCE_DIR}/paddle/gserver/tests - ./.set_port.sh -p port -n 6 - ${CMAKE_CURRENT_BINARY_DIR}/test_CompareSparse - WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle/) -endif() - -################ test_CompareTwoNets ###################### -add_unittest_without_exec(test_CompareTwoNets - test_CompareTwoNets.cpp) -add_test(NAME test_CompareTwoNets - COMMAND ${PADDLE_SOURCE_DIR}/paddle/.set_python_path.sh -d - ${PADDLE_SOURCE_DIR}/python:${PADDLE_SOURCE_DIR}/paddle/gserver/tests - ${CMAKE_CURRENT_BINARY_DIR}/test_CompareTwoNets - WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle/) From da62d6cc24e22b499204b415f8ab7d4ca96c71d2 Mon Sep 17 00:00:00 2001 From: Yibing Liu Date: Thu, 30 Nov 2017 02:54:37 +0000 Subject: [PATCH 19/67] fix the doc display problem in rank_loss_op --- paddle/operators/rank_loss_op.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/paddle/operators/rank_loss_op.cc b/paddle/operators/rank_loss_op.cc index 87774a56f3..912f88f455 100644 --- a/paddle/operators/rank_loss_op.cc +++ b/paddle/operators/rank_loss_op.cc @@ -77,11 +77,11 @@ The RankLoss operator takes three inputs: Left (o_i), Right (o_j) and Label the label respectively, and yields the rank loss C_{i,j} using the following equation: -\f$$ - C_{i,j} = -\tilde{P_{ij}} * o_{i,j} + log(1 + e^{o_{i,j}}) \\ +$$ + C_{i,j} = -\tilde{P_{ij}} * o_{i,j} + \log(1 + e^{o_{i,j}}) \\ o_{i,j} = o_i - o_j \\ \tilde{P_{i,j}} = \left \{0, 0.5, 1 \right \} \ or \ \left \{0, 1 \right \} -\f$$ +$$ The operator can take batch inputs with size batch_size (batch_size >= 1). From e1b8c27acbba44a52b10b8593e95eb1279f60bf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AD=A6=E6=AF=85?= Date: Thu, 30 Nov 2017 12:01:06 +0800 Subject: [PATCH 20/67] Add back print_operators_doc (#5970) * add back print_operators_doc * fix style check * fix style check --- paddle/operators/detail/send_recv.proto | 2 +- paddle/scripts/docker/build.sh | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/paddle/operators/detail/send_recv.proto b/paddle/operators/detail/send_recv.proto index 962c7d5981..07ff9d2c62 100644 --- a/paddle/operators/detail/send_recv.proto +++ b/paddle/operators/detail/send_recv.proto @@ -32,4 +32,4 @@ message VariableMessage { bytes serialized = 2; } -message VoidMessage {} \ No newline at end of file +message VoidMessage {} diff --git a/paddle/scripts/docker/build.sh b/paddle/scripts/docker/build.sh index a2fdc5ce69..502637c881 100644 --- a/paddle/scripts/docker/build.sh +++ b/paddle/scripts/docker/build.sh @@ -183,6 +183,7 @@ EOF ${DOCKERFILE_GPU_ENV} ADD go/cmd/pserver/pserver /usr/bin/ ADD go/cmd/master/master /usr/bin/ + ADD paddle/pybind/print_operators_doc /usr/bin/ # default command shows the paddle version and exit CMD ["paddle", "version"] EOF From dc91c4e3a42b678ad14742af8845b94c4a0ac50d Mon Sep 17 00:00:00 2001 From: Yu Yang Date: Thu, 30 Nov 2017 12:13:12 +0800 Subject: [PATCH 21/67] Fix MacOS compile (#6062) --- cmake/external/grpc.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/external/grpc.cmake b/cmake/external/grpc.cmake index 219ea1b908..86122aec8c 100644 --- a/cmake/external/grpc.cmake +++ b/cmake/external/grpc.cmake @@ -24,9 +24,9 @@ SET(GRPC_INSTALL_DIR ${THIRD_PARTY_PATH}/install/grpc) SET(GRPC_INCLUDE_DIR "${GRPC_INSTALL_DIR}/include/" CACHE PATH "grpc include directory." FORCE) SET(GRPC_CPP_PLUGIN "${GRPC_INSTALL_DIR}/bin/grpc_cpp_plugin" CACHE FILEPATH "GRPC_CPP_PLUGIN" FORCE) IF(APPLE) - SET(BUILD_CMD make -n | sed "s/-Werror//g" | sh) + SET(BUILD_CMD make -n HAS_SYSTEM_PROTOBUF=false -s -j8 static grpc_cpp_plugin | sed "s/-Werror//g" | sh) ELSE() - SET(BUILD_CMD make) + SET(BUILD_CMD make HAS_SYSTEM_PROTOBUF=false -s -j8 static grpc_cpp_plugin) ENDIF() ExternalProject_Add( @@ -42,7 +42,7 @@ ExternalProject_Add( # Disable -Werror, otherwise the compile will fail in MacOS. # It seems that we cannot configure that by make command. # Just dry run make command and remove `-Werror`, then use a shell to run make commands - BUILD_COMMAND ${BUILD_CMD} HAS_SYSTEM_PROTOBUF=false -s -j8 static grpc_cpp_plugin + BUILD_COMMAND ${BUILD_CMD} INSTALL_COMMAND make prefix=${GRPC_INSTALL_DIR} install ) From 82dd1653ae48a54a2ec8371f927812b351164820 Mon Sep 17 00:00:00 2001 From: QI JUN Date: Thu, 30 Nov 2017 12:18:23 +0800 Subject: [PATCH 22/67] Fix python.v2.fluid arg parse (#6055) * fix python gflags init * format code --- python/paddle/v2/fluid/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/paddle/v2/fluid/__init__.py b/python/paddle/v2/fluid/__init__.py index c033b27bea..dd25bc19ec 100644 --- a/python/paddle/v2/fluid/__init__.py +++ b/python/paddle/v2/fluid/__init__.py @@ -36,7 +36,8 @@ def __read_gflags_from_env__(): read_env_flags = ['use_pinned_memory'] if core.is_compile_gpu(): read_env_flags.append('fraction_of_gpu_memory_to_use') - core.init_gflags(sys.argv + ["--tryfromenv=" + ",".join(read_env_flags)]) + core.init_gflags([sys.argv[0]] + + ["--tryfromenv=" + ",".join(read_env_flags)]) __read_gflags_from_env__() From 35453df18f738c18a7c66d886296068d88dc1304 Mon Sep 17 00:00:00 2001 From: Yu Yang Date: Thu, 30 Nov 2017 13:41:28 +0800 Subject: [PATCH 23/67] Fix ShareLoD bug (#6084) Fix #6087 --- paddle/framework/op_desc.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paddle/framework/op_desc.cc b/paddle/framework/op_desc.cc index 48cd131550..02a8253243 100644 --- a/paddle/framework/op_desc.cc +++ b/paddle/framework/op_desc.cc @@ -65,7 +65,7 @@ class CompileTimeInferShapeContext : public InferShapeContext { PADDLE_ENFORCE_EQ(in_var->GetType(), VarDesc::LOD_TENSOR, "The %d-th output of Output(%s) must be LoDTensor.", j, out); - in_var->SetLoDLevel(out_var->GetLodLevel()); + out_var->SetLoDLevel(in_var->GetLodLevel()); } bool IsRuntime() const override; From 5fc88244b5247c687694cc792eea0f20b8eebd49 Mon Sep 17 00:00:00 2001 From: Liu Yiqun Date: Thu, 30 Nov 2017 06:07:31 +0000 Subject: [PATCH 24/67] Fix the compiling error when seting WITH_C_API=ON and WITH_PYTHON=ON. --- CMakeLists.txt | 2 ++ paddle/pserver/CMakeLists.txt | 2 +- paddle/trainer/CMakeLists.txt | 6 ++---- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e76512166f..2d38f398ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -83,6 +83,8 @@ if(ANDROID OR IOS) "Disable RDMA when cross-compiling for Android and iOS" FORCE) set(WITH_MKL OFF CACHE STRING "Disable MKL when cross-compiling for Android and iOS" FORCE) + set(WITH_GOLANG OFF CACHE STRING + "Disable golang when cross-compiling for Android and iOS" FORCE) # Compile PaddlePaddle mobile inference library if (NOT WITH_C_API) diff --git a/paddle/pserver/CMakeLists.txt b/paddle/pserver/CMakeLists.txt index ccfc0e7602..f75475a88f 100644 --- a/paddle/pserver/CMakeLists.txt +++ b/paddle/pserver/CMakeLists.txt @@ -49,7 +49,7 @@ if(WITH_TESTING) add_subdirectory(test) endif() -if(NOT WITH_C_API) +if(NOT MOBILE_INFERENCE) add_executable(paddle_pserver_main ${PSERVER_MAIN_SOURCES}) link_paddle_exe(paddle_pserver_main) diff --git a/paddle/trainer/CMakeLists.txt b/paddle/trainer/CMakeLists.txt index 3d471a0c01..72911695bd 100644 --- a/paddle/trainer/CMakeLists.txt +++ b/paddle/trainer/CMakeLists.txt @@ -54,7 +54,7 @@ if(WITH_TESTING) add_subdirectory(tests) endif() -if(NOT WITH_C_API) +if(NOT MOBILE_INFERENCE) add_paddle_exe(paddle_trainer TrainerMain.cpp) add_paddle_exe(paddle_merge_model MergeModel.cpp) @@ -74,7 +74,5 @@ endif() if(WITH_GOLANG) add_dependencies(paddle_trainer_lib paddle_pserver_cclient) target_link_libraries(paddle_trainer_lib paddle_pserver_cclient) - if(NOT WITH_C_API) - target_link_libraries(paddle_trainer paddle_pserver_cclient) - endif() + target_link_libraries(paddle_trainer paddle_pserver_cclient) endif(WITH_GOLANG) From ac596a3952a3f75cc12f1eefafb14a165a57ff95 Mon Sep 17 00:00:00 2001 From: Yu Yang Date: Thu, 30 Nov 2017 14:14:13 +0800 Subject: [PATCH 25/67] Feature/switch program (#5932) * Unify fluid submodules to fluid module Change books just use `import fluid`, not submodules * Remove g_main_program/g_startup_program Use default_main_program/default_startup_program instead * Typo * Add API for switch default program * Two functions: switch_main_program/switch_startup_program * A guard: program_guard. Users can use the `with` statement change default programs * Change unittests in `test_layers` * Fix CI * Fix CI * Fix CI --- python/paddle/v2/fluid/framework.py | 79 +++++- python/paddle/v2/fluid/tests/test_layers.py | 271 ++++++++------------ 2 files changed, 188 insertions(+), 162 deletions(-) diff --git a/python/paddle/v2/fluid/framework.py b/python/paddle/v2/fluid/framework.py index 1c42e4d44f..49c6d89834 100644 --- a/python/paddle/v2/fluid/framework.py +++ b/python/paddle/v2/fluid/framework.py @@ -3,10 +3,12 @@ import collections import numpy as np from . import core import proto.framework_pb2 as framework_pb2 +import contextlib __all__ = [ 'Block', 'Variable', 'Program', 'Operator', 'default_startup_program', - 'default_main_program' + 'default_main_program', 'program_guard', 'switch_startup_program', + 'switch_main_program' ] @@ -659,8 +661,83 @@ _startup_program_ = Program() def default_startup_program(): + """ + Get default startup program. In startup program, Paddle will initialize + parameters, initialize nccl handle, etc. + + Returns: + Program: startup program + """ return _startup_program_ def default_main_program(): + """ + Get default main program. The main program is used for training or testing. + + Returns: + Program: main program + """ return _main_program_ + + +def switch_main_program(program): + """ + Switch the main program to a new program. + + Args: + program(Program): The new main program + + Returns: + Program: The previous main program + """ + global _main_program_ + prev_program = _main_program_ + _main_program_ = program + return prev_program + + +def switch_startup_program(program): + """ + Switch the startup program to a new program + Args: + program(Program): The new startup program + + Returns: + Program: The previous startup program + """ + global _startup_program_ + prev_program = _startup_program_ + _startup_program_ = program + return prev_program + + +@contextlib.contextmanager +def program_guard(main_program, startup_program=None): + """ + Switch program with `with` statement + + Examples: + >>> with program_guard(Program()): + >>> data = fluid.layers.data(...) + >>> hidden = fluid.layers.fc(...) + + Args: + main_program(Program): New main program inside `with` statement + startup_program(Program): New startup program inside `with` statement. + None means do not change startup program. + + Returns: + None + """ + if not isinstance(main_program, Program): + raise TypeError("main_program should be Program") + main_program = switch_main_program(main_program) + if startup_program is not None: + if not isinstance(startup_program, Program): + raise TypeError("startup_program should be Program") + startup_program = switch_startup_program(startup_program) + yield + switch_main_program(main_program) + if startup_program is not None: + switch_startup_program(startup_program) diff --git a/python/paddle/v2/fluid/tests/test_layers.py b/python/paddle/v2/fluid/tests/test_layers.py index b6906be60b..33b0e54f42 100644 --- a/python/paddle/v2/fluid/tests/test_layers.py +++ b/python/paddle/v2/fluid/tests/test_layers.py @@ -1,192 +1,141 @@ +from __future__ import print_function import unittest import paddle.v2.fluid.layers as layers import paddle.v2.fluid.nets as nets -from paddle.v2.fluid.framework import Program +from paddle.v2.fluid.framework import Program, program_guard class TestBook(unittest.TestCase): def test_fit_a_line(self): program = Program() - x = layers.data( - name='x', shape=[13], dtype='float32', main_program=program) - y_predict = layers.fc(input=x, size=1, act=None, main_program=program) + with program_guard(program, startup_program=Program()): + x = layers.data(name='x', shape=[13], dtype='float32') + y_predict = layers.fc(input=x, size=1, act=None) + y = layers.data(name='y', shape=[1], dtype='float32') + cost = layers.square_error_cost(input=y_predict, label=y) + avg_cost = layers.mean(x=cost) + self.assertIsNotNone(avg_cost) + program.append_backward(avg_cost) - y = layers.data( - name='y', shape=[1], dtype='float32', main_program=program) - cost = layers.square_error_cost( - input=y_predict, label=y, main_program=program) - - avg_cost = layers.mean(x=cost, main_program=program) - self.assertIsNotNone(avg_cost) - program.append_backward(avg_cost) - - print str(program) + print(str(program)) def test_recognize_digits_mlp(self): program = Program() - - # Change g_program, so the rest layers use `g_program` - images = layers.data( - name='pixel', shape=[784], dtype='float32', main_program=program) - label = layers.data( - name='label', shape=[1], dtype='int32', main_program=program) - hidden1 = layers.fc(input=images, - size=128, - act='relu', - main_program=program) - hidden2 = layers.fc(input=hidden1, - size=64, - act='relu', - main_program=program) - predict = layers.fc(input=hidden2, - size=10, - act='softmax', - main_program=program) - cost = layers.cross_entropy( - input=predict, label=label, main_program=program) - avg_cost = layers.mean(x=cost, main_program=program) - self.assertIsNotNone(avg_cost) - - print str(program) + with program_guard(program, startup_program=Program()): + # Change g_program, so the rest layers use `g_program` + images = layers.data(name='pixel', shape=[784], dtype='float32') + label = layers.data(name='label', shape=[1], dtype='int32') + hidden1 = layers.fc(input=images, size=128, act='relu') + hidden2 = layers.fc(input=hidden1, size=64, act='relu') + predict = layers.fc(input=hidden2, size=10, act='softmax') + cost = layers.cross_entropy(input=predict, label=label) + avg_cost = layers.mean(x=cost) + self.assertIsNotNone(avg_cost) + + print(str(program)) def test_simple_conv2d(self): program = Program() - images = layers.data( - name='pixel', - shape=[3, 48, 48], - dtype='int32', - main_program=program) - layers.conv2d( - input=images, - num_filters=3, - filter_size=[4, 4], - main_program=program) - - print str(program) + with program_guard(program, startup_program=Program()): + images = layers.data(name='pixel', shape=[3, 48, 48], dtype='int32') + layers.conv2d(input=images, num_filters=3, filter_size=[4, 4]) + + print(str(program)) def test_conv2d_transpose(self): program = Program() - kwargs = {'main_program': program} - img = layers.data( - name='pixel', shape=[3, 2, 2], dtype='float32', **kwargs) - layers.conv2d_transpose( - input=img, num_filters=10, output_size=28, **kwargs) - print str(program) + with program_guard(program): + img = layers.data(name='pixel', shape=[3, 2, 2], dtype='float32') + layers.conv2d_transpose(input=img, num_filters=10, output_size=28) + print(str(program)) def test_recognize_digits_conv(self): program = Program() - - images = layers.data( - name='pixel', - shape=[1, 28, 28], - dtype='float32', - main_program=program) - label = layers.data( - name='label', shape=[1], dtype='int32', main_program=program) - conv_pool_1 = nets.simple_img_conv_pool( - input=images, - filter_size=5, - num_filters=2, - pool_size=2, - pool_stride=2, - act="relu", - main_program=program) - conv_pool_2 = nets.simple_img_conv_pool( - input=conv_pool_1, - filter_size=5, - num_filters=4, - pool_size=2, - pool_stride=2, - act="relu", - main_program=program) - - predict = layers.fc(input=conv_pool_2, - size=10, - act="softmax", - main_program=program) - cost = layers.cross_entropy( - input=predict, label=label, main_program=program) - avg_cost = layers.mean(x=cost, main_program=program) - - program.append_backward(avg_cost) - - print str(program) + with program_guard(program, startup_program=Program()): + images = layers.data( + name='pixel', shape=[1, 28, 28], dtype='float32') + label = layers.data(name='label', shape=[1], dtype='int32') + conv_pool_1 = nets.simple_img_conv_pool( + input=images, + filter_size=5, + num_filters=2, + pool_size=2, + pool_stride=2, + act="relu") + conv_pool_2 = nets.simple_img_conv_pool( + input=conv_pool_1, + filter_size=5, + num_filters=4, + pool_size=2, + pool_stride=2, + act="relu") + + predict = layers.fc(input=conv_pool_2, size=10, act="softmax") + cost = layers.cross_entropy(input=predict, label=label) + avg_cost = layers.mean(x=cost) + + program.append_backward(avg_cost) + + print(str(program)) def test_word_embedding(self): program = Program() - dict_size = 10000 - embed_size = 32 - first_word = layers.data( - name='firstw', shape=[1], dtype='int64', main_program=program) - second_word = layers.data( - name='secondw', shape=[1], dtype='int64', main_program=program) - third_word = layers.data( - name='thirdw', shape=[1], dtype='int64', main_program=program) - forth_word = layers.data( - name='forthw', shape=[1], dtype='int64', main_program=program) - next_word = layers.data( - name='nextw', shape=[1], dtype='int64', main_program=program) - - embed_first = layers.embedding( - input=first_word, - size=[dict_size, embed_size], - dtype='float32', - param_attr='shared_w', - main_program=program) - embed_second = layers.embedding( - input=second_word, - size=[dict_size, embed_size], - dtype='float32', - param_attr='shared_w', - main_program=program) - - embed_third = layers.embedding( - input=third_word, - size=[dict_size, embed_size], - dtype='float32', - param_attr='shared_w', - main_program=program) - embed_forth = layers.embedding( - input=forth_word, - size=[dict_size, embed_size], - dtype='float32', - param_attr='shared_w', - main_program=program) - - concat_embed = layers.concat( - input=[embed_first, embed_second, embed_third, embed_forth], - axis=1, - main_program=program) - - hidden1 = layers.fc(input=concat_embed, - size=256, - act='sigmoid', - main_program=program) - predict_word = layers.fc(input=hidden1, - size=dict_size, - act='softmax', - main_program=program) - cost = layers.cross_entropy( - input=predict_word, label=next_word, main_program=program) - avg_cost = layers.mean(x=cost, main_program=program) - self.assertIsNotNone(avg_cost) - - print str(program) + with program_guard(program, startup_program=Program()): + dict_size = 10000 + embed_size = 32 + first_word = layers.data(name='firstw', shape=[1], dtype='int64') + second_word = layers.data(name='secondw', shape=[1], dtype='int64') + third_word = layers.data(name='thirdw', shape=[1], dtype='int64') + forth_word = layers.data(name='forthw', shape=[1], dtype='int64') + next_word = layers.data(name='nextw', shape=[1], dtype='int64') + + embed_first = layers.embedding( + input=first_word, + size=[dict_size, embed_size], + dtype='float32', + param_attr='shared_w') + embed_second = layers.embedding( + input=second_word, + size=[dict_size, embed_size], + dtype='float32', + param_attr='shared_w') + + embed_third = layers.embedding( + input=third_word, + size=[dict_size, embed_size], + dtype='float32', + param_attr='shared_w') + embed_forth = layers.embedding( + input=forth_word, + size=[dict_size, embed_size], + dtype='float32', + param_attr='shared_w') + + concat_embed = layers.concat( + input=[embed_first, embed_second, embed_third, embed_forth], + axis=1) + + hidden1 = layers.fc(input=concat_embed, size=256, act='sigmoid') + predict_word = layers.fc(input=hidden1, + size=dict_size, + act='softmax') + cost = layers.cross_entropy(input=predict_word, label=next_word) + avg_cost = layers.mean(x=cost) + self.assertIsNotNone(avg_cost) + + print(str(program)) def test_linear_chain_crf(self): program = Program() - - # Change g_program, so the rest layers use `g_program` - images = layers.data( - name='pixel', shape=[784], dtype='float32', main_program=program) - label = layers.data( - name='label', shape=[1], dtype='int32', main_program=program) - hidden = layers.fc(input=images, size=128, main_program=program) - crf = layers.linear_chain_crf( - input=hidden, label=label, main_program=program) - - print str(program) + with program_guard(program, startup_program=Program()): + images = layers.data(name='pixel', shape=[784], dtype='float32') + label = layers.data(name='label', shape=[1], dtype='int32') + hidden = layers.fc(input=images, size=128) + crf = layers.linear_chain_crf(input=hidden, label=label) + self.assertNotEqual(crf, None) + + print(str(program)) if __name__ == '__main__': From 849bf9d0d0ebf7ab6509a588b8e1b28e9f4d3d67 Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Thu, 30 Nov 2017 14:17:25 +0800 Subject: [PATCH 26/67] separate mkldnn benchmark as train and infer --- benchmark/paddle/image/run_mkldnn.sh | 107 --------------------- benchmark/paddle/image/run_mkldnn_infer.sh | 68 +++++++++++++ benchmark/paddle/image/run_mkldnn_train.sh | 47 +++++++++ 3 files changed, 115 insertions(+), 107 deletions(-) delete mode 100755 benchmark/paddle/image/run_mkldnn.sh create mode 100755 benchmark/paddle/image/run_mkldnn_infer.sh create mode 100755 benchmark/paddle/image/run_mkldnn_train.sh diff --git a/benchmark/paddle/image/run_mkldnn.sh b/benchmark/paddle/image/run_mkldnn.sh deleted file mode 100755 index c78079fa45..0000000000 --- a/benchmark/paddle/image/run_mkldnn.sh +++ /dev/null @@ -1,107 +0,0 @@ -set -e - -function train() { - unset OMP_NUM_THREADS MKL_NUM_THREADS OMP_DYNAMIC KMP_AFFINITY - topology=$1 - layer_num=$2 - bs=$3 - use_mkldnn=$4 - if [ $4 == "True" ]; then - thread=1 - log="logs/train-${topology}-${layer_num}-mkldnn-${bs}.log" - elif [ $4 == "False" ]; then - thread=`nproc` - # each trainer_count use only 1 core to avoid conflict - log="logs/train-${topology}-${layer_num}-${thread}mklml-${bs}.log" - else - echo "Wrong input $4, use True or False." - exit 0 - fi - args="batch_size=${bs},layer_num=${layer_num}" - config="${topology}.py" - paddle train --job=time \ - --config=$config \ - --use_mkldnn=$use_mkldnn \ - --use_gpu=False \ - --trainer_count=$thread \ - --log_period=10 \ - --test_period=100 \ - --config_args=$args \ - 2>&1 | tee ${log} -} - -function test() { - unset OMP_NUM_THREADS MKL_NUM_THREADS OMP_DYNAMIC KMP_AFFINITY - topology=$1 - layer_num=$2 - bs=$3 - use_mkldnn=$4 - if [ $4 == "True" ]; then - thread=1 - log="logs/test-${topology}-${layer_num}-mkldnn-${bs}.log" - elif [ $4 == "False" ]; then - thread=`nproc` - if [ $thread -gt $bs ]; then - thread=$bs - fi - log="logs/test-${topology}-${layer_num}-${thread}mklml-${bs}.log" - else - echo "Wrong input $4, use True or False." - exit 0 - fi - - models_in="models/${topology}-${layer_num}/pass-00000/" - if [ ! -d $models_in ]; then - echo "Training model ${topology}_${layer_num}" - paddle train --job=train \ - --config="${topology}.py" \ - --use_mkldnn=True \ - --use_gpu=False \ - --trainer_count=1 \ - --num_passes=1 \ - --save_dir="models/${topology}-${layer_num}" \ - --config_args="batch_size=128,layer_num=${layer_num}" \ - > /dev/null 2>&1 - echo "Done" - fi - paddle train --job=test \ - --config="${topology}.py" \ - --use_mkldnn=$use_mkldnn \ - --use_gpu=False \ - --trainer_count=$thread \ - --log_period=10 \ - --config_args="batch_size=${bs},layer_num=${layer_num},is_test=True" \ - --init_model_path=$models_in \ - 2>&1 | tee ${log} -} - -if [ ! -f "train.list" ]; then - echo " " > train.list -fi -if [ ! -f "test.list" ]; then - echo " " > test.list -fi -if [ ! -d "logs" ]; then - mkdir logs -fi -if [ ! -d "models" ]; then - mkdir -p models -fi - -# inference benchmark -for use_mkldnn in True False; do - for batchsize in 1 2 4 8 16; do - test googlenet v1 $batchsize $use_mkldnn - test resnet 50 $batchsize $use_mkldnn - test vgg 19 $batchsize $use_mkldnn - done -done - -# training benchmark -for use_mkldnn in True False; do - for batchsize in 64 128 256; do - train vgg 19 $batchsize $use_mkldnn - train resnet 50 $batchsize $use_mkldnn - train googlenet v1 $batchsize $use_mkldnn - done -done diff --git a/benchmark/paddle/image/run_mkldnn_infer.sh b/benchmark/paddle/image/run_mkldnn_infer.sh new file mode 100755 index 0000000000..3081d5e7b5 --- /dev/null +++ b/benchmark/paddle/image/run_mkldnn_infer.sh @@ -0,0 +1,68 @@ +set -e + +function infer() { + unset OMP_NUM_THREADS MKL_NUM_THREADS OMP_DYNAMIC KMP_AFFINITY + topology=$1 + layer_num=$2 + bs=$3 + use_mkldnn=$4 + if [ $4 == "True" ]; then + thread=1 + log="logs/infer-${topology}-${layer_num}-mkldnn-${bs}.log" + elif [ $4 == "False" ]; then + thread=`nproc` + if [ $thread -gt $bs ]; then + thread=$bs + fi + log="logs/infer-${topology}-${layer_num}-${thread}mklml-${bs}.log" + else + echo "Wrong input $4, use True or False." + exit 0 + fi + + models_in="models/${topology}-${layer_num}/pass-00000/" + if [ ! -d $models_in ]; then + echo "Training model ${topology}_${layer_num}" + paddle train --job=train \ + --config="${topology}.py" \ + --use_mkldnn=True \ + --use_gpu=False \ + --trainer_count=1 \ + --num_passes=1 \ + --save_dir="models/${topology}-${layer_num}" \ + --config_args="batch_size=128,layer_num=${layer_num}" \ + > /dev/null 2>&1 + echo "Done" + fi + paddle train --job=test \ + --config="${topology}.py" \ + --use_mkldnn=$use_mkldnn \ + --use_gpu=False \ + --trainer_count=$thread \ + --log_period=32 \ + --config_args="batch_size=${bs},layer_num=${layer_num},is_infer=True" \ + --init_model_path=$models_in \ + 2>&1 | tee ${log} +} + +if [ ! -f "train.list" ]; then + echo " " > train.list +fi +if [ ! -f "test.list" ]; then + echo " " > test.list +fi +if [ ! -d "logs" ]; then + mkdir logs +fi +if [ ! -d "models" ]; then + mkdir -p models +fi + +# inference benchmark +for use_mkldnn in True False; do + for batchsize in 1 2 4 8 16; do + infer googlenet v1 $batchsize $use_mkldnn + infer resnet 50 $batchsize $use_mkldnn + infer vgg 19 $batchsize $use_mkldnn + done +done diff --git a/benchmark/paddle/image/run_mkldnn_train.sh b/benchmark/paddle/image/run_mkldnn_train.sh new file mode 100755 index 0000000000..320206239a --- /dev/null +++ b/benchmark/paddle/image/run_mkldnn_train.sh @@ -0,0 +1,47 @@ +set -e + +function train() { + unset OMP_NUM_THREADS MKL_NUM_THREADS OMP_DYNAMIC KMP_AFFINITY + topology=$1 + layer_num=$2 + bs=$3 + use_mkldnn=$4 + if [ $4 == "True" ]; then + thread=1 + log="logs/train-${topology}-${layer_num}-mkldnn-${bs}.log" + elif [ $4 == "False" ]; then + thread=`nproc` + # each trainer_count use only 1 core to avoid conflict + log="logs/train-${topology}-${layer_num}-${thread}mklml-${bs}.log" + else + echo "Wrong input $4, use True or False." + exit 0 + fi + args="batch_size=${bs},layer_num=${layer_num}" + config="${topology}.py" + paddle train --job=time \ + --config=$config \ + --use_mkldnn=$use_mkldnn \ + --use_gpu=False \ + --trainer_count=$thread \ + --log_period=10 \ + --test_period=100 \ + --config_args=$args \ + 2>&1 | tee ${log} +} + +if [ ! -f "train.list" ]; then + echo " " > train.list +fi +if [ ! -d "logs" ]; then + mkdir logs +fi + +# training benchmark +for use_mkldnn in True False; do + for batchsize in 64 128 256; do + train vgg 19 $batchsize $use_mkldnn + train resnet 50 $batchsize $use_mkldnn + train googlenet v1 $batchsize $use_mkldnn + done +done From a5aac614108c4b2b6d88d0c3446e4184911a319c Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Thu, 30 Nov 2017 14:24:35 +0800 Subject: [PATCH 27/67] skip cost when inference --- benchmark/paddle/image/googlenet.py | 20 +++++++++++++++----- benchmark/paddle/image/provider.py | 14 ++++++++++---- benchmark/paddle/image/resnet.py | 27 +++++++++++++++++++-------- benchmark/paddle/image/vgg.py | 18 ++++++++++++++---- 4 files changed, 58 insertions(+), 21 deletions(-) diff --git a/benchmark/paddle/image/googlenet.py b/benchmark/paddle/image/googlenet.py index 5b1f0ca006..d3dc0506d5 100644 --- a/benchmark/paddle/image/googlenet.py +++ b/benchmark/paddle/image/googlenet.py @@ -6,8 +6,15 @@ width = 224 num_class = 1000 batch_size = get_config_arg('batch_size', int, 128) use_gpu = get_config_arg('use_gpu', bool, True) - -args = {'height': height, 'width': width, 'color': True, 'num_class': num_class} +is_infer = get_config_arg("is_infer", bool, False) + +args = { + 'height': height, + 'width': width, + 'color': True, + 'num_class': num_class, + 'is_infer': is_infer +} define_py_data_sources2( "train.list", "test.list", module="provider", obj="process", args=args) @@ -146,7 +153,6 @@ def inception(name, input, channels, \ return cat -lab = data_layer(name="label", size=1000) data = data_layer(name="input", size=3 * height * width) # stage 1 @@ -224,6 +230,10 @@ pool5 = img_pool_layer( dropout = dropout_layer(name="dropout", input=pool5, dropout_rate=0.4) out3 = fc_layer( name="output3", input=dropout, size=1000, act=SoftmaxActivation()) -loss3 = cross_entropy(name='loss3', input=out3, label=lab) -outputs(loss3) +if is_infer: + outputs(out3) +else: + lab = data_layer(name="label", size=num_class) + loss3 = cross_entropy(name='loss3', input=out3, label=lab) + outputs(loss3) diff --git a/benchmark/paddle/image/provider.py b/benchmark/paddle/image/provider.py index 4703944c87..a3a6b6fc4d 100644 --- a/benchmark/paddle/image/provider.py +++ b/benchmark/paddle/image/provider.py @@ -13,8 +13,11 @@ def initHook(settings, height, width, color, num_class, **kwargs): settings.data_size = settings.height * settings.width * 3 else: settings.data_size = settings.height * settings.width - - settings.slots = [dense_vector(settings.data_size), integer_value(1)] + settings.is_infer = kwargs.get('is_infer', False) + if settings.is_infer: + settings.slots = [dense_vector(settings.data_size)] + else: + settings.slots = [dense_vector(settings.data_size), integer_value(1)] @provider( @@ -22,5 +25,8 @@ def initHook(settings, height, width, color, num_class, **kwargs): def process(settings, file_list): for i in xrange(1024): img = np.random.rand(1, settings.data_size).reshape(-1, 1).flatten() - lab = random.randint(0, settings.num_class - 1) - yield img.astype('float32'), int(lab) + if settings.is_infer: + yield img.astype('float32') + else: + lab = random.randint(0, settings.num_class - 1) + yield img.astype('float32'), int(lab) diff --git a/benchmark/paddle/image/resnet.py b/benchmark/paddle/image/resnet.py index f8c1c2df88..163394e566 100644 --- a/benchmark/paddle/image/resnet.py +++ b/benchmark/paddle/image/resnet.py @@ -6,9 +6,15 @@ width = 224 num_class = 1000 batch_size = get_config_arg('batch_size', int, 64) layer_num = get_config_arg("layer_num", int, 50) -is_test = get_config_arg("is_test", bool, False) - -args = {'height': height, 'width': width, 'color': True, 'num_class': num_class} +is_infer = get_config_arg("is_infer", bool, False) + +args = { + 'height': height, + 'width': width, + 'color': True, + 'num_class': num_class, + 'is_infer': is_infer +} define_py_data_sources2( "train.list", "test.list", module="provider", obj="process", args=args) @@ -45,7 +51,10 @@ def conv_bn_layer(name, act=LinearActivation(), bias_attr=False) return batch_norm_layer( - name=name + "_bn", input=tmp, act=active_type, use_global_stats=is_test) + name=name + "_bn", + input=tmp, + act=active_type, + use_global_stats=is_infer) def bottleneck_block(name, input, num_filters1, num_filters2): @@ -207,7 +216,9 @@ elif layer_num == 152: else: print("Wrong layer number.") -lbl = data_layer(name="label", size=num_class) -loss = cross_entropy(name='loss', input=resnet, label=lbl) -inputs(img, lbl) -outputs(loss) +if is_infer: + outputs(resnet) +else: + lbl = data_layer(name="label", size=num_class) + loss = cross_entropy(name='loss', input=resnet, label=lbl) + outputs(loss) diff --git a/benchmark/paddle/image/vgg.py b/benchmark/paddle/image/vgg.py index 97f4dbe0e1..2d8075bcf2 100644 --- a/benchmark/paddle/image/vgg.py +++ b/benchmark/paddle/image/vgg.py @@ -6,8 +6,15 @@ width = 224 num_class = 1000 batch_size = get_config_arg('batch_size', int, 64) layer_num = get_config_arg('layer_num', int, 19) +is_infer = get_config_arg("is_infer", bool, False) -args = {'height': height, 'width': width, 'color': True, 'num_class': num_class} +args = { + 'height': height, + 'width': width, + 'color': True, + 'num_class': num_class, + 'is_infer': is_infer +} define_py_data_sources2( "train.list", "test.list", module="provider", obj="process", args=args) @@ -98,6 +105,9 @@ elif layer_num == 19: else: print("Wrong layer number.") -lab = data_layer('label', num_class) -loss = cross_entropy(input=vgg, label=lab) -outputs(loss) +if is_infer: + outputs(vgg) +else: + lab = data_layer('label', num_class) + loss = cross_entropy(input=vgg, label=lab) + outputs(loss) From aef639448c67999e3bfc094c6d39ca528fe193a4 Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Thu, 30 Nov 2017 14:33:43 +0800 Subject: [PATCH 28/67] skip train list when inference, skip test list when training --- benchmark/paddle/image/googlenet.py | 6 +++++- benchmark/paddle/image/resnet.py | 6 +++++- benchmark/paddle/image/vgg.py | 6 +++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/benchmark/paddle/image/googlenet.py b/benchmark/paddle/image/googlenet.py index d3dc0506d5..7059c13bd2 100644 --- a/benchmark/paddle/image/googlenet.py +++ b/benchmark/paddle/image/googlenet.py @@ -16,7 +16,11 @@ args = { 'is_infer': is_infer } define_py_data_sources2( - "train.list", "test.list", module="provider", obj="process", args=args) + "train.list" if not is_infer else None, + "test.list" if is_infer else None, + module="provider", + obj="process", + args=args) settings( batch_size=batch_size, diff --git a/benchmark/paddle/image/resnet.py b/benchmark/paddle/image/resnet.py index 163394e566..4a14363ff1 100644 --- a/benchmark/paddle/image/resnet.py +++ b/benchmark/paddle/image/resnet.py @@ -16,7 +16,11 @@ args = { 'is_infer': is_infer } define_py_data_sources2( - "train.list", "test.list", module="provider", obj="process", args=args) + "train.list" if not is_infer else None, + "test.list" if is_infer else None, + module="provider", + obj="process", + args=args) settings( batch_size=batch_size, diff --git a/benchmark/paddle/image/vgg.py b/benchmark/paddle/image/vgg.py index 2d8075bcf2..8d0a1e97a4 100644 --- a/benchmark/paddle/image/vgg.py +++ b/benchmark/paddle/image/vgg.py @@ -16,7 +16,11 @@ args = { 'is_infer': is_infer } define_py_data_sources2( - "train.list", "test.list", module="provider", obj="process", args=args) + "train.list" if not is_infer else None, + "test.list" if is_infer else None, + module="provider", + obj="process", + args=args) settings( batch_size=batch_size, From 605b3e449911420e5a171085d457916d668268e1 Mon Sep 17 00:00:00 2001 From: Yi Wang Date: Wed, 29 Nov 2017 23:22:19 -0800 Subject: [PATCH 29/67] Translate the CPU profiling document (#6073) * Translate the CPU profiling document * Paragraphing --- doc/howto/optimization/cpu_profiling.md | 166 +++++++++++++-------- doc/howto/optimization/cpu_profiling_cn.md | 155 +++++++++++++++++++ 2 files changed, 255 insertions(+), 66 deletions(-) create mode 100644 doc/howto/optimization/cpu_profiling_cn.md diff --git a/doc/howto/optimization/cpu_profiling.md b/doc/howto/optimization/cpu_profiling.md index b3330b0b59..e1d91c668e 100644 --- a/doc/howto/optimization/cpu_profiling.md +++ b/doc/howto/optimization/cpu_profiling.md @@ -1,42 +1,52 @@ -此教程会介绍如何使用Python的cProfile包,与Python库yep,google perftools来运行性能分析(Profiling)与调优。 +This tutorial introduces techniques we used to profile and tune the +CPU performance of PaddlePaddle. We will use Python packages +`cProfile` and `yep`, and Google `perftools`. -运行性能分析可以让开发人员科学的,有条不紊的对程序进行性能优化。性能分析是性能调优的基础。因为在程序实际运行中,真正的瓶颈可能和程序员开发过程中想象的瓶颈相去甚远。 +Profiling is the process that reveals the performance bottlenecks, +which could be very different from what's in the developers' mind. +Performance tuning is to fix the bottlenecks. Performance optimization +repeats the steps of profiling and tuning alternatively. -性能优化的步骤,通常是循环重复若干次『性能分析 --> 寻找瓶颈 ---> 调优瓶颈 --> 性能分析确认调优效果』。其中性能分析是性能调优的至关重要的量化指标。 +PaddlePaddle users program AI by calling the Python API, which calls +into `libpaddle.so.` written in C++. In this tutorial, we focus on +the profiling and tuning of -Paddle提供了Python语言绑定。用户使用Python进行神经网络编程,训练,测试。Python解释器通过`pybind`和`swig`调用Paddle的动态链接库,进而调用Paddle C++部分的代码。所以Paddle的性能分析与调优分为两个部分: +1. the Python code and +1. the mixture of Python and C++ code. -* Python代码的性能分析 -* Python与C++混合代码的性能分析 +## Profiling the Python Code +### Generate the Performance Profiling File -## Python代码的性能分析 - -### 生成性能分析文件 - -Python标准库中提供了性能分析的工具包,[cProfile](https://docs.python.org/2/library/profile.html)。生成Python性能分析的命令如下: +We can use Python standard +package, [`cProfile`](https://docs.python.org/2/library/profile.html), +to generate Python profiling file. For example: ```bash python -m cProfile -o profile.out main.py ``` -其中`-o`标识了一个输出的文件名,用来存储本次性能分析的结果。如果不指定这个文件,`cProfile`会打印一些统计信息到`stdout`。这不方便我们进行后期处理(进行`sort`, `split`, `cut`等等)。 - -### 查看性能分析文件 +where `main.py` is the program we are going to profile, `-o` specifies +the output file. Without `-o`, `cProfile` would outputs to standard +output. -当main.py运行完毕后,性能分析结果文件`profile.out`就生成出来了。我们可以使用[cprofilev](https://github.com/ymichael/cprofilev)来查看性能分析结果。`cprofilev`是一个Python的第三方库。使用它会开启一个HTTP服务,将性能分析结果以网页的形式展示出来。 +### Look into the Profiling File -使用`pip install cprofilev`安装`cprofilev`工具。安装完成后,使用如下命令开启HTTP服务 +`cProfile` generates `profile.out` after `main.py` completes. We can +use [`cprofilev`](https://github.com/ymichael/cprofilev) to look into +the details: ```bash cprofilev -a 0.0.0.0 -p 3214 -f profile.out main.py ``` -其中`-a`标识HTTP服务绑定的IP。使用`0.0.0.0`允许外网访问这个HTTP服务。`-p`标识HTTP服务的端口。`-f`标识性能分析的结果文件。`main.py`标识被性能分析的源文件。 +where `-a` specifies the HTTP IP, `-p` specifies the port, `-f` +specifies the profiling file, and `main.py` is the source file. -访问对应网址,即可显示性能分析的结果。性能分析结果格式如下: +Open the Web browser and points to the local IP and the specifies +port, we will see the output like the following: -```text +``` ncalls tottime percall cumtime percall filename:lineno(function) 1 0.284 0.284 29.514 29.514 main.py:1() 4696 0.128 0.000 15.748 0.003 /home/yuyang/perf_test/.env/lib/python2.7/site-packages/paddle/v2/fluid/executor.py:20(run) @@ -44,23 +54,23 @@ cprofilev -a 0.0.0.0 -p 3214 -f profile.out main.py 1 0.144 0.144 6.534 6.534 /home/yuyang/perf_test/.env/lib/python2.7/site-packages/paddle/v2/__init__.py:14() ``` -每一列的含义是: +where each line corresponds to Python function, and the meaning of +each column is as follows: -| 列名 | 含义 | +| column | meaning | | --- | --- | -| ncalls | 函数的调用次数 | -| tottime | 函数实际使用的总时间。该时间去除掉本函数调用其他函数的时间 | -| percall | tottime的每次调用平均时间 | -| cumtime | 函数总时间。包含这个函数调用其他函数的时间 | -| percall | cumtime的每次调用平均时间 | -| filename:lineno(function) | 文件名, 行号,函数名 | +| ncalls | the number of calls into a function | +| tottime | the total execution time of the function, not including the + execution time of other functions called by the function | +| percall | tottime divided by ncalls | +| cumtime | the total execution time of the function, including the execution time of other functions being called | +| percall | cumtime divided by ncalls | +| filename:lineno(function) | where the function is defined | +### Identify Performance Bottlenecks -### 寻找性能瓶颈 - -通常`tottime`和`cumtime`是寻找瓶颈的关键指标。这两个指标代表了某一个函数真实的运行时间。 - -将性能分析结果按照tottime排序,效果如下: +Usually, `tottime` and the related `percall` time is what we want to +focus on. We can sort above profiling file by tottime: ```text 4696 12.040 0.003 12.040 0.003 {built-in method run} @@ -68,12 +78,15 @@ cprofilev -a 0.0.0.0 -p 3214 -f profile.out main.py 107991 0.676 0.000 1.519 0.000 /home/yuyang/perf_test/.env/lib/python2.7/site-packages/paddle/v2/fluid/framework.py:219(__init__) 4697 0.626 0.000 2.291 0.000 /home/yuyang/perf_test/.env/lib/python2.7/site-packages/paddle/v2/fluid/framework.py:428(sync_with_cpp) 1 0.618 0.618 0.618 0.618 /home/yuyang/perf_test/.env/lib/python2.7/site-packages/paddle/v2/fluid/__init__.py:1() - ``` -可以看到最耗时的函数是C++端的`run`函数。这需要联合我们第二节`Python`与`C++`混合代码的性能分析来进行调优。而`sync_with_cpp`函数的总共耗时很长,每次调用的耗时也很长。于是我们可以点击`sync_with_cpp`的详细信息,了解其调用关系。 +We can see that the most time-consuming function is the `built-in +method run`, which is a C++ function in `libpaddle.so`. We will +explain how to profile C++ code in the next section. At the right +moment, let's look into the third function `sync_with_cpp`, which is a +Python function. We can click it to understand more about it: -```text +``` Called By: Ordered by: internal time @@ -92,72 +105,93 @@ Called: List reduced from 4497 to 2 due to restriction <'sync_with_cpp'> ``` -通常观察热点函数间的调用关系,和对应行的代码,就可以了解到问题代码在哪里。当我们做出性能修正后,再次进行性能分析(profiling)即可检查我们调优后的修正是否能够改善程序的性能。 +The lists of the callers of `sync_with_cpp` might help us understand +how to improve the function definition. +## Profiling Python and C++ Code +### Generate the Profiling File -## Python与C++混合代码的性能分析 +To profile a mixture of Python and C++ code, we can use a Python +package, `yep`, that can work with Google's `perftools`, which is a +commonly-used profiler for C/C++ code. -### 生成性能分析文件 - -C++的性能分析工具非常多。常见的包括`gprof`, `valgrind`, `google-perftools`。但是调试Python中使用的动态链接库与直接调试原始二进制相比增加了很多复杂度。幸而Python的一个第三方库`yep`提供了方便的和`google-perftools`交互的方法。于是这里使用`yep`进行Python与C++混合代码的性能分析 - -使用`yep`前需要安装`google-perftools`与`yep`包。ubuntu下安装命令为 +In Ubuntu systems, we can install `yep` and `perftools` by running the +following commands: ```bash +apt update apt install libgoogle-perftools-dev pip install yep ``` -安装完毕后,我们可以通过 +Then we can run the following command ```bash python -m yep -v main.py ``` -生成性能分析文件。生成的性能分析文件为`main.py.prof`。 +to generate the profiling file. The default filename is +`main.py.prof`. + +Please be aware of the `-v` command line option, which prints the +analysis results after generating the profiling file. By taking a +glance at the print result, we'd know that if we stripped debug +information from `libpaddle.so` at build time. The following hints +help make sure that the analysis results are readable: -命令行中的`-v`指定在生成性能分析文件之后,在命令行显示分析结果。我们可以在命令行中简单的看一下生成效果。因为C++与Python不同,编译时可能会去掉调试信息,运行时也可能因为多线程产生混乱不可读的性能分析结果。为了生成更可读的性能分析结果,可以采取下面几点措施: +1. Use GCC command line option `-g` when building `libpaddle.so` so to + include the debug information. The standard building system of + PaddlePaddle is CMake, so you might want to set + `CMAKE_BUILD_TYPE=RelWithDebInfo`. -1. 编译时指定`-g`生成调试信息。使用cmake的话,可以将CMAKE_BUILD_TYPE指定为`RelWithDebInfo`。 -2. 编译时一定要开启优化。单纯的`Debug`编译性能会和`-O2`或者`-O3`有非常大的差别。`Debug`模式下的性能测试是没有意义的。 -3. 运行性能分析的时候,先从单线程开始,再开启多线程,进而多机。毕竟单线程调试更容易。可以设置`OMP_NUM_THREADS=1`这个环境变量关闭openmp优化。 +1. Use GCC command line option `-O2` or `-O3` to generate optimized + binary code. It doesn't make sense to profile `libpaddle.so` + without optimization, because it would anyway run slowly. -### 查看性能分析文件 +1. Profiling the single-threaded binary file before the + multi-threading version, because the latter often generates tangled + profiling analysis result. You might want to set environment + variable `OMP_NUM_THREADS=1` to prevents OpenMP from automatically + starting multiple threads. -在运行完性能分析后,会生成性能分析结果文件。我们可以使用[pprof](https://github.com/google/pprof)来显示性能分析结果。注意,这里使用了用`Go`语言重构后的`pprof`,因为这个工具具有web服务界面,且展示效果更好。 +### Look into the Profiling File -安装`pprof`的命令和一般的`Go`程序是一样的,其命令如下: +The tool we used to look into the profiling file generated by +`perftools` is [`pprof`](https://github.com/google/pprof), which +provides a Web-based GUI like `cprofilev`. + +We can rely on the standard Go toolchain to retrieve the source code +of `pprof` and build it: ```bash go get github.com/google/pprof ``` -进而我们可以使用如下命令开启一个HTTP服务: +Then we can use it to profile `main.py.prof` generated in the previous +section: ```bash pprof -http=0.0.0.0:3213 `which python` ./main.py.prof ``` -这行命令中,`-http`指开启HTTP服务。`which python`会产生当前Python二进制的完整路径,进而指定了Python可执行文件的路径。`./main.py.prof`输入了性能分析结果。 - -访问对应的网址,我们可以查看性能分析的结果。结果如下图所示: +Where `-http` specifies the IP and port of the HTTP service. +Directing our Web browser to the service, we would see something like +the following: ![result](./pprof_1.png) +### Identifying the Performance Bottlenecks -### 寻找性能瓶颈 - -与寻找Python代码的性能瓶颈类似,寻找Python与C++混合代码的性能瓶颈也是要看`tottime`和`cumtime`。而`pprof`展示的调用图也可以帮助我们发现性能中的问题。 - -例如下图中, +Similar to how we work with `cprofilev`, we'd focus on `tottime` and +`cumtime`. ![kernel_perf](./pprof_2.png) -在一次训练中,乘法和乘法梯度的计算占用2%-4%左右的计算时间。而`MomentumOp`占用了17%左右的计算时间。显然,`MomentumOp`的性能有问题。 - -在`pprof`中,对于性能的关键路径都做出了红色标记。先检查关键路径的性能问题,再检查其他部分的性能问题,可以更有次序的完成性能的优化。 - -## 总结 +We can see that the execution time of multiplication and the computing +of the gradient of multiplication takes 2% to 4% of the total running +time, and `MomentumOp` takes about 17%. Obviously, we'd want to +optimize `MomentumOp`. -至此,两种性能分析的方式都介绍完毕了。希望通过这两种性能分析的方式,Paddle的开发人员和使用人员可以有次序的,科学的发现和解决性能问题。 +`pprof` would mark performance critical parts of the program in +red. It's a good idea to follow the hint. diff --git a/doc/howto/optimization/cpu_profiling_cn.md b/doc/howto/optimization/cpu_profiling_cn.md new file mode 100644 index 0000000000..14eba0e2f3 --- /dev/null +++ b/doc/howto/optimization/cpu_profiling_cn.md @@ -0,0 +1,155 @@ +此教程会介绍如何使用Python的cProfile包、Python库yep、Google perftools来进行性能分析 (profiling) 与调优(performance tuning)。 + +Profling 指发现性能瓶颈。系统中的瓶颈可能和程序员开发过程中想象的瓶颈相去甚远。Tuning 指消除瓶颈。性能优化的过程通常是不断重复地 profiling 和 tuning。 + +PaddlePaddle 用户一般通过调用 Python API 编写深度学习程序。大部分 Python API 调用用 C++ 写的 libpaddle.so。所以 PaddlePaddle 的性能分析与调优分为两个部分: + +* Python 代码的性能分析 +* Python 与 C++ 混合代码的性能分析 + + +## Python代码的性能分析 + +### 生成性能分析文件 + +Python标准库中提供了性能分析的工具包,[cProfile](https://docs.python.org/2/library/profile.html)。生成Python性能分析的命令如下: + +```bash +python -m cProfile -o profile.out main.py +``` + +其中 `main.py` 是我们要分析的程序,`-o`标识了一个输出的文件名,用来存储本次性能分析的结果。如果不指定这个文件,`cProfile`会打印到标准输出。 + +### 查看性能分析文件 + +`cProfile` 在main.py 运行完毕后输出`profile.out`。我们可以使用[`cprofilev`](https://github.com/ymichael/cprofilev)来查看性能分析结果。`cprofilev`是一个Python的第三方库。使用它会开启一个HTTP服务,将性能分析结果以网页的形式展示出来: + +```bash +cprofilev -a 0.0.0.0 -p 3214 -f profile.out main.py +``` + +其中`-a`标识HTTP服务绑定的IP。使用`0.0.0.0`允许外网访问这个HTTP服务。`-p`标识HTTP服务的端口。`-f`标识性能分析的结果文件。`main.py`标识被性能分析的源文件。 + +用Web浏览器访问对应网址,即可显示性能分析的结果: + +``` + ncalls tottime percall cumtime percall filename:lineno(function) + 1 0.284 0.284 29.514 29.514 main.py:1() + 4696 0.128 0.000 15.748 0.003 /home/yuyang/perf_test/.env/lib/python2.7/site-packages/paddle/v2/fluid/executor.py:20(run) + 4696 12.040 0.003 12.040 0.003 {built-in method run} + 1 0.144 0.144 6.534 6.534 /home/yuyang/perf_test/.env/lib/python2.7/site-packages/paddle/v2/__init__.py:14() +``` + +每一列的含义是: + +| 列名 | 含义 | +| --- | --- | +| ncalls | 函数的调用次数 | +| tottime | 函数实际使用的总时间。该时间去除掉本函数调用其他函数的时间 | +| percall | tottime的每次调用平均时间 | +| cumtime | 函数总时间。包含这个函数调用其他函数的时间 | +| percall | cumtime的每次调用平均时间 | +| filename:lineno(function) | 文件名, 行号,函数名 | + + +### 寻找性能瓶颈 + +通常`tottime`和`cumtime`是寻找瓶颈的关键指标。这两个指标代表了某一个函数真实的运行时间。 + +将性能分析结果按照tottime排序,效果如下: + +```text + 4696 12.040 0.003 12.040 0.003 {built-in method run} + 300005 0.874 0.000 1.681 0.000 /home/yuyang/perf_test/.env/lib/python2.7/site-packages/paddle/v2/dataset/mnist.py:38(reader) + 107991 0.676 0.000 1.519 0.000 /home/yuyang/perf_test/.env/lib/python2.7/site-packages/paddle/v2/fluid/framework.py:219(__init__) + 4697 0.626 0.000 2.291 0.000 /home/yuyang/perf_test/.env/lib/python2.7/site-packages/paddle/v2/fluid/framework.py:428(sync_with_cpp) + 1 0.618 0.618 0.618 0.618 /home/yuyang/perf_test/.env/lib/python2.7/site-packages/paddle/v2/fluid/__init__.py:1() +``` + +可以看到最耗时的函数是C++端的`run`函数。这需要联合我们第二节`Python`与`C++`混合代码的性能分析来进行调优。而`sync_with_cpp`函数的总共耗时很长,每次调用的耗时也很长。于是我们可以点击`sync_with_cpp`的详细信息,了解其调用关系。 + +```text +Called By: + + Ordered by: internal time + List reduced from 4497 to 2 due to restriction <'sync_with_cpp'> + +Function was called by... + ncalls tottime cumtime +/home/yuyang/perf_test/.env/lib/python2.7/site-packages/paddle/v2/fluid/framework.py:428(sync_with_cpp) <- 4697 0.626 2.291 /home/yuyang/perf_test/.env/lib/python2.7/site-packages/paddle/v2/fluid/framework.py:562(sync_with_cpp) +/home/yuyang/perf_test/.env/lib/python2.7/site-packages/paddle/v2/fluid/framework.py:562(sync_with_cpp) <- 4696 0.019 2.316 /home/yuyang/perf_test/.env/lib/python2.7/site-packages/paddle/v2/fluid/framework.py:487(clone) + 1 0.000 0.001 /home/yuyang/perf_test/.env/lib/python2.7/site-packages/paddle/v2/fluid/framework.py:534(append_backward) + + +Called: + + Ordered by: internal time + List reduced from 4497 to 2 due to restriction <'sync_with_cpp'> +``` + +通常观察热点函数间的调用关系,和对应行的代码,就可以了解到问题代码在哪里。当我们做出性能修正后,再次进行性能分析(profiling)即可检查我们调优后的修正是否能够改善程序的性能。 + + + +## Python与C++混合代码的性能分析 + +### 生成性能分析文件 + +C++的性能分析工具非常多。常见的包括`gprof`, `valgrind`, `google-perftools`。但是调试Python中使用的动态链接库与直接调试原始二进制相比增加了很多复杂度。幸而Python的一个第三方库`yep`提供了方便的和`google-perftools`交互的方法。于是这里使用`yep`进行Python与C++混合代码的性能分析 + +使用`yep`前需要安装`google-perftools`与`yep`包。ubuntu下安装命令为 + +```bash +apt update +apt install libgoogle-perftools-dev +pip install yep +``` + +安装完毕后,我们可以通过 + +```bash +python -m yep -v main.py +``` + +生成性能分析文件。生成的性能分析文件为`main.py.prof`。 + +命令行中的`-v`指定在生成性能分析文件之后,在命令行显示分析结果。我们可以在命令行中简单的看一下生成效果。因为C++与Python不同,编译时可能会去掉调试信息,运行时也可能因为多线程产生混乱不可读的性能分析结果。为了生成更可读的性能分析结果,可以采取下面几点措施: + +1. 编译时指定`-g`生成调试信息。使用cmake的话,可以将CMAKE_BUILD_TYPE指定为`RelWithDebInfo`。 +2. 编译时一定要开启优化。单纯的`Debug`编译性能会和`-O2`或者`-O3`有非常大的差别。`Debug`模式下的性能测试是没有意义的。 +3. 运行性能分析的时候,先从单线程开始,再开启多线程,进而多机。毕竟单线程调试更容易。可以设置`OMP_NUM_THREADS=1`这个环境变量关闭openmp优化。 + +### 查看性能分析文件 + +在运行完性能分析后,会生成性能分析结果文件。我们可以使用[`pprof`](https://github.com/google/pprof)来显示性能分析结果。注意,这里使用了用`Go`语言重构后的`pprof`,因为这个工具具有web服务界面,且展示效果更好。 + +安装`pprof`的命令和一般的`Go`程序是一样的,其命令如下: + +```bash +go get github.com/google/pprof +``` + +进而我们可以使用如下命令开启一个HTTP服务: + +```bash +pprof -http=0.0.0.0:3213 `which python` ./main.py.prof +``` + +这行命令中,`-http`指开启HTTP服务。`which python`会产生当前Python二进制的完整路径,进而指定了Python可执行文件的路径。`./main.py.prof`输入了性能分析结果。 + +访问对应的网址,我们可以查看性能分析的结果。结果如下图所示: + +![result](./pprof_1.png) + + +### 寻找性能瓶颈 + +与寻找Python代码的性能瓶颈类似,寻找Python与C++混合代码的性能瓶颈也是要看`tottime`和`cumtime`。而`pprof`展示的调用图也可以帮助我们发现性能中的问题。 + +例如下图中, + +![kernel_perf](./pprof_2.png) + +在一次训练中,乘法和乘法梯度的计算占用2%-4%左右的计算时间。而`MomentumOp`占用了17%左右的计算时间。显然,`MomentumOp`的性能有问题。 + +在`pprof`中,对于性能的关键路径都做出了红色标记。先检查关键路径的性能问题,再检查其他部分的性能问题,可以更有次序的完成性能的优化。 From 1238706d724687bea415d053111adee6cd0aa90b Mon Sep 17 00:00:00 2001 From: QI JUN Date: Thu, 30 Nov 2017 16:33:09 +0800 Subject: [PATCH 30/67] Refine unittest with setting gflags (#5476) * add gflags for C++ unittest --- cmake/generic.cmake | 8 ++-- paddle/memory/memory.cc | 27 +++++++++++--- paddle/optimizer/parameter_optimizer_test.cc | 5 --- paddle/optimizer/serialization_test.cc | 5 --- paddle/testing/CMakeLists.txt | 2 + paddle/testing/paddle_gtest_main.cc | 39 ++++++++++++++++++++ 6 files changed, 66 insertions(+), 20 deletions(-) create mode 100644 paddle/testing/paddle_gtest_main.cc diff --git a/cmake/generic.cmake b/cmake/generic.cmake index c917ca0ff4..9cf256fb6d 100644 --- a/cmake/generic.cmake +++ b/cmake/generic.cmake @@ -227,8 +227,8 @@ function(cc_test TARGET_NAME) set(multiValueArgs SRCS DEPS) cmake_parse_arguments(cc_test "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) add_executable(${TARGET_NAME} ${cc_test_SRCS}) - target_link_libraries(${TARGET_NAME} ${cc_test_DEPS} gtest gtest_main) - add_dependencies(${TARGET_NAME} ${cc_test_DEPS} gtest gtest_main) + target_link_libraries(${TARGET_NAME} ${cc_test_DEPS} paddle_gtest_main paddle_memory gtest gflags) + add_dependencies(${TARGET_NAME} ${cc_test_DEPS} paddle_gtest_main paddle_memory gtest gflags) add_test(NAME ${TARGET_NAME} COMMAND ${TARGET_NAME} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) endif() endfunction(cc_test) @@ -288,8 +288,8 @@ function(nv_test TARGET_NAME) set(multiValueArgs SRCS DEPS) cmake_parse_arguments(nv_test "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) cuda_add_executable(${TARGET_NAME} ${nv_test_SRCS}) - target_link_libraries(${TARGET_NAME} ${nv_test_DEPS} gtest gtest_main) - add_dependencies(${TARGET_NAME} ${nv_test_DEPS} gtest gtest_main) + target_link_libraries(${TARGET_NAME} ${nv_test_DEPS} paddle_gtest_main paddle_memory gtest gflags) + add_dependencies(${TARGET_NAME} ${nv_test_DEPS} paddle_gtest_main paddle_memory gtest gflags) add_test(${TARGET_NAME} ${TARGET_NAME}) endif() endfunction(nv_test) diff --git a/paddle/memory/memory.cc b/paddle/memory/memory.cc index 5eb1c44eb6..95cfe2525e 100644 --- a/paddle/memory/memory.cc +++ b/paddle/memory/memory.cc @@ -81,18 +81,33 @@ BuddyAllocator* GetGPUBuddyAllocator(int gpu_id) { } template <> -void* Alloc(platform::GPUPlace place, size_t size) { - return GetGPUBuddyAllocator(place.device)->Alloc(size); +size_t Used(platform::GPUPlace place) { + return GetGPUBuddyAllocator(place.device)->Used(); } template <> -void Free(platform::GPUPlace place, void* p) { - GetGPUBuddyAllocator(place.device)->Free(p); +void* Alloc(platform::GPUPlace place, size_t size) { + auto* buddy_allocator = GetGPUBuddyAllocator(place.device); + auto* ptr = buddy_allocator->Alloc(size); + if (ptr == nullptr) { + int cur_dev = platform::GetCurrentDeviceId(); + platform::SetDeviceId(place.device); + size_t avail, total; + platform::GpuMemoryUsage(avail, total); + LOG(WARNING) << "Cannot allocate " << size << " bytes in GPU " + << place.device << ", available " << avail << " bytes"; + LOG(WARNING) << "total " << total; + LOG(WARNING) << "GpuMinChunkSize " << platform::GpuMinChunkSize(); + LOG(WARNING) << "GpuMaxChunkSize " << platform::GpuMaxChunkSize(); + LOG(WARNING) << "GPU memory used: " << Used(place); + platform::SetDeviceId(cur_dev); + } + return ptr; } template <> -size_t Used(platform::GPUPlace place) { - return GetGPUBuddyAllocator(place.device)->Used(); +void Free(platform::GPUPlace place, void* p) { + GetGPUBuddyAllocator(place.device)->Free(p); } #endif diff --git a/paddle/optimizer/parameter_optimizer_test.cc b/paddle/optimizer/parameter_optimizer_test.cc index f29e531712..83757a3917 100644 --- a/paddle/optimizer/parameter_optimizer_test.cc +++ b/paddle/optimizer/parameter_optimizer_test.cc @@ -127,8 +127,3 @@ TEST_F(OptimizerTest, TestGetWeight) { TestGetWeight(); } TEST_F(OptimizerTest, TestUpdate) { TestUpdate(); } TEST_F(OptimizerTest, TestCheckPoint) { TestCheckPoint(); } - -int main(int argc, char** argv) { - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/paddle/optimizer/serialization_test.cc b/paddle/optimizer/serialization_test.cc index 4c416f55ee..940e941e90 100644 --- a/paddle/optimizer/serialization_test.cc +++ b/paddle/optimizer/serialization_test.cc @@ -46,8 +46,3 @@ TEST(TensorToProto, Case2) { EXPECT_EQ(t1[i], t[i]); } } - -int main(int argc, char** argv) { - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/paddle/testing/CMakeLists.txt b/paddle/testing/CMakeLists.txt index 4245df5ab7..2275c950ba 100644 --- a/paddle/testing/CMakeLists.txt +++ b/paddle/testing/CMakeLists.txt @@ -5,4 +5,6 @@ if(WITH_TESTING) add_dependencies(paddle_test_main paddle_proto ${external_project_dependencies}) add_library(paddle_test_util STATIC TestUtil.cpp) add_dependencies(paddle_test_util paddle_proto ${external_project_dependencies}) + add_library(paddle_gtest_main STATIC paddle_gtest_main.cc) + add_dependencies(paddle_gtest_main paddle_memory gtest gflags) endif() diff --git a/paddle/testing/paddle_gtest_main.cc b/paddle/testing/paddle_gtest_main.cc new file mode 100644 index 0000000000..a491322b7e --- /dev/null +++ b/paddle/testing/paddle_gtest_main.cc @@ -0,0 +1,39 @@ +/* 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 +#include "gflags/gflags.h" +#include "gtest/gtest.h" +#include "paddle/memory/memory.h" + +int main(int argc, char** argv) { + std::vector new_argv; + std::string gflags_env; + new_argv.push_back(argv[0]); +#ifdef PADDLE_WITH_CUDA + new_argv.push_back( + strdup("--tryfromenv=fraction_of_gpu_memory_to_use,use_pinned_memory")); +#else + new_argv.push_back(strdup("--tryfromenv=use_pinned_memory")); +#endif + int new_argc = static_cast(new_argv.size()); + char** new_argv_address = new_argv.data(); + google::ParseCommandLineFlags(&new_argc, &new_argv_address, false); + testing::InitGoogleTest(&argc, argv); + paddle::memory::Used(paddle::platform::CPUPlace()); +#ifdef PADDLE_WITH_CUDA + paddle::memory::Used(paddle::platform::GPUPlace(0)); +#endif + return RUN_ALL_TESTS(); +} From 4c95301e98eb031a702831dd75312bf743c88c8a Mon Sep 17 00:00:00 2001 From: Luo Tao Date: Thu, 30 Nov 2017 16:55:00 +0800 Subject: [PATCH 31/67] add WITH_DOC for print_operators_doc --- paddle/pybind/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/paddle/pybind/CMakeLists.txt b/paddle/pybind/CMakeLists.txt index a54dc0d9fd..fd55f410d3 100644 --- a/paddle/pybind/CMakeLists.txt +++ b/paddle/pybind/CMakeLists.txt @@ -5,4 +5,6 @@ if(WITH_PYTHON) ${GLOB_OP_LIB}) endif(WITH_PYTHON) -cc_binary(print_operators_doc SRCS print_operators_doc.cc DEPS ${GLOB_OP_LIB}) +if(WITH_DOC) + cc_binary(print_operators_doc SRCS print_operators_doc.cc DEPS ${GLOB_OP_LIB}) +endif(WITH_DOC) From a38c1512437531d429d25254e774c2f9bc29e31e Mon Sep 17 00:00:00 2001 From: fengjiayi Date: Thu, 30 Nov 2017 17:20:39 +0800 Subject: [PATCH 32/67] Add GetInputsElementDim (#6091) --- paddle/framework/shape_inference.cc | 6 ++++++ paddle/framework/shape_inference.h | 1 + paddle/operators/while_op.cc | 6 +++--- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/paddle/framework/shape_inference.cc b/paddle/framework/shape_inference.cc index 0af41b164f..2298507471 100644 --- a/paddle/framework/shape_inference.cc +++ b/paddle/framework/shape_inference.cc @@ -22,6 +22,12 @@ std::vector InferShapeContext::GetInputsDim( return GetDims(names); } +DDim InferShapeContext::GetInputsElementDim(const std::string &name, + int idx) const { + const std::vector &names = Inputs(name); + return this->GetDim(names[idx]); +} + void InferShapeContext::SetOutputsDim( const std::string &name, const std::vector &dims) { auto &names = Outputs(name); diff --git a/paddle/framework/shape_inference.h b/paddle/framework/shape_inference.h index 05dc47f06a..46f2ea84b4 100644 --- a/paddle/framework/shape_inference.h +++ b/paddle/framework/shape_inference.h @@ -37,6 +37,7 @@ class InferShapeContext { virtual framework::DDim GetInputDim(const std::string &name) const = 0; std::vector GetInputsDim(const std::string &name) const; + DDim GetInputsElementDim(const std::string &name, int idx) const; virtual void SetOutputDim(const std::string &name, const DDim &dim) = 0; void SetOutputsDim(const std::string &name, diff --git a/paddle/operators/while_op.cc b/paddle/operators/while_op.cc index 68b4f77059..59460f6c87 100644 --- a/paddle/operators/while_op.cc +++ b/paddle/operators/while_op.cc @@ -287,7 +287,6 @@ class WhileGradOpShapeInference : public framework::InferShapeBase { auto p_names = ctx->Inputs(kParameters); auto pg_names = ctx->Outputs(kParamGrads); - auto dims = ctx->GetInputsDim(kParameters); auto var_types = ctx->GetInputsVarType(kParameters); std::vector names_to_set; std::vector dims_to_set; @@ -295,13 +294,14 @@ class WhileGradOpShapeInference : public framework::InferShapeBase { if (pg_names[i] == framework::kEmptyVarName) { continue; } + auto dims = ctx->GetInputsElementDim(kParameters, i); if (var_types[i] == framework::VarDesc::LOD_TENSOR) { names_to_set.push_back(pg_names[i]); - dims_to_set.push_back(dims[i]); + dims_to_set.push_back(dims); } else if (var_types[i] == framework::VarDesc::LOD_TENSOR_ARRAY) { // not sure how to set the dim of LOD_TENSOR_ARRAY names_to_set.push_back(pg_names[i]); - dims_to_set.push_back(dims[i]); + dims_to_set.push_back(dims); } } ctx->SetDims(names_to_set, dims_to_set); From 4e564e4852f3733027e7eb382c9f4b660a9e0d3d Mon Sep 17 00:00:00 2001 From: fengjiayi Date: Thu, 30 Nov 2017 17:26:07 +0800 Subject: [PATCH 33/67] make WriteToArrayOp supporting empty tensor input (#6030) --- paddle/operators/tensor_array_read_write_op.cc | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/paddle/operators/tensor_array_read_write_op.cc b/paddle/operators/tensor_array_read_write_op.cc index ad09fb53ce..efde850143 100644 --- a/paddle/operators/tensor_array_read_write_op.cc +++ b/paddle/operators/tensor_array_read_write_op.cc @@ -37,9 +37,15 @@ class WriteToArrayOp : public ArrayOp { << " to " << offset + 1; out->resize(offset + 1); } - auto *out_tensor = &out->at(offset); - CopyFrom(x_tensor, dev_ctx.GetPlace(), dev_ctx, out_tensor); - out_tensor->set_lod(x_tensor.lod()); + if (x_tensor.memory_size() > 0) { + auto *out_tensor = &out->at(offset); + CopyFrom(x_tensor, dev_ctx.GetPlace(), dev_ctx, out_tensor); + out_tensor->set_lod(x_tensor.lod()); + } else { + VLOG(10) << "WARNING: The input tensor 'x_tensor' holds no memory, so " + "nothing has been written to output array[" + << offset << "]."; + } } }; From 0d40a4dbc6f6a8c1b50d65fa096cadd55dae71c4 Mon Sep 17 00:00:00 2001 From: Yu Yang Date: Thu, 30 Nov 2017 17:43:11 +0800 Subject: [PATCH 34/67] Add lod_level for data layer (#6040) --- python/paddle/v2/fluid/layers.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python/paddle/v2/fluid/layers.py b/python/paddle/v2/fluid/layers.py index 9dcc11d216..5a977978bf 100644 --- a/python/paddle/v2/fluid/layers.py +++ b/python/paddle/v2/fluid/layers.py @@ -185,6 +185,7 @@ def data(name, shape, append_batch_size=True, dtype='float32', + lod_level=0, type=core.VarDesc.VarType.LOD_TENSOR, main_program=None, startup_program=None, @@ -198,6 +199,7 @@ def data(name, append_batch_size: Whether or not to append the data as a batch. dtype: The type of data : float32, float_16, int etc type: The output type. By default it is LOD_TENSOR. + lod_level(int): The LoD Level. 0 means the input data is not a sequence. main_program: Name of the main program that calls this startup_program: Name of the startup program stop_gradient: A boolean that mentions whether gradient should flow. @@ -228,7 +230,8 @@ def data(name, shape=shape, dtype=dtype, type=type, - stop_gradient=stop_gradient) + stop_gradient=stop_gradient, + lod_level=lod_level) def create_tensor(dtype, name=None, main_program=None, startup_program=None): From 4d47683b1cd2ef2a6bfce70af34743dac56d1f5e Mon Sep 17 00:00:00 2001 From: dangqingqing Date: Thu, 30 Nov 2017 19:23:43 +0800 Subject: [PATCH 35/67] Use protobuf v3.2.0 for MOBILE_INFERENCE compiling. --- cmake/external/protobuf.cmake | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/cmake/external/protobuf.cmake b/cmake/external/protobuf.cmake index 7cfe1e6807..7ae0b16b08 100644 --- a/cmake/external/protobuf.cmake +++ b/cmake/external/protobuf.cmake @@ -188,14 +188,24 @@ FUNCTION(build_protobuf TARGET_NAME BUILD_FOR_HOST) SET(OPTIONAL_CACHE_ARGS "-DZLIB_ROOT:STRING=${ZLIB_ROOT}") ENDIF() + SET(PROTOBUF_REPO "https://github.com/google/protobuf.git") + SET(PROTOBUF_TAG "9f75c5aa851cd877fb0d93ccc31b8567a6706546") + IF(MOBILE_INFERENCE) + SET(PROTOBUF_REPO "https://github.com/qingqing01/protobuf.git") + SET(PROTOBUF_TAG "v3.2.0") + IF(NOT BUILD_FOR_HOST) + SET(OPTIONAL_ARGS ${OPTIONAL_ARGS} "-Dprotobuf_BUILD_PROTOC_BINARIES=OFF") + ENDIF() + ENDIF() + ExternalProject_Add( ${TARGET_NAME} ${EXTERNAL_PROJECT_LOG_ARGS} PREFIX ${PROTOBUF_SOURCES_DIR} UPDATE_COMMAND "" DEPENDS zlib - GIT_REPOSITORY "https://github.com/google/protobuf.git" - GIT_TAG "9f75c5aa851cd877fb0d93ccc31b8567a6706546" + GIT_REPOSITORY ${PROTOBUF_REPO} + GIT_TAG ${PROTOBUF_TAG} CONFIGURE_COMMAND ${CMAKE_COMMAND} ${PROTOBUF_SOURCES_DIR}/src/${TARGET_NAME}/cmake ${OPTIONAL_ARGS} @@ -213,7 +223,11 @@ FUNCTION(build_protobuf TARGET_NAME BUILD_FOR_HOST) ) ENDFUNCTION() -SET(PROTOBUF_VERSION 3.1) +IF(NOT MOBILE_INFERENCE) + SET(PROTOBUF_VERSION 3.1) +ELSE() + SET(PROTOBUF_VERSION 3.2) +ENDIF() IF(CMAKE_CROSSCOMPILING) build_protobuf(protobuf_host TRUE) LIST(APPEND external_project_dependencies protobuf_host) From a0648ee449335ba4989a83230874089b8685e3ad Mon Sep 17 00:00:00 2001 From: dangqingqing Date: Thu, 30 Nov 2017 20:37:23 +0800 Subject: [PATCH 36/67] Add comments. --- cmake/external/protobuf.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmake/external/protobuf.cmake b/cmake/external/protobuf.cmake index 7ae0b16b08..fab2af362b 100644 --- a/cmake/external/protobuf.cmake +++ b/cmake/external/protobuf.cmake @@ -191,6 +191,8 @@ FUNCTION(build_protobuf TARGET_NAME BUILD_FOR_HOST) SET(PROTOBUF_REPO "https://github.com/google/protobuf.git") SET(PROTOBUF_TAG "9f75c5aa851cd877fb0d93ccc31b8567a6706546") IF(MOBILE_INFERENCE) + # The reason why the official version is not used is described in + # https://github.com/PaddlePaddle/Paddle/issues/6114 SET(PROTOBUF_REPO "https://github.com/qingqing01/protobuf.git") SET(PROTOBUF_TAG "v3.2.0") IF(NOT BUILD_FOR_HOST) From 79b17097f65a0c6a0b25eb7385b423c01129f003 Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Fri, 1 Dec 2017 00:27:43 +0800 Subject: [PATCH 37/67] cal FPS of inference result --- benchmark/paddle/image/provider.py | 2 +- benchmark/paddle/image/run_mkldnn_infer.sh | 22 ++++++++++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/benchmark/paddle/image/provider.py b/benchmark/paddle/image/provider.py index a3a6b6fc4d..927b175994 100644 --- a/benchmark/paddle/image/provider.py +++ b/benchmark/paddle/image/provider.py @@ -23,7 +23,7 @@ def initHook(settings, height, width, color, num_class, **kwargs): @provider( init_hook=initHook, min_pool_size=-1, cache=CacheType.CACHE_PASS_IN_MEM) def process(settings, file_list): - for i in xrange(1024): + for i in xrange(2560 if settings.is_infer else 1024): img = np.random.rand(1, settings.data_size).reshape(-1, 1).flatten() if settings.is_infer: yield img.astype('float32') diff --git a/benchmark/paddle/image/run_mkldnn_infer.sh b/benchmark/paddle/image/run_mkldnn_infer.sh index 3081d5e7b5..03a76c0540 100755 --- a/benchmark/paddle/image/run_mkldnn_infer.sh +++ b/benchmark/paddle/image/run_mkldnn_infer.sh @@ -1,5 +1,12 @@ set -e +function clock_to_seconds() { + hours=`echo $1 | awk -F ':' '{print $1}'` + mins=`echo $1 | awk -F ':' '{print $2}'` + secs=`echo $1 | awk -F ':' '{print $3}'` + echo `bc -l <<< "$secs + $mins * 60 + $hours * 3600"` +} + function infer() { unset OMP_NUM_THREADS MKL_NUM_THREADS OMP_DYNAMIC KMP_AFFINITY topology=$1 @@ -34,15 +41,26 @@ function infer() { > /dev/null 2>&1 echo "Done" fi + log_period=$((256 / bs)) paddle train --job=test \ --config="${topology}.py" \ --use_mkldnn=$use_mkldnn \ --use_gpu=False \ --trainer_count=$thread \ - --log_period=32 \ + --log_period=$log_period \ --config_args="batch_size=${bs},layer_num=${layer_num},is_infer=True" \ --init_model_path=$models_in \ - 2>&1 | tee ${log} + 2>&1 | tee ${log} + + # calculate the last 5 logs period time of 1280 samples, + # the time before are burning time. + start=`tail ${log} -n 7 | head -n 1 | awk -F ' ' '{print $2}' | xargs` + end=`tail ${log} -n 2 | head -n 1 | awk -F ' ' '{print $2}' | xargs` + start_sec=`clock_to_seconds $start` + end_sec=`clock_to_seconds $end` + fps=`bc <<< "scale = 2; 1280 / ($end_sec - $start_sec)"` + echo "Last 1280 samples start: ${start}(${start_sec} sec), end: ${end}(${end_sec} sec;" >> ${log} + echo "FPS: $fps images/sec" >> ${log} } if [ ! -f "train.list" ]; then From d36db0d3ec9a5ef3cc30c2c55ec2e28541ca9b1a Mon Sep 17 00:00:00 2001 From: xuwei06 Date: Thu, 30 Nov 2017 09:40:38 -0800 Subject: [PATCH 38/67] Fix comments in sequence_rnn_(mixed/matched)_inputs.py --- paddle/gserver/tests/sequence_rnn_matched_inputs.py | 2 +- paddle/gserver/tests/sequence_rnn_mixed_inputs.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/paddle/gserver/tests/sequence_rnn_matched_inputs.py b/paddle/gserver/tests/sequence_rnn_matched_inputs.py index e2635b4400..59e8c91733 100644 --- a/paddle/gserver/tests/sequence_rnn_matched_inputs.py +++ b/paddle/gserver/tests/sequence_rnn_matched_inputs.py @@ -41,7 +41,7 @@ nonseq = embedding_layer(input=label, size=word_dim) # This hierarchical RNN is designed to be equivalent to the simple RNN in -# sequence_rnn_multi_unequalength_inputs.conf +# sequence_rnn_mixed_inputs.conf def outer_step(subseq, seq, nonseq, encoding): outer_mem = memory(name="outer_rnn_state", size=hidden_dim) diff --git a/paddle/gserver/tests/sequence_rnn_mixed_inputs.py b/paddle/gserver/tests/sequence_rnn_mixed_inputs.py index 84a66e2944..6fe9dca6e2 100644 --- a/paddle/gserver/tests/sequence_rnn_mixed_inputs.py +++ b/paddle/gserver/tests/sequence_rnn_mixed_inputs.py @@ -37,7 +37,7 @@ encoding = embedding_layer(input=data2, size=word_dim) # This hierarchical RNN is designed to be equivalent to the simple RNN in -# sequence_rnn_multi_unequalength_inputs.conf +# sequence_rnn_matched_inputs.conf def outer_step(subseq, seq, nonseq, encoding): outer_mem = memory(name="outer_rnn_state", size=hidden_dim) From 6dc5b34e5b9fc462f6cbd5c96883981fe8258264 Mon Sep 17 00:00:00 2001 From: Abhinav Arora Date: Fri, 1 Dec 2017 03:25:04 +0530 Subject: [PATCH 39/67] Polishing the cpu profiling doc (#6116) --- doc/howto/optimization/cpu_profiling.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/doc/howto/optimization/cpu_profiling.md b/doc/howto/optimization/cpu_profiling.md index e1d91c668e..1775374cf6 100644 --- a/doc/howto/optimization/cpu_profiling.md +++ b/doc/howto/optimization/cpu_profiling.md @@ -1,13 +1,13 @@ -This tutorial introduces techniques we used to profile and tune the +This tutorial introduces techniques we use to profile and tune the CPU performance of PaddlePaddle. We will use Python packages -`cProfile` and `yep`, and Google `perftools`. +`cProfile` and `yep`, and Google's `perftools`. -Profiling is the process that reveals the performance bottlenecks, +Profiling is the process that reveals performance bottlenecks, which could be very different from what's in the developers' mind. -Performance tuning is to fix the bottlenecks. Performance optimization +Performance tuning is done to fix these bottlenecks. Performance optimization repeats the steps of profiling and tuning alternatively. -PaddlePaddle users program AI by calling the Python API, which calls +PaddlePaddle users program AI applications by calling the Python API, which calls into `libpaddle.so.` written in C++. In this tutorial, we focus on the profiling and tuning of @@ -82,7 +82,7 @@ focus on. We can sort above profiling file by tottime: We can see that the most time-consuming function is the `built-in method run`, which is a C++ function in `libpaddle.so`. We will -explain how to profile C++ code in the next section. At the right +explain how to profile C++ code in the next section. At this moment, let's look into the third function `sync_with_cpp`, which is a Python function. We can click it to understand more about it: @@ -135,8 +135,8 @@ to generate the profiling file. The default filename is `main.py.prof`. Please be aware of the `-v` command line option, which prints the -analysis results after generating the profiling file. By taking a -glance at the print result, we'd know that if we stripped debug +analysis results after generating the profiling file. By examining the + the print result, we'd know that if we stripped debug information from `libpaddle.so` at build time. The following hints help make sure that the analysis results are readable: @@ -155,9 +155,9 @@ help make sure that the analysis results are readable: variable `OMP_NUM_THREADS=1` to prevents OpenMP from automatically starting multiple threads. -### Look into the Profiling File +### Examining the Profiling File -The tool we used to look into the profiling file generated by +The tool we used to examine the profiling file generated by `perftools` is [`pprof`](https://github.com/google/pprof), which provides a Web-based GUI like `cprofilev`. @@ -194,4 +194,4 @@ time, and `MomentumOp` takes about 17%. Obviously, we'd want to optimize `MomentumOp`. `pprof` would mark performance critical parts of the program in -red. It's a good idea to follow the hint. +red. It's a good idea to follow the hints. From 3a8311f819977acdbfe35e884846e0201d9211cd Mon Sep 17 00:00:00 2001 From: Yu Yang Date: Fri, 1 Dec 2017 10:23:29 +0800 Subject: [PATCH 40/67] Fix compile error for gcc 6.3 (#6112) --- cmake/flags.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmake/flags.cmake b/cmake/flags.cmake index 2b125cef6a..1120677a37 100644 --- a/cmake/flags.cmake +++ b/cmake/flags.cmake @@ -111,6 +111,8 @@ set(COMMON_FLAGS -Wno-error=sign-compare -Wno-error=unused-local-typedefs -Wno-error=parentheses-equality # Warnings in pybind11 + -Wno-error=ignored-attributes # Warnings in Eigen, gcc 6.3 + -Wno-error=terminate # Warning in PADDLE_ENFORCE ) set(GPU_COMMON_FLAGS From 8ac02279f28e2e944b983793bc91da8e58a75e94 Mon Sep 17 00:00:00 2001 From: Yu Yang Date: Fri, 1 Dec 2017 10:23:47 +0800 Subject: [PATCH 41/67] Fix the proformance problem of enforce (#6085) * Fix Proformance problem of enforce * Fix missing `;` in code * Fix CI --- paddle/operators/concat_op.cc | 4 ++-- paddle/operators/elementwise_op.h | 4 ++-- paddle/operators/elementwise_op_function.h | 2 +- paddle/operators/sequence_slice_op.h | 10 ++++---- paddle/operators/sum_op.h | 2 +- paddle/platform/enforce.h | 28 ++++++++++++++-------- 6 files changed, 29 insertions(+), 21 deletions(-) diff --git a/paddle/operators/concat_op.cc b/paddle/operators/concat_op.cc index 5f05268925..6134ac78b1 100644 --- a/paddle/operators/concat_op.cc +++ b/paddle/operators/concat_op.cc @@ -25,7 +25,7 @@ class ConcatOp : public framework::OperatorWithKernel { void InferShape(framework::InferShapeContext *ctx) const override { PADDLE_ENFORCE_GE(ctx->Inputs("X").size(), 1UL, - "Inputs(X) of ConcatOp should be empty.") + "Inputs(X) of ConcatOp should be empty."); PADDLE_ENFORCE(ctx->HasOutput("Out"), "Output(Out) of ConcatOp should not be null."); @@ -45,7 +45,7 @@ class ConcatOp : public framework::OperatorWithKernel { } PADDLE_ENFORCE_EQ(out_dims[j], ins[i][j], "Input tensors should have the same " - "elements except the specify axis.") + "elements except the specify axis."); } } ctx->SetOutputDim("Out", out_dims); diff --git a/paddle/operators/elementwise_op.h b/paddle/operators/elementwise_op.h index 56e5eb69bc..ea533503e4 100644 --- a/paddle/operators/elementwise_op.h +++ b/paddle/operators/elementwise_op.h @@ -35,7 +35,7 @@ class ElementwiseOp : public framework::OperatorWithKernel { auto x_dim = ctx->GetInputDim("X"); auto y_dim = ctx->GetInputDim("Y"); PADDLE_ENFORCE_GE(x_dim.size(), y_dim.size(), - "Rank of first input must >= rank of second input.") + "Rank of first input must >= rank of second input."); ctx->SetOutputDim("Out", x_dim); ctx->ShareLoD("X", /*->*/ "Out"); } @@ -120,7 +120,7 @@ class ElementwiseOpGrad : public framework::OperatorWithKernel { auto out_dims = ctx->GetInputDim(framework::GradVarName("Out")); PADDLE_ENFORCE_GE(x_dims.size(), y_dims.size(), - "Rank of first input must >= rank of second input.") + "Rank of first input must >= rank of second input."); auto x_grad_name = framework::GradVarName("X"); auto y_grad_name = framework::GradVarName("Y"); diff --git a/paddle/operators/elementwise_op_function.h b/paddle/operators/elementwise_op_function.h index 488a35aafc..8aa35b2c46 100644 --- a/paddle/operators/elementwise_op_function.h +++ b/paddle/operators/elementwise_op_function.h @@ -106,7 +106,7 @@ void ElementwiseCompute(const framework::ExecutionContext& ctx) { auto x_dims = x->dims(); auto y_dims = y->dims(); PADDLE_ENFORCE_GE(x_dims.size(), y_dims.size(), - "Rank of first input must >= rank of second input.") + "Rank of first input must >= rank of second input."); if (x_dims == y_dims) { functor f; diff --git a/paddle/operators/sequence_slice_op.h b/paddle/operators/sequence_slice_op.h index 6411e0a466..428ef556da 100644 --- a/paddle/operators/sequence_slice_op.h +++ b/paddle/operators/sequence_slice_op.h @@ -54,10 +54,10 @@ class SequenceSliceOpKernel : public framework::OpKernel { PADDLE_ENFORCE_EQ(lod.size(), 1UL, "Only support one level sequence now."); PADDLE_ENFORCE_EQ( n, static_cast(length->dims()[0]), - "The size of input-sequence and length-array should be the same") + "The size of input-sequence and length-array should be the same"); PADDLE_ENFORCE_EQ( n, static_cast(offset->dims()[0]), - "The size of input-sequence and offset-array should be the same") + "The size of input-sequence and offset-array should be the same"); const int64_t* offset_data = offset->data(); const int64_t* length_data = length->data(); @@ -78,11 +78,11 @@ class SequenceSliceOpKernel : public framework::OpKernel { for (size_t i = 0; i < n; ++i) { PADDLE_ENFORCE_LT(0, offset_data[i], - "The offset[%d] must greater than zero.", i) + "The offset[%d] must greater than zero.", i); PADDLE_ENFORCE_LT(0, length_data[i], - "The length[%d] must greater than zero.", i) + "The length[%d] must greater than zero.", i); PADDLE_ENFORCE_LT(lod[0][i] + offset_data[i] + length_data[i], - lod[0][i + 1], "The target tensor's length overflow.") + lod[0][i + 1], "The target tensor's length overflow."); } out->mutable_data(ctx.GetPlace()); diff --git a/paddle/operators/sum_op.h b/paddle/operators/sum_op.h index 4afec03ece..a1eb3b014e 100644 --- a/paddle/operators/sum_op.h +++ b/paddle/operators/sum_op.h @@ -84,7 +84,7 @@ class SumKernel : public framework::OpKernel { int64_t offset = 0; for (int i = 0; i < N; i++) { PADDLE_ENFORCE_EQ(out->height(), - in_vars[i]->Get().height()) + in_vars[i]->Get().height()); functor(context.device_context(), in_vars[i]->Get(), offset, out); offset += in_vars[i]->Get().value().numel(); diff --git a/paddle/platform/enforce.h b/paddle/platform/enforce.h index 415020ab96..97338a4ce6 100644 --- a/paddle/platform/enforce.h +++ b/paddle/platform/enforce.h @@ -234,16 +234,24 @@ inline void throw_on_error(T e) { __PADDLE_BINARY_COMPARE(__VAL0, __VAL1, <, >=, __VA_ARGS__) #define PADDLE_ENFORCE_LE(__VAL0, __VAL1, ...) \ __PADDLE_BINARY_COMPARE(__VAL0, __VAL1, <=, >, __VA_ARGS__) -#define PADDLE_ENFORCE_NOT_NULL(__VAL, ...) \ - PADDLE_ENFORCE(nullptr != (__VAL), #__VAL " should not be null\n%s", \ - paddle::string::Sprintf("" __VA_ARGS__)); - -#define __PADDLE_BINARY_COMPARE(__VAL0, __VAL1, __CMP, __INV_CMP, ...) \ - PADDLE_ENFORCE(__VAL0 __CMP __VAL1, \ - "enforce %s " #__CMP " %s failed, %s " #__INV_CMP " %s\n%s", \ - #__VAL0, #__VAL1, paddle::string::to_string(__VAL0), \ - paddle::string::to_string(__VAL1), \ - paddle::string::Sprintf("" __VA_ARGS__)); +#define PADDLE_ENFORCE_NOT_NULL(__VAL, ...) \ + do { \ + if (UNLIKELY(nullptr == (__VAL))) { \ + PADDLE_THROW(#__VAL " should not be null\n%s", \ + paddle::string::Sprintf("" __VA_ARGS__)); \ + } \ + } while (0) + +#define __PADDLE_BINARY_COMPARE(__VAL0, __VAL1, __CMP, __INV_CMP, ...) \ + do { \ + if (!UNLIKELY((__VAL0)__CMP(__VAL1))) { \ + PADDLE_THROW("enforce %s " #__CMP " %s failed, %s " #__INV_CMP \ + " %s\n%s", \ + #__VAL0, #__VAL1, paddle::string::to_string(__VAL0), \ + paddle::string::to_string(__VAL1), \ + paddle::string::Sprintf("" __VA_ARGS__)); \ + } \ + } while (0) } // namespace platform } // namespace paddle From 42708ded549cf4c731abd75df8e7b3ef797a4052 Mon Sep 17 00:00:00 2001 From: Yiqun Liu Date: Fri, 1 Dec 2017 13:04:08 +0800 Subject: [PATCH 42/67] Enable the case N != ldc in EigenBlasGemm. (#5976) * Enable the case N != ldc in EigenBlasGemm. * Use MemoryHandle instead of direct calling of posix_memalign to alloc temporary memory. * Use Eigen's slice() instead of a temporary memory. * Add if-else for different cases in EigenBlasGemm (for N ?= ldc). --- paddle/function/EigenGemm.cpp | 36 ++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/paddle/function/EigenGemm.cpp b/paddle/function/EigenGemm.cpp index b3e666e860..644098a9e7 100644 --- a/paddle/function/EigenGemm.cpp +++ b/paddle/function/EigenGemm.cpp @@ -21,7 +21,7 @@ template struct EigenBlasGemm { typedef Eigen::TensorMap, Eigen::Aligned> - Matrix; + EigenMatrix; static void compute(const bool transA, const bool transB, @@ -56,14 +56,13 @@ struct EigenBlasGemm { sizeB[1] = N; CHECK_EQ(N, ldb); } - Eigen::array sizeC; - sizeC[0] = M; - sizeC[1] = N; - CHECK_EQ(N, ldc); + Eigen::array sizeC = {{M, ldc}}; + Eigen::array offsetC = {{0, 0}}; + Eigen::array extentC = {{M, N}}; - const Matrix a(const_cast(A), sizeA); - const Matrix b(const_cast(B), sizeB); - Matrix c(C, sizeC); + const EigenMatrix a(const_cast(A), sizeA); + const EigenMatrix b(const_cast(B), sizeB); + EigenMatrix c(C, sizeC); typedef typename Eigen::Tensor::DimensionPair DimPair; Eigen::array dims; @@ -72,12 +71,23 @@ struct EigenBlasGemm { dims[0].second = transB ? 1 : 0; Eigen::DefaultDevice device; - if (alpha == T(1) && beta == T(0)) { - c.device(device) = a.contract(b, dims); - } else if (alpha == T(1) && beta == T(1)) { - c.device(device) += a.contract(b, dims); + if (N == ldc) { + if (alpha == T(1) && beta == T(0)) { + c.device(device) = a.contract(b, dims); + } else if (alpha == T(1) && beta == T(1)) { + c.device(device) += a.contract(b, dims); + } else { + c.device(device) = alpha * a.contract(b, dims) + beta * c; + } } else { - c.device(device) = alpha * a.contract(b, dims) + beta * c; + if (alpha == T(1) && beta == T(0)) { + c.slice(offsetC, extentC).device(device) = a.contract(b, dims); + } else if (alpha == T(1) && beta == T(1)) { + c.slice(offsetC, extentC).device(device) += a.contract(b, dims); + } else { + c.slice(offsetC, extentC).device(device) = + alpha * a.contract(b, dims) + beta * c.slice(offsetC, extentC); + } } } }; From ade6c8327812c52c91066bf8eeda3036a001d0dc Mon Sep 17 00:00:00 2001 From: QI JUN Date: Fri, 1 Dec 2017 13:07:15 +0800 Subject: [PATCH 43/67] open test_word2vec (#6104) --- python/paddle/v2/fluid/tests/book/test_word2vec.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/python/paddle/v2/fluid/tests/book/test_word2vec.py b/python/paddle/v2/fluid/tests/book/test_word2vec.py index 92d3629d42..1b441e15c7 100644 --- a/python/paddle/v2/fluid/tests/book/test_word2vec.py +++ b/python/paddle/v2/fluid/tests/book/test_word2vec.py @@ -58,10 +58,6 @@ train_reader = paddle.batch( place = fluid.CPUPlace() exe = fluid.Executor(place) -# fix https://github.com/PaddlePaddle/Paddle/issues/5434 then remove -# below exit line. -exit(0) - exe.run(fluid.default_startup_program()) for pass_id in range(PASS_NUM): @@ -79,6 +75,6 @@ for pass_id in range(PASS_NUM): 'nextw': input_data[4] }, fetch_list=[avg_cost]) - if avg_cost_np[0] < 10.0: + if avg_cost_np[0] < 5.0: exit(0) # if avg cost less than 10.0, we think our code is good. exit(1) From 1a852861b287c4b7c1bc8bcd8610acdc073e3164 Mon Sep 17 00:00:00 2001 From: typhoonzero Date: Fri, 1 Dec 2017 13:30:12 +0800 Subject: [PATCH 44/67] add switch for distributed support --- CMakeLists.txt | 1 + cmake/external/cares.cmake | 2 +- cmake/external/grpc.cmake | 2 +- paddle/operators/CMakeLists.txt | 5 ++++- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e76512166f..3bdb3b7388 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,6 +54,7 @@ option(WITH_C_API "Compile PaddlePaddle with C-API(Prediction)" OFF) option(WITH_GOLANG "Compile PaddlePaddle with GOLANG" OFF) option(GLIDE_INSTALL "Download and install go dependencies " ON) option(USE_NNPACK "Compile PaddlePaddle with NNPACK library" OFF) +option(WITH_DISTRIBUTE "Compile with grpc distributed support" OFF) option(USE_EIGEN_FOR_BLAS "Use matrix multiplication in Eigen" OFF) # CMAKE_BUILD_TYPE diff --git a/cmake/external/cares.cmake b/cmake/external/cares.cmake index e05111ee18..ac456933bd 100644 --- a/cmake/external/cares.cmake +++ b/cmake/external/cares.cmake @@ -13,7 +13,7 @@ # limitations under the License. # -IF(MOBILE_INFERENCE) +IF(MOBILE_INFERENCE OR NOT WITH_DISTRIBUTE) return() ENDIF() diff --git a/cmake/external/grpc.cmake b/cmake/external/grpc.cmake index 86122aec8c..abee6698e3 100644 --- a/cmake/external/grpc.cmake +++ b/cmake/external/grpc.cmake @@ -13,7 +13,7 @@ # limitations under the License. # -IF(MOBILE_INFERENCE) +IF(MOBILE_INFERENCE OR NOT WITH_DISTRIBUTE) return() ENDIF() diff --git a/paddle/operators/CMakeLists.txt b/paddle/operators/CMakeLists.txt index 937441b318..54d3881b8c 100644 --- a/paddle/operators/CMakeLists.txt +++ b/paddle/operators/CMakeLists.txt @@ -212,6 +212,7 @@ set(DEPS_OPS send_op recv_op) +if(WITH_DISTRIBUTE) add_subdirectory(detail) op_library(send_op SRCS send_op.cc DEPS sendrecvop_grpc grpc++_unsecure grpc_unsecure gpr cares zlib_target protobuf) set_source_files_properties( @@ -225,6 +226,9 @@ set_source_files_properties( PROPERTIES COMPILE_FLAGS "-Wno-error=non-virtual-dtor -Wno-error=delete-non-virtual-dtor") +cc_test(test_send_recv SRCS send_recv_op_test.cc DEPS send_op recv_op sum_op executor) +endif() + op_library(cond_op SRCS cond_op.cc DEPS framework_proto tensor operator net_op) op_library(cross_entropy_op DEPS cross_entropy) op_library(softmax_with_cross_entropy_op DEPS cross_entropy softmax) @@ -275,4 +279,3 @@ if(WITH_GPU) cc_test(nccl_op_test SRCS nccl_op_test.cu.cc DEPS nccl_op gpu_info device_context) endif() cc_test(save_load_op_test SRCS save_load_op_test.cc DEPS save_op load_op) -cc_test(test_send_recv SRCS send_recv_op_test.cc DEPS send_op recv_op sum_op executor) From d4fcd2a59fc3cdc6a750e695ea90e3a867c09a77 Mon Sep 17 00:00:00 2001 From: dangqingqing Date: Fri, 1 Dec 2017 14:01:34 +0800 Subject: [PATCH 45/67] Fix the doc of LSTM operator. --- paddle/operators/lstm_op.cc | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/paddle/operators/lstm_op.cc b/paddle/operators/lstm_op.cc index 4cbb60f3fd..fa8e5f2da8 100644 --- a/paddle/operators/lstm_op.cc +++ b/paddle/operators/lstm_op.cc @@ -181,7 +181,7 @@ class LSTMOpMaker : public framework::OpProtoAndCheckerMaker { AddComment(R"DOC( Long-Short Term Memory (LSTM) Operator. -The defalut implementation is diagonal/peephole connection +The defalut implementation is diagonal/peephole connection (https://arxiv.org/pdf/1402.1128.pdf), the formula is as follows: $$ @@ -198,27 +198,27 @@ c_t = f_t \odot c_{t-1} + i_t \odot \tilde{c_t} \\ h_t = o_t \odot act_h(c_t) $$ -where the W terms denote weight matrices (e.g. \f$W_{xi}\f$ is the matrix -of weights from the input gate to the input), \f$W_{ic}, W_{fc}, W_{oc}\f$ +where the W terms denote weight matrices (e.g. $W_{xi}$ is the matrix +of weights from the input gate to the input), $W_{ic}, W_{fc}, W_{oc}$ are diagonal weight matrices for peephole connections. In our implementation, we use vectors to reprenset these diagonal weight matrices. The b terms -denote bias vectors (\f$b_i\f$ is the input gate bias vector), \f$\sigma\f$ +denote bias vectors ($b_i$ is the input gate bias vector), $\sigma$ is the non-line activations, such as logistic sigmoid function, and -\f$i, f, o\f$ and \f$c\f$ are the input gate, forget gate, output gate, +$i, f, o$ and $c$ are the input gate, forget gate, output gate, and cell activation vectors, respectively, all of which have the same size as -the cell output activation vector \f$h\f$. +the cell output activation vector $h$. -The \f$\odot\f$ is the element-wise product of the vectors. \f$act_g\f$ and \f$act_h\f$ +The $\odot$ is the element-wise product of the vectors. $act_g$ and $act_h$ are the cell input and cell output activation functions and `tanh` is usually -used for them. \f$\tilde{c_t}\f$ is also called candidate hidden state, +used for them. $\tilde{c_t}$ is also called candidate hidden state, which is computed based on the current input and the previous hidden state. -Set `use_peepholes` False to disable peephole connection -(http://www.bioinf.jku.at/publications/older/2604.pdf). The formula -is omitted here. +Set `use_peepholes` False to disable peephole connection. The formula +is omitted here, please refer to the paper +http://www.bioinf.jku.at/publications/older/2604.pdf for details. -Note that these \f$W_{xi}x_{t}, W_{xf}x_{t}, W_{xc}x_{t}, W_{xo}x_{t}\f$ -operations on the input \f$x_{t}\f$ are NOT included in this operator. +Note that these $W_{xi}x_{t}, W_{xf}x_{t}, W_{xc}x_{t}, W_{xo}x_{t}$ +operations on the input $x_{t}$ are NOT included in this operator. Users can choose to use fully-connect operator before LSTM operator. )DOC"); From 1fe5acb25a2cedd765da28642510b2ce497dc659 Mon Sep 17 00:00:00 2001 From: Yu Yang Date: Fri, 1 Dec 2017 14:47:15 +0800 Subject: [PATCH 46/67] Expose sigmoid_cross_entropy_with_logits (#6147) Also, change the `labels` to `label` for api consistency --- .../sigmoid_cross_entropy_with_logits_op.cc | 24 +++++++-------- .../sigmoid_cross_entropy_with_logits_op.h | 6 ++-- python/paddle/v2/fluid/layers.py | 1 + python/paddle/v2/fluid/tests/test_layers.py | 10 +++++++ ...st_sigmoid_cross_entropy_with_logits_op.py | 29 +++++++++++-------- 5 files changed, 41 insertions(+), 29 deletions(-) diff --git a/paddle/operators/sigmoid_cross_entropy_with_logits_op.cc b/paddle/operators/sigmoid_cross_entropy_with_logits_op.cc index d9e4054652..782f4c7936 100644 --- a/paddle/operators/sigmoid_cross_entropy_with_logits_op.cc +++ b/paddle/operators/sigmoid_cross_entropy_with_logits_op.cc @@ -25,20 +25,19 @@ class SigmoidCrossEntropyWithLogitsOp : public framework::OperatorWithKernel { void InferShape(framework::InferShapeContext* ctx) const override { PADDLE_ENFORCE(ctx->HasInput("X"), "Input(X) should be not null."); - PADDLE_ENFORCE(ctx->HasInput("Labels"), - "Input(Labels) should be not null."); + PADDLE_ENFORCE(ctx->HasInput("Label"), "Input(Label) should be not null."); PADDLE_ENFORCE(ctx->HasOutput("Out"), "Output(Out) should be not null."); auto x_dims = ctx->GetInputDim("X"); - auto labels_dims = ctx->GetInputDim("Labels"); + auto labels_dims = ctx->GetInputDim("Label"); PADDLE_ENFORCE_EQ(x_dims.size(), 2, "Input(X)'s rank should be 2."); PADDLE_ENFORCE_EQ(labels_dims.size(), 2, - "Input(Labels)'s rank should be 2."); + "Input(Label)'s rank should be 2."); PADDLE_ENFORCE_EQ(x_dims[0], labels_dims[0], - "The 1st dimension of Input(X) and Input(Labels) should " + "The 1st dimension of Input(X) and Input(Label) should " "be equal."); PADDLE_ENFORCE_EQ(x_dims[1], labels_dims[1], - "The 2nd dimension of Input(X) and Input(Labels) should " + "The 2nd dimension of Input(X) and Input(Label) should " "be equal."); ctx->SetOutputDim("Out", x_dims); @@ -53,26 +52,25 @@ class SigmoidCrossEntropyWithLogitsGradOp void InferShape(framework::InferShapeContext* ctx) const override { PADDLE_ENFORCE(ctx->HasInput("X"), "Input(X) should be not null."); - PADDLE_ENFORCE(ctx->HasInput("Labels"), - "Input(Labels) should be not null."); + PADDLE_ENFORCE(ctx->HasInput("Label"), "Input(Label) should be not null."); PADDLE_ENFORCE(ctx->HasInput(framework::GradVarName("Out")), "Input(Out@GRAD) shoudl be not null."); PADDLE_ENFORCE(ctx->HasOutput(framework::GradVarName("X")), "Output(X@GRAD) should be not null."); auto x_dims = ctx->GetInputDim("X"); - auto labels_dims = ctx->GetInputDim("Labels"); + auto labels_dims = ctx->GetInputDim("Label"); auto dout_dims = ctx->GetInputDim(framework::GradVarName("Out")); PADDLE_ENFORCE_EQ(x_dims.size(), 2, "Input(X)'s rank should be 2."); PADDLE_ENFORCE_EQ(labels_dims.size(), 2, - "Input(Labels)'s rank should be 2."); + "Input(Label)'s rank should be 2."); PADDLE_ENFORCE_EQ(dout_dims.size(), 2, "Input(Out@Grad)'s rank should be 2."); PADDLE_ENFORCE_EQ(x_dims[0], labels_dims[0], - "The 1st dimension of Input(X) and Input(Labels) should " + "The 1st dimension of Input(X) and Input(Label) should " "be equal."); PADDLE_ENFORCE_EQ(x_dims[1], labels_dims[1], - "The 2nd dimension of Input(X) and Input(Labels) should " + "The 2nd dimension of Input(X) and Input(Label) should " "be equal."); PADDLE_ENFORCE_EQ(x_dims[0], dout_dims[0], "The 1st dimension of Input(X) and Input(Out@Grad) " @@ -97,7 +95,7 @@ class SigmoidCrossEntropyWithLogitsOpMaker "This input is a tensor of logits computed by the previous " " operator. Logits are unscaled log probabilities given as " "log(p/(1-p))."); - AddInput("Labels", + AddInput("Label", "(Tensor, default Tensor), a 2-D tensor of the same type " "and shape as X. This input is a tensor of probabalistic labels " "for each logit"); diff --git a/paddle/operators/sigmoid_cross_entropy_with_logits_op.h b/paddle/operators/sigmoid_cross_entropy_with_logits_op.h index 41c619f181..2a9d9bbc77 100644 --- a/paddle/operators/sigmoid_cross_entropy_with_logits_op.h +++ b/paddle/operators/sigmoid_cross_entropy_with_logits_op.h @@ -25,8 +25,7 @@ class SigmoidCrossEntropyWithLogitsKernel : public framework::OpKernel { public: void Compute(const framework::ExecutionContext &context) const override { const framework::Tensor *X = context.Input("X"); - const framework::Tensor *Labels = - context.Input("Labels"); + const framework::Tensor *Labels = context.Input("Label"); framework::Tensor *Out = context.Output("Out"); Out->mutable_data(context.GetPlace()); @@ -52,8 +51,7 @@ class SigmoidCrossEntropyWithLogitsGradKernel : public framework::OpKernel { public: void Compute(const framework::ExecutionContext &context) const override { const framework::Tensor *X = context.Input("X"); - const framework::Tensor *Labels = - context.Input("Labels"); + const framework::Tensor *Labels = context.Input("Label"); const framework::Tensor *dOut = context.Input(framework::GradVarName("Out")); framework::Tensor *dX = diff --git a/python/paddle/v2/fluid/layers.py b/python/paddle/v2/fluid/layers.py index 5a977978bf..e41bfae285 100644 --- a/python/paddle/v2/fluid/layers.py +++ b/python/paddle/v2/fluid/layers.py @@ -403,6 +403,7 @@ _create_op_func_('sigmoid') _create_op_func_('scale') _create_op_func_('reshape') _create_op_func_('transpose') +_create_op_func_('sigmoid_cross_entropy_with_logits') def cast(x, dtype, main_program=None): diff --git a/python/paddle/v2/fluid/tests/test_layers.py b/python/paddle/v2/fluid/tests/test_layers.py index 33b0e54f42..a9d9d369c7 100644 --- a/python/paddle/v2/fluid/tests/test_layers.py +++ b/python/paddle/v2/fluid/tests/test_layers.py @@ -137,6 +137,16 @@ class TestBook(unittest.TestCase): print(str(program)) + def test_sigmoid_cross_entropy(self): + program = Program() + with program_guard(program): + dat = layers.data(name='data', shape=[10], dtype='float32') + lbl = layers.data(name='label', shape=[10], dtype='float32') + self.assertIsNotNone( + layers.sigmoid_cross_entropy_with_logits( + x=dat, label=lbl)) + print(str(program)) + if __name__ == '__main__': unittest.main() diff --git a/python/paddle/v2/fluid/tests/test_sigmoid_cross_entropy_with_logits_op.py b/python/paddle/v2/fluid/tests/test_sigmoid_cross_entropy_with_logits_op.py index e53856b38a..c42f578f72 100644 --- a/python/paddle/v2/fluid/tests/test_sigmoid_cross_entropy_with_logits_op.py +++ b/python/paddle/v2/fluid/tests/test_sigmoid_cross_entropy_with_logits_op.py @@ -2,11 +2,12 @@ import numpy as np from op_test import OpTest from scipy.special import logit from scipy.special import expit +import unittest class TestSigmoidCrossEntropyWithLogitsOp1(OpTest): - '''Test sigmoid_cross_entropy_with_logit_op with binary labels - ''' + """Test sigmoid_cross_entropy_with_logit_op with binary label + """ def setUp(self): self.op_type = "sigmoid_cross_entropy_with_logits" @@ -16,16 +17,16 @@ class TestSigmoidCrossEntropyWithLogitsOp1(OpTest): 'X': logit( np.random.uniform(0, 1, (batch_size, num_classes)) .astype("float32")), - 'Labels': np.random.randint(0, 2, (batch_size, num_classes)) + 'Label': np.random.randint(0, 2, (batch_size, num_classes)) .astype("float32") } # Fw Pass is implemented as elementwise sigmoid followed by # elementwise logistic loss - # Labels * -log(sigmoid(X)) + (1 - labels) * -log(1 - sigmoid(X)) + # Label * -log(sigmoid(X)) + (1 - label) * -log(1 - sigmoid(X)) sigmoid_X = expit(self.inputs['X']) - term1 = self.inputs['Labels'] * np.log(sigmoid_X) - term2 = (1 - self.inputs['Labels']) * np.log(1 - sigmoid_X) + term1 = self.inputs['Label'] * np.log(sigmoid_X) + term2 = (1 - self.inputs['Label']) * np.log(1 - sigmoid_X) self.outputs = {'Out': -term1 - term2} def test_check_output(self): @@ -36,8 +37,8 @@ class TestSigmoidCrossEntropyWithLogitsOp1(OpTest): class TestSigmoidCrossEntropyWithLogitsOp2(OpTest): - '''Test sigmoid_cross_entropy_with_logit_op with probabalistic labels - ''' + """Test sigmoid_cross_entropy_with_logit_op with probabalistic label + """ def setUp(self): self.op_type = "sigmoid_cross_entropy_with_logits" @@ -47,16 +48,16 @@ class TestSigmoidCrossEntropyWithLogitsOp2(OpTest): 'X': logit( np.random.uniform(0, 1, (batch_size, num_classes)) .astype("float32")), - 'Labels': np.random.uniform(0, 1, (batch_size, num_classes)) + 'Label': np.random.uniform(0, 1, (batch_size, num_classes)) .astype("float32") } # Fw Pass is implemented as elementwise sigmoid followed by # elementwise logistic loss - # Labels * -log(sigmoid(X)) + (1 - labels) * -log(1 - sigmoid(X)) + # Label * -log(sigmoid(X)) + (1 - label) * -log(1 - sigmoid(X)) sigmoid_X = expit(self.inputs['X']) - term1 = self.inputs['Labels'] * np.log(sigmoid_X) - term2 = (1 - self.inputs['Labels']) * np.log(1 - sigmoid_X) + term1 = self.inputs['Label'] * np.log(sigmoid_X) + term2 = (1 - self.inputs['Label']) * np.log(1 - sigmoid_X) self.outputs = {'Out': -term1 - term2} def test_check_output(self): @@ -64,3 +65,7 @@ class TestSigmoidCrossEntropyWithLogitsOp2(OpTest): def test_check_grad(self): self.check_grad(['X'], 'Out') + + +if __name__ == '__main__': + unittest.main() From 02e0b5f9eabcc183f4c05a139270a49fbe725852 Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Fri, 1 Dec 2017 15:00:31 +0800 Subject: [PATCH 47/67] follow comments, unify picture fonts, color and sizes --- doc/design/mkldnn/README.MD | 67 ++++++++++++++------------ doc/design/mkldnn/image/engine.png | Bin 36180 -> 17102 bytes doc/design/mkldnn/image/gradients.png | Bin 57433 -> 31247 bytes doc/design/mkldnn/image/layers.png | Bin 57028 -> 14414 bytes doc/design/mkldnn/image/matrix.png | Bin 19755 -> 22085 bytes doc/design/mkldnn/image/overview.png | Bin 9884 -> 16329 bytes 6 files changed, 37 insertions(+), 30 deletions(-) diff --git a/doc/design/mkldnn/README.MD b/doc/design/mkldnn/README.MD index 7c863197e7..287ee620e1 100644 --- a/doc/design/mkldnn/README.MD +++ b/doc/design/mkldnn/README.MD @@ -5,7 +5,7 @@ 充分展现英特尔平台的优势,有效提升PaddlePaddle在英特尔架构上的性能。

@@ -28,9 +28,7 @@ Figure 1. PaddlePaddle on IA - [Parameters](#parameters) - [Gradients](#gradients) - [Unit Tests](#unit-tests) - - [Protobuf Messages](#protobuf-messages) - [Python API](#python-api) - - [Demos](#demos) - [Benchmarking](#benchmarking) - [Others](#others) - [Design Concerns](#design-concerns) @@ -41,10 +39,19 @@ Figure 1. PaddlePaddle on IA 同时,为了进一步提升PaddlePaddle在基本数学运算的计算速度,我们也将MKLML即(MKL small library\[[1](#references)\]) 作为另一个第三方库集成进PaddlePaddle,它只会包括生成好的动态库和头文件。 + +MKL,MKLML以及MKL-DNN三者关系如下表: + +| Name | Open Source | License | Descriptions | +|------------|----------------| ------------| --------------| +| MKL | No | Proprietary | Accelerate math processing routines | +| MKLML | No | Proprietary | Small package of MKL, especially for Machine Learning | +| MKL-DNN | Yes | Apache 2.0 | Accelerate primitives processing routines especially for Deep Neural Networks | + MKLML可以与MKL-DNN共同使用,以此达到最好的性能。
-
+
Figure 2. PaddlePaddle with MKL Engines
@@ -84,15 +91,19 @@ PaddlePaddle/Paddle - `WITH_MKLML` 控制是否使用MKLML库。 当打开`WITH_MKL`时,会自动使用MKLML库作为PaddlePaddle的CBLAS和LAPACK库,同时会开启Intel OpenMP用于提高MKLML的性能。 +编译时会把对应的头文件和库放在`build/third_party/install/mklml/*`目录下对应的地方。 +MKLML的库目前都是动态库,主要包括`libiomp5.so`和`libmklml_intel.so`。 - `WITH_MKLDNN` 控制是否使用MKL-DNN。 当开启`WITH_MKL`时,会自动根据硬件配置[[2](#references)]选择是否编译MKL-DNN。 +编译时会把对应的头文件和库放在`build/third_party/install/mkldnn/*`目录下对应的地方。 +MKL-DNN的库目前只有动态库`libmkldnn.so`。 ### Matrix -目前在PaddlePaddle中数据都是以`nchw`的格式存储,但是在MKL-DNN中的排列方式不止这一种。 +目前在PaddlePaddle中数据都是以`NCHW`的格式存储,但是在MKL-DNN中的排列方式不止这一种。 所以我们定义了一个`MKLDNNMatrix`用于管理MKL-DNN数据的不同格式以及相互之间的转换。
-
+
Figure 3. MKLDNNMatrix
@@ -102,29 +113,30 @@ Figure 3. MKLDNNMatrix 子类只需要使用定义好的接口,实现具体的函数功能即可。
-
+
Figure 4. MKLDNNLayer
-每个`MKLDNNlayer`都会有`inVal_`,`inGrad_`,`outVal_`和`outGrad_`的`MKLDNNMatrix`, -分别代表input value, input gradient,output value和output gradient。 -它们会存放MKL-DNN用到的internal memory,同时还会定义以*ext*开头的`MKLDNNMatrix`(表示external的memory)。 -他们主要是当数据格式与PaddlePaddle默认的`nchw`格式不匹配时,用于转换内存的工作。 +每个MKLDNNLayer都包含用于内部存储和外部存储的一系列MKLDNNMatrix: -必要的转换函数也会在`MKLDNNLayer`中提前定义好(具体包括reset input、output的value和grad), -这些函数会根据输入参数重新设置internal和external的memory(当然这两者也可以相等,即表示不需要转换), -每个`MKLDNNlayer`的子类只需要使用internal的memory就可以了,所有external的转换工作都会在reset函数中都准备好。 +- 内部存储(internel memory):`inVal_`,`inGrad_`,`outVal_`和`outGrad_`,分别代表输入数据,输入梯度,输出数据和输出梯度。 +- 外部存储(external memory):都是以ext开头,比如`extInVal_`和`extInGrad_`,它们主要是用于, +当数据格式与PaddlePaddle默认的`NCHW`格式不匹配时,转换内存的工作。 +需要注意的是,PaddlePaddle的activation会直接使用`output_.value`和`output_.grad`, +所以`extOutVal_`和`extOutGrad_`必须分别与`output_.value`和`output_.grad`共享内存, +如果不需要外部存储用于转换,那么对应的内部存储也会与它们共享内存。 +- 转换函数(resetXXX): 包括`resetInValue`,`resetInGrad`,`resetOutValue`和`resetOutGrad`, +表示对输入数据,输入梯度,输出数据和输出梯度的转换。 +这些函数会根据输入参数重新设置内部和外部存储,当然这两者也可以相等,即表示不需要转换。 -一般来说,每个`MKLDNNLayer`中的`extOutVal_`和`extOutGrad_`必须分别与`output_.value`和`output_.grad`共享内存, -因为PaddlePaddle的activation会直接使用`output_.value`和`output_.grad`, -如果不需要external的buffer用于转换,那么internal的buffer也会与它们共享内存。 +注意:每个`MKLDNNlayer`的子类只需要使用内部存储就可以了,所有外部的转换工作都会在reset系列函数中都准备好。 ### Activations -在重构前的PaddlePaddle中,激活函数是独立于`Layer`的概念,并且输入输出都是公用一块内存, +在重构前的PaddlePaddle中,激活函数是独立于`Layer`的概念,并且输入输出都是共用一块内存, 所以添加了对应的`MKLDNNActivation`来实现,方式类似于`MKLDNNLayer`。 ### Parameters -对于有参数的层,我们会保证`MKLDNNLayer`使用的参数与PaddlePaddle申请的buffer公用一块内存。 +对于有参数的层,我们会保证`MKLDNNLayer`使用的参数与PaddlePaddle申请的buffer共用一块内存。 如果存在数据排列格式不一样的情况时,我们会在网络训练之前把格式转换为MKL-DNN希望的格式, 在训练结束的时候再保存为PaddlePaddle的格式,但是整个训练过程中不需要任何转换。 这样既使得最终保存的参数格式与PaddlePaddle一致,又可以避免不必要的转换。 @@ -138,18 +150,15 @@ Figure 4. MKLDNNLayer 所以整体上,在实现每个子类的时候就不需要关心分支的事情了。
-
+
Figure 5. Merge Gradients
### Unit Tests 我们会添加`test_MKLDNN.cpp`和`MKLDNNTester.*`用于MKL-DNN的测试。 -测试分为每个Layer(或Activation)的单元测试和简单网络的整体测试。 +测试分为每个Layer(或Activation)的单元测试和简单网络的整体测试。 每个测试会对比PaddlePaddle中CPU算出的结果与MKL-DNN的结果,小于某个比较小的阈值认为通过。 -### Protobuf Messages -根据具体layer的需求可能会在`proto/ModelConfig.proto`里面添加必要的选项。 - ### Python API 目前只考虑**v1 API**。 @@ -167,11 +176,9 @@ if use_mkldnn 同时,会在`paddle/utils.Flags`中添加一个`use_mkldnn`的flag,用于选择是否使用MKL-DNN的相关功能。 -### Demos -可能会在`v1_api_demo`目录下添加一个`mkldnn`的文件夹,里面放入一些用于MKL-DNN测试的demo脚本。 - ### Benchmarking -会添加`benchmark/paddle/image/run_mkldnn.sh`,用于测试和对比,在使用MKL-DNN前后的性能。 +会添加相应的脚本在[这里](https://github.com/PaddlePaddle/Paddle/tree/develop/benchmark/paddle/image),用于测试和对比在使用MKL-DNN前后的CNN网络性能。 +测试的性能对比结果会在[IntelOptimizedPaddle.md](https://github.com/PaddlePaddle/Paddle/blob/develop/benchmark/IntelOptimizedPaddle.md) ### Others 1. 如果在使用MKL-DNN的情况下,会把CPU的Buffer对齐为4096,具体可以参考MKL-DNN中的[memory](https://github.com/01org/mkl-dnn/blob/master/include/mkldnn.hpp#L673)。 @@ -189,8 +196,8 @@ if use_mkldnn 3. 创建`MKLDNNBase`,定义一些除了layer和memory相关的类和函数。 包括MKL-DNN会用到`MKLDNNStream`和`CPUEngine`,和未来可能还会用到`FPGAEngine`等。 4. 如果MKL-DNN layer的后面接有cpu device,那么就会使`output_.value`与`extOutVal_`共享内存, -同时数据格式就是`nchw`,这样下一个cpu device就能拿到正确的数据。 -在有cpu device的时候,external的memory的格式始终是`nchw`或者`nc`。 +同时数据格式就是`NCHW`,这样下一个cpu device就能拿到正确的数据。 +在有普通的CPU layer时, `extOutVal_`和`extOutGrad_`的格式始终是`NCHW`或者`NC`。 ## References 1. [MKL small library](https://github.com/01org/mkl-dnn#linking-your-application)是[Intel MKL](https://software.intel.com/en-us/mkl)的一个子集。 diff --git a/doc/design/mkldnn/image/engine.png b/doc/design/mkldnn/image/engine.png index 65bbb41fbb389ff5f7906b0284ada77ac2dc4ec9..a60b7ad5553bd6d7d5e255fabc14467ef8a57c88 100644 GIT binary patch literal 17102 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 @@ -42,16 +42,16 @@ Figure 1. PaddlePaddle on IA MKL,MKLML以及MKL-DNN三者关系如下表: -| Name | Open Source | License | Descriptions | -|------------|----------------| ------------| --------------| -| MKL | No | Proprietary | Accelerate math processing routines | -| MKLML | No | Proprietary | Small package of MKL, especially for Machine Learning | -| MKL-DNN | Yes | Apache 2.0 | Accelerate primitives processing routines especially for Deep Neural Networks | +| Name | Open Source | License | Descriptions | +| :---------- | :--------------- | :---------- | :------------ | +| MKL | No | Proprietary | Accelerate math processing routines | +| MKLML | No | Proprietary | Small package of MKL, especially for Machine Learning | +| MKL-DNN | Yes | Apache 2.0 | Accelerate primitives processing routines especially for Deep Neural Networks | MKLML可以与MKL-DNN共同使用,以此达到最好的性能。
-
+
Figure 2. PaddlePaddle with MKL Engines
@@ -103,7 +103,7 @@ MKL-DNN的库目前只有动态库`libmkldnn.so`。 所以我们定义了一个`MKLDNNMatrix`用于管理MKL-DNN数据的不同格式以及相互之间的转换。
-
+
Figure 3. MKLDNNMatrix
@@ -113,7 +113,7 @@ Figure 3. MKLDNNMatrix 子类只需要使用定义好的接口,实现具体的函数功能即可。
-
+
Figure 4. MKLDNNLayer
@@ -150,7 +150,7 @@ Figure 4. MKLDNNLayer 所以整体上,在实现每个子类的时候就不需要关心分支的事情了。
-
+
Figure 5. Merge Gradients
diff --git a/doc/design/mkldnn/image/engine.png b/doc/design/mkldnn/image/engine.png index a60b7ad5553bd6d7d5e255fabc14467ef8a57c88..1f5f65c2cc765a514a3ba9e7b7f468e1dc4b0c3b 100644 GIT binary patch literal 13586 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 diff --git a/doc/design/mkldnn/image/layers.png b/doc/design/mkldnn/image/layers.png index 4f87553b41b2c38caca2e54c039e9ccaa9d605e5..306f79b7a844610915eb8944128f57d2b7a3065a 100644 GIT binary patch literal 11646 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 GGywooK%VIU literal 22085 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ⅈGu*u(g~eVz=rG>{s&^mT9a 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 bZx6dwj(Ge|H_ZZgUC>Pv%j=a!?lJ!c<|gv( diff --git a/doc/design/mkldnn/image/overview.png b/doc/design/mkldnn/image/overview.png index 1d81b5a4b5db687c06b92f88648f9895711fdef4..8fb7bbb9dd654bf363d701d0c8cd4a557043d188 100644 GIT binary patch literal 10766 zcmb_?XIN8FmoCjn3B83P1VI4>2?Pxw9i&JH6$AuA6i|ATfb>wMOBd-#QK`~<4N{~C z5~Q~v5L)P+8@_L5zWFor%$<9mC+A5{a(2$%Ywfe%^}cI`>uRe~Q?gSM5fM?Vt10Uf z5fO(F4jOU*;hQK#nJ(dt*i~Ouk*H*VbCqyHX04#5Ktxm?Lv;qfMmVQ%QhV-7L`2hh zbr2`srQskV63kUsR(OIm+eoL4d}Zh}yS3iGPIzrXO!S@33vLU@2fi@0cbcq~XVmCQ zxiGk+A9=?x;7+vsohON(>mQTmJ$*s(^nPNP;uFT-Um(pZHk$#a6AA ze(od&7`zE-cnE#?5ZaJF_~-P-<>sH$nTD;cM%dPAT=b^TVfwo*$-m8~rx~+bQyzo; zi9VEwNYVTyqdIXk2*JPtAe=D^0STY)0PP}qxX>LeXppN!e}8|1?eBeu6%)|-i_kFK z(BJGRQM5Q8E16F;PHR;I#7CAaVlJyTG1d(y{IKV*?C-WdUXB_qme>;lid}@Y&CHmU zH#DR%;Pk+eqG({I0QvXt-@B%!Odo&qxJ%LT3*5pKju{ZBU-U7b8BgMbi`MHX-f z8f4JcZb5KaU~n4OYF(R0|P6m#)p$KY%$; z*0%&nE)JthIqp*<G(!Zzm|ReOifP-0Omc8^{{j@S-MhK4WyT}L0Bg2{*Rf`PITG;8L& zd)PLP5+&^I7)eecAR`G(e37~}DqOTxfE>sSd!VB4^xb7wmG{6;?)1-Bv^b8q;+%G~ zj=z{!nKQz>$z}RcvSM#!qwc7klmo)HQqxI?&#R4jx`rMopX?jxfw5MhS z;Q3G>rsTWVh2%QS76eW|It|}h&lmwc|KHKX}#y7(DGHvhTj*)65P?< z4&fPc9r-D;n8?8`O~%uqHqNs=zgwm!lDO098t<|*?)I$%o|ADz+jKgOP5Np^H|e5K zLb}Le&|p?EbcAAnCo8mVsqz%0xNHIOv7+58yqlviFYfhtjw92TJgi@})$Ckodm_20 zI>=h1+M`krTU{4(nZg&TMDL3_WN{%yI`P3NFZpnmBkNF_!k-lJ%DfYI?#+m}FDe9C z11MC?yQ2j|lF2#Lh3LpVKdtvkprqA2S#t(b?(Y3zLL1Y~aG&>E_X;nUl|eL#-V^s_ zeW@8MvA%_AR#&^WFn3cR-xR~R9@I{r-Yyoz50Pm4gcvWBap9E`g0yQj#hHOCM>} zq;0d#HPMM0V-cI3=Q!=>C*^yQ$JSDcj5?aOIAVSfZ^KJus?$kastA6$G}zbc7=NA? zG<5S5txovtBiVb1s|e8lqnpe>})rG06E-3dhdqL6KW~%Uw0XLq?tnL^S+@%Y`e@jZNo-8ED1(YDE!A*eH>6v^;rxOw**hy0SNK+Mq2`b6c+}2NQFeS5&O)V+0K&cd z=(4^mi2VNDz|O&e1&70RP56o=bV^M+^qOiwGluROd@=`2nr^5oT}Tv9_C-Pmw;~RH z{n9V5sL=nmNd*meul_kW$a)2;{l3?;84o~_MFB%JP0QY#*Y&bI?DQb2Yy_;hPdCBy ziN3>$uNrF>B&teH)sfI4K~ipg=wEuxIc$+CPa*`-uh;cILl{lAv)r1@)IYaxG)>fx zd}TQbg&r?7L>zr;IbAKA_Zn+Dy%Q>Z0)vT9izu3izkUckHUj07JJ?+OoP7BqrGsoS zzQAVmHU))V23pJFW2@hbBMeMr9IDDe)xp%32kr2#+6B*ZqfZRjwr(j7)o;v4GvZ%) zH)h2qQ)w?uD&03%y7)rpT1nWk^sg^aEF+Jip?gm}ad#XKq$%OR3w!OshXc9Nf-73z zj?bu;ZQ$?}21p@jtX|TCS7DRs^FywrFrK zwl`d^#@!ul(=<=-Bwp-kI7cAK$Wz@ON@_5QuQrmA?9dNwuQ4P2*2s9! z4}V&Q-fYYZTW_)sW?T-jd$7cKs8naTp8&6WltqH*$I<6DzdpUS5PD4-UFuSnrzI6m z8Q3nFilwnRJ&P(%fU^5&ZLym#7{chIt;`}8A}!n%Em`{7L?x02gx?Nk*z<{7xs*dR zDcpBO5L2~nyW1a@kL{(*_aI@=sG{pro9>u9-)*owSocJWWzwDWT-`{4ZP%Sy6&+b_RmHNU^vXcV0$g;=^|m_P51dUj4NHjN^5{lQ z!7JJG+w&;xq1-4`!J4z|?i7pN(M?RH5ds=0&X7i31<{Fp8_`G*4;vkj4J`sgcKAc+ z0g~w9v>=U;05t2)sMf8fi7z9JFC;thi^snV9zPdQN8#|{ z{1XpC<+6B4I7w@hv=mXZ^_Lfw-1bb__oR~KkpkZ(>j}#t+h5-0f|kEiR3H9Nr(yfdn9gfm%JRpl5?&m{dU43 zwvlFs*HQ{#nUtA{Q~g5^#4fsCBrw}|G{4~}xy5rw`P+>!nGL$@=j!BIK*8SA6>$m2 z+i9tHPZMxa@Z_eZCQEDUg7K*-p)m|5qUziXOo(&S3HzEq=an2|XUwJ%(bMv!%%s6Q z;b3xd(vYIxN6KOkPE};MH6O$i&yePz#*vZmGvq~_>U#nguzdCEv(a?Rp!&D(_P)N& zDe39LRu!k!H1B{0Wb@=+@6Hcan0tGafRx>p4A;nlGs+*qUD|lp#JMbQa}#1xcCCcS z2rg9GG`n-|)#fARZLtOJp8l5#pzzqqK3X|b2T@h#&<~NK%VBf69|&oA_Wx_X{;vd| zloc>Dg<0D0xuOYuS?gQ!K?=d)Jy(Q-EkStG1mw1Z=#Ku}dD^T_xI!gjy>#8VbYbW_ zWF?4?IV2n$%`ImpBqWqjcST_+2^7XCekV4k*x|7`7fKoK;Bb$5r8AuIb$%8WixmsK zB1HikT>wNxdC$02xDsqnx{Cj`hX+h+tFEf*shyo&o}kS07PnRXk0-l3*gh;5;ie(s zipNM`L`1~(bhj})m@Yg&LrJHd2;{@i?oVghh%6tUnbD?bn|nwRM)?1E9#o8kKwz#w zw4w&PhmGQ3d#eM;PuV{R3}*P&XHq6`wX|4>Kw}I@;FIz~!|fT8Jz-!Nk)=N-i;h8k zmV-bfWW~T%;O}08iinJArT={V<8`rpu&DlmNZ}jRyx2fyp98 zAS>TKx2yk?CR%J`g~231321ZHXD;)n&Dwjnfb1snzI$*(Om7V!w177p&L zEkjE+PVq5$xY|FxB2sO{<74MmvxgN`3&@ek(&f7v^5_7@8f;UVHdND{#rQ!WNckW1 zFb))(W51cy98A2>^G+~`V6!$ld*{*vB)B~m;S52i3_}GbA2MB0LTKH8L;C);iT;Ny zG4dAoSr5m1zw;WuZ%3w*lizA{qy{{{j>%7CIZ@X>ovJR7Z&v((!SLBvFlbO$(X8mZL zsa@W#!@(`4%N02pLx&=l$teDa(^+Q&<4Zq|hxWME@{1ok-sPUJOONVQo8ziqUpjhQ ze|Mqj-9cmSF<v9+sS2G)FcgPlBmlzgWT)O`cip zcy_m63M3^Zy-!R`v~+jBe{1Z|135YK{(%7&C=~i?b8H*|Q6lYlg28PiatsRb@!obM z?m$}u-54P!pcAR_dwPY?QTnwGNpsUl?VKSI^0C>o!Hcxn8w)rJMH24relZig%SMstz{RX7c_TspQq zceo)GMm-^02Lwc& zR^x7vhV1n3m>PHcGqTUua#}k#?+zjn??l+t8l$Bgj9v(sNxXY1J%Mk_Y?Mv+8=q%g z)9@5D959%bO-^$3Tm1T~f^m8oMwqP^02NJaaD>{XoaY6r3_j51Vlx2NFC0*2(&0Aw zX!e%rS);WYKb=EHby~{wmThPOAIxfd1@`KGO9anNtzY)cdKOKIneshxm+_fk5doKK z+^zrVn9o6lT4e*uQjq%%#XJ1wjTv&i#l%RBFSzXMt5SKWxDpbuyz-h(`wjcwf^P|U z>e3fNHKy2Vj$~Rztz!8-$Tv=2?#>%5BBkcSBkp%G)kPC&6|iiAE9=W4DH+Ghht<7R zUx#q!(lf{W!2w;ZsxR%B@4dNZc~b<%2Y*A^ zG?GYSq#1kPs&dWo&pk^xBvg^pJgmszQ;YewCL}tjkxsBNG#|l4PKvdXuT3+9@su@Y zXz3c2q}%x5Ipoy(B(`UxueL^KyG4k~3mw)DO~p_sAwAz`|J+f!gDDu)>J}iH z*Dp1h8HCJ@lgBC=70Zi*xL==`|HsG>G;#GwH+a(Ij@MXn1*JQ zd?*95%Ty)O4%I9S7c{3OjTTT4+&dB)+yB=u@!*U`1(>d+iaH1mjTz%*6EjSBf^ zUJunVC$u;;66bQ^Mpx-g!Jn89=Ruq;tA4je2g=-6U5u-92_`?;ZulpndE8wzUIS1Y=@UeL#5;}$?u+~@2FZwZHIOE@nm9~b`%*6tt6J*YRa zDp@xufrY$KYiMZb>WW~Eh>D6@Xb-09I^O*|azaoc-|P6{?!R)liqUgEa%a>MX&7A>5AKD*lDHD6K0N8w$ ziA3d@TpR;lUICgKC_LlsI{U2S`r15?|`-f*=nieGg(tNgfFl zYPb=$`EHwy7Vhc_y%hy5qaFrdIZqbk%fJGw#RJAR(>|`wQfB^+Mnc5TqttffWHs~X{lKCY<9=uJtBxys(PQV}#)VQo^ z+`_IL{jPj?`rz^%3qU(BuPhFbBbY$2_V7)GR+hedswUG3&C-A&ihZUJS8}se<&TSA zE|0t9IgW6mbm`ky_I;&WO#n8!)dIqwBD}114?O0sNa$4-{QpDw{MYvScM!D7Yupt% z3t#B1@hd+1RVt_JP}i#}yJtA^_H(K4Ja_UeCs%Rr$Bx6LTpBdzH3B~XqFGUI$1QZ_ zPUc&l^*3%>)BgIiAGL5idv5(I`rwCuyb%n&M|kGkv_>-A3}!B2GtZ{Zzh6pI1OU%a zEoW(~4Y!-Nugh+iECeSHvdBr)q>icnob+98wkM%&YN0ja1^ zl5X01PD$Cw{;CJ+kQ# z^edbHY`pY*y7FSIrg0vu^KxTVhs%!NopS5bY%MbO`XKS5cInYFhuI71{k@uzgJ-&9 zhUsRHxzNyAq-gP9nN=f^MBy>PvBb_gSMg~11yI~LDjBxF3^UDa9+4ex(D(gZk7r7J zuKxGsaYoCT^)IP;ShK+kx2eXaW7(wV?t0JF%e_6{Ep5DxmSq9BQiC|X=7LlqqcAg% z8r9B5_Kt0?wy0) z9Hof7!^78Rgz~~kdh_FnB5&lh*71Tm$@gPkW&4bmveK~~#4OT-%zK6KFZ+Wl*4T+~ z_+4-oryde7LDzKXyJA4dtS})Jy0@V+1Rvg4R-yvE2CDFrpIa!qY=gBkjfLcXqq3if zB#w5H-&xwN>u&y#x`|Q^pL&^Br zMX4iWzKcc>l*mT`4~4ctYrW|4kW{X#{Ka^`jG{d9&Qg}HUV7Z`IlC7Uf;q7R5Wq8> z*V&HMl4K=`^DQ9^WpIYePHckBS~-8Ryr z=0OGL>(#~djS+n(BnlSh(fIkp^DdXZr`ysej~40l+{9xC4b+{z1EulK`e;SSa`{it zIA*RHtt_r`bCkt7OMs&F6M&gZM){Aa3~A zS-x$BE)HDtcGmzY(&>OO=9}Tz#O`5QuaByW6kUvy9IgFvTOqtU5z`re8iW34aJXCgD`5W2#j(~pF@zRGlI5?!yTsWH4NcBQ4I zb)B4el~-3MG2rYP4q5#g`t1opk4bt*L++`NurTx8yLY?R*4{k+PlVOVs&}fmy!(Gy z1A@2D{ibZppDUa9k#gUfhnsk(x97Qzt|2rT2}J_*3&)nKolOq2){r*#J&iRlV%{%B zQ1!-|(26QA^El;_l6CSyILCqmj-V2@wL=uZB#lkt14d)C#a`!4{>~VU4M;}bRgl{g zt9>@&wNoK6S!gLm{uC7*W-01Q9zMBNWpSbWgJJ9IE|1yzOt)r;n&C z=Eifz1r9~e$-)CUA+KV>rNo3w1IoXo8Ax6iAyWFP8}&gEOer^fH^x8`9ZRz{i+QVG zaEKpiNOBq;`3vRhX_7ll>|&DKlV0oQ`@X9plyG#>eX4dOl3UcM{fW z0ZD!Jl>%gp=4Ikm)fL~kg*KX8p|~5ftl2uZUE*iCW@p{=cgjh5JXujJ!F8l=G(jqu|X0~0gh9fhwiUTn6CHsUPxUJk48=8_T0~lI+3pP4 z8Db0y*Y4pyUu${>-il4>A?y#HN->g-o9Jl77bxIdF^r5fSo9{#3)Yitj~9j+AhRzx zywcZ=-25W>-=6Nnxo;lF+X}>lZ8O93$2U!Bn|Y;GIhIMg-Dh}jI$ViOL0M^%F{s0i zx?L0O#9e{qDf^W#hT;$W!9?XY%HRVBiU2n;3Cflwc--6hRCoaWp&Vgtwer{h%P z=JVpgPE2CGt627#E5z-qpJl}=O1M$Got>SHrq;uJhT8sFVz1zuG}tI~CT~}z@8yG!;CFZZmKhIkAbmIH#ygIL zQ`-niJa`QNISt@Q;dowd!^YbV-J|=H-^q9%OeZK%N<5 zzYdIi8UDWCF!JyX%3URDVsrfWjQ`-^;C*^}x}~e@cO!Fik+HEceam$F!;1yS)8do43B;Qqg8!Z3;sh#eDOl-KNns4HB%ezAAoL?clyt) zG;H~5)ODrw1YV}ky*6S(6Dk@(ZD8T-Ylz#_jn{k|jx$gCkLIsLLQZA$Y_ zLvC%0ATH8B=B8F9P3=f5w<@4&T~kyDnhBBs+8ew+6?~ zd#a{o#Vv;%VPq*iF3>MugWQIHC*@+k$;h<9c@4?=ijX%`mcET1kyIO{KB3=Y=&rbN)0-(E3sncmpI=_TP(>ZBF}W%V zAKmi->Iv9oKVjVrw`diA#M@*hYXER)tpC$ahE6<72zH4x>YiI3g2uf1g4@7iZ$EnC zk(8k0X|#3iXY?}|o3@bSjPIw?@B;jt$fcE6IG5`qzs>T}1QU~vy4q;I^fYCkM|Px% zqGK%v9PiJ(I$h-paKB3;=~^O6_gIecyV75-|9zT)ys^?a`ZLWQ5JTvfXsz8@eiAIR zV!xA`7DN}V$U4Q@n`_1d1Hq|&j(2?+taFYi6Y(U{3FyR?dY+5nHii!SedKdAk2QvC zi#(~n_r6cre)iMGZpS6QkoRFEZwK~-i)fM1fGGi9jw1EeyXGhfx;&6PnW|u zUlpgR3oD-I%;#(fa7>DGDP)+6yGj^(Yt@vx@S+!*e;RY1B3TJpVbjn$c(AlSPDYD3GE{!Ft{>Ig@2KM{zo@J zoYByC1ncCA7*fNlY$*#FawWl&BqNPzWY8l8EF<+^o1S$=F#EW{Cj}>A`6?tGyo8)C zYB(J5c**rmcLhyaqeJn8Kk>NQ$b*E|-)tr}E=_JU6Xi4q-MNLk6$_PlanB5b7@}=e z3uY@PD%J+O0WIud^UP%%CR6`Q=pbn|^nxYQPokkA{JxOYQ* zNAlkX!vhPUOfKE;ntJLVIomtjeS3WtigcvCwWgM)U&!>$f&if^X&a@xwWBZc;liQ533YL z9*p|CpKVdJ@W>R6E_vZ!aLNtsRRp0{ggSS2Qy108_fEsQ`DxR`pOOA_2WO#x(2rwA zgpj%4DV~u961gYc^tQ`aU3x1$)cz!RhzKSfqp4C`Ue~tDtbaS0bD>ovylv#@2{pE( ztV??8Xc`#EeXaKG-sW#WCGQ3%DBu1EenBFTYcTjWCZY`P9T?RZB=Uzf;5`-tZV3vP z_L9E8JpcMj{tf>rDzT0(gLmw%VHSM!A#O!*$bLgA_&@`1yTLb(R047$lehKQ6j^`b}pXuWEa>wh_NcvtkQsZRL6_1JA) crc3TKz7~P4PLFXy4=0iOBW>jpMT@}y11`C-kN^Mx literal 16329 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 C Date: Fri, 1 Dec 2017 19:26:01 +0800 Subject: [PATCH 56/67] Fix grpc compile warning (#6050) * fix grpc compile warn * update * -Wnon-virtual-dtor -> -Wno-non-virtual-dtor --- cmake/generic.cmake | 4 ++-- paddle/operators/CMakeLists.txt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cmake/generic.cmake b/cmake/generic.cmake index 9cf256fb6d..66c8e3ad7e 100644 --- a/cmake/generic.cmake +++ b/cmake/generic.cmake @@ -505,12 +505,12 @@ function(grpc_library TARGET_NAME) set_source_files_properties( ${grpc_grpc_srcs} PROPERTIES - COMPILE_FLAGS "-Wno-error=non-virtual-dtor -Wno-error=delete-non-virtual-dtor") + COMPILE_FLAGS "-Wno-non-virtual-dtor -Wno-error=non-virtual-dtor -Wno-error=delete-non-virtual-dtor") cc_library("${TARGET_NAME}_grpc" SRCS "${grpc_grpc_srcs}") set_source_files_properties( ${grpc_library_SRCS} PROPERTIES - COMPILE_FLAGS "-Wno-error=non-virtual-dtor -Wno-error=delete-non-virtual-dtor") + COMPILE_FLAGS "-Wno-non-virtual-dtor -Wno-error=non-virtual-dtor -Wno-error=delete-non-virtual-dtor") cc_library("${TARGET_NAME}" SRCS "${grpc_library_SRCS}" DEPS "${TARGET_NAME}_grpc" "${TARGET_NAME}_proto" "${grpc_library_DEPS}") endfunction() diff --git a/paddle/operators/CMakeLists.txt b/paddle/operators/CMakeLists.txt index 937441b318..8187af9374 100644 --- a/paddle/operators/CMakeLists.txt +++ b/paddle/operators/CMakeLists.txt @@ -217,13 +217,13 @@ op_library(send_op SRCS send_op.cc DEPS sendrecvop_grpc grpc++_unsecure grpc_uns set_source_files_properties( send_op.cc PROPERTIES - COMPILE_FLAGS "-Wno-error=non-virtual-dtor -Wno-error=delete-non-virtual-dtor") + COMPILE_FLAGS "-Wno-non-virtual-dtor -Wno-error=non-virtual-dtor -Wno-error=delete-non-virtual-dtor") op_library(recv_op SRCS recv_op.cc DEPS sendrecvop_grpc grpc++_unsecure grpc_unsecure gpr cares zlib_target protobuf) set_source_files_properties( recv_op.cc PROPERTIES - COMPILE_FLAGS "-Wno-error=non-virtual-dtor -Wno-error=delete-non-virtual-dtor") + COMPILE_FLAGS "-Wno-non-virtual-dtor -Wno-error=non-virtual-dtor -Wno-error=delete-non-virtual-dtor") op_library(cond_op SRCS cond_op.cc DEPS framework_proto tensor operator net_op) op_library(cross_entropy_op DEPS cross_entropy) From 362b7d8a5e8d3ac09f99e449a876a315d7b0cf90 Mon Sep 17 00:00:00 2001 From: Luo Tao Date: Sat, 2 Dec 2017 09:39:26 +0800 Subject: [PATCH 57/67] Rename gserver_test2 to gserver_test_with_python --- paddle/gserver/tests/CMakeLists.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/paddle/gserver/tests/CMakeLists.txt b/paddle/gserver/tests/CMakeLists.txt index 6dbf5a01cb..b578a906c2 100644 --- a/paddle/gserver/tests/CMakeLists.txt +++ b/paddle/gserver/tests/CMakeLists.txt @@ -31,21 +31,21 @@ gserver_test(test_MaxPoolingWithMaskOutput) set(PYTHON_PATH ${PADDLE_SOURCE_DIR}/paddle/.set_python_path.sh -d ${PADDLE_SOURCE_DIR}/python/:${PADDLE_SOURCE_DIR}/paddle/gserver/tests) -function(gserver_test2 TARGET) +function(gserver_test_with_python TARGET) add_unittest_without_exec(${TARGET} ${TARGET}.cpp) add_test(NAME ${TARGET} COMMAND ${PYTHON_PATH} ${CMAKE_CURRENT_BINARY_DIR}/${TARGET} WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle/) endfunction() -gserver_test2(test_PyDataProvider2) +gserver_test_with_python(test_PyDataProvider2) if(WITH_PYTHON) - gserver_test2(test_PyDataProvider) + gserver_test_with_python(test_PyDataProvider) endif() if(NOT MOBILE_INFERENCE) - gserver_test2(test_CompareTwoNets) + gserver_test_with_python(test_CompareTwoNets) # TODO(yuyang18): There is some bug in test_RecurrentGradientMachine, I will fix it. - gserver_test2(test_RecurrentGradientMachine) + gserver_test_with_python(test_RecurrentGradientMachine) endif() ########## test_MKLDNN layers and activations ########## From 3e8c3638dce3fb274a2914fe37eb92ed10723656 Mon Sep 17 00:00:00 2001 From: Luo Tao Date: Sat, 2 Dec 2017 10:00:13 +0800 Subject: [PATCH 58/67] add WITH_DOC for print_operators_doc in docker/build.sh --- paddle/scripts/docker/build.sh | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/paddle/scripts/docker/build.sh b/paddle/scripts/docker/build.sh index fbd0b6b078..0f889e6853 100644 --- a/paddle/scripts/docker/build.sh +++ b/paddle/scripts/docker/build.sh @@ -185,7 +185,14 @@ EOF ${DOCKERFILE_GPU_ENV} ADD go/cmd/pserver/pserver /usr/bin/ ADD go/cmd/master/master /usr/bin/ - ADD paddle/pybind/print_operators_doc /usr/bin/ +EOF + + if [[ ${WITH_DOC:-OFF} == 'ON' ]]; then + cat >> /paddle/build/Dockerfile <> /paddle/build/Dockerfile < Date: Sat, 2 Dec 2017 21:46:43 -0800 Subject: [PATCH 59/67] Add hinge loss op (#5837) * Add hinge loss op * Update hinge-loss equation for proper latex --- paddle/operators/hinge_loss_op.cc | 113 ++++++++++++++++++ paddle/operators/hinge_loss_op.cu | 23 ++++ paddle/operators/hinge_loss_op.h | 69 +++++++++++ .../v2/fluid/tests/test_hinge_loss_op.py | 28 +++++ 4 files changed, 233 insertions(+) create mode 100644 paddle/operators/hinge_loss_op.cc create mode 100644 paddle/operators/hinge_loss_op.cu create mode 100644 paddle/operators/hinge_loss_op.h create mode 100644 python/paddle/v2/fluid/tests/test_hinge_loss_op.py diff --git a/paddle/operators/hinge_loss_op.cc b/paddle/operators/hinge_loss_op.cc new file mode 100644 index 0000000000..1e13897bb6 --- /dev/null +++ b/paddle/operators/hinge_loss_op.cc @@ -0,0 +1,113 @@ +/* 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/hinge_loss_op.h" + +namespace paddle { +namespace operators { + +class HingeLossOp : public framework::OperatorWithKernel { + public: + using framework::OperatorWithKernel::OperatorWithKernel; + + void InferShape(framework::InferShapeContext* ctx) const override { + PADDLE_ENFORCE(ctx->HasInput("Logits"), + "Input(Logits) must be initialized."); + PADDLE_ENFORCE(ctx->HasInput("Labels"), + "Input(Labels) must be initialized."); + + auto pred_dims = ctx->GetInputDim("Logits"); + auto label_dims = ctx->GetInputDim("Labels"); + + PADDLE_ENFORCE_EQ(pred_dims, label_dims); + PADDLE_ENFORCE_EQ(pred_dims.size(), 2, + "The rank of Input(Logits) must be 2 and the shape is " + "[batch_size, 1]."); + PADDLE_ENFORCE_EQ(pred_dims[1], 1, + "Each row of Input(Logits) contains a real value, " + "so the 2nd dimension of Input(Logits) must be 1."); + + ctx->SetOutputDim("Loss", {pred_dims[0], 1}); + ctx->ShareLoD("Logits", "Loss"); + } +}; + +template +class HingeLossOpMaker : public framework::OpProtoAndCheckerMaker { + public: + HingeLossOpMaker(framework::OpProto* proto, + framework::OpAttrChecker* op_checker) + : OpProtoAndCheckerMaker(proto, op_checker) { + AddInput("Logits", + "The input value (Logits) of Hinge loss op." + "Logits is a 2-D tensor with shape [batch_size, 1]."); + AddInput("Labels", + "The target value (Labels) of Hinge loss op." + "Labels is a 2-D tensor with shape [batch_size, 1]."); + AddOutput("Loss", + "The output tensor with shape [batch_size, 1] " + "which represents the hinge loss."); + AddComment(R"DOC( +HingeLoss Operator. + +Let x be a logit (prediction) and y be the actual label. The logit can +take any values from (-inf, inf), but the labels should be either -1 or 1. +Then, the hinge loss is computed as follows: + +$$ +L_(x, y) = max(1 - y.x, 0) +$$ + +Note that the labels passed as input will have values as either 0 or 1. + +)DOC"); + } +}; + +class HingeLossGradOp : public framework::OperatorWithKernel { + public: + using framework::OperatorWithKernel::OperatorWithKernel; + + void InferShape(framework::InferShapeContext* ctx) const override { + PADDLE_ENFORCE(ctx->HasInput("Logits"), + "Input(Logits) should not be null."); + PADDLE_ENFORCE(ctx->HasInput("Labels"), + "Input(Labels) should not be null."); + PADDLE_ENFORCE(ctx->HasInput(framework::GradVarName("Loss")), + "Input(Loss@GRAD) should not be null."); + PADDLE_ENFORCE(ctx->HasOutput(framework::GradVarName("Logits")), + "Input(Logits@GRAD) should not be null."); + + auto pred_dims = ctx->GetInputDim("Logits"); + auto lab_dims = ctx->GetInputDim("Labels"); + auto loss_grad_dims = ctx->GetInputDim(framework::GradVarName("Loss")); + + PADDLE_ENFORCE_EQ(loss_grad_dims, pred_dims); + + auto pred_grad_name = framework::GradVarName("Logits"); + ctx->SetOutputDim(pred_grad_name, pred_dims); + } +}; + +} // namespace operators +} // namespace paddle + +namespace ops = paddle::operators; +REGISTER_OP(hinge_loss, ops::HingeLossOp, ops::HingeLossOpMaker, + hinge_loss_grad, ops::HingeLossGradOp); +REGISTER_OP_CPU_KERNEL(hinge_loss, + ops::HingeLossKernel); +REGISTER_OP_CPU_KERNEL( + hinge_loss_grad, + ops::HingeLossGradKernel); diff --git a/paddle/operators/hinge_loss_op.cu b/paddle/operators/hinge_loss_op.cu new file mode 100644 index 0000000000..ec20b08e30 --- /dev/null +++ b/paddle/operators/hinge_loss_op.cu @@ -0,0 +1,23 @@ +/* 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. */ + +#define EIGEN_USE_GPU +#include "paddle/operators/hinge_loss_op.h" + +namespace ops = paddle::operators; +REGISTER_OP_GPU_KERNEL(hinge_loss, + ops::HingeLossKernel); +REGISTER_OP_GPU_KERNEL( + hinge_loss_grad, + ops::HingeLossGradKernel); diff --git a/paddle/operators/hinge_loss_op.h b/paddle/operators/hinge_loss_op.h new file mode 100644 index 0000000000..c0be496f9c --- /dev/null +++ b/paddle/operators/hinge_loss_op.h @@ -0,0 +1,69 @@ +/* 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 "paddle/framework/eigen.h" +#include "paddle/framework/op_registry.h" + +namespace paddle { +namespace operators { + +template +class HingeLossKernel : public framework::OpKernel { + public: + void Compute(const framework::ExecutionContext& context) const override { + auto* pred = context.Input("Logits"); + auto* label = context.Input("Labels"); + auto* loss = context.Output("Loss"); + auto place = context.GetEigenDevice(); + + auto x = framework::EigenVector::Flatten(*pred); + auto y = framework::EigenVector::Flatten(*label); + loss->mutable_data(context.GetPlace()); + auto l = framework::EigenVector::Flatten(*loss); + l.device(place) = + (static_cast(1) - x * (static_cast(2) * y - static_cast(1))) + .cwiseMax(static_cast(0)); + } +}; + +template +class HingeLossGradKernel : public framework::OpKernel { + public: + void Compute(const framework::ExecutionContext& context) const override { + auto* pred = context.Input("Logits"); + auto* label = context.Input("Labels"); + auto* dloss = + context.Input(framework::GradVarName("Loss")); + auto* dpred = + context.Output(framework::GradVarName("Logits")); + auto place = context.GetEigenDevice(); + + auto x = framework::EigenVector::Flatten(*pred); + auto y = framework::EigenVector::Flatten(*label); + auto dl = framework::EigenVector::Flatten(*dloss); + + if (dpred) { + dpred->mutable_data(context.GetPlace()); + auto dx = framework::EigenVector::Flatten(*dpred); + auto alt_labels = static_cast(2) * y - static_cast(1); + dx.device(place) = + dl * ((x * alt_labels) < static_cast(1)).template cast() * + (-alt_labels); + } + } +}; + +} // namespace operators +} // namespace paddle diff --git a/python/paddle/v2/fluid/tests/test_hinge_loss_op.py b/python/paddle/v2/fluid/tests/test_hinge_loss_op.py new file mode 100644 index 0000000000..a8757a891f --- /dev/null +++ b/python/paddle/v2/fluid/tests/test_hinge_loss_op.py @@ -0,0 +1,28 @@ +import unittest +import numpy as np +from op_test import OpTest + + +class TestHingeLossOp(OpTest): + def setUp(self): + self.op_type = 'hinge_loss' + samples_num = 64 + logits = np.random.uniform(-10, 10, (samples_num, 1)).astype('float32') + labels = np.random.randint(0, 2, (samples_num, 1)).astype('float32') + + self.inputs = { + 'Logits': logits, + 'Labels': labels, + } + loss = np.maximum(1.0 - (2 * labels - 1) * logits, 0) + self.outputs = {'Loss': loss} + + def test_check_output(self): + self.check_output() + + def test_check_grad(self): + self.check_grad(['Logits'], 'Loss', max_relative_error=0.008) + + +if __name__ == '__main__': + unittest.main() From e5b51c4d102ed180aef3940bd8e885c4bf5f9d95 Mon Sep 17 00:00:00 2001 From: qingqing01 Date: Sun, 3 Dec 2017 16:50:24 +0800 Subject: [PATCH 60/67] Make lstm_op follow google code style. --- paddle/operators/lstm_op.h | 70 +-- .../operators/math/detail/lstm_cpu_kernel.h | 426 +++++++++--------- .../operators/math/detail/lstm_gpu_kernel.h | 305 ++++++------- paddle/operators/math/detail/lstm_kernel.h | 128 +++--- paddle/operators/math/lstm_compute.cc | 36 +- paddle/operators/math/lstm_compute.h | 32 +- 6 files changed, 505 insertions(+), 492 deletions(-) diff --git a/paddle/operators/lstm_op.h b/paddle/operators/lstm_op.h index 721aa42c92..a78f548aaf 100644 --- a/paddle/operators/lstm_op.h +++ b/paddle/operators/lstm_op.h @@ -73,15 +73,15 @@ class LSTMKernel : public framework::OpKernel { T* bias_data = const_cast(bias->data()); // the code style in LstmMetaValue will be updated later. - lstm_value.checkIg = bias_data + 4 * frame_size; - lstm_value.checkFg = lstm_value.checkIg + frame_size; - lstm_value.checkOg = lstm_value.checkFg + frame_size; + lstm_value.check_ig = bias_data + 4 * frame_size; + lstm_value.check_fg = lstm_value.check_ig + frame_size; + lstm_value.check_og = lstm_value.check_fg + frame_size; } else { - lstm_value.checkIg = nullptr; - lstm_value.checkFg = nullptr; - lstm_value.checkOg = nullptr; + lstm_value.check_ig = nullptr; + lstm_value.check_fg = nullptr; + lstm_value.check_og = nullptr; } - lstm_value.prevStateValue = nullptr; + lstm_value.prev_state_value = nullptr; Tensor ordered_c0; const size_t* order = batch_gate->lod()[2].data(); if (cell_t0) { @@ -90,7 +90,7 @@ class LSTMKernel : public framework::OpKernel { // to reorder. ReorderInitState(device_ctx, *cell_t0, order, &ordered_c0, true); - lstm_value.prevStateValue = ordered_c0.data(); + lstm_value.prev_state_value = ordered_c0.data(); } // Use the local variable as here. @@ -140,14 +140,14 @@ class LSTMKernel : public framework::OpKernel { static_cast(1.0)); } - lstm_value.gateValue = gate_t.data(); - lstm_value.outputValue = out_t.data(); - lstm_value.stateValue = cell_t.data(); - lstm_value.stateActiveValue = cell_pre_act_t.data(); + lstm_value.gate_value = gate_t.data(); + lstm_value.output_value = out_t.data(); + lstm_value.state_value = cell_t.data(); + lstm_value.state_active_value = cell_pre_act_t.data(); math::LstmUnitFunctor::compute(device_ctx, lstm_value, frame_size, cur_batch_size, gate_act, cell_act, cand_act); - lstm_value.prevStateValue = lstm_value.stateValue; + lstm_value.prev_state_value = lstm_value.state_value; } math::Batch2LoDTensorFunctor to_seq; @@ -214,13 +214,13 @@ class LSTMGradKernel : public framework::OpKernel { math::LstmMetaValue lstm_value; if (bias && ctx.Attr("use_peepholes")) { T* bias_data = const_cast(bias->data()); - lstm_value.checkIg = bias_data + 4 * frame_size; - lstm_value.checkFg = lstm_value.checkIg + frame_size; - lstm_value.checkOg = lstm_value.checkFg + frame_size; + lstm_value.check_ig = bias_data + 4 * frame_size; + lstm_value.check_fg = lstm_value.check_ig + frame_size; + lstm_value.check_og = lstm_value.check_fg + frame_size; } else { - lstm_value.checkIg = nullptr; - lstm_value.checkFg = nullptr; - lstm_value.checkOg = nullptr; + lstm_value.check_ig = nullptr; + lstm_value.check_fg = nullptr; + lstm_value.check_og = nullptr; } math::LstmMetaGrad lstm_grad; @@ -231,13 +231,13 @@ class LSTMGradKernel : public framework::OpKernel { } if (bias && bias_g && ctx.Attr("use_peepholes")) { T* bias_g_data = bias_g->data(); - lstm_grad.checkIgGrad = bias_g_data + 4 * frame_size; - lstm_grad.checkFgGrad = lstm_grad.checkIgGrad + frame_size; - lstm_grad.checkOgGrad = lstm_grad.checkFgGrad + frame_size; + lstm_grad.check_ig_grad = bias_g_data + 4 * frame_size; + lstm_grad.check_fg_grad = lstm_grad.check_ig_grad + frame_size; + lstm_grad.check_og_grad = lstm_grad.check_fg_grad + frame_size; } else { - lstm_grad.checkIgGrad = nullptr; - lstm_grad.checkFgGrad = nullptr; - lstm_grad.checkOgGrad = nullptr; + lstm_grad.check_ig_grad = nullptr; + lstm_grad.check_fg_grad = nullptr; + lstm_grad.check_og_grad = nullptr; } math::LoDTensor2BatchFunctor to_batch; @@ -276,26 +276,26 @@ class LSTMGradKernel : public framework::OpKernel { Tensor gate = batch_gate->Slice(bstart, bend); Tensor cell = batch_cell.Slice(bstart, bend); Tensor cell_pre_act = batch_cell_pre_act->Slice(bstart, bend); - lstm_value.gateValue = gate.data(); - lstm_value.stateValue = cell.data(); - lstm_value.stateActiveValue = cell_pre_act.data(); + lstm_value.gate_value = gate.data(); + lstm_value.state_value = cell.data(); + lstm_value.state_active_value = cell_pre_act.data(); Tensor out_g = batch_hidden_g.Slice(bstart, bend); Tensor gate_g = batch_gate_g.Slice(bstart, bend); Tensor cell_g = batch_cell_g.Slice(bstart, bend); - lstm_grad.stateGrad = cell_g.data(); - lstm_grad.gateGrad = gate_g.data(); - lstm_grad.outputGrad = out_g.data(); + lstm_grad.state_grad = cell_g.data(); + lstm_grad.gate_grad = gate_g.data(); + lstm_grad.output_grad = out_g.data(); if (n > 0) { int bstart_pre = static_cast(batch_starts[n - 1]); Tensor cell_pre = batch_cell.Slice(bstart_pre, bstart); Tensor cell_pre_g = batch_cell_g.Slice(bstart_pre, bstart); - lstm_value.prevStateValue = cell_pre.data(); - lstm_grad.prevStateGrad = cell_pre_g.data(); + lstm_value.prev_state_value = cell_pre.data(); + lstm_grad.prev_state_grad = cell_pre_g.data(); } else { - lstm_value.prevStateValue = c0 ? ordered_c0.data() : nullptr; - lstm_grad.prevStateGrad = c0_g ? ordered_c0_g.data() : nullptr; + lstm_value.prev_state_value = c0 ? ordered_c0.data() : nullptr; + lstm_grad.prev_state_grad = c0_g ? ordered_c0_g.data() : nullptr; } int cur_batch_size = bend - bstart; diff --git a/paddle/operators/math/detail/lstm_cpu_kernel.h b/paddle/operators/math/detail/lstm_cpu_kernel.h index fc3ad0ce58..a734ad31ee 100644 --- a/paddle/operators/math/detail/lstm_cpu_kernel.h +++ b/paddle/operators/math/detail/lstm_cpu_kernel.h @@ -26,278 +26,284 @@ namespace detail { template void naive_lstm_forward_one_sequence(Op op, LstmMetaValue value, - int frameSize, + int frame_size, activation_mode_t active_node, activation_mode_t active_gate, activation_mode_t active_state) { - T rValueIn; - T rValueIg; - T rValueFg; - T rValueOg; - T rCheckI; - T rCheckF; - T rCheckO; - T rState; - T rPrevState = 0; - T rStateAtv; - T rOut; - - T *valueIn = value.gateValue; - T *valueIg = value.gateValue + frameSize; - T *valueFg = value.gateValue + frameSize * 2; - T *valueOg = value.gateValue + frameSize * 3; - - for (int i = 0; i < frameSize; i++) { - rValueIn = valueIn[i]; - rValueIg = valueIg[i]; - rValueFg = valueFg[i]; - rValueOg = valueOg[i]; - rCheckI = value.checkIg ? value.checkIg[i] : 0; - rCheckF = value.checkFg ? value.checkFg[i] : 0; - rCheckO = value.checkOg ? value.checkOg[i] : 0; - - if (value.prevStateValue) { - rPrevState = value.prevStateValue[i]; + T r_value_in; + T r_value_ig; + T r_value_fg; + T r_value_og; + T r_checkI; + T r_checkF; + T r_checkO; + T r_state; + T r_prev_state = 0; + T r_state_atv; + T r_out; + + T *value_in = value.gate_value; + T *value_ig = value.gate_value + frame_size; + T *value_fg = value.gate_value + frame_size * 2; + T *value_og = value.gate_value + frame_size * 3; + + for (int i = 0; i < frame_size; i++) { + r_value_in = value_in[i]; + r_value_ig = value_ig[i]; + r_value_fg = value_fg[i]; + r_value_og = value_og[i]; + r_checkI = value.check_ig ? value.check_ig[i] : 0; + r_checkF = value.check_fg ? value.check_fg[i] : 0; + r_checkO = value.check_og ? value.check_og[i] : 0; + + if (value.prev_state_value) { + r_prev_state = value.prev_state_value[i]; } - op(rValueIn, rValueIg, rValueFg, rValueOg, rPrevState, rState, rStateAtv, - rOut, rCheckI, rCheckF, rCheckO, active_node, active_gate, active_state); - - valueIn[i] = rValueIn; - valueIg[i] = rValueIg; - valueFg[i] = rValueFg; - valueOg[i] = rValueOg; - value.stateValue[i] = rState; - value.stateActiveValue[i] = rStateAtv; - value.outputValue[i] = rOut; + op(r_value_in, r_value_ig, r_value_fg, r_value_og, r_prev_state, r_state, + r_state_atv, r_out, r_checkI, r_checkF, r_checkO, active_node, + active_gate, active_state); + + value_in[i] = r_value_in; + value_ig[i] = r_value_ig; + value_fg[i] = r_value_fg; + value_og[i] = r_value_og; + value.state_value[i] = r_state; + value.state_active_value[i] = r_state_atv; + value.output_value[i] = r_out; } } template void naive_lstm_backward_one_sequence(Op op, LstmMetaValue value, - LstmMetaGrad grad, int frameSize, + LstmMetaGrad grad, int frame_size, activation_mode_t active_node, activation_mode_t active_gate, activation_mode_t active_state) { - T rValueIn; - T rValueIg; - T rValueFg; - T rValueOg; - T rGradIn; - T rGradIg; - T rGradFg; - T rGradOg; - T rPrevState = 0; - T rPrevStateGrad; - T rState; - T rStateGrad; - T rStateAtv; - T rOutputGrad; - T rCheckI; - T rCheckF; - T rCheckO; - T rCheckIGrad; - T rCheckFGrad; - T rCheckOGrad; - - T *valueIn = value.gateValue; - T *valueIg = value.gateValue + frameSize; - T *valueFg = value.gateValue + frameSize * 2; - T *valueOg = value.gateValue + frameSize * 3; - T *gradIn = grad.gateGrad; - T *gradIg = grad.gateGrad + frameSize; - T *gradFg = grad.gateGrad + frameSize * 2; - T *gradOg = grad.gateGrad + frameSize * 3; - - for (int i = 0; i < frameSize; i++) { - rValueIn = valueIn[i]; - rValueIg = valueIg[i]; - rValueFg = valueFg[i]; - rValueOg = valueOg[i]; - rCheckI = value.checkIg ? value.checkIg[i] : 0; - rCheckF = value.checkFg ? value.checkFg[i] : 0; - rCheckO = value.checkOg ? value.checkOg[i] : 0; - rState = value.stateValue[i]; - rStateAtv = value.stateActiveValue[i]; - rOutputGrad = grad.outputGrad[i]; - rStateGrad = grad.stateGrad[i]; - if (value.prevStateValue) { - rPrevState = value.prevStateValue[i]; + T r_value_in; + T r_value_ig; + T r_value_fg; + T r_value_og; + T r_grad_in; + T r_grad_ig; + T r_grad_fg; + T r_grad_og; + T r_prev_state = 0; + T r_prev_state_grad; + T r_state; + T r_state_grad; + T r_state_atv; + T r_output_grad; + T r_checkI; + T r_checkF; + T r_checkO; + T r_checkIGrad; + T r_checkFGrad; + T r_checkOGrad; + + T *value_in = value.gate_value; + T *value_ig = value.gate_value + frame_size; + T *value_fg = value.gate_value + frame_size * 2; + T *value_og = value.gate_value + frame_size * 3; + T *grad_in = grad.gate_grad; + T *grad_ig = grad.gate_grad + frame_size; + T *grad_fg = grad.gate_grad + frame_size * 2; + T *grad_og = grad.gate_grad + frame_size * 3; + + for (int i = 0; i < frame_size; i++) { + r_value_in = value_in[i]; + r_value_ig = value_ig[i]; + r_value_fg = value_fg[i]; + r_value_og = value_og[i]; + r_checkI = value.check_ig ? value.check_ig[i] : 0; + r_checkF = value.check_fg ? value.check_fg[i] : 0; + r_checkO = value.check_og ? value.check_og[i] : 0; + r_state = value.state_value[i]; + r_state_atv = value.state_active_value[i]; + r_output_grad = grad.output_grad[i]; + r_state_grad = grad.state_grad[i]; + if (value.prev_state_value) { + r_prev_state = value.prev_state_value[i]; } - op(rValueIn, rValueIg, rValueFg, rValueOg, rGradIn, rGradIg, rGradFg, - rGradOg, rPrevState, rPrevStateGrad, rState, rStateGrad, rStateAtv, - rOutputGrad, rCheckI, rCheckF, rCheckO, rCheckIGrad, rCheckFGrad, - rCheckOGrad, active_node, active_gate, active_state); - - gradIn[i] = rGradIn; - gradIg[i] = rGradIg; - gradFg[i] = rGradFg; - gradOg[i] = rGradOg; - grad.stateGrad[i] = rStateGrad; - - if (grad.prevStateGrad) grad.prevStateGrad[i] = rPrevStateGrad; - if (value.prevStateValue) { - if (grad.checkIgGrad) grad.checkIgGrad[i] += rCheckIGrad; - if (grad.checkFgGrad) grad.checkFgGrad[i] += rCheckFGrad; + op(r_value_in, r_value_ig, r_value_fg, r_value_og, r_grad_in, r_grad_ig, + r_grad_fg, r_grad_og, r_prev_state, r_prev_state_grad, r_state, + r_state_grad, r_state_atv, r_output_grad, r_checkI, r_checkF, r_checkO, + r_checkIGrad, r_checkFGrad, r_checkOGrad, active_node, active_gate, + active_state); + + grad_in[i] = r_grad_in; + grad_ig[i] = r_grad_ig; + grad_fg[i] = r_grad_fg; + grad_og[i] = r_grad_og; + grad.state_grad[i] = r_state_grad; + + if (grad.prev_state_grad) grad.prev_state_grad[i] = r_prev_state_grad; + if (value.prev_state_value) { + if (grad.check_ig_grad) grad.check_ig_grad[i] += r_checkIGrad; + if (grad.check_fg_grad) grad.check_fg_grad[i] += r_checkFGrad; } - if (grad.checkOgGrad) grad.checkOgGrad[i] += rCheckOGrad; + if (grad.check_og_grad) grad.check_og_grad[i] += r_checkOGrad; } } template -void avx_lstm_forward_one_sequence(Op op, LstmMetaValue value, int frameSize, +void avx_lstm_forward_one_sequence(Op op, LstmMetaValue value, + int frame_size, activation_mode_t active_node, activation_mode_t active_gate, activation_mode_t active_state) { #ifdef __AVX__ - __m256 rValueIn; - __m256 rValueIg; - __m256 rValueFg; - __m256 rValueOg; - __m256 rCheckI = _mm256_set1_ps(0.0f); - __m256 rCheckF = _mm256_set1_ps(0.0f); - __m256 rCheckO = _mm256_set1_ps(0.0f); - __m256 rState; - __m256 rPrevState = _mm256_set1_ps(0.0f); - __m256 rStateAtv; - __m256 rOut; - - __m256 *valueIn = (__m256 *)value.gateValue; - __m256 *valueIg = (__m256 *)(value.gateValue + frameSize); - __m256 *valueFg = (__m256 *)(value.gateValue + frameSize * 2); - __m256 *valueOg = (__m256 *)(value.gateValue + frameSize * 3); - - for (int i = 0; i < frameSize / 8; i++) { - rValueIn = valueIn[i]; - rValueIg = valueIg[i]; - rValueFg = valueFg[i]; - rValueOg = valueOg[i]; - if (value.checkIg) { - rCheckI = ((__m256 *)value.checkIg)[i]; - rCheckF = ((__m256 *)value.checkFg)[i]; - rCheckO = ((__m256 *)value.checkOg)[i]; + __m256 r_value_in; + __m256 r_value_ig; + __m256 r_value_fg; + __m256 r_value_og; + __m256 r_checkI = _mm256_set1_ps(0.0f); + __m256 r_checkF = _mm256_set1_ps(0.0f); + __m256 r_checkO = _mm256_set1_ps(0.0f); + __m256 r_state; + __m256 r_prev_state = _mm256_set1_ps(0.0f); + __m256 r_state_atv; + __m256 r_out; + + __m256 *value_in = (__m256 *)value.gate_value; + __m256 *value_ig = (__m256 *)(value.gate_value + frame_size); + __m256 *value_fg = (__m256 *)(value.gate_value + frame_size * 2); + __m256 *value_og = (__m256 *)(value.gate_value + frame_size * 3); + + for (int i = 0; i < frame_size / 8; i++) { + r_value_in = value_in[i]; + r_value_ig = value_ig[i]; + r_value_fg = value_fg[i]; + r_value_og = value_og[i]; + if (value.check_ig) { + r_checkI = ((__m256 *)value.check_ig)[i]; + r_checkF = ((__m256 *)value.check_fg)[i]; + r_checkO = ((__m256 *)value.check_og)[i]; } - if (value.prevStateValue) { - rPrevState = ((__m256 *)value.prevStateValue)[i]; + if (value.prev_state_value) { + r_prev_state = ((__m256 *)value.prev_state_value)[i]; } - op(rValueIn, rValueIg, rValueFg, rValueOg, rPrevState, rState, rStateAtv, - rOut, rCheckI, rCheckF, rCheckO, active_node, active_gate, active_state); - - valueIn[i] = rValueIn; - valueIg[i] = rValueIg; - valueFg[i] = rValueFg; - valueOg[i] = rValueOg; - ((__m256 *)value.stateValue)[i] = rState; - ((__m256 *)value.stateActiveValue)[i] = rStateAtv; - ((__m256 *)value.outputValue)[i] = rOut; + op(r_value_in, r_value_ig, r_value_fg, r_value_og, r_prev_state, r_state, + r_state_atv, r_out, r_checkI, r_checkF, r_checkO, active_node, + active_gate, active_state); + + value_in[i] = r_value_in; + value_ig[i] = r_value_ig; + value_fg[i] = r_value_fg; + value_og[i] = r_value_og; + ((__m256 *)value.state_value)[i] = r_state; + ((__m256 *)value.state_active_value)[i] = r_state_atv; + ((__m256 *)value.output_value)[i] = r_out; } #endif } template void avx_lstm_backward_one_sequence(Op op, LstmMetaValue value, - LstmMetaGrad grad, int frameSize, + LstmMetaGrad grad, int frame_size, activation_mode_t active_node, activation_mode_t active_gate, activation_mode_t active_state) { #ifdef __AVX__ - __m256 rValueIn; - __m256 rValueIg; - __m256 rValueFg; - __m256 rValueOg; - __m256 rGradIn; - __m256 rGradIg; - __m256 rGradFg; - __m256 rGradOg; - __m256 rPrevState = _mm256_set1_ps(0.0f); - __m256 rPrevStateGrad; - __m256 rStateGrad; - __m256 rState; - __m256 rStateAtv; - __m256 rOutputGrad; - __m256 rCheckI = _mm256_set1_ps(0.0f); - __m256 rCheckF = _mm256_set1_ps(0.0f); - __m256 rCheckO = _mm256_set1_ps(0.0f); - __m256 rCheckIGrad; - __m256 rCheckFGrad; - __m256 rCheckOGrad; - - __m256 *valueIn = (__m256 *)value.gateValue; - __m256 *valueIg = (__m256 *)(value.gateValue + frameSize); - __m256 *valueFg = (__m256 *)(value.gateValue + frameSize * 2); - __m256 *valueOg = (__m256 *)(value.gateValue + frameSize * 3); - __m256 *gradIn = (__m256 *)grad.gateGrad; - __m256 *gradIg = (__m256 *)(grad.gateGrad + frameSize); - __m256 *gradFg = (__m256 *)(grad.gateGrad + frameSize * 2); - __m256 *gradOg = (__m256 *)(grad.gateGrad + frameSize * 3); - - for (int i = 0; i < frameSize / 8; i++) { - rValueIn = valueIn[i]; - rValueIg = valueIg[i]; - rValueFg = valueFg[i]; - rValueOg = valueOg[i]; - if (value.checkIg) { - rCheckI = ((__m256 *)value.checkIg)[i]; - rCheckF = ((__m256 *)value.checkFg)[i]; - rCheckO = ((__m256 *)value.checkOg)[i]; + __m256 r_value_in; + __m256 r_value_ig; + __m256 r_value_fg; + __m256 r_value_og; + __m256 r_grad_in; + __m256 r_grad_ig; + __m256 r_grad_fg; + __m256 r_grad_og; + __m256 r_prev_state = _mm256_set1_ps(0.0f); + __m256 r_prev_state_grad; + __m256 r_state_grad; + __m256 r_state; + __m256 r_state_atv; + __m256 r_output_grad; + __m256 r_checkI = _mm256_set1_ps(0.0f); + __m256 r_checkF = _mm256_set1_ps(0.0f); + __m256 r_checkO = _mm256_set1_ps(0.0f); + __m256 r_checkIGrad; + __m256 r_checkFGrad; + __m256 r_checkOGrad; + + __m256 *value_in = (__m256 *)value.gate_value; + __m256 *value_ig = (__m256 *)(value.gate_value + frame_size); + __m256 *value_fg = (__m256 *)(value.gate_value + frame_size * 2); + __m256 *value_og = (__m256 *)(value.gate_value + frame_size * 3); + __m256 *grad_in = (__m256 *)grad.gate_grad; + __m256 *grad_ig = (__m256 *)(grad.gate_grad + frame_size); + __m256 *grad_fg = (__m256 *)(grad.gate_grad + frame_size * 2); + __m256 *grad_og = (__m256 *)(grad.gate_grad + frame_size * 3); + + for (int i = 0; i < frame_size / 8; i++) { + r_value_in = value_in[i]; + r_value_ig = value_ig[i]; + r_value_fg = value_fg[i]; + r_value_og = value_og[i]; + if (value.check_ig) { + r_checkI = ((__m256 *)value.check_ig)[i]; + r_checkF = ((__m256 *)value.check_fg)[i]; + r_checkO = ((__m256 *)value.check_og)[i]; } - rState = ((__m256 *)value.stateValue)[i]; - rStateAtv = ((__m256 *)value.stateActiveValue)[i]; - rOutputGrad = ((__m256 *)grad.outputGrad)[i]; - rStateGrad = ((__m256 *)grad.stateGrad)[i]; - if (value.prevStateValue) { - rPrevState = ((__m256 *)value.prevStateValue)[i]; + r_state = ((__m256 *)value.state_value)[i]; + r_state_atv = ((__m256 *)value.state_active_value)[i]; + r_output_grad = ((__m256 *)grad.output_grad)[i]; + r_state_grad = ((__m256 *)grad.state_grad)[i]; + if (value.prev_state_value) { + r_prev_state = ((__m256 *)value.prev_state_value)[i]; } - op(rValueIn, rValueIg, rValueFg, rValueOg, rGradIn, rGradIg, rGradFg, - rGradOg, rPrevState, rPrevStateGrad, rState, rStateGrad, rStateAtv, - rOutputGrad, rCheckI, rCheckF, rCheckO, rCheckIGrad, rCheckFGrad, - rCheckOGrad, active_node, active_gate, active_state); - - gradIn[i] = rGradIn; - gradIg[i] = rGradIg; - gradFg[i] = rGradFg; - gradOg[i] = rGradOg; - ((__m256 *)grad.stateGrad)[i] = rStateGrad; - - if (grad.prevStateGrad) ((__m256 *)grad.prevStateGrad)[i] = rPrevStateGrad; - if (value.prevStateValue) { - if (grad.checkIgGrad) ((__m256 *)grad.checkIgGrad)[i] += rCheckIGrad; - if (grad.checkFgGrad) ((__m256 *)grad.checkFgGrad)[i] += rCheckFGrad; + op(r_value_in, r_value_ig, r_value_fg, r_value_og, r_grad_in, r_grad_ig, + r_grad_fg, r_grad_og, r_prev_state, r_prev_state_grad, r_state, + r_state_grad, r_state_atv, r_output_grad, r_checkI, r_checkF, r_checkO, + r_checkIGrad, r_checkFGrad, r_checkOGrad, active_node, active_gate, + active_state); + + grad_in[i] = r_grad_in; + grad_ig[i] = r_grad_ig; + grad_fg[i] = r_grad_fg; + grad_og[i] = r_grad_og; + ((__m256 *)grad.state_grad)[i] = r_state_grad; + + if (grad.prev_state_grad) + ((__m256 *)grad.prev_state_grad)[i] = r_prev_state_grad; + if (value.prev_state_value) { + if (grad.check_ig_grad) ((__m256 *)grad.check_ig_grad)[i] += r_checkIGrad; + if (grad.check_fg_grad) ((__m256 *)grad.check_fg_grad)[i] += r_checkFGrad; } - if (grad.checkOgGrad) ((__m256 *)grad.checkOgGrad)[i] += rCheckOGrad; + if (grad.check_og_grad) ((__m256 *)grad.check_og_grad)[i] += r_checkOGrad; } #endif } template -void cpu_lstm_forward(Op op, LstmMetaValue value, int frameSize, +void cpu_lstm_forward(Op op, LstmMetaValue value, int frame_size, activation_mode_t active_node, activation_mode_t active_gate, activation_mode_t active_state) { - if (Op::avx && !(frameSize & (8 - 1)) && (std::is_same::value)) { - avx_lstm_forward_one_sequence(op, value, frameSize, active_node, + if (Op::avx && !(frame_size & (8 - 1)) && (std::is_same::value)) { + avx_lstm_forward_one_sequence(op, value, frame_size, active_node, active_gate, active_state); } else { - naive_lstm_forward_one_sequence(op, value, frameSize, active_node, + naive_lstm_forward_one_sequence(op, value, frame_size, active_node, active_gate, active_state); } } template void cpu_lstm_backward(Op op, LstmMetaValue value, LstmMetaGrad grad, - int frameSize, activation_mode_t active_node, + int frame_size, activation_mode_t active_node, activation_mode_t active_gate, activation_mode_t active_state) { - if (Op::avx && !(frameSize & (8 - 1)) && (std::is_same::value)) { - avx_lstm_backward_one_sequence(op, value, grad, frameSize, active_node, + if (Op::avx && !(frame_size & (8 - 1)) && (std::is_same::value)) { + avx_lstm_backward_one_sequence(op, value, grad, frame_size, active_node, active_gate, active_state); } else { - naive_lstm_backward_one_sequence(op, value, grad, frameSize, active_node, - active_gate, active_state); + naive_lstm_backward_one_sequence(op, value, grad, frame_size, + active_node, active_gate, active_state); } } diff --git a/paddle/operators/math/detail/lstm_gpu_kernel.h b/paddle/operators/math/detail/lstm_gpu_kernel.h index d138bbe411..91bfedea53 100644 --- a/paddle/operators/math/detail/lstm_gpu_kernel.h +++ b/paddle/operators/math/detail/lstm_gpu_kernel.h @@ -26,189 +26,192 @@ namespace math { namespace detail { /* - * threads(framePerBlock, batchPerBlock) - * grid(frameBlocks, batchBlocks) + * threads(frame_per_block, batch_per_block) + * grid(frame_blocks, batch_blocks) */ -template -__global__ void KeLstmForward(Op op, LstmMetaValue value, int frameSize, - int batchSize, activation_mode_t active_node, +template +__global__ void KeLstmForward(Op op, LstmMetaValue value, int frame_size, + int batch_size, activation_mode_t active_node, activation_mode_t active_gate, activation_mode_t active_state) { - const int frameIdx = blockIdx.x * blockDim.x + threadIdx.x; - if (frameIdx >= frameSize) return; - - int batchIdx = 0; - if (isBatch) { - batchIdx = blockIdx.y * blockDim.y + threadIdx.y; - if (batchIdx >= batchSize) return; - value.gateValue += batchIdx * frameSize * 4; - value.outputValue += batchIdx * frameSize; - value.stateValue += batchIdx * frameSize; - value.stateActiveValue += batchIdx * frameSize; + const int frame_idx = blockIdx.x * blockDim.x + threadIdx.x; + if (frame_idx >= frame_size) return; + + int batch_idx = 0; + if (is_batch) { + batch_idx = blockIdx.y * blockDim.y + threadIdx.y; + if (batch_idx >= batch_size) return; + value.gate_value += batch_idx * frame_size * 4; + value.output_value += batch_idx * frame_size; + value.state_value += batch_idx * frame_size; + value.state_active_value += batch_idx * frame_size; } - T rState; - T rPrevState = 0; - T rStateAtv; - T rOut; - T rValueIn; - T rValueIg; - T rValueFg; - T rValueOg; - - T rCheckI = value.checkIg ? value.checkIg[frameIdx] : 0; - T rCheckF = value.checkFg ? value.checkFg[frameIdx] : 0; - T rCheckO = value.checkOg ? value.checkOg[frameIdx] : 0; - - rValueIn = value.gateValue[frameIdx]; - rValueIg = value.gateValue[frameIdx + frameSize]; - rValueFg = value.gateValue[frameIdx + frameSize * 2]; - rValueOg = value.gateValue[frameIdx + frameSize * 3]; - - if (value.prevStateValue) { - if (isBatch) value.prevStateValue += batchIdx * frameSize; - rPrevState = value.prevStateValue[frameIdx]; + T r_state; + T r_prev_state = 0; + T r_state_atv; + T r_out; + T r_value_in; + T r_value_ig; + T r_value_fg; + T r_value_og; + + T r_checkI = value.check_ig ? value.check_ig[frame_idx] : 0; + T r_checkF = value.check_fg ? value.check_fg[frame_idx] : 0; + T r_checkO = value.check_og ? value.check_og[frame_idx] : 0; + + r_value_in = value.gate_value[frame_idx]; + r_value_ig = value.gate_value[frame_idx + frame_size]; + r_value_fg = value.gate_value[frame_idx + frame_size * 2]; + r_value_og = value.gate_value[frame_idx + frame_size * 3]; + + if (value.prev_state_value) { + if (is_batch) value.prev_state_value += batch_idx * frame_size; + r_prev_state = value.prev_state_value[frame_idx]; } - op(rValueIn, rValueIg, rValueFg, rValueOg, rPrevState, rState, rStateAtv, - rOut, rCheckI, rCheckF, rCheckO, active_node, active_gate, active_state); + op(r_value_in, r_value_ig, r_value_fg, r_value_og, r_prev_state, r_state, + r_state_atv, r_out, r_checkI, r_checkF, r_checkO, active_node, active_gate, + active_state); - value.gateValue[frameIdx] = rValueIn; - value.gateValue[frameIdx + frameSize] = rValueIg; - value.gateValue[frameIdx + frameSize * 2] = rValueFg; - value.gateValue[frameIdx + frameSize * 3] = rValueOg; + value.gate_value[frame_idx] = r_value_in; + value.gate_value[frame_idx + frame_size] = r_value_ig; + value.gate_value[frame_idx + frame_size * 2] = r_value_fg; + value.gate_value[frame_idx + frame_size * 3] = r_value_og; - value.stateValue[frameIdx] = rState; - value.stateActiveValue[frameIdx] = rStateAtv; - value.outputValue[frameIdx] = rOut; + value.state_value[frame_idx] = r_state; + value.state_active_value[frame_idx] = r_state_atv; + value.output_value[frame_idx] = r_out; } /* - * threads(framePerBlock, batchPerBlock) - * grid(frameBlocks, batchBlocks) + * threads(frame_per_block, batch_per_block) + * grid(frame_blocks, batch_blocks) */ -template +template __global__ void KeLstmBackward(Op op, LstmMetaValue value, - LstmMetaGrad grad, int frameSize, - int batchSize, activation_mode_t active_node, + LstmMetaGrad grad, int frame_size, + int batch_size, activation_mode_t active_node, activation_mode_t active_gate, activation_mode_t active_state) { - const int frameIdx = blockIdx.x * blockDim.x + threadIdx.x; - if (frameIdx >= frameSize) return; - - int batchIdx = 0; - if (isBatch) { - batchIdx = blockIdx.y * blockDim.y + threadIdx.y; - if (batchIdx >= batchSize) return; - value.gateValue += batchIdx * frameSize * 4; - value.stateValue += batchIdx * frameSize; - value.stateActiveValue += batchIdx * frameSize; - grad.gateGrad += batchIdx * frameSize * 4; - grad.stateGrad += batchIdx * frameSize; - grad.outputGrad += batchIdx * frameSize; + const int frame_idx = blockIdx.x * blockDim.x + threadIdx.x; + if (frame_idx >= frame_size) return; + + int batch_idx = 0; + if (is_batch) { + batch_idx = blockIdx.y * blockDim.y + threadIdx.y; + if (batch_idx >= batch_size) return; + value.gate_value += batch_idx * frame_size * 4; + value.state_value += batch_idx * frame_size; + value.state_active_value += batch_idx * frame_size; + grad.gate_grad += batch_idx * frame_size * 4; + grad.state_grad += batch_idx * frame_size; + grad.output_grad += batch_idx * frame_size; } - T rValueIn; - T rValueIg; - T rValueFg; - T rValueOg; - T rGradIn; - T rGradIg; - T rGradFg; - T rGradOg; - T rPrevState = 0; - T rPrevStateGrad; - T rState; - T rStateGrad; - T rStateAtv; - T rOutputGrad; - T rCheckI = value.checkIg ? value.checkIg[frameIdx] : 0; - T rCheckF = value.checkFg ? value.checkFg[frameIdx] : 0; - T rCheckO = value.checkOg ? value.checkOg[frameIdx] : 0; - - T rCheckIGrad; - T rCheckFGrad; - T rCheckOGrad; - - rValueIn = value.gateValue[frameIdx]; - rValueIg = value.gateValue[frameIdx + frameSize]; - rValueFg = value.gateValue[frameIdx + frameSize * 2]; - rValueOg = value.gateValue[frameIdx + frameSize * 3]; - rState = value.stateValue[frameIdx]; - rStateAtv = value.stateActiveValue[frameIdx]; - rOutputGrad = grad.outputGrad[frameIdx]; - rStateGrad = grad.stateGrad[frameIdx]; - - if (value.prevStateValue) { - if (isBatch) value.prevStateValue += batchIdx * frameSize; - rPrevState = value.prevStateValue[frameIdx]; + T r_value_in; + T r_value_ig; + T r_value_fg; + T r_value_og; + T r_grad_in; + T r_grad_ig; + T r_grad_fg; + T r_grad_og; + T r_prev_state = 0; + T r_prev_state_grad; + T r_state; + T r_state_grad; + T r_state_atv; + T r_output_grad; + T r_checkI = value.check_ig ? value.check_ig[frame_idx] : 0; + T r_checkF = value.check_fg ? value.check_fg[frame_idx] : 0; + T r_checkO = value.check_og ? value.check_og[frame_idx] : 0; + + T r_checkIGrad; + T r_checkFGrad; + T r_checkOGrad; + + r_value_in = value.gate_value[frame_idx]; + r_value_ig = value.gate_value[frame_idx + frame_size]; + r_value_fg = value.gate_value[frame_idx + frame_size * 2]; + r_value_og = value.gate_value[frame_idx + frame_size * 3]; + r_state = value.state_value[frame_idx]; + r_state_atv = value.state_active_value[frame_idx]; + r_output_grad = grad.output_grad[frame_idx]; + r_state_grad = grad.state_grad[frame_idx]; + + if (value.prev_state_value) { + if (is_batch) value.prev_state_value += batch_idx * frame_size; + r_prev_state = value.prev_state_value[frame_idx]; } - op(rValueIn, rValueIg, rValueFg, rValueOg, rGradIn, rGradIg, rGradFg, rGradOg, - rPrevState, rPrevStateGrad, rState, rStateGrad, rStateAtv, rOutputGrad, - rCheckI, rCheckF, rCheckO, rCheckIGrad, rCheckFGrad, rCheckOGrad, - active_node, active_gate, active_state); - - grad.gateGrad[frameIdx] = rGradIn; - grad.gateGrad[frameIdx + frameSize] = rGradIg; - grad.gateGrad[frameIdx + frameSize * 2] = rGradFg; - grad.gateGrad[frameIdx + frameSize * 3] = rGradOg; - grad.stateGrad[frameIdx] = rStateGrad; - if (grad.prevStateGrad) { - if (isBatch) grad.prevStateGrad += batchIdx * frameSize; - grad.prevStateGrad[frameIdx] = rPrevStateGrad; + op(r_value_in, r_value_ig, r_value_fg, r_value_og, r_grad_in, r_grad_ig, + r_grad_fg, r_grad_og, r_prev_state, r_prev_state_grad, r_state, + r_state_grad, r_state_atv, r_output_grad, r_checkI, r_checkF, r_checkO, + r_checkIGrad, r_checkFGrad, r_checkOGrad, active_node, active_gate, + active_state); + + grad.gate_grad[frame_idx] = r_grad_in; + grad.gate_grad[frame_idx + frame_size] = r_grad_ig; + grad.gate_grad[frame_idx + frame_size * 2] = r_grad_fg; + grad.gate_grad[frame_idx + frame_size * 3] = r_grad_og; + grad.state_grad[frame_idx] = r_state_grad; + if (grad.prev_state_grad) { + if (is_batch) grad.prev_state_grad += batch_idx * frame_size; + grad.prev_state_grad[frame_idx] = r_prev_state_grad; } - if (isBatch) { - if (value.prevStateValue) { - if (grad.checkIgGrad) - paddle::platform::CudaAtomicAdd(grad.checkIgGrad + frameIdx, - rCheckIGrad); - if (grad.checkFgGrad) - paddle::platform::CudaAtomicAdd(grad.checkFgGrad + frameIdx, - rCheckFGrad); + if (is_batch) { + if (value.prev_state_value) { + if (grad.check_ig_grad) + paddle::platform::CudaAtomicAdd(grad.check_ig_grad + frame_idx, + r_checkIGrad); + if (grad.check_fg_grad) + paddle::platform::CudaAtomicAdd(grad.check_fg_grad + frame_idx, + r_checkFGrad); } - if (grad.checkOgGrad) - paddle::platform::CudaAtomicAdd(grad.checkOgGrad + frameIdx, rCheckOGrad); + if (grad.check_og_grad) + paddle::platform::CudaAtomicAdd(grad.check_og_grad + frame_idx, + r_checkOGrad); } else { - if (value.prevStateValue) { - if (grad.checkIgGrad) grad.checkIgGrad[frameIdx] += rCheckIGrad; - if (grad.checkFgGrad) grad.checkFgGrad[frameIdx] += rCheckFGrad; + if (value.prev_state_value) { + if (grad.check_ig_grad) grad.check_ig_grad[frame_idx] += r_checkIGrad; + if (grad.check_fg_grad) grad.check_fg_grad[frame_idx] += r_checkFGrad; } - if (grad.checkOgGrad) grad.checkOgGrad[frameIdx] += rCheckOGrad; + if (grad.check_og_grad) grad.check_og_grad[frame_idx] += r_checkOGrad; } } template void gpu_lstm_forward(const platform::DeviceContext& context, Op op, - LstmMetaValue value, int frameSize, int batchSize, + LstmMetaValue value, int frame_size, int batch_size, activation_mode_t active_node, activation_mode_t active_gate, activation_mode_t active_state) { dim3 threads; dim3 grid; - if (batchSize == 1) { - int framePerBlock = frameSize <= 1024 ? frameSize : 1024; - int frameBlocks = (frameSize + 1024 - 1) / 1024; - threads = dim3(framePerBlock, 1); - grid = dim3(frameBlocks, 1); + if (batch_size == 1) { + int frame_per_block = frame_size <= 1024 ? frame_size : 1024; + int frame_blocks = (frame_size + 1024 - 1) / 1024; + threads = dim3(frame_per_block, 1); + grid = dim3(frame_blocks, 1); } else { - /* framePerBlock = 32 batchPerBlock = 32 */ + /* frame_per_block = 32 batch_per_block = 32 */ threads = dim3(32, 32); - grid = dim3((frameSize + 32 - 1) / 32, (batchSize + 32 - 1) / 32); + grid = dim3((frame_size + 32 - 1) / 32, (batch_size + 32 - 1) / 32); } auto stream = reinterpret_cast(context).stream(); - if (batchSize == 1) { + if (batch_size == 1) { KeLstmForward<<>>( - op, value, frameSize, batchSize, active_node, active_gate, + /* is_batch= */ false><<>>( + op, value, frame_size, batch_size, active_node, active_gate, active_state); } else { KeLstmForward<<>>( - op, value, frameSize, batchSize, active_node, active_gate, + /* is_batch= */ true><<>>( + op, value, frame_size, batch_size, active_node, active_gate, active_state); } } @@ -216,34 +219,34 @@ void gpu_lstm_forward(const platform::DeviceContext& context, Op op, template void gpu_lstm_backward(const platform::DeviceContext& context, Op op, LstmMetaValue value, LstmMetaGrad grad, - int frameSize, int batchSize, + int frame_size, int batch_size, activation_mode_t active_node, activation_mode_t active_gate, activation_mode_t active_state) { dim3 threads; dim3 grid; - if (batchSize == 1) { - int framePerBlock = frameSize <= 1024 ? frameSize : 1024; - int frameBlocks = (frameSize + 1024 - 1) / 1024; - threads = dim3(framePerBlock, 1); - grid = dim3(frameBlocks, 1); + if (batch_size == 1) { + int frame_per_block = frame_size <= 1024 ? frame_size : 1024; + int frame_blocks = (frame_size + 1024 - 1) / 1024; + threads = dim3(frame_per_block, 1); + grid = dim3(frame_blocks, 1); } else { - /* framePerBlock = 32 batchPerBlock = 16 */ + /* frame_per_block = 32 batch_per_block = 16 */ threads = dim3(32, 16); - grid = dim3((frameSize + 32 - 1) / 32, (batchSize + 16 - 1) / 16); + grid = dim3((frame_size + 32 - 1) / 32, (batch_size + 16 - 1) / 16); } auto stream = reinterpret_cast(context).stream(); - if (batchSize == 1) { + if (batch_size == 1) { KeLstmBackward<<>>( - op, value, grad, frameSize, batchSize, active_node, active_gate, + /* is_batch= */ false><<>>( + op, value, grad, frame_size, batch_size, active_node, active_gate, active_state); } else { KeLstmBackward<<>>( - op, value, grad, frameSize, batchSize, active_node, active_gate, + /* is_batch= */ true><<>>( + op, value, grad, frame_size, batch_size, active_node, active_gate, active_state); } } diff --git a/paddle/operators/math/detail/lstm_kernel.h b/paddle/operators/math/detail/lstm_kernel.h index 9daaf91981..78f9a249a3 100644 --- a/paddle/operators/math/detail/lstm_kernel.h +++ b/paddle/operators/math/detail/lstm_kernel.h @@ -27,19 +27,19 @@ namespace forward { template class lstm { public: - HOSTDEVICE void operator()(T &valueIn, T &valueIg, T &valueFg, T &valueOg, - T &prevState, T &state, T &stateAtv, T &output, + HOSTDEVICE void operator()(T &value_in, T &value_ig, T &value_fg, T &value_og, + T &prev_state, T &state, T &state_atv, T &output, T &checkI, T &checkF, T &checkO, activation_mode_t active_node, activation_mode_t active_gate, activation_mode_t active_state) { - valueIn = activation(valueIn, active_node); - valueIg = activation(valueIg + prevState * checkI, active_gate); - valueFg = activation(valueFg + prevState * checkF, active_gate); - state = valueIn * valueIg + prevState * valueFg; - valueOg = activation(valueOg + state * checkO, active_gate); - stateAtv = activation(state, active_state); - output = valueOg * stateAtv; + value_in = activation(value_in, active_node); + value_ig = activation(value_ig + prev_state * checkI, active_gate); + value_fg = activation(value_fg + prev_state * checkF, active_gate); + state = value_in * value_ig + prev_state * value_fg; + value_og = activation(value_og + state * checkO, active_gate); + state_atv = activation(state, active_state); + output = value_og * state_atv; } #ifndef __NVCC__ #ifndef __AVX__ // If not compiled with AVX instructs. Disable AVX by default @@ -48,24 +48,27 @@ class lstm { // Only float support AVX optimization static const bool avx = std::is_same::value; - HOSTDEVICE void operator()(__m256 &valueIn, __m256 &valueIg, __m256 &valueFg, - __m256 &valueOg, __m256 &prevState, __m256 &state, - __m256 &stateAtv, __m256 &output, __m256 &checkI, + HOSTDEVICE void operator()(__m256 &value_in, __m256 &value_ig, + __m256 &value_fg, __m256 &value_og, + __m256 &prev_state, __m256 &state, + __m256 &state_atv, __m256 &output, __m256 &checkI, __m256 &checkF, __m256 &checkO, activation_mode_t active_node, activation_mode_t active_gate, activation_mode_t active_state) { - valueIn = activation(valueIn, active_node); - valueIg = activation( - _mm256_add_ps(valueIg, _mm256_mul_ps(prevState, checkI)), active_gate); - valueFg = activation( - _mm256_add_ps(valueFg, _mm256_mul_ps(prevState, checkF)), active_gate); - state = _mm256_add_ps(_mm256_mul_ps(valueIn, valueIg), - _mm256_mul_ps(prevState, valueFg)); - valueOg = activation(_mm256_add_ps(valueOg, _mm256_mul_ps(state, checkO)), - active_gate); - stateAtv = activation(state, active_state); - output = _mm256_mul_ps(valueOg, stateAtv); + value_in = activation(value_in, active_node); + value_ig = + activation(_mm256_add_ps(value_ig, _mm256_mul_ps(prev_state, checkI)), + active_gate); + value_fg = + activation(_mm256_add_ps(value_fg, _mm256_mul_ps(prev_state, checkF)), + active_gate); + state = _mm256_add_ps(_mm256_mul_ps(value_in, value_ig), + _mm256_mul_ps(prev_state, value_fg)); + value_og = activation(_mm256_add_ps(value_og, _mm256_mul_ps(state, checkO)), + active_gate); + state_atv = activation(state, active_state); + output = _mm256_mul_ps(value_og, state_atv); } #endif #endif @@ -78,25 +81,26 @@ namespace backward { template class lstm { public: - HOSTDEVICE void operator()(T &valueIn, T &valueIg, T &valueFg, T &valueOg, - T &gradIn, T &gradIg, T &gradFg, T &gradOg, - T &prevState, T &prevStateGrad, T &state, - T &stateGrad, T &stateAtv, T &outputGrad, + HOSTDEVICE void operator()(T &value_in, T &value_ig, T &value_fg, T &value_og, + T &grad_in, T &grad_ig, T &grad_fg, T &grad_og, + T &prev_state, T &prev_state_grad, T &state, + T &state_grad, T &state_atv, T &output_grad, T &checkI, T &checkF, T &checkO, T &checkIGrad, T &checkFGrad, T &checkOGrad, activation_mode_t active_node, activation_mode_t active_gate, activation_mode_t active_state) { - gradOg = activation(outputGrad * stateAtv, valueOg, active_gate); - stateGrad += activation(outputGrad * valueOg, stateAtv, active_state) + - gradOg * checkO; - gradIn = activation(stateGrad * valueIg, valueIn, active_node); - gradIg = activation(stateGrad * valueIn, valueIg, active_gate); - gradFg = activation(stateGrad * prevState, valueFg, active_gate); - prevStateGrad = gradIg * checkI + gradFg * checkF + stateGrad * valueFg; - checkIGrad = gradIg * prevState; - checkFGrad = gradFg * prevState; - checkOGrad = gradOg * state; + grad_og = activation(output_grad * state_atv, value_og, active_gate); + state_grad += activation(output_grad * value_og, state_atv, active_state) + + grad_og * checkO; + grad_in = activation(state_grad * value_ig, value_in, active_node); + grad_ig = activation(state_grad * value_in, value_ig, active_gate); + grad_fg = activation(state_grad * prev_state, value_fg, active_gate); + prev_state_grad = + grad_ig * checkI + grad_fg * checkF + state_grad * value_fg; + checkIGrad = grad_ig * prev_state; + checkFGrad = grad_fg * prev_state; + checkOGrad = grad_og * state; } #ifndef __NVCC__ #ifndef __AVX__ // If not compiled with AVX instructs. Disable AVX by default @@ -105,32 +109,32 @@ class lstm { // Only float support AVX optimization static const bool avx = std::is_same::value; HOSTDEVICE void operator()( - __m256 &valueIn, __m256 &valueIg, __m256 &valueFg, __m256 &valueOg, - __m256 &gradIn, __m256 &gradIg, __m256 &gradFg, __m256 &gradOg, - __m256 &prevState, __m256 &prevStateGrad, __m256 &state, - __m256 &stateGrad, __m256 &stateAtv, __m256 &outputGrad, __m256 &checkI, - __m256 &checkF, __m256 &checkO, __m256 &checkIGrad, __m256 &checkFGrad, - __m256 &checkOGrad, activation_mode_t active_node, + __m256 &value_in, __m256 &value_ig, __m256 &value_fg, __m256 &value_og, + __m256 &grad_in, __m256 &grad_ig, __m256 &grad_fg, __m256 &grad_og, + __m256 &prev_state, __m256 &prev_state_grad, __m256 &state, + __m256 &state_grad, __m256 &state_atv, __m256 &output_grad, + __m256 &checkI, __m256 &checkF, __m256 &checkO, __m256 &checkIGrad, + __m256 &checkFGrad, __m256 &checkOGrad, activation_mode_t active_node, activation_mode_t active_gate, activation_mode_t active_state) { - gradOg = - activation(_mm256_mul_ps(outputGrad, stateAtv), valueOg, active_gate); - stateGrad = _mm256_add_ps( - activation(_mm256_mul_ps(outputGrad, valueOg), stateAtv, active_state), - stateGrad); - stateGrad = _mm256_add_ps(_mm256_mul_ps(gradOg, checkO), stateGrad); - gradIn = - activation(_mm256_mul_ps(stateGrad, valueIg), valueIn, active_node); - gradIg = - activation(_mm256_mul_ps(stateGrad, valueIn), valueIg, active_gate); - gradFg = - activation(_mm256_mul_ps(stateGrad, prevState), valueFg, active_gate); - prevStateGrad = _mm256_add_ps(_mm256_mul_ps(gradIg, checkI), - _mm256_mul_ps(gradFg, checkF)); - prevStateGrad = - _mm256_add_ps(_mm256_mul_ps(stateGrad, valueFg), prevStateGrad); - checkIGrad = _mm256_mul_ps(gradIg, prevState); - checkFGrad = _mm256_mul_ps(gradFg, prevState); - checkOGrad = _mm256_mul_ps(gradOg, state); + grad_og = activation(_mm256_mul_ps(output_grad, state_atv), value_og, + active_gate); + state_grad = _mm256_add_ps(activation(_mm256_mul_ps(output_grad, value_og), + state_atv, active_state), + state_grad); + state_grad = _mm256_add_ps(_mm256_mul_ps(grad_og, checkO), state_grad); + grad_in = + activation(_mm256_mul_ps(state_grad, value_ig), value_in, active_node); + grad_ig = + activation(_mm256_mul_ps(state_grad, value_in), value_ig, active_gate); + grad_fg = activation(_mm256_mul_ps(state_grad, prev_state), value_fg, + active_gate); + prev_state_grad = _mm256_add_ps(_mm256_mul_ps(grad_ig, checkI), + _mm256_mul_ps(grad_fg, checkF)); + prev_state_grad = + _mm256_add_ps(_mm256_mul_ps(state_grad, value_fg), prev_state_grad); + checkIGrad = _mm256_mul_ps(grad_ig, prev_state); + checkFGrad = _mm256_mul_ps(grad_fg, prev_state); + checkOGrad = _mm256_mul_ps(grad_og, state); } #endif #endif diff --git a/paddle/operators/math/lstm_compute.cc b/paddle/operators/math/lstm_compute.cc index 0febf8e3b7..ad3a59bcdb 100644 --- a/paddle/operators/math/lstm_compute.cc +++ b/paddle/operators/math/lstm_compute.cc @@ -30,12 +30,12 @@ struct LstmUnitFunctor { detail::cpu_lstm_forward(detail::forward::lstm(), value, frame_size, ActiveType(cand_act), ActiveType(gate_act), ActiveType(cell_act)); - value.gateValue += frame_size * 4; - value.stateValue += frame_size; - value.stateActiveValue += frame_size; - value.outputValue += frame_size; - if (value.prevStateValue) { - value.prevStateValue += frame_size; + value.gate_value += frame_size * 4; + value.state_value += frame_size; + value.state_active_value += frame_size; + value.output_value += frame_size; + if (value.prev_state_value) { + value.prev_state_value += frame_size; } } } @@ -53,20 +53,20 @@ struct LstmUnitGradFunctor { frame_size, ActiveType(cand_act), ActiveType(gate_act), ActiveType(cell_act)); - value.gateValue += frame_size * 4; - value.stateValue += frame_size; - value.stateActiveValue += frame_size; - value.outputValue += frame_size; - if (value.prevStateValue) { - value.prevStateValue += frame_size; + value.gate_value += frame_size * 4; + value.state_value += frame_size; + value.state_active_value += frame_size; + value.output_value += frame_size; + if (value.prev_state_value) { + value.prev_state_value += frame_size; } - grad.gateGrad += frame_size * 4; - grad.stateGrad += frame_size; - grad.stateActiveGrad += frame_size; - grad.outputGrad += frame_size; - if (grad.prevStateGrad) { - grad.prevStateGrad += frame_size; + grad.gate_grad += frame_size * 4; + grad.state_grad += frame_size; + grad.state_active_grad += frame_size; + grad.output_grad += frame_size; + if (grad.prev_state_grad) { + grad.prev_state_grad += frame_size; } } } diff --git a/paddle/operators/math/lstm_compute.h b/paddle/operators/math/lstm_compute.h index 28d2c6fd3b..9652399d4c 100644 --- a/paddle/operators/math/lstm_compute.h +++ b/paddle/operators/math/lstm_compute.h @@ -31,26 +31,26 @@ typedef enum { template struct LstmMetaValue { - T *gateValue; - T *prevStateValue; - T *stateValue; - T *stateActiveValue; - T *outputValue; - T *checkIg; - T *checkFg; - T *checkOg; + T *gate_value; + T *prev_state_value; + T *state_value; + T *state_active_value; + T *output_value; + T *check_ig; + T *check_fg; + T *check_og; }; template struct LstmMetaGrad { - T *gateGrad; - T *prevStateGrad; - T *stateGrad; - T *stateActiveGrad; - T *outputGrad; - T *checkIgGrad; - T *checkFgGrad; - T *checkOgGrad; + T *gate_grad; + T *prev_state_grad; + T *state_grad; + T *state_active_grad; + T *output_grad; + T *check_ig_grad; + T *check_fg_grad; + T *check_og_grad; }; inline activation_mode_t ActiveType(const std::string &type) { From 3ca8a8ea4fc898bfd20fc5609c694f82df82fe61 Mon Sep 17 00:00:00 2001 From: Abhinav Arora Date: Sun, 3 Dec 2017 16:14:04 +0530 Subject: [PATCH 61/67] Changing RelWithDebInfo flags (#6193) --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ba29d6bbc..6aeef23330 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,8 @@ cmake_minimum_required(VERSION 3.0) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake") set(PADDLE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(PADDLE_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) +SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -g -DNDEBUG") +SET(CMAKE_C_FLAGS_RELWITHDEBINFO "-O3 -g -DNDEBUG") include(system) From f40bdb155edbe9a1352f614ff2add76d33ab0444 Mon Sep 17 00:00:00 2001 From: Abhinav Arora Date: Sun, 3 Dec 2017 18:24:45 +0530 Subject: [PATCH 62/67] Polish the Evaliuator design doc (#6195) --- doc/design/evaluator.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/design/evaluator.md b/doc/design/evaluator.md index a62d75ffef..11cc129d56 100644 --- a/doc/design/evaluator.md +++ b/doc/design/evaluator.md @@ -1,22 +1,22 @@ ## Evaluator Design -### The Problem +### Problem Statement -During training or serving, we provide the evaluation function to measure the model performance, e.g., accuracy, precision. In the operator based framework design, the data go through the network pipeline batch by batch. As a result, inside the operator, we only can calculate one minibatch metrics. We need to provide a mechanism to calculate the metrics for each N pass/batch the user wanted. +During training or inference, we provide an evaluation function to measure the model performance, for example, accuracy, precision, etc. In the operator based framework design, the data passes through the network pipeline batch by batch. As a result, inside the operator, we only calculate the metrics for one minibatch. Thus, we need to provide a mechanism to calculate the metrics for each N pass/batch the user wants. ### Evaluator Design -Currently, every operation is expressed in the graph. we divide the evaluator process into three steps. +Currently, every operation is expressed in the graph. We divide the evaluator process into three steps. 1. Initialize the metric state and add it into the block. -2. Calculate the statistic of the metric state in every mini-batch. The single operator is only responsible for calculating necessary statistics for one mini-batch. For example, accuracy operator only calculate a minibatch data if run once. +2. Calculate the concerned metrics for every mini-batch. The single evaluator operator is only responsible for calculating the necessary statistics for one mini-batch. For example, the accuracy operator only calculates the accuracy for a minibatch data if run once. 3. Merge the mini-batch statistics to form the evaluation result for multiple mini-batches. When it comes to distributed training/Multi-GPU training, aggregate the value from different devices. ### Implementation -This design is shown in python API. -Each metric operator need to caculate the metric statistic and return the batch aware states, Python side responsible for accumulate the states for each pass. +This design is shown in the Python API. +Each metric operator needs to caculate the metric statistic and return the batch-aware states. Python side is responsible for accumulating the states for each pass. ```python From 2a3a1e9a93258a67c8491361d9d83e3181723a3a Mon Sep 17 00:00:00 2001 From: Yu Yang Date: Mon, 4 Dec 2017 10:56:53 +0800 Subject: [PATCH 63/67] Add DataFeeder (#6102) * Add DataFeeder A v2 API like data feeder for book demos. We can feed data directly from reader. * Fix CI * Remove batch_size_dim for feeder Also add __all__ to data_feeder.py * Follow comment --- python/paddle/v2/fluid/__init__.py | 5 +- python/paddle/v2/fluid/data_feeder.py | 98 +++++++++++++++++++ .../v2/fluid/tests/book/test_fit_a_line.py | 7 +- .../book/test_image_classification_train.py | 13 +-- .../tests/book/test_label_semantic_roles.py | 60 +++++------- .../tests/book/test_recognize_digits_conv.py | 10 +- .../tests/book/test_recognize_digits_mlp.py | 28 +----- .../book/test_understand_sentiment_conv.py | 28 ++---- .../test_understand_sentiment_dynamic_lstm.py | 28 +++--- .../v2/fluid/tests/book/test_word2vec.py | 15 +-- .../paddle/v2/fluid/tests/test_data_feeder.py | 13 +++ 11 files changed, 177 insertions(+), 128 deletions(-) create mode 100644 python/paddle/v2/fluid/data_feeder.py create mode 100644 python/paddle/v2/fluid/tests/test_data_feeder.py diff --git a/python/paddle/v2/fluid/__init__.py b/python/paddle/v2/fluid/__init__.py index dd25bc19ec..59986c9f0c 100644 --- a/python/paddle/v2/fluid/__init__.py +++ b/python/paddle/v2/fluid/__init__.py @@ -14,20 +14,21 @@ import optimizer import backward import regularizer from param_attr import ParamAttr - +from data_feeder import DataFeeder from core import LoDTensor, CPUPlace, GPUPlace Tensor = LoDTensor __all__ = framework.__all__ + executor.__all__ + [ 'io', 'initializer', 'layers', 'nets', 'optimizer', 'backward', 'regularizer', 'LoDTensor', 'CPUPlace', 'GPUPlace', 'Tensor', 'ParamAttr' + 'DataFeeder' ] def __read_gflags_from_env__(): """ Enable reading gflags from environment variables. - + Returns: None """ diff --git a/python/paddle/v2/fluid/data_feeder.py b/python/paddle/v2/fluid/data_feeder.py new file mode 100644 index 0000000000..3dee0b5b73 --- /dev/null +++ b/python/paddle/v2/fluid/data_feeder.py @@ -0,0 +1,98 @@ +from __future__ import print_function + +import core +import numpy +import six.moves as six + +from framework import Variable + +__all__ = ['DataFeeder'] + + +class DataToLoDTensorConverter(object): + def __init__(self, place, lod_level, shape, dtype): + self.place = place + self.lod_level = lod_level + self.shape = shape + if dtype == core.DataType.FP32: + self.dtype = 'float32' + elif dtype == core.DataType.INT64: + self.dtype = 'int64' + elif dtype == core.DataType.FP64: + self.dtype = 'float64' + elif dtype == core.DataType.INT32: + self.dtype = 'int32' + else: + raise ValueError("dtype must be any of [int32, float32, int64, " + "float64]") + + self.data = [] + self.lod = [] + + for i in six.range(lod_level): + self.lod.append([0]) + + def feed(self, data): + self._feed_impl_(data, self.lod, self.lod_level) + + def _feed_impl_(self, data, lod, lod_level): + if lod_level == 0: + self.data.append(data) + else: + cur_lod_len = len(data) + lod[-1].append(lod[-1][-1] + cur_lod_len) + for each_data in data: + self._feed_impl_(each_data, lod[:-1], lod_level - 1) + + def done(self): + arr = numpy.array(self.data, dtype=self.dtype).reshape(self.shape) + t = core.LoDTensor() + t.set(arr, self.place) + if self.lod_level > 0: + t.set_lod(self.lod) + return t + + +class DataFeeder(object): + def __init__(self, feed_list, place): + self.feed_dtypes = [] + self.feed_names = [] + self.feed_shapes = [] + self.feed_lod_level = [] + for each_var in feed_list: + if not isinstance(each_var, Variable): + raise TypeError("Feed list should contain a list of variable") + self.feed_dtypes.append(each_var.dtype) + self.feed_names.append(each_var.name) + shape = each_var.shape + batch_size_dim = -1 + for i, s in enumerate(shape): + if s < 0: + batch_size_dim = i + break + if batch_size_dim == -1: + raise ValueError("Variable {0} must has a batch size dimension", + each_var.name) + self.feed_lod_level.append(each_var.lod_level) + self.feed_shapes.append(shape) + + self.place = place + + def feed(self, iterable): + converter = [] + for lod_level, shape, dtype in six.zip( + self.feed_lod_level, self.feed_shapes, self.feed_dtypes): + converter.append( + DataToLoDTensorConverter( + place=self.place, + lod_level=lod_level, + shape=shape, + dtype=dtype)) + + for each_sample in iterable: + for each_converter, each_slot in six.zip(converter, each_sample): + each_converter.feed(each_slot) + ret_dict = {} + for each_name, each_converter in six.zip(self.feed_names, converter): + ret_dict[each_name] = each_converter.done() + return ret_dict diff --git a/python/paddle/v2/fluid/tests/book/test_fit_a_line.py b/python/paddle/v2/fluid/tests/book/test_fit_a_line.py index 9f98493adb..fbf46ac6cb 100644 --- a/python/paddle/v2/fluid/tests/book/test_fit_a_line.py +++ b/python/paddle/v2/fluid/tests/book/test_fit_a_line.py @@ -22,6 +22,7 @@ train_reader = paddle.batch( batch_size=BATCH_SIZE) place = fluid.CPUPlace() +feeder = fluid.DataFeeder(place=place, feed_list=[x, y]) exe = fluid.Executor(place) exe.run(fluid.default_startup_program()) @@ -31,12 +32,8 @@ for pass_id in range(PASS_NUM): fluid.io.save_persistables(exe, "./fit_a_line.model/") fluid.io.load_persistables(exe, "./fit_a_line.model/") for data in train_reader(): - x_data = np.array(map(lambda _: _[0], data)).astype("float32") - y_data = np.array(map(lambda _: _[1], data)).astype("float32") - avg_loss_value, = exe.run(fluid.default_main_program(), - feed={'x': x_data, - 'y': y_data}, + feed=feeder.feed(data), fetch_list=[avg_cost]) if avg_loss_value[0] < 10.0: diff --git a/python/paddle/v2/fluid/tests/book/test_image_classification_train.py b/python/paddle/v2/fluid/tests/book/test_image_classification_train.py index 0f0cc5b540..4e71b6f345 100644 --- a/python/paddle/v2/fluid/tests/book/test_image_classification_train.py +++ b/python/paddle/v2/fluid/tests/book/test_image_classification_train.py @@ -113,23 +113,14 @@ train_reader = paddle.batch( place = fluid.CPUPlace() exe = fluid.Executor(place) - +feeder = fluid.DataFeeder(place=place, feed_list=[images, label]) exe.run(fluid.default_startup_program()) for pass_id in range(PASS_NUM): accuracy.reset(exe) for data in train_reader(): - img_data = np.array(map(lambda x: x[0].reshape(data_shape), - data)).astype("float32") - y_data = np.array(map(lambda x: x[1], data)).astype("int64") - batch_size = 1 - for i in y_data.shape: - batch_size = batch_size * i - y_data = y_data.reshape([batch_size, 1]) - loss, acc = exe.run(fluid.default_main_program(), - feed={"pixel": img_data, - "label": y_data}, + feed=feeder.feed(data), fetch_list=[avg_cost] + accuracy.metrics) pass_acc = accuracy.eval(exe) print("loss:" + str(loss) + " acc:" + str(acc) + " pass_acc:" + str( diff --git a/python/paddle/v2/fluid/tests/book/test_label_semantic_roles.py b/python/paddle/v2/fluid/tests/book/test_label_semantic_roles.py index bcd6f4d6bc..0494c7cdca 100644 --- a/python/paddle/v2/fluid/tests/book/test_label_semantic_roles.py +++ b/python/paddle/v2/fluid/tests/book/test_label_semantic_roles.py @@ -28,17 +28,9 @@ def load_parameter(file_name, h, w): return np.fromfile(f, dtype=np.float32).reshape(h, w) -def db_lstm(): +def db_lstm(word, predicate, ctx_n2, ctx_n1, ctx_0, ctx_p1, ctx_p2, mark, + **ignored): # 8 features - word = fluid.layers.data(name='word_data', shape=[1], dtype='int64') - predicate = fluid.layers.data(name='verb_data', shape=[1], dtype='int64') - ctx_n2 = fluid.layers.data(name='ctx_n2_data', shape=[1], dtype='int64') - ctx_n1 = fluid.layers.data(name='ctx_n1_data', shape=[1], dtype='int64') - ctx_0 = fluid.layers.data(name='ctx_0_data', shape=[1], dtype='int64') - ctx_p1 = fluid.layers.data(name='ctx_p1_data', shape=[1], dtype='int64') - ctx_p2 = fluid.layers.data(name='ctx_p2_data', shape=[1], dtype='int64') - mark = fluid.layers.data(name='mark_data', shape=[1], dtype='int64') - predicate_embedding = fluid.layers.embedding( input=predicate, size=[pred_len, word_dim], @@ -120,8 +112,25 @@ def to_lodtensor(data, place): def main(): # define network topology - feature_out = db_lstm() - target = fluid.layers.data(name='target', shape=[1], dtype='int64') + word = fluid.layers.data( + name='word_data', shape=[1], dtype='int64', lod_level=1) + predicate = fluid.layers.data( + name='verb_data', shape=[1], dtype='int64', lod_level=1) + ctx_n2 = fluid.layers.data( + name='ctx_n2_data', shape=[1], dtype='int64', lod_level=1) + ctx_n1 = fluid.layers.data( + name='ctx_n1_data', shape=[1], dtype='int64', lod_level=1) + ctx_0 = fluid.layers.data( + name='ctx_0_data', shape=[1], dtype='int64', lod_level=1) + ctx_p1 = fluid.layers.data( + name='ctx_p1_data', shape=[1], dtype='int64', lod_level=1) + ctx_p2 = fluid.layers.data( + name='ctx_p2_data', shape=[1], dtype='int64', lod_level=1) + mark = fluid.layers.data( + name='mark_data', shape=[1], dtype='int64', lod_level=1) + feature_out = db_lstm(**locals()) + target = fluid.layers.data( + name='target', shape=[1], dtype='int64', lod_level=1) crf_cost = fluid.layers.linear_chain_crf( input=feature_out, label=target, @@ -139,6 +148,11 @@ def main(): paddle.dataset.conll05.test(), buf_size=8192), batch_size=BATCH_SIZE) place = fluid.CPUPlace() + feeder = fluid.DataFeeder( + feed_list=[ + word, ctx_n2, ctx_n1, ctx_0, ctx_p1, ctx_p2, predicate, mark, target + ], + place=place) exe = fluid.Executor(place) exe.run(fluid.default_startup_program()) @@ -150,28 +164,8 @@ def main(): batch_id = 0 for pass_id in xrange(PASS_NUM): for data in train_data(): - word_data = to_lodtensor(map(lambda x: x[0], data), place) - ctx_n2_data = to_lodtensor(map(lambda x: x[1], data), place) - ctx_n1_data = to_lodtensor(map(lambda x: x[2], data), place) - ctx_0_data = to_lodtensor(map(lambda x: x[3], data), place) - ctx_p1_data = to_lodtensor(map(lambda x: x[4], data), place) - ctx_p2_data = to_lodtensor(map(lambda x: x[5], data), place) - verb_data = to_lodtensor(map(lambda x: x[6], data), place) - mark_data = to_lodtensor(map(lambda x: x[7], data), place) - target = to_lodtensor(map(lambda x: x[8], data), place) - outs = exe.run(fluid.default_main_program(), - feed={ - 'word_data': word_data, - 'ctx_n2_data': ctx_n2_data, - 'ctx_n1_data': ctx_n1_data, - 'ctx_0_data': ctx_0_data, - 'ctx_p1_data': ctx_p1_data, - 'ctx_p2_data': ctx_p2_data, - 'verb_data': verb_data, - 'mark_data': mark_data, - 'target': target - }, + feed=feeder.feed(data), fetch_list=[avg_cost]) avg_cost_val = np.array(outs[0]) diff --git a/python/paddle/v2/fluid/tests/book/test_recognize_digits_conv.py b/python/paddle/v2/fluid/tests/book/test_recognize_digits_conv.py index ba686b56f8..35bf8da924 100644 --- a/python/paddle/v2/fluid/tests/book/test_recognize_digits_conv.py +++ b/python/paddle/v2/fluid/tests/book/test_recognize_digits_conv.py @@ -37,20 +37,14 @@ train_reader = paddle.batch( place = fluid.CPUPlace() exe = fluid.Executor(place) - +feeder = fluid.DataFeeder(feed_list=[images, label], place=place) exe.run(fluid.default_startup_program()) for pass_id in range(PASS_NUM): accuracy.reset(exe) for data in train_reader(): - img_data = np.array(map(lambda x: x[0].reshape([1, 28, 28]), - data)).astype("float32") - y_data = np.array(map(lambda x: x[1], data)).astype("int64") - y_data = y_data.reshape([BATCH_SIZE, 1]) - loss, acc = exe.run(fluid.default_main_program(), - feed={"pixel": img_data, - "label": y_data}, + feed=feeder.feed(data), fetch_list=[avg_cost] + accuracy.metrics) pass_acc = accuracy.eval(exe) print("pass_id=" + str(pass_id) + " acc=" + str(acc) + " pass_acc=" + diff --git a/python/paddle/v2/fluid/tests/book/test_recognize_digits_mlp.py b/python/paddle/v2/fluid/tests/book/test_recognize_digits_mlp.py index fa18965aac..4dc2c50e1c 100644 --- a/python/paddle/v2/fluid/tests/book/test_recognize_digits_mlp.py +++ b/python/paddle/v2/fluid/tests/book/test_recognize_digits_mlp.py @@ -48,40 +48,22 @@ test_reader = paddle.batch(paddle.dataset.mnist.test(), batch_size=128) place = fluid.CPUPlace() exe = fluid.Executor(place) - +feeder = fluid.DataFeeder(feed_list=[image, label], place=place) exe.run(fluid.default_startup_program()) PASS_NUM = 100 for pass_id in range(PASS_NUM): accuracy.reset(exe) for data in train_reader(): - x_data = np.array(map(lambda x: x[0], data)).astype("float32") - y_data = np.array(map(lambda x: x[1], data)).astype("int64") - y_data = np.expand_dims(y_data, axis=1) - - tensor_x = fluid.LoDTensor() - tensor_x.set(x_data, place) - - tensor_y = fluid.LoDTensor() - tensor_y.set(y_data, place) - - outs = exe.run(fluid.default_main_program(), - feed={'x': tensor_x, - 'y': tensor_y}, - fetch_list=[avg_cost] + accuracy.metrics) - out = np.array(outs[0]) - acc = np.array(outs[1]) + out, acc = exe.run(fluid.default_main_program(), + feed=feeder.feed(data), + fetch_list=[avg_cost] + accuracy.metrics) pass_acc = accuracy.eval(exe) test_accuracy.reset(exe) for data in test_reader(): - x_data = np.array(map(lambda x: x[0], data)).astype("float32") - y_data = np.array(map(lambda x: x[1], data)).astype("int64") - y_data = np.expand_dims(y_data, axis=1) - out, acc = exe.run(inference_program, - feed={'x': x_data, - 'y': y_data}, + feed=feeder.feed(data), fetch_list=[avg_cost] + test_accuracy.metrics) test_pass_acc = test_accuracy.eval(exe) diff --git a/python/paddle/v2/fluid/tests/book/test_understand_sentiment_conv.py b/python/paddle/v2/fluid/tests/book/test_understand_sentiment_conv.py index be875a952b..f103358edc 100644 --- a/python/paddle/v2/fluid/tests/book/test_understand_sentiment_conv.py +++ b/python/paddle/v2/fluid/tests/book/test_understand_sentiment_conv.py @@ -4,10 +4,8 @@ import paddle.v2 as paddle import paddle.v2.fluid as fluid -def convolution_net(input_dim, class_dim=2, emb_dim=32, hid_dim=32): - data = fluid.layers.data(name="words", shape=[1], dtype="int64") - label = fluid.layers.data(name="label", shape=[1], dtype="int64") - +def convolution_net(data, label, input_dim, class_dim=2, emb_dim=32, + hid_dim=32): emb = fluid.layers.embedding(input=data, size=[input_dim, emb_dim]) conv_3 = fluid.nets.sequence_conv_pool( input=emb, @@ -55,8 +53,11 @@ def main(): dict_dim = len(word_dict) class_dim = 2 + data = fluid.layers.data( + name="words", shape=[1], dtype="int64", lod_level=1) + label = fluid.layers.data(name="label", shape=[1], dtype="int64") cost, accuracy, acc_out = convolution_net( - input_dim=dict_dim, class_dim=class_dim) + data, label, input_dim=dict_dim, class_dim=class_dim) train_data = paddle.batch( paddle.reader.shuffle( @@ -64,25 +65,16 @@ def main(): batch_size=BATCH_SIZE) place = fluid.CPUPlace() exe = fluid.Executor(place) + feeder = fluid.DataFeeder(feed_list=[data, label], place=place) exe.run(fluid.default_startup_program()) for pass_id in xrange(PASS_NUM): accuracy.reset(exe) for data in train_data(): - tensor_words = to_lodtensor(map(lambda x: x[0], data), place) - - label = np.array(map(lambda x: x[1], data)).astype("int64") - label = label.reshape([BATCH_SIZE, 1]) - - tensor_label = fluid.LoDTensor() - tensor_label.set(label, place) - - cost_val, acc_val = exe.run( - fluid.default_main_program(), - feed={"words": tensor_words, - "label": tensor_label}, - fetch_list=[cost, acc_out]) + cost_val, acc_val = exe.run(fluid.default_main_program(), + feed=feeder.feed(data), + fetch_list=[cost, acc_out]) pass_acc = accuracy.eval(exe) print("cost=" + str(cost_val) + " acc=" + str(acc_val) + " pass_acc=" + str(pass_acc)) diff --git a/python/paddle/v2/fluid/tests/book/test_understand_sentiment_dynamic_lstm.py b/python/paddle/v2/fluid/tests/book/test_understand_sentiment_dynamic_lstm.py index 094a3cdcda..cd28f04b85 100644 --- a/python/paddle/v2/fluid/tests/book/test_understand_sentiment_dynamic_lstm.py +++ b/python/paddle/v2/fluid/tests/book/test_understand_sentiment_dynamic_lstm.py @@ -3,14 +3,14 @@ import paddle.v2 as paddle import paddle.v2.fluid as fluid -def stacked_lstm_net(input_dim, +def stacked_lstm_net(data, + label, + input_dim, class_dim=2, emb_dim=128, hid_dim=512, stacked_num=3): assert stacked_num % 2 == 1 - data = fluid.layers.data(name="words", shape=[1], dtype="int64") - label = fluid.layers.data(name="label", shape=[1], dtype="int64") emb = fluid.layers.embedding(input=data, size=[input_dim, emb_dim]) # add bias attr @@ -65,8 +65,11 @@ def main(): dict_dim = len(word_dict) class_dim = 2 + data = fluid.layers.data( + name="words", shape=[1], dtype="int64", lod_level=1) + label = fluid.layers.data(name="label", shape=[1], dtype="int64") cost, accuracy, acc_out = stacked_lstm_net( - input_dim=dict_dim, class_dim=class_dim) + data, label, input_dim=dict_dim, class_dim=class_dim) train_data = paddle.batch( paddle.reader.shuffle( @@ -74,25 +77,16 @@ def main(): batch_size=BATCH_SIZE) place = fluid.CPUPlace() exe = fluid.Executor(place) + feeder = fluid.DataFeeder(feed_list=[data, label], place=place) exe.run(fluid.default_startup_program()) for pass_id in xrange(PASS_NUM): accuracy.reset(exe) for data in train_data(): - tensor_words = to_lodtensor(map(lambda x: x[0], data), place) - - label = np.array(map(lambda x: x[1], data)).astype("int64") - label = label.reshape([BATCH_SIZE, 1]) - - tensor_label = fluid.LoDTensor() - tensor_label.set(label, place) - - cost_val, acc_val = exe.run( - fluid.default_main_program(), - feed={"words": tensor_words, - "label": tensor_label}, - fetch_list=[cost, acc_out]) + cost_val, acc_val = exe.run(fluid.default_main_program(), + feed=feeder.feed(data), + fetch_list=[cost, acc_out]) pass_acc = accuracy.eval(exe) print("cost=" + str(cost_val) + " acc=" + str(acc_val) + " pass_acc=" + str(pass_acc)) diff --git a/python/paddle/v2/fluid/tests/book/test_word2vec.py b/python/paddle/v2/fluid/tests/book/test_word2vec.py index 1b441e15c7..8b928ff9ee 100644 --- a/python/paddle/v2/fluid/tests/book/test_word2vec.py +++ b/python/paddle/v2/fluid/tests/book/test_word2vec.py @@ -57,23 +57,16 @@ train_reader = paddle.batch( place = fluid.CPUPlace() exe = fluid.Executor(place) +feeder = fluid.DataFeeder( + feed_list=[first_word, second_word, third_word, forth_word, next_word], + place=place) exe.run(fluid.default_startup_program()) for pass_id in range(PASS_NUM): for data in train_reader(): - input_data = [[data_idx[idx] for data_idx in data] for idx in xrange(5)] - input_data = map(lambda x: np.array(x).astype("int64"), input_data) - input_data = map(lambda x: np.expand_dims(x, axis=1), input_data) - avg_cost_np = exe.run(fluid.default_main_program(), - feed={ - 'firstw': input_data[0], - 'secondw': input_data[1], - 'thirdw': input_data[2], - 'forthw': input_data[3], - 'nextw': input_data[4] - }, + feed=feeder.feed(data), fetch_list=[avg_cost]) if avg_cost_np[0] < 5.0: exit(0) # if avg cost less than 10.0, we think our code is good. diff --git a/python/paddle/v2/fluid/tests/test_data_feeder.py b/python/paddle/v2/fluid/tests/test_data_feeder.py new file mode 100644 index 0000000000..4549693203 --- /dev/null +++ b/python/paddle/v2/fluid/tests/test_data_feeder.py @@ -0,0 +1,13 @@ +import paddle.v2.fluid as fluid + + +def test_converter(): + img = fluid.layers.data(name='image', shape=[1, 28, 28]) + label = fluid.layers.data(name='label', shape=[1], dtype='int64') + feeder = fluid.DataFeeder([img, label], fluid.CPUPlace()) + result = feeder.feed([[[0] * 784, [9]], [[1] * 784, [1]]]) + print(result) + + +if __name__ == '__main__': + test_converter() From 4786ad1457ba923476b04ea62a2396d3936bae24 Mon Sep 17 00:00:00 2001 From: qingqing01 Date: Mon, 4 Dec 2017 13:40:26 +0800 Subject: [PATCH 64/67] Make the new framework independent the old framework. (#6201) --- paddle/operators/softmax_with_cross_entropy_op.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/paddle/operators/softmax_with_cross_entropy_op.cc b/paddle/operators/softmax_with_cross_entropy_op.cc index fc027d6f95..0c30228863 100644 --- a/paddle/operators/softmax_with_cross_entropy_op.cc +++ b/paddle/operators/softmax_with_cross_entropy_op.cc @@ -13,7 +13,6 @@ See the License for the specific language governing permissions and limitations under the License. */ #include "paddle/operators/softmax_with_cross_entropy_op.h" -#include namespace paddle { namespace operators { From 7b827d95adb0c8ae5dff1bdad7fd51ff50065dfe Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Mon, 4 Dec 2017 15:37:03 +0800 Subject: [PATCH 65/67] use awk command to replace bc --- benchmark/paddle/image/run_mkldnn_infer.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/benchmark/paddle/image/run_mkldnn_infer.sh b/benchmark/paddle/image/run_mkldnn_infer.sh index 03a76c0540..d795bcab1b 100755 --- a/benchmark/paddle/image/run_mkldnn_infer.sh +++ b/benchmark/paddle/image/run_mkldnn_infer.sh @@ -4,7 +4,7 @@ function clock_to_seconds() { hours=`echo $1 | awk -F ':' '{print $1}'` mins=`echo $1 | awk -F ':' '{print $2}'` secs=`echo $1 | awk -F ':' '{print $3}'` - echo `bc -l <<< "$secs + $mins * 60 + $hours * 3600"` + echo `awk 'BEGIN{printf "%.2f",('$secs' + '$mins' * 60 + '$hours' * 3600)}'` } function infer() { @@ -58,9 +58,9 @@ function infer() { end=`tail ${log} -n 2 | head -n 1 | awk -F ' ' '{print $2}' | xargs` start_sec=`clock_to_seconds $start` end_sec=`clock_to_seconds $end` - fps=`bc <<< "scale = 2; 1280 / ($end_sec - $start_sec)"` + fps=`awk 'BEGIN{printf "%.2f",(1280 / ('$end_sec' - '$start_sec'))}'` echo "Last 1280 samples start: ${start}(${start_sec} sec), end: ${end}(${end_sec} sec;" >> ${log} - echo "FPS: $fps images/sec" >> ${log} + echo "FPS: $fps images/sec" 2>&1 | tee -a ${log} } if [ ! -f "train.list" ]; then From 1fe05c458fa1d7ff1949759c2a06ed6d19ab8048 Mon Sep 17 00:00:00 2001 From: QI JUN Date: Mon, 4 Dec 2017 16:04:07 +0800 Subject: [PATCH 66/67] Generate docs for Fluid API (#6215) * add doc config for fluid * fix typo * follow comments --- doc/api/index_en.rst | 1 + doc/api/v2/fluid.rst | 18 ++ doc/api/v2/fluid/data_feeder.rst | 9 + doc/api/v2/fluid/evaluator.rst | 9 + doc/api/v2/fluid/executor.rst | 9 + doc/api/v2/fluid/initializer.rst | 50 +++++ doc/api/v2/fluid/layers.rst | 302 +++++++++++++++++++++++++++++++ doc/api/v2/fluid/nets.rst | 22 +++ doc/api/v2/fluid/optimizer.rst | 54 ++++++ doc/api/v2/fluid/param_attr.rst | 11 ++ doc/api/v2/fluid/profiler.rst | 10 + doc/api/v2/fluid/regularizer.rst | 25 +++ python/paddle/v2/fluid/layers.py | 6 +- 13 files changed, 522 insertions(+), 4 deletions(-) create mode 100644 doc/api/v2/fluid.rst create mode 100644 doc/api/v2/fluid/data_feeder.rst create mode 100644 doc/api/v2/fluid/evaluator.rst create mode 100644 doc/api/v2/fluid/executor.rst create mode 100644 doc/api/v2/fluid/initializer.rst create mode 100644 doc/api/v2/fluid/layers.rst create mode 100644 doc/api/v2/fluid/nets.rst create mode 100644 doc/api/v2/fluid/optimizer.rst create mode 100644 doc/api/v2/fluid/param_attr.rst create mode 100644 doc/api/v2/fluid/profiler.rst create mode 100644 doc/api/v2/fluid/regularizer.rst diff --git a/doc/api/index_en.rst b/doc/api/index_en.rst index 25c1dd00b9..e6f632e1a5 100644 --- a/doc/api/index_en.rst +++ b/doc/api/index_en.rst @@ -7,3 +7,4 @@ API v2/model_configs.rst v2/data.rst v2/run_logic.rst + v2/fluid.rst diff --git a/doc/api/v2/fluid.rst b/doc/api/v2/fluid.rst new file mode 100644 index 0000000000..43fc19dc49 --- /dev/null +++ b/doc/api/v2/fluid.rst @@ -0,0 +1,18 @@ +====================== +Fluid +====================== + +.. toctree:: + :maxdepth: 1 + + fluid/layers.rst + fluid/data_feeder.rst + fluid/executor.rst + fluid/initializer.rst + fluid/evaluator.rst + fluid/nets.rst + fluid/optimizer.rst + fluid/param_attr.rst + fluid/profiler.rst + fluid/regularizer.rst + diff --git a/doc/api/v2/fluid/data_feeder.rst b/doc/api/v2/fluid/data_feeder.rst new file mode 100644 index 0000000000..0fa78f7dfb --- /dev/null +++ b/doc/api/v2/fluid/data_feeder.rst @@ -0,0 +1,9 @@ +=========== +DataFeeder +=========== + +DataFeeder +----------- +.. automodule:: paddle.v2.fluid.data_feeder + :members: DataFeeder + :noindex: diff --git a/doc/api/v2/fluid/evaluator.rst b/doc/api/v2/fluid/evaluator.rst new file mode 100644 index 0000000000..a23f3301d0 --- /dev/null +++ b/doc/api/v2/fluid/evaluator.rst @@ -0,0 +1,9 @@ +=========== +Evaluator +=========== + +Evaluator +----------- +.. automodule:: paddle.v2.fluid.evaluator + :members: Evaluator + :noindex: diff --git a/doc/api/v2/fluid/executor.rst b/doc/api/v2/fluid/executor.rst new file mode 100644 index 0000000000..3a283538c1 --- /dev/null +++ b/doc/api/v2/fluid/executor.rst @@ -0,0 +1,9 @@ +=========== +Executor +=========== + +Executor +----------- +.. automodule:: paddle.v2.fluid.executor + :members: Executor + :noindex: diff --git a/doc/api/v2/fluid/initializer.rst b/doc/api/v2/fluid/initializer.rst new file mode 100644 index 0000000000..8f587837e9 --- /dev/null +++ b/doc/api/v2/fluid/initializer.rst @@ -0,0 +1,50 @@ +=========== +Initializer +=========== + + + +Initializer +----------- +.. automodule:: paddle.v2.fluid.initializer + :members: Initializer + :noindex: + + + +ConstantInitializer +------------------- +.. automodule:: paddle.v2.fluid.initializer + :members: ConstantInitializer + :noindex: + + + +UniformInitializer +------------------ +.. automodule:: paddle.v2.fluid.initializer + :members: UniformInitializer + :noindex: + + + +NormalInitializer +----------------- +.. automodule:: paddle.v2.fluid.initializer + :members: NormalInitializer + :noindex: + + +XavierInitializer +----------------- +.. automodule:: paddle.v2.fluid.initializer + :members: XavierInitializer + :noindex: + + +MSRAInitializer +--------------- +.. automodule:: paddle.v2.fluid.initializer + :members: MSRAInitializer + :noindex: + diff --git a/doc/api/v2/fluid/layers.rst b/doc/api/v2/fluid/layers.rst new file mode 100644 index 0000000000..89e5fec13b --- /dev/null +++ b/doc/api/v2/fluid/layers.rst @@ -0,0 +1,302 @@ +========== +Layers +========== + + +fc +--- +.. autofunction:: paddle.v2.fluid.layers.fc + :noindex: + +embedding +--------- +.. autofunction:: paddle.v2.fluid.layers.embedding + :noindex: + +dynamic_lstm +------------ +.. autofunction:: paddle.v2.fluid.layers.dynamic_lstm + :noindex: + +data +--------- +.. autofunction:: paddle.v2.fluid.layers.data + :noindex: + +mean +--------- +.. autofunction:: paddle.v2.fluid.layers.mean + :noindex: + +mul +--------- +.. autofunction:: paddle.v2.fluid.layers.mul + :noindex: + +elementwise_add +--------------- +.. autofunction:: paddle.v2.fluid.layers.elementwise_add + :noindex: + +elementwise_div +--------------- +.. autofunction:: paddle.v2.fluid.layers.elementwise_div + :noindex: + + +dropout +--------- +.. autofunction:: paddle.v2.fluid.layers.dropout + :noindex: + + +reshape +--------- +.. autofunction:: paddle.v2.fluid.layers.reshape + :noindex: + + +sigmoid +--------- +.. autofunction:: paddle.v2.fluid.layers.sigmoid + :noindex: + + +scale +--------- +.. autofunction:: paddle.v2.fluid.layers.scale + :noindex: + + +reshape +--------- +.. autofunction:: paddle.v2.fluid.layers.reshape + :noindex: + + +transpose +--------- +.. autofunction:: paddle.v2.fluid.layers.transpose + :noindex: + + +sigmoid_cross_entropy_with_logits +--------- +.. autofunction:: paddle.v2.fluid.layers.esigmoid_cross_entropy_with_logits + :noindex: + + +cast +--------- +.. autofunction:: paddle.v2.fluid.layers.cast + :noindex: + + +concat +--------- +.. autofunction:: paddle.v2.fluid.layers.concat + :noindex: + + +sums +--------- +.. autofunction:: paddle.v2.fluid.layers.sums + :noindex: + + +linear_chain_crf +--------- +.. autofunction:: paddle.v2.fluid.layers.linear_chain_crf + :noindex: + + +assign +--------- +.. autofunction:: paddle.v2.fluid.layers.embedding + :noindex: + + +split_lod_tensor +--------- +.. autofunction:: paddle.v2.fluid.layers.split_lod_tensor + :noindex: + + +merge_lod_tensor +--------- +.. autofunction:: paddle.v2.fluid.layers.merge_lod_tensor + :noindex: + +cos_sim +--------- +.. autofunction:: paddle.v2.fluid.layers.cos_sim + :noindex: + + +cross_entropy +--------- +.. autofunction:: paddle.v2.fluid.layers.cross_entropy + :noindex: + + + +square_error_cost +--------- +.. autofunction:: paddle.v2.fluid.layers.square_error_cost + :noindex: + + +accuracy +--------- +.. autofunction:: paddle.v2.fluid.layers.accuracy + :noindex: + + +sequence_conv +--------- +.. autofunction:: paddle.v2.fluid.layers.sequence_conv + :noindex: + + +conv2d +--------- +.. autofunction:: paddle.v2.fluid.layers.conv2d + :noindex: + + +sequence_pool +--------- +.. autofunction:: paddle.v2.fluid.layers.sequence_pool + :noindex: + + +pool2d +--------- +.. autofunction:: paddle.v2.fluid.layers.pool2d + :noindex: + + +batch_norm +--------- +.. autofunction:: paddle.v2.fluid.layers.batch_norm + :noindex: + + +beam_search_decode +--------- +.. autofunction:: paddle.v2.fluid.layers.beam_search_decode + :noindex: + + +lstm +--------- +.. autofunction:: paddle.v2.fluid.layers.lstm + :noindex: + + +lod_rank_table +--------- +.. autofunction:: paddle.v2.fluid.layers.lod_rank_table + :noindex: + + +max_sequence_len +--------- +.. autofunction:: paddle.v2.fluid.layers.max_sequence_len + :noindex: + + +topk +--------- +.. autofunction:: paddle.v2.fluid.layers.topk + :noindex: + + +lod_tensor_to_array +--------- +.. autofunction:: paddle.v2.fluid.layers.lod_tensor_to_array + :noindex: + + + +array_to_lod_tensor +--------- +.. autofunction:: paddle.v2.fluid.layers.array_to_lod_tensor + :noindex: + + + + +fill_constant +--------- +.. autofunction:: paddle.v2.fluid.layers.fill_constant + :noindex: + + + +fill_constant_batch_size_like +--------- +.. autofunction:: paddle.v2.fluid.layers.fill_constant_batch_size_like + :noindex: + + +ones +--------- +.. autofunction:: paddle.v2.fluid.layers.ones + :noindex: + + +zeros +--------- +.. autofunction:: paddle.v2.fluid.layers.zeros + :noindex: + + +increment +--------- +.. autofunction:: paddle.v2.fluid.layers.increment + :noindex: + + +array_write +--------- +.. autofunction:: paddle.v2.fluid.layers.array_write + :noindex: + + + +create_array +--------- +.. autofunction:: paddle.v2.fluid.layers.create_array + :noindex: + + +less_than +--------- +.. autofunction:: paddle.v2.fluid.layers.less_than + :noindex: + + +array_read +--------- +.. autofunction:: paddle.v2.fluid.layers.array_read + :noindex: + + +shrink_memory +--------- +.. autofunction:: paddle.v2.fluid.layers.shrink_memory + :noindex: + + +array_length +--------- +.. autofunction:: paddle.v2.fluid.layers.array_length + :noindex: + + +conv2d_transpose +--------- +.. autofunction:: paddle.v2.fluid.layers.conv2d_transpose + :noindex: + diff --git a/doc/api/v2/fluid/nets.rst b/doc/api/v2/fluid/nets.rst new file mode 100644 index 0000000000..2c3d075422 --- /dev/null +++ b/doc/api/v2/fluid/nets.rst @@ -0,0 +1,22 @@ +=========== +Nets +=========== + +simple_img_conv_pool +----------- +.. autofunction:: paddle.v2.fluid.nets.simple_img_conv_pool + :noindex: + + +img_conv_group +----------- +.. autofunction:: paddle.v2.fluid.nets.img_conv_group + :noindex: + + +sequence_conv_pool +----------- +.. autofunction:: paddle.v2.fluid.nets.sequence_conv_pool + :noindex: + + diff --git a/doc/api/v2/fluid/optimizer.rst b/doc/api/v2/fluid/optimizer.rst new file mode 100644 index 0000000000..233762fcdf --- /dev/null +++ b/doc/api/v2/fluid/optimizer.rst @@ -0,0 +1,54 @@ +=========== +Optimizer +=========== + +Optimizer +----------- +.. automodule:: paddle.v2.fluid.optimizer + :members: Optimizer + :noindex: + + +SGDOptimizer +----------- +.. automodule:: paddle.v2.fluid.optimizer + :members: SGDOptimizer + :noindex: + + + +MomentumOptimizer +----------- +.. automodule:: paddle.v2.fluid.optimizer + :members: MomentumOptimizer + :noindex: + + + +AdagradOptimizer +----------- +.. automodule:: paddle.v2.fluid.optimizer + :members: AdagradOptimizer + :noindex: + + +AdamOptimizer +----------- +.. automodule:: paddle.v2.fluid.optimizer + :members: AdamOptimizer + :noindex: + + +AdamaxOptimizer +----------- +.. automodule:: paddle.v2.fluid.optimizer + :members: AdamaxOptimizer + :noindex: + + +DecayedAdagradOptimizer +----------- +.. automodule:: paddle.v2.fluid.optimizer + :members: DecayedAdagradOptimizer + :noindex: + diff --git a/doc/api/v2/fluid/param_attr.rst b/doc/api/v2/fluid/param_attr.rst new file mode 100644 index 0000000000..ca0c8af9e8 --- /dev/null +++ b/doc/api/v2/fluid/param_attr.rst @@ -0,0 +1,11 @@ +=========== +ParamAttr +=========== + + + +ParamAttr +----------- +.. automodule:: paddle.v2.fluid.param_attr + :members: ParamAttr + :noindex: diff --git a/doc/api/v2/fluid/profiler.rst b/doc/api/v2/fluid/profiler.rst new file mode 100644 index 0000000000..7d4042d1f4 --- /dev/null +++ b/doc/api/v2/fluid/profiler.rst @@ -0,0 +1,10 @@ +=========== +Profiler +=========== + + + +Profiler +----------- +.. autofunction:: paddle.v2.fluid.profiler.cuda_profiler + :noindex: diff --git a/doc/api/v2/fluid/regularizer.rst b/doc/api/v2/fluid/regularizer.rst new file mode 100644 index 0000000000..3af2b07d2a --- /dev/null +++ b/doc/api/v2/fluid/regularizer.rst @@ -0,0 +1,25 @@ +=========== +Regularizer +=========== + +WeightDecayRegularizer +----------- +.. automodule:: paddle.v2.fluid.regularizer + :members: WeightDecayRegularizer + :noindex: + + +L2DecayRegularizer +----------- +.. automodule:: paddle.v2.fluid.regularizer + :members: L2DecayRegularizer + :noindex: + + + +L1DecayRegularizer +----------- +.. automodule:: paddle.v2.fluid.regularizer + :members: L1DecayRegularizer + + diff --git a/python/paddle/v2/fluid/layers.py b/python/paddle/v2/fluid/layers.py index e41bfae285..5568619fe6 100644 --- a/python/paddle/v2/fluid/layers.py +++ b/python/paddle/v2/fluid/layers.py @@ -31,11 +31,9 @@ def fc(input, size: The size of the layer num_flatten_dims: Number of columns in input param_attr: The parameters/weights to the FC Layer - param_initializer: Initializer used for the weight/parameter. - If None, XavierInitializer() is used + param_initializer: Initializer used for the weight/parameter. If None, XavierInitializer() is used bias_attr: The bias parameter for the FC layer - bias_initializer: Initializer used for the bias. - If None, then ConstantInitializer() is used + bias_initializer: Initializer used for the bias. If None, then ConstantInitializer() is used act: Activation to be applied to the output of FC layer name: Name/alias of the function main_program: Name of the main program that calls this From d5e327945145f30e09209db04a0a4066fd5eeae7 Mon Sep 17 00:00:00 2001 From: Yu Yang Date: Mon, 4 Dec 2017 18:50:36 +0800 Subject: [PATCH 67/67] While op forward for sentimental analysis (#6140) * Add DataFeeder A v2 API like data feeder for book demos. We can feed data directly from reader. * Fix CI * Add an unittest for while/rnn op forward * Add unittest for raw while op backward * Fix CI --- paddle/framework/backward.cc | 41 ++++++++- paddle/framework/block_desc.cc | 3 + paddle/framework/executor.cc | 4 + paddle/framework/op_desc.cc | 7 +- paddle/framework/scope.cc | 20 +++-- paddle/framework/scope.h | 2 + paddle/framework/shape_inference.cc | 5 ++ paddle/operators/increment_op.cc | 2 + paddle/operators/lod_tensor_to_array_op.cc | 21 +++-- paddle/operators/multiplex_op.cc | 8 +- paddle/operators/recurrent_op.cc | 4 +- paddle/operators/sequence_pool_op.cc | 1 + paddle/operators/sum_op.cc | 34 ++++++-- paddle/operators/sum_op.h | 3 + .../operators/tensor_array_read_write_op.cc | 24 +++-- paddle/operators/while_op.cc | 45 +++++++--- python/paddle/v2/fluid/data_feeder.py | 1 - python/paddle/v2/fluid/layers.py | 4 +- python/paddle/v2/fluid/optimizer.py | 3 +- .../book/test_understand_sentiment_lstm.py | 4 +- python/paddle/v2/fluid/tests/test_dyn_rnn.py | 87 +++++++++++++++++++ 21 files changed, 262 insertions(+), 61 deletions(-) create mode 100644 python/paddle/v2/fluid/tests/test_dyn_rnn.py diff --git a/paddle/framework/backward.cc b/paddle/framework/backward.cc index 8fd2906107..c8b85caaca 100644 --- a/paddle/framework/backward.cc +++ b/paddle/framework/backward.cc @@ -27,6 +27,18 @@ namespace paddle { namespace framework { +static std::unordered_set* g_ctrl_flow_ops_ = nullptr; +// Control Flow operators's backward is significantly different from +// computational operators. Hack Code here. +// We should design a better way to backward CtrlFlowOps. +static std::unordered_set& CtrlFlowOps() { + if (g_ctrl_flow_ops_ == nullptr) { + g_ctrl_flow_ops_ = + new std::unordered_set{"increment", "lod_rank_table"}; + } + return *g_ctrl_flow_ops_; +} + static inline std::unique_ptr CreateGradOp( const OperatorBase& op, const std::unordered_set& no_grad_set, std::unordered_map* grad_to_var) { @@ -288,12 +300,24 @@ static void CreateGradVarInBlock( for (size_t op_index = grad_op_start_index; op_index < ops.size(); ++op_index) { std::unordered_set new_vars; + auto& ctrl_flow_ops = CtrlFlowOps(); ForEachVarName(ops[op_index]->Outputs(), [&](const std::string& grad_var_name) { - if (block_desc->HasVar(grad_var_name)) { + if (ctrl_flow_ops.find(ops[op_index]->Type()) != + ctrl_flow_ops.end()) { + if (block_desc->HasVarRecursive(grad_var_name)) { + return false; + } + } else { + if (block_desc->HasVar(grad_var_name)) { + return false; + } + } + if (grad_var_name == framework::kEmptyVarName) { return false; } auto var = block_desc->Var(grad_var_name); + VLOG(10) << "Creating Variable " << grad_var_name; new_vars.insert(var->Name()); auto it = param_name_map.find(grad_var_name); if (it == param_name_map.end()) { @@ -333,14 +357,25 @@ std::vector> MakeOpGrad( // All input gradients of forwarding operator do not need to calculate. const std::vector& inputs = op_desc->InputArgumentNames(); if (AllGradInSet(inputs, *no_grad_vars)) { + VLOG(10) << "Drop operator " << op_desc->Type(); return grad_op_descs; // empty vector } + // All output gradients of forwarding operator do not need to calculate. const std::vector& outputs = op_desc->OutputArgumentNames(); + if (AllGradInSet(outputs, *no_grad_vars)) { - for (const std::string& name : inputs) { - no_grad_vars->insert(GradVarName(name)); + VLOG(10) << "Drop operator " << op_desc->Type(); + // FIXME: Hack code here + auto& ctrl_flow_ops = CtrlFlowOps(); + if (ctrl_flow_ops.find(op_desc->Type()) == ctrl_flow_ops.end()) { + // Only computational op need drop input's gradient. + for (const std::string& name : inputs) { + no_grad_vars->insert(GradVarName(name)); + VLOG(10) << " Also drop " << GradVarName(name); + } } + return grad_op_descs; // empty vector } diff --git a/paddle/framework/block_desc.cc b/paddle/framework/block_desc.cc index 11764810e1..6a7a07d5cf 100644 --- a/paddle/framework/block_desc.cc +++ b/paddle/framework/block_desc.cc @@ -13,6 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ #include "paddle/framework/block_desc.h" +#include "paddle/framework/operator.h" #include "paddle/framework/program_desc.h" namespace paddle { @@ -42,6 +43,8 @@ bool BlockDescBind::HasVar(const std::string &name) const { } VarDescBind *BlockDescBind::FindVarRecursive(const std::string &name) const { + if (name == kEmptyVarName) return nullptr; + auto it = vars_.find(name); if (it == vars_.end()) { return Parent() == kNoneBlockIndex ? nullptr diff --git a/paddle/framework/executor.cc b/paddle/framework/executor.cc index 2ffb5b7dbb..83aa927c29 100644 --- a/paddle/framework/executor.cc +++ b/paddle/framework/executor.cc @@ -97,6 +97,10 @@ void Executor::Run(const ProgramDescBind& pdesc, Scope* scope, int block_id, if (create_local_scope) { local_scope = &scope->NewScope(); for (auto& var : block.AllVars()) { + if (var->Name() == framework::kEmptyVarName) { + continue; + } + if (var->Persistable()) { auto* ptr = scope->Var(var->Name()); CreateTensor(ptr, var->GetType()); diff --git a/paddle/framework/op_desc.cc b/paddle/framework/op_desc.cc index 02a8253243..2281d93df9 100644 --- a/paddle/framework/op_desc.cc +++ b/paddle/framework/op_desc.cc @@ -466,7 +466,12 @@ DDim CompileTimeInferShapeContext::GetDim(const std::string &name) const { auto var = block_.FindVarRecursive(name); PADDLE_ENFORCE(var != nullptr, "Cannot find variable %s", name); try { - return framework::make_ddim(var->Shape()); + auto shape = var->Shape(); + if (shape.empty()) { + return framework::make_ddim({0UL}); + } else { + return framework::make_ddim(var->Shape()); + } } catch (...) { VLOG(5) << "GetDim of variable " << name << " error"; std::rethrow_exception(std::current_exception()); diff --git a/paddle/framework/scope.cc b/paddle/framework/scope.cc index 9ad6272c99..656736e238 100644 --- a/paddle/framework/scope.cc +++ b/paddle/framework/scope.cc @@ -36,12 +36,9 @@ Scope& Scope::NewScope() const { } Variable* Scope::Var(const std::string& name) { - auto iter = vars_.find(name); - if (iter != vars_.end()) { - VLOG(3) << "Get existing variable " << name; - return iter->second; - } - Variable* v = new Variable(); + auto* v = FindVarLocally(name); + if (v != nullptr) return v; + v = new Variable(); vars_[name] = v; VLOG(3) << "Create variable " << name; v->name_ = &(vars_.find(name)->first); @@ -57,8 +54,10 @@ Variable* Scope::Var(std::string* name) { } Variable* Scope::FindVar(const std::string& name) const { - auto it = vars_.find(name); - if (it != vars_.end()) return it->second; + auto var = FindVarLocally(name); + if (var != nullptr) { + return var; + } return (parent_ == nullptr) ? nullptr : parent_->FindVar(name); } @@ -116,6 +115,11 @@ std::string Scope::Rename(const std::string& origin_name) const { Rename(origin_name, var_name); return var_name; } +Variable* Scope::FindVarLocally(const std::string& name) const { + auto it = vars_.find(name); + if (it != vars_.end()) return it->second; + return nullptr; +} } // namespace framework } // namespace paddle diff --git a/paddle/framework/scope.h b/paddle/framework/scope.h index c2aafb6ad8..56e815db54 100644 --- a/paddle/framework/scope.h +++ b/paddle/framework/scope.h @@ -76,6 +76,8 @@ class Scope { std::string Rename(const std::string& origin_name) const; private: + Variable* FindVarLocally(const std::string& name) const; + // Call Scope::NewScope for a sub-scope. explicit Scope(Scope const* parent) : parent_(parent) {} diff --git a/paddle/framework/shape_inference.cc b/paddle/framework/shape_inference.cc index 2298507471..7dac1cfd5e 100644 --- a/paddle/framework/shape_inference.cc +++ b/paddle/framework/shape_inference.cc @@ -12,6 +12,8 @@ See the License for the specific language governing permissions and limitations under the License. */ #include "paddle/framework/shape_inference.h" +#include "grad_op_desc_maker.h" +#include "paddle/framework/operator.h" namespace paddle { namespace framework { @@ -49,6 +51,9 @@ void InferShapeContext::SetDims(const std::vector &names, size_t length = names.size(); PADDLE_ENFORCE_EQ(length, dims.size()); for (size_t i = 0; i < length; ++i) { + if (names[i] == framework::kEmptyVarName) { + continue; + } SetDim(names[i], dims[i]); } } diff --git a/paddle/operators/increment_op.cc b/paddle/operators/increment_op.cc index 35efb12932..54911267e3 100644 --- a/paddle/operators/increment_op.cc +++ b/paddle/operators/increment_op.cc @@ -61,6 +61,8 @@ class IncrementOp : public framework::OperatorBase { out.Resize(x.dims()); out.mutable_data(x.place(), x.type()); float value = Attr("step"); + VLOG(10) << Output("Out") << " increase " << Input("X") << " with " + << value; framework::VisitDataType(framework::ToDataType(out.type()), IncrementFunctor(x, &out, value)); } diff --git a/paddle/operators/lod_tensor_to_array_op.cc b/paddle/operators/lod_tensor_to_array_op.cc index 010c79d4e1..b970bf3177 100644 --- a/paddle/operators/lod_tensor_to_array_op.cc +++ b/paddle/operators/lod_tensor_to_array_op.cc @@ -14,6 +14,7 @@ #include "paddle/framework/lod_rank_table.h" #include "paddle/framework/lod_tensor_array.h" #include "paddle/framework/op_registry.h" +#include "paddle/operators/detail/safe_ref.h" namespace paddle { namespace operators { @@ -32,15 +33,20 @@ class LoDTensorToArrayOp : public framework::OperatorBase { : OperatorBase(type, inputs, outputs, attrs) {} void Run(const framework::Scope &scope, const platform::DeviceContext &dev_ctx) const override { - auto &x = scope.FindVar(Input("X"))->Get(); - auto &rank_table = - scope.FindVar(Input("RankTable"))->Get(); - auto &out = - *scope.FindVar(Output("Out"))->GetMutable(); - + auto &x = detail::Ref(scope.FindVar(Input("X")), "Cannot find input %s", + Input("X")) + .Get(); + auto &rank_table = detail::Ref(scope.FindVar(Input("RankTable"))) + .Get(); + auto &out = *detail::Ref(scope.FindVar(Output("Out"))) + .GetMutable(); auto &items = rank_table.items(); auto max_seq_len = items[0].length; auto rank_level = rank_table.level(); + + PADDLE_ENFORCE_LT(rank_level, x.lod().size(), + "Input should be a LOD tensor, and size is at least %d", + rank_level + 1); out.resize(max_seq_len); std::vector> copy_ranges(max_seq_len); @@ -55,16 +61,13 @@ class LoDTensorToArrayOp : public framework::OperatorBase { size_t start_idx = x.lod()[rank_level][item.index] + t; auto lod_and_offset = framework::GetSubLoDAndAbsoluteOffset( x.lod(), start_idx, start_idx + 1, rank_level + 1); - auto &lod_length = lod_and_offset.first; framework::AppendLoD(&lod, lod_length); - size_t start_offset = lod_and_offset.second.first; size_t end_offset = lod_and_offset.second.second; copy_ranges[t].emplace_back(CopyRange{start_offset, end_offset}); } } - for (size_t i = 0; i < max_seq_len; ++i) { auto &ranges = copy_ranges[i]; size_t height = std::accumulate( diff --git a/paddle/operators/multiplex_op.cc b/paddle/operators/multiplex_op.cc index f8527dfab3..8e7f544e0d 100644 --- a/paddle/operators/multiplex_op.cc +++ b/paddle/operators/multiplex_op.cc @@ -99,13 +99,7 @@ class MultiplexGradOp : public framework::OperatorWithKernel { "Output(X@Grad) should not be null."); PADDLE_ENFORCE(ctx->HasInput(framework::GradVarName("Out")), "Input(Out@GRAD) should not be null."); - std::vector d_ins; - auto ins = ctx->GetInputsDim("X"); - // No need to compute gradient for Input(Ids) - for (size_t i = 0; i < ins.size(); i++) { - d_ins.push_back(ins[i]); - } - ctx->SetOutputsDim(framework::GradVarName("X"), d_ins); + ctx->SetOutputsDim(framework::GradVarName("X"), ctx->GetInputsDim("X")); } protected: diff --git a/paddle/operators/recurrent_op.cc b/paddle/operators/recurrent_op.cc index c976e22c77..8b60b9c912 100644 --- a/paddle/operators/recurrent_op.cc +++ b/paddle/operators/recurrent_op.cc @@ -599,7 +599,9 @@ class RecurrentGradOpShapeInference : public framework::InferShapeBase { std::vector output{kOutputs}; for (auto &s : input) { PADDLE_ENFORCE(ctx->HasInputs(s)); - PADDLE_ENFORCE(ctx->HasOutputs(framework::GradVarName(s))); + PADDLE_ENFORCE(ctx->HasOutputs(framework::GradVarName(s)), + "Cannot find the gradient variable %s", + framework::GradVarName(s)); } for (auto &s : output) { PADDLE_ENFORCE(ctx->HasInputs(s)); diff --git a/paddle/operators/sequence_pool_op.cc b/paddle/operators/sequence_pool_op.cc index 2a000ac60b..a2f4257037 100644 --- a/paddle/operators/sequence_pool_op.cc +++ b/paddle/operators/sequence_pool_op.cc @@ -104,6 +104,7 @@ class SequencePoolGradOp : public framework::OperatorWithKernel { PADDLE_ENFORCE_EQ(og_dims[i], x_dims[i], "The dimension mismatch."); } ctx->SetOutputDim(framework::GradVarName("X"), x_dims); + ctx->ShareLoD("X", framework::GradVarName("X")); } protected: diff --git a/paddle/operators/sum_op.cc b/paddle/operators/sum_op.cc index ddc210c26e..744b2fe3f2 100644 --- a/paddle/operators/sum_op.cc +++ b/paddle/operators/sum_op.cc @@ -37,10 +37,16 @@ class SumOp : public framework::OperatorWithKernel { size_t N = x_dims.size(); PADDLE_ENFORCE_GT(N, 1, "Input tensors count should > 1."); - auto in_dim = x_dims[0]; - for (size_t i = 1; i < N; i++) { - auto dim = x_dims[i]; - PADDLE_ENFORCE_EQ(in_dim, dim, "Input tensors must have same shape"); + framework::DDim in_dim({0}); + for (auto& x_dim : x_dims) { + if (framework::product(x_dim) == 0) { + continue; + } + if (framework::product(in_dim) == 0) { + in_dim = x_dim; + } else { + PADDLE_ENFORCE_EQ(in_dim, x_dim, "Input tensors must have same shape"); + } } ctx->SetOutputDim("Out", in_dim); ctx->ShareLoD("X", /*->*/ "Out"); @@ -51,9 +57,23 @@ class SumOp : public framework::OperatorWithKernel { const framework::ExecutionContext& ctx) const override { auto x_vars = ctx.MultiInputVar("X"); if (x_vars[0]->IsType()) { - return framework::OpKernelType( - framework::ToDataType(x_vars[0]->Get().type()), - ctx.device_context()); + int dtype = -1; + for (auto& x_var : x_vars) { + auto& lod_tensor = x_var->Get(); + if (lod_tensor.numel() == 0) { + continue; + } + if (dtype == -1) { + dtype = framework::ToDataType(lod_tensor.type()); + } else { + PADDLE_ENFORCE_EQ(dtype, framework::ToDataType(lod_tensor.type())); + } + } + PADDLE_ENFORCE_NE(dtype, -1, + "Sum operator should have at least one tensor"); + + return framework::OpKernelType(static_cast(dtype), + ctx.device_context()); } else if (x_vars[0]->IsType()) { return framework::OpKernelType( framework::ToDataType( diff --git a/paddle/operators/sum_op.h b/paddle/operators/sum_op.h index a1eb3b014e..ed6c80ce60 100644 --- a/paddle/operators/sum_op.h +++ b/paddle/operators/sum_op.h @@ -53,6 +53,9 @@ class SumKernel : public framework::OpKernel { for (int i = in_place ? 1 : 0; i < N; i++) { if (in_vars[i]->IsType()) { auto &in_t = in_vars[i]->Get(); + if (in_t.numel() == 0) { + continue; + } auto in = EigenVector::Flatten(in_t); result.device(place) = result + in; } else if (in_vars[i]->IsType()) { diff --git a/paddle/operators/tensor_array_read_write_op.cc b/paddle/operators/tensor_array_read_write_op.cc index efde850143..4eb8b60f47 100644 --- a/paddle/operators/tensor_array_read_write_op.cc +++ b/paddle/operators/tensor_array_read_write_op.cc @@ -27,7 +27,7 @@ class WriteToArrayOp : public ArrayOp { void Run(const framework::Scope &scope, const platform::DeviceContext &dev_ctx) const override { auto *x = scope.FindVar(Input("X")); - PADDLE_ENFORCE(x != nullptr, "X must be set"); + if (x == nullptr) return; auto &x_tensor = x->Get(); size_t offset = GetOffset(scope, dev_ctx); auto *out = @@ -76,7 +76,9 @@ class WriteToArrayInferShape : public framework::InferShapeBase { PADDLE_ENFORCE(context->HasInput("I"), "Must set the subscript index"); PADDLE_ENFORCE_EQ(framework::product(context->GetInputDim("I")), 1, "The number of element of subscript index must be 1"); - PADDLE_ENFORCE(context->HasInput("X"), NotHasXError()); + if (!context->HasInput("X")) { + return; + } PADDLE_ENFORCE(context->HasOutput("Out"), NotHasOutError()); context->SetOutputDim("Out", context->GetInputDim("X")); } @@ -99,9 +101,10 @@ class WriteToArrayInferVarType : public framework::VarTypeInference { auto &out = detail::Ref(block->FindRecursiveOrCreateVar(out_name), "Cannot found %s", out_name); out.SetType(framework::VarDesc::LOD_TENSOR_ARRAY); - auto &x = - detail::Ref(block->FindVarRecursive(x_name), "Cannot found %s", x_name); - out.SetDataType(x.GetDataType()); + auto *x = block->FindVarRecursive(x_name); + if (x != nullptr) { + out.SetDataType(x->GetDataType()); + } } }; @@ -121,10 +124,13 @@ class ReadFromArrayOp : public ArrayOp { PADDLE_ENFORCE(out != nullptr, "Out must be set"); auto *out_tensor = out->GetMutable(); size_t offset = GetOffset(scope, dev_ctx); - PADDLE_ENFORCE_LT(offset, x_array.size()); - framework::CopyFrom(x_array[offset], dev_ctx.GetPlace(), dev_ctx, - out_tensor); - out_tensor->set_lod(x_array[offset].lod()); + if (offset < x_array.size()) { + framework::CopyFrom(x_array[offset], dev_ctx.GetPlace(), dev_ctx, + out_tensor); + out_tensor->set_lod(x_array[offset].lod()); + } else { + VLOG(10) << "offset " << offset << " >= " << x_array.size(); + } } }; diff --git a/paddle/operators/while_op.cc b/paddle/operators/while_op.cc index 59460f6c87..9b3f21cf94 100644 --- a/paddle/operators/while_op.cc +++ b/paddle/operators/while_op.cc @@ -98,8 +98,6 @@ class WhileGradOp : public framework::OperatorBase { void Run(const framework::Scope &scope, const platform::DeviceContext &dev_ctx) const override { - // PADDLE_ENFORCE(...) - framework::Executor executor(dev_ctx); auto *block = Attr(kStepBlock); auto *program = block->Program(); @@ -124,8 +122,12 @@ class WhileGradOp : public framework::OperatorBase { auto inside_og_name = inside_og_names[i]; VLOG(10) << "Linking outside " << outside_og_name << " --> inside " << inside_og_name; - auto &og_outside = detail::Ref(scope.FindVar(outside_og_name)); - auto &og_inside = detail::Ref(cur_scope.Var(inside_og_name)); + auto &og_outside = + detail::Ref(scope.FindVar(outside_og_name), + "Cannot find Outside Gradient %s", outside_og_name); + auto &og_inside = + detail::Ref(cur_scope.Var(inside_og_name), + "Cannot find inside gradient %s", inside_og_name); if (og_outside.Type().hash_code() == typeid(framework::LoDTensor).hash_code()) { auto &outside_tensor = og_outside.Get(); @@ -160,7 +162,7 @@ class WhileGradOp : public framework::OperatorBase { PADDLE_ENFORCE_EQ(pg_names.size(), p_names.size()); for (size_t param_id = 0; param_id < pg_names.size(); ++param_id) { if (pg_names[param_id] == framework::kEmptyVarName) { - continue; // iterator doesn't have gradient + continue; // parameter doesn't have gradient } auto inside_grad_name = framework::GradVarName(p_names[param_id]); @@ -190,7 +192,6 @@ class WhileGradOp : public framework::OperatorBase { } } - // sum gradient auto new_inside_name = cur_scope.Rename(inside_grad_name); auto sum_op = framework::OpRegistry::CreateOp( "sum", {{"X", {pg_names[param_id], new_inside_name}}}, @@ -207,18 +208,35 @@ class WhileGradOpDescMaker : public framework::SingleGradOpDescMaker { using framework::SingleGradOpDescMaker::SingleGradOpDescMaker; protected: - virtual std::unique_ptr Apply() const { + std::unique_ptr Apply() const override { auto *grad = new framework::OpDescBind(); grad->SetType("while_grad"); grad->SetInput(kParameters, Input(kParameters)); - grad->SetOutput( - framework::GradVarName(kParameters), - InputGrad(kParameters, /*do not drop empty gradient*/ false)); + + // Not all of IGs will be generated by inner gradient operators of while op. + // Ignore IGs that is not generated by the inside block. + auto igs = InputGrad(kParameters, /*do not drop empty gradient*/ false); + std::unordered_set all_outs; + for (size_t i = 0; i < grad_block_[0]->OpSize(); ++i) { + for (auto &oname : grad_block_[0]->Op(i)->OutputArgumentNames()) { + all_outs.insert(oname); + } + } + for (auto &each_ig : igs) { + if (all_outs.find(each_ig) == all_outs.end()) { + VLOG(10) << "Ignore " << each_ig; + each_ig = framework::kEmptyVarName; + } + } + + grad->SetOutput(framework::GradVarName(kParameters), igs); + grad->SetInput(kOutputs, Output(kOutputs)); // OG should be re-calculated by step blocks, since many outputs of while op // do not need to calculate gradients. std::unordered_set block_ins; + auto *fwd_block = this->grad_block_[0]->ParentBlock(); { for (auto &p : Input(kParameters)) { block_ins.insert(p); @@ -233,6 +251,13 @@ class WhileGradOpDescMaker : public framework::SingleGradOpDescMaker { if (block_ins.find(input_name) != block_ins.end()) { continue; } + + // If the input of Op is generated by the forward block, do not make it + // as input again. + if (fwd_block->FindVar(input_name) != nullptr) { + continue; + } + extra_inputs.insert(input_name); } diff --git a/python/paddle/v2/fluid/data_feeder.py b/python/paddle/v2/fluid/data_feeder.py index 3dee0b5b73..30a542af21 100644 --- a/python/paddle/v2/fluid/data_feeder.py +++ b/python/paddle/v2/fluid/data_feeder.py @@ -1,5 +1,4 @@ from __future__ import print_function - import core import numpy import six.moves as six diff --git a/python/paddle/v2/fluid/layers.py b/python/paddle/v2/fluid/layers.py index 5568619fe6..99d0ac4a1b 100644 --- a/python/paddle/v2/fluid/layers.py +++ b/python/paddle/v2/fluid/layers.py @@ -10,7 +10,7 @@ from param_attr import ParamAttr __all__ = [ 'fc', 'data', 'cross_entropy', 'conv2d', 'pool2d', 'embedding', 'concat', 'StaticRNN', 'cast', 'sequence_conv', 'sequence_pool', 'sums', 'cos_sim', - 'batch_norm', 'accuracy', 'split_lod_tensor' + 'batch_norm', 'accuracy', 'split_lod_tensor', 'While' ] @@ -1439,7 +1439,7 @@ def increment(x, value=1.0, in_place=True, main_program=None): type='increment', inputs={'X': [x]}, outputs={'Out': [out]}, - attrs={'step': value}) + attrs={'step': float(value)}) return out diff --git a/python/paddle/v2/fluid/optimizer.py b/python/paddle/v2/fluid/optimizer.py index 934e024742..719e3b2563 100644 --- a/python/paddle/v2/fluid/optimizer.py +++ b/python/paddle/v2/fluid/optimizer.py @@ -197,8 +197,7 @@ class Optimizer(object): This method combines interface `append_backward_ops()` and `create_optimization_pass()` into one. """ - params_grads = append_backward_ops(loss, parameter_list, no_grad_set or - set()) + params_grads = append_backward_ops(loss, parameter_list, no_grad_set) # Add regularization if any params_grads = append_regularization_ops(params_grads) optimize_ops = self.create_optimization_pass(params_grads, loss, diff --git a/python/paddle/v2/fluid/tests/book/test_understand_sentiment_lstm.py b/python/paddle/v2/fluid/tests/book/test_understand_sentiment_lstm.py index b247932033..80f8599679 100644 --- a/python/paddle/v2/fluid/tests/book/test_understand_sentiment_lstm.py +++ b/python/paddle/v2/fluid/tests/book/test_understand_sentiment_lstm.py @@ -8,7 +8,8 @@ def lstm_net(dict_dim, class_dim=2, emb_dim=32, seq_len=80, batch_size=50): name="words", shape=[seq_len * batch_size, 1], append_batch_size=False, - dtype="int64") + dtype="int64", + lod_level=1) label = fluid.layers.data( name="label", shape=[batch_size, 1], @@ -21,6 +22,7 @@ def lstm_net(dict_dim, class_dim=2, emb_dim=32, seq_len=80, batch_size=50): c_pre_init = fluid.layers.fill_constant( dtype=emb.dtype, shape=[batch_size, emb_dim], value=0.0) + c_pre_init.stop_gradient = False layer_1_out = fluid.layers.lstm( emb, c_pre_init=c_pre_init, hidden_dim=emb_dim) layer_1_out = fluid.layers.transpose(x=layer_1_out, axis=[1, 0, 2]) diff --git a/python/paddle/v2/fluid/tests/test_dyn_rnn.py b/python/paddle/v2/fluid/tests/test_dyn_rnn.py new file mode 100644 index 0000000000..271e39a0e0 --- /dev/null +++ b/python/paddle/v2/fluid/tests/test_dyn_rnn.py @@ -0,0 +1,87 @@ +import paddle.v2.fluid as fluid +import paddle.v2 as paddle +import unittest +import numpy + + +class TestDynRNN(unittest.TestCase): + def setUp(self): + self.word_dict = paddle.dataset.imdb.word_dict() + self.BATCH_SIZE = 100 + self.train_data = paddle.batch( + paddle.dataset.imdb.train(self.word_dict), + batch_size=self.BATCH_SIZE) + + def test_plain_while_op(self): + main_program = fluid.Program() + startup_program = fluid.Program() + + with fluid.program_guard(main_program, startup_program): + sentence = fluid.layers.data( + name='word', shape=[1], dtype='int64', lod_level=1) + sent_emb = fluid.layers.embedding( + input=sentence, size=[len(self.word_dict), 32], dtype='float32') + + label = fluid.layers.data(name='label', shape=[1], dtype='float32') + + rank_table = fluid.layers.lod_rank_table(x=sent_emb) + + sent_emb_array = fluid.layers.lod_tensor_to_array( + x=sent_emb, table=rank_table) + + seq_len = fluid.layers.max_sequence_len(rank_table=rank_table) + i = fluid.layers.fill_constant(shape=[1], dtype='int64', value=0) + i.stop_gradient = False + + boot_mem = fluid.layers.fill_constant_batch_size_like( + input=fluid.layers.array_read( + array=sent_emb_array, i=i), + value=0, + shape=[-1, 100], + dtype='float32') + boot_mem.stop_gradient = False + + mem_array = fluid.layers.array_write(x=boot_mem, i=i) + + cond = fluid.layers.less_than(x=i, y=seq_len) + cond.stop_gradient = False + while_op = fluid.layers.While(cond=cond) + out = fluid.layers.create_array(dtype='float32') + + with while_op.block(): + mem = fluid.layers.array_read(array=mem_array, i=i) + ipt = fluid.layers.array_read(array=sent_emb_array, i=i) + + mem = fluid.layers.shrink_memory(x=mem, i=i, table=rank_table) + + hidden = fluid.layers.fc(input=[mem, ipt], size=100, act='tanh') + fluid.layers.array_write(x=hidden, i=i, array=out) + fluid.layers.increment(x=i, in_place=True) + fluid.layers.array_write(x=hidden, i=i, array=mem_array) + fluid.layers.less_than(x=i, y=seq_len, cond=cond) + + all_timesteps = fluid.layers.array_to_lod_tensor( + x=out, table=rank_table) + last = fluid.layers.sequence_pool( + input=all_timesteps, pool_type='last') + logits = fluid.layers.fc(input=last, size=1, act=None) + loss = fluid.layers.sigmoid_cross_entropy_with_logits( + x=logits, label=label) + loss = fluid.layers.mean(x=loss) + sgd = fluid.optimizer.SGD(1e-4) + sgd.minimize(loss=loss) + cpu = fluid.CPUPlace() + exe = fluid.Executor(cpu) + exe.run(startup_program) + feeder = fluid.DataFeeder(feed_list=[sentence, label], place=cpu) + + data = next(self.train_data()) + val = exe.run(main_program, feed=feeder.feed(data), + fetch_list=[loss])[0] + self.assertEqual((1, ), val.shape) + print(val) + self.assertFalse(numpy.isnan(val)) + + +if __name__ == '__main__': + unittest.main()

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 literal 57433 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 diff --git a/doc/design/mkldnn/image/layers.png b/doc/design/mkldnn/image/layers.png index e65e1aeca47cd2f0c5289d0bb209ef394545bd31..4f87553b41b2c38caca2e54c039e9ccaa9d605e5 100644 GIT binary patch literal 14414 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ⅈGu*u(g~eVz=rG>{s&^mT9a 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 bZx6dwj(Ge|H_ZZgUC>Pv%j=a!?lJ!c<|gv( literal 19755 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 bXvf(?U#`U2=OD4O0xZmI|Ej&>`RM-vK7}V@ diff --git a/doc/design/mkldnn/image/overview.png b/doc/design/mkldnn/image/overview.png index 84b455c28230703599a2529f014cfbb222138fef..1d81b5a4b5db687c06b92f88648f9895711fdef4 100644 GIT binary patch literal 16329 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 ANdN!< From 7480291c627b7a63cfb39b84ebc6b3d01813793b Mon Sep 17 00:00:00 2001 From: Yiqun Liu Date: Fri, 1 Dec 2017 15:12:31 +0800 Subject: [PATCH 48/67] Add version and commit information in capi config.h and use unofficial glog for Android API < 21. (#6113) * Automatically configure the version and commit information in capi. * Use the unofficial glog repository for building for Android (API < 21). --- CMakeLists.txt | 3 --- cmake/external/glog.cmake | 13 +++++++++++-- paddle/capi/CMakeLists.txt | 10 ++++++++++ paddle/capi/config.h.in | 3 +++ paddle/testing/CMakeLists.txt | 6 ++++-- 5 files changed, 28 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e76512166f..f7824b1066 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,9 +67,6 @@ if(ANDROID OR IOS) if(ANDROID) if(${CMAKE_SYSTEM_VERSION} VERSION_LESS "16") message(FATAL_ERROR "Unsupport standalone toolchains with Android API level lower than 16") - elseif(${CMAKE_SYSTEM_VERSION} VERSION_LESS "21") - # TODO: support glog for Android api 16 ~ 19 in the future - message(WARNING "Using the unofficial git repository instead") endif() endif() diff --git a/cmake/external/glog.cmake b/cmake/external/glog.cmake index 08bdc1e162..0c6b3aafcb 100644 --- a/cmake/external/glog.cmake +++ b/cmake/external/glog.cmake @@ -26,12 +26,21 @@ ENDIF(WIN32) INCLUDE_DIRECTORIES(${GLOG_INCLUDE_DIR}) +IF(ANDROID AND ${CMAKE_SYSTEM_VERSION} VERSION_LESS "21") + # Using the unofficial glog for Android API < 21 + SET(GLOG_REPOSITORY "https://github.com/Xreki/glog.git") + SET(GLOG_TAG "8a547150548b284382ccb6582408e9140ff2bea8") +ELSE() + SET(GLOG_REPOSITORY "https://github.com/google/glog.git") + SET(GLOG_TAG "v0.3.5") +ENDIF() + ExternalProject_Add( extern_glog ${EXTERNAL_PROJECT_LOG_ARGS} DEPENDS gflags - GIT_REPOSITORY "https://github.com/google/glog.git" - GIT_TAG v0.3.5 + GIT_REPOSITORY ${GLOG_REPOSITORY} + GIT_TAG ${GLOG_TAG} PREFIX ${GLOG_SOURCES_DIR} UPDATE_COMMAND "" CMAKE_ARGS -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} diff --git a/paddle/capi/CMakeLists.txt b/paddle/capi/CMakeLists.txt index d267b14657..ebb083c5a4 100644 --- a/paddle/capi/CMakeLists.txt +++ b/paddle/capi/CMakeLists.txt @@ -4,6 +4,16 @@ else () set(PADDLE_FLOAT_TYPE float) endif() +execute_process( + COMMAND ${GIT_EXECUTABLE} log --pretty=format:%H -1 + WORKING_DIRECTORY ${PADDLE_SOURCE_DIR} + OUTPUT_VARIABLE PADDLE_GIT_COMMIT + RESULT_VARIABLE PADDLE_GIT_COMMIT_RESULT + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) +if(NOT PADDLE_GIT_COMMIT) + set(PADDLE_GIT_COMMIT "no commit information") +endif() + # config.h used for C-API. It will store Paddle building configuration as a # header. Make user just include PaddleCAPI.h then can get building # configuration without explicitly set -DPADDLE_WITH_DOUBLE when building their diff --git a/paddle/capi/config.h.in b/paddle/capi/config.h.in index d205307588..0ddbd8c753 100644 --- a/paddle/capi/config.h.in +++ b/paddle/capi/config.h.in @@ -3,6 +3,9 @@ typedef @PADDLE_FLOAT_TYPE@ paddle_real; +#define __PADDLE_VERSION__ "@PADDLE_VERSION@" +#define __PADDLE_COMMIT__ "@PADDLE_GIT_COMMIT@" + // Since we only support linux and macos in compile, always use clang or // gcc 4.8+. DLL_IMPORT/DLL_EXPORT is as simple as below. #define PD_API __attribute__((visibility("default"))) diff --git a/paddle/testing/CMakeLists.txt b/paddle/testing/CMakeLists.txt index 2275c950ba..8132742749 100644 --- a/paddle/testing/CMakeLists.txt +++ b/paddle/testing/CMakeLists.txt @@ -5,6 +5,8 @@ if(WITH_TESTING) add_dependencies(paddle_test_main paddle_proto ${external_project_dependencies}) add_library(paddle_test_util STATIC TestUtil.cpp) add_dependencies(paddle_test_util paddle_proto ${external_project_dependencies}) - add_library(paddle_gtest_main STATIC paddle_gtest_main.cc) - add_dependencies(paddle_gtest_main paddle_memory gtest gflags) + if(NOT MOBILE_INFERENCE) + add_library(paddle_gtest_main STATIC paddle_gtest_main.cc) + add_dependencies(paddle_gtest_main paddle_memory gtest gflags) + endif() endif() From 57dc8de934b4bbb8be06090436dfec7d4e788fa1 Mon Sep 17 00:00:00 2001 From: Yiqun Liu Date: Fri, 1 Dec 2017 15:26:41 +0800 Subject: [PATCH 49/67] Fix the linking error for iOS simulator (architecture x86_64). (#6081) --- paddle/math/CMakeLists.txt | 2 -- paddle/math/SIMDFunctions.h | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/paddle/math/CMakeLists.txt b/paddle/math/CMakeLists.txt index 86bb270a43..922fb51722 100644 --- a/paddle/math/CMakeLists.txt +++ b/paddle/math/CMakeLists.txt @@ -26,8 +26,6 @@ else() endif() if(MOBILE_INFERENCE) - list(REMOVE_ITEM MATH_SOURCES - ${CMAKE_CURRENT_SOURCE_DIR}/SIMDFunctions.cpp) # Remove sparse list(REMOVE_ITEM MATH_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/CpuSparseMatrix.h diff --git a/paddle/math/SIMDFunctions.h b/paddle/math/SIMDFunctions.h index 439f11b79d..76909720f6 100644 --- a/paddle/math/SIMDFunctions.h +++ b/paddle/math/SIMDFunctions.h @@ -116,9 +116,11 @@ inline bool vec_check(size_t len) { } namespace internal { +#ifdef __SSE3__ void addToImpl(float* a, const float* b, size_t len); void batchAddToImpl(float* a, const float* b[], int batch, size_t len); void colMaxImpl(float* result, const float* data, int dim, int numSamples); +#endif #ifdef __AVX__ void decayL1AvxImpl(float* dst, float* src, float lambda, size_t len); void decayL1AvxImpl( From 813bbf40a1a5a2583354d4bd516c469e137c1668 Mon Sep 17 00:00:00 2001 From: QI JUN Date: Fri, 1 Dec 2017 15:42:05 +0800 Subject: [PATCH 50/67] disable test_recurrent_op (#6153) --- python/paddle/v2/fluid/tests/test_recurrent_op.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/python/paddle/v2/fluid/tests/test_recurrent_op.py b/python/paddle/v2/fluid/tests/test_recurrent_op.py index 36e0c84c0b..694ff0d8dd 100644 --- a/python/paddle/v2/fluid/tests/test_recurrent_op.py +++ b/python/paddle/v2/fluid/tests/test_recurrent_op.py @@ -454,4 +454,6 @@ class RecurrentOpNoMemBootTest(RecurrentOpTest1): if __name__ == '__main__': + # FIXME(qijun) https://github.com/PaddlePaddle/Paddle/issues/6152 + exit(0) unittest.main() From aabe1db111625519bd7f85d7100a3ab7747f1e12 Mon Sep 17 00:00:00 2001 From: Yu Yang Date: Fri, 1 Dec 2017 16:12:29 +0800 Subject: [PATCH 51/67] Feature/simple gan for api (#6149) * Expose sigmoid_cross_entropy_with_logits Also, change the `labels` to `label` for api consistency * Very simple GAN based on pure FC layers --- python/paddle/v2/fluid/tests/demo/fc_gan.py | 157 ++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 python/paddle/v2/fluid/tests/demo/fc_gan.py diff --git a/python/paddle/v2/fluid/tests/demo/fc_gan.py b/python/paddle/v2/fluid/tests/demo/fc_gan.py new file mode 100644 index 0000000000..cae959593e --- /dev/null +++ b/python/paddle/v2/fluid/tests/demo/fc_gan.py @@ -0,0 +1,157 @@ +import errno +import math +import os + +import matplotlib +import numpy + +import paddle.v2 as paddle +import paddle.v2.fluid as fluid + +matplotlib.use('Agg') +import matplotlib.pyplot as plt +import matplotlib.gridspec as gridspec + +NOISE_SIZE = 100 +NUM_PASS = 1000 +NUM_REAL_IMGS_IN_BATCH = 121 +NUM_TRAIN_TIMES_OF_DG = 3 +LEARNING_RATE = 2e-5 + + +def D(x): + hidden = fluid.layers.fc(input=x, + size=200, + act='relu', + param_attr='D.w1', + bias_attr='D.b1') + logits = fluid.layers.fc(input=hidden, + size=1, + act=None, + param_attr='D.w2', + bias_attr='D.b2') + return logits + + +def G(x): + hidden = fluid.layers.fc(input=x, + size=200, + act='relu', + param_attr='G.w1', + bias_attr='G.b1') + img = fluid.layers.fc(input=hidden, + size=28 * 28, + act='tanh', + param_attr='G.w2', + bias_attr='G.b2') + return img + + +def plot(gen_data): + gen_data.resize(gen_data.shape[0], 28, 28) + n = int(math.ceil(math.sqrt(gen_data.shape[0]))) + fig = plt.figure(figsize=(n, n)) + gs = gridspec.GridSpec(n, n) + gs.update(wspace=0.05, hspace=0.05) + + for i, sample in enumerate(gen_data): + ax = plt.subplot(gs[i]) + plt.axis('off') + ax.set_xticklabels([]) + ax.set_yticklabels([]) + ax.set_aspect('equal') + plt.imshow(sample.reshape(28, 28), cmap='Greys_r') + + return fig + + +def main(): + try: + os.makedirs("./out") + except OSError as e: + if e.errno != errno.EEXIST: + raise + + startup_program = fluid.Program() + d_program = fluid.Program() + dg_program = fluid.Program() + + with fluid.program_guard(d_program, startup_program): + img = fluid.layers.data(name='img', shape=[784], dtype='float32') + d_loss = fluid.layers.sigmoid_cross_entropy_with_logits( + x=D(img), + label=fluid.layers.data( + name='label', shape=[1], dtype='float32')) + d_loss = fluid.layers.mean(x=d_loss) + + with fluid.program_guard(dg_program, startup_program): + noise = fluid.layers.data( + name='noise', shape=[NOISE_SIZE], dtype='float32') + g_img = G(x=noise) + g_program = dg_program.clone() + dg_loss = fluid.layers.sigmoid_cross_entropy_with_logits( + x=D(g_img), + label=fluid.layers.fill_constant_batch_size_like( + input=noise, dtype='float32', shape=[-1, 1], value=1.0)) + dg_loss = fluid.layers.mean(x=dg_loss) + + opt = fluid.optimizer.Adam(learning_rate=LEARNING_RATE) + + opt.minimize(loss=d_loss, startup_program=startup_program) + opt.minimize( + loss=dg_loss, + startup_program=startup_program, + parameter_list=[ + p.name for p in g_program.global_block().all_parameters() + ]) + exe = fluid.Executor(fluid.CPUPlace()) + exe.run(startup_program) + + num_true = NUM_REAL_IMGS_IN_BATCH + train_reader = paddle.batch( + paddle.reader.shuffle( + paddle.dataset.mnist.train(), buf_size=60000), + batch_size=num_true) + + for pass_id in range(NUM_PASS): + for batch_id, data in enumerate(train_reader()): + num_true = len(data) + n = numpy.random.uniform( + low=-1.0, high=1.0, + size=[num_true * NOISE_SIZE]).astype('float32').reshape( + [num_true, NOISE_SIZE]) + generated_img = exe.run(g_program, + feed={'noise': n}, + fetch_list={g_img})[0] + real_data = numpy.array(map(lambda x: x[0], data)).astype('float32') + real_data = real_data.reshape(num_true, 784) + total_data = numpy.concatenate([real_data, generated_img]) + total_label = numpy.concatenate([ + numpy.ones( + shape=[real_data.shape[0], 1], dtype='float32'), + numpy.zeros( + shape=[real_data.shape[0], 1], dtype='float32') + ]) + d_loss_np = exe.run(d_program, + feed={'img': total_data, + 'label': total_label}, + fetch_list={d_loss})[0] + for _ in xrange(NUM_TRAIN_TIMES_OF_DG): + n = numpy.random.uniform( + low=-1.0, high=1.0, + size=[2 * num_true * NOISE_SIZE]).astype('float32').reshape( + [2 * num_true, NOISE_SIZE, 1, 1]) + dg_loss_np = exe.run(dg_program, + feed={'noise': n}, + fetch_list={dg_loss})[0] + print("Pass ID={0}, Batch ID={1}, D-Loss={2}, DG-Loss={3}".format( + pass_id, batch_id, d_loss_np, dg_loss_np)) + # generate image each batch + fig = plot(generated_img) + plt.savefig( + 'out/{0}.png'.format(str(pass_id).zfill(3)), bbox_inches='tight') + plt.close(fig) + + +if __name__ == '__main__': + main() From dda277ba6c386d63e052fe50a8e21d8dd2df579d Mon Sep 17 00:00:00 2001 From: typhoonzero Date: Fri, 1 Dec 2017 17:50:54 +0800 Subject: [PATCH 52/67] update build.sh --- paddle/scripts/docker/build.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/paddle/scripts/docker/build.sh b/paddle/scripts/docker/build.sh index 502637c881..fbd0b6b078 100644 --- a/paddle/scripts/docker/build.sh +++ b/paddle/scripts/docker/build.sh @@ -36,6 +36,7 @@ function cmake_gen() { ${PYTHON_FLAGS} -DWITH_DOC=OFF -DWITH_GPU=${WITH_GPU:-OFF} + -DWITH_DISTRIBUTE=${WITH_DISTRIBUTE:-OFF} -DWITH_MKL=${WITH_MKL:-ON} -DWITH_AVX=${WITH_AVX:-OFF} -DWITH_GOLANG=${WITH_GOLANG:-ON} @@ -57,6 +58,7 @@ EOF ${PYTHON_FLAGS} \ -DWITH_DOC=OFF \ -DWITH_GPU=${WITH_GPU:-OFF} \ + -DWITH_DISTRIBUTE=${WITH_DISTRIBUTE:-OFF} \ -DWITH_MKL=${WITH_MKL:-ON} \ -DWITH_AVX=${WITH_AVX:-OFF} \ -DWITH_GOLANG=${WITH_GOLANG:-ON} \ From e50f35706a2d64b2724bff483e0f203ed4882c28 Mon Sep 17 00:00:00 2001 From: chengduo Date: Fri, 1 Dec 2017 18:19:22 +0800 Subject: [PATCH 53/67] code refine (#6164) --- paddle/platform/enforce.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paddle/platform/enforce.h b/paddle/platform/enforce.h index 97338a4ce6..5abd4d4a34 100644 --- a/paddle/platform/enforce.h +++ b/paddle/platform/enforce.h @@ -244,7 +244,7 @@ inline void throw_on_error(T e) { #define __PADDLE_BINARY_COMPARE(__VAL0, __VAL1, __CMP, __INV_CMP, ...) \ do { \ - if (!UNLIKELY((__VAL0)__CMP(__VAL1))) { \ + if (UNLIKELY(!((__VAL0)__CMP(__VAL1)))) { \ PADDLE_THROW("enforce %s " #__CMP " %s failed, %s " #__INV_CMP \ " %s\n%s", \ #__VAL0, #__VAL1, paddle::string::to_string(__VAL0), \ From d066b07f144589ef72fe05faa8ca0f91889fefda Mon Sep 17 00:00:00 2001 From: QI JUN Date: Fri, 1 Dec 2017 18:21:05 +0800 Subject: [PATCH 54/67] change GPU memory allocating policy (#6159) * change GPU memory allocating policy * fix potential overflow bug --- paddle/platform/gpu_info.cc | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/paddle/platform/gpu_info.cc b/paddle/platform/gpu_info.cc index 36b216d872..63a3351708 100644 --- a/paddle/platform/gpu_info.cc +++ b/paddle/platform/gpu_info.cc @@ -75,15 +75,19 @@ size_t GpuMaxChunkSize() { GpuMemoryUsage(available, total); // Reserving the rest memory for page tables, etc. - size_t reserving = (1 - FLAGS_fraction_of_gpu_memory_to_use) * total; + size_t reserving = 0.05 * total; // If available less than minimum chunk size, no usable memory exists. - available = std::max(available, GpuMinChunkSize()) - GpuMinChunkSize(); + available = + std::max(std::max(available, GpuMinChunkSize()) - GpuMinChunkSize(), + reserving) - + reserving; - // If available less than reserving, no usable memory exists. - size_t usable = std::max(available, reserving) - reserving; + size_t allocating = FLAGS_fraction_of_gpu_memory_to_use * total; - return usable; + PADDLE_ENFORCE_LT(allocating, available); + + return allocating; } void GpuMemcpyAsync(void *dst, const void *src, size_t count, From 7d9ff4081e3a0816cab4119dc146c73c576b71cd Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Fri, 1 Dec 2017 18:16:31 +0800 Subject: [PATCH 55/67] narrow pictures --- doc/design/mkldnn/README.MD | 20 ++++++++++---------- doc/design/mkldnn/image/engine.png | Bin 17102 -> 13586 bytes doc/design/mkldnn/image/gradients.png | Bin 31247 -> 22890 bytes doc/design/mkldnn/image/layers.png | Bin 14414 -> 11646 bytes doc/design/mkldnn/image/matrix.png | Bin 22085 -> 18407 bytes doc/design/mkldnn/image/overview.png | Bin 16329 -> 10766 bytes 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/doc/design/mkldnn/README.MD b/doc/design/mkldnn/README.MD index 287ee620e1..61d453de24 100644 --- a/doc/design/mkldnn/README.MD +++ b/doc/design/mkldnn/README.MD @@ -5,7 +5,7 @@ 充分展现英特尔平台的优势,有效提升PaddlePaddle在英特尔架构上的性能。