From 65451b5c4df5a78eec7cb7778d1c1daa51dbada0 Mon Sep 17 00:00:00 2001 From: wwhu Date: Thu, 2 Nov 2017 10:30:39 +0800 Subject: [PATCH 01/41] add cliy_by_norm op --- paddle/operators/clip_by_norm_op.cc | 90 +++++++++++++++++++ paddle/operators/clip_by_norm_op.cu | 20 +++++ paddle/operators/clip_by_norm_op.h | 55 ++++++++++++ .../framework/tests/test_clip_by_norm_op.py | 52 +++++++++++ 4 files changed, 217 insertions(+) create mode 100644 paddle/operators/clip_by_norm_op.cc create mode 100644 paddle/operators/clip_by_norm_op.cu create mode 100644 paddle/operators/clip_by_norm_op.h create mode 100644 python/paddle/v2/framework/tests/test_clip_by_norm_op.py diff --git a/paddle/operators/clip_by_norm_op.cc b/paddle/operators/clip_by_norm_op.cc new file mode 100644 index 0000000000..440542d331 --- /dev/null +++ b/paddle/operators/clip_by_norm_op.cc @@ -0,0 +1,90 @@ +/* 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/clip_by_norm_op.h" + +namespace paddle { +namespace operators { + +class ClipByNormOp : public framework::OperatorWithKernel { + public: + using framework::OperatorWithKernel::OperatorWithKernel; + + protected: + void InferShape(framework::InferShapeContext* ctx) const override { + PADDLE_ENFORCE(ctx->HasInput("X"), + "Input(X) of ClipByNormOp should not be null."); + PADDLE_ENFORCE(ctx->HasOutput("Out"), + "Output(Out) of ClipByNormOp should not be null."); + auto max_norm = Attr("max_norm"); + PADDLE_ENFORCE_GT(max_norm, 0, "max_norm should be greater than 0."); + auto x_dims = ctx->GetInputDim("X"); + ctx->SetOutputDim("Out", x_dims); + ctx->ShareLoD("X", /*->*/ "Out"); + } +}; + +template +class ClipByNormOpMaker : public framework::OpProtoAndCheckerMaker { + public: + ClipByNormOpMaker(framework::OpProto* proto, + framework::OpAttrChecker* op_checker) + : OpProtoAndCheckerMaker(proto, op_checker) { + AddInput("X", + "(Tensor)The input of clip_by_norm op." + "The number of dimensions must be between [1, 9]."); + AddOutput("Out", + "(Tensor)The output of clip_by_norm op with shape as input(X)"); + AddAttr( + "max_norm", "(float)The maximum norm value."); + AddComment(R"DOC( +ClipByNorm operator limits the L2 norm of the input 'X' within 'max_norm'. +If the L2 norm of 'X' is less than or equal to 'max_norm', 'Out' will be +the same as 'X'. If the L2 norm of 'X' is greater than 'max_norm', 'X' will +be linearly scaled to make the L2 norm of 'Out' equal to 'max_norm', as +shown in the following formulaļ¼š + +'Out' = 'max_norm' * 'X' / norm('X'), + +where norm('X') represents the L2 norm of 'X'. +)DOC"); + } +}; + +class ClipByNormOpGrad : public framework::OperatorWithKernel { + public: + using framework::OperatorWithKernel::OperatorWithKernel; + + protected: + void InferShape(framework::InferShapeContext* ctx) const override { + PADDLE_ENFORCE(ctx->HasInput("X"), "Input(X) should not be null"); + PADDLE_ENFORCE(ctx->HasInput(framework::GradVarName("Out")), + "Input(Out@GRAD) should not be null"); + auto x_dims = ctx->GetInputDim("X"); + if (ctx->HasOutput(framework::GradVarName("X"))) { + ctx->SetOutputDim(framework::GradVarName("X"), x_dims); + } + } +}; + +} // namespace operators +} // namespace paddle + +namespace ops = paddle::operators; +REGISTER_OP_WITHOUT_GRADIENT(clip_by_norm, + ops::ClipByNormOp, + ops::ClipByNormOpMaker); +REGISTER_OP_CPU_KERNEL(clip_by_norm, + ops::ClipByNormKernel + ); diff --git a/paddle/operators/clip_by_norm_op.cu b/paddle/operators/clip_by_norm_op.cu new file mode 100644 index 0000000000..5f363b999f --- /dev/null +++ b/paddle/operators/clip_by_norm_op.cu @@ -0,0 +1,20 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ + +#include "paddle/operators/clip_by_norm_op.h" + +namespace ops = paddle::operators; +REGISTER_OP_GPU_KERNEL(clip_by_norm, + ops::ClipByNormKernel + ); diff --git a/paddle/operators/clip_by_norm_op.h b/paddle/operators/clip_by_norm_op.h new file mode 100644 index 0000000000..6f5f8c20bf --- /dev/null +++ b/paddle/operators/clip_by_norm_op.h @@ -0,0 +1,55 @@ +/* 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" +#include "paddle/platform/transform.h" + +namespace paddle { +namespace operators { + +using Tensor = framework::Tensor; +template +using EigenVector = framework::EigenVector; +template +using EigenScalar = framework::EigenScalar; + +template +class ClipByNormKernel : public framework::OpKernel { + public: + void Compute(const framework::ExecutionContext& context) const override { + auto max_norm = context.Attr("max_norm"); + auto* input = context.Input("X"); + auto* output = context.Output("Out"); + output->mutable_data(context.GetPlace()); + + auto x = EigenVector::Flatten(*input); + auto out = EigenVector::Flatten(*output); + auto x_norm = x.square().sum().sqrt(); + auto place = context.GetEigenDevice(); + + auto temp = (x_norm <= max_norm).template cast().eval(); + auto scaling = temp + (static_cast(1) - temp) * max_norm / x_norm; + Eigen::array one_dim{{1}}; + Eigen::DSizes m_dsize(input->numel()); + out.device(place) = x * scaling.reshape(one_dim).broadcast(m_dsize); + } +}; + +} // namespace operators +} // namespace paddle diff --git a/python/paddle/v2/framework/tests/test_clip_by_norm_op.py b/python/paddle/v2/framework/tests/test_clip_by_norm_op.py new file mode 100644 index 0000000000..bf4f1a794c --- /dev/null +++ b/python/paddle/v2/framework/tests/test_clip_by_norm_op.py @@ -0,0 +1,52 @@ +import unittest +import numpy as np +from op_test import OpTest + + +class TestClipByNormOp(OpTest): + def setUp(self): + self.max_relative_error = 0.006 + self.initTestCase() + input = np.random.random(self.shape).astype("float32") + input[np.abs(input) < self.max_relative_error] = 0.5 + self.op_type = "clip_by_norm" + self.inputs = {'X': input, } + self.attrs = {} + self.attrs['max_norm'] = self.max_norm + norm = np.sqrt(np.sum(np.square(input))) + if norm > self.max_norm: + output = self.max_norm * input / norm + else: + output = input + self.outputs = { + 'Out': output + } + + def test_check_output(self): + self.check_output() + + def initTestCase(self): + self.shape = (100,) + self.max_norm = 1.0 + + +class TestCase1(TestClipByNormOp): + def initTestCase(self): + self.shape = (100,) + self.max_norm = 1e20 + + +class TestCase2(TestClipByNormOp): + def initTestCase(self): + self.shape = (16, 16) + self.max_norm = 0.1 + + +class TestCase3(TestClipByNormOp): + def initTestCase(self): + self.shape = (4, 8, 16) + self.max_norm = 1.0 + + +if __name__ == '__main__': + unittest.main() From 34d68f24fc5890341a47a124aaa7ed76fc5c12c1 Mon Sep 17 00:00:00 2001 From: wwhu Date: Fri, 3 Nov 2017 15:24:34 +0800 Subject: [PATCH 02/41] fix doc and code style --- paddle/operators/clip_by_norm_op.cc | 33 ++++--------------- paddle/operators/clip_by_norm_op.cu | 5 ++- paddle/operators/clip_by_norm_op.h | 3 -- .../framework/tests/test_clip_by_norm_op.py | 8 ++--- 4 files changed, 12 insertions(+), 37 deletions(-) diff --git a/paddle/operators/clip_by_norm_op.cc b/paddle/operators/clip_by_norm_op.cc index 440542d331..b0ca53b525 100644 --- a/paddle/operators/clip_by_norm_op.cc +++ b/paddle/operators/clip_by_norm_op.cc @@ -39,15 +39,14 @@ template class ClipByNormOpMaker : public framework::OpProtoAndCheckerMaker { public: ClipByNormOpMaker(framework::OpProto* proto, - framework::OpAttrChecker* op_checker) + framework::OpAttrChecker* op_checker) : OpProtoAndCheckerMaker(proto, op_checker) { AddInput("X", - "(Tensor)The input of clip_by_norm op." + "(Tensor) The input of clip_by_norm op." "The number of dimensions must be between [1, 9]."); AddOutput("Out", - "(Tensor)The output of clip_by_norm op with shape as input(X)"); - AddAttr( - "max_norm", "(float)The maximum norm value."); + "(Tensor) The output of clip_by_norm op with shape as input(X)"); + AddAttr("max_norm", "(float)The maximum norm value."); AddComment(R"DOC( ClipByNorm operator limits the L2 norm of the input 'X' within 'max_norm'. If the L2 norm of 'X' is less than or equal to 'max_norm', 'Out' will be @@ -62,29 +61,11 @@ where norm('X') represents the L2 norm of 'X'. } }; -class ClipByNormOpGrad : public framework::OperatorWithKernel { - public: - using framework::OperatorWithKernel::OperatorWithKernel; - - protected: - void InferShape(framework::InferShapeContext* ctx) const override { - PADDLE_ENFORCE(ctx->HasInput("X"), "Input(X) should not be null"); - PADDLE_ENFORCE(ctx->HasInput(framework::GradVarName("Out")), - "Input(Out@GRAD) should not be null"); - auto x_dims = ctx->GetInputDim("X"); - if (ctx->HasOutput(framework::GradVarName("X"))) { - ctx->SetOutputDim(framework::GradVarName("X"), x_dims); - } - } -}; - } // namespace operators } // namespace paddle namespace ops = paddle::operators; -REGISTER_OP_WITHOUT_GRADIENT(clip_by_norm, - ops::ClipByNormOp, +REGISTER_OP_WITHOUT_GRADIENT(clip_by_norm, ops::ClipByNormOp, ops::ClipByNormOpMaker); -REGISTER_OP_CPU_KERNEL(clip_by_norm, - ops::ClipByNormKernel - ); +REGISTER_OP_CPU_KERNEL( + clip_by_norm, ops::ClipByNormKernel); diff --git a/paddle/operators/clip_by_norm_op.cu b/paddle/operators/clip_by_norm_op.cu index 5f363b999f..2593a24ebb 100644 --- a/paddle/operators/clip_by_norm_op.cu +++ b/paddle/operators/clip_by_norm_op.cu @@ -15,6 +15,5 @@ #include "paddle/operators/clip_by_norm_op.h" namespace ops = paddle::operators; -REGISTER_OP_GPU_KERNEL(clip_by_norm, - ops::ClipByNormKernel - ); +REGISTER_OP_GPU_KERNEL( + clip_by_norm, ops::ClipByNormKernel); diff --git a/paddle/operators/clip_by_norm_op.h b/paddle/operators/clip_by_norm_op.h index 6f5f8c20bf..b26476cae9 100644 --- a/paddle/operators/clip_by_norm_op.h +++ b/paddle/operators/clip_by_norm_op.h @@ -25,9 +25,6 @@ using Tensor = framework::Tensor; template using EigenVector = framework::EigenVector; -template -using EigenScalar = framework::EigenScalar; template class ClipByNormKernel : public framework::OpKernel { diff --git a/python/paddle/v2/framework/tests/test_clip_by_norm_op.py b/python/paddle/v2/framework/tests/test_clip_by_norm_op.py index bf4f1a794c..02f6108a3a 100644 --- a/python/paddle/v2/framework/tests/test_clip_by_norm_op.py +++ b/python/paddle/v2/framework/tests/test_clip_by_norm_op.py @@ -18,21 +18,19 @@ class TestClipByNormOp(OpTest): output = self.max_norm * input / norm else: output = input - self.outputs = { - 'Out': output - } + self.outputs = {'Out': output} def test_check_output(self): self.check_output() def initTestCase(self): - self.shape = (100,) + self.shape = (100, ) self.max_norm = 1.0 class TestCase1(TestClipByNormOp): def initTestCase(self): - self.shape = (100,) + self.shape = (100, ) self.max_norm = 1e20 From 59cbaf9fe75e054afee290a9037248c4657c66d6 Mon Sep 17 00:00:00 2001 From: wwhu Date: Fri, 3 Nov 2017 16:12:45 +0800 Subject: [PATCH 03/41] fix doc --- paddle/operators/clip_by_norm_op.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paddle/operators/clip_by_norm_op.cc b/paddle/operators/clip_by_norm_op.cc index b0ca53b525..ebb7bdda55 100644 --- a/paddle/operators/clip_by_norm_op.cc +++ b/paddle/operators/clip_by_norm_op.cc @@ -46,7 +46,7 @@ class ClipByNormOpMaker : public framework::OpProtoAndCheckerMaker { "The number of dimensions must be between [1, 9]."); AddOutput("Out", "(Tensor) The output of clip_by_norm op with shape as input(X)"); - AddAttr("max_norm", "(float)The maximum norm value."); + AddAttr("max_norm", "(float) The maximum norm value."); AddComment(R"DOC( ClipByNorm operator limits the L2 norm of the input 'X' within 'max_norm'. If the L2 norm of 'X' is less than or equal to 'max_norm', 'Out' will be From 2f3665e988502d2574849af126f5688cf4f1abca Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Mon, 6 Nov 2017 13:25:57 +0800 Subject: [PATCH 04/41] update reset script for benchmark --- benchmark/paddle/image/resnet.py | 213 +++++++++++++++++++++++++++ benchmark/paddle/image/run_mkldnn.sh | 35 +++-- 2 files changed, 233 insertions(+), 15 deletions(-) create mode 100644 benchmark/paddle/image/resnet.py diff --git a/benchmark/paddle/image/resnet.py b/benchmark/paddle/image/resnet.py new file mode 100644 index 0000000000..6ae1857642 --- /dev/null +++ b/benchmark/paddle/image/resnet.py @@ -0,0 +1,213 @@ +#!/usr/bin/env python +from paddle.trainer_config_helpers import * + +height = 224 +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} +define_py_data_sources2( + "train.list", None, module="provider", obj="process", args=args) + +settings( + batch_size=batch_size, + learning_rate=0.01 / batch_size, + learning_method=MomentumOptimizer(0.9), + regularization=L2Regularization(0.0005 * batch_size)) + + +#######################Network Configuration ############# +def conv_bn_layer(name, + input, + filter_size, + num_filters, + stride, + padding, + channels=None, + active_type=ReluActivation()): + """ + A wrapper for conv layer with batch normalization layers. + Note: + conv layer has no activation. + """ + + tmp = img_conv_layer( + name=name + "_conv", + input=input, + filter_size=filter_size, + num_channels=channels, + num_filters=num_filters, + stride=stride, + padding=padding, + act=LinearActivation(), + bias_attr=False) + return batch_norm_layer( + name=name + "_bn", input=tmp, act=active_type, use_global_stats=is_test) + + +def bottleneck_block(name, input, num_filters1, num_filters2): + """ + A wrapper for bottlenect building block in ResNet. + Last conv_bn_layer has no activation. + Addto layer has activation of relu. + """ + last_name = conv_bn_layer( + name=name + '_branch2a', + input=input, + filter_size=1, + num_filters=num_filters1, + stride=1, + padding=0) + last_name = conv_bn_layer( + name=name + '_branch2b', + input=last_name, + filter_size=3, + num_filters=num_filters1, + stride=1, + padding=1) + last_name = conv_bn_layer( + name=name + '_branch2c', + input=last_name, + filter_size=1, + num_filters=num_filters2, + stride=1, + padding=0, + active_type=LinearActivation()) + + return addto_layer( + name=name + "_addto", input=[input, last_name], act=ReluActivation()) + + +def mid_projection(name, input, num_filters1, num_filters2, stride=2): + """ + A wrapper for middile projection in ResNet. + projection shortcuts are used for increasing dimensions, + and other shortcuts are identity + branch1: projection shortcuts are used for increasing + dimensions, has no activation. + branch2x: bottleneck building block, shortcuts are identity. + """ + # stride = 2 + branch1 = conv_bn_layer( + name=name + '_branch1', + input=input, + filter_size=1, + num_filters=num_filters2, + stride=stride, + padding=0, + active_type=LinearActivation()) + + last_name = conv_bn_layer( + name=name + '_branch2a', + input=input, + filter_size=1, + num_filters=num_filters1, + stride=stride, + padding=0) + last_name = conv_bn_layer( + name=name + '_branch2b', + input=last_name, + filter_size=3, + num_filters=num_filters1, + stride=1, + padding=1) + + last_name = conv_bn_layer( + name=name + '_branch2c', + input=last_name, + filter_size=1, + num_filters=num_filters2, + stride=1, + padding=0, + active_type=LinearActivation()) + + return addto_layer( + name=name + "_addto", input=[branch1, last_name], act=ReluActivation()) + + +img = data_layer(name='image', size=height * width * 3) + + +def deep_res_net(res2_num=3, res3_num=4, res4_num=6, res5_num=3): + """ + A wrapper for 50,101,152 layers of ResNet. + res2_num: number of blocks stacked in conv2_x + res3_num: number of blocks stacked in conv3_x + res4_num: number of blocks stacked in conv4_x + res5_num: number of blocks stacked in conv5_x + """ + # For ImageNet + # conv1: 112x112 + tmp = conv_bn_layer( + "conv1", + input=img, + filter_size=7, + channels=3, + num_filters=64, + stride=2, + padding=3) + tmp = img_pool_layer(name="pool1", input=tmp, pool_size=3, stride=2) + + # conv2_x: 56x56 + tmp = mid_projection( + name="res2_1", input=tmp, num_filters1=64, num_filters2=256, stride=1) + for i in xrange(2, res2_num + 1, 1): + tmp = bottleneck_block( + name="res2_" + str(i), input=tmp, num_filters1=64, num_filters2=256) + + # conv3_x: 28x28 + tmp = mid_projection( + name="res3_1", input=tmp, num_filters1=128, num_filters2=512) + for i in xrange(2, res3_num + 1, 1): + tmp = bottleneck_block( + name="res3_" + str(i), + input=tmp, + num_filters1=128, + num_filters2=512) + + # conv4_x: 14x14 + tmp = mid_projection( + name="res4_1", input=tmp, num_filters1=256, num_filters2=1024) + for i in xrange(2, res4_num + 1, 1): + tmp = bottleneck_block( + name="res4_" + str(i), + input=tmp, + num_filters1=256, + num_filters2=1024) + + # conv5_x: 7x7 + tmp = mid_projection( + name="res5_1", input=tmp, num_filters1=512, num_filters2=2048) + for i in xrange(2, res5_num + 1, 1): + tmp = bottleneck_block( + name="res5_" + str(i), + input=tmp, + num_filters1=512, + num_filters2=2048) + + tmp = img_pool_layer( + name='avgpool', + input=tmp, + pool_size=7, + stride=1, + pool_type=AvgPooling()) + + return fc_layer(input=tmp, size=num_class, act=SoftmaxActivation()) + + +if layer_num == 50: + resnet = deep_res_net(3, 4, 6, 3) +elif layer_num == 101: + resnet = deep_res_net(3, 4, 23, 3) +elif layer_num == 152: + resnet = deep_res_net(3, 8, 36, 3) +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) diff --git a/benchmark/paddle/image/run_mkldnn.sh b/benchmark/paddle/image/run_mkldnn.sh index e31fec1cd8..4a19601507 100755 --- a/benchmark/paddle/image/run_mkldnn.sh +++ b/benchmark/paddle/image/run_mkldnn.sh @@ -3,24 +3,26 @@ set -e function train() { unset OMP_NUM_THREADS MKL_NUM_THREADS export OMP_DYNAMIC="FALSE" + # TODO(TJ): auto 1.0 or 0,0 for HT on or off export KMP_AFFINITY="granularity=fine,compact,0,0" topology=$1 - bs=$2 - use_mkldnn=$3 - if [ $3 == "True" ]; then + layer_num=$2 + bs=$3 + use_mkldnn=$4 + if [ $4 == "True" ]; then thread=1 - log="logs/${topology}-mkldnn-${bs}.log" - elif [ $3 == "False" ]; then + log="logs/${topology}-${layer_num}-mkldnn-${bs}.log" + elif [ $4 == "False" ]; then thread=`nproc` # each trainer_count use only 1 core to avoid conflict export OMP_NUM_THREADS=1 export MKL_NUM_THREADS=1 - log="logs/${topology}-${thread}mklml-${bs}.log" + log="logs/${topology}-${layer_num}-${thread}mklml-${bs}.log" else echo "Wrong input $3, use True or False." exit 0 fi - args="batch_size=${bs}" + args="batch_size=${bs},layer_num=${layer_num}" config="${topology}.py" paddle train --job=time \ --config=$config \ @@ -40,12 +42,15 @@ if [ ! -d "logs" ]; then mkdir logs fi -#========== mkldnn ==========# -train vgg 64 True -train vgg 128 True -train vgg 256 True +for use_mkldnn in True False; do + for batchsize in 64 128 256; do + # vgg-19 and vgg-16 + train vgg 19 $batchsize $use_mkldnn + train vgg 16 $batchsize $use_mkldnn -#========== mklml ===========# -train vgg 64 False -train vgg 128 False -train vgg 256 False + # resnet-50, 101 and 152 + train resnet 50 $batchsize $use_mkldnn + train resnet 101 $batchsize $use_mkldnn + train resnet 152 $batchsize $use_mkldnn + done +done From d34780e1931c05b1ab98664be102b1d69b030729 Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Tue, 7 Nov 2017 11:44:53 +0800 Subject: [PATCH 05/41] fix issue for resnet --- paddle/gserver/layers/MKLDNNFcLayer.cpp | 6 ++---- paddle/gserver/layers/MKLDNNLayer.cpp | 14 +++++--------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/paddle/gserver/layers/MKLDNNFcLayer.cpp b/paddle/gserver/layers/MKLDNNFcLayer.cpp index d82063a713..3429c53d23 100644 --- a/paddle/gserver/layers/MKLDNNFcLayer.cpp +++ b/paddle/gserver/layers/MKLDNNFcLayer.cpp @@ -60,18 +60,16 @@ void MKLDNNFcLayer::convertWeightsFromPaddle() { } CHECK(wgtVal_) << "should have been initialized"; - bool hasNoSpatial_ = ih_ == 1 && iw_ == 1; auto targetDim = wgtVal_->getDims(); - auto srcFmt = hasNoSpatial_ ? format::io : format::ihwo; + auto srcFmt = targetDim.size() == 2 ? format::io : format::ihwo; wgtVal_->reorderDataFrom(wgtVal_, srcFmt, targetDim); hasInitedWgt_ = true; } void MKLDNNFcLayer::convertWeightsToPaddle() { CHECK(wgtVal_) << "should have been initialized"; - bool hasNoSpatial_ = ih_ == 1 && iw_ == 1; auto targetDim = wgtVal_->getDims(); - auto dstFmt = hasNoSpatial_ ? format::io : format::ihwo; + auto dstFmt = targetDim.size() == 2 ? format::io : format::ihwo; wgtVal_->reorderDataTo(wgtVal_, dstFmt, targetDim); } diff --git a/paddle/gserver/layers/MKLDNNLayer.cpp b/paddle/gserver/layers/MKLDNNLayer.cpp index 5fd62f4f73..82ef344c7b 100644 --- a/paddle/gserver/layers/MKLDNNLayer.cpp +++ b/paddle/gserver/layers/MKLDNNLayer.cpp @@ -181,21 +181,17 @@ void MKLDNNLayer::resetInValue( auto extPD = MKLDNNMatrix::createPrimitiveDesc( {bs_, ic_, ih_, iw_}, format::nchw, engine_); const MatrixPtr& inMat = inputLayers_[inputIdx]->getOutputValue(); - in = std::dynamic_pointer_cast(inMat); - CHECK_EQ(inputIsOnlyMKLDNN(), in != nullptr); - if (in == nullptr || in->getFormat() == format::nc) { - in = MKLDNNMatrix::create(extPD, inMat); - } - extInVal_ = isPaddleFormat(in->getFormat()) ? in : nullptr; - if (in->getFormat() == format::nc) { - CHECK(ih_ == 1 && iw_ == 1); + extInVal_ = std::dynamic_pointer_cast(inMat); + CHECK_EQ(inputIsOnlyMKLDNN(), extInVal_ != nullptr); + if (extInVal_ == nullptr || extInVal_->getFormat() == format::nc) { + extInVal_ = MKLDNNMatrix::create(extPD, inMat); } + in = extInVal_; if (nullptr == intPD || in->getPrimitiveDesc() == *intPD) { return; } // need create reorder in = MKLDNNMatrix::create(*intPD); - extInVal_ = extInVal_ ? extInVal_ : MKLDNNMatrix::create(extPD, inMat); cvtInVal_ = MKLDNNMatrix::createReorder(extInVal_, in); CHECK(cvtInVal_) << "should not be emptry"; } From 30b57eef402c2919c923b710ba0254f14d57055d Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Tue, 7 Nov 2017 11:51:23 +0800 Subject: [PATCH 06/41] auto KMP setting with HT --- benchmark/paddle/image/run_mkldnn.sh | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/benchmark/paddle/image/run_mkldnn.sh b/benchmark/paddle/image/run_mkldnn.sh index 4a19601507..68f3747e03 100755 --- a/benchmark/paddle/image/run_mkldnn.sh +++ b/benchmark/paddle/image/run_mkldnn.sh @@ -2,9 +2,6 @@ set -e function train() { unset OMP_NUM_THREADS MKL_NUM_THREADS - export OMP_DYNAMIC="FALSE" - # TODO(TJ): auto 1.0 or 0,0 for HT on or off - export KMP_AFFINITY="granularity=fine,compact,0,0" topology=$1 layer_num=$2 bs=$3 @@ -42,6 +39,17 @@ if [ ! -d "logs" ]; then mkdir logs fi +total_cores=`ls -l /sys/devices/system/cpu/ | grep "cpu[0-9]*$" | wc -l` +online_cores=`cat /sys/devices/system/cpu/cpu*/online | grep -o '1' | wc -l` +if [ $online_cores -eq $total_cores ]; then + echo "Hyper Threading is ON" + export KMP_AFFINITY="granularity=fine,compact,1,0" +else + echo "Hyper Threading is OFF" + export OMP_DYNAMIC="FALSE" + export KMP_AFFINITY="granularity=fine,compact,0,0" +fi + for use_mkldnn in True False; do for batchsize in 64 128 256; do # vgg-19 and vgg-16 From f1fac487b115670093bff9d1ef343ee4e466ce40 Mon Sep 17 00:00:00 2001 From: ranqiu Date: Tue, 7 Nov 2017 20:19:30 +0800 Subject: [PATCH 07/41] Update annotations of layers.py --- .../paddle/trainer_config_helpers/layers.py | 83 ++++++++++--------- 1 file changed, 44 insertions(+), 39 deletions(-) diff --git a/python/paddle/trainer_config_helpers/layers.py b/python/paddle/trainer_config_helpers/layers.py index 169e201046..0fd77a0be6 100644 --- a/python/paddle/trainer_config_helpers/layers.py +++ b/python/paddle/trainer_config_helpers/layers.py @@ -6548,26 +6548,27 @@ def switch_order_layer(input, @layer_support() def crop_layer(input, offset, axis=2, shape=None, name=None, layer_attr=None): """ - This layer crops images by offset and shape. User can set crop shape by - args 'shape' explicitly or by reference input layer. + This layer crops images according to the offset and shape. Users can set + the crop shape through the argument 'shape' explicitly or by specifying a + reference input layer. The example usage is: .. code-block:: python crop = crop_layer(input=[image_input, reference_input], axis=2, offset=[2, 3]) - :param input: The input of this layer. If two inputs are given, the second input - will be regarded as reference input. + :param input: The input of this layer. If two inputs are given, the second one + will be regarded as the reference. :type input: LayerOutput | Sequence :param offset: The crop offset. :type offset: Sequence - :param axis: start axis to be cropped. To image input layer: + :param axis: The start axis to be cropped. For image input layer: - 0: batch size - 1: channels - 2: height - 3: width - :type partial_sum: int - :param shape: The shape to be cropped. Default is None. + :type axis: int + :param shape: The shape to be cropped to. Default is None. :type shape: Sequence | None :param name: The name of this layer. It is optional. :type name: basestring @@ -6702,9 +6703,9 @@ def seq_slice_layer(input, starts, ends, name=None): :type name: basestring :param input: The input of this layer, which should be a sequence. :type input: LayerOutput - :param starts: start indices to slice the input sequence. + :param starts: The start indices to slice the input sequence. :type starts: LayerOutput | None - :param ends: end indices to slice the input sequence. + :param ends: The end indices to slice the input sequence. :type ends: LayerOutput | None :return: LayerOutput object. :rtype: LayerOutput @@ -6744,7 +6745,7 @@ def seq_slice_layer(input, starts, ends, name=None): @layer_support() def kmax_seq_score_layer(input, name=None, beam_size=1): """ - This layer accepts one input which are scores over a sequence or a nested + This layer accepts one input which is scores over a sequence or a nested sequence, and returns indices of beam_size sequences with highest scores. .. code-block:: python @@ -6754,11 +6755,11 @@ def kmax_seq_score_layer(input, name=None, beam_size=1): :param name: The name of this layer. It is optional. :type name: basestring - :param input: The input of this layer. It stores scores over a sequence or a nested - sequence and its size must be 1. + :param input: The input of this layer. It stores scores over a sequence or + a nested sequence and its size must be 1. :type input: LayerOutput - :param beam_size: sequence indices with top beam_size scores are returned. - :type beam_size: double + :param beam_size: The indices of the sequences with top beam_size scores are returned. + :type beam_size: int :return: LayerOutput object. :rtype: LayerOutput """ @@ -6814,38 +6815,42 @@ def img_conv3d_layer(input, :type name: basestring :param input: The input of this layer. :type input: LayerOutput - :param filter_size: The x dimension of a filter kernel. Or input a list. + :param filter_size: The dimensions of the filter kernel along three axises. If the parameter + is set to one integer, the three dimensions will be same. :type filter_size: int | tuple | list - :param num_filters: Each filter group's number of filter + :param num_filters: The number of filters in each group. + :type num_filters: int :param act: Activation type. ReluActivation is the default. :type act: BaseActivation - :param groups: Group size of filters. + :param groups: The number of the filter groups. :type groups: int - :param stride: The x dimension of the stride. Or input a tuple for two image - dimension. + :param stride: The strides of the convolution along three axises. If the parameter + is set to one integer, the three strides will be same. :type stride: int | tuple | list - :param padding: The x dimension of the padding. Or input a tuple for two - image dimension + :param padding: The numbers of padding along three axises. If the parameter is set to + one integer, they will be same. :type padding: int | tuple | list - :param bias_attr: Convolution bias attribute. None means default bias. - False means no bias. + :param bias_attr: The Bias Attribute. If the parameter is set to + False or something not type of ParameterAttribute, + no bias is defined. If the parameter is set to + True, the bias is initialized to zero. :type bias_attr: ParameterAttribute | None | bool | Any - :param num_channels: number of input channels. If None will be set - automatically from previous output. + :param num_channels: The number of input channels. If the parameter is not set or + set to None, its actual value will be automatically set to + the channels number of the input . :type num_channels: int - :param param_attr: Convolution param attribute. None means default attribute + :param param_attr: The parameter attribute of the convolution. :type param_attr: ParameterAttribute - :param shared_biases: Is biases will be shared between filters or not. + :param shared_biases: Whether biases will be shared between filters or not. :type shared_biases: bool - :param layer_attr: Layer Extra Attribute. + :param layer_attr: Extra layer attributes. :type layer_attr: ExtraLayerAttribute - :param trans: true if it is a convTransLayer, false if it is a convLayer + :param trans: True if it is a convTransLayer, False if it is a convLayer :type trans: bool - :param layer_type: specify the layer_type, default is None. If trans=True, - layer_type has to be "exconvt" or "cudnn_convt", - otherwise layer_type has to be either "exconv" or - "cudnn_conv" - :type layer_type: String + :param layer_type: Specify the layer_type. If the parameter is set, it must be "deconv3d" + when trans=True. If not set, it will be automatically set to "deconv3d" + when trans=True and "conv3d" when trans=False. + :type layer_type: basestring :return: LayerOutput object. :rtype: LayerOutput """ @@ -6927,7 +6932,7 @@ def img_conv3d_layer(input, def scale_shift_layer(input, name=None, param_attr=None, bias_attr=None): """ A layer applies a linear transformation to each element in each row of - the input matrix. For each element, the layer first re-scale it and then + the input matrix. For each element, the layer first re-scales it and then adds a bias to it. This layer is very like the SlopeInterceptLayer, except the scale and @@ -7001,12 +7006,12 @@ def sub_seq_layer(input, offsets, sizes, act=None, bias_attr=None, name=None): :type name: basestring :param input: The input of this layer, which should be sequence. :type input: LayerOutput - :param offsets: offset indices to slice the input sequence, which should be - sequence type. + :param offsets: The offset indices to slice the input sequence, which should + be sequence type. :type offsets: LayerOutput - :param sizes: sizes of the sub-sequences, which should be sequence type. + :param sizes: The sizes of the sub-sequences, which should be sequence type. :type sizes: LayerOutput - :param act: Layer activation, default is LinearActivation + :param act: Activation type, LinearActivation is the default. :type act: BaseActivation. :param bias_attr: The Bias Attribute. If the parameter is set to False or something not type of ParameterAttribute, From 714fa9e37c0425775952fd712671782ef695f00b Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Tue, 7 Nov 2017 20:22:19 +0800 Subject: [PATCH 08/41] remove some topology tests --- benchmark/paddle/image/run_mkldnn.sh | 6 ------ 1 file changed, 6 deletions(-) diff --git a/benchmark/paddle/image/run_mkldnn.sh b/benchmark/paddle/image/run_mkldnn.sh index 68f3747e03..4d1d3e1b56 100755 --- a/benchmark/paddle/image/run_mkldnn.sh +++ b/benchmark/paddle/image/run_mkldnn.sh @@ -52,13 +52,7 @@ fi for use_mkldnn in True False; do for batchsize in 64 128 256; do - # vgg-19 and vgg-16 train vgg 19 $batchsize $use_mkldnn - train vgg 16 $batchsize $use_mkldnn - - # resnet-50, 101 and 152 train resnet 50 $batchsize $use_mkldnn - train resnet 101 $batchsize $use_mkldnn - train resnet 152 $batchsize $use_mkldnn done done From 93e22e7b67c264448e6eacbf458dd146fd481115 Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Tue, 7 Nov 2017 22:20:57 +0800 Subject: [PATCH 09/41] enable bias for mkldnn_addto --- paddle/gserver/layers/MKLDNNAddtoLayer.cpp | 83 ++++++++++++++++++++-- paddle/gserver/layers/MKLDNNAddtoLayer.h | 22 +++++- paddle/gserver/tests/test_MKLDNN.cpp | 9 +-- 3 files changed, 99 insertions(+), 15 deletions(-) diff --git a/paddle/gserver/layers/MKLDNNAddtoLayer.cpp b/paddle/gserver/layers/MKLDNNAddtoLayer.cpp index 8eb700723f..9c13a23d48 100644 --- a/paddle/gserver/layers/MKLDNNAddtoLayer.cpp +++ b/paddle/gserver/layers/MKLDNNAddtoLayer.cpp @@ -62,16 +62,14 @@ void MKLDNNAddtoLayer::resetFwd(std::vector& pipeline, MKLDNNMatrixPtr& wgt, MKLDNNMatrixPtr& bias, MKLDNNMatrixPtr& out) { - if (biases_) { - LOG(FATAL) << "not implemented yet"; - } - resetFwdBuffers(inVals_, out); + resetFwdBuffers(inVals_, bias, out); in = inVals_[0]; std::shared_ptr fwdPD; - resetFwdPD(fwdPD, inVals_, out); + std::shared_ptr biasPD; + resetFwdPD(fwdPD, biasPD, inVals_, bias, out); - resetFwdPipeline(pipeline, fwdPD, inVals_, out); + resetFwdPipeline(pipeline, fwdPD, biasPD, inVals_, bias, out); } void MKLDNNAddtoLayer::resetBwd(std::vector& pipeline, @@ -79,7 +77,7 @@ void MKLDNNAddtoLayer::resetBwd(std::vector& pipeline, MKLDNNMatrixPtr& wgt, MKLDNNMatrixPtr& bias, MKLDNNMatrixPtr& out) { - resetBwdBuffers(inGrads_, out); + resetBwdBuffers(inGrads_, bias, out); in = inGrads_[0]; // backward only need share output grad to input grad @@ -89,6 +87,20 @@ void MKLDNNAddtoLayer::resetBwd(std::vector& pipeline, inputLayers_[i]->getOutputGrad()->setData(inGrads_[i]->getData()); } } + + // backward bias + bwdBias_ = nullptr; + if (bias) { + std::vector scales(bs_, 1.0); + std::vector srcPDs(bs_, bias->getPrimitiveDesc()); + auto biasPD = sum::primitive_desc(bias->getMemoryDesc(), scales, srcPDs); + std::vector srcs; + for (size_t i = 0; i < grads_.size(); ++i) { + srcs.push_back(*(grads_[i])); + } + bwdBias_.reset(new sum(biasPD, srcs, *bias)); + pipeline.push_back(*bwdBias_); + } } void MKLDNNAddtoLayer::updateWeights(const UpdateCallback& callback) { @@ -97,7 +109,25 @@ void MKLDNNAddtoLayer::updateWeights(const UpdateCallback& callback) { } } +void MKLDNNAddtoLayer::prepareBias(MKLDNNMatrixPtr& bias, + const MatrixPtr& biasMat, + const MKLDNNMatrixPtr& out, + std::vector& outs) { + auto pd = MKLDNNMatrix::createPrimitiveDesc( + {(int)layerSize_}, memory::format::x, engine_); + bias = MKLDNNMatrix::create(pd, biasMat); + outs.clear(); + real* data = out->getData(); + CHECK_EQ(bs_ * layerSize_, out->getElementCnt()); + for (int i = 0; i < bs_; ++i) { + MatrixPtr tmp = + Matrix::create(data + i * layerSize_, 1, layerSize_, false, false); + outs.push_back(MKLDNNMatrix::create(bias->getPrimitiveDesc(), tmp)); + } +} + void MKLDNNAddtoLayer::resetFwdBuffers(std::vector& inputs, + MKLDNNMatrixPtr& bias, MKLDNNMatrixPtr& out) { inputs.resize(inputLayers_.size()); for (size_t i = 0; i < inputs.size(); i++) { @@ -110,10 +140,18 @@ void MKLDNNAddtoLayer::resetFwdBuffers(std::vector& inputs, } resetOutValue(out, inputs[0]->getPrimitiveDesc()); + + if (biases_ && biases_->getW()) { + prepareBias(bias, biases_->getW(), out, vals_); + } else { + bias = nullptr; + } } void MKLDNNAddtoLayer::resetFwdPD(std::shared_ptr& pd, + std::shared_ptr& biasPD, std::vector& inputs, + MKLDNNMatrixPtr bias, MKLDNNMatrixPtr out) { std::vector scales(inputs.size(), 1.0); std::vector srcPDs; @@ -123,12 +161,23 @@ void MKLDNNAddtoLayer::resetFwdPD(std::shared_ptr& pd, CHECK(out); pd.reset(new sum::primitive_desc(out->getMemoryDesc(), scales, srcPDs)); CHECK_PRIMITIVE_DESC_EQ(out, pd->dst_primitive_desc()); + + biasPD = nullptr; + if (bias) { + std::vector scales(2, 1.0); + std::vector srcPDs(2, bias->getPrimitiveDesc()); + biasPD.reset( + new sum::primitive_desc(bias->getMemoryDesc(), scales, srcPDs)); + CHECK_PRIMITIVE_DESC_EQ(bias, biasPD->dst_primitive_desc()); + } } void MKLDNNAddtoLayer::resetFwdPipeline( std::vector& pipeline, std::shared_ptr& pd, + std::shared_ptr& biasPD, std::vector& inputs, + MKLDNNMatrixPtr& bias, MKLDNNMatrixPtr& out) { std::vector srcs; for (size_t i = 0; i < inputs.size(); i++) { @@ -136,9 +185,23 @@ void MKLDNNAddtoLayer::resetFwdPipeline( } fwd_.reset(new sum(*pd, srcs, *out)); pipeline.push_back(*fwd_); + + fwdBias_.clear(); + if (biasPD == nullptr || bias == nullptr) { + return; + } + fwdBias_.resize(vals_.size()); + for (size_t i = 0; i < vals_.size(); ++i) { + std::vector srcs; + srcs.push_back(*(vals_[i])); + srcs.push_back(*bias); + fwdBias_[i].reset(new sum(*biasPD, srcs, *vals_[i])); + pipeline.push_back(*fwdBias_[i]); + } } void MKLDNNAddtoLayer::resetBwdBuffers(std::vector& inputs, + MKLDNNMatrixPtr& bias, MKLDNNMatrixPtr& out) { CHECK(outVal_); resetOutGrad(out, outVal_->getPrimitiveDesc()); @@ -149,6 +212,12 @@ void MKLDNNAddtoLayer::resetBwdBuffers(std::vector& inputs, resetInGrad(inputs[i], inVal_->getPrimitiveDesc(), i); CHECK_PRIMITIVE_DESC_EQ(inputs[i], out->getPrimitiveDesc()); } + + if (biases_ && biases_->getWGrad()) { + prepareBias(bias, biases_->getWGrad(), out, grads_); + } else { + bias = nullptr; + } } } // namespace paddle diff --git a/paddle/gserver/layers/MKLDNNAddtoLayer.h b/paddle/gserver/layers/MKLDNNAddtoLayer.h index 15f74ec5bd..24504b7b4f 100644 --- a/paddle/gserver/layers/MKLDNNAddtoLayer.h +++ b/paddle/gserver/layers/MKLDNNAddtoLayer.h @@ -32,9 +32,15 @@ protected: // layer size == ic * ih * iw == oc * oh *ow, and can not be changed size_t layerSize_; - // TODO(TJ): this part has not been optimized by MKL-DNN std::unique_ptr biases_; + // buffers for adding bias + std::vector vals_; + std::vector grads_; + // primitives for adding bias + std::vector> fwdBias_; + std::shared_ptr bwdBias_; + public: explicit MKLDNNAddtoLayer(const LayerConfig& config) : MKLDNNLayer(config) {} @@ -91,20 +97,34 @@ protected: * reset pipeline. */ void resetFwdBuffers(std::vector& inputs, + MKLDNNMatrixPtr& bias, MKLDNNMatrixPtr& out); void resetFwdPD(std::shared_ptr& pd, + std::shared_ptr& biasPD, std::vector& inputs, + MKLDNNMatrixPtr bias, MKLDNNMatrixPtr out); void resetFwdPipeline(std::vector& pipeline, std::shared_ptr& pd, + std::shared_ptr& biasPD, std::vector& inputs, + MKLDNNMatrixPtr& bias, MKLDNNMatrixPtr& out); /** * Backward functions: reset buffers(inputs, output, bias) */ void resetBwdBuffers(std::vector& inputs, + MKLDNNMatrixPtr& bias, MKLDNNMatrixPtr& out); + + /** + * prepare for bias + */ + void prepareBias(MKLDNNMatrixPtr& bias, + const MatrixPtr& biasMat, + const MKLDNNMatrixPtr& out, + std::vector& outs); }; } // namespace paddle diff --git a/paddle/gserver/tests/test_MKLDNN.cpp b/paddle/gserver/tests/test_MKLDNN.cpp index 2e8d9f3333..3960d699ac 100644 --- a/paddle/gserver/tests/test_MKLDNN.cpp +++ b/paddle/gserver/tests/test_MKLDNN.cpp @@ -300,13 +300,8 @@ void testAddtoLayer(const testImageDesc& pm, const size_t nInputs) { TestConfig dnnConfig; getAddtoConfig(dnnConfig, pm, nInputs); dnnConfig.layerConfig.set_type("mkldnn_addto"); - // TODO(TJ): test with bias - for (auto withBias : {false}) { - if (withBias) { - dnnConfig.biasSize = pm.ic * pm.ih * pm.iw; - } else { - dnnConfig.biasSize = 0; - } + for (auto withBias : {false, true}) { + dnnConfig.biasSize = withBias ? pm.ic * pm.ih * pm.iw : 0; RUN_MKLDNN_TEST_LAYER(dnnConfig, "addto", pm) } } From 2dff98ca11a48afcceedbfb4ec6ead4eddff0118 Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Tue, 7 Nov 2017 23:03:11 +0800 Subject: [PATCH 10/41] remove auto setting from HT, since it's hard to unify with MacOS --- benchmark/paddle/image/run_mkldnn.sh | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/benchmark/paddle/image/run_mkldnn.sh b/benchmark/paddle/image/run_mkldnn.sh index 4d1d3e1b56..a4527e0496 100755 --- a/benchmark/paddle/image/run_mkldnn.sh +++ b/benchmark/paddle/image/run_mkldnn.sh @@ -2,6 +2,8 @@ set -e function train() { unset OMP_NUM_THREADS MKL_NUM_THREADS + export OMP_DYNAMIC="FALSE" + export KMP_AFFINITY="granularity=fine,compact,0,0" topology=$1 layer_num=$2 bs=$3 @@ -39,17 +41,6 @@ if [ ! -d "logs" ]; then mkdir logs fi -total_cores=`ls -l /sys/devices/system/cpu/ | grep "cpu[0-9]*$" | wc -l` -online_cores=`cat /sys/devices/system/cpu/cpu*/online | grep -o '1' | wc -l` -if [ $online_cores -eq $total_cores ]; then - echo "Hyper Threading is ON" - export KMP_AFFINITY="granularity=fine,compact,1,0" -else - echo "Hyper Threading is OFF" - export OMP_DYNAMIC="FALSE" - export KMP_AFFINITY="granularity=fine,compact,0,0" -fi - for use_mkldnn in True False; do for batchsize in 64 128 256; do train vgg 19 $batchsize $use_mkldnn From bbdac7f7d839df7ef7f4c4d3657bf350b161f3ab Mon Sep 17 00:00:00 2001 From: Yu Yang Date: Tue, 7 Nov 2017 13:56:50 -0800 Subject: [PATCH 11/41] Polish OpWithKernel * Chage `IndicateDataType` to `GetKernelType`. Make it easier to understand. * Change `OpKernelKey` to `OpKernelType` * Make operator developers can customize which kernel the operator will use in runtime. --- doc/design/float16.md | 2 +- paddle/framework/op_registry.h | 3 +- paddle/framework/operator.cc | 37 ++++++++- paddle/framework/operator.h | 79 +++++++------------ paddle/framework/operator_test.cc | 4 +- paddle/operators/accuracy_op.cc | 7 +- paddle/operators/auc_op.cc | 7 +- paddle/operators/batch_norm_op.cc | 6 +- paddle/operators/crf_decoding_op.cc | 6 +- paddle/operators/cross_entropy_op.cc | 12 ++- .../fill_constant_batch_size_like_op.cc | 6 +- paddle/operators/fill_constant_op.cc | 5 +- paddle/operators/gather_op.cc | 12 ++- paddle/operators/gaussian_random_op.cc | 6 +- paddle/operators/linear_chain_crf_op.cc | 15 ++-- paddle/operators/lookup_table_op.cc | 12 ++- paddle/operators/lstm_op.cc | 14 ++-- paddle/operators/multiplex_op.cc | 12 ++- paddle/operators/positive_negative_pair_op.cc | 6 +- paddle/operators/precision_recall_op.cc | 6 +- paddle/operators/scatter_op.cc | 12 ++- paddle/operators/sequence_pool_op.cc | 6 +- .../softmax_with_cross_entropy_op.cc | 14 ++-- paddle/operators/sum_op.cc | 16 ++-- paddle/operators/uniform_random_op.cc | 6 +- 25 files changed, 185 insertions(+), 126 deletions(-) diff --git a/doc/design/float16.md b/doc/design/float16.md index bc1c20c3d1..078801ba2e 100644 --- a/doc/design/float16.md +++ b/doc/design/float16.md @@ -55,6 +55,6 @@ After float16 class is available, some of the future items are below: - Update pybind/tensor_py.h to bind c++ float16 with numpy float16. -- Modify `IndicateDataType()` method in `framework/operator.h` to make it compatible with float16. +- Modify `GetKernelType()` method in `framework/operator.h` to make it compatible with float16. - Create a type-casting operator that can convert the data type in tensor between float16 and other types. diff --git a/paddle/framework/op_registry.h b/paddle/framework/op_registry.h index 2bb5e0e8ec..daade439e5 100644 --- a/paddle/framework/op_registry.h +++ b/paddle/framework/op_registry.h @@ -92,8 +92,7 @@ struct OpKernelRegistrarFunctor { void operator()(const char* op_type) const { using T = typename KERNEL_TYPE::ELEMENT_TYPE; - OperatorWithKernel::OpKernelKey key(ToDataType(std::type_index(typeid(T))), - PlaceType()); + OpKernelType key(ToDataType(std::type_index(typeid(T))), PlaceType()); OperatorWithKernel::AllOpKernels()[op_type][key].reset(new KERNEL_TYPE); constexpr auto size = std::tuple_size>::value; diff --git a/paddle/framework/operator.cc b/paddle/framework/operator.cc index 8150bf9239..3276f8af39 100644 --- a/paddle/framework/operator.cc +++ b/paddle/framework/operator.cc @@ -254,8 +254,7 @@ std::vector ExecutionContext::MultiOutput( return res; } -std::ostream& operator<<(std::ostream& os, - const OperatorWithKernel::OpKernelKey& kernel_key) { +std::ostream& operator<<(std::ostream& os, const OpKernelType& kernel_key) { os << "place[" << kernel_key.place_ << "]:data_type[" << kernel_key.data_type_ << "]"; return os; @@ -432,7 +431,7 @@ void OperatorWithKernel::Run(const Scope& scope, // check if op[type] have kernel for kernel_key OpKernelMap& kernels = kernels_iter->second; - auto kernel_key = OpKernelKey(IndicateDataType(ctx), dev_ctx); + auto kernel_key = GetKernelType(ctx); auto kernel_iter = kernels.find(kernel_key); if (kernel_iter == kernels.end()) { @@ -444,6 +443,38 @@ void OperatorWithKernel::Run(const Scope& scope, // throws errors if have. dev_ctx.Finish(); } +OpKernelType OperatorWithKernel::GetKernelType( + const ExecutionContext& ctx) const { + return OpKernelType(IndicateDataType(ctx), ctx.device_context()); +} +DataType OperatorWithKernel::IndicateDataType( + const ExecutionContext& ctx) const { + auto& scope = ctx.scope(); + int data_type = -1; + for (auto& input : this->inputs_) { + for (auto& ipt_name : input.second) { + auto* var = scope.FindVar(ipt_name); + if (var != nullptr) { + const Tensor* t = nullptr; + if (var->IsType()) { + t = &var->Get(); + } else if (var->IsType()) { + t = &var->Get(); + } else if (var->IsType()) { + t = &(var->Get().value()); + } + if (t != nullptr) { + int tmp = static_cast(ToDataType(t->type())); + PADDLE_ENFORCE(tmp == data_type || data_type == -1, + "DataType of Paddle Op %s must be the same.", Type()); + data_type = tmp; + } + } + } + } + PADDLE_ENFORCE(data_type != -1, "DataType should be indicated by input"); + return static_cast(data_type); +} } // namespace framework } // namespace paddle diff --git a/paddle/framework/operator.h b/paddle/framework/operator.h index a1303a9098..60861d9293 100644 --- a/paddle/framework/operator.h +++ b/paddle/framework/operator.h @@ -345,27 +345,10 @@ class OpKernel : public OpKernelBase { using ELEMENT_TYPE = T; }; -class OperatorWithKernel : public OperatorBase { - public: - struct OpKernelKey { - platform::Place place_; - DataType data_type_; - - OpKernelKey(DataType data_type, platform::Place place) - : place_(place), data_type_(data_type) {} - - OpKernelKey(DataType data_type, const platform::DeviceContext& dev_ctx) - : place_(dev_ctx.GetPlace()), data_type_(data_type) {} - - bool operator==(const OpKernelKey& o) const { - return platform::places_are_same_class(place_, o.place_) && - data_type_ == o.data_type_; - } - }; - - struct OpKernelHash { +struct OpKernelType { + struct Hash { std::hash hash_; - size_t operator()(const OpKernelKey& key) const { + size_t operator()(const OpKernelType& key) const { int place = key.place_.which(); int data_type = static_cast(key.data_type_); int pre_hash = data_type << NUM_PLACE_TYPE_LIMIT_IN_BIT | @@ -374,9 +357,26 @@ class OperatorWithKernel : public OperatorBase { } }; + platform::Place place_; + DataType data_type_; + + OpKernelType(DataType data_type, platform::Place place) + : place_(place), data_type_(data_type) {} + + OpKernelType(DataType data_type, const platform::DeviceContext& dev_ctx) + : place_(dev_ctx.GetPlace()), data_type_(data_type) {} + + bool operator==(const OpKernelType& o) const { + return platform::places_are_same_class(place_, o.place_) && + data_type_ == o.data_type_; + } +}; + +class OperatorWithKernel : public OperatorBase { + public: using OpKernelMap = - std::unordered_map, - OpKernelHash>; + std::unordered_map, + OpKernelType::Hash>; OperatorWithKernel(const std::string& type, const VariableNameMap& inputs, const VariableNameMap& outputs, const AttributeMap& attrs) @@ -404,40 +404,15 @@ class OperatorWithKernel : public OperatorBase { } protected: + virtual OpKernelType GetKernelType(const ExecutionContext& ctx) const; + + private: // indicate kernel DataType by input data. Defaultly all input data must be // same. - virtual DataType IndicateDataType(const ExecutionContext& ctx) const { - auto& scope = ctx.scope(); - int data_type = -1; - for (auto& input : this->inputs_) { - for (auto& ipt_name : input.second) { - auto* var = scope.FindVar(ipt_name); - if (var != nullptr) { - const Tensor* t = nullptr; - if (var->IsType()) { - t = &var->Get(); - } else if (var->IsType()) { - t = &var->Get(); - } else if (var->IsType()) { - t = &(var->Get().value()); - } - if (t != nullptr) { - int tmp = static_cast(ToDataType(t->type())); - PADDLE_ENFORCE(tmp == data_type || data_type == -1, - "DataType of Paddle Op %s must be the same.", - Type()); - data_type = tmp; - } - } - } - } - PADDLE_ENFORCE(data_type != -1, "DataType should be indicated by input"); - return static_cast(data_type); - } + DataType IndicateDataType(const ExecutionContext& ctx) const; }; -std::ostream& operator<<(std::ostream& os, - const OperatorWithKernel::OpKernelKey& kernel_key); +std::ostream& operator<<(std::ostream& os, const OpKernelType& kernel_key); extern bool OpSupportGPU(const std::string& op_type); diff --git a/paddle/framework/operator_test.cc b/paddle/framework/operator_test.cc index 42e0d52eed..1e19f82b34 100644 --- a/paddle/framework/operator_test.cc +++ b/paddle/framework/operator_test.cc @@ -114,8 +114,8 @@ class OpWithKernelTest : public OperatorWithKernel { protected: void InferShape(framework::InferShapeContext* ctx) const override {} - DataType IndicateDataType(const ExecutionContext& ctx) const override { - return DataType::FP32; + OpKernelType GetKernelType(const ExecutionContext& ctx) const override { + return OpKernelType(DataType::FP32, ctx.device_context()); } }; diff --git a/paddle/operators/accuracy_op.cc b/paddle/operators/accuracy_op.cc index eaafb9ad54..03c2fa945d 100644 --- a/paddle/operators/accuracy_op.cc +++ b/paddle/operators/accuracy_op.cc @@ -47,10 +47,11 @@ class AccuracyOp : public framework::OperatorWithKernel { } protected: - // IndicateDataType - framework::DataType IndicateDataType( + framework::OpKernelType GetKernelType( const framework::ExecutionContext &ctx) const override { - return framework::ToDataType(ctx.Input("Out")->type()); + return framework::OpKernelType( + framework::ToDataType(ctx.Input("Out")->type()), + ctx.device_context()); } }; diff --git a/paddle/operators/auc_op.cc b/paddle/operators/auc_op.cc index ccb969ab23..6c3f67ec32 100644 --- a/paddle/operators/auc_op.cc +++ b/paddle/operators/auc_op.cc @@ -39,10 +39,11 @@ class AucOp : public framework::OperatorWithKernel { } protected: - // IndicateDataType - framework::DataType IndicateDataType( + framework::OpKernelType GetKernelType( const framework::ExecutionContext &ctx) const override { - return framework::ToDataType(ctx.Input("Out")->type()); + return framework::OpKernelType( + framework::ToDataType(ctx.Input("Out")->type()), + ctx.device_context()); } }; diff --git a/paddle/operators/batch_norm_op.cc b/paddle/operators/batch_norm_op.cc index 7d73dfde78..8721ca3528 100644 --- a/paddle/operators/batch_norm_op.cc +++ b/paddle/operators/batch_norm_op.cc @@ -303,7 +303,8 @@ class BatchNormGradOp : public framework::OperatorWithKernel { ctx->SetOutputDim(framework::GradVarName("Bias"), {C}); } - framework::DataType IndicateDataType( + protected: + framework::OpKernelType GetKernelType( const framework::ExecutionContext &ctx) const override { const auto *var = ctx.InputVar(framework::GradVarName("Y")); if (var == nullptr) { @@ -318,7 +319,8 @@ class BatchNormGradOp : public framework::OperatorWithKernel { if (t == nullptr) { PADDLE_THROW("can't find Y@GRAD"); } - return framework::ToDataType(t->type()); + return framework::OpKernelType(framework::ToDataType(t->type()), + ctx.device_context()); } }; diff --git a/paddle/operators/crf_decoding_op.cc b/paddle/operators/crf_decoding_op.cc index d1ce74c4b9..f418f489c0 100644 --- a/paddle/operators/crf_decoding_op.cc +++ b/paddle/operators/crf_decoding_op.cc @@ -120,9 +120,11 @@ class CRFDecodingOp : public framework::OperatorWithKernel { } protected: - framework::DataType IndicateDataType( + framework::OpKernelType GetKernelType( const framework::ExecutionContext& ctx) const override { - return framework::ToDataType(ctx.Input("Emission")->type()); + return framework::OpKernelType( + framework::ToDataType(ctx.Input("Emission")->type()), + ctx.device_context()); } }; } // namespace operators diff --git a/paddle/operators/cross_entropy_op.cc b/paddle/operators/cross_entropy_op.cc index 9d41879b27..1e82742eaf 100644 --- a/paddle/operators/cross_entropy_op.cc +++ b/paddle/operators/cross_entropy_op.cc @@ -51,9 +51,11 @@ class CrossEntropyOp : public framework::OperatorWithKernel { protected: // Explicitly set that the data type of computation kernel of cross_entropy // is determined by its input "X". - framework::DataType IndicateDataType( + framework::OpKernelType GetKernelType( const framework::ExecutionContext& ctx) const override { - return framework::ToDataType(ctx.Input("X")->type()); + return framework::OpKernelType( + framework::ToDataType(ctx.Input("X")->type()), + ctx.device_context()); } }; @@ -98,9 +100,11 @@ class CrossEntropyGradientOp : public framework::OperatorWithKernel { protected: // Explicitly set that the data type of computation kernel of cross_entropy // is determined by its input "X". - framework::DataType IndicateDataType( + framework::OpKernelType GetKernelType( const framework::ExecutionContext& ctx) const override { - return framework::ToDataType(ctx.Input("X")->type()); + return framework::OpKernelType( + framework::ToDataType(ctx.Input("X")->type()), + ctx.device_context()); } }; diff --git a/paddle/operators/fill_constant_batch_size_like_op.cc b/paddle/operators/fill_constant_batch_size_like_op.cc index 232d88e26b..f86ee3c3d8 100644 --- a/paddle/operators/fill_constant_batch_size_like_op.cc +++ b/paddle/operators/fill_constant_batch_size_like_op.cc @@ -49,9 +49,11 @@ class FillConstantBatchSizeLikeOp : public framework::OperatorWithKernel { } protected: - framework::DataType IndicateDataType( + framework::OpKernelType GetKernelType( const framework::ExecutionContext &ctx) const override { - return static_cast(ctx.Attr("data_type")); + return framework::OpKernelType( + static_cast(ctx.Attr("data_type")), + ctx.device_context()); } }; diff --git a/paddle/operators/fill_constant_op.cc b/paddle/operators/fill_constant_op.cc index f60425051c..5a1cba51f8 100644 --- a/paddle/operators/fill_constant_op.cc +++ b/paddle/operators/fill_constant_op.cc @@ -33,11 +33,12 @@ class FillConstantOp : public framework::OperatorWithKernel { } protected: - framework::DataType IndicateDataType( + framework::OpKernelType GetKernelType( const framework::ExecutionContext &ctx) const override { int data_type = ctx.Attr("data_type"); VLOG(10) << " FillConstant data_type = " << data_type; - return static_cast(data_type); + return framework::OpKernelType(static_cast(data_type), + ctx.device_context()); } }; diff --git a/paddle/operators/gather_op.cc b/paddle/operators/gather_op.cc index aee672500e..8f80fb1625 100644 --- a/paddle/operators/gather_op.cc +++ b/paddle/operators/gather_op.cc @@ -40,9 +40,11 @@ class GatherOp : public framework::OperatorWithKernel { } protected: - framework::DataType IndicateDataType( + framework::OpKernelType GetKernelType( const framework::ExecutionContext& ctx) const override { - return framework::ToDataType(ctx.Input("X")->type()); + return framework::OpKernelType( + framework::ToDataType(ctx.Input("X")->type()), + ctx.device_context()); } }; @@ -55,9 +57,11 @@ class GatherGradOp : public framework::OperatorWithKernel { } protected: - framework::DataType IndicateDataType( + framework::OpKernelType GetKernelType( const framework::ExecutionContext& ctx) const override { - return framework::ToDataType(ctx.Input("X")->type()); + return framework::OpKernelType( + framework::ToDataType(ctx.Input("X")->type()), + ctx.device_context()); } }; diff --git a/paddle/operators/gaussian_random_op.cc b/paddle/operators/gaussian_random_op.cc index 802c98ae76..53ad86c6c4 100644 --- a/paddle/operators/gaussian_random_op.cc +++ b/paddle/operators/gaussian_random_op.cc @@ -57,9 +57,11 @@ class GaussianRandomOp : public framework::OperatorWithKernel { } protected: - framework::DataType IndicateDataType( + framework::OpKernelType GetKernelType( const framework::ExecutionContext& ctx) const override { - return static_cast(ctx.Attr("data_type")); + return framework::OpKernelType( + static_cast(ctx.Attr("data_type")), + ctx.device_context()); } }; diff --git a/paddle/operators/linear_chain_crf_op.cc b/paddle/operators/linear_chain_crf_op.cc index bcb48e13bd..066bdf67aa 100644 --- a/paddle/operators/linear_chain_crf_op.cc +++ b/paddle/operators/linear_chain_crf_op.cc @@ -183,9 +183,11 @@ class LinearChainCRFOp : public framework::OperatorWithKernel { protected: // Explicitly set that the data type of computation kernel of linear_chain_crf // is determined by its input "Emission". - framework::DataType IndicateDataType( + framework::OpKernelType GetKernelType( const framework::ExecutionContext& ctx) const override { - return framework::ToDataType(ctx.Input("Emission")->type()); + return framework::OpKernelType( + framework::ToDataType(ctx.Input("Emission")->type()), + ctx.device_context()); } }; @@ -240,10 +242,13 @@ class LinearChainCRFGradOp : public framework::OperatorWithKernel { protected: // Explicitly set that the data type of output of the linear_chain_crf_grad // operator is determined by its input: gradients of LogLikelihood. - framework::DataType IndicateDataType( + framework::OpKernelType GetKernelType( const framework::ExecutionContext& ctx) const override { - return framework::ToDataType( - ctx.Input(framework::GradVarName("LogLikelihood"))->type()); + return framework::OpKernelType( + framework::ToDataType( + ctx.Input(framework::GradVarName("LogLikelihood")) + ->type()), + ctx.device_context()); } }; diff --git a/paddle/operators/lookup_table_op.cc b/paddle/operators/lookup_table_op.cc index 2163c8ce4e..93e812ac5b 100644 --- a/paddle/operators/lookup_table_op.cc +++ b/paddle/operators/lookup_table_op.cc @@ -41,9 +41,11 @@ class LookupTableOp : public framework::OperatorWithKernel { } protected: - framework::DataType IndicateDataType( + framework::OpKernelType GetKernelType( const framework::ExecutionContext& ctx) const override { - return framework::ToDataType(ctx.Input("W")->type()); + return framework::OpKernelType( + framework::ToDataType(ctx.Input("W")->type()), + ctx.device_context()); } }; @@ -97,9 +99,11 @@ class LookupTableOpGrad : public framework::OperatorWithKernel { } protected: - framework::DataType IndicateDataType( + framework::OpKernelType GetKernelType( const framework::ExecutionContext& ctx) const override { - return framework::ToDataType(ctx.Input("W")->type()); + return framework::OpKernelType( + framework::ToDataType(ctx.Input("W")->type()), + ctx.device_context()); } }; diff --git a/paddle/operators/lstm_op.cc b/paddle/operators/lstm_op.cc index fdf52cf424..6b859dbbe7 100644 --- a/paddle/operators/lstm_op.cc +++ b/paddle/operators/lstm_op.cc @@ -84,10 +84,11 @@ class LSTMOp : public framework::OperatorWithKernel { } protected: - framework::DataType IndicateDataType( + framework::OpKernelType GetKernelType( const framework::ExecutionContext& ctx) const override { - return framework::ToDataType( - ctx.Input("Input")->type()); + return framework::OpKernelType( + framework::ToDataType(ctx.Input("Input")->type()), + ctx.device_context()); } }; @@ -245,10 +246,11 @@ class LSTMGradOp : public framework::OperatorWithKernel { } protected: - framework::DataType IndicateDataType( + framework::OpKernelType GetKernelType( const framework::ExecutionContext& ctx) const override { - return framework::ToDataType( - ctx.Input("Input")->type()); + return framework::OpKernelType( + framework::ToDataType(ctx.Input("Input")->type()), + ctx.device_context()); } }; diff --git a/paddle/operators/multiplex_op.cc b/paddle/operators/multiplex_op.cc index 234fddcfd5..f8527dfab3 100644 --- a/paddle/operators/multiplex_op.cc +++ b/paddle/operators/multiplex_op.cc @@ -51,9 +51,11 @@ class MultiplexOp : public framework::OperatorWithKernel { } protected: - framework::DataType IndicateDataType( + framework::OpKernelType GetKernelType( const framework::ExecutionContext& ctx) const override { - return framework::ToDataType(ctx.MultiInput("X")[0]->type()); + return framework::OpKernelType( + framework::ToDataType(ctx.MultiInput("X")[0]->type()), + ctx.device_context()); } }; @@ -107,9 +109,11 @@ class MultiplexGradOp : public framework::OperatorWithKernel { } protected: - framework::DataType IndicateDataType( + framework::OpKernelType GetKernelType( const framework::ExecutionContext& ctx) const override { - return framework::ToDataType(ctx.MultiInput("X")[0]->type()); + return framework::OpKernelType( + framework::ToDataType(ctx.MultiInput("X")[0]->type()), + ctx.device_context()); } }; diff --git a/paddle/operators/positive_negative_pair_op.cc b/paddle/operators/positive_negative_pair_op.cc index afbb63cc60..4ba40a62ec 100644 --- a/paddle/operators/positive_negative_pair_op.cc +++ b/paddle/operators/positive_negative_pair_op.cc @@ -85,9 +85,11 @@ class PositiveNegativePairOp : public framework::OperatorWithKernel { } protected: - framework::DataType IndicateDataType( + framework::OpKernelType GetKernelType( const framework::ExecutionContext &ctx) const override { - return framework::ToDataType(ctx.Input("Score")->type()); + return framework::OpKernelType( + framework::ToDataType(ctx.Input("Score")->type()), + ctx.device_context()); } }; diff --git a/paddle/operators/precision_recall_op.cc b/paddle/operators/precision_recall_op.cc index 641f7135de..1ace4f2a59 100644 --- a/paddle/operators/precision_recall_op.cc +++ b/paddle/operators/precision_recall_op.cc @@ -80,9 +80,11 @@ class PrecisionRecallOp : public framework::OperatorWithKernel { } protected: - framework::DataType IndicateDataType( + framework::OpKernelType GetKernelType( const framework::ExecutionContext &ctx) const override { - return framework::ToDataType(ctx.Input("MaxProbs")->type()); + return framework::OpKernelType( + framework::ToDataType(ctx.Input("MaxProbs")->type()), + ctx.device_context()); } }; diff --git a/paddle/operators/scatter_op.cc b/paddle/operators/scatter_op.cc index 62e6c70b45..ce4b794bc3 100644 --- a/paddle/operators/scatter_op.cc +++ b/paddle/operators/scatter_op.cc @@ -49,9 +49,11 @@ class ScatterOp : public framework::OperatorWithKernel { } protected: - framework::DataType IndicateDataType( + framework::OpKernelType GetKernelType( const framework::ExecutionContext& ctx) const override { - return framework::ToDataType(ctx.Input("Ref")->type()); + return framework::OpKernelType( + framework::ToDataType(ctx.Input("Ref")->type()), + ctx.device_context()); } }; @@ -66,9 +68,11 @@ class ScatterGradOp : public framework::OperatorWithKernel { } protected: - framework::DataType IndicateDataType( + framework::OpKernelType GetKernelType( const framework::ExecutionContext& ctx) const override { - return framework::ToDataType(ctx.Input("Ref")->type()); + return framework::OpKernelType( + framework::ToDataType(ctx.Input("Ref")->type()), + ctx.device_context()); } }; diff --git a/paddle/operators/sequence_pool_op.cc b/paddle/operators/sequence_pool_op.cc index 710f280017..2a000ac60b 100644 --- a/paddle/operators/sequence_pool_op.cc +++ b/paddle/operators/sequence_pool_op.cc @@ -107,9 +107,11 @@ class SequencePoolGradOp : public framework::OperatorWithKernel { } protected: - framework::DataType IndicateDataType( + framework::OpKernelType GetKernelType( const framework::ExecutionContext& ctx) const override { - return framework::ToDataType(ctx.Input("X")->type()); + return framework::OpKernelType( + framework::ToDataType(ctx.Input("X")->type()), + ctx.device_context()); } }; diff --git a/paddle/operators/softmax_with_cross_entropy_op.cc b/paddle/operators/softmax_with_cross_entropy_op.cc index c6b94f5cc9..ed96e8cee5 100644 --- a/paddle/operators/softmax_with_cross_entropy_op.cc +++ b/paddle/operators/softmax_with_cross_entropy_op.cc @@ -121,9 +121,11 @@ class SoftmaxWithCrossEntropyOp : public framework::OperatorWithKernel { } protected: - framework::DataType IndicateDataType( + framework::OpKernelType GetKernelType( const framework::ExecutionContext& ctx) const override { - return framework::ToDataType(ctx.Input("Logits")->type()); + return framework::OpKernelType( + framework::ToDataType(ctx.Input("Logits")->type()), + ctx.device_context()); } }; @@ -160,10 +162,12 @@ class SoftmaxWithCrossEntropyOpGrad : public framework::OperatorWithKernel { } protected: - framework::DataType IndicateDataType( + framework::OpKernelType GetKernelType( const framework::ExecutionContext& ctx) const override { - return framework::ToDataType( - ctx.Input(framework::GradVarName("Loss"))->type()); + return framework::OpKernelType( + framework::ToDataType( + ctx.Input(framework::GradVarName("Loss"))->type()), + ctx.device_context()); } }; diff --git a/paddle/operators/sum_op.cc b/paddle/operators/sum_op.cc index b1e58952fd..750f96296a 100644 --- a/paddle/operators/sum_op.cc +++ b/paddle/operators/sum_op.cc @@ -47,20 +47,24 @@ class SumOp : public framework::OperatorWithKernel { } protected: - framework::DataType IndicateDataType( + framework::OpKernelType GetKernelType( const framework::ExecutionContext& ctx) const override { auto x_vars = ctx.MultiInputVar("X"); if (x_vars[0]->IsType()) { - return framework::ToDataType( - x_vars[0]->Get().type()); + return framework::OpKernelType( + framework::ToDataType(x_vars[0]->Get().type()), + ctx.device_context()); } else if (x_vars[0]->IsType()) { - return framework::ToDataType( - x_vars[0]->Get().value().type()); + return framework::OpKernelType( + framework::ToDataType( + x_vars[0]->Get().value().type()), + ctx.device_context()); } else if (x_vars[0]->IsType()) { auto& array = x_vars[0]->Get(); for (auto& each : array) { if (each.numel() != 0) { - return framework::ToDataType(each.type()); + return framework::OpKernelType(framework::ToDataType(each.type()), + ctx.device_context()); } } } diff --git a/paddle/operators/uniform_random_op.cc b/paddle/operators/uniform_random_op.cc index cd22c561ac..7975efc7cf 100644 --- a/paddle/operators/uniform_random_op.cc +++ b/paddle/operators/uniform_random_op.cc @@ -63,9 +63,11 @@ class UniformRandomOp : public framework::OperatorWithKernel { } protected: - framework::DataType IndicateDataType( + framework::OpKernelType GetKernelType( const framework::ExecutionContext& ctx) const override { - return static_cast(ctx.Attr("data_type")); + return framework::OpKernelType( + static_cast(ctx.Attr("data_type")), + ctx.device_context()); } }; From db3b49fe0e32c516e2d51ecf13c5953c15664a17 Mon Sep 17 00:00:00 2001 From: Yu Yang Date: Tue, 7 Nov 2017 14:40:16 -0800 Subject: [PATCH 12/41] Add gtest for drnn --- paddle/operators/CMakeLists.txt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/paddle/operators/CMakeLists.txt b/paddle/operators/CMakeLists.txt index b497c877d1..4ae50655b2 100644 --- a/paddle/operators/CMakeLists.txt +++ b/paddle/operators/CMakeLists.txt @@ -191,8 +191,13 @@ op_library(sequence_pool_op DEPS sequence_pooling) op_library(lstm_op DEPS sequence2batch lstm_compute) op_library(conv_transpose_op DEPS vol2col) op_library(gru_op DEPS sequence2batch gru_compute) -op_library(dynamic_recurrent_op SRCS dynamic_recurrent_op.cc rnn/recurrent_op_utils.cc - DEPS net_op tensor_array) +if(WITH_TESTING) + op_library(dynamic_recurrent_op SRCS dynamic_recurrent_op.cc rnn/recurrent_op_utils.cc + DEPS net_op tensor_array gtest) +else() + op_library(dynamic_recurrent_op SRCS dynamic_recurrent_op.cc rnn/recurrent_op_utils.cc + DEPS net_op tensor_array) +endif() op_library(recurrent_op SRCS recurrent_op.cc DEPS executor) list(REMOVE_ITEM GENERAL_OPS ${DEPS_OPS}) From aadb098138efafc60eaa4b902db04f78db1e62b4 Mon Sep 17 00:00:00 2001 From: Yu Yang Date: Tue, 7 Nov 2017 15:13:36 -0800 Subject: [PATCH 13/41] Add `op::math::set_constant` without template --- paddle/operators/math/math_function.cc | 48 +++++++++++++++++++++ paddle/operators/math/math_function.cu | 24 +++++++++++ paddle/operators/math/math_function.h | 7 +++ paddle/operators/math/math_function_test.cc | 12 ++++++ 4 files changed, 91 insertions(+) diff --git a/paddle/operators/math/math_function.cc b/paddle/operators/math/math_function.cc index 2a9c09a0f1..175df2030d 100644 --- a/paddle/operators/math/math_function.cc +++ b/paddle/operators/math/math_function.cc @@ -13,6 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ #include "paddle/operators/math/math_function.h" +#include "paddle/framework/data_type.h" namespace paddle { namespace operators { @@ -233,6 +234,53 @@ void gemv(const platform::DeviceContext& context, template struct SetConstant; +struct TensorSetConstant { + TensorSetConstant(framework::Tensor* tensor, float value) + : tensor_(tensor), value_(value) {} + template + void operator()() const { + auto cpu = platform::CPUPlace(); + auto* begin = tensor_->mutable_data(cpu); + std::fill(begin, begin + tensor_->numel(), static_cast(value_)); + } + framework::Tensor* tensor_; + float value_; +}; + +template <> +void set_constant_with_place( + const platform::DeviceContext& context, framework::Tensor* tensor, + float value) { + framework::VisitDataType(framework::ToDataType(tensor->type()), + TensorSetConstant(tensor, value)); +} + +struct TensorSetConstantWithPlace : public boost::static_visitor { + TensorSetConstantWithPlace(const platform::DeviceContext& context, + framework::Tensor* tensor, float value) + : context_(context), tensor_(tensor), value_(value) {} + + template + void operator()(Place place) const { + set_constant_with_place(context_, tensor_, value_); + } + + const platform::DeviceContext& context_; + framework::Tensor* tensor_; + float value_; +}; + +void set_constant(const platform::DeviceContext& context, + framework::Tensor* tensor, float value) { +#ifdef PADDLE_WITH_CUDA + boost::apply_visitor(TensorSetConstantWithPlace(context, tensor, value), + tensor->place()); +#else + TensorSetConstantWithPlace func(context, tensor, value); + func(platform::CPUPlace()); +#endif +} + } // namespace math } // namespace operators } // namespace paddle diff --git a/paddle/operators/math/math_function.cu b/paddle/operators/math/math_function.cu index e6fd8bf235..3a216993ac 100644 --- a/paddle/operators/math/math_function.cu +++ b/paddle/operators/math/math_function.cu @@ -232,6 +232,30 @@ void gemv(const platform::DeviceContext& context, template struct SetConstant; +struct TensorSetConstant { + TensorSetConstant(const platform::DeviceContext& context, + framework::Tensor* tensor, float value) + : context_(context), tensor_(tensor), value_(value) {} + + template + void operator()() const { + SetConstant functor; + functor(context_, tensor_, static_cast(value_)); + } + + const platform::DeviceContext& context_; + framework::Tensor* tensor_; + float value_; +}; + +template <> +void set_constant_with_place( + const platform::DeviceContext& context, framework::Tensor* tensor, + float value) { + framework::VisitDataType(framework::ToDataType(tensor->type()), + TensorSetConstant(context, tensor, value)); +} + } // namespace math } // namespace operators } // namespace paddle diff --git a/paddle/operators/math/math_function.h b/paddle/operators/math/math_function.h index 3bb5aa0332..1c9eabb2b7 100644 --- a/paddle/operators/math/math_function.h +++ b/paddle/operators/math/math_function.h @@ -108,6 +108,13 @@ struct SetConstant { } }; +template +void set_constant_with_place(const platform::DeviceContext& context, + framework::Tensor* tensor, float value); + +void set_constant(const platform::DeviceContext& context, + framework::Tensor* tensor, float value); + } // namespace math } // namespace operators } // namespace paddle diff --git a/paddle/operators/math/math_function_test.cc b/paddle/operators/math/math_function_test.cc index 7d84ad9aad..983c9fdcff 100644 --- a/paddle/operators/math/math_function_test.cc +++ b/paddle/operators/math/math_function_test.cc @@ -139,3 +139,15 @@ TEST(math_function, gemv) { GemvTest(12, 7, true); GemvTest(7, 9, true); } + +TEST(math_funciton, set_constant) { + paddle::framework::Tensor t; + t.Resize({10, 10}); + t.mutable_data(paddle::platform::CPUPlace()); + auto* ctx = new paddle::platform::CPUDeviceContext(); + paddle::operators::math::set_constant(*ctx, &t, 10); + for (int64_t i = 0; i < t.numel(); ++i) { + PADDLE_ENFORCE_EQ(10, t.data()[i]); + } + delete ctx; +} From 5ee62383bd6f238994c0c8a949626aadb7c81c5a Mon Sep 17 00:00:00 2001 From: Yu Yang Date: Tue, 7 Nov 2017 15:20:43 -0800 Subject: [PATCH 14/41] Rewrite fill_constant op --- paddle/framework/data_type.h | 15 ++++++++ paddle/framework/ddim.cc | 7 ++++ paddle/framework/ddim.h | 2 + paddle/operators/fill_constant_op.cc | 56 ++++++++++++++++------------ paddle/operators/fill_constant_op.cu | 24 ------------ paddle/operators/fill_constant_op.h | 37 ------------------ 6 files changed, 57 insertions(+), 84 deletions(-) delete mode 100644 paddle/operators/fill_constant_op.cu delete mode 100644 paddle/operators/fill_constant_op.h diff --git a/paddle/framework/data_type.h b/paddle/framework/data_type.h index c5ae7b1854..3ec88d7a72 100644 --- a/paddle/framework/data_type.h +++ b/paddle/framework/data_type.h @@ -34,6 +34,21 @@ inline DataType ToDataType(std::type_index type) { } } +inline std::type_index ToTypeIndex(DataType type) { + switch (type) { + case DataType::FP32: + return typeid(float); + case DataType::FP64: + return typeid(double); + case DataType::INT32: + return typeid(int); + case DataType::INT64: + return typeid(int64_t); + default: + PADDLE_THROW("Not support type %d", type); + } +} + template inline void VisitDataType(DataType type, Visitor visitor) { switch (type) { diff --git a/paddle/framework/ddim.cc b/paddle/framework/ddim.cc index 239ae5e123..bc2c5b7b5f 100644 --- a/paddle/framework/ddim.cc +++ b/paddle/framework/ddim.cc @@ -79,6 +79,13 @@ DDim make_ddim(const std::vector& dims) { return result; } +DDim make_ddim(const std::vector& dims) { + std::vector res(dims.size()); + std::transform(dims.begin(), dims.end(), res.begin(), + [](int d) { return static_cast(d); }); + return make_ddim(res); +} + /// @cond HIDDEN // XXX For some reason, putting this in an anonymous namespace causes errors class DynamicMutableIndexer : public boost::static_visitor { diff --git a/paddle/framework/ddim.h b/paddle/framework/ddim.h index 2a5e2d2b69..19b841fbb3 100644 --- a/paddle/framework/ddim.h +++ b/paddle/framework/ddim.h @@ -81,6 +81,8 @@ struct DDim { */ DDim make_ddim(const std::vector& dims); +DDim make_ddim(const std::vector& dims); + /** * \brief Make a DDim from an initializer list * diff --git a/paddle/operators/fill_constant_op.cc b/paddle/operators/fill_constant_op.cc index f60425051c..818f113b90 100644 --- a/paddle/operators/fill_constant_op.cc +++ b/paddle/operators/fill_constant_op.cc @@ -12,32 +12,41 @@ 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/fill_constant_op.h" +#include "paddle/framework/data_type.h" +#include "paddle/framework/op_registry.h" +#include "paddle/operators/math/math_function.h" namespace paddle { namespace operators { -class FillConstantOp : public framework::OperatorWithKernel { +class FillConstantInferShape : public framework::InferShapeBase { public: - using framework::OperatorWithKernel::OperatorWithKernel; - - void InferShape(framework::InferShapeContext *ctx) const override { + void operator()(framework::InferShapeContext *ctx) const override { PADDLE_ENFORCE(ctx->HasOutput("Out"), "Output(Out) of FillConstantOp should not be null."); auto &shape = ctx->Attrs().Get>("shape"); - std::vector shape_int64(shape.size(), 0); - std::transform(shape.begin(), shape.end(), shape_int64.begin(), - [](int a) { return static_cast(a); }); - auto dims = framework::make_ddim(shape_int64); - ctx->SetOutputDim("Out", dims); + ctx->SetOutputDim("Out", framework::make_ddim(shape)); } +}; - protected: - framework::DataType IndicateDataType( - const framework::ExecutionContext &ctx) const override { - int data_type = ctx.Attr("data_type"); - VLOG(10) << " FillConstant data_type = " << data_type; - return static_cast(data_type); +class FillConstantOp : public framework::OperatorBase { + public: + using framework::OperatorBase::OperatorBase; + void Run(const framework::Scope &scope, + const platform::DeviceContext &dev_ctx) const override { + auto data_type = static_cast(Attr("data_type")); + auto value = Attr("value"); + auto force_cpu = Attr("force_cpu"); + auto &out = + *scope.FindVar(Output("Out"))->GetMutable(); + out.Resize(framework::make_ddim(Attr>("shape"))); + if (force_cpu) { + auto cpu = platform::CPUPlace(); + out.mutable_data(cpu, framework::ToTypeIndex(data_type)); + } else { + out.mutable_data(dev_ctx.GetPlace(), framework::ToTypeIndex(data_type)); + } + math::set_constant(dev_ctx, &out, value); } }; @@ -53,6 +62,11 @@ class FillConstantOpMaker : public framework::OpProtoAndCheckerMaker { AddAttr>("shape", "(vector) The shape of the output"); AddAttr("value", "(float, default 0) The value to be filled") .SetDefault(0.0f); + AddAttr("force_cpu", + "(bool, default false) Force fill output variable to cpu " + "memory. Otherwise, fill output variable to the running " + "device") + .SetDefault(false); AddOutput("Out", "(Tensor) Tensor of specified shape will be filled " "with the specified value"); @@ -68,10 +82,6 @@ Fill up a variable with specified constant value. } // namespace paddle namespace ops = paddle::operators; -REGISTER_OP_WITHOUT_GRADIENT(fill_constant, ops::FillConstantOp, - ops::FillConstantOpMaker); -REGISTER_OP_CPU_KERNEL( - fill_constant, ops::FillConstantOpKernel, - ops::FillConstantOpKernel, - ops::FillConstantOpKernel, - ops::FillConstantOpKernel); +REGISTER_OPERATOR(fill_constant, ops::FillConstantOp, + ops::FillConstantInferShape, ops::FillConstantOpMaker, + paddle::framework::EmptyGradOpMaker); diff --git a/paddle/operators/fill_constant_op.cu b/paddle/operators/fill_constant_op.cu deleted file mode 100644 index bca402a8b9..0000000000 --- a/paddle/operators/fill_constant_op.cu +++ /dev/null @@ -1,24 +0,0 @@ -/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. */ - -#define EIGEN_USE_GPU -#include "paddle/framework/op_registry.h" -#include "paddle/operators/fill_constant_op.h" - -namespace ops = paddle::operators; -REGISTER_OP_GPU_KERNEL( - fill_constant, ops::FillConstantOpKernel, - ops::FillConstantOpKernel, - ops::FillConstantOpKernel, - ops::FillConstantOpKernel); diff --git a/paddle/operators/fill_constant_op.h b/paddle/operators/fill_constant_op.h deleted file mode 100644 index 3668f42f1c..0000000000 --- a/paddle/operators/fill_constant_op.h +++ /dev/null @@ -1,37 +0,0 @@ -/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. */ - -#pragma once -#include "paddle/framework/eigen.h" -#include "paddle/framework/op_registry.h" - -namespace paddle { -namespace operators { - -template -class FillConstantOpKernel : public framework::OpKernel { - public: - void Compute(const framework::ExecutionContext& ctx) const override { - auto* out = ctx.Output("Out"); - out->mutable_data(ctx.GetPlace()); - auto value = ctx.Attr("value"); - - auto out_eigen = framework::EigenVector::Flatten(*out); - auto place = ctx.GetEigenDevice(); - out_eigen.device(place) = out_eigen.constant(static_cast(value)); - } -}; - -} // namespace operators -} // namespace paddle From 0708a1550cd8a0df2c549e5b0bbb4faea79dc13e Mon Sep 17 00:00:00 2001 From: Yu Yang Date: Tue, 7 Nov 2017 15:57:03 -0800 Subject: [PATCH 15/41] Fix CI --- paddle/operators/math/math_function.cu | 1 + 1 file changed, 1 insertion(+) diff --git a/paddle/operators/math/math_function.cu b/paddle/operators/math/math_function.cu index 3a216993ac..255e480680 100644 --- a/paddle/operators/math/math_function.cu +++ b/paddle/operators/math/math_function.cu @@ -12,6 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ +#include "paddle/framework/data_type.h" #include "paddle/operators/math/math_function.h" namespace paddle { From b4e18243633a9af9609926f4c413f8b22cb6a653 Mon Sep 17 00:00:00 2001 From: Yu Yang Date: Tue, 7 Nov 2017 16:25:04 -0800 Subject: [PATCH 16/41] Fix CI --- paddle/operators/math/math_function.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/paddle/operators/math/math_function.cc b/paddle/operators/math/math_function.cc index 175df2030d..09c3f0b1e6 100644 --- a/paddle/operators/math/math_function.cc +++ b/paddle/operators/math/math_function.cc @@ -272,11 +272,10 @@ struct TensorSetConstantWithPlace : public boost::static_visitor { void set_constant(const platform::DeviceContext& context, framework::Tensor* tensor, float value) { + TensorSetConstantWithPlace func(context, tensor, value); #ifdef PADDLE_WITH_CUDA - boost::apply_visitor(TensorSetConstantWithPlace(context, tensor, value), - tensor->place()); + tensor->place().apply_visitor(func); #else - TensorSetConstantWithPlace func(context, tensor, value); func(platform::CPUPlace()); #endif } From 2dd91dd57202570028536a75c1b3093002f783a2 Mon Sep 17 00:00:00 2001 From: Yang Yu Date: Tue, 7 Nov 2017 17:33:33 -0800 Subject: [PATCH 17/41] Shrink State Operator Used for shrink memories state in DyRNN. The height of state could be shrinked after running a step block. --- paddle/operators/array_operator.h | 50 ++++++ paddle/operators/shrink_state_op.cc | 156 ++++++++++++++++++ .../operators/tensor_array_read_write_op.cc | 41 +---- python/paddle/v2/framework/layers.py | 18 +- .../v2/framework/tests/test_shrink_state.py | 47 ++++++ 5 files changed, 274 insertions(+), 38 deletions(-) create mode 100644 paddle/operators/array_operator.h create mode 100644 paddle/operators/shrink_state_op.cc create mode 100644 python/paddle/v2/framework/tests/test_shrink_state.py diff --git a/paddle/operators/array_operator.h b/paddle/operators/array_operator.h new file mode 100644 index 0000000000..666043e824 --- /dev/null +++ b/paddle/operators/array_operator.h @@ -0,0 +1,50 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved. + + 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/lod_tensor_array.h" +#include "paddle/framework/op_registry.h" + +namespace paddle { +namespace operators { +class ArrayOp : public framework::OperatorBase { + public: + ArrayOp(const std::string &type, const framework::VariableNameMap &inputs, + const framework::VariableNameMap &outputs, + const framework::AttributeMap &attrs) + : OperatorBase(type, inputs, outputs, attrs) {} + + protected: + size_t GetOffset(const framework::Scope &scope, + const platform::DeviceContext &dev_ctx) const { + auto *i = scope.FindVar(Input("I")); + PADDLE_ENFORCE(i != nullptr, "I must be set"); + auto &i_tensor = i->Get(); + PADDLE_ENFORCE_EQ(i_tensor.numel(), 1); + size_t offset; + if (platform::is_gpu_place(i_tensor.place())) { + // FIXME: Avoid copy from GPU to CPU + framework::Tensor t; + t.CopyFrom(i_tensor, platform::CPUPlace(), dev_ctx); + dev_ctx.Wait(); + offset = static_cast(*t.data()); + } else { + offset = static_cast(*i_tensor.data()); + } + return offset; + } +}; + +} // namespace operators +} // namespace paddle diff --git a/paddle/operators/shrink_state_op.cc b/paddle/operators/shrink_state_op.cc new file mode 100644 index 0000000000..5aaecf0aae --- /dev/null +++ b/paddle/operators/shrink_state_op.cc @@ -0,0 +1,156 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ +#include "paddle/framework/lod_rank_table.h" +#include "paddle/operators/array_operator.h" +#include "paddle/operators/math/math_function.h" + +namespace paddle { +namespace operators { + +class ShrinkStateOp : public ArrayOp { + public: + ShrinkStateOp(const std::string &type, + const framework::VariableNameMap &inputs, + const framework::VariableNameMap &outputs, + const framework::AttributeMap &attrs) + : ArrayOp(type, inputs, outputs, attrs) {} + + void Run(const framework::Scope &scope, + const platform::DeviceContext &dev_ctx) const override { + auto *x_var = scope.FindVar(Input("X")); + PADDLE_ENFORCE(x_var != nullptr, "Input X must be set"); + auto &x_tensor = x_var->Get(); + size_t offset = this->GetOffset(scope, dev_ctx); + auto *rank_table_var = scope.FindVar(Input("RankTable")); + PADDLE_ENFORCE(rank_table_var != nullptr, "RankTable must be set"); + auto &rank_table = rank_table_var->Get(); + + int dst_num_rows = 0; + + { + auto &rank_items = rank_table.items(); + for (auto &rank_item : rank_items) { + if (offset < rank_item.length) { + ++dst_num_rows; + } else { + break; + } + } + } + + auto *out_var = scope.FindVar(Output("Out")); + PADDLE_ENFORCE(out_var != nullptr, "Output Out must be set"); + auto &out_tensor = *out_var->GetMutable(); + if (dst_num_rows != 0) { + out_tensor.ShareDataWith(x_tensor.Slice(0, dst_num_rows)); + } + } +}; + +class ShrinkStateOpProtoMaker : public framework::OpProtoAndCheckerMaker { + public: + ShrinkStateOpProtoMaker(framework::OpProto *proto, + framework::OpAttrChecker *op_checker) + : OpProtoAndCheckerMaker(proto, op_checker) { + AddInput("X", ""); + AddInput("RankTable", ""); + AddInput("I", ""); + AddOutput("Out", ""); + AddComment(""); + } +}; + +class ShrinkStateOpInferShape : public framework::InferShapeBase { + public: + void operator()(framework::InferShapeContext *context) const override { + PADDLE_ENFORCE(context->HasInput("X")); + PADDLE_ENFORCE(context->HasInput("I")); + PADDLE_ENFORCE(context->HasInput("RankTable")); + context->SetOutputDim("Out", context->GetInputDim("X")); + } +}; + +class ShrinkStateGradOp : public ArrayOp { + public: + ShrinkStateGradOp(const std::string &type, + const framework::VariableNameMap &inputs, + const framework::VariableNameMap &outputs, + const framework::AttributeMap &attrs) + : ArrayOp(type, inputs, outputs, attrs) {} + + void Run(const framework::Scope &scope, + const platform::DeviceContext &dev_ctx) const override { + auto *dout_var = scope.FindVar(Input(framework::GradVarName("Out"))); + auto dx_name = Output(framework::GradVarName("X")); + auto *dx_var = scope.FindVar(dx_name); + PADDLE_ENFORCE(dx_var != nullptr, "Input Gradient should not be nullptr"); + auto *x_var = scope.FindVar(Input("X")); + PADDLE_ENFORCE(x_var != nullptr); + + auto &x_tensor = x_var->Get(); + auto &dx_tensor = *dx_var->GetMutable(); + dx_tensor.Resize(x_tensor.dims()); + dx_tensor.mutable_data(x_tensor.place(), x_tensor.type()); + + if (dout_var == nullptr) { // dx_tensor fill zero + math::set_constant(dev_ctx, &dx_tensor, 0.0f); + } else { + auto &dout_tensor = dout_var->Get(); + auto height = dout_tensor.dims()[0]; + dx_tensor.Slice(0, static_cast(height)) + .CopyFrom(dout_tensor, dout_tensor.place(), dev_ctx); + if (height < dout_tensor.dims()[0]) { + auto rest_tensor = dx_tensor.Slice( + static_cast(height), static_cast(dout_tensor.dims()[0])); + math::set_constant(dev_ctx, &rest_tensor, 0.0f); + } + } + } +}; + +class ShrikStateGradInferShape : public framework::InferShapeBase { + public: + void operator()(framework::InferShapeContext *context) const override { + PADDLE_ENFORCE(context->HasInput("X")); + PADDLE_ENFORCE(context->HasOutput(framework::GradVarName("X"))); + context->SetOutputDim(framework::GradVarName("X"), + context->GetInputDim("X")); + } +}; + +class ShrinkStateGradOpMaker : public framework::SingleGradOpDescMaker { + public: + using framework::SingleGradOpDescMaker::SingleGradOpDescMaker; + + protected: + std::unique_ptr Apply() const override { + auto *op = new framework::OpDescBind(); + op->SetType("shrink_state_grad"); + op->SetInput("X", Input("X")); + op->SetInput(framework::GradVarName("Out"), OutputGrad("Out")); + op->SetOutput(framework::GradVarName("X"), InputGrad("X")); + op->SetAttrMap(Attrs()); + return std::unique_ptr(op); + } +}; + +} // namespace operators +} // namespace paddle + +namespace ops = paddle::operators; +REGISTER_OPERATOR(shrink_state, ops::ShrinkStateOp, + ops::ShrinkStateOpInferShape, ops::ShrinkStateOpProtoMaker, + ops::ShrinkStateGradOpMaker); +REGISTER_OPERATOR(shrink_state_grad, ops::ShrinkStateGradOp, + ops::ShrikStateGradInferShape); diff --git a/paddle/operators/tensor_array_read_write_op.cc b/paddle/operators/tensor_array_read_write_op.cc index 50824032ca..87b6b6929d 100644 --- a/paddle/operators/tensor_array_read_write_op.cc +++ b/paddle/operators/tensor_array_read_write_op.cc @@ -11,48 +11,18 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -#include "paddle/framework/lod_tensor_array.h" -#include "paddle/framework/op_registry.h" +#include "paddle/operators/array_operator.h" namespace paddle { namespace operators { -class ArrayOpBase : public framework::OperatorBase { - public: - ArrayOpBase(const std::string &type, const framework::VariableNameMap &inputs, - const framework::VariableNameMap &outputs, - const framework::AttributeMap &attrs) - : OperatorBase(type, inputs, outputs, attrs) {} - void Run(const framework::Scope &scope, - const platform::DeviceContext &dev_ctx) const override {} - - protected: - size_t GetOffset(const framework::Scope &scope, - const platform::DeviceContext &dev_ctx) const { - auto *i = scope.FindVar(Input("I")); - PADDLE_ENFORCE(i != nullptr, "I must be set"); - auto &i_tensor = i->Get(); - PADDLE_ENFORCE_EQ(i_tensor.numel(), 1); - size_t offset; - if (platform::is_gpu_place(i_tensor.place())) { - // FIXME: Avoid copy from GPU to CPU - framework::Tensor t; - t.CopyFrom(i_tensor, platform::CPUPlace(), dev_ctx); - dev_ctx.Wait(); - offset = static_cast(*t.data()); - } else { - offset = static_cast(*i_tensor.data()); - } - return offset; - } -}; -class WriteToArrayOp : public ArrayOpBase { +class WriteToArrayOp : public ArrayOp { public: WriteToArrayOp(const std::string &type, const framework::VariableNameMap &inputs, const framework::VariableNameMap &outputs, const framework::AttributeMap &attrs) - : ArrayOpBase(type, inputs, outputs, attrs) {} + : ArrayOp(type, inputs, outputs, attrs) {} void Run(const framework::Scope &scope, const platform::DeviceContext &dev_ctx) const override { @@ -115,6 +85,7 @@ class WriteToArrayInferVarType : public framework::VarTypeInference { public: void operator()(const framework::OpDescBind &op_desc, framework::BlockDescBind *block) const override { + VLOG(10) << "I am here?"; for (auto &out_var : op_desc.OutputArgumentNames()) { VLOG(10) << "Set Variable " << out_var << " as LOD_TENSOR_ARRAY"; block->Var(out_var)->SetType(framework::VarDesc::LOD_TENSOR_ARRAY); @@ -122,13 +93,13 @@ class WriteToArrayInferVarType : public framework::VarTypeInference { } }; -class ReadFromArrayOp : public ArrayOpBase { +class ReadFromArrayOp : public ArrayOp { public: ReadFromArrayOp(const std::string &type, const framework::VariableNameMap &inputs, const framework::VariableNameMap &outputs, const framework::AttributeMap &attrs) - : ArrayOpBase(type, inputs, outputs, attrs) {} + : ArrayOp(type, inputs, outputs, attrs) {} void Run(const framework::Scope &scope, const platform::DeviceContext &dev_ctx) const override { auto *x = scope.FindVar(Input("X")); diff --git a/python/paddle/v2/framework/layers.py b/python/paddle/v2/framework/layers.py index 917d3d9388..e235ff369e 100644 --- a/python/paddle/v2/framework/layers.py +++ b/python/paddle/v2/framework/layers.py @@ -801,13 +801,12 @@ def zeros(shape, dtype, main_program=None): def increment(x, value=1.0, main_program=None): helper = LayerHelper("increment", **locals()) - tmp = helper.create_tmp_variable(dtype=x.data_type) helper.append_op( type='increment', inputs={'X': [x]}, - outputs={'Out': [tmp]}, + outputs={'Out': [x]}, attrs={'step': value}) - return tmp + return x def array_write(x, i, array=None, main_program=None): @@ -838,3 +837,16 @@ def array_read(array, i, main_program=None): 'I': [i]}, outputs={'Out': [out]}) return out + + +def shrink_memory(x, i, table, main_program=None): + helper = LayerHelper('shrink_memory', **locals()) + out = helper.create_tmp_variable(dtype=x.data_type) + helper.append_op( + type='shrink_state', + inputs={'X': [x], + 'I': [i], + 'RankTable': [table]}, + outputs={'Out': [out]}, + attrs={}) + return out diff --git a/python/paddle/v2/framework/tests/test_shrink_state.py b/python/paddle/v2/framework/tests/test_shrink_state.py new file mode 100644 index 0000000000..2601c769e5 --- /dev/null +++ b/python/paddle/v2/framework/tests/test_shrink_state.py @@ -0,0 +1,47 @@ +import unittest +import paddle.v2.framework.core as core +from paddle.v2.framework.executor import Executor +import paddle.v2.framework.layers as layers +from paddle.v2.framework.backward import append_backward_ops +from paddle.v2.framework.framework import g_main_program +import numpy + + +class TestShrinkState(unittest.TestCase): + def test_shrink_state(self): + x = layers.data('x', shape=[100], data_type='float32') + x.stop_gradient = False + table = layers.lod_rank_table(x=x) + i = layers.zeros(dtype='int64', shape=[1]) + mem1 = layers.shrink_memory(x=x, i=i, table=table) + i = layers.increment(x=i) + i.stop_gradient = True + mem2 = layers.shrink_memory(x=mem1, i=i, table=table) + i = layers.increment(x=i) + i.stop_gradient = True + mem3 = layers.shrink_memory(x=mem2, i=i, table=table) + + cpu = core.CPUPlace() + tensor = core.LoDTensor() + tensor.set_lod([[0, 2, 5, 6]]) + tensor_np = numpy.random.random(size=(3, 100)).astype('float32') + tensor.set(tensor_np, cpu) + exe = Executor(cpu) + outs = map(numpy.array, + exe.run(feed={'x': tensor}, fetch_list=[mem1, mem2, mem3])) + self.assertTrue(numpy.allclose(tensor_np[0:3], outs[0])) + self.assertTrue(numpy.allclose(tensor_np[0:2], outs[1])) + self.assertTrue(numpy.allclose(tensor_np[0:1], outs[2])) + + mem3_mean = layers.mean(x=mem3) + append_backward_ops(loss=mem3_mean) + x_grad = map(numpy.array, + exe.run(feed={'x': tensor}, + fetch_list=[ + g_main_program.global_block().var('x@GRAD') + ]))[0] + self.assertAlmostEqual(1.0, x_grad.sum(), delta=0.1) + + +if __name__ == '__main__': + unittest.main() From f72729d407fcc33ad5de5f6285637c45a1425d5a Mon Sep 17 00:00:00 2001 From: Yu Yang Date: Tue, 7 Nov 2017 17:37:30 -0800 Subject: [PATCH 18/41] Feature/rnn to array to lod tensor (#5411) * Add LoDRankTable LoD Rank Table stores the `level` of `lod` which is ordered by sequence length in descending order. It is useful when implement dynamic RNN and is shared by dynamic RNN memory, dynamic RNN slice input and dynamic RNN slice output operators. * Add skeleton for array_to_lod_tensor and lod_tensor_to_array * Add VarType::LoDTensorArray * Add PyBind of LoDTensorArray * Add InferVarType * Add first unittest * Add ut * Add unittest * Add unittest * Add unittests * update * init * add infershape for lod_tensor_to_array_op * compelete array_to_lod_tensor_op * copy data * clean code * clean code * Fix unittest data * fix bugs * fix compile error * Refine TensorToArrayOp * refactor array_to_lod_tensor * Unittest * fix bugs * Fix unittest * Fix unittest * debug * Debug * Fix unittest * clean code * refactor * use ostream * update test * fix gpu build error * make gpu test pass --- paddle/framework/ddim.cc | 2 +- paddle/framework/ddim.h | 2 +- paddle/framework/lod_rank_table.cc | 1 + paddle/framework/lod_tensor.cc | 50 +++--- paddle/framework/lod_tensor.h | 9 +- paddle/framework/lod_tensor_test.cc | 39 ++--- paddle/framework/var_desc.cc | 6 +- paddle/operators/CMakeLists.txt | 4 + paddle/operators/array_to_lod_tensor_op.cc | 152 ++++++++++++++++++ paddle/operators/lod_rank_table_op.cc | 1 + paddle/operators/lod_tensor_to_array_op.cc | 143 ++++++++++++++++ python/paddle/v2/framework/layers.py | 24 +++ .../v2/framework/tests/test_lod_rank_table.py | 1 - .../tests/test_lod_tensor_array_ops.py | 127 +++++++++++++++ 14 files changed, 514 insertions(+), 47 deletions(-) create mode 100644 paddle/operators/array_to_lod_tensor_op.cc create mode 100644 paddle/operators/lod_tensor_to_array_op.cc create mode 100644 python/paddle/v2/framework/tests/test_lod_tensor_array_ops.py diff --git a/paddle/framework/ddim.cc b/paddle/framework/ddim.cc index 239ae5e123..10c785e04c 100644 --- a/paddle/framework/ddim.cc +++ b/paddle/framework/ddim.cc @@ -117,7 +117,7 @@ int64_t DDim::operator[](int idx) const { return boost::apply_visitor(DynamicConstIndexer(idx), var); } -int64_t DDim::size() const { return arity(*this); } +int DDim::size() const { return arity(*this); } bool DDim::operator==(DDim d) const { if (var.which() != d.getVar().which()) { diff --git a/paddle/framework/ddim.h b/paddle/framework/ddim.h index 2a5e2d2b69..aa773868ab 100644 --- a/paddle/framework/ddim.h +++ b/paddle/framework/ddim.h @@ -71,7 +71,7 @@ struct DDim { DDim operator*(DDim d) const; - int64_t size() const; + int size() const; }; /** diff --git a/paddle/framework/lod_rank_table.cc b/paddle/framework/lod_rank_table.cc index 68a83def7e..1c2fba70c8 100644 --- a/paddle/framework/lod_rank_table.cc +++ b/paddle/framework/lod_rank_table.cc @@ -31,6 +31,7 @@ void LoDRankTable::Reset(const LoD& lod, size_t level) { TableItem item; item.index = i; item.length = vec[i + 1] - vec[i]; + VLOG(10) << "Add item to rank table " << item.index << " " << item.length; items_.emplace_back(item); } // NOTE(yuyang18): diff --git a/paddle/framework/lod_tensor.cc b/paddle/framework/lod_tensor.cc index 2bcfffb134..a0f2906c74 100644 --- a/paddle/framework/lod_tensor.cc +++ b/paddle/framework/lod_tensor.cc @@ -27,6 +27,20 @@ namespace paddle { namespace framework { +std::ostream& operator<<(std::ostream& os, const LoD& lod) { + os << "{"; + for (auto& v : lod) { + os << "{"; + for (auto& i : v) { + os << i << ","; + } + os << "}"; + } + os << "}"; + + return os; +} + LoD SliceLevels(const LoD& in, size_t level_begin, size_t level_end) { LoD new_lod; new_lod.reserve(level_end - level_begin); @@ -136,37 +150,35 @@ void LoDTensor::ShrinkInLevel(size_t level, size_t elem_begin, ShareDataWith(Slice(begin, end)); } -void GetFineGrainedLoDLength(const LoD& lod, size_t start_idx, size_t end_idx, - std::vector>* lod_length, - size_t* start_offset) { - lod_length->clear(); - PADDLE_ENFORCE(start_idx < lod.size() - 1, - "start_idx should be >= 0 and < lod.size() - 1."); - PADDLE_ENFORCE(end_idx < lod.size(), - "end_idx should be >= 0 and < lod.size()."); - PADDLE_ENFORCE_LE(start_idx, end_idx, - "start_idx should be less than end_idx."); - for (size_t level_idx = 0; level_idx < lod.size(); ++level_idx) { +using LoDAndOffset = std::pair>; +LoDAndOffset GetSubLoDAndAbsoluteOffset(const LoD& lod, size_t start_idx, + size_t end_idx, size_t start_level) { + LoD sub_lod; + + for (size_t level_idx = start_level; level_idx < lod.size(); ++level_idx) { + PADDLE_ENFORCE_LE(start_idx, end_idx); + PADDLE_ENFORCE_LT(end_idx, lod[level_idx].size()); std::vector level_lens; for (size_t i = start_idx; i < end_idx; ++i) { level_lens.push_back(lod[level_idx][i + 1] - lod[level_idx][i]); } - lod_length->emplace_back(level_lens); + sub_lod.emplace_back(level_lens); start_idx = lod[level_idx][start_idx]; end_idx = lod[level_idx][end_idx]; } - *start_offset = start_idx; + + return LoDAndOffset{sub_lod, {start_idx, end_idx}}; } -void AppendLoD(LoD* lod, const std::vector>& lod_length) { - PADDLE_ENFORCE_EQ( - lod->size(), lod_length.size(), +void AppendLoD(LoD* lod, const LoD& lod_length) { + PADDLE_ENFORCE( + lod->empty() || lod->size() == lod_length.size(), "The lod_length should has the same size with the appended lod."); + if (lod->empty()) { + *lod = LoD(lod_length.size(), std::vector({0})); + } for (size_t i = 0; i < lod->size(); ++i) { auto& level = (*lod)[i]; - if (level.empty()) { - level.push_back(0); - } for (size_t len : lod_length[i]) { level.push_back(level.back() + len); } diff --git a/paddle/framework/lod_tensor.h b/paddle/framework/lod_tensor.h index 1437da399a..7f8a51cc58 100644 --- a/paddle/framework/lod_tensor.h +++ b/paddle/framework/lod_tensor.h @@ -56,6 +56,8 @@ using Vector = thrust::host_vector< */ using LoD = std::vector>; +std::ostream& operator<<(std::ostream& os, const LoD& lod); + /* * Slice levels from a LoD. * NOTE the lowest level should always be the absolute offsets of the underlying @@ -181,11 +183,10 @@ LoDTensor LodExpand(const LoDTensor& source, const LoD& lod, size_t level, return tensor; } -void GetFineGrainedLoDLength(const LoD& lod, size_t start_idx, size_t end_idx, - std::vector>* lod_length, - size_t* start_offset); +std::pair> GetSubLoDAndAbsoluteOffset( + const LoD& lod, size_t start_idx, size_t end_idx, size_t start_level); -void AppendLoD(LoD* lod, const std::vector>& lod_length); +void AppendLoD(LoD* lod, const LoD& lod_length); } // namespace framework } // namespace paddle diff --git a/paddle/framework/lod_tensor_test.cc b/paddle/framework/lod_tensor_test.cc index bf61c9ee7a..02d84b6823 100644 --- a/paddle/framework/lod_tensor_test.cc +++ b/paddle/framework/lod_tensor_test.cc @@ -146,43 +146,44 @@ TEST(LodExpand, test) { TEST(LoD, GetFineGrainedLoDLength) { LoD lod; - lod.push_back(std::vector{0, 2, 4, 5}); - lod.push_back(std::vector{0, 1, 6, 8, 10, 11}); + lod.push_back(std::vector({0, 2, 4, 5})); + lod.push_back(std::vector({0, 1, 6, 8, 10, 11})); lod.push_back( - std::vector{0, 2, 5, 7, 10, 12, 15, 17, 20, 24, 26, 29}); + std::vector({0, 2, 5, 7, 10, 12, 15, 17, 20, 24, 26, 29})); - std::vector> lod_length; - size_t start_offset; - paddle::framework::GetFineGrainedLoDLength(lod, 1, 2, &lod_length, - &start_offset); + auto lod_and_offset = + paddle::framework::GetSubLoDAndAbsoluteOffset(lod, 1, 2, 0); + LoD lod_length = lod_and_offset.first; + size_t start_offset = lod_and_offset.second.first; + size_t end_offset = lod_and_offset.second.second; - std::vector> expected; + LoD expected; expected.push_back(std::vector{2}); expected.push_back(std::vector{2, 2}); expected.push_back(std::vector{2, 3, 4, 2}); EXPECT_EQ(lod_length, expected); EXPECT_EQ(start_offset, 15UL); + EXPECT_EQ(end_offset, 26UL); } TEST(LoD, AppendLoD) { - std::vector> lod_lens; - lod_lens.push_back(std::vector{2}); - lod_lens.push_back(std::vector{2, 2}); - lod_lens.push_back(std::vector{2, 3, 4, 2}); + LoD lod_lens; + lod_lens.push_back(std::vector({2})); + lod_lens.push_back(std::vector({2, 2})); + lod_lens.push_back(std::vector({2, 3, 4, 2})); LoD origin; - origin.push_back(std::vector{0, 2}); - origin.push_back(std::vector{0, 1, 6}); - origin.push_back(std::vector{0, 2, 5, 7, 10, 12, 15}); + origin.push_back(std::vector({0, 2})); + origin.push_back(std::vector({0, 1, 6})); + origin.push_back(std::vector({0, 2, 5, 7, 10, 12, 15})); paddle::framework::AppendLoD(&origin, lod_lens); LoD expected; - expected.push_back(std::vector{0, 2, 4}); - expected.push_back(std::vector{0, 1, 6, 8, 10}); + expected.push_back(std::vector({0, 2, 4})); + expected.push_back(std::vector({0, 1, 6, 8, 10})); expected.push_back( - std::vector{0, 2, 5, 7, 10, 12, 15, 17, 20, 24, 26}); - + std::vector({0, 2, 5, 7, 10, 12, 15, 17, 20, 24, 26})); EXPECT_EQ(origin, expected); } diff --git a/paddle/framework/var_desc.cc b/paddle/framework/var_desc.cc index 16aca192d4..0babec29f6 100644 --- a/paddle/framework/var_desc.cc +++ b/paddle/framework/var_desc.cc @@ -45,7 +45,8 @@ void VarDescBind::SetLoDLevel(int32_t lod_level) { desc_.mutable_tensor_array()->set_lod_level(lod_level); break; default: - PADDLE_THROW("Tensor type=%d does not support LoDLevel", desc_.type()); + PADDLE_THROW("Tensor type=%d does not support LoDLevel", + desc_.tensor_array().lod_level()); } } @@ -56,7 +57,8 @@ int32_t VarDescBind::GetLodLevel() const { case VarDesc::LOD_TENSOR_ARRAY: return desc_.tensor_array().lod_level(); default: - PADDLE_THROW("Tensor type=%d does not support LoDLevel", desc_.type()); + PADDLE_THROW("Tensor type=%d does not support LoDLevel", + desc_.tensor_array().lod_level()); } } diff --git a/paddle/operators/CMakeLists.txt b/paddle/operators/CMakeLists.txt index b497c877d1..eae87a5141 100644 --- a/paddle/operators/CMakeLists.txt +++ b/paddle/operators/CMakeLists.txt @@ -170,6 +170,8 @@ set(DEPS_OPS sequence_conv_op sequence_pool_op lod_rank_table_op + lod_tensor_to_array_op + array_to_lod_tensor_op lstm_op tensor_array_read_write_op gru_op) @@ -182,6 +184,8 @@ op_library(sum_op DEPS net_op selected_rows_functor) op_library(pool_op DEPS pooling) op_library(pool_with_index_op DEPS pooling) op_library(lod_rank_table_op SRCS lod_rank_table_op.cc DEPS lod_rank_table) +op_library(lod_tensor_to_array_op SRCS lod_tensor_to_array_op.cc DEPS lod_rank_table_op) +op_library(array_to_lod_tensor_op SRCS array_to_lod_tensor_op.cc DEPS lod_rank_table_op) op_library(tensor_array_read_write_op SRCS tensor_array_read_write_op.cc) if(WITH_GPU) op_library(nccl_op DEPS nccl_common) diff --git a/paddle/operators/array_to_lod_tensor_op.cc b/paddle/operators/array_to_lod_tensor_op.cc new file mode 100644 index 0000000000..6cd9c06b8a --- /dev/null +++ b/paddle/operators/array_to_lod_tensor_op.cc @@ -0,0 +1,152 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved. + + 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 "paddle/framework/lod_rank_table.h" +#include "paddle/framework/lod_tensor_array.h" +#include "paddle/framework/op_registry.h" +#include "paddle/memory/memcpy.h" + +namespace paddle { +namespace operators { + +using LoD = framework::LoD; + +class ArrayToLoDTensorOp : public framework::OperatorBase { + public: + ArrayToLoDTensorOp(const std::string &type, + const framework::VariableNameMap &inputs, + const framework::VariableNameMap &outputs, + const framework::AttributeMap &attrs) + : 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(); + + // Check dims, place and data type of input's elements and infer output's + // dim + PADDLE_ENFORCE(!x.empty(), "There's no element in the input array."); + int rank = x[0].dims().size(); + platform::Place place = x[0].place(); + std::type_index data_type = x[0].type(); + framework::DDim ins_dims = framework::slice_ddim(x[0].dims(), 1, rank); + int64_t batch_size = x[0].dims()[0]; + for (size_t i = 1; i < x.size(); ++i) { + PADDLE_ENFORCE_EQ(framework::slice_ddim(x[i].dims(), 1, rank), ins_dims, + "The dimension of the %zu'th element in LoDTensorArray " + "differs from previous ones.", + i); + PADDLE_ENFORCE(platform::places_are_same_class(x[i].place(), place), + "The place class of the %zu'th element in LoDTensorArray " + "differs from previous ones.", + i); + PADDLE_ENFORCE(x[i].type() == data_type, + "The date type of the %zu'th element in LoDTensorArray " + "differs from previous ones.", + i); + batch_size += x[i].dims()[0]; + } + auto ins_dim_vec = framework::vectorize(ins_dims); + ins_dim_vec.insert(ins_dim_vec.begin(), batch_size); + framework::DDim out_dims = framework::make_ddim(ins_dim_vec); + out->Resize(out_dims); + out->mutable_data(place, data_type); + + auto &table_items = rank_table.items(); + std::vector table_item_idx(table_items.size()); + // table_item_idx = range(table_items_idx.size()) + std::iota(table_item_idx.begin(), table_item_idx.end(), 0); + std::sort(table_item_idx.begin(), table_item_idx.end(), + [&](size_t a, size_t b) { + return table_items[a].index < table_items[b].index; + }); + + // Build LoDTensor `out` + framework::LoD *out_lod = out->mutable_lod(); + out_lod->clear(); + size_t out_offset = 0; + auto prefix_lod = rank_table.coarse_lod(); + prefix_lod.emplace_back(); + auto &cur_level_lod = prefix_lod.back(); + cur_level_lod.push_back(0); + for (size_t idx : table_item_idx) { + cur_level_lod.push_back(cur_level_lod.back() + table_items[idx].length); + for (size_t x_idx = 0; x_idx < table_items[idx].length; ++x_idx) { + auto lod_and_offset = framework::GetSubLoDAndAbsoluteOffset( + x[x_idx].lod(), idx, idx + 1, 0); + + auto &lod_length = lod_and_offset.first; + framework::AppendLoD(out_lod, lod_length); + + size_t start_offset = lod_and_offset.second.first; + size_t end_offset = lod_and_offset.second.second; + VLOG(10) << "idx=" << idx << " x_idx=" << x_idx << " [" + << ", " << end_offset << "]"; + // Copy data + PADDLE_ENFORCE_GE(end_offset, start_offset); + size_t len = end_offset - start_offset; + if (len == 0) { + continue; + } + out->Slice(out_offset, out_offset + len) + .CopyFrom(x[x_idx].Slice(start_offset, end_offset), place, dev_ctx); + out_offset += len; + } + } + out_lod->insert(out_lod->begin(), prefix_lod.begin(), prefix_lod.end()); + } +}; + +class ArrayToLoDTensorOpProtoMaker : public framework::OpProtoAndCheckerMaker { + public: + ArrayToLoDTensorOpProtoMaker(framework::OpProto *proto, + framework::OpAttrChecker *op_checker) + : OpProtoAndCheckerMaker(proto, op_checker) { + AddInput("X", + "(std::vector) A vector of tensors that is going to " + "be casted to a big LoDTensor."); + AddInput("RankTable", + "(LoDRankTable) RankTable provides the coarse lod infomation to " + "build the output LoDTensor. See " + "'paddle/framework/lod_rank_table.h' for more details."); + AddOutput("Out", "(LoDTensor) The LoDTensor formed by input tensor array."); + AddComment( + R"DOC(This Op build a big LoDTensor from a std::vector + and a LoDRankTable. It is supposed to be used in getting dynamic RNN's + outputs back to a normal LoDTensor. The std::vector + would be the output of RNN Op and the LoDRankTable would be build + with RNN's input.)DOC"); + } +}; + +class ArrayToLoDTensorInferShape : public framework::InferShapeBase { + public: + void operator()(framework::InferShapeContext *context) const override { + PADDLE_ENFORCE(context->HasInput("X"), + "ArrayToLoDTensorOp must has input X."); + PADDLE_ENFORCE(context->HasInput("RankTable"), + "ArrayToLoDTensorOp must has input RankTable."); + } +}; + +} // namespace operators +} // namespace paddle + +namespace ops = paddle::operators; +REGISTER_OPERATOR(array_to_lod_tensor, ops::ArrayToLoDTensorOp, + ops::ArrayToLoDTensorOpProtoMaker, + ops::ArrayToLoDTensorInferShape); diff --git a/paddle/operators/lod_rank_table_op.cc b/paddle/operators/lod_rank_table_op.cc index be198951c2..ce010fcb91 100644 --- a/paddle/operators/lod_rank_table_op.cc +++ b/paddle/operators/lod_rank_table_op.cc @@ -28,6 +28,7 @@ class LoDRankTableOp : public framework::OperatorBase { auto x = scope.FindVar(Input("X"))->Get(); auto *out = scope.FindVar(Output("Out"))->GetMutable(); + VLOG(10) << "Level = " << static_cast(Attr("level")); out->Reset(x.lod(), static_cast(Attr("level"))); } }; diff --git a/paddle/operators/lod_tensor_to_array_op.cc b/paddle/operators/lod_tensor_to_array_op.cc new file mode 100644 index 0000000000..5f02f5e8a1 --- /dev/null +++ b/paddle/operators/lod_tensor_to_array_op.cc @@ -0,0 +1,143 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ +#include "paddle/framework/lod_rank_table.h" +#include "paddle/framework/lod_tensor_array.h" +#include "paddle/framework/op_registry.h" + +namespace paddle { +namespace operators { + +struct CopyRange { + size_t begin; + size_t end; +}; + +class LoDTensorToArrayOp : public framework::OperatorBase { + public: + LoDTensorToArrayOp(const std::string &type, + const framework::VariableNameMap &inputs, + const framework::VariableNameMap &outputs, + const framework::AttributeMap &attrs) + : 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 &items = rank_table.items(); + auto max_seq_len = items[0].length; + auto rank_level = rank_table.level(); + out.resize(max_seq_len); + std::vector> copy_ranges(max_seq_len); + + // set out[i] lod + for (size_t t = 0; t < max_seq_len; t++) { + auto &lod = *out[t].mutable_lod(); + lod.clear(); + for (auto &item : items) { + if (t >= item.length) { + break; + } + 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( + ranges.begin(), ranges.end(), 0UL, + [](size_t a, const CopyRange &b) { return a + b.end - b.begin; }); + auto x_dim = x.dims(); + x_dim[0] = static_cast(height); + out[i].Resize(x_dim); + out[i].mutable_data(x.place(), x.type()); + size_t offset = 0; + for (auto &each_range : ranges) { + size_t len = each_range.end - each_range.begin; + if (len == 0) { + continue; + } + // out[i][offset: offset+len] = x[each_range.begin: each_range.end] + out[i] + .Slice(static_cast(offset), static_cast(offset + len)) + .CopyFrom(x.Slice(static_cast(each_range.begin), + static_cast(each_range.end)), + x.place(), dev_ctx); + offset += len; + } + } + } +}; + +class LoDTensorToArrayOpProtoMaker : public framework::OpProtoAndCheckerMaker { + public: + LoDTensorToArrayOpProtoMaker(framework::OpProto *proto, + framework::OpAttrChecker *op_checker) + : OpProtoAndCheckerMaker(proto, op_checker) { + AddInput("X", ""); + AddInput("RankTable", ""); + AddOutput("Out", ""); + AddComment(""); + } +}; + +class LoDTensorToArrayInferShape : public framework::InferShapeBase { + public: + void operator()(framework::InferShapeContext *context) const override { + PADDLE_ENFORCE(context->HasInput("X"), + "Input(X) of LoDTensorToArrayOp should not be null."); + PADDLE_ENFORCE( + context->HasInput("RankTable"), + "Input(RankTable) of LoDTensorToArrayOp should not be null."); + + PADDLE_ENFORCE(context->HasOutput("Out"), + "Output(Out) of LoDTensorToArrayOp should not be null."); + + auto x_dim = context->GetInputDim("X"); + // The first dim of each LoDTensor in Output can only be set at run-time.; + // We still have to Resize each LoDTensor in Output. + context->SetOutputDim("Out", x_dim); + } +}; + +class LoDTensorToArrayInferVarType : public framework::VarTypeInference { + public: + void operator()(const framework::OpDescBind &op_desc, + framework::BlockDescBind *block) const override { + for (auto &out_var : op_desc.Output("Out")) { + block->Var(out_var)->SetType(framework::VarDesc::LOD_TENSOR_ARRAY); + } + } +}; + +} // namespace operators +} // namespace paddle + +namespace ops = paddle::operators; +REGISTER_OPERATOR(lod_tensor_to_array, ops::LoDTensorToArrayOp, + ops::LoDTensorToArrayOpProtoMaker, + ops::LoDTensorToArrayInferShape, + ops::LoDTensorToArrayInferVarType); diff --git a/python/paddle/v2/framework/layers.py b/python/paddle/v2/framework/layers.py index 917d3d9388..d42af89eae 100644 --- a/python/paddle/v2/framework/layers.py +++ b/python/paddle/v2/framework/layers.py @@ -775,6 +775,30 @@ def lod_rank_table(x, level=0, main_program=None): return table +def lod_tensor_to_array(x, table, main_program=None): + helper = LayerHelper("lod_tensor_to_array", **locals()) + array = helper.create_variable( + name=unique_name("lod_tensor_to_array"), + type=core.VarDesc.VarType.LOD_TENSOR_ARRAY) + helper.append_op( + type='lod_tensor_to_array', + inputs={'X': x, + 'RankTable': table}, + outputs={'Out': array}) + return array + + +def array_to_lod_tensor(x, table, main_program=None): + helper = LayerHelper("array_to_lod_tensor", **locals()) + tmp = helper.create_tmp_variable(dtype=x.data_type) + helper.append_op( + type="array_to_lod_tensor", + inputs={'X': x, + 'RankTable': table}, + outputs={'Out': tmp}) + return tmp + + def fill_constant(shape, dtype, value, main_program=None): helper = LayerHelper("ones", **locals()) out = helper.create_tmp_variable(dtype=dtype) diff --git a/python/paddle/v2/framework/tests/test_lod_rank_table.py b/python/paddle/v2/framework/tests/test_lod_rank_table.py index 2242d4391d..408145c10f 100644 --- a/python/paddle/v2/framework/tests/test_lod_rank_table.py +++ b/python/paddle/v2/framework/tests/test_lod_rank_table.py @@ -18,7 +18,6 @@ class TestLoDRankTable(unittest.TestCase): tensor = core.LoDTensor() tensor.set(numpy.random.random(size=(17, 100)), cpu) tensor.set_lod([[0, 1, 3], [0, 5, 6, 7], [0, 3, 4, 9, 10, 13, 16, 17]]) - exe.run(g_main_program, scope=scope, feed={'x': tensor}) var = scope.find_var(rank_table.name) table = var.get_lod_rank_table() diff --git a/python/paddle/v2/framework/tests/test_lod_tensor_array_ops.py b/python/paddle/v2/framework/tests/test_lod_tensor_array_ops.py new file mode 100644 index 0000000000..61a5fcf07d --- /dev/null +++ b/python/paddle/v2/framework/tests/test_lod_tensor_array_ops.py @@ -0,0 +1,127 @@ +import unittest +import paddle.v2.framework.core as core +import numpy +import paddle.v2.framework.layers as layers +from paddle.v2.framework.framework import Program +from paddle.v2.framework.executor import Executor + + +class TestCPULoDTensorArrayOps(unittest.TestCase): + def place(self): + return core.CPUPlace() + + def test_lod_tensor_to_array_level_0(self): + tensor = core.LoDTensor() + tensor.set( + numpy.arange(10).reshape(10, 1).astype('int32'), self.place()) + tensor.set_lod([[0, 3, 9, 10]]) + expect = map(lambda x: numpy.array(x).astype('int32'), + [[3, 0, 9], [4, 1], [5, 2], [6], [7], [8]]) + self.main(tensor=tensor, expect_array=expect, expect_lod=[] * 6) + + def test_lod_tensor_to_array_level_0_empty_seq(self): + tensor = core.LoDTensor() + tensor.set( + numpy.arange(10).reshape(10, 1).astype('int32'), self.place()) + tensor.set_lod([[0, 3, 9, 9, 10]]) + expect = map(lambda x: numpy.array(x).astype('int32'), + [[3, 0, 9], [4, 1], [5, 2], [6], [7], [8]]) + self.main(tensor=tensor, expect_array=expect, expect_lod=[] * 6) + + def test_lod_tensor_to_array_level_1(self): + tensor = core.LoDTensor() + tensor.set( + numpy.arange(20).reshape(20, 1).astype('int32'), self.place()) + tensor.set_lod([[0, 2, 5], [0, 3, 9, 11, 17, 20]]) + + expect = [ + numpy.array( + [9, 10, 0, 1, 2], dtype='int32'), numpy.array( + [11, 12, 13, 14, 15, 16, 3, 4, 5, 6, 7, 8], dtype='int32'), + numpy.array( + [17, 18, 19], dtype='int32') + ] + + lod = [[[0, 2, 5]], [[0, 6, 12]], [[0, 3]]] + self.main(tensor=tensor, expect_array=expect, expect_lod=lod) + + def test_lod_tensor_to_array_level_1_empty_seq(self): + tensor = core.LoDTensor() + tensor.set( + numpy.arange(31).reshape(31, 1).astype('int32'), self.place()) + + tensor.set_lod([[0, 3, 5, 9, 11], + [0, 3, 7, 11, 11, 12, 17, 19, 21, 23, 30, 31]]) + + expect = [ + numpy.array( + item, dtype='int32') + for item in [[ + 12, 13, 14, 15, 16, 0, 1, 2, 23, 24, 25, 26, 27, 28, 29 + ], [17, 18, 3, 4, 5, 6, 11, 30], [19, 20, 7, 8, 9, 10], [21, 22]] + ] + + lod = [[[0, 5, 8, 8, 15]], [[0, 2, 6, 7, 8]], [[0, 2, 6]], [[0, 2]]] + self.main(tensor=tensor, expect_array=expect, expect_lod=lod) + + def test_lod_tensor_to_array_level_2(self): + tensor = core.LoDTensor() + tensor.set( + numpy.arange(50).reshape(50, 1).astype('int32'), self.place()) + tensor.set_lod([[0, 2, 5, 6], [0, 2, 5, 6, 10, 12, 13], + [0, 3, 7, 11, 17, 21, 22, 23, 27, 31, 39, 45, 46, 50]]) + + expect = [ + numpy.array( + item, dtype='int32') + for item in [[21, 0, 1, 2, 3, 4, 5, 6, 46, 47, 48, 49], range( + 22, 39) + range(7, 21), range(39, 46)] + ] + lod = [[[0, 1, 3, 4], [0, 1, 4, 8, 12]], + [[0, 4, 7], [0, 1, 5, 9, 17, 21, 27, 31]], [[0, 2], [0, 6, 7]]] + self.main(tensor=tensor, expect_array=expect, expect_lod=lod) + + def test_lod_tensor_to_array_level_2_skip_level(self): + tensor = core.LoDTensor() + tensor.set( + numpy.arange(50).reshape(50, 1).astype('int32'), self.place()) + tensor.set_lod([[0, 2, 5, 6], [0, 2, 5, 6, 10, 12, 13], + [0, 3, 7, 11, 17, 21, 22, 23, 27, 31, 39, 45, 46, 50]]) + self.main(tensor=tensor, expect_array=None, expect_lod=None, level=1) + + def main(self, tensor, expect_array, expect_lod, level=0): + place = self.place() + program = Program() + x = layers.data(name='x', shape=[10], main_program=program) + x.persistable = True + table = layers.lod_rank_table(x, level=level, main_program=program) + array = layers.lod_tensor_to_array(x, table, main_program=program) + array.persistable = True + + result = layers.array_to_lod_tensor(array, table, main_program=program) + result.persistable = True + exe = Executor(place) + scope = core.Scope() + exe.run(program, feed={'x': tensor}, scope=scope) + var = scope.find_var(array.name) + array = var.get_lod_tensor_array() + if expect_array is not None and expect_lod is not None: + self.check_array_same(array, expect_array, expect_lod) + self.check_tensor_same(scope.find_var(result.name).get_tensor(), tensor) + + def check_array_same(self, array, expect_tensor, expect_lod): + self.assertEqual(len(expect_tensor), len(array)) + for i, exp in enumerate(zip(expect_tensor, expect_lod)): + exp_tensor, exp_lod = exp + exp_tensor = numpy.expand_dims(exp_tensor, axis=1) + self.assertTrue(numpy.allclose(exp_tensor, numpy.array(array[i]))) + self.assertEqual(exp_lod, array[i].lod()) + + def check_tensor_same(self, actual, expect): + self.assertTrue( + numpy.allclose(numpy.array(actual), numpy.array(expect))) + self.assertEqual(actual.lod(), expect.lod()) + + +if __name__ == '__main__': + unittest.main() From cdf5e87104c124944ce6c6c256664b048dc6e413 Mon Sep 17 00:00:00 2001 From: chengduoZH Date: Wed, 8 Nov 2017 10:16:36 +0800 Subject: [PATCH 19/41] fix attr name --- paddle/operators/pool_cudnn_op.cu | 8 ++--- paddle/operators/pool_op.cc | 31 ++++++++++--------- paddle/operators/pool_op.h | 8 ++--- paddle/operators/pool_with_index_op.cc | 18 +++++------ paddle/operators/pool_with_index_op.h | 4 +-- python/paddle/v2/framework/layers.py | 4 +-- .../v2/framework/tests/test_pool2d_op.py | 4 +-- .../v2/framework/tests/test_pool3d_op.py | 4 +-- .../v2/framework/tests/test_pool_max_op.py | 2 +- 9 files changed, 42 insertions(+), 41 deletions(-) diff --git a/paddle/operators/pool_cudnn_op.cu b/paddle/operators/pool_cudnn_op.cu index 8d0741dccc..8711567b95 100644 --- a/paddle/operators/pool_cudnn_op.cu +++ b/paddle/operators/pool_cudnn_op.cu @@ -37,11 +37,11 @@ class PoolCudnnOpKernel : public framework::OpKernel { const T *input_data = input->data(); T *output_data = output->mutable_data(ctx.GetPlace()); - std::string pooling_type = ctx.Attr("poolingType"); + std::string pooling_type = ctx.Attr("pooling_type"); std::vector ksize = ctx.Attr>("ksize"); std::vector strides = ctx.Attr>("strides"); std::vector paddings = ctx.Attr>("paddings"); - if (ctx.Attr("globalPooling")) { + if (ctx.Attr("global_pooling")) { for (size_t i = 0; i < ksize.size(); ++i) { paddings[i] = 0; ksize[i] = static_cast(input->dims()[i + 2]); @@ -92,12 +92,12 @@ class PoolCudnnGradOpKernel : public framework::OpKernel { ctx.Input(framework::GradVarName("Out")); Tensor *input_grad = ctx.Output(framework::GradVarName("X")); - std::string pooling_type = ctx.Attr("poolingType"); + std::string pooling_type = ctx.Attr("pooling_type"); std::vector ksize = ctx.Attr>("ksize"); std::vector strides = ctx.Attr>("strides"); std::vector paddings = ctx.Attr>("paddings"); - if (ctx.Attr("globalPooling")) { + if (ctx.Attr("global_pooling")) { for (size_t i = 0; i < ksize.size(); ++i) { paddings[i] = 0; ksize[i] = static_cast(input->dims()[i + 2]); diff --git a/paddle/operators/pool_op.cc b/paddle/operators/pool_op.cc index f58aab7338..f3963b1995 100644 --- a/paddle/operators/pool_op.cc +++ b/paddle/operators/pool_op.cc @@ -29,7 +29,7 @@ void PoolOp::InferShape(framework::InferShapeContext *ctx) const { auto in_x_dims = ctx->GetInputDim("X"); - std::string pooling_type = ctx->Attrs().Get("poolingType"); + std::string pooling_type = ctx->Attrs().Get("pooling_type"); std::vector ksize = ctx->Attrs().Get>("ksize"); std::vector strides = ctx->Attrs().Get>("strides"); std::vector paddings = ctx->Attrs().Get>("paddings"); @@ -37,7 +37,7 @@ void PoolOp::InferShape(framework::InferShapeContext *ctx) const { PADDLE_ENFORCE(in_x_dims.size() == 4 || in_x_dims.size() == 5, "Pooling intput should be 4-D or 5-D tensor."); - if (ctx->Attrs().Get("globalPooling")) { + if (ctx->Attrs().Get("global_pooling")) { ksize.resize(static_cast(in_x_dims.size()) - 2); for (size_t i = 0; i < ksize.size(); ++i) { paddings[i] = 0; @@ -83,20 +83,20 @@ Pool2dOpMaker::Pool2dOpMaker(framework::OpProto *proto, "H is the height of the feature, " "and W is the width of the feature."); - AddAttr("poolingType", + AddAttr("pooling_type", "(string), pooling type, can be \"max\" for max-pooling " "and \"avg\" for average-pooling.") .InEnum({"max", "avg"}); AddAttr>("ksize", "(vector) The pooling window " "size(height, width) of the pooling operator. " - "If globalPooling = true, ksize and paddings will " + "If global_pooling = true, ksize and paddings will " "be ignored."); // TODO(Chengduo): Add checker. // (Currently, // TypedAttrChecker don't support vector type.) - AddAttr("globalPooling", + AddAttr("global_pooling", "(bool, default false) Whether to use the global pooling. " - "If globalPooling = true, ksize and paddings will be ignored.") + "If global_pooling = true, ksize and paddings will be ignored.") .SetDefault(false); AddAttr>("strides", "(vector, default {1, 1}), strides(height, " @@ -107,7 +107,7 @@ Pool2dOpMaker::Pool2dOpMaker(framework::OpProto *proto, "paddings", "(vector, defalut {0,0}), paddings(height, width) of pooling " "operator." - "If globalPooling = true, paddings and ksize will be ignored.") + "If global_pooling = true, paddings and ksize will be ignored.") .SetDefault({0, 0}); // TODO(Chengduo): Add checker. (Currently, // TypedAttrChecker don't support vector type.) @@ -115,7 +115,7 @@ Pool2dOpMaker::Pool2dOpMaker(framework::OpProto *proto, Pool2d Operator. The pooling2d operation calculates the output based on -the input, poolingType and ksize, strides, paddings parameters. +the input, pooling_type and ksize, strides, paddings parameters. Input(X) and output(Out) are in NCHW format, where N is batch size, C is the number of channels, H is the height of the feature, and W is the width of the feature. Parameters(ksize, strides, paddings) are two elements. @@ -152,7 +152,7 @@ Pool3dOpMaker::Pool3dOpMaker(framework::OpProto *proto, "the number of channels, and D, H and W is the depth, height and " "width of the feature, respectively."); - AddAttr("poolingType", + AddAttr("pooling_type", "(string) Pooling type, can be \"max\" for max-pooling " "and \"avg\" for average-pooling.") .InEnum({"max", "avg"}); @@ -160,13 +160,14 @@ Pool3dOpMaker::Pool3dOpMaker(framework::OpProto *proto, "ksize", "(vector) The pooling window size(depth, height, " "width) of pooling operator. " - "If globalPooling = true, ksize and paddings will " + "If global_pooling = true, ksize and paddings will " "be ignored."); // TODO(Chengduo): Add checker. // (Currently, // TypedAttrChecker don't support vector type.) - AddAttr("globalPooling", - "(bool, default false) Whether to use the global pooling. " - "If globalPooling = true, ksize and paddings wille be ignored.") + AddAttr( + "global_pooling", + "(bool, default false) Whether to use the global pooling. " + "If global_pooling = true, ksize and paddings wille be ignored.") .SetDefault(false); AddAttr>( "strides", @@ -178,7 +179,7 @@ Pool3dOpMaker::Pool3dOpMaker(framework::OpProto *proto, "paddings", "(vector, defalut {0,0,0}), paddings(depth, height, " "width) of pooling operator. " - "If globalPooling = true, ksize and paddings will be ignored.") + "If global_pooling = true, ksize and paddings will be ignored.") .SetDefault({0, 0, 0}); // TODO(Chengduo): Add checker. (Currently, // TypedAttrChecker don't support vector type.) @@ -186,7 +187,7 @@ Pool3dOpMaker::Pool3dOpMaker(framework::OpProto *proto, Pool3d Operator. The pooling3d operation calculates the output based on -the input, poolingType, ksize, strides, and paddings parameters. +the input, pooling_type, ksize, strides, and paddings parameters. Input(X) and output(Out) are in NCDHW format, where N is batch size, C is the number of channels, and D, H and W are the depth, height and width of the feature, respectively. Parameters(ksize, strides, paddings) diff --git a/paddle/operators/pool_op.h b/paddle/operators/pool_op.h index d9d445f6a6..4da1941ab5 100644 --- a/paddle/operators/pool_op.h +++ b/paddle/operators/pool_op.h @@ -57,11 +57,11 @@ class PoolKernel : public framework::OpKernel { const Tensor* in_x = context.Input("X"); Tensor* out = context.Output("Out"); - std::string pooling_type = context.Attr("poolingType"); + std::string pooling_type = context.Attr("pooling_type"); std::vector ksize = context.Attr>("ksize"); std::vector strides = context.Attr>("strides"); std::vector paddings = context.Attr>("paddings"); - if (context.Attr("globalPooling")) { + if (context.Attr("global_pooling")) { for (size_t i = 0; i < ksize.size(); ++i) { paddings[i] = 0; ksize[i] = static_cast(in_x->dims()[i + 2]); @@ -119,12 +119,12 @@ class PoolGradKernel : public framework::OpKernel { context.Input(framework::GradVarName("Out")); Tensor* in_x_grad = context.Output(framework::GradVarName("X")); - std::string pooling_type = context.Attr("poolingType"); + std::string pooling_type = context.Attr("pooling_type"); std::vector ksize = context.Attr>("ksize"); std::vector strides = context.Attr>("strides"); std::vector paddings = context.Attr>("paddings"); - if (context.Attr("globalPooling")) { + if (context.Attr("global_pooling")) { for (size_t i = 0; i < ksize.size(); ++i) { paddings[i] = 0; ksize[i] = static_cast(in_x->dims()[i + 2]); diff --git a/paddle/operators/pool_with_index_op.cc b/paddle/operators/pool_with_index_op.cc index a31b3fcb70..1df36e965a 100644 --- a/paddle/operators/pool_with_index_op.cc +++ b/paddle/operators/pool_with_index_op.cc @@ -44,7 +44,7 @@ class MaxPoolWithIndexOp : public framework::OperatorWithKernel { PADDLE_ENFORCE(in_x_dims.size() == 4 || in_x_dims.size() == 5, "Pooling intput should be 4-D or 5-D tensor."); - if (ctx->Attrs().Get("globalPooling")) { + if (ctx->Attrs().Get("global_pooling")) { ksize.resize(static_cast(in_x_dims.size()) - 2); for (size_t i = 0; i < ksize.size(); ++i) { paddings[i] = 0; @@ -110,14 +110,14 @@ class MaxPool2dWithIndexOpMaker : public framework::OpProtoAndCheckerMaker { AddAttr>("ksize", "(vector) The pooling window size(height, " "width) of pooling operator. " - "If globalPooling = true, ksize and paddings " + "If global_pooling = true, ksize and paddings " "will be ignored."); // TODO(Chengduo): Add // checker. (Currently, // TypedAttrChecker don't support vector type.) AddAttr( - "globalPooling", + "global_pooling", "(bool, default false) Whether to use the global pooling. " - "If globalPooling = true, ksize and paddings will be ignored.") + "If global_pooling = true, ksize and paddings will be ignored.") .SetDefault(false); AddAttr>("strides", "(vector, default {1, 1}), strides(height, " @@ -128,7 +128,7 @@ class MaxPool2dWithIndexOpMaker : public framework::OpProtoAndCheckerMaker { "paddings", "(vector, defalut {0, 0}), paddings(height, width) of pooling " "operator. " - "If globalPooling = true, paddings and will be ignored.") + "If global_pooling = true, paddings and will be ignored.") .SetDefault({0, 0}); // TODO(Chengduo): Add checker. (Currently, // TypedAttrChecker don't support vector type.) @@ -188,14 +188,14 @@ class MaxPool3dWithIndexOpMaker : public framework::OpProtoAndCheckerMaker { AddAttr>("ksize", "(vector) The pooling window size(depth, " "height, width) of pooling operator. " - "If globalPooling = true, ksize and paddings " + "If global_pooling = true, ksize and paddings " "will be ignored."); // TODO(Chengduo): Add // checker. (Currently, // TypedAttrChecker don't support vector type.) AddAttr( - "globalPooling", + "global_pooling", "(bool, default false) Whether to use the global pooling. " - "If globalPooling = true, ksize and paddings will be ignored.") + "If global_pooling = true, ksize and paddings will be ignored.") .SetDefault(false); AddAttr>("strides", "(vector, default {1,1,1}), strides(depth, " @@ -206,7 +206,7 @@ class MaxPool3dWithIndexOpMaker : public framework::OpProtoAndCheckerMaker { "paddings", "(vector, defalut {0,0,0}), paddings(depth, " "height, width) of pooling operator. " - "If globalPooling = true, paddings and ksize will be ignored.") + "If global_pooling = true, paddings and ksize will be ignored.") .SetDefault({0, 0, 0}); // TODO(Chengduo): Add checker. (Currently, // TypedAttrChecker don't support vector type.) diff --git a/paddle/operators/pool_with_index_op.h b/paddle/operators/pool_with_index_op.h index 4862774043..ea37de84ab 100644 --- a/paddle/operators/pool_with_index_op.h +++ b/paddle/operators/pool_with_index_op.h @@ -35,7 +35,7 @@ class MaxPoolWithIndexKernel : public framework::OpKernel { std::vector ksize = context.Attr>("ksize"); std::vector strides = context.Attr>("strides"); std::vector paddings = context.Attr>("paddings"); - if (context.Attr("globalPooling")) { + if (context.Attr("global_pooling")) { for (size_t i = 0; i < ksize.size(); ++i) { paddings[i] = 0; ksize[i] = static_cast(in_x->dims()[i + 2]); @@ -72,7 +72,7 @@ class MaxPoolWithIndexGradKernel : public framework::OpKernel { std::vector ksize = context.Attr>("ksize"); std::vector strides = context.Attr>("strides"); std::vector paddings = context.Attr>("paddings"); - if (context.Attr("globalPooling")) { + if (context.Attr("global_pooling")) { for (size_t i = 0; i < ksize.size(); ++i) { paddings[i] = 0; ksize[i] = static_cast(in_x_grad->dims()[i + 2]); diff --git a/python/paddle/v2/framework/layers.py b/python/paddle/v2/framework/layers.py index d42af89eae..345ea436cc 100644 --- a/python/paddle/v2/framework/layers.py +++ b/python/paddle/v2/framework/layers.py @@ -414,9 +414,9 @@ def pool2d(input, inputs={"X": input}, outputs={"Out": pool_out}, attrs={ - "poolingType": pool_type, + "pooling_type": pool_type, "ksize": pool_size, - "globalPooling": global_pooling, + "global_pooling": global_pooling, "strides": pool_stride, "paddings": pool_padding }) diff --git a/python/paddle/v2/framework/tests/test_pool2d_op.py b/python/paddle/v2/framework/tests/test_pool2d_op.py index c93469e119..ac3fa6aa87 100644 --- a/python/paddle/v2/framework/tests/test_pool2d_op.py +++ b/python/paddle/v2/framework/tests/test_pool2d_op.py @@ -61,8 +61,8 @@ class TestPool2d_Op(OpTest): 'strides': self.strides, 'paddings': self.paddings, 'ksize': self.ksize, - 'poolingType': self.pool_type, - 'globalPooling': self.global_pool, + 'pooling_type': self.pool_type, + 'global_pooling': self.global_pool, } self.outputs = {'Out': output.astype('float32')} diff --git a/python/paddle/v2/framework/tests/test_pool3d_op.py b/python/paddle/v2/framework/tests/test_pool3d_op.py index 416f0df7cd..87483ae5e5 100644 --- a/python/paddle/v2/framework/tests/test_pool3d_op.py +++ b/python/paddle/v2/framework/tests/test_pool3d_op.py @@ -67,8 +67,8 @@ class TestPool3d_Op(OpTest): 'strides': self.strides, 'paddings': self.paddings, 'ksize': self.ksize, - 'poolingType': self.pool_type, - 'globalPooling': self.global_pool, + 'pooling_type': self.pool_type, + 'global_pooling': self.global_pool, } self.outputs = {'Out': output.astype('float32')} diff --git a/python/paddle/v2/framework/tests/test_pool_max_op.py b/python/paddle/v2/framework/tests/test_pool_max_op.py index cc1a867761..04843a28ac 100644 --- a/python/paddle/v2/framework/tests/test_pool_max_op.py +++ b/python/paddle/v2/framework/tests/test_pool_max_op.py @@ -86,7 +86,7 @@ class TestMaxPoolWithIndex_Op(OpTest): 'strides': self.strides, 'paddings': self.paddings, 'ksize': self.ksize, - 'globalPooling': self.global_pool, + 'global_pooling': self.global_pool, } self.inputs = {'X': input} From 0ede2a731120966dc0171b55eb403b2ec90f8fd8 Mon Sep 17 00:00:00 2001 From: Yang Yu Date: Tue, 7 Nov 2017 19:10:39 -0800 Subject: [PATCH 20/41] Fix CI Compile --- paddle/framework/backward_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paddle/framework/backward_test.cc b/paddle/framework/backward_test.cc index 4e8d630c26..d485cdf610 100644 --- a/paddle/framework/backward_test.cc +++ b/paddle/framework/backward_test.cc @@ -21,7 +21,7 @@ #include "paddle/framework/var_desc.h" #include "paddle/operators/net_op.h" -USE_OP(fill_constant); +USE_NO_KERNEL_OP(fill_constant); namespace paddle { namespace framework { From ac7cca1865e5e8a2206ed74e3c7c17f81a96942e Mon Sep 17 00:00:00 2001 From: "Wang,Jeff" Date: Tue, 7 Nov 2017 19:24:15 -0800 Subject: [PATCH 21/41] uci_housing.py can download the trained model automatically. --- python/paddle/v2/dataset/uci_housing.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/python/paddle/v2/dataset/uci_housing.py b/python/paddle/v2/dataset/uci_housing.py index ce60aa21c2..98b97c75ca 100644 --- a/python/paddle/v2/dataset/uci_housing.py +++ b/python/paddle/v2/dataset/uci_housing.py @@ -22,6 +22,7 @@ parse training set and test set into paddle reader creators. import numpy as np import os import paddle.v2.dataset.common +from paddle.v2.parameters import Parameters __all__ = ['train', 'test'] @@ -34,7 +35,8 @@ feature_names = [ UCI_TRAIN_DATA = None UCI_TEST_DATA = None - +URL_MODEL = 'https://github.com/PaddlePaddle/book/raw/develop/01.fit_a_line/fit_a_line.tar' +MD5_MODEL = '52fc3da8ef3937822fcdd87ee05c0c9b' def feature_range(maximums, minimums): import matplotlib @@ -111,6 +113,13 @@ def test(): return reader +def model(): + tar_file = paddle.v2.dataset.common.download(URL_MODEL, 'fit_a_line.tar', MD5_MODEL) + with open(tar_file, 'r') as f: + parameters = Parameters.from_tar(f) + return parameters + + def fetch(): paddle.v2.dataset.common.download(URL, 'uci_housing', MD5) From b4dddb2994ffe64e43132d44276fd65ca3c57aa1 Mon Sep 17 00:00:00 2001 From: Yang Yu Date: Tue, 7 Nov 2017 19:31:48 -0800 Subject: [PATCH 22/41] Fix Unittest --- python/paddle/v2/framework/layers.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/python/paddle/v2/framework/layers.py b/python/paddle/v2/framework/layers.py index e235ff369e..8fc34501c6 100644 --- a/python/paddle/v2/framework/layers.py +++ b/python/paddle/v2/framework/layers.py @@ -801,12 +801,13 @@ def zeros(shape, dtype, main_program=None): def increment(x, value=1.0, main_program=None): helper = LayerHelper("increment", **locals()) + out = helper.create_tmp_variable(dtype=x.data_type) helper.append_op( type='increment', inputs={'X': [x]}, - outputs={'Out': [x]}, + outputs={'Out': [out]}, attrs={'step': value}) - return x + return out def array_write(x, i, array=None, main_program=None): From 01425309292983205a5fff9658799a0c3efcf6b9 Mon Sep 17 00:00:00 2001 From: Yang Yu Date: Tue, 7 Nov 2017 20:13:16 -0800 Subject: [PATCH 23/41] Rename shrink_state -> shrink_rnn_memory Follow comments --- ...nk_state_op.cc => shrink_rnn_memory_op.cc} | 67 +++++++++---------- .../operators/tensor_array_read_write_op.cc | 1 - python/paddle/v2/framework/layers.py | 2 +- ...ink_state.py => test_shrink_rnn_memory.py} | 4 +- 4 files changed, 33 insertions(+), 41 deletions(-) rename paddle/operators/{shrink_state_op.cc => shrink_rnn_memory_op.cc} (73%) rename python/paddle/v2/framework/tests/{test_shrink_state.py => test_shrink_rnn_memory.py} (95%) diff --git a/paddle/operators/shrink_state_op.cc b/paddle/operators/shrink_rnn_memory_op.cc similarity index 73% rename from paddle/operators/shrink_state_op.cc rename to paddle/operators/shrink_rnn_memory_op.cc index 5aaecf0aae..65bccc0c81 100644 --- a/paddle/operators/shrink_state_op.cc +++ b/paddle/operators/shrink_rnn_memory_op.cc @@ -18,12 +18,12 @@ namespace paddle { namespace operators { -class ShrinkStateOp : public ArrayOp { +class ShrinkRNNMemoryOp : public ArrayOp { public: - ShrinkStateOp(const std::string &type, - const framework::VariableNameMap &inputs, - const framework::VariableNameMap &outputs, - const framework::AttributeMap &attrs) + ShrinkRNNMemoryOp(const std::string &type, + const framework::VariableNameMap &inputs, + const framework::VariableNameMap &outputs, + const framework::AttributeMap &attrs) : ArrayOp(type, inputs, outputs, attrs) {} void Run(const framework::Scope &scope, @@ -36,18 +36,12 @@ class ShrinkStateOp : public ArrayOp { PADDLE_ENFORCE(rank_table_var != nullptr, "RankTable must be set"); auto &rank_table = rank_table_var->Get(); - int dst_num_rows = 0; - - { - auto &rank_items = rank_table.items(); - for (auto &rank_item : rank_items) { - if (offset < rank_item.length) { - ++dst_num_rows; - } else { - break; - } - } - } + auto &rank_items = rank_table.items(); + int dst_num_rows = + std::lower_bound(rank_items.begin(), rank_items.end(), offset, + [](const framework::LoDRankTable::TableItem &a, + size_t b) { return a.length > b; }) - + rank_items.begin(); auto *out_var = scope.FindVar(Output("Out")); PADDLE_ENFORCE(out_var != nullptr, "Output Out must be set"); @@ -58,10 +52,10 @@ class ShrinkStateOp : public ArrayOp { } }; -class ShrinkStateOpProtoMaker : public framework::OpProtoAndCheckerMaker { +class ShrinkRNNMemoryOpProtoMaker : public framework::OpProtoAndCheckerMaker { public: - ShrinkStateOpProtoMaker(framework::OpProto *proto, - framework::OpAttrChecker *op_checker) + ShrinkRNNMemoryOpProtoMaker(framework::OpProto *proto, + framework::OpAttrChecker *op_checker) : OpProtoAndCheckerMaker(proto, op_checker) { AddInput("X", ""); AddInput("RankTable", ""); @@ -71,7 +65,7 @@ class ShrinkStateOpProtoMaker : public framework::OpProtoAndCheckerMaker { } }; -class ShrinkStateOpInferShape : public framework::InferShapeBase { +class ShrinkRNNMemoryInferShape : public framework::InferShapeBase { public: void operator()(framework::InferShapeContext *context) const override { PADDLE_ENFORCE(context->HasInput("X")); @@ -81,19 +75,18 @@ class ShrinkStateOpInferShape : public framework::InferShapeBase { } }; -class ShrinkStateGradOp : public ArrayOp { +class ShrinkRNNMemoryGradOp : public ArrayOp { public: - ShrinkStateGradOp(const std::string &type, - const framework::VariableNameMap &inputs, - const framework::VariableNameMap &outputs, - const framework::AttributeMap &attrs) + ShrinkRNNMemoryGradOp(const std::string &type, + const framework::VariableNameMap &inputs, + const framework::VariableNameMap &outputs, + const framework::AttributeMap &attrs) : ArrayOp(type, inputs, outputs, attrs) {} void Run(const framework::Scope &scope, const platform::DeviceContext &dev_ctx) const override { auto *dout_var = scope.FindVar(Input(framework::GradVarName("Out"))); - auto dx_name = Output(framework::GradVarName("X")); - auto *dx_var = scope.FindVar(dx_name); + auto *dx_var = scope.FindVar(Output(framework::GradVarName("X"))); PADDLE_ENFORCE(dx_var != nullptr, "Input Gradient should not be nullptr"); auto *x_var = scope.FindVar(Input("X")); PADDLE_ENFORCE(x_var != nullptr); @@ -110,7 +103,7 @@ class ShrinkStateGradOp : public ArrayOp { auto height = dout_tensor.dims()[0]; dx_tensor.Slice(0, static_cast(height)) .CopyFrom(dout_tensor, dout_tensor.place(), dev_ctx); - if (height < dout_tensor.dims()[0]) { + if (dx_tensor.dims()[0] < height) { auto rest_tensor = dx_tensor.Slice( static_cast(height), static_cast(dout_tensor.dims()[0])); math::set_constant(dev_ctx, &rest_tensor, 0.0f); @@ -119,7 +112,7 @@ class ShrinkStateGradOp : public ArrayOp { } }; -class ShrikStateGradInferShape : public framework::InferShapeBase { +class ShrinkRNNMemoryGradInferShape : public framework::InferShapeBase { public: void operator()(framework::InferShapeContext *context) const override { PADDLE_ENFORCE(context->HasInput("X")); @@ -129,14 +122,14 @@ class ShrikStateGradInferShape : public framework::InferShapeBase { } }; -class ShrinkStateGradOpMaker : public framework::SingleGradOpDescMaker { +class ShrinkRNNGradOpMaker : public framework::SingleGradOpDescMaker { public: using framework::SingleGradOpDescMaker::SingleGradOpDescMaker; protected: std::unique_ptr Apply() const override { auto *op = new framework::OpDescBind(); - op->SetType("shrink_state_grad"); + op->SetType("shrink_rnn_memory_grad"); op->SetInput("X", Input("X")); op->SetInput(framework::GradVarName("Out"), OutputGrad("Out")); op->SetOutput(framework::GradVarName("X"), InputGrad("X")); @@ -149,8 +142,8 @@ class ShrinkStateGradOpMaker : public framework::SingleGradOpDescMaker { } // namespace paddle namespace ops = paddle::operators; -REGISTER_OPERATOR(shrink_state, ops::ShrinkStateOp, - ops::ShrinkStateOpInferShape, ops::ShrinkStateOpProtoMaker, - ops::ShrinkStateGradOpMaker); -REGISTER_OPERATOR(shrink_state_grad, ops::ShrinkStateGradOp, - ops::ShrikStateGradInferShape); +REGISTER_OPERATOR(shrink_rnn_memory, ops::ShrinkRNNMemoryOp, + ops::ShrinkRNNMemoryInferShape, + ops::ShrinkRNNMemoryOpProtoMaker, ops::ShrinkRNNGradOpMaker); +REGISTER_OPERATOR(shrink_rnn_memory_grad, ops::ShrinkRNNMemoryGradOp, + ops::ShrinkRNNMemoryGradInferShape); diff --git a/paddle/operators/tensor_array_read_write_op.cc b/paddle/operators/tensor_array_read_write_op.cc index 87b6b6929d..eaf6352748 100644 --- a/paddle/operators/tensor_array_read_write_op.cc +++ b/paddle/operators/tensor_array_read_write_op.cc @@ -85,7 +85,6 @@ class WriteToArrayInferVarType : public framework::VarTypeInference { public: void operator()(const framework::OpDescBind &op_desc, framework::BlockDescBind *block) const override { - VLOG(10) << "I am here?"; for (auto &out_var : op_desc.OutputArgumentNames()) { VLOG(10) << "Set Variable " << out_var << " as LOD_TENSOR_ARRAY"; block->Var(out_var)->SetType(framework::VarDesc::LOD_TENSOR_ARRAY); diff --git a/python/paddle/v2/framework/layers.py b/python/paddle/v2/framework/layers.py index 8fc34501c6..4504cf736c 100644 --- a/python/paddle/v2/framework/layers.py +++ b/python/paddle/v2/framework/layers.py @@ -844,7 +844,7 @@ def shrink_memory(x, i, table, main_program=None): helper = LayerHelper('shrink_memory', **locals()) out = helper.create_tmp_variable(dtype=x.data_type) helper.append_op( - type='shrink_state', + type='shrink_rnn_memory', inputs={'X': [x], 'I': [i], 'RankTable': [table]}, diff --git a/python/paddle/v2/framework/tests/test_shrink_state.py b/python/paddle/v2/framework/tests/test_shrink_rnn_memory.py similarity index 95% rename from python/paddle/v2/framework/tests/test_shrink_state.py rename to python/paddle/v2/framework/tests/test_shrink_rnn_memory.py index 2601c769e5..2090455b96 100644 --- a/python/paddle/v2/framework/tests/test_shrink_state.py +++ b/python/paddle/v2/framework/tests/test_shrink_rnn_memory.py @@ -7,8 +7,8 @@ from paddle.v2.framework.framework import g_main_program import numpy -class TestShrinkState(unittest.TestCase): - def test_shrink_state(self): +class TestShrinkRNNMemory(unittest.TestCase): + def test_shrink_rnn_memory(self): x = layers.data('x', shape=[100], data_type='float32') x.stop_gradient = False table = layers.lod_rank_table(x=x) From 3187451ae7dc8f8e1155e952dc725d321967a85a Mon Sep 17 00:00:00 2001 From: Yang Yu Date: Tue, 7 Nov 2017 20:23:09 -0800 Subject: [PATCH 24/41] CompareOp's kernel device type is decided by input tensor place CompareOp can run on CPU even other operators are running on GPU, since opeatations like comparing control flags should be performed only on CPU --- paddle/operators/compare_op.cc | 36 ++++++++++++++++++++++++---------- paddle/platform/transform.h | 4 ---- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/paddle/operators/compare_op.cc b/paddle/operators/compare_op.cc index 8b425d14df..716b5ee92d 100644 --- a/paddle/operators/compare_op.cc +++ b/paddle/operators/compare_op.cc @@ -14,6 +14,7 @@ #include "paddle/operators/compare_op.h" #include "paddle/framework/op_registry.h" + namespace paddle { namespace operators { template @@ -61,19 +62,34 @@ class CompareOpInferShape : public framework::InferShapeBase { } }; +class CompareOp : public framework::OperatorWithKernel { + public: + using framework::OperatorWithKernel::OperatorWithKernel; + + protected: + framework::OpKernelType GetKernelType( + const framework::ExecutionContext &ctx) const override { + framework::OpKernelType kt = OperatorWithKernel::GetKernelType(ctx); + // CompareOp kernel's device type is decided by input tensor place + kt.place_ = ctx.Input("X")->place(); + return kt; + } +}; + } // namespace operators } // namespace paddle -#define REGISTER_LOGICAL_OP(op_type, _equation) \ - struct _##op_type##Comment { \ - static char type[]; \ - static char equation[]; \ - }; \ - char _##op_type##Comment::type[]{#op_type}; \ - char _##op_type##Comment::equation[]{_equation}; \ - REGISTER_OP_WITH_KERNEL( \ - op_type, ::paddle::operators::CompareOpProtoMaker<_##op_type##Comment>, \ - ::paddle::operators::CompareOpInferShape<_##op_type##Comment>, \ +#define REGISTER_LOGICAL_OP(op_type, _equation) \ + struct _##op_type##Comment { \ + static char type[]; \ + static char equation[]; \ + }; \ + char _##op_type##Comment::type[]{#op_type}; \ + char _##op_type##Comment::equation[]{_equation}; \ + REGISTER_OPERATOR( \ + op_type, ::paddle::operators::CompareOp, \ + ::paddle::operators::CompareOpProtoMaker<_##op_type##Comment>, \ + ::paddle::operators::CompareOpInferShape<_##op_type##Comment>, \ ::paddle::framework::EmptyGradOpMaker); REGISTER_LOGICAL_OP(less_than, "Out = X < Y"); diff --git a/paddle/platform/transform.h b/paddle/platform/transform.h index f196868c72..bb9d59ec0a 100644 --- a/paddle/platform/transform.h +++ b/paddle/platform/transform.h @@ -49,8 +49,6 @@ struct Transform { template void operator()(const DeviceContext& context, InputIter first, InputIter last, OutputIter result, UnaryOperation op) { - auto place = context.GetPlace(); - PADDLE_ENFORCE(is_cpu_place(place), "It must use CPU place."); std::transform(first, last, result, op); } @@ -59,8 +57,6 @@ struct Transform { void operator()(const DeviceContext& context, InputIter1 first1, InputIter1 last1, InputIter2 first2, OutputIter result, BinaryOperation op) { - auto place = context.GetPlace(); - PADDLE_ENFORCE(is_cpu_place(place), "It must use CPU place."); std::transform(first1, last1, first2, result, op); } }; From 6308ccc265247974c9ab253948fbb7b90c77d087 Mon Sep 17 00:00:00 2001 From: typhoonzero Date: Wed, 8 Nov 2017 13:03:57 +0800 Subject: [PATCH 25/41] fix accuracy cudamemset --- paddle/operators/accuracy_op.cu | 4 +++- python/paddle/v2/framework/tests/test_accuracy_op.py | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/paddle/operators/accuracy_op.cu b/paddle/operators/accuracy_op.cu index d0c4c0d25d..ccb2c06c22 100644 --- a/paddle/operators/accuracy_op.cu +++ b/paddle/operators/accuracy_op.cu @@ -14,6 +14,7 @@ limitations under the License. */ #include #include +#include #include "paddle/operators/accuracy_op.h" #include "paddle/platform/cuda_helper.h" @@ -65,7 +66,8 @@ class AccuracyOpCUDAKernel : public framework::OpKernel { size_t num_samples = inference->dims()[0]; size_t infer_width = inference->dims()[1]; - cudaMemset((void**)&accuracy_data, 0, sizeof(float)); + cudaError_t e = cudaMemset(accuracy_data, 0, sizeof(float)); + PADDLE_ENFORCE_EQ(0, e, "cudaMemset error"); if (num_samples == 0) { return; diff --git a/python/paddle/v2/framework/tests/test_accuracy_op.py b/python/paddle/v2/framework/tests/test_accuracy_op.py index 85eabdcfb8..6536c297e8 100644 --- a/python/paddle/v2/framework/tests/test_accuracy_op.py +++ b/python/paddle/v2/framework/tests/test_accuracy_op.py @@ -26,5 +26,4 @@ class TestAccuracyOp(OpTest): if __name__ == '__main__': - exit(0) unittest.main() From b007055e9d72fc8cb00177aa89cc4fbb245ef8b2 Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Wed, 8 Nov 2017 14:34:08 +0800 Subject: [PATCH 26/41] reduce the lr in case of nan in small batchsize --- benchmark/paddle/image/vgg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmark/paddle/image/vgg.py b/benchmark/paddle/image/vgg.py index b8429975f5..420884ed8e 100644 --- a/benchmark/paddle/image/vgg.py +++ b/benchmark/paddle/image/vgg.py @@ -13,7 +13,7 @@ define_py_data_sources2( settings( batch_size=batch_size, - learning_rate=0.01 / batch_size, + learning_rate=0.001 / batch_size, learning_method=MomentumOptimizer(0.9), regularization=L2Regularization(0.0005 * batch_size)) From 11ee50ceb93bc9a350d6de10134a239ebf6dfde2 Mon Sep 17 00:00:00 2001 From: typhoonzero Date: Wed, 8 Nov 2017 16:31:11 +0800 Subject: [PATCH 27/41] update --- paddle/operators/accuracy_op.cu | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/paddle/operators/accuracy_op.cu b/paddle/operators/accuracy_op.cu index ccb2c06c22..1776f33105 100644 --- a/paddle/operators/accuracy_op.cu +++ b/paddle/operators/accuracy_op.cu @@ -14,7 +14,6 @@ limitations under the License. */ #include #include -#include #include "paddle/operators/accuracy_op.h" #include "paddle/platform/cuda_helper.h" @@ -66,8 +65,7 @@ class AccuracyOpCUDAKernel : public framework::OpKernel { size_t num_samples = inference->dims()[0]; size_t infer_width = inference->dims()[1]; - cudaError_t e = cudaMemset(accuracy_data, 0, sizeof(float)); - PADDLE_ENFORCE_EQ(0, e, "cudaMemset error"); + PADDLE_ENFORCE(cudaMemset(accuracy_data, 0, sizeof(float))); if (num_samples == 0) { return; From 870650d8c171bbcd1e6e0c1da5b1057cf066d32b Mon Sep 17 00:00:00 2001 From: "Yang Yang(Tony)" Date: Wed, 8 Nov 2017 00:50:15 -0800 Subject: [PATCH 28/41] Static lstm sanity check (#5365) * add fill_constant_batch_size_like_op to rnn h_boot * first commit * merge develop; fix conflict * update to main_program --- .../fill_constant_batch_size_like_op.cc | 4 +- paddle/operators/lstm_unit_op.cc | 8 +- python/paddle/v2/framework/layers.py | 72 +++++++++++- .../tests/test_understand_sentiment_lstm.py | 107 ++++++++++++++++++ 4 files changed, 182 insertions(+), 9 deletions(-) create mode 100644 python/paddle/v2/framework/tests/test_understand_sentiment_lstm.py diff --git a/paddle/operators/fill_constant_batch_size_like_op.cc b/paddle/operators/fill_constant_batch_size_like_op.cc index f86ee3c3d8..85871ebbfc 100644 --- a/paddle/operators/fill_constant_batch_size_like_op.cc +++ b/paddle/operators/fill_constant_batch_size_like_op.cc @@ -75,10 +75,10 @@ class FillConstantBatchSizeLikeOpMaker "with the specified value"); AddAttr>("shape", "(vector) The shape of the output"); AddAttr("input_dim_idx", - "(int, default 0) the index of input's batch size dimension") + "(int, default 0) The index of input's batch size dimension") .SetDefault(0); AddAttr("output_dim_idx", - "(int, default 0) the index of output's batch size dimension") + "(int, default 0) The index of output's batch size dimension") .SetDefault(0); AddAttr("value", "(float, default 0) The value to be filled") .SetDefault(0.0f); diff --git a/paddle/operators/lstm_unit_op.cc b/paddle/operators/lstm_unit_op.cc index f4519ec16f..18b9cdf2a3 100644 --- a/paddle/operators/lstm_unit_op.cc +++ b/paddle/operators/lstm_unit_op.cc @@ -34,10 +34,10 @@ class LstmUnitOp : public framework::OperatorWithKernel { auto c_prev_dims = ctx->GetInputDim("C_prev"); PADDLE_ENFORCE_EQ(x_dims.size(), 2, "Input(X)'s rank must be 2."); - PADDLE_ENFORCE(x_dims[0] == c_prev_dims[0], - "Batch size of inputs and states must be equal"); - PADDLE_ENFORCE(x_dims[1] == c_prev_dims[1] * 4, - "Dimension of FC should equal to prev state * 4"); + PADDLE_ENFORCE_EQ(x_dims[0], c_prev_dims[0], + "Batch size of inputs and states must be equal"); + PADDLE_ENFORCE_EQ(x_dims[1], c_prev_dims[1] * 4, + "Dimension of FC should equal to prev state * 4"); int b_size = c_prev_dims[0]; // batch size int s_dim = c_prev_dims[1]; // state dim diff --git a/python/paddle/v2/framework/layers.py b/python/paddle/v2/framework/layers.py index d42af89eae..f1c09af8ed 100644 --- a/python/paddle/v2/framework/layers.py +++ b/python/paddle/v2/framework/layers.py @@ -134,9 +134,7 @@ def _create_op_func_(op_type): o_name = not_intermediate_outputs[0].name intermediate_output_names = [output.name for output in intermediate_outputs] - def func(**kwargs): - helper = LayerHelper(op_type, **kwargs) - inputs = dict() + def infer_and_check_data_type(op_proto, **kwargs): dtype = None for ipt in op_proto.inputs: name = _convert_(ipt.name) @@ -153,6 +151,20 @@ def _create_op_func_(op_type): elif dtype != each.data_type: raise ValueError( "operator {0} must input same dtype".format(op_type)) + + return dtype + + def func(**kwargs): + helper = LayerHelper(op_type, **kwargs) + + dtype = infer_and_check_data_type(op_proto, **kwargs) + + inputs = dict() + for ipt in op_proto.inputs: + name = _convert_(ipt.name) + val = kwargs.pop(name, []) + if not isinstance(val, list) and not isinstance(val, tuple): + val = [val] inputs[ipt.name] = val outputs = dict() @@ -178,6 +190,20 @@ _create_op_func_('reshape') _create_op_func_('elementwise_add') _create_op_func_('sigmoid') _create_op_func_('scale') +_create_op_func_('reshape') +_create_op_func_('transpose') + + +def fill_constant(data_type, shape, value=None, program=None): + helper = LayerHelper('fill_constant', **locals()) + out = helper.create_tmp_variable(dtype=data_type) + helper.append_op( + type='fill_constant', + outputs={'Out': [out]}, + attrs={'data_type': data_type, + 'shape': shape, + 'value': value}) + return out def cast(x, data_type, main_program=None): @@ -762,6 +788,46 @@ class StaticRNN(object): }) +def lstm(x, + c_pre_init, + hidden_dim, + forget_bias=None, + main_program=None, + startup_program=None): + helper = LayerHelper('lstm_unit', **locals()) + rnn = StaticRNN() + with rnn.step(): + c_pre = rnn.memory(init=c_pre_init) + x_t = rnn.step_input(x) + + before_fc = concat( + input=[x_t, c_pre], + axis=1, + main_program=main_program, + startup_program=startup_program) + after_fc = fc(input=before_fc, + size=hidden_dim * 4, + main_program=main_program, + startup_program=startup_program) + + data_type = x.data_type + c = helper.create_tmp_variable(data_type) + h = helper.create_tmp_variable(data_type) + + helper.append_op( + type='lstm_unit', + inputs={"X": after_fc, + "C_prev": c_pre}, + outputs={"C": c, + "H": h}, + attrs={"forget_bias": forget_bias}) + + rnn.update_memory(c_pre, c) + rnn.output(h) + + return rnn() + + def lod_rank_table(x, level=0, main_program=None): helper = LayerHelper("lod_rank_table", **locals()) table = helper.create_variable( diff --git a/python/paddle/v2/framework/tests/test_understand_sentiment_lstm.py b/python/paddle/v2/framework/tests/test_understand_sentiment_lstm.py new file mode 100644 index 0000000000..26cbd01bc0 --- /dev/null +++ b/python/paddle/v2/framework/tests/test_understand_sentiment_lstm.py @@ -0,0 +1,107 @@ +import paddle.v2 as paddle +import paddle.v2.framework.layers as layers +import paddle.v2.framework.core as core +import paddle.v2.framework.optimizer as optimizer + +from paddle.v2.framework.framework import g_main_program, g_startup_program +from paddle.v2.framework.executor import Executor + +import numpy as np + + +def lstm_net(dict_dim, class_dim=2, emb_dim=32, seq_len=80, batch_size=50): + data = layers.data( + name="words", + shape=[seq_len * batch_size, 1], + append_batch_size=False, + data_type="int64") + label = layers.data( + name="label", + shape=[batch_size, 1], + append_batch_size=False, + data_type="int64") + + emb = layers.embedding(input=data, size=[dict_dim, emb_dim]) + emb = layers.reshape(x=emb, shape=[batch_size, seq_len, emb_dim]) + emb = layers.transpose(x=emb, axis=[1, 0, 2]) + + c_pre_init = layers.fill_constant( + dtype=emb.data_type, shape=[batch_size, emb_dim], value=0.0) + layer_1_out = layers.lstm(emb, c_pre_init=c_pre_init, hidden_dim=emb_dim) + layer_1_out = layers.transpose(x=layer_1_out, axis=[1, 0, 2]) + + prediction = layers.fc(input=layer_1_out, size=class_dim, act="softmax") + cost = layers.cross_entropy(input=prediction, label=label) + + avg_cost = layers.mean(x=cost) + adam_optimizer = optimizer.AdamOptimizer(learning_rate=0.002) + opts = adam_optimizer.minimize(avg_cost) + acc = layers.accuracy(input=prediction, label=label) + + return avg_cost, acc + + +def to_lodtensor(data, place): + seq_lens = [len(seq) for seq in data] + cur_len = 0 + lod = [cur_len] + for l in seq_lens: + cur_len += l + lod.append(cur_len) + flattened_data = np.concatenate(data, axis=0).astype("int64") + flattened_data = flattened_data.reshape([len(flattened_data), 1]) + res = core.LoDTensor() + res.set(flattened_data, place) + res.set_lod([lod]) + return res + + +def chop_data(data, chop_len=80, batch_len=50): + data = [(x[0][:chop_len], x[1]) for x in data if len(x[0]) >= chop_len] + + return data[:batch_len] + + +def prepare_feed_data(data, place): + 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([50, 1]) + tensor_label = core.LoDTensor() + tensor_label.set(label, place) + + return tensor_words, tensor_label + + +def main(): + word_dict = paddle.dataset.imdb.word_dict() + cost, acc = lstm_net(dict_dim=len(word_dict), class_dim=2) + + batch_size = 100 + train_data = paddle.batch( + paddle.reader.buffered( + paddle.dataset.imdb.train(word_dict), size=batch_size * 10), + batch_size=batch_size) + + data = chop_data(next(train_data())) + + place = core.CPUPlace() + tensor_words, tensor_label = prepare_feed_data(data, place) + exe = Executor(place) + exe.run(g_startup_program) + + while True: + outs = exe.run(g_main_program, + feed={"words": tensor_words, + "label": tensor_label}, + fetch_list=[cost, acc]) + cost_val = np.array(outs[0]) + acc_val = np.array(outs[1]) + + print("cost=" + str(cost_val) + " acc=" + str(acc_val)) + if acc_val > 0.9: + break + + +if __name__ == '__main__': + main() From 151332298330b6eb1a42ec31a4d977a8611072c9 Mon Sep 17 00:00:00 2001 From: Luo Tao Date: Wed, 8 Nov 2017 17:04:46 +0800 Subject: [PATCH 29/41] add doc for image.py --- doc/api/v2/data.rst | 113 ++------------------------------ doc/api/v2/data/data_reader.rst | 36 ++++++++++ doc/api/v2/data/dataset.rst | 75 +++++++++++++++++++++ doc/api/v2/data/image.rst | 5 ++ python/paddle/v2/image.py | 74 ++++++++++++++------- 5 files changed, 170 insertions(+), 133 deletions(-) create mode 100644 doc/api/v2/data/data_reader.rst create mode 100644 doc/api/v2/data/dataset.rst create mode 100644 doc/api/v2/data/image.rst diff --git a/doc/api/v2/data.rst b/doc/api/v2/data.rst index fef87c4fbd..b56c7332cc 100644 --- a/doc/api/v2/data.rst +++ b/doc/api/v2/data.rst @@ -2,112 +2,9 @@ Data Reader Interface and DataSets ================================== +.. toctree:: + :maxdepth: 1 -DataTypes -========= - -.. automodule:: paddle.v2.data_type - :members: - :noindex: - -DataFeeder -========== - -.. automodule:: paddle.v2.data_feeder - :members: - :noindex: - -Reader -====== - -.. automodule:: paddle.v2.reader - :members: - :noindex: - -.. automodule:: paddle.v2.reader.creator - :members: - :noindex: - -minibatch -========= - -.. automodule:: paddle.v2.minibatch - :members: - :noindex: - -Dataset -======= - -.. automodule:: paddle.v2.dataset - :members: - :noindex: - -mnist -+++++ - -.. automodule:: paddle.v2.dataset.mnist - :members: - :noindex: - -cifar -+++++ - -.. automodule:: paddle.v2.dataset.cifar - :members: - :noindex: - -conll05 -+++++++ - -.. automodule:: paddle.v2.dataset.conll05 - :members: get_dict,get_embedding,test - :noindex: - -imdb -++++ - -.. automodule:: paddle.v2.dataset.imdb - :members: - :noindex: - -imikolov -++++++++ - -.. automodule:: paddle.v2.dataset.imikolov - :members: - :noindex: - -movielens -+++++++++ - -.. automodule:: paddle.v2.dataset.movielens - :members: - :noindex: - -.. autoclass:: paddle.v2.dataset.movielens.MovieInfo - :noindex: - -.. autoclass:: paddle.v2.dataset.movielens.UserInfo - :noindex: - -sentiment -+++++++++ - -.. automodule:: paddle.v2.dataset.sentiment - :members: - :noindex: - -uci_housing -+++++++++++ - -.. automodule:: paddle.v2.dataset.uci_housing - :members: - :noindex: - -wmt14 -+++++ - -.. automodule:: paddle.v2.dataset.wmt14 - :members: - :noindex: - + data/data_reader.rst + data/image.rst + data/dataset.rst diff --git a/doc/api/v2/data/data_reader.rst b/doc/api/v2/data/data_reader.rst new file mode 100644 index 0000000000..2ccfec9c28 --- /dev/null +++ b/doc/api/v2/data/data_reader.rst @@ -0,0 +1,36 @@ +===================== +Data Reader Interface +===================== + + +DataTypes +========= + +.. automodule:: paddle.v2.data_type + :members: + :noindex: + +DataFeeder +========== + +.. automodule:: paddle.v2.data_feeder + :members: + :noindex: + +Reader +====== + +.. automodule:: paddle.v2.reader + :members: + :noindex: + +.. automodule:: paddle.v2.reader.creator + :members: + :noindex: + +minibatch +========= + +.. automodule:: paddle.v2.minibatch + :members: + :noindex: diff --git a/doc/api/v2/data/dataset.rst b/doc/api/v2/data/dataset.rst new file mode 100644 index 0000000000..6a8ecc5bb1 --- /dev/null +++ b/doc/api/v2/data/dataset.rst @@ -0,0 +1,75 @@ +Dataset +======= + +.. automodule:: paddle.v2.dataset + :members: + :noindex: + +mnist ++++++ + +.. automodule:: paddle.v2.dataset.mnist + :members: + :noindex: + +cifar ++++++ + +.. automodule:: paddle.v2.dataset.cifar + :members: + :noindex: + +conll05 ++++++++ + +.. automodule:: paddle.v2.dataset.conll05 + :members: get_dict,get_embedding,test + :noindex: + +imdb +++++ + +.. automodule:: paddle.v2.dataset.imdb + :members: + :noindex: + +imikolov +++++++++ + +.. automodule:: paddle.v2.dataset.imikolov + :members: + :noindex: + +movielens ++++++++++ + +.. automodule:: paddle.v2.dataset.movielens + :members: + :noindex: + +.. autoclass:: paddle.v2.dataset.movielens.MovieInfo + :noindex: + +.. autoclass:: paddle.v2.dataset.movielens.UserInfo + :noindex: + +sentiment ++++++++++ + +.. automodule:: paddle.v2.dataset.sentiment + :members: + :noindex: + +uci_housing ++++++++++++ + +.. automodule:: paddle.v2.dataset.uci_housing + :members: + :noindex: + +wmt14 ++++++ + +.. automodule:: paddle.v2.dataset.wmt14 + :members: + :noindex: diff --git a/doc/api/v2/data/image.rst b/doc/api/v2/data/image.rst new file mode 100644 index 0000000000..97651ffa6b --- /dev/null +++ b/doc/api/v2/data/image.rst @@ -0,0 +1,5 @@ +Image Interface +=============== + +.. automodule:: paddle.v2.image + :members: diff --git a/python/paddle/v2/image.py b/python/paddle/v2/image.py index 965d965335..7408ea8ef6 100644 --- a/python/paddle/v2/image.py +++ b/python/paddle/v2/image.py @@ -1,33 +1,35 @@ -import numpy as np -try: - import cv2 -except ImportError: - cv2 = None -import os -import tarfile -import cPickle - -__all__ = [ - "load_image_bytes", "load_image", "resize_short", "to_chw", "center_crop", - "random_crop", "left_right_flip", "simple_transform", "load_and_transform", - "batch_images_from_tar" -] """ This file contains some common interfaces for image preprocess. Many users are confused about the image layout. We introduce the image layout as follows. - CHW Layout + - The abbreviations: C=channel, H=Height, W=Width - The default layout of image opened by cv2 or PIL is HWC. PaddlePaddle only supports the CHW layout. And CHW is simply a transpose of HWC. It must transpose the input image. - Color format: RGB or BGR + OpenCV use BGR color format. PIL use RGB color format. Both formats can be used for training. Noted that, the format should be keep consistent between the training and inference peroid. """ +import numpy as np +try: + import cv2 +except ImportError: + cv2 = None +import os +import tarfile +import cPickle + +__all__ = [ + "load_image_bytes", "load_image", "resize_short", "to_chw", "center_crop", + "random_crop", "left_right_flip", "simple_transform", "load_and_transform", + "batch_images_from_tar" +] def batch_images_from_tar(data_file, @@ -36,17 +38,18 @@ def batch_images_from_tar(data_file, num_per_batch=1024): """ Read images from tar file and batch them into batch file. - param data_file: path of image tar file - type data_file: string - param dataset_name: 'train','test' or 'valid' - type dataset_name: string - param img2label: a dic with image file name as key + + :param data_file: path of image tar file + :type data_file: string + :param dataset_name: 'train','test' or 'valid' + :type dataset_name: string + :param img2label: a dic with image file name as key and image's label as value - type img2label: dic - param num_per_batch: image number per batch file - type num_per_batch: int - return: path of list file containing paths of batch file - rtype: string + :type img2label: dic + :param num_per_batch: image number per batch file + :type num_per_batch: int + :return: path of list file containing paths of batch file + :rtype: string """ batch_dir = data_file + "_batch" out_path = "%s/%s" % (batch_dir, dataset_name) @@ -99,14 +102,16 @@ def load_image_bytes(bytes, is_color=True): Example usage: .. code-block:: python + with open('cat.jpg') as f: im = load_image_bytes(f.read()) :param bytes: the input image bytes array. - :type file: str + :type bytes: str :param is_color: If set is_color True, it will load and return a color image. Otherwise, it will load and return a gray image. + :type is_color: bool """ flag = 1 if is_color else 0 file_bytes = np.asarray(bytearray(bytes), dtype=np.uint8) @@ -121,6 +126,7 @@ def load_image(file, is_color=True): Example usage: .. code-block:: python + im = load_image('cat.jpg') :param file: the input image path. @@ -128,6 +134,7 @@ def load_image(file, is_color=True): :param is_color: If set is_color True, it will load and return a color image. Otherwise, it will load and return a gray image. + :type is_color: bool """ # cv2.IMAGE_COLOR for OpenCV3 # cv2.CV_LOAD_IMAGE_COLOR for older OpenCV Version @@ -147,6 +154,7 @@ def resize_short(im, size): Example usage: .. code-block:: python + im = load_image('cat.jpg') im = resize_short(im, 256) @@ -175,6 +183,7 @@ def to_chw(im, order=(2, 0, 1)): Example usage: .. code-block:: python + im = load_image('cat.jpg') im = resize_short(im, 256) im = to_chw(im) @@ -196,6 +205,7 @@ def center_crop(im, size, is_color=True): Example usage: .. code-block:: python + im = center_crop(im, 224) :param im: the input image with HWC layout. @@ -223,6 +233,7 @@ def random_crop(im, size, is_color=True): Example usage: .. code-block:: python + im = random_crop(im, 224) :param im: the input image with HWC layout. @@ -251,6 +262,7 @@ def left_right_flip(im): Example usage: .. code-block:: python + im = left_right_flip(im) :paam im: input image with HWC layout @@ -275,6 +287,7 @@ def simple_transform(im, Example usage: .. code-block:: python + im = simple_transform(im, 256, 224, True) :param im: The input image with HWC layout. @@ -285,6 +298,11 @@ def simple_transform(im, :type crop_size: int :param is_train: Whether it is training or not. :type is_train: bool + :param is_color: whether the image is color or not. + :type is_color: bool + :param mean: the mean values, which can be element-wise mean values or + mean values per channel. + :type mean: numpy array | list """ im = resize_short(im, resize_size) if is_train: @@ -324,6 +342,7 @@ def load_and_transform(filename, Example usage: .. code-block:: python + im = load_and_transform('cat.jpg', 256, 224, True) :param filename: The file name of input image. @@ -334,6 +353,11 @@ def load_and_transform(filename, :type crop_size: int :param is_train: Whether it is training or not. :type is_train: bool + :param is_color: whether the image is color or not. + :type is_color: bool + :param mean: the mean values, which can be element-wise mean values or + mean values per channel. + :type mean: numpy array | list """ im = load_image(filename) im = simple_transform(im, resize_size, crop_size, is_train, is_color, mean) From cfad83ce894ed558715354dca79ffc0629af1809 Mon Sep 17 00:00:00 2001 From: yangyaming Date: Wed, 8 Nov 2017 19:02:57 +0800 Subject: [PATCH 30/41] Add MulValueLayer. --- paddle/function/CMakeLists.txt | 1 + paddle/function/FunctionTest.h | 10 ++ paddle/function/MulValueOp.cpp | 155 ++++++++++++++++++ paddle/function/MulValueOp.h | 55 +++++++ paddle/function/MulValueOpGpu.cu | 116 +++++++++++++ paddle/function/MulValueOpTest.cpp | 82 +++++++++ paddle/gserver/layers/MulValueLayer.cpp | 75 +++++++++ paddle/gserver/layers/MulValueLayer.h | 52 ++++++ paddle/gserver/tests/test_LayerGrad.cpp | 31 ++++ paddle/math/tests/TensorCheck.h | 2 +- proto/ModelConfig.proto | 6 + python/paddle/trainer/config_parser.py | 17 ++ .../paddle/trainer_config_helpers/layers.py | 50 ++++++ .../tests/configs/file_list.sh | 2 +- .../protostr/test_mul_value_layer.protostr | 48 ++++++ .../tests/configs/test_mul_value_layer.py | 10 ++ 16 files changed, 710 insertions(+), 2 deletions(-) create mode 100644 paddle/function/MulValueOp.cpp create mode 100644 paddle/function/MulValueOp.h create mode 100644 paddle/function/MulValueOpGpu.cu create mode 100644 paddle/function/MulValueOpTest.cpp create mode 100644 paddle/gserver/layers/MulValueLayer.cpp create mode 100644 paddle/gserver/layers/MulValueLayer.h create mode 100644 python/paddle/trainer_config_helpers/tests/configs/protostr/test_mul_value_layer.protostr create mode 100644 python/paddle/trainer_config_helpers/tests/configs/test_mul_value_layer.py diff --git a/paddle/function/CMakeLists.txt b/paddle/function/CMakeLists.txt index 4fd72d64a9..1b3068b8ff 100644 --- a/paddle/function/CMakeLists.txt +++ b/paddle/function/CMakeLists.txt @@ -45,6 +45,7 @@ if(WITH_GPU) add_simple_unittest(BlockExpandOpTest) add_simple_unittest(CropOpTest) add_simple_unittest(SwitchOpTest) + add_simple_unittest(MulValueOpTest) endif() add_simple_unittest(Im2ColTest) diff --git a/paddle/function/FunctionTest.h b/paddle/function/FunctionTest.h index ba446bf92d..2fc51a3aa8 100644 --- a/paddle/function/FunctionTest.h +++ b/paddle/function/FunctionTest.h @@ -110,6 +110,7 @@ public: function2_(FunctionBase::funcRegistrar_.createByType(name2)) { function1_->init(config); function2_->init(config); + initArgsCallBack_ = nullptr; } ~Compare2Function() {} @@ -170,6 +171,10 @@ public: *seq2_)); } + void registerInitCallBack(std::function callback) { + initArgsCallBack_ = callback; + } + // output need only contains shape, do not contains data. void addOutputs(const BufferArg& output, ArgType argType = ASSIGN_TO) { size_t size = @@ -340,6 +345,10 @@ protected: initArg(*func1Inputs_[i]); } + if (initArgsCallBack_ != nullptr) { + initArgsCallBack_(*func1Inputs_[i], i); + } + copyArg_(*func1Inputs_[i], *func2Inputs_[i]); } } @@ -386,6 +395,7 @@ protected: std::shared_ptr seq1_; std::shared_ptr seq2_; test::CopyArgument copyArg_; + std::function initArgsCallBack_; }; class CpuGpuFuncCompare diff --git a/paddle/function/MulValueOp.cpp b/paddle/function/MulValueOp.cpp new file mode 100644 index 0000000000..fec30aac02 --- /dev/null +++ b/paddle/function/MulValueOp.cpp @@ -0,0 +1,155 @@ +/* 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 "MulValueOp.h" +#include "paddle/function/TensorShape.h" + +namespace paddle { + +template <> +void MulValue(real* outputs, + const real* inputs, + const real* indices, + const TensorShape shape, + const FuncConfig& conf) { + real value = conf.get("value"); + + int number = shape[0]; + int channel = shape[1]; + int height = shape[2]; + int width = shape[3]; + + memcpy(outputs, inputs, number * channel * height * width * sizeof(real)); + + for (int n = 0; n < number; ++n) { + // indices start from 1 + int offset = n * 6; + for (int c = indices[offset] - 1; c < indices[offset + 1]; ++c) { + for (int h = indices[offset + 2] - 1; h < indices[offset + 3]; ++h) { + for (int w = indices[offset + 4] - 1; w < indices[offset + 5]; ++w) { + int idx = ((n * channel + c) * height + h) * width + w; + outputs[idx] *= value; + } + } + } + } +} + +template <> +void MulValueGrad(const real* inGrad, + real* outGrad, + const real* indices, + const TensorShape shape, + const FuncConfig& conf) { + real value = conf.get("value"); + + int number = shape[0]; + int channel = shape[1]; + int height = shape[2]; + int width = shape[3]; + + for (int n = 0; n < number; ++n) { + for (int c = 0; c < channel; ++c) { + for (int h = 0; h < height; ++h) { + for (int w = 0; w < width; ++w) { + int idx = ((n * channel + c) * height + h) * width + w; + int offset = n * 6; + if (c >= (indices[offset] - 1) && c <= (indices[offset + 1] - 1) && + h >= (indices[offset + 2] - 1) && + h <= (indices[offset + 3] - 1) && + w >= (indices[offset + 4] - 1) && + w <= (indices[offset + 5] - 1)) { + outGrad[idx] += inGrad[idx] * value; + } else { + outGrad[idx] += inGrad[idx]; + } + } + } + } + } +} + +/** + * \brief For each instance, MulValue can be used to multiply a value to a + * specified sub continuous region. By providing start index and end + * index for C/H/W, you can specify the location and shape of the region. + * + * Argument in this Function: + * \param inputs A 4-D tensor with shape [N, C, H, W], only one input. + * \param indices A 2-D tensor with shape [N, 6], indicates the sub region. + * \param outputs A 4-D tensor with same shape as inputs, output value. + */ +template +class MulValueFunc : public FunctionBase { +public: + void init(const FuncConfig& config) override { conf_ = config; } + + void calc(const BufferArgs& inputs, const BufferArgs& outputs) override { + CHECK_EQ(2UL, inputs.size()); + CHECK_EQ(1UL, outputs.size()); + CHECK_EQ(outputs[0].getArgType(), ASSIGN_TO); + + TensorShape shape = inputs[0].shape(); + + MulValue(outputs[0].data(), + inputs[0].data(), + inputs[1].data(), + shape, + conf_); + } + +private: + FuncConfig conf_; +}; + +/** + * \brief The backward propagation of MulValue Function. + * + * Argument in this Function: + * \param inputs A 4-D tensor with shape [N, C, H, W], output gradient. + * \param indices A 2-D tensor with shape [N, 6], indicates the sub region. + * \param outputs A 4-D tensor with shape [N, C, H, W], gradient of input value. + */ + +template +class MulValueGradFunc : public FunctionBase { +public: + void init(const FuncConfig& config) override { conf_ = config; } + + void calc(const BufferArgs& inputs, const BufferArgs& outputs) override { + CHECK_EQ(2UL, inputs.size()); + CHECK_EQ(1UL, outputs.size()); + CHECK_EQ(outputs[0].getArgType(), ADD_TO); + + TensorShape shape = inputs[0].shape(); + + MulValueGrad(inputs[0].data(), + outputs[0].data(), + inputs[1].data(), + shape, + conf_); + } + +private: + FuncConfig conf_; +}; + +REGISTER_TYPED_FUNC(MulValue, CPU, MulValueFunc); +REGISTER_TYPED_FUNC(MulValueGrad, CPU, MulValueGradFunc); +#ifdef PADDLE_WITH_CUDA +REGISTER_TYPED_FUNC(MulValue, GPU, MulValueFunc); +REGISTER_TYPED_FUNC(MulValueGrad, GPU, MulValueGradFunc); +#endif + +} // namespace paddle diff --git a/paddle/function/MulValueOp.h b/paddle/function/MulValueOp.h new file mode 100644 index 0000000000..2e7ce105c7 --- /dev/null +++ b/paddle/function/MulValueOp.h @@ -0,0 +1,55 @@ +/* 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 "Function.h" + +namespace paddle { + +/** + * \brief Function to multiply a value to values in specified sub continuous + * region. Indices must be provided to indcate the location and shape of + * the region and the multiplied value is passed by configure variable. + * + * + * \param[out] outputs Output value. + * \param[in] inputs Input data which contains NCHW information. + * \param[in] indices Indices data to indcate the sub region. + * \param[in] shape Tensor shape of input value. + * \param[in] conf Configure variable which contains the multiplied value. + */ +template +void MulValue(real* outputs, + const real* inputs, + const real* indices, + const TensorShape shape, + const FuncConfig& conf); + +/** + * \brief Back propagation function of MulValue. + * + * \param[out] inGrad Gradients of previous layer. + * \param[in] outGrad Output gradient. + * \param[in] indices Indices data. + * \param[in] shape The Shape of input tensor. + * \param[in] conf Configure variable. + */ +template +void MulValueGrad(const real* inGrad, + real* outGrad, + const real* indices, + const TensorShape shape, + const FuncConfig& conf); +} // namespace paddle diff --git a/paddle/function/MulValueOpGpu.cu b/paddle/function/MulValueOpGpu.cu new file mode 100644 index 0000000000..005be82131 --- /dev/null +++ b/paddle/function/MulValueOpGpu.cu @@ -0,0 +1,116 @@ +/* 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 "MulValueOp.h" +#include "hl_base.h" + +namespace paddle { + +__global__ void KeMulValue(real* outputs, + const real* inputs, + const real* indices, + real value, + int channel, + int height, + int width, + int nthreads) { + const int idx = threadIdx.x + blockIdx.x * blockDim.x; + if (idx < nthreads) { + const int w = idx % width; + const int h = (idx / width) % height; + const int c = (idx / width / height) % channel; + const int n = idx / width / height / channel; + + const int offset = n * 6; + if (c >= (indices[offset] - 1) && c <= (indices[offset + 1] - 1) && + h >= (indices[offset + 2] - 1) && h <= (indices[offset + 3] - 1) && + w >= (indices[offset + 4] - 1) && w <= (indices[offset + 5] - 1)) { + outputs[idx] = inputs[idx] * value; + } else { + outputs[idx] = inputs[idx]; + } + } +} + +template <> +void MulValue(real* outputs, + const real* inputs, + const real* indices, + const TensorShape shape, + const FuncConfig& conf) { + real value = conf.get("value"); + + int number = shape[0]; + int channel = shape[1]; + int height = shape[2]; + int width = shape[3]; + + size_t nth = number * channel * height * width; + int blockSize = 1024; + int gridSize = (nth + blockSize - 1) / blockSize; + + KeMulValue<<>>( + outputs, inputs, indices, value, channel, height, width, nth); + CHECK_SYNC("MulValue"); +} + +__global__ void KeMulValueDiff(const real* inGrad, + real* outGrad, + const real* indices, + real value, + int channel, + int height, + int width, + int nthreads) { + const int idx = threadIdx.x + blockIdx.x * blockDim.x; + if (idx < nthreads) { + const int w = idx % width; + const int h = (idx / width) % height; + const int c = (idx / width / height) % channel; + const int n = idx / width / height / channel; + + const int offset = n * 6; + if (c >= (indices[offset] - 1) && c <= (indices[offset + 1] - 1) && + h >= (indices[offset + 2] - 1) && h <= (indices[offset + 3] - 1) && + w >= (indices[offset + 4] - 1) && w <= (indices[offset + 5] - 1)) { + outGrad[idx] += inGrad[idx] * value; + } else { + outGrad[idx] += inGrad[idx]; + } + } +} + +template <> +void MulValueGrad(const real* inGrad, + real* outGrad, + const real* indices, + const TensorShape shape, + const FuncConfig& conf) { + real value = conf.get("value"); + + int number = shape[0]; + int channel = shape[1]; + int height = shape[2]; + int width = shape[3]; + + size_t nth = number * channel * height * width; + int blockSize = 1024; + int gridSize = (nth + blockSize - 1) / blockSize; + + KeMulValueDiff<<>>( + inGrad, outGrad, indices, value, channel, height, width, nth); + CHECK_SYNC("MulValueGrad"); +} + +} // namespace paddle diff --git a/paddle/function/MulValueOpTest.cpp b/paddle/function/MulValueOpTest.cpp new file mode 100644 index 0000000000..c1d5a3e544 --- /dev/null +++ b/paddle/function/MulValueOpTest.cpp @@ -0,0 +1,82 @@ +/* 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 "FunctionTest.h" + +namespace paddle { +/* + for (size_t numSamples : {5, 32}) { + for (size_t channels : {5, 5, 32}) { + for (size_t imgSizeH : {5, 33, 100}) { + for (size_t imgSizeW : {5, 32, 96}) { + for (real value : {-0.5, 0.0, 0.5}) { +*/ + +TEST(MulValue, real) { + for (size_t numSamples : {5, 32}) { + for (size_t channels : {5, 5, 32}) { + for (size_t imgSizeH : {5, 33, 100}) { + for (size_t imgSizeW : {5, 32, 96}) { + for (real value : {-0.5, 0.0, 0.5}) { + for (bool firstHalf : {false, true}) { + VLOG(3) << " numSamples=" << numSamples + << " channels=" << channels << " imgSizeH=" << imgSizeH + << " imgSizeW=" << imgSizeW; + + for (bool test_grad : {false}) { + CpuGpuFuncCompare compare( + test_grad ? "MulValueGrad" : "MulValue", + FuncConfig().set("value", value)); + + TensorShape shape{numSamples, channels, imgSizeH, imgSizeW}; + TensorShape indicesShape{numSamples, 6}; + + compare.addInputs(BufferArg(VALUE_TYPE_FLOAT, shape)); + compare.addInputs(BufferArg(VALUE_TYPE_FLOAT, indicesShape)); + + compare.registerInitCallBack([=](BufferArg& arg, size_t index) { + if (index == 1) { + real* data = (real*)arg.data(); + + for (size_t i = 0; i < numSamples; ++i) { + size_t offset = i * 6; + data[offset] = firstHalf ? 1 : (int)channels / 2; + data[offset + 1] = + firstHalf ? (int)channels / 2 : channels; + data[offset + 2] = firstHalf ? 1 : (int)imgSizeH / 2; + data[offset + 3] = + firstHalf ? (int)imgSizeH / 2 : imgSizeH; + data[offset + 4] = firstHalf ? 1 : (int)imgSizeW / 2; + data[offset + 5] = + firstHalf ? (int)imgSizeW / 2 : imgSizeW; + } + } + }); + + compare.addOutputs(BufferArg(VALUE_TYPE_FLOAT, + shape, + test_grad ? ADD_TO : ASSIGN_TO), + test_grad ? ADD_TO : ASSIGN_TO); + compare.run(); + } + } + } + } + } + } + } +} + +} // namespace paddle diff --git a/paddle/gserver/layers/MulValueLayer.cpp b/paddle/gserver/layers/MulValueLayer.cpp new file mode 100644 index 0000000000..ef71de73bd --- /dev/null +++ b/paddle/gserver/layers/MulValueLayer.cpp @@ -0,0 +1,75 @@ +/* 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 "MulValueLayer.h" +#include "paddle/utils/Stat.h" +namespace paddle { + +REGISTER_LAYER(mul_value, MulValueLayer); + +bool MulValueLayer::init(const LayerMap& layerMap, + const ParameterMap& parameterMap) { + Layer::init(layerMap, parameterMap); + CHECK_EQ(static_cast(inputLayers_.size()), 2); + auto& conf = config_.inputs(0).mul_value_conf(); + value_ = conf.value(); + + createFunction(forward_, "MulValue", FuncConfig().set("value", value_)); + createFunction(backward_, "MulValueGrad", FuncConfig().set("value", value_)); + + return true; +} + +void MulValueLayer::forward(PassType passType) { + Layer::forward(passType); + auto in0 = getInput(0); + imgH_ = in0.getFrameHeight(); + imgW_ = in0.getFrameWidth(); + if (imgH_ == 0 || imgW_ == 0) { + auto& conf = config_.inputs(0).mul_value_conf(); + imgH_ = conf.image_conf().img_size_y(); + imgW_ = conf.image_conf().img_size(); + } + MatrixPtr imgV = in0.value; + size_t batchSize = imgV->getHeight(); + size_t spatialSize = imgH_ * imgW_; + channelsNum_ = imgV->getWidth() / spatialSize; + shape_ = TensorShape({batchSize, channelsNum_, imgH_, imgW_}); + + resetOutput(batchSize, imgV->getWidth()); + + MatrixPtr indicesV = getInputValue(1); + indicesShape_ = TensorShape({batchSize, 6}); + + REGISTER_TIMER_INFO("MulValueForward", getName().c_str()); + BufferArgs inArgs; + BufferArgs outArgs; + inArgs.addArg(*imgV, shape_); + inArgs.addArg(*indicesV, indicesShape_); + MatrixPtr outV = getOutputValue(); + outArgs.addArg(*outV, shape_, ASSIGN_TO); + forward_[0]->calc(inArgs, outArgs); +} + +void MulValueLayer::backward(const UpdateCallback& callback) { + REGISTER_TIMER_INFO("MulValueBackward", getName().c_str()); + BufferArgs inArgs; + BufferArgs outArgs; + inArgs.addArg(*getOutputGrad(), shape_); + inArgs.addArg(*getInputValue(1), indicesShape_); + outArgs.addArg(*getInputGrad(0), shape_, ADD_TO); + backward_[0]->calc(inArgs, outArgs); +} + +} // namespace paddle diff --git a/paddle/gserver/layers/MulValueLayer.h b/paddle/gserver/layers/MulValueLayer.h new file mode 100644 index 0000000000..8b315c0ede --- /dev/null +++ b/paddle/gserver/layers/MulValueLayer.h @@ -0,0 +1,52 @@ +/* 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 "Layer.h" + +namespace paddle { + +/** + * \brief For each instance, this layer can be used to multiply a value to a + * specified sub continuous region. By providing start index and end + * index for C/H/W, you can specify the location and shape of the + * region. + * + * input_0: Input value. + * input_1: Indices value to specify the location an shape of the + * region. + */ +class MulValueLayer : public Layer { +public: + explicit MulValueLayer(const LayerConfig& config) : Layer(config) {} + + ~MulValueLayer() {} + + bool init(const LayerMap& layerMap, const ParameterMap& parameterMap); + + void forward(PassType passType); + + void backward(const UpdateCallback& callback = nullptr); + +protected: + TensorShape shape_; + TensorShape indicesShape_; + size_t imgH_; + size_t imgW_; + size_t channelsNum_; + real value_; +}; + +} // namespace paddle diff --git a/paddle/gserver/tests/test_LayerGrad.cpp b/paddle/gserver/tests/test_LayerGrad.cpp index 1a46fb4915..89da15839e 100644 --- a/paddle/gserver/tests/test_LayerGrad.cpp +++ b/paddle/gserver/tests/test_LayerGrad.cpp @@ -2358,6 +2358,37 @@ TEST(Layer, ScaleShiftLayer) { } } +TEST(Layer, MulValueLayer) { + const size_t batchSize = 64; + const size_t size = 4096; + TestConfig config; + config.layerConfig.set_type("mul_value"); + config.inputDefs.push_back({INPUT_DATA, "input", size, 0}); + MatrixPtr indicesV = Matrix::create(batchSize, 6, false, false); + auto* data = indicesV->getData(); + for (size_t i = 0; i < batchSize; ++i) { + data[i * 2] = 2; + data[i * 2 + 1] = 4; + data[i * 2 + 2] = 16; + data[i * 2 + 3] = 32; + data[i * 2 + 4] = 16; + data[i * 2 + 5] = 32; + } + config.inputDefs.push_back({INPUT_SELF_DEFINE_DATA, "indices", indicesV, {}}); + LayerInputConfig* input = config.layerConfig.add_inputs(); + MulValueConfig* mulValueConf = input->mutable_mul_value_conf(); + ImageConfig* imgConf = mulValueConf->mutable_image_conf(); + imgConf->set_img_size(32); + imgConf->set_img_size_y(32); + imgConf->set_channels(4); + mulValueConf->set_value(1.0); + config.layerConfig.add_inputs(); + + for (auto useGpu : {false, true}) { + testLayerGrad(config, "mul_value", batchSize, false, useGpu, false); + } +} + int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); initMain(argc, argv); diff --git a/paddle/math/tests/TensorCheck.h b/paddle/math/tests/TensorCheck.h index 5bc4a03067..b998e5772e 100644 --- a/paddle/math/tests/TensorCheck.h +++ b/paddle/math/tests/TensorCheck.h @@ -169,7 +169,7 @@ void TensorCheck(AssertEq compare, count++; } } - EXPECT_EQ(count, 0) << "There are " << count << " different element."; + EXPECT_EQ(count, 0) << "There are " << count << " different elements."; } template diff --git a/proto/ModelConfig.proto b/proto/ModelConfig.proto index ebf0911d6e..0fecad3f7d 100644 --- a/proto/ModelConfig.proto +++ b/proto/ModelConfig.proto @@ -321,6 +321,11 @@ message ClipConfig { required double max = 2; } +message MulValueConfig { + required ImageConfig image_conf = 1; + required float value = 2; +} + message LayerInputConfig { required string input_layer_name = 1; optional string input_parameter_name = 2; @@ -342,6 +347,7 @@ message LayerInputConfig { optional MultiBoxLossConfig multibox_loss_conf = 16; optional DetectionOutputConfig detection_output_conf = 17; optional ClipConfig clip_conf = 18; + optional MulValueConfig mul_value_conf = 19; } message LayerConfig { diff --git a/python/paddle/trainer/config_parser.py b/python/paddle/trainer/config_parser.py index 0e65598485..222e195efe 100644 --- a/python/paddle/trainer/config_parser.py +++ b/python/paddle/trainer/config_parser.py @@ -3801,6 +3801,23 @@ class SwitchOrderLayer(LayerBase): self.config.reshape_conf.width_axis.extend(reshape['width']) +@config_layer('mul_value') +class MulValueLayer(LayerBase): + def __init__(self, name, inputs, value, **xargs): + super(MulValueLayer, self).__init__( + name, 'mul_value', 0, inputs=inputs, **xargs) + mul_value_conf = self.config.inputs[0].mul_value_conf + mul_value_conf.value = value + + # get channel, width and height from input_0 layer + input_layer = self.get_input_layer(0) + image_conf = mul_value_conf.image_conf + image_conf.img_size = input_layer.width + image_conf.img_size_y = input_layer.height + image_conf.channels = input_layer.size / (input_layer.width * + input_layer.height) + + # Deprecated, use a new layer specific class instead @config_func def Layer(name, type, **xargs): diff --git a/python/paddle/trainer_config_helpers/layers.py b/python/paddle/trainer_config_helpers/layers.py index 169e201046..e6901de14b 100644 --- a/python/paddle/trainer_config_helpers/layers.py +++ b/python/paddle/trainer_config_helpers/layers.py @@ -144,6 +144,7 @@ __all__ = [ 'img_conv3d_layer', 'resize_layer', 'sub_seq_layer', + 'mul_value_layer', ] @@ -255,6 +256,8 @@ class LayerType(object): RESIZE = 'resize' SUB_SEQ_LAYER = 'subseq' + MUL_VALUE_LAYER = 'mul_value' + @staticmethod def is_layer_type(type_name): """ @@ -7037,3 +7040,50 @@ def sub_seq_layer(input, offsets, sizes, act=None, bias_attr=None, name=None): LayerType.SUB_SEQ_LAYER, parents=[input, offsets, sizes], size=input.size) + + +@wrap_name_default('mul_value') +def mul_value_layer(input, indices, value, name=None): + """ + Given an image or feature map with CHW information, mul_value_layer can be + used to multiply a real value to values of a sub continuous region. You can + provide start and end indices of CHW for each instance. Please notice that + all start indices are counting from 1. The shape of indices should be + [batch_size, 6] and the layout for each row is [C_Start, C_End, H_Start, + H_End, W_Start, W_End]. + + .. code-block:: python + + mul_value = mul_value_layer(input=input, indices=indices, value=value) + + :param name: The name of this layer. It is optional. + :type name: basestring + :param input: The input of this layer which should contains CHW information. + :type input: LayerOutput + :param indices: Start index and end index for C H W, the input value should + be a 2-D matrix with shape [batch_size, 6]. + :type indices: LayerOutput. + :param value: value to multiply. + :type value: float + :return: LayerOutput object. + :rtype: LayerOutput + """ + + assert isinstance(input, LayerOutput), ( + 'The first input of mul_value_layer, must be a PaddlePaddle layer.') + assert isinstance(indices, LayerOutput), ( + 'The start and end indices for CHW, must be a PaddlePaddle layer.') + assert isinstance(value, float), ( + 'The value to multiply, must be a real value.') + + Layer( + name=name, + type=LayerType.MUL_VALUE_LAYER, + inputs=[input.name, indices.name], + value=value) + + return LayerOutput( + name, + LayerType.MUL_VALUE_LAYER, + parents=[input, indices], + size=input.size) diff --git a/python/paddle/trainer_config_helpers/tests/configs/file_list.sh b/python/paddle/trainer_config_helpers/tests/configs/file_list.sh index 6a4550c209..4c00400dda 100755 --- a/python/paddle/trainer_config_helpers/tests/configs/file_list.sh +++ b/python/paddle/trainer_config_helpers/tests/configs/file_list.sh @@ -10,6 +10,6 @@ test_prelu_layer test_row_conv test_detection_output_layer test_multibox_loss_la test_recursive_topology test_gated_unit_layer test_clip_layer test_row_l2_norm_layer test_kmax_seq_socre_layer test_sub_nested_seq_select_layer test_scale_shift_layer test_seq_slice_layer test_cross_entropy_over_beam test_pooling3D_layer -test_conv3d_layer test_deconv3d_layer test_BatchNorm3D test_resize_layer) +test_conv3d_layer test_deconv3d_layer test_BatchNorm3D test_resize_layer test_mul_value_layer) export whole_configs=(test_split_datasource) diff --git a/python/paddle/trainer_config_helpers/tests/configs/protostr/test_mul_value_layer.protostr b/python/paddle/trainer_config_helpers/tests/configs/protostr/test_mul_value_layer.protostr new file mode 100644 index 0000000000..389ed9d4a3 --- /dev/null +++ b/python/paddle/trainer_config_helpers/tests/configs/protostr/test_mul_value_layer.protostr @@ -0,0 +1,48 @@ +type: "nn" +layers { + name: "data" + type: "data" + size: 2016 + active_type: "" + height: 48 + width: 42 +} +layers { + name: "indices" + type: "data" + size: 6 + active_type: "" +} +layers { + name: "__mul_value_0__" + type: "mul_value" + active_type: "" + inputs { + input_layer_name: "data" + mul_value_conf { + image_conf { + channels: 1 + img_size: 42 + img_size_y: 48 + } + value: 0.0 + } + } + inputs { + input_layer_name: "indices" + } +} +input_layer_names: "data" +input_layer_names: "indices" +output_layer_names: "__mul_value_0__" +sub_models { + name: "root" + layer_names: "data" + layer_names: "indices" + layer_names: "__mul_value_0__" + input_layer_names: "data" + input_layer_names: "indices" + output_layer_names: "__mul_value_0__" + is_recurrent_layer_group: false +} + diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_mul_value_layer.py b/python/paddle/trainer_config_helpers/tests/configs/test_mul_value_layer.py new file mode 100644 index 0000000000..47d508d4a3 --- /dev/null +++ b/python/paddle/trainer_config_helpers/tests/configs/test_mul_value_layer.py @@ -0,0 +1,10 @@ +from paddle.trainer_config_helpers import * + +settings(batch_size=1000, learning_rate=1e-5) + +data = data_layer(name='data', size=2016, height=48, width=42) +indices = data_layer(name='indices', size=6) + +mul_value = mul_value_layer(input=data, indices=indices, value=0.0) + +outputs(mul_value) From db209f48156faf3efcc399e434dc183ec9bbdf5c Mon Sep 17 00:00:00 2001 From: ranqiu Date: Wed, 8 Nov 2017 19:04:57 +0800 Subject: [PATCH 31/41] Update annotations of layers.py --- .../paddle/trainer_config_helpers/layers.py | 196 ++++++++++-------- 1 file changed, 107 insertions(+), 89 deletions(-) diff --git a/python/paddle/trainer_config_helpers/layers.py b/python/paddle/trainer_config_helpers/layers.py index 0fd77a0be6..ebe81d6f68 100644 --- a/python/paddle/trainer_config_helpers/layers.py +++ b/python/paddle/trainer_config_helpers/layers.py @@ -5770,20 +5770,21 @@ def cross_entropy(input, :param input: The first input layer. :type input: LayerOutput. :param label: The input label. - :type input: LayerOutput. + :type input: LayerOutput :param name: The name of this layer. It is optional. - :type name: None | basestring. - :param coeff: The cost is multiplied with coeff. - The coefficient affects the gradient in the backward. - :type coeff: float. + :type name: basestring + :param coeff: The weight of the gradient in the back propagation. + 1.0 is the default. + :type coeff: float :param weight: The cost of each sample is multiplied with each weight. The weight should be a layer with size=1. Note that gradient will not be calculated for weight. :type weight: LayerOutout - :param layer_attr: Extra Layer Attribute. + :param layer_attr: The extra layer attribute. See ExtraLayerAttribute for + details. :type layer_attr: ExtraLayerAttribute :return: LayerOutput object. - :rtype: LayerOutput. + :rtype: LayerOutput """ ipts, parents = __cost_input__(input, label, weight) @@ -5816,19 +5817,21 @@ def cross_entropy_with_selfnorm(input, label=label_layer) :param input: The first input layer. - :type input: LayerOutput. + :type input: LayerOutput :param label: The input label. - :type input: LayerOutput. + :type input: LayerOutput :param name: The name of this layer. It is optional. - :type name: None | basestring. - :param coeff: The coefficient affects the gradient in the backward. - :type coeff: float. + :type name: basestring + :param coeff: The weight of the gradient in the back propagation. + 1.0 is the default. + :type coeff: float :param softmax_selfnorm_alpha: The scale factor affects the cost. - :type softmax_selfnorm_alpha: float. - :param layer_attr: Extra Layer Attribute. + :type softmax_selfnorm_alpha: float + :param layer_attr: The extra layer attribute. See ExtraLayerAttribute for + details. :type layer_attr: ExtraLayerAttribute :return: LayerOutput object. - :rtype: LayerOutput. + :rtype: LayerOutput """ Layer( name=name, @@ -5849,7 +5852,7 @@ def cross_entropy_with_selfnorm(input, @layer_support() def sum_cost(input, name=None, layer_attr=None): """ - A loss layer which calculate the sum of the input as loss + A loss layer which calculates the sum of the input as loss. The example usage is: @@ -5858,10 +5861,11 @@ def sum_cost(input, name=None, layer_attr=None): cost = sum_cost(input=input_layer) :param input: The input of this layer. - :type input: LayerOutput. + :type input: LayerOutput :param name: The name of this layer. It is optional. - :type name: None | basestring. - :param layer_attr: Extra Layer Attribute. + :type name: basestring + :param layer_attr: The extra layer attribute. See ExtraLayerAttribute for + details. :type layer_attr: ExtraLayerAttribute :return: LayerOutput object. :rtype: LayerOutput. @@ -5901,16 +5905,18 @@ def huber_regression_cost(input, cost = huber_regression_cost(input=input_layer, label=label_layer) :param input: The first input layer. - :type input: LayerOutput. + :type input: LayerOutput :param label: The input label. - :type input: LayerOutput. + :type input: LayerOutput :param name: The name of this layer. It is optional. - :type name: None | basestring. + :type name: basestring :param delta: The difference between the observed and predicted values. - :type delta: float. - :param coeff: The coefficient affects the gradient in the backward. - :type coeff: float. - :param layer_attr: Extra Layer Attribute. + :type delta: float + :param coeff: The weight of the gradient in the back propagation. + 1.0 is the default. + :type coeff: float + :param layer_attr: The extra layer attribute. See ExtraLayerAttribute for + details. :type layer_attr: ExtraLayerAttribute :return: LayerOutput object. :rtype: LayerOutput. @@ -5951,17 +5957,19 @@ def huber_classification_cost(input, cost = huber_classification_cost(input=input_layer, label=label_layer) :param input: The first input layer. - :type input: LayerOutput. + :type input: LayerOutput :param label: The input label. - :type input: LayerOutput. + :type input: LayerOutput :param name: The name of this layer. It is optional. - :type name: None | basestring. - :param coeff: The coefficient affects the gradient in the backward. - :type coeff: float. - :param layer_attr: Extra Layer Attribute. + :type name: basestring + :param coeff: The weight of the gradient in the back propagation. + 1.0 is the default. + :type coeff: float + :param layer_attr: The extra layer attribute. See ExtraLayerAttribute for + details. :type layer_attr: ExtraLayerAttribute :return: LayerOutput object. - :rtype: LayerOutput. + :rtype: LayerOutput """ assert isinstance(input, LayerOutput) if input.size is not None: @@ -5998,10 +6006,12 @@ def multi_binary_label_cross_entropy(input, :param label: The input label. :type input: LayerOutput :param name: The name of this layer. It is optional. - :type name: None | basestring - :param coeff: The coefficient affects the gradient in the backward. + :type name: basestring + :param coeff: The weight of the gradient in the back propagation. + 1.0 is the default. :type coeff: float - :param layer_attr: Extra Layer Attribute. + :param layer_attr: The extra layer attribute. See ExtraLayerAttribute for + details. :type layer_attr: ExtraLayerAttribute :return: LayerOutput object. :rtype: LayerOutput @@ -6104,7 +6114,7 @@ def cross_entropy_over_beam(input, name=None): :param input: Input beams for this layer. :type input: BeamInput - :param name: The name of this layer. + :param name: The name of this layer. It is optional. :type name: basestring :return: LayerOutput object. :rtype: LayerOutput @@ -6139,7 +6149,7 @@ def cross_entropy_over_beam(input, name=None): def smooth_l1_cost(input, label, name=None, coeff=1.0, layer_attr=None): """ This is a L1 loss but more smooth. It requires that the - size of input and label are equal. The formula is as follows, + sizes of input and label are equal. The formula is as follows, .. math:: @@ -6151,8 +6161,9 @@ def smooth_l1_cost(input, label, name=None, coeff=1.0, layer_attr=None): smooth_{L1}(x) = \\begin{cases} 0.5x^2& \\text{if} \\ |x| < 1 \\\\ |x|-0.5& \\text{otherwise} \end{cases} - More details can be found by referring to `Fast R-CNN - `_ + Reference: + Fast R-CNN + https://arxiv.org/pdf/1504.08083v2.pdf The example usage is: @@ -6166,10 +6177,11 @@ def smooth_l1_cost(input, label, name=None, coeff=1.0, layer_attr=None): :param label: The input label. :type input: LayerOutput :param name: The name of this layer. It is optional. - :type name: None | basestring + :type name: basestring :param coeff: The coefficient affects the gradient in the backward. :type coeff: float - :param layer_attr: Extra Layer Attribute. + :param layer_attr: The extra layer attribute. See ExtraLayerAttribute for + details. :type layer_attr: ExtraLayerAttribute :return: LayerOutput object. :rtype: LayerOutput @@ -6191,12 +6203,12 @@ def smooth_l1_cost(input, label, name=None, coeff=1.0, layer_attr=None): @wrap_name_default() def multiplex_layer(input, name=None, layer_attr=None): """ - This layer multiplex multiple layers according to the index, - which is provided by the first input layer. - inputs[0]: the index of the layer to output of size batchSize. + This layer multiplex multiple layers according to the indexes, + which are provided by the first input layer. + inputs[0]: the indexes of the layers to form the output of size batchSize. inputs[1:N]; the candidate output data. - For each index i from 0 to batchSize -1, the output is the i-th row of the - (index[i] + 1)-th layer. + For each index i from 0 to batchSize - 1, the i-th row of the output is the + the same to the i-th row of the (index[i] + 1)-th layer. For each i-th row of output: .. math:: @@ -6215,7 +6227,8 @@ def multiplex_layer(input, name=None, layer_attr=None): :type input: list of LayerOutput :param name: The name of this layer. It is optional. :type name: basestring - :param layer_attr: extra layer attributes. + :param layer_attr: The extra layer attribute. See ExtraLayerAttribute for + details. :type layer_attr: ExtraLayerAttribute. :return: LayerOutput object. :rtype: LayerOutput @@ -6319,14 +6332,14 @@ def row_conv_layer(input, :type context_len: int :param act: Activation Type. LinearActivation is the default. :type act: BaseActivation - :param param_attr: The Parameter Attribute. If None, the parameter will be - initialized smartly. It's better to set it by yourself. + :param param_attr: The parameter attribute. See ParameterAttribute for + details. :type param_attr: ParameterAttribute - :param layer_attr: Extra Layer config. + :param layer_attr: The extra layer attribute. See ExtraLayerAttribute for + details. :type layer_attr: ExtraLayerAttribute | None :return: LayerOutput object. :rtype: LayerOutput - """ assert isinstance(input, LayerOutput) assert context_len > 0, "the context_len must be greatet than 0." @@ -6351,7 +6364,7 @@ def prelu_layer(input, param_attr=None, layer_attr=None): """ - The Parameter Relu activation that actives outputs with a learnable weight. + The Parametric Relu activation that actives outputs with a learnable weight. Reference: Delving Deep into Rectifiers: Surpassing Human-Level Performance on @@ -6371,16 +6384,17 @@ def prelu_layer(input, :type name: basestring :param input: The input of this layer. :type input: LayerOutput - :param partial_sum: this parameter makes a group of inputs share a same weight. + :param partial_sum: this parameter makes a group of inputs share the same weight. - partial_sum = 1, indicates the element-wise activation: each element has a weight. - - partial_sum = number of elements in one channel, indicates the channel-wise activation, elements in a channel share a same weight. - - partial_sum = number of outputs, indicates all elements share a same weight. + - partial_sum = number of elements in one channel, indicates the channel-wise activation, elements in a channel share the same weight. + - partial_sum = number of outputs, indicates all elements share the same weight. :type partial_sum: int :param param_attr: The parameter attribute. See ParameterAttribute for details. - :type param_attr: ParameterAttribute | None - :param layer_attr: Extra layer configurations. Default is None. + :type param_attr: ParameterAttribute + :param layer_attr: The extra layer attribute. See ExtraLayerAttribute for + details. :type layer_attr: ExtraLayerAttribute | None :return: LayerOutput object. :rtype: LayerOutput @@ -6436,34 +6450,36 @@ def gated_unit_layer(input, :param input: The input of this layer. :type input: LayerOutput - :param size: output size of the gated unit. + :param size: The dimemsion of this layer's output. :type size: int - :param act: Activation type of the projected input. LinearActivation is the default. + :param act: Activation type of the projection. LinearActivation is the default. :type act: BaseActivation :param name: The name of this layer. It is optional. :type name: basestring - :param gate_attr: Attributes to tune the gate output, for example, error - clipping threshold, dropout and so on. See ExtraLayerAttribute for - more details. + :param gate_attr: The extra layer attribute of the gate. See ExtraLayerAttribute for + details. :type gate_attr: ExtraLayerAttribute | None - :param gate_param_attr: Attributes to tune the learnable projected matrix - parameter of the gate. - :type gate_param_attr: ParameterAttribute | None - :param gate_bias_attr: Attributes to tune the learnable bias of the gate. - :type gate_bias_attr: ParameterAttribute | None - :param inproj_attr: Attributes to the tune the projected input, for - example, error clipping threshold, dropout and so on. See - ExtraLayerAttribute for more details. + :param gate_param_attr: The parameter attribute of the gate. See ParameterAttribute + for details. + :type gate_param_attr: ParameterAttribute + :param gate_bias_attr: The bias attribute of the gate. If the parameter is set to + False or something not type of ParameterAttribute, no bias is + defined. If the parameter is set to True, the bias is initialized + to zero. + :type gate_bias_attr: ParameterAttribute | bool | None | Any + :param inproj_attr: Extra layer attributes of the projection. See ExtraLayerAttribute for + details. :type inproj_attr: ExtraLayerAttribute | None - :param inproj_param_attr: Attributes to tune the learnable parameter of - the projection of input. - :type inproj_param_attr: ParameterAttribute | None - :param inproj_bias_attr: Attributes to tune the learnable bias of - projection of the input. - :type inproj_bias_attr: ParameterAttribute | None - :param layer_attr: Attributes to tune the final output of the gated unit, - for example, error clipping threshold, dropout and so on. See - ExtraLayerAttribute for more details. + :param inproj_param_attr: The parameter attribute of the projection. See ParameterAttribute + for details. + :type inproj_param_attr: ParameterAttribute + :param inproj_bias_attr: The bias attribute of the projection. If the parameter is set to + False or something not type of ParameterAttribute, no bias is + defined. If the parameter is set to True, the bias is initialized + to zero. + :type inproj_bias_attr: ParameterAttribute | bool | None | Any + :param layer_attr: Extra layer attribute of the product. See ExtraLayerAttribute for + details. :type layer_attr: ExtraLayerAttribute | None :return: LayerOutput object. :rtype: LayerOutput @@ -6659,9 +6675,9 @@ def clip_layer(input, min, max, name=None): :param input: The input of this layer. :type input: LayerOutput. :param min: The lower threshold for clipping. - :type min: double + :type min: float :param max: The upper threshold for clipping. - :type max: double + :type max: float :return: LayerOutput object. :rtype: LayerOutput """ @@ -6709,7 +6725,6 @@ def seq_slice_layer(input, starts, ends, name=None): :type ends: LayerOutput | None :return: LayerOutput object. :rtype: LayerOutput - """ assert isinstance(input, LayerOutput), ( @@ -6830,7 +6845,7 @@ def img_conv3d_layer(input, :param padding: The numbers of padding along three axises. If the parameter is set to one integer, they will be same. :type padding: int | tuple | list - :param bias_attr: The Bias Attribute. If the parameter is set to + :param bias_attr: The bias attribute. If the parameter is set to False or something not type of ParameterAttribute, no bias is defined. If the parameter is set to True, the bias is initialized to zero. @@ -6839,11 +6854,13 @@ def img_conv3d_layer(input, set to None, its actual value will be automatically set to the channels number of the input . :type num_channels: int - :param param_attr: The parameter attribute of the convolution. + :param param_attr: The parameter attribute of the convolution. See ParameterAttribute for + details. :type param_attr: ParameterAttribute :param shared_biases: Whether biases will be shared between filters or not. :type shared_biases: bool - :param layer_attr: Extra layer attributes. + :param layer_attr: The extra layer attributes. See ExtraLayerAttribute for + details. :type layer_attr: ExtraLayerAttribute :param trans: True if it is a convTransLayer, False if it is a convLayer :type trans: bool @@ -6950,9 +6967,10 @@ def scale_shift_layer(input, name=None, param_attr=None, bias_attr=None): :type name: basestring :param input: The input of this layer. :type input: LayerOutput - :param param_attr: The parameter attribute of scaling. + :param param_attr: The parameter attribute of scaling. See ParameterAttribute for + details. :type param_attr: ParameterAttribute - :param bias_attr: The Bias Attribute. If the parameter is set to + :param bias_attr: The bias attribute. If the parameter is set to False or something not type of ParameterAttribute, no bias is defined. If the parameter is set to True, the bias is initialized to zero. @@ -7013,7 +7031,7 @@ def sub_seq_layer(input, offsets, sizes, act=None, bias_attr=None, name=None): :type sizes: LayerOutput :param act: Activation type, LinearActivation is the default. :type act: BaseActivation. - :param bias_attr: The Bias Attribute. If the parameter is set to + :param bias_attr: The bias attribute. If the parameter is set to False or something not type of ParameterAttribute, no bias is defined. If the parameter is set to True, the bias is initialized to zero. From cfde85bc52b55918906e4ad518211a07be907bd9 Mon Sep 17 00:00:00 2001 From: yangyaming Date: Wed, 8 Nov 2017 19:11:20 +0800 Subject: [PATCH 32/41] CallBack --> Callback --- paddle/function/FunctionTest.h | 12 ++++++------ paddle/function/MulValueOpTest.cpp | 9 +-------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/paddle/function/FunctionTest.h b/paddle/function/FunctionTest.h index 2fc51a3aa8..370940532e 100644 --- a/paddle/function/FunctionTest.h +++ b/paddle/function/FunctionTest.h @@ -110,7 +110,7 @@ public: function2_(FunctionBase::funcRegistrar_.createByType(name2)) { function1_->init(config); function2_->init(config); - initArgsCallBack_ = nullptr; + initArgsCallback_ = nullptr; } ~Compare2Function() {} @@ -171,8 +171,8 @@ public: *seq2_)); } - void registerInitCallBack(std::function callback) { - initArgsCallBack_ = callback; + void registerInitCallback(std::function callback) { + initArgsCallback_ = callback; } // output need only contains shape, do not contains data. @@ -345,8 +345,8 @@ protected: initArg(*func1Inputs_[i]); } - if (initArgsCallBack_ != nullptr) { - initArgsCallBack_(*func1Inputs_[i], i); + if (initArgsCallback_ != nullptr) { + initArgsCallback_(*func1Inputs_[i], i); } copyArg_(*func1Inputs_[i], *func2Inputs_[i]); @@ -395,7 +395,7 @@ protected: std::shared_ptr seq1_; std::shared_ptr seq2_; test::CopyArgument copyArg_; - std::function initArgsCallBack_; + std::function initArgsCallback_; }; class CpuGpuFuncCompare diff --git a/paddle/function/MulValueOpTest.cpp b/paddle/function/MulValueOpTest.cpp index c1d5a3e544..048660f34f 100644 --- a/paddle/function/MulValueOpTest.cpp +++ b/paddle/function/MulValueOpTest.cpp @@ -16,13 +16,6 @@ limitations under the License. */ #include "FunctionTest.h" namespace paddle { -/* - for (size_t numSamples : {5, 32}) { - for (size_t channels : {5, 5, 32}) { - for (size_t imgSizeH : {5, 33, 100}) { - for (size_t imgSizeW : {5, 32, 96}) { - for (real value : {-0.5, 0.0, 0.5}) { -*/ TEST(MulValue, real) { for (size_t numSamples : {5, 32}) { @@ -46,7 +39,7 @@ TEST(MulValue, real) { compare.addInputs(BufferArg(VALUE_TYPE_FLOAT, shape)); compare.addInputs(BufferArg(VALUE_TYPE_FLOAT, indicesShape)); - compare.registerInitCallBack([=](BufferArg& arg, size_t index) { + compare.registerInitCallback([=](BufferArg& arg, size_t index) { if (index == 1) { real* data = (real*)arg.data(); From a1856be5ebd3033316824251269cf84b7663f72c Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Wed, 8 Nov 2017 15:56:08 +0800 Subject: [PATCH 33/41] update mklml tag --- cmake/external/mklml.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/external/mklml.cmake b/cmake/external/mklml.cmake index 74f3279831..20dbc32a73 100644 --- a/cmake/external/mklml.cmake +++ b/cmake/external/mklml.cmake @@ -27,8 +27,8 @@ ENDIF() INCLUDE(ExternalProject) SET(MKLML_PROJECT "extern_mklml") -SET(MKLML_VER "mklml_lnx_2018.0.20170720") -SET(MKLML_URL "https://github.com/01org/mkl-dnn/releases/download/v0.10/${MKLML_VER}.tgz") +SET(MKLML_VER "mklml_lnx_2018.0.1.20171007") +SET(MKLML_URL "https://github.com/01org/mkl-dnn/releases/download/v0.11/${MKLML_VER}.tgz") SET(MKLML_SOURCE_DIR "${THIRD_PARTY_PATH}/mklml") SET(MKLML_DOWNLOAD_DIR "${MKLML_SOURCE_DIR}/src/${MKLML_PROJECT}") SET(MKLML_DST_DIR "mklml") From 07f3f07ff379a069b5af264470e856d21e7a3144 Mon Sep 17 00:00:00 2001 From: yangyaming Date: Wed, 8 Nov 2017 22:42:29 +0800 Subject: [PATCH 34/41] MulValue --> ScaleSubRegion --- paddle/function/CMakeLists.txt | 2 +- paddle/function/ScaleSubRegionOp.cpp | 155 ++++++++++++++++++ paddle/function/ScaleSubRegionOp.h | 55 +++++++ paddle/function/ScaleSubRegionOpGpu.cu | 116 +++++++++++++ paddle/function/ScaleSubRegionOpTest.cpp | 72 ++++++++ paddle/gserver/layers/ScaleSubRegionLayer.cpp | 78 +++++++++ paddle/gserver/layers/ScaleSubRegionLayer.h | 52 ++++++ paddle/gserver/tests/test_LayerGrad.cpp | 13 +- proto/ModelConfig.proto | 4 +- python/paddle/trainer/config_parser.py | 16 +- .../paddle/trainer_config_helpers/layers.py | 32 ++-- .../tests/configs/file_list.sh | 2 +- .../test_scale_sub_region_layer.protostr | 51 ++++++ .../configs/test_scale_sub_region_layer.py | 11 ++ 14 files changed, 628 insertions(+), 31 deletions(-) create mode 100644 paddle/function/ScaleSubRegionOp.cpp create mode 100644 paddle/function/ScaleSubRegionOp.h create mode 100644 paddle/function/ScaleSubRegionOpGpu.cu create mode 100644 paddle/function/ScaleSubRegionOpTest.cpp create mode 100644 paddle/gserver/layers/ScaleSubRegionLayer.cpp create mode 100644 paddle/gserver/layers/ScaleSubRegionLayer.h create mode 100644 python/paddle/trainer_config_helpers/tests/configs/protostr/test_scale_sub_region_layer.protostr create mode 100644 python/paddle/trainer_config_helpers/tests/configs/test_scale_sub_region_layer.py diff --git a/paddle/function/CMakeLists.txt b/paddle/function/CMakeLists.txt index 1b3068b8ff..9b2779b42c 100644 --- a/paddle/function/CMakeLists.txt +++ b/paddle/function/CMakeLists.txt @@ -45,7 +45,7 @@ if(WITH_GPU) add_simple_unittest(BlockExpandOpTest) add_simple_unittest(CropOpTest) add_simple_unittest(SwitchOpTest) - add_simple_unittest(MulValueOpTest) + add_simple_unittest(ScaleSubRegionOpTest) endif() add_simple_unittest(Im2ColTest) diff --git a/paddle/function/ScaleSubRegionOp.cpp b/paddle/function/ScaleSubRegionOp.cpp new file mode 100644 index 0000000000..a080505d7d --- /dev/null +++ b/paddle/function/ScaleSubRegionOp.cpp @@ -0,0 +1,155 @@ +/* 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 "ScaleSubRegionOp.h" +#include "paddle/function/TensorShape.h" + +namespace paddle { + +template <> +void ScaleSubRegion(real* outputs, + const real* inputs, + const real* indices, + const TensorShape shape, + const FuncConfig& conf) { + real value = conf.get("value"); + + int number = shape[0]; + int channel = shape[1]; + int height = shape[2]; + int width = shape[3]; + + memcpy(outputs, inputs, number * channel * height * width * sizeof(real)); + + for (int n = 0; n < number; ++n) { + // indices start from 1 + int offset = n * 6; + for (int c = indices[offset] - 1; c < indices[offset + 1]; ++c) { + for (int h = indices[offset + 2] - 1; h < indices[offset + 3]; ++h) { + for (int w = indices[offset + 4] - 1; w < indices[offset + 5]; ++w) { + int idx = ((n * channel + c) * height + h) * width + w; + outputs[idx] *= value; + } + } + } + } +} + +template <> +void ScaleSubRegionGrad(const real* inGrad, + real* outGrad, + const real* indices, + const TensorShape shape, + const FuncConfig& conf) { + real value = conf.get("value"); + + int number = shape[0]; + int channel = shape[1]; + int height = shape[2]; + int width = shape[3]; + + for (int n = 0; n < number; ++n) { + for (int c = 0; c < channel; ++c) { + for (int h = 0; h < height; ++h) { + for (int w = 0; w < width; ++w) { + int idx = ((n * channel + c) * height + h) * width + w; + int offset = n * 6; + if (c >= (indices[offset] - 1) && c <= (indices[offset + 1] - 1) && + h >= (indices[offset + 2] - 1) && + h <= (indices[offset + 3] - 1) && + w >= (indices[offset + 4] - 1) && + w <= (indices[offset + 5] - 1)) { + outGrad[idx] += inGrad[idx] * value; + } else { + outGrad[idx] += inGrad[idx]; + } + } + } + } + } +} + +/** + * \brief For each instance, ScaleSubRegion can be used to multiply a value to + * a specified sub continuous region. By providing start index and end + * index for C/H/W, you can specify the location and shape of the region. + * + * Argument in this Function: + * \param inputs A 4-D tensor with shape [N, C, H, W], only one input. + * \param indices A 2-D tensor with shape [N, 6], indicates the sub region. + * \param outputs A 4-D tensor with same shape as inputs, output value. + */ +template +class ScaleSubRegionFunc : public FunctionBase { +public: + void init(const FuncConfig& config) override { conf_ = config; } + + void calc(const BufferArgs& inputs, const BufferArgs& outputs) override { + CHECK_EQ(2UL, inputs.size()); + CHECK_EQ(1UL, outputs.size()); + CHECK_EQ(outputs[0].getArgType(), ASSIGN_TO); + + TensorShape shape = inputs[0].shape(); + + ScaleSubRegion(outputs[0].data(), + inputs[0].data(), + inputs[1].data(), + shape, + conf_); + } + +private: + FuncConfig conf_; +}; + +/** + * \brief The backward propagation of ScaleSubRegion Function. + * + * Argument in this Function: + * \param inputs A 4-D tensor with shape [N, C, H, W], output gradient. + * \param indices A 2-D tensor with shape [N, 6], indicates the sub region. + * \param outputs A 4-D tensor with shape [N, C, H, W], gradient of input value. + */ + +template +class ScaleSubRegionGradFunc : public FunctionBase { +public: + void init(const FuncConfig& config) override { conf_ = config; } + + void calc(const BufferArgs& inputs, const BufferArgs& outputs) override { + CHECK_EQ(2UL, inputs.size()); + CHECK_EQ(1UL, outputs.size()); + CHECK_EQ(outputs[0].getArgType(), ADD_TO); + + TensorShape shape = inputs[0].shape(); + + ScaleSubRegionGrad(inputs[0].data(), + outputs[0].data(), + inputs[1].data(), + shape, + conf_); + } + +private: + FuncConfig conf_; +}; + +REGISTER_TYPED_FUNC(ScaleSubRegion, CPU, ScaleSubRegionFunc); +REGISTER_TYPED_FUNC(ScaleSubRegionGrad, CPU, ScaleSubRegionGradFunc); +#ifdef PADDLE_WITH_CUDA +REGISTER_TYPED_FUNC(ScaleSubRegion, GPU, ScaleSubRegionFunc); +REGISTER_TYPED_FUNC(ScaleSubRegionGrad, GPU, ScaleSubRegionGradFunc); +#endif + +} // namespace paddle diff --git a/paddle/function/ScaleSubRegionOp.h b/paddle/function/ScaleSubRegionOp.h new file mode 100644 index 0000000000..0480c8577f --- /dev/null +++ b/paddle/function/ScaleSubRegionOp.h @@ -0,0 +1,55 @@ +/* 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 "Function.h" + +namespace paddle { + +/** + * \brief Function to multiply a value to values in specified sub continuous + * region. Indices must be provided to indcate the location and shape of + * the region and the multiplied value is passed by configure variable. + * + * + * \param[out] outputs Output value. + * \param[in] inputs Input data which contains NCHW information. + * \param[in] indices Indices data to indcate the sub region. + * \param[in] shape Tensor shape of input value. + * \param[in] conf Configure variable which contains the multiplied value. + */ +template +void ScaleSubRegion(real* outputs, + const real* inputs, + const real* indices, + const TensorShape shape, + const FuncConfig& conf); + +/** + * \brief Backward propagation function of ScaleSubRegion. + * + * \param[out] inGrad Gradients of previous layer. + * \param[in] outGrad Output gradient. + * \param[in] indices Indices data. + * \param[in] shape The Shape of input tensor. + * \param[in] conf Configure variable. + */ +template +void ScaleSubRegionGrad(const real* inGrad, + real* outGrad, + const real* indices, + const TensorShape shape, + const FuncConfig& conf); +} // namespace paddle diff --git a/paddle/function/ScaleSubRegionOpGpu.cu b/paddle/function/ScaleSubRegionOpGpu.cu new file mode 100644 index 0000000000..8aae2e44c3 --- /dev/null +++ b/paddle/function/ScaleSubRegionOpGpu.cu @@ -0,0 +1,116 @@ +/* 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 "ScaleSubRegionOp.h" +#include "hl_base.h" + +namespace paddle { + +__global__ void KeScaleSubRegion(real* outputs, + const real* inputs, + const real* indices, + real value, + int channel, + int height, + int width, + int nthreads) { + const int idx = threadIdx.x + blockIdx.x * blockDim.x; + if (idx < nthreads) { + const int w = idx % width; + const int h = (idx / width) % height; + const int c = (idx / width / height) % channel; + const int n = idx / width / height / channel; + + const int offset = n * 6; + if (c >= (indices[offset] - 1) && c <= (indices[offset + 1] - 1) && + h >= (indices[offset + 2] - 1) && h <= (indices[offset + 3] - 1) && + w >= (indices[offset + 4] - 1) && w <= (indices[offset + 5] - 1)) { + outputs[idx] = inputs[idx] * value; + } else { + outputs[idx] = inputs[idx]; + } + } +} + +template <> +void ScaleSubRegion(real* outputs, + const real* inputs, + const real* indices, + const TensorShape shape, + const FuncConfig& conf) { + real value = conf.get("value"); + + int number = shape[0]; + int channel = shape[1]; + int height = shape[2]; + int width = shape[3]; + + size_t nth = number * channel * height * width; + int blockSize = 1024; + int gridSize = (nth + blockSize - 1) / blockSize; + + KeScaleSubRegion<<>>( + outputs, inputs, indices, value, channel, height, width, nth); + CHECK_SYNC("ScaleSubRegion"); +} + +__global__ void KeScaleSubRegionDiff(const real* inGrad, + real* outGrad, + const real* indices, + real value, + int channel, + int height, + int width, + int nthreads) { + const int idx = threadIdx.x + blockIdx.x * blockDim.x; + if (idx < nthreads) { + const int w = idx % width; + const int h = (idx / width) % height; + const int c = (idx / width / height) % channel; + const int n = idx / width / height / channel; + + const int offset = n * 6; + if (c >= (indices[offset] - 1) && c <= (indices[offset + 1] - 1) && + h >= (indices[offset + 2] - 1) && h <= (indices[offset + 3] - 1) && + w >= (indices[offset + 4] - 1) && w <= (indices[offset + 5] - 1)) { + outGrad[idx] += inGrad[idx] * value; + } else { + outGrad[idx] += inGrad[idx]; + } + } +} + +template <> +void ScaleSubRegionGrad(const real* inGrad, + real* outGrad, + const real* indices, + const TensorShape shape, + const FuncConfig& conf) { + real value = conf.get("value"); + + int number = shape[0]; + int channel = shape[1]; + int height = shape[2]; + int width = shape[3]; + + size_t nth = number * channel * height * width; + int blockSize = 1024; + int gridSize = (nth + blockSize - 1) / blockSize; + + KeScaleSubRegionDiff<<>>( + inGrad, outGrad, indices, value, channel, height, width, nth); + CHECK_SYNC("ScaleSubRegionGrad"); +} + +} // namespace paddle diff --git a/paddle/function/ScaleSubRegionOpTest.cpp b/paddle/function/ScaleSubRegionOpTest.cpp new file mode 100644 index 0000000000..2cbbf9d4b3 --- /dev/null +++ b/paddle/function/ScaleSubRegionOpTest.cpp @@ -0,0 +1,72 @@ +/* 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 "FunctionTest.h" + +namespace paddle { + +TEST(ScaleSubRegion, real) { + for (size_t numSamples : {5, 32}) { + for (size_t channels : {5, 5, 32}) { + for (size_t imgSizeH : {5, 33, 100}) { + for (size_t imgSizeW : {5, 32, 96}) { + for (real value : {-0.5, 0.0, 0.5}) { + for (bool firstHalf : {false, true}) { + VLOG(3) << " numSamples=" << numSamples + << " channels=" << channels << " imgSizeH=" << imgSizeH + << " imgSizeW=" << imgSizeW; + + for (bool testGrad : {false, true}) { + CpuGpuFuncCompare compare( + testGrad ? "ScaleSubRegionGrad" : "ScaleSubRegion", + FuncConfig().set("value", value)); + + TensorShape shape{numSamples, channels, imgSizeH, imgSizeW}; + TensorShape indicesShape{numSamples, 6}; + + compare.addInputs(BufferArg(VALUE_TYPE_FLOAT, shape)); + compare.addInputs(BufferArg(VALUE_TYPE_FLOAT, indicesShape)); + + compare.registerInitCallback([=](BufferArg& arg, size_t index) { + if (index == 1) { + real* data = (real*)arg.data(); + + for (size_t i = 0; i < numSamples; ++i) { + size_t offset = i * 6; + data[offset] = firstHalf ? 1 : channels / 2; + data[offset + 1] = firstHalf ? channels / 2 : channels; + data[offset + 2] = firstHalf ? 1 : imgSizeH / 2; + data[offset + 3] = firstHalf ? imgSizeH / 2 : imgSizeH; + data[offset + 4] = firstHalf ? 1 : imgSizeW / 2; + data[offset + 5] = firstHalf ? imgSizeW / 2 : imgSizeW; + } + } + }); + + compare.addOutputs( + BufferArg( + VALUE_TYPE_FLOAT, shape, testGrad ? ADD_TO : ASSIGN_TO), + testGrad ? ADD_TO : ASSIGN_TO); + compare.run(); + } + } + } + } + } + } + } +} + +} // namespace paddle diff --git a/paddle/gserver/layers/ScaleSubRegionLayer.cpp b/paddle/gserver/layers/ScaleSubRegionLayer.cpp new file mode 100644 index 0000000000..b18bc0c1b9 --- /dev/null +++ b/paddle/gserver/layers/ScaleSubRegionLayer.cpp @@ -0,0 +1,78 @@ +/* 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 "ScaleSubRegionLayer.h" +#include "paddle/utils/Stat.h" +namespace paddle { + +REGISTER_LAYER(scale_sub_region, ScaleSubRegionLayer); + +bool ScaleSubRegionLayer::init(const LayerMap& layerMap, + const ParameterMap& parameterMap) { + Layer::init(layerMap, parameterMap); + CHECK_EQ(static_cast(inputLayers_.size()), 2); + auto& conf = config_.inputs(0).scale_sub_region_conf(); + value_ = conf.value(); + + createFunction(forward_, "ScaleSubRegion", FuncConfig().set("value", value_)); + createFunction( + backward_, "ScaleSubRegionGrad", FuncConfig().set("value", value_)); + + return true; +} + +void ScaleSubRegionLayer::forward(PassType passType) { + Layer::forward(passType); + auto in0 = getInput(0); + imgH_ = in0.getFrameHeight(); + imgW_ = in0.getFrameWidth(); + if (imgH_ == 0 || imgW_ == 0) { + auto& conf = config_.inputs(0).scale_sub_region_conf(); + imgH_ = conf.image_conf().img_size_y(); + imgW_ = conf.image_conf().img_size(); + } + MatrixPtr imgV = in0.value; + size_t batchSize = imgV->getHeight(); + size_t spatialSize = imgH_ * imgW_; + channelsNum_ = imgV->getWidth() / spatialSize; + shape_ = TensorShape({batchSize, channelsNum_, imgH_, imgW_}); + + resetOutput(batchSize, imgV->getWidth()); + auto out = getOutput(); + out.setFrameHeight(imgH_); + out.setFrameWidth(imgW_); + + MatrixPtr indicesV = getInputValue(1); + indicesShape_ = TensorShape({batchSize, 6}); + + REGISTER_TIMER_INFO("ScaleSubRegionForward", getName().c_str()); + BufferArgs inArgs; + BufferArgs outArgs; + inArgs.addArg(*imgV, shape_); + inArgs.addArg(*indicesV, indicesShape_); + outArgs.addArg(*out.value, shape_, ASSIGN_TO); + forward_[0]->calc(inArgs, outArgs); +} + +void ScaleSubRegionLayer::backward(const UpdateCallback& callback) { + REGISTER_TIMER_INFO("ScaleSubRegionBackward", getName().c_str()); + BufferArgs inArgs; + BufferArgs outArgs; + inArgs.addArg(*getOutputGrad(), shape_); + inArgs.addArg(*getInputValue(1), indicesShape_); + outArgs.addArg(*getInputGrad(0), shape_, ADD_TO); + backward_[0]->calc(inArgs, outArgs); +} + +} // namespace paddle diff --git a/paddle/gserver/layers/ScaleSubRegionLayer.h b/paddle/gserver/layers/ScaleSubRegionLayer.h new file mode 100644 index 0000000000..a27c56de93 --- /dev/null +++ b/paddle/gserver/layers/ScaleSubRegionLayer.h @@ -0,0 +1,52 @@ +/* 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 "Layer.h" + +namespace paddle { + +/** + * \brief For each instance, this layer can be used to multiply a value to a + * specified sub continuous region. By providing start index and end + * index for C/H/W, you can specify the location and shape of the + * region. + * + * input_0: Input value. + * input_1: Indices value to specify the location an shape of the + * region. + */ +class ScaleSubRegionLayer : public Layer { +public: + explicit ScaleSubRegionLayer(const LayerConfig& config) : Layer(config) {} + + ~ScaleSubRegionLayer() {} + + bool init(const LayerMap& layerMap, const ParameterMap& parameterMap); + + void forward(PassType passType); + + void backward(const UpdateCallback& callback = nullptr); + +protected: + TensorShape shape_; + TensorShape indicesShape_; + size_t imgH_; + size_t imgW_; + size_t channelsNum_; + real value_; +}; + +} // namespace paddle diff --git a/paddle/gserver/tests/test_LayerGrad.cpp b/paddle/gserver/tests/test_LayerGrad.cpp index 89da15839e..3f7d881051 100644 --- a/paddle/gserver/tests/test_LayerGrad.cpp +++ b/paddle/gserver/tests/test_LayerGrad.cpp @@ -2358,11 +2358,11 @@ TEST(Layer, ScaleShiftLayer) { } } -TEST(Layer, MulValueLayer) { +TEST(Layer, ScaleSubRegionLayer) { const size_t batchSize = 64; const size_t size = 4096; TestConfig config; - config.layerConfig.set_type("mul_value"); + config.layerConfig.set_type("scale_sub_region"); config.inputDefs.push_back({INPUT_DATA, "input", size, 0}); MatrixPtr indicesV = Matrix::create(batchSize, 6, false, false); auto* data = indicesV->getData(); @@ -2376,16 +2376,17 @@ TEST(Layer, MulValueLayer) { } config.inputDefs.push_back({INPUT_SELF_DEFINE_DATA, "indices", indicesV, {}}); LayerInputConfig* input = config.layerConfig.add_inputs(); - MulValueConfig* mulValueConf = input->mutable_mul_value_conf(); - ImageConfig* imgConf = mulValueConf->mutable_image_conf(); + ScaleSubRegionConfig* scaleSubRegionConf = + input->mutable_scale_sub_region_conf(); + ImageConfig* imgConf = scaleSubRegionConf->mutable_image_conf(); imgConf->set_img_size(32); imgConf->set_img_size_y(32); imgConf->set_channels(4); - mulValueConf->set_value(1.0); + scaleSubRegionConf->set_value(2.0); config.layerConfig.add_inputs(); for (auto useGpu : {false, true}) { - testLayerGrad(config, "mul_value", batchSize, false, useGpu, false); + testLayerGrad(config, "scale_sub_region", batchSize, false, useGpu, false); } } diff --git a/proto/ModelConfig.proto b/proto/ModelConfig.proto index 0fecad3f7d..2d7ff1df98 100644 --- a/proto/ModelConfig.proto +++ b/proto/ModelConfig.proto @@ -321,7 +321,7 @@ message ClipConfig { required double max = 2; } -message MulValueConfig { +message ScaleSubRegionConfig { required ImageConfig image_conf = 1; required float value = 2; } @@ -347,7 +347,7 @@ message LayerInputConfig { optional MultiBoxLossConfig multibox_loss_conf = 16; optional DetectionOutputConfig detection_output_conf = 17; optional ClipConfig clip_conf = 18; - optional MulValueConfig mul_value_conf = 19; + optional ScaleSubRegionConfig scale_sub_region_conf = 19; } message LayerConfig { diff --git a/python/paddle/trainer/config_parser.py b/python/paddle/trainer/config_parser.py index 222e195efe..9e2c6f59bd 100644 --- a/python/paddle/trainer/config_parser.py +++ b/python/paddle/trainer/config_parser.py @@ -3801,21 +3801,23 @@ class SwitchOrderLayer(LayerBase): self.config.reshape_conf.width_axis.extend(reshape['width']) -@config_layer('mul_value') -class MulValueLayer(LayerBase): +@config_layer('scale_sub_region') +class ScaleSubRegionLayer(LayerBase): def __init__(self, name, inputs, value, **xargs): - super(MulValueLayer, self).__init__( - name, 'mul_value', 0, inputs=inputs, **xargs) - mul_value_conf = self.config.inputs[0].mul_value_conf - mul_value_conf.value = value + super(ScaleSubRegionLayer, self).__init__( + name, 'scale_sub_region', 0, inputs=inputs, **xargs) + scale_sub_region_conf = self.config.inputs[0].scale_sub_region_conf + scale_sub_region_conf.value = value # get channel, width and height from input_0 layer input_layer = self.get_input_layer(0) - image_conf = mul_value_conf.image_conf + image_conf = scale_sub_region_conf.image_conf image_conf.img_size = input_layer.width image_conf.img_size_y = input_layer.height image_conf.channels = input_layer.size / (input_layer.width * input_layer.height) + self.set_cnn_layer(name, image_conf.img_size_y, image_conf.img_size, + image_conf.channels) # Deprecated, use a new layer specific class instead diff --git a/python/paddle/trainer_config_helpers/layers.py b/python/paddle/trainer_config_helpers/layers.py index e6901de14b..f6527267f9 100644 --- a/python/paddle/trainer_config_helpers/layers.py +++ b/python/paddle/trainer_config_helpers/layers.py @@ -144,7 +144,7 @@ __all__ = [ 'img_conv3d_layer', 'resize_layer', 'sub_seq_layer', - 'mul_value_layer', + 'scale_sub_region_layer', ] @@ -256,7 +256,7 @@ class LayerType(object): RESIZE = 'resize' SUB_SEQ_LAYER = 'subseq' - MUL_VALUE_LAYER = 'mul_value' + SCALE_SUB_REGION_LAYER = 'scale_sub_region' @staticmethod def is_layer_type(type_name): @@ -7042,19 +7042,21 @@ def sub_seq_layer(input, offsets, sizes, act=None, bias_attr=None, name=None): size=input.size) -@wrap_name_default('mul_value') -def mul_value_layer(input, indices, value, name=None): +@wrap_name_default('scale_sub_region') +def scale_sub_region_layer(input, indices, value, name=None): """ - Given an image or feature map with CHW information, mul_value_layer can be - used to multiply a real value to values of a sub continuous region. You can - provide start and end indices of CHW for each instance. Please notice that - all start indices are counting from 1. The shape of indices should be - [batch_size, 6] and the layout for each row is [C_Start, C_End, H_Start, - H_End, W_Start, W_End]. + Given an image or feature map with CHW information, scale_sub_region_layer + can be used to multiply a real value to values of a sub continuous region. + You can provide start and end indices of CHW for each instance. + Please notice that all start indices are counting from 1. + The shape of indices should be [batch_size, 6] and the layout for each row + is [C_Start, C_End, H_Start, H_End, W_Start, W_End]. .. code-block:: python - mul_value = mul_value_layer(input=input, indices=indices, value=value) + scale_sub_region = scale_sub_region_layer(input=input, + indices=indices, + value=value) :param name: The name of this layer. It is optional. :type name: basestring @@ -7070,7 +7072,8 @@ def mul_value_layer(input, indices, value, name=None): """ assert isinstance(input, LayerOutput), ( - 'The first input of mul_value_layer, must be a PaddlePaddle layer.') + 'The first input of scale_sub_region_layer, ' + 'must be a PaddlePaddle layer.') assert isinstance(indices, LayerOutput), ( 'The start and end indices for CHW, must be a PaddlePaddle layer.') assert isinstance(value, float), ( @@ -7078,12 +7081,13 @@ def mul_value_layer(input, indices, value, name=None): Layer( name=name, - type=LayerType.MUL_VALUE_LAYER, + type=LayerType.SCALE_SUB_REGION_LAYER, inputs=[input.name, indices.name], value=value) return LayerOutput( name, - LayerType.MUL_VALUE_LAYER, + LayerType.SCALE_SUB_REGION_LAYER, parents=[input, indices], + num_filters=input.num_filters, size=input.size) diff --git a/python/paddle/trainer_config_helpers/tests/configs/file_list.sh b/python/paddle/trainer_config_helpers/tests/configs/file_list.sh index 4c00400dda..42aaed7a64 100755 --- a/python/paddle/trainer_config_helpers/tests/configs/file_list.sh +++ b/python/paddle/trainer_config_helpers/tests/configs/file_list.sh @@ -10,6 +10,6 @@ test_prelu_layer test_row_conv test_detection_output_layer test_multibox_loss_la test_recursive_topology test_gated_unit_layer test_clip_layer test_row_l2_norm_layer test_kmax_seq_socre_layer test_sub_nested_seq_select_layer test_scale_shift_layer test_seq_slice_layer test_cross_entropy_over_beam test_pooling3D_layer -test_conv3d_layer test_deconv3d_layer test_BatchNorm3D test_resize_layer test_mul_value_layer) +test_conv3d_layer test_deconv3d_layer test_BatchNorm3D test_resize_layer test_scale_sub_region_layer) export whole_configs=(test_split_datasource) diff --git a/python/paddle/trainer_config_helpers/tests/configs/protostr/test_scale_sub_region_layer.protostr b/python/paddle/trainer_config_helpers/tests/configs/protostr/test_scale_sub_region_layer.protostr new file mode 100644 index 0000000000..d20133a10e --- /dev/null +++ b/python/paddle/trainer_config_helpers/tests/configs/protostr/test_scale_sub_region_layer.protostr @@ -0,0 +1,51 @@ +type: "nn" +layers { + name: "data" + type: "data" + size: 2016 + active_type: "" + height: 48 + width: 42 +} +layers { + name: "indices" + type: "data" + size: 6 + active_type: "" +} +layers { + name: "__scale_sub_region_0__" + type: "scale_sub_region" + size: 2016 + active_type: "" + inputs { + input_layer_name: "data" + scale_sub_region_conf { + image_conf { + channels: 1 + img_size: 42 + img_size_y: 48 + } + value: 0.0 + } + } + inputs { + input_layer_name: "indices" + } + height: 48 + width: 42 +} +input_layer_names: "data" +input_layer_names: "indices" +output_layer_names: "__scale_sub_region_0__" +sub_models { + name: "root" + layer_names: "data" + layer_names: "indices" + layer_names: "__scale_sub_region_0__" + input_layer_names: "data" + input_layer_names: "indices" + output_layer_names: "__scale_sub_region_0__" + is_recurrent_layer_group: false +} + diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_scale_sub_region_layer.py b/python/paddle/trainer_config_helpers/tests/configs/test_scale_sub_region_layer.py new file mode 100644 index 0000000000..8d4bf28bf1 --- /dev/null +++ b/python/paddle/trainer_config_helpers/tests/configs/test_scale_sub_region_layer.py @@ -0,0 +1,11 @@ +from paddle.trainer_config_helpers import * + +settings(batch_size=1000, learning_rate=1e-5) + +data = data_layer(name='data', size=2016, height=48, width=42) +indices = data_layer(name='indices', size=6) + +scale_sub_region = scale_sub_region_layer( + input=data, indices=indices, value=0.0) + +outputs(scale_sub_region) From b3a86b6dbbf387a2823019a2435c76542232f864 Mon Sep 17 00:00:00 2001 From: wwhu Date: Wed, 8 Nov 2017 22:47:41 +0800 Subject: [PATCH 35/41] fix CI --- paddle/operators/clip_by_norm_op.cc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/paddle/operators/clip_by_norm_op.cc b/paddle/operators/clip_by_norm_op.cc index ebb7bdda55..d9fc532e39 100644 --- a/paddle/operators/clip_by_norm_op.cc +++ b/paddle/operators/clip_by_norm_op.cc @@ -27,7 +27,7 @@ class ClipByNormOp : public framework::OperatorWithKernel { "Input(X) of ClipByNormOp should not be null."); PADDLE_ENFORCE(ctx->HasOutput("Out"), "Output(Out) of ClipByNormOp should not be null."); - auto max_norm = Attr("max_norm"); + auto max_norm = ctx->Attrs().Get("max_norm"); PADDLE_ENFORCE_GT(max_norm, 0, "max_norm should be greater than 0."); auto x_dims = ctx->GetInputDim("X"); ctx->SetOutputDim("Out", x_dims); @@ -35,7 +35,6 @@ class ClipByNormOp : public framework::OperatorWithKernel { } }; -template class ClipByNormOpMaker : public framework::OpProtoAndCheckerMaker { public: ClipByNormOpMaker(framework::OpProto* proto, @@ -46,7 +45,7 @@ class ClipByNormOpMaker : public framework::OpProtoAndCheckerMaker { "The number of dimensions must be between [1, 9]."); AddOutput("Out", "(Tensor) The output of clip_by_norm op with shape as input(X)"); - AddAttr("max_norm", "(float) The maximum norm value."); + AddAttr("max_norm", "(float) The maximum norm value."); AddComment(R"DOC( ClipByNorm operator limits the L2 norm of the input 'X' within 'max_norm'. If the L2 norm of 'X' is less than or equal to 'max_norm', 'Out' will be @@ -66,6 +65,6 @@ where norm('X') represents the L2 norm of 'X'. namespace ops = paddle::operators; REGISTER_OP_WITHOUT_GRADIENT(clip_by_norm, ops::ClipByNormOp, - ops::ClipByNormOpMaker); + ops::ClipByNormOpMaker); REGISTER_OP_CPU_KERNEL( clip_by_norm, ops::ClipByNormKernel); From 4fd432fdaca4de977df3a9cb3a5dd58c6539a6c9 Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Wed, 8 Nov 2017 20:36:41 +0800 Subject: [PATCH 36/41] update mkldnn tag and abandoned deprecated sum API interface --- cmake/external/mkldnn.cmake | 6 +++++- paddle/gserver/layers/MKLDNNAddtoLayer.cpp | 6 +++--- paddle/gserver/layers/MKLDNNLayer.cpp | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/cmake/external/mkldnn.cmake b/cmake/external/mkldnn.cmake index 9686df0021..5a06825beb 100644 --- a/cmake/external/mkldnn.cmake +++ b/cmake/external/mkldnn.cmake @@ -46,16 +46,20 @@ IF(${CBLAS_PROVIDER} STREQUAL "MKLML") MESSAGE(STATUS "Build MKLDNN with ${MKLDNN_MKLROOT}") ENDIF() +SET(MKLDNN_CFLAG "${CMAKE_C_FLAGS} -Wno-error=strict-overflow") +SET(MKLDNN_CXXFLAG "${CMAKE_CXX_FLAGS} -Wno-error=strict-overflow") ExternalProject_Add( ${MKLDNN_PROJECT} ${EXTERNAL_PROJECT_LOG_ARGS} DEPENDS ${MKLDNN_DEPENDS} GIT_REPOSITORY "https://github.com/01org/mkl-dnn.git" - GIT_TAG "v0.10" + GIT_TAG "v0.11" PREFIX ${MKLDNN_SOURCES_DIR} UPDATE_COMMAND "" CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${MKLDNN_INSTALL_DIR} CMAKE_ARGS -DMKLROOT=${MKLDNN_MKLROOT} + CMAKE_ARGS -DCMAKE_C_FLAGS=${MKLDNN_CFLAG} + CMAKE_ARGS -DCMAKE_CXX_FLAGS=${MKLDNN_CXXFLAG} CMAKE_CACHE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${MKLDNN_INSTALL_DIR} -DMKLROOT:PATH=${MKLDNN_MKLROOT} ) diff --git a/paddle/gserver/layers/MKLDNNAddtoLayer.cpp b/paddle/gserver/layers/MKLDNNAddtoLayer.cpp index 9c13a23d48..6ffe4fbec6 100644 --- a/paddle/gserver/layers/MKLDNNAddtoLayer.cpp +++ b/paddle/gserver/layers/MKLDNNAddtoLayer.cpp @@ -91,7 +91,7 @@ void MKLDNNAddtoLayer::resetBwd(std::vector& pipeline, // backward bias bwdBias_ = nullptr; if (bias) { - std::vector scales(bs_, 1.0); + std::vector scales(bs_, 1.0); std::vector srcPDs(bs_, bias->getPrimitiveDesc()); auto biasPD = sum::primitive_desc(bias->getMemoryDesc(), scales, srcPDs); std::vector srcs; @@ -153,7 +153,7 @@ void MKLDNNAddtoLayer::resetFwdPD(std::shared_ptr& pd, std::vector& inputs, MKLDNNMatrixPtr bias, MKLDNNMatrixPtr out) { - std::vector scales(inputs.size(), 1.0); + std::vector scales(inputs.size(), 1.0); std::vector srcPDs; for (size_t i = 0; i < inputs.size(); i++) { srcPDs.push_back(inputs[i]->getPrimitiveDesc()); @@ -164,7 +164,7 @@ void MKLDNNAddtoLayer::resetFwdPD(std::shared_ptr& pd, biasPD = nullptr; if (bias) { - std::vector scales(2, 1.0); + std::vector scales(2, 1.0); std::vector srcPDs(2, bias->getPrimitiveDesc()); biasPD.reset( new sum::primitive_desc(bias->getMemoryDesc(), scales, srcPDs)); diff --git a/paddle/gserver/layers/MKLDNNLayer.cpp b/paddle/gserver/layers/MKLDNNLayer.cpp index 82ef344c7b..e75ac5ba46 100644 --- a/paddle/gserver/layers/MKLDNNLayer.cpp +++ b/paddle/gserver/layers/MKLDNNLayer.cpp @@ -287,7 +287,7 @@ void MKLDNNLayer::resetMergeGrad(MKLDNNMatrixPtr& out) { return; } CHECK(out) << "should have reset internal ouput grad"; - std::vector scales(outputMap_.size(), 1.0); + std::vector scales(outputMap_.size(), 1.0); std::vector srcPDs; std::vector srcs; for (auto it = outputMap_.begin(); it != outputMap_.end(); ++it) { From b698d19bfb64dbcf7084926425eb0693fcf20ce5 Mon Sep 17 00:00:00 2001 From: fengjiayi Date: Wed, 8 Nov 2017 14:07:46 -0800 Subject: [PATCH 37/41] Add grad for lodtensor array ops (#5461) * Add LoDRankTable LoD Rank Table stores the `level` of `lod` which is ordered by sequence length in descending order. It is useful when implement dynamic RNN and is shared by dynamic RNN memory, dynamic RNN slice input and dynamic RNN slice output operators. * Add skeleton for array_to_lod_tensor and lod_tensor_to_array * Add VarType::LoDTensorArray * Add PyBind of LoDTensorArray * Add InferVarType * Add first unittest * Add ut * Add unittest * Add unittest * Add unittests * update * init * add infershape for lod_tensor_to_array_op * compelete array_to_lod_tensor_op * copy data * clean code * clean code * Fix unittest data * fix bugs * fix compile error * Refine TensorToArrayOp * refactor array_to_lod_tensor * Unittest * fix bugs * Fix unittest * Fix unittest * debug * Debug * Fix unittest * Add grad for ops * Debug * Fix a bug * fix a bug * fix a bug --- paddle/operators/array_to_lod_tensor_op.cc | 20 +++++++++- paddle/operators/lod_tensor_to_array_op.cc | 19 +++++++++- paddle/operators/mean_op.cc | 1 + python/paddle/v2/framework/layers.py | 12 ++++-- .../tests/test_lod_tensor_array_ops.py | 38 +++++++++++++++++++ 5 files changed, 85 insertions(+), 5 deletions(-) diff --git a/paddle/operators/array_to_lod_tensor_op.cc b/paddle/operators/array_to_lod_tensor_op.cc index 6cd9c06b8a..c0903bb4e5 100644 --- a/paddle/operators/array_to_lod_tensor_op.cc +++ b/paddle/operators/array_to_lod_tensor_op.cc @@ -140,6 +140,23 @@ class ArrayToLoDTensorInferShape : public framework::InferShapeBase { "ArrayToLoDTensorOp must has input X."); PADDLE_ENFORCE(context->HasInput("RankTable"), "ArrayToLoDTensorOp must has input RankTable."); + context->SetOutputDim("Out", context->GetInputDim("X")); + } +}; + +class ArrayToLoDTensorGradMaker : public framework::SingleGradOpDescMaker { + public: + using framework::SingleGradOpDescMaker::SingleGradOpDescMaker; + + protected: + std::unique_ptr Apply() const override { + auto *grad_op = new framework::OpDescBind(); + grad_op->SetType("lod_tensor_to_array"); + grad_op->SetInput("X", OutputGrad("Out")); + grad_op->SetInput("RankTable", Input("RankTable")); + grad_op->SetOutput("Out", InputGrad("X")); + grad_op->SetAttrMap(Attrs()); + return std::unique_ptr(grad_op); } }; @@ -149,4 +166,5 @@ class ArrayToLoDTensorInferShape : public framework::InferShapeBase { namespace ops = paddle::operators; REGISTER_OPERATOR(array_to_lod_tensor, ops::ArrayToLoDTensorOp, ops::ArrayToLoDTensorOpProtoMaker, - ops::ArrayToLoDTensorInferShape); + ops::ArrayToLoDTensorInferShape, + ops::ArrayToLoDTensorGradMaker); diff --git a/paddle/operators/lod_tensor_to_array_op.cc b/paddle/operators/lod_tensor_to_array_op.cc index 5f02f5e8a1..58af35564d 100644 --- a/paddle/operators/lod_tensor_to_array_op.cc +++ b/paddle/operators/lod_tensor_to_array_op.cc @@ -133,6 +133,22 @@ class LoDTensorToArrayInferVarType : public framework::VarTypeInference { } }; +class LoDTensorToArrayGradMaker : public framework::SingleGradOpDescMaker { + public: + using framework::SingleGradOpDescMaker::SingleGradOpDescMaker; + + protected: + std::unique_ptr Apply() const override { + auto *grad_op = new framework::OpDescBind(); + grad_op->SetType("array_to_lod_tensor"); + grad_op->SetInput("X", OutputGrad("Out")); + grad_op->SetInput("RankTable", Input("RankTable")); + grad_op->SetOutput("Out", InputGrad("X")); + grad_op->SetAttrMap(Attrs()); + return std::unique_ptr(grad_op); + } +}; + } // namespace operators } // namespace paddle @@ -140,4 +156,5 @@ namespace ops = paddle::operators; REGISTER_OPERATOR(lod_tensor_to_array, ops::LoDTensorToArrayOp, ops::LoDTensorToArrayOpProtoMaker, ops::LoDTensorToArrayInferShape, - ops::LoDTensorToArrayInferVarType); + ops::LoDTensorToArrayInferVarType, + ops::LoDTensorToArrayGradMaker); diff --git a/paddle/operators/mean_op.cc b/paddle/operators/mean_op.cc index 78b4bbca84..dcc5b4286f 100644 --- a/paddle/operators/mean_op.cc +++ b/paddle/operators/mean_op.cc @@ -51,6 +51,7 @@ class MeanGradOp : public framework::OperatorWithKernel { void InferShape(framework::InferShapeContext* ctx) const override { ctx->SetOutputDim(framework::GradVarName("X"), ctx->GetInputDim("X")); + ctx->ShareLoD("X", framework::GradVarName("X")); } }; diff --git a/python/paddle/v2/framework/layers.py b/python/paddle/v2/framework/layers.py index 22540b2b97..4c6703cd8b 100644 --- a/python/paddle/v2/framework/layers.py +++ b/python/paddle/v2/framework/layers.py @@ -87,7 +87,8 @@ def data(name, type=core.VarDesc.VarType.LOD_TENSOR, append_batch_size=True, main_program=None, - startup_program=None): + startup_program=None, + stop_gradient=True): helper = LayerHelper('data', **locals()) shape = list(shape) for i in xrange(len(shape)): @@ -101,7 +102,11 @@ def data(name, shape = [-1] + shape # append batch size as -1 return helper.create_global_variable( - name=name, shape=shape, dtype=data_type, type=type, stop_gradient=True) + name=name, + shape=shape, + dtype=data_type, + type=type, + stop_gradient=stop_gradient) def _convert_(name): @@ -845,7 +850,8 @@ def lod_tensor_to_array(x, table, main_program=None): helper = LayerHelper("lod_tensor_to_array", **locals()) array = helper.create_variable( name=unique_name("lod_tensor_to_array"), - type=core.VarDesc.VarType.LOD_TENSOR_ARRAY) + type=core.VarDesc.VarType.LOD_TENSOR_ARRAY, + dtype=x.data_type) helper.append_op( type='lod_tensor_to_array', inputs={'X': x, diff --git a/python/paddle/v2/framework/tests/test_lod_tensor_array_ops.py b/python/paddle/v2/framework/tests/test_lod_tensor_array_ops.py index 61a5fcf07d..e9713666b3 100644 --- a/python/paddle/v2/framework/tests/test_lod_tensor_array_ops.py +++ b/python/paddle/v2/framework/tests/test_lod_tensor_array_ops.py @@ -4,6 +4,7 @@ import numpy import paddle.v2.framework.layers as layers from paddle.v2.framework.framework import Program from paddle.v2.framework.executor import Executor +from paddle.v2.framework.backward import append_backward_ops class TestCPULoDTensorArrayOps(unittest.TestCase): @@ -123,5 +124,42 @@ class TestCPULoDTensorArrayOps(unittest.TestCase): self.assertEqual(actual.lod(), expect.lod()) +class TestCPULoDTensorArrayOpGrad(unittest.TestCase): + def test_grad(self): + place = core.CPUPlace() + program = Program() + + x = layers.data( + name='x', + shape=[1], + data_type='float32', + main_program=program, + stop_gradient=False) + table = layers.lod_rank_table(x, level=0, main_program=program) + array = layers.lod_tensor_to_array(x, table, main_program=program) + result = layers.array_to_lod_tensor(array, table, main_program=program) + + mean = layers.mean(x=result, main_program=program) + + append_backward_ops(mean) + + tensor = core.LoDTensor() + tensor.set(numpy.arange(10).reshape(10, 1).astype('float32'), place) + tensor.set_lod([[0, 3, 9, 10]]) + + g_vars = program.global_block().var(x.name + "@GRAD") + + exe = Executor(place) + g_out = [ + item.sum() + for item in map( + numpy.array, + exe.run(program, feed={'x': tensor}, fetch_list=[g_vars])) + ] + g_out_sum = numpy.array(g_out).sum() + + self.assertAlmostEqual(1.0, g_out_sum, delta=0.1) + + if __name__ == '__main__': unittest.main() From 04a351500fde7efb2f8eafad06b1a118328ed8d7 Mon Sep 17 00:00:00 2001 From: yangyaming Date: Thu, 9 Nov 2017 10:30:03 +0800 Subject: [PATCH 38/41] Remove MulValu* and reduce time cost for unit test. --- paddle/function/MulValueOp.cpp | 155 ----------------------- paddle/function/MulValueOp.h | 55 -------- paddle/function/MulValueOpGpu.cu | 116 ----------------- paddle/function/MulValueOpTest.cpp | 75 ----------- paddle/function/ScaleSubRegionOpTest.cpp | 6 +- paddle/gserver/layers/MulValueLayer.cpp | 75 ----------- paddle/gserver/layers/MulValueLayer.h | 52 -------- 7 files changed, 3 insertions(+), 531 deletions(-) delete mode 100644 paddle/function/MulValueOp.cpp delete mode 100644 paddle/function/MulValueOp.h delete mode 100644 paddle/function/MulValueOpGpu.cu delete mode 100644 paddle/function/MulValueOpTest.cpp delete mode 100644 paddle/gserver/layers/MulValueLayer.cpp delete mode 100644 paddle/gserver/layers/MulValueLayer.h diff --git a/paddle/function/MulValueOp.cpp b/paddle/function/MulValueOp.cpp deleted file mode 100644 index fec30aac02..0000000000 --- a/paddle/function/MulValueOp.cpp +++ /dev/null @@ -1,155 +0,0 @@ -/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. */ - -#include "MulValueOp.h" -#include "paddle/function/TensorShape.h" - -namespace paddle { - -template <> -void MulValue(real* outputs, - const real* inputs, - const real* indices, - const TensorShape shape, - const FuncConfig& conf) { - real value = conf.get("value"); - - int number = shape[0]; - int channel = shape[1]; - int height = shape[2]; - int width = shape[3]; - - memcpy(outputs, inputs, number * channel * height * width * sizeof(real)); - - for (int n = 0; n < number; ++n) { - // indices start from 1 - int offset = n * 6; - for (int c = indices[offset] - 1; c < indices[offset + 1]; ++c) { - for (int h = indices[offset + 2] - 1; h < indices[offset + 3]; ++h) { - for (int w = indices[offset + 4] - 1; w < indices[offset + 5]; ++w) { - int idx = ((n * channel + c) * height + h) * width + w; - outputs[idx] *= value; - } - } - } - } -} - -template <> -void MulValueGrad(const real* inGrad, - real* outGrad, - const real* indices, - const TensorShape shape, - const FuncConfig& conf) { - real value = conf.get("value"); - - int number = shape[0]; - int channel = shape[1]; - int height = shape[2]; - int width = shape[3]; - - for (int n = 0; n < number; ++n) { - for (int c = 0; c < channel; ++c) { - for (int h = 0; h < height; ++h) { - for (int w = 0; w < width; ++w) { - int idx = ((n * channel + c) * height + h) * width + w; - int offset = n * 6; - if (c >= (indices[offset] - 1) && c <= (indices[offset + 1] - 1) && - h >= (indices[offset + 2] - 1) && - h <= (indices[offset + 3] - 1) && - w >= (indices[offset + 4] - 1) && - w <= (indices[offset + 5] - 1)) { - outGrad[idx] += inGrad[idx] * value; - } else { - outGrad[idx] += inGrad[idx]; - } - } - } - } - } -} - -/** - * \brief For each instance, MulValue can be used to multiply a value to a - * specified sub continuous region. By providing start index and end - * index for C/H/W, you can specify the location and shape of the region. - * - * Argument in this Function: - * \param inputs A 4-D tensor with shape [N, C, H, W], only one input. - * \param indices A 2-D tensor with shape [N, 6], indicates the sub region. - * \param outputs A 4-D tensor with same shape as inputs, output value. - */ -template -class MulValueFunc : public FunctionBase { -public: - void init(const FuncConfig& config) override { conf_ = config; } - - void calc(const BufferArgs& inputs, const BufferArgs& outputs) override { - CHECK_EQ(2UL, inputs.size()); - CHECK_EQ(1UL, outputs.size()); - CHECK_EQ(outputs[0].getArgType(), ASSIGN_TO); - - TensorShape shape = inputs[0].shape(); - - MulValue(outputs[0].data(), - inputs[0].data(), - inputs[1].data(), - shape, - conf_); - } - -private: - FuncConfig conf_; -}; - -/** - * \brief The backward propagation of MulValue Function. - * - * Argument in this Function: - * \param inputs A 4-D tensor with shape [N, C, H, W], output gradient. - * \param indices A 2-D tensor with shape [N, 6], indicates the sub region. - * \param outputs A 4-D tensor with shape [N, C, H, W], gradient of input value. - */ - -template -class MulValueGradFunc : public FunctionBase { -public: - void init(const FuncConfig& config) override { conf_ = config; } - - void calc(const BufferArgs& inputs, const BufferArgs& outputs) override { - CHECK_EQ(2UL, inputs.size()); - CHECK_EQ(1UL, outputs.size()); - CHECK_EQ(outputs[0].getArgType(), ADD_TO); - - TensorShape shape = inputs[0].shape(); - - MulValueGrad(inputs[0].data(), - outputs[0].data(), - inputs[1].data(), - shape, - conf_); - } - -private: - FuncConfig conf_; -}; - -REGISTER_TYPED_FUNC(MulValue, CPU, MulValueFunc); -REGISTER_TYPED_FUNC(MulValueGrad, CPU, MulValueGradFunc); -#ifdef PADDLE_WITH_CUDA -REGISTER_TYPED_FUNC(MulValue, GPU, MulValueFunc); -REGISTER_TYPED_FUNC(MulValueGrad, GPU, MulValueGradFunc); -#endif - -} // namespace paddle diff --git a/paddle/function/MulValueOp.h b/paddle/function/MulValueOp.h deleted file mode 100644 index 2e7ce105c7..0000000000 --- a/paddle/function/MulValueOp.h +++ /dev/null @@ -1,55 +0,0 @@ -/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. */ - -#pragma once - -#include "Function.h" - -namespace paddle { - -/** - * \brief Function to multiply a value to values in specified sub continuous - * region. Indices must be provided to indcate the location and shape of - * the region and the multiplied value is passed by configure variable. - * - * - * \param[out] outputs Output value. - * \param[in] inputs Input data which contains NCHW information. - * \param[in] indices Indices data to indcate the sub region. - * \param[in] shape Tensor shape of input value. - * \param[in] conf Configure variable which contains the multiplied value. - */ -template -void MulValue(real* outputs, - const real* inputs, - const real* indices, - const TensorShape shape, - const FuncConfig& conf); - -/** - * \brief Back propagation function of MulValue. - * - * \param[out] inGrad Gradients of previous layer. - * \param[in] outGrad Output gradient. - * \param[in] indices Indices data. - * \param[in] shape The Shape of input tensor. - * \param[in] conf Configure variable. - */ -template -void MulValueGrad(const real* inGrad, - real* outGrad, - const real* indices, - const TensorShape shape, - const FuncConfig& conf); -} // namespace paddle diff --git a/paddle/function/MulValueOpGpu.cu b/paddle/function/MulValueOpGpu.cu deleted file mode 100644 index 005be82131..0000000000 --- a/paddle/function/MulValueOpGpu.cu +++ /dev/null @@ -1,116 +0,0 @@ -/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. */ - -#include "MulValueOp.h" -#include "hl_base.h" - -namespace paddle { - -__global__ void KeMulValue(real* outputs, - const real* inputs, - const real* indices, - real value, - int channel, - int height, - int width, - int nthreads) { - const int idx = threadIdx.x + blockIdx.x * blockDim.x; - if (idx < nthreads) { - const int w = idx % width; - const int h = (idx / width) % height; - const int c = (idx / width / height) % channel; - const int n = idx / width / height / channel; - - const int offset = n * 6; - if (c >= (indices[offset] - 1) && c <= (indices[offset + 1] - 1) && - h >= (indices[offset + 2] - 1) && h <= (indices[offset + 3] - 1) && - w >= (indices[offset + 4] - 1) && w <= (indices[offset + 5] - 1)) { - outputs[idx] = inputs[idx] * value; - } else { - outputs[idx] = inputs[idx]; - } - } -} - -template <> -void MulValue(real* outputs, - const real* inputs, - const real* indices, - const TensorShape shape, - const FuncConfig& conf) { - real value = conf.get("value"); - - int number = shape[0]; - int channel = shape[1]; - int height = shape[2]; - int width = shape[3]; - - size_t nth = number * channel * height * width; - int blockSize = 1024; - int gridSize = (nth + blockSize - 1) / blockSize; - - KeMulValue<<>>( - outputs, inputs, indices, value, channel, height, width, nth); - CHECK_SYNC("MulValue"); -} - -__global__ void KeMulValueDiff(const real* inGrad, - real* outGrad, - const real* indices, - real value, - int channel, - int height, - int width, - int nthreads) { - const int idx = threadIdx.x + blockIdx.x * blockDim.x; - if (idx < nthreads) { - const int w = idx % width; - const int h = (idx / width) % height; - const int c = (idx / width / height) % channel; - const int n = idx / width / height / channel; - - const int offset = n * 6; - if (c >= (indices[offset] - 1) && c <= (indices[offset + 1] - 1) && - h >= (indices[offset + 2] - 1) && h <= (indices[offset + 3] - 1) && - w >= (indices[offset + 4] - 1) && w <= (indices[offset + 5] - 1)) { - outGrad[idx] += inGrad[idx] * value; - } else { - outGrad[idx] += inGrad[idx]; - } - } -} - -template <> -void MulValueGrad(const real* inGrad, - real* outGrad, - const real* indices, - const TensorShape shape, - const FuncConfig& conf) { - real value = conf.get("value"); - - int number = shape[0]; - int channel = shape[1]; - int height = shape[2]; - int width = shape[3]; - - size_t nth = number * channel * height * width; - int blockSize = 1024; - int gridSize = (nth + blockSize - 1) / blockSize; - - KeMulValueDiff<<>>( - inGrad, outGrad, indices, value, channel, height, width, nth); - CHECK_SYNC("MulValueGrad"); -} - -} // namespace paddle diff --git a/paddle/function/MulValueOpTest.cpp b/paddle/function/MulValueOpTest.cpp deleted file mode 100644 index 048660f34f..0000000000 --- a/paddle/function/MulValueOpTest.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. */ - -#include -#include "FunctionTest.h" - -namespace paddle { - -TEST(MulValue, real) { - for (size_t numSamples : {5, 32}) { - for (size_t channels : {5, 5, 32}) { - for (size_t imgSizeH : {5, 33, 100}) { - for (size_t imgSizeW : {5, 32, 96}) { - for (real value : {-0.5, 0.0, 0.5}) { - for (bool firstHalf : {false, true}) { - VLOG(3) << " numSamples=" << numSamples - << " channels=" << channels << " imgSizeH=" << imgSizeH - << " imgSizeW=" << imgSizeW; - - for (bool test_grad : {false}) { - CpuGpuFuncCompare compare( - test_grad ? "MulValueGrad" : "MulValue", - FuncConfig().set("value", value)); - - TensorShape shape{numSamples, channels, imgSizeH, imgSizeW}; - TensorShape indicesShape{numSamples, 6}; - - compare.addInputs(BufferArg(VALUE_TYPE_FLOAT, shape)); - compare.addInputs(BufferArg(VALUE_TYPE_FLOAT, indicesShape)); - - compare.registerInitCallback([=](BufferArg& arg, size_t index) { - if (index == 1) { - real* data = (real*)arg.data(); - - for (size_t i = 0; i < numSamples; ++i) { - size_t offset = i * 6; - data[offset] = firstHalf ? 1 : (int)channels / 2; - data[offset + 1] = - firstHalf ? (int)channels / 2 : channels; - data[offset + 2] = firstHalf ? 1 : (int)imgSizeH / 2; - data[offset + 3] = - firstHalf ? (int)imgSizeH / 2 : imgSizeH; - data[offset + 4] = firstHalf ? 1 : (int)imgSizeW / 2; - data[offset + 5] = - firstHalf ? (int)imgSizeW / 2 : imgSizeW; - } - } - }); - - compare.addOutputs(BufferArg(VALUE_TYPE_FLOAT, - shape, - test_grad ? ADD_TO : ASSIGN_TO), - test_grad ? ADD_TO : ASSIGN_TO); - compare.run(); - } - } - } - } - } - } - } -} - -} // namespace paddle diff --git a/paddle/function/ScaleSubRegionOpTest.cpp b/paddle/function/ScaleSubRegionOpTest.cpp index 2cbbf9d4b3..43331f258d 100644 --- a/paddle/function/ScaleSubRegionOpTest.cpp +++ b/paddle/function/ScaleSubRegionOpTest.cpp @@ -19,9 +19,9 @@ namespace paddle { TEST(ScaleSubRegion, real) { for (size_t numSamples : {5, 32}) { - for (size_t channels : {5, 5, 32}) { - for (size_t imgSizeH : {5, 33, 100}) { - for (size_t imgSizeW : {5, 32, 96}) { + for (size_t channels : {5, 32}) { + for (size_t imgSizeH : {5, 33}) { + for (size_t imgSizeW : {5, 32}) { for (real value : {-0.5, 0.0, 0.5}) { for (bool firstHalf : {false, true}) { VLOG(3) << " numSamples=" << numSamples diff --git a/paddle/gserver/layers/MulValueLayer.cpp b/paddle/gserver/layers/MulValueLayer.cpp deleted file mode 100644 index ef71de73bd..0000000000 --- a/paddle/gserver/layers/MulValueLayer.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. */ - -#include "MulValueLayer.h" -#include "paddle/utils/Stat.h" -namespace paddle { - -REGISTER_LAYER(mul_value, MulValueLayer); - -bool MulValueLayer::init(const LayerMap& layerMap, - const ParameterMap& parameterMap) { - Layer::init(layerMap, parameterMap); - CHECK_EQ(static_cast(inputLayers_.size()), 2); - auto& conf = config_.inputs(0).mul_value_conf(); - value_ = conf.value(); - - createFunction(forward_, "MulValue", FuncConfig().set("value", value_)); - createFunction(backward_, "MulValueGrad", FuncConfig().set("value", value_)); - - return true; -} - -void MulValueLayer::forward(PassType passType) { - Layer::forward(passType); - auto in0 = getInput(0); - imgH_ = in0.getFrameHeight(); - imgW_ = in0.getFrameWidth(); - if (imgH_ == 0 || imgW_ == 0) { - auto& conf = config_.inputs(0).mul_value_conf(); - imgH_ = conf.image_conf().img_size_y(); - imgW_ = conf.image_conf().img_size(); - } - MatrixPtr imgV = in0.value; - size_t batchSize = imgV->getHeight(); - size_t spatialSize = imgH_ * imgW_; - channelsNum_ = imgV->getWidth() / spatialSize; - shape_ = TensorShape({batchSize, channelsNum_, imgH_, imgW_}); - - resetOutput(batchSize, imgV->getWidth()); - - MatrixPtr indicesV = getInputValue(1); - indicesShape_ = TensorShape({batchSize, 6}); - - REGISTER_TIMER_INFO("MulValueForward", getName().c_str()); - BufferArgs inArgs; - BufferArgs outArgs; - inArgs.addArg(*imgV, shape_); - inArgs.addArg(*indicesV, indicesShape_); - MatrixPtr outV = getOutputValue(); - outArgs.addArg(*outV, shape_, ASSIGN_TO); - forward_[0]->calc(inArgs, outArgs); -} - -void MulValueLayer::backward(const UpdateCallback& callback) { - REGISTER_TIMER_INFO("MulValueBackward", getName().c_str()); - BufferArgs inArgs; - BufferArgs outArgs; - inArgs.addArg(*getOutputGrad(), shape_); - inArgs.addArg(*getInputValue(1), indicesShape_); - outArgs.addArg(*getInputGrad(0), shape_, ADD_TO); - backward_[0]->calc(inArgs, outArgs); -} - -} // namespace paddle diff --git a/paddle/gserver/layers/MulValueLayer.h b/paddle/gserver/layers/MulValueLayer.h deleted file mode 100644 index 8b315c0ede..0000000000 --- a/paddle/gserver/layers/MulValueLayer.h +++ /dev/null @@ -1,52 +0,0 @@ -/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. */ - -#pragma once - -#include "Layer.h" - -namespace paddle { - -/** - * \brief For each instance, this layer can be used to multiply a value to a - * specified sub continuous region. By providing start index and end - * index for C/H/W, you can specify the location and shape of the - * region. - * - * input_0: Input value. - * input_1: Indices value to specify the location an shape of the - * region. - */ -class MulValueLayer : public Layer { -public: - explicit MulValueLayer(const LayerConfig& config) : Layer(config) {} - - ~MulValueLayer() {} - - bool init(const LayerMap& layerMap, const ParameterMap& parameterMap); - - void forward(PassType passType); - - void backward(const UpdateCallback& callback = nullptr); - -protected: - TensorShape shape_; - TensorShape indicesShape_; - size_t imgH_; - size_t imgW_; - size_t channelsNum_; - real value_; -}; - -} // namespace paddle From 7d343fcaca90b1f027ced44c25122349a1e77735 Mon Sep 17 00:00:00 2001 From: ranqiu Date: Wed, 8 Nov 2017 20:49:13 +0800 Subject: [PATCH 39/41] Update doc of layers.py --- .../paddle/trainer_config_helpers/layers.py | 173 ++++++++---------- 1 file changed, 75 insertions(+), 98 deletions(-) diff --git a/python/paddle/trainer_config_helpers/layers.py b/python/paddle/trainer_config_helpers/layers.py index ebe81d6f68..92499b52ab 100644 --- a/python/paddle/trainer_config_helpers/layers.py +++ b/python/paddle/trainer_config_helpers/layers.py @@ -786,10 +786,9 @@ class MixedLayerType(LayerOutput): :type size: int :param act: Activation type. :type act: BaseActivation - :param bias_attr: The Bias Attribute. If the parameter is set to - False or something not type of ParameterAttribute, - no bias is defined. If the parameter is set to - True, the bias is initialized to zero. + :param bias_attr: The bias attribute. If the parameter is set to False or an object + whose type is not ParameterAttribute, no bias is defined. If the + parameter is set to True, the bias is initialized to zero. :type bias_attr: ParameterAttribute | None | bool | Any :param layer_attr: Extra Layer Attribute. :type layer_attr: ExtraLayerAttribute or None @@ -886,10 +885,9 @@ def mixed_layer(size=0, then this function will just return layer's name. :param act: Activation Type. LinearActivation is the default. :type act: BaseActivation - :param bias_attr: The Bias Attribute. If the parameter is set to - False or something not type of ParameterAttribute, - no bias is defined. If the parameter is set to - True, the bias is initialized to zero. + :param bias_attr: The bias attribute. If the parameter is set to False or an object + whose type is not ParameterAttribute, no bias is defined. If the + parameter is set to True, the bias is initialized to zero. :type bias_attr: ParameterAttribute | None | bool | Any :param layer_attr: The extra layer config. Default is None. :type layer_attr: ExtraLayerAttribute @@ -1031,10 +1029,9 @@ def fc_layer(input, :type act: BaseActivation :param param_attr: The Parameter Attribute|list. :type param_attr: ParameterAttribute - :param bias_attr: The Bias Attribute. If the parameter is set to - False or something not type of ParameterAttribute, - no bias is defined. If the parameter is set to - True, the bias is initialized to zero. + :param bias_attr: The bias attribute. If the parameter is set to False or an object + whose type is not ParameterAttribute, no bias is defined. If the + parameter is set to True, the bias is initialized to zero. :type bias_attr: ParameterAttribute | None | bool | Any :param layer_attr: Extra Layer config. :type layer_attr: ExtraLayerAttribute | None @@ -1387,10 +1384,9 @@ def pooling_layer(input, :type pooling_type: BasePoolingType | None :param stride: The step size between successive pooling regions. :type stride: Int - :param bias_attr: The Bias Attribute. If the parameter is set to - False or something not type of ParameterAttribute, - no bias is defined. If the parameter is set to - True, the bias is initialized to zero. + :param bias_attr: The bias attribute. If the parameter is set to False or an object + whose type is not ParameterAttribute, no bias is defined. If the + parameter is set to True, the bias is initialized to zero. :type bias_attr: ParameterAttribute | None | bool | Any :param layer_attr: The Extra Attributes for layer, such as dropout. :type layer_attr: ExtraLayerAttribute | None @@ -1488,10 +1484,9 @@ def lstmemory(input, :type gate_act: BaseActivation :param state_act: state activation type, TanhActivation by default. :type state_act: BaseActivation - :param bias_attr: The Bias Attribute. If the parameter is set to - False or something not type of ParameterAttribute, - no bias is defined. If the parameter is set to - True, the bias is initialized to zero. + :param bias_attr: The bias attribute. If the parameter is set to False or an object + whose type is not ParameterAttribute, no bias is defined. If the + parameter is set to True, the bias is initialized to zero. :type bias_attr: ParameterAttribute | None | bool | Any :param param_attr: Parameter Attribute. :type param_attr: ParameterAttribute | None | False @@ -1614,10 +1609,9 @@ def grumemory(input, This activation affects the :math:`z_t` and :math:`r_t`. It is the :math:`\\sigma` in the above formula. :type gate_act: BaseActivation - :param bias_attr: The Bias Attribute. If the parameter is set to - False or something not type of ParameterAttribute, - no bias is defined. If the parameter is set to - True, the bias is initialized to zero. + :param bias_attr: The bias attribute. If the parameter is set to False or an object + whose type is not ParameterAttribute, no bias is defined. If the + parameter is set to True, the bias is initialized to zero. :type bias_attr: ParameterAttribute | None | bool | Any :param param_attr: Parameter Attribute. :type param_attr: ParameterAttribute | None | False @@ -1814,10 +1808,9 @@ def expand_layer(input, :type expand_as: LayerOutput :param name: The name of this layer. It is optional. :type name: basestring - :param bias_attr: The Bias Attribute. If the parameter is set to - False or something not type of ParameterAttribute, - no bias is defined. If the parameter is set to - True, the bias is initialized to zero. + :param bias_attr: The bias attribute. If the parameter is set to False or an object + whose type is not ParameterAttribute, no bias is defined. If the + parameter is set to True, the bias is initialized to zero. :type bias_attr: ParameterAttribute | None | bool | Any :param expand_level: whether input layer is timestep(default) or sequence. :type expand_level: ExpandLevel @@ -1936,10 +1929,9 @@ def seq_reshape_layer(input, :type act: BaseActivation :param layer_attr: extra layer attributes. :type layer_attr: ExtraLayerAttribute. - :param bias_attr: The Bias Attribute. If the parameter is set to - False or something not type of ParameterAttribute, - no bias is defined. If the parameter is set to - True, the bias is initialized to zero. + :param bias_attr: The bias attribute. If the parameter is set to False or an object + whose type is not ParameterAttribute, no bias is defined. If the + parameter is set to True, the bias is initialized to zero. :type bias_attr: ParameterAttribute | None | bool | Any :return: LayerOutput object. :rtype: LayerOutput @@ -2323,10 +2315,9 @@ def hsigmoid(input, :type num_classes: int | None :param name: The name of this layer. It is optional. :type name: basestring - :param bias_attr: The Bias Attribute. If the parameter is set to - False or something not type of ParameterAttribute, - no bias is defined. If the parameter is set to - True, the bias is initialized to zero. + :param bias_attr: The bias attribute. If the parameter is set to False or an object + whose type is not ParameterAttribute, no bias is defined. If the + parameter is set to True, the bias is initialized to zero. :type bias_attr: ParameterAttribute | None | bool | Any :param param_attr: Parameter Attribute. None means default parameter. :type param_attr: ParameterAttribute | None @@ -2466,10 +2457,9 @@ def img_conv_layer(input, :type dilation: int | tuple | list :param dilation_y: The y dimension of the dilation. :type dilation_y: int - :param bias_attr: The Bias Attribute. If the parameter is set to - False or something not type of ParameterAttribute, - no bias is defined. If the parameter is set to - True, the bias is initialized to zero. + :param bias_attr: The bias attribute. If the parameter is set to False or an object + whose type is not ParameterAttribute, no bias is defined. If the + parameter is set to True, the bias is initialized to zero. :type bias_attr: ParameterAttribute | None | bool | Any :param num_channels: number of input channels. If None will be set automatically from previous output. @@ -3216,10 +3206,9 @@ def addto_layer(input, act=None, name=None, bias_attr=None, layer_attr=None): :type input: LayerOutput | list | tuple :param act: Activation Type. LinearActivation is the default. :type act: BaseActivation - :param bias_attr: The Bias Attribute. If the parameter is set to - False or something not type of ParameterAttribute, - no bias is defined. If the parameter is set to - True, the bias is initialized to zero. + :param bias_attr: The bias attribute. If the parameter is set to False or an object + whose type is not ParameterAttribute, no bias is defined. If the + parameter is set to True, the bias is initialized to zero. :type bias_attr: ParameterAttribute | None | bool | Any :param layer_attr: Extra Layer attribute. :type layer_attr: ExtraLayerAttribute @@ -3372,10 +3361,9 @@ def seq_concat_layer(a, b, act=None, name=None, layer_attr=None, :type act: BaseActivation :param layer_attr: Extra Layer Attribute. :type layer_attr: ExtraLayerAttribute - :param bias_attr: The Bias Attribute. If the parameter is set to - False or something not type of ParameterAttribute, - no bias is defined. If the parameter is set to - True, the bias is initialized to zero. + :param bias_attr: The bias attribute. If the parameter is set to False or an object + whose type is not ParameterAttribute, no bias is defined. If the + parameter is set to True, the bias is initialized to zero. :type bias_attr: ParameterAttribute | None | bool | Any :return: LayerOutput object. :rtype: LayerOutput @@ -3555,10 +3543,9 @@ def lstm_step_layer(input, :type gate_act: BaseActivation :param state_act: State Activation Type. TanhActivation is the default. :type state_act: BaseActivation - :param bias_attr: The Bias Attribute. If the parameter is set to - False or something not type of ParameterAttribute, - no bias is defined. If the parameter is set to - True, the bias is initialized to zero. + :param bias_attr: The bias attribute. If the parameter is set to False or an object + whose type is not ParameterAttribute, no bias is defined. If the + parameter is set to True, the bias is initialized to zero. :type bias_attr: ParameterAttribute | None | bool | Any :param layer_attr: layer's extra attribute. :type layer_attr: ExtraLayerAttribute @@ -3614,10 +3601,9 @@ def gru_step_layer(input, :param name: The name of this layer. It is optional. :param gate_act: Activation type of this layer's two gates. Default is Sigmoid. :type gate_act: BaseActivation - :param bias_attr: The Bias Attribute. If the parameter is set to - False or something not type of ParameterAttribute, - no bias is defined. If the parameter is set to - True, the bias is initialized to zero. + :param bias_attr: The bias attribute. If the parameter is set to False or an object + whose type is not ParameterAttribute, no bias is defined. If the + parameter is set to True, the bias is initialized to zero. :type bias_attr: ParameterAttribute | None | bool | Any :param param_attr: the parameter_attribute for transforming the output_mem from previous step. @@ -3677,10 +3663,9 @@ def gru_step_naive_layer(input, :type act: BaseActivation :param gate_act: Activation type of this layer's two gates. Default is Sigmoid. :type gate_act: BaseActivation - :param bias_attr: The Bias Attribute. If the parameter is set to - False or something not type of ParameterAttribute, - no bias is defined. If the parameter is set to - True, the bias is initialized to zero. + :param bias_attr: The bias attribute. If the parameter is set to False or an object + whose type is not ParameterAttribute, no bias is defined. If the + parameter is set to True, the bias is initialized to zero. :type bias_attr: ParameterAttribute | None | bool | Any :param param_attr: :param layer_attr: @@ -3810,10 +3795,9 @@ def recurrent_layer(input, :type input: LayerOutput :param act: Activation type. TanhActivation is the default. :type act: BaseActivation - :param bias_attr: The Bias Attribute. If the parameter is set to - False or something not type of ParameterAttribute, - no bias is defined. If the parameter is set to - True, the bias is initialized to zero. + :param bias_attr: The bias attribute. If the parameter is set to False or an object + whose type is not ParameterAttribute, no bias is defined. If the + parameter is set to True, the bias is initialized to zero. :type bias_attr: ParameterAttribute | None | bool | Any :param param_attr: parameter attribute. :type param_attr: ParameterAttribute @@ -4803,10 +4787,9 @@ def tensor_layer(a, :type act: BaseActivation :param param_attr: The Parameter Attribute. :type param_attr: ParameterAttribute - :param bias_attr: The Bias Attribute. If the parameter is set to - False or something not type of ParameterAttribute, - no bias is defined. If the parameter is set to - True, the bias is initialized to zero. + :param bias_attr: The bias attribute. If the parameter is set to False or an object + whose type is not ParameterAttribute, no bias is defined. If the + parameter is set to True, the bias is initialized to zero. :type bias_attr: ParameterAttribute | None | bool | Any :param layer_attr: Extra Layer config. :type layer_attr: ExtraLayerAttribute | None @@ -4868,10 +4851,9 @@ def selective_fc_layer(input, :type act: BaseActivation :param param_attr: The Parameter Attribute. :type param_attr: ParameterAttribute - :param bias_attr: The Bias Attribute. If the parameter is set to - False or something not type of ParameterAttribute, - no bias is defined. If the parameter is set to - True, the bias is initialized to zero. + :param bias_attr: The bias attribute. If the parameter is set to False or an object + whose type is not ParameterAttribute, no bias is defined. If the + parameter is set to True, the bias is initialized to zero. :type bias_attr: ParameterAttribute | None | bool | Any :param layer_attr: Extra Layer config. :type layer_attr: ExtraLayerAttribute | None @@ -5543,10 +5525,9 @@ def nce_layer(input, A uniform distribution will be used if not provided. If not None, its length must be equal to num_classes. :type neg_distribution: list | tuple | collections.Sequence | None - :param bias_attr: The Bias Attribute. If the parameter is set to - False or something not type of ParameterAttribute, - no bias is defined. If the parameter is set to - True, the bias is initialized to zero. + :param bias_attr: The bias attribute. If the parameter is set to False or an object + whose type is not ParameterAttribute, no bias is defined. If the + parameter is set to True, the bias is initialized to zero. :type bias_attr: ParameterAttribute | None | bool | Any :param layer_attr: Extra Layer Attribute. :type layer_attr: ExtraLayerAttribute @@ -6178,7 +6159,8 @@ def smooth_l1_cost(input, label, name=None, coeff=1.0, layer_attr=None): :type input: LayerOutput :param name: The name of this layer. It is optional. :type name: basestring - :param coeff: The coefficient affects the gradient in the backward. + :param coeff: The weight of the gradient in the back propagation. + 1.0 is the default. :type coeff: float :param layer_attr: The extra layer attribute. See ExtraLayerAttribute for details. @@ -6450,7 +6432,7 @@ def gated_unit_layer(input, :param input: The input of this layer. :type input: LayerOutput - :param size: The dimemsion of this layer's output. + :param size: The dimension of this layer's output. :type size: int :param act: Activation type of the projection. LinearActivation is the default. :type act: BaseActivation @@ -6462,10 +6444,9 @@ def gated_unit_layer(input, :param gate_param_attr: The parameter attribute of the gate. See ParameterAttribute for details. :type gate_param_attr: ParameterAttribute - :param gate_bias_attr: The bias attribute of the gate. If the parameter is set to - False or something not type of ParameterAttribute, no bias is - defined. If the parameter is set to True, the bias is initialized - to zero. + :param gate_bias_attr: The bias attribute of the gate. If the parameter is set to False or + an object whose type is not ParameterAttribute, no bias is defined. + If the parameter is set to True, the bias is initialized to zero. :type gate_bias_attr: ParameterAttribute | bool | None | Any :param inproj_attr: Extra layer attributes of the projection. See ExtraLayerAttribute for details. @@ -6473,10 +6454,9 @@ def gated_unit_layer(input, :param inproj_param_attr: The parameter attribute of the projection. See ParameterAttribute for details. :type inproj_param_attr: ParameterAttribute - :param inproj_bias_attr: The bias attribute of the projection. If the parameter is set to - False or something not type of ParameterAttribute, no bias is - defined. If the parameter is set to True, the bias is initialized - to zero. + :param inproj_bias_attr: The bias attribute of the projection. If the parameter is set to False + or an object whose type is not ParameterAttribute, no bias is defined. + If the parameter is set to True, the bias is initialized to zero. :type inproj_bias_attr: ParameterAttribute | bool | None | Any :param layer_attr: Extra layer attribute of the product. See ExtraLayerAttribute for details. @@ -6845,10 +6825,9 @@ def img_conv3d_layer(input, :param padding: The numbers of padding along three axises. If the parameter is set to one integer, they will be same. :type padding: int | tuple | list - :param bias_attr: The bias attribute. If the parameter is set to - False or something not type of ParameterAttribute, - no bias is defined. If the parameter is set to - True, the bias is initialized to zero. + :param bias_attr: The bias attribute. If the parameter is set to False or an object + whose type is not ParameterAttribute, no bias is defined. If the + parameter is set to True, the bias is initialized to zero. :type bias_attr: ParameterAttribute | None | bool | Any :param num_channels: The number of input channels. If the parameter is not set or set to None, its actual value will be automatically set to @@ -6970,10 +6949,9 @@ def scale_shift_layer(input, name=None, param_attr=None, bias_attr=None): :param param_attr: The parameter attribute of scaling. See ParameterAttribute for details. :type param_attr: ParameterAttribute - :param bias_attr: The bias attribute. If the parameter is set to - False or something not type of ParameterAttribute, - no bias is defined. If the parameter is set to - True, the bias is initialized to zero. + :param bias_attr: The bias attribute. If the parameter is set to False or an object + whose type is not ParameterAttribute, no bias is defined. If the + parameter is set to True, the bias is initialized to zero. :type bias_attr: ParameterAttribute | None | bool | Any :return: LayerOutput object. :rtype: LayerOutput @@ -7031,10 +7009,9 @@ def sub_seq_layer(input, offsets, sizes, act=None, bias_attr=None, name=None): :type sizes: LayerOutput :param act: Activation type, LinearActivation is the default. :type act: BaseActivation. - :param bias_attr: The bias attribute. If the parameter is set to - False or something not type of ParameterAttribute, - no bias is defined. If the parameter is set to - True, the bias is initialized to zero. + :param bias_attr: The bias attribute. If the parameter is set to False or an object + whose type is not ParameterAttribute, no bias is defined. If the + parameter is set to True, the bias is initialized to zero. :type bias_attr: ParameterAttribute | None | bool | Any :return: LayerOutput object. :rtype: LayerOutput From 930d2e89be5c16a024f3b100c627bf08b80b6d17 Mon Sep 17 00:00:00 2001 From: yangyaming Date: Thu, 9 Nov 2017 10:36:02 +0800 Subject: [PATCH 40/41] remove test_mul_value_layer.protostr and test_mul_value_layer.py --- .../protostr/test_mul_value_layer.protostr | 48 ------------------- .../tests/configs/test_mul_value_layer.py | 10 ---- 2 files changed, 58 deletions(-) delete mode 100644 python/paddle/trainer_config_helpers/tests/configs/protostr/test_mul_value_layer.protostr delete mode 100644 python/paddle/trainer_config_helpers/tests/configs/test_mul_value_layer.py diff --git a/python/paddle/trainer_config_helpers/tests/configs/protostr/test_mul_value_layer.protostr b/python/paddle/trainer_config_helpers/tests/configs/protostr/test_mul_value_layer.protostr deleted file mode 100644 index 389ed9d4a3..0000000000 --- a/python/paddle/trainer_config_helpers/tests/configs/protostr/test_mul_value_layer.protostr +++ /dev/null @@ -1,48 +0,0 @@ -type: "nn" -layers { - name: "data" - type: "data" - size: 2016 - active_type: "" - height: 48 - width: 42 -} -layers { - name: "indices" - type: "data" - size: 6 - active_type: "" -} -layers { - name: "__mul_value_0__" - type: "mul_value" - active_type: "" - inputs { - input_layer_name: "data" - mul_value_conf { - image_conf { - channels: 1 - img_size: 42 - img_size_y: 48 - } - value: 0.0 - } - } - inputs { - input_layer_name: "indices" - } -} -input_layer_names: "data" -input_layer_names: "indices" -output_layer_names: "__mul_value_0__" -sub_models { - name: "root" - layer_names: "data" - layer_names: "indices" - layer_names: "__mul_value_0__" - input_layer_names: "data" - input_layer_names: "indices" - output_layer_names: "__mul_value_0__" - is_recurrent_layer_group: false -} - diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_mul_value_layer.py b/python/paddle/trainer_config_helpers/tests/configs/test_mul_value_layer.py deleted file mode 100644 index 47d508d4a3..0000000000 --- a/python/paddle/trainer_config_helpers/tests/configs/test_mul_value_layer.py +++ /dev/null @@ -1,10 +0,0 @@ -from paddle.trainer_config_helpers import * - -settings(batch_size=1000, learning_rate=1e-5) - -data = data_layer(name='data', size=2016, height=48, width=42) -indices = data_layer(name='indices', size=6) - -mul_value = mul_value_layer(input=data, indices=indices, value=0.0) - -outputs(mul_value) From 53cb4df0a2b9deb6b1fd5e9c8e2027c4ad27b352 Mon Sep 17 00:00:00 2001 From: Yan Chunwei Date: Thu, 9 Nov 2017 13:32:29 +0800 Subject: [PATCH 41/41] design/sequence decoder (#4905) --- .../LOD-and-shape-changes-during-decoding.jpg | Bin 0 -> 62624 bytes doc/design/ops/sequence_decoder.md | 245 ++++++++++++++++++ 2 files changed, 245 insertions(+) create mode 100644 doc/design/ops/images/LOD-and-shape-changes-during-decoding.jpg create mode 100644 doc/design/ops/sequence_decoder.md diff --git a/doc/design/ops/images/LOD-and-shape-changes-during-decoding.jpg b/doc/design/ops/images/LOD-and-shape-changes-during-decoding.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8b0d90f7b9d8184b314b0ee4e521f53eb5f1b455 GIT binary patch literal 62624 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 L{?BXh$MpXK&hBrf literal 0 HcmV?d00001 diff --git a/doc/design/ops/sequence_decoder.md b/doc/design/ops/sequence_decoder.md new file mode 100644 index 0000000000..9007aae7a8 --- /dev/null +++ b/doc/design/ops/sequence_decoder.md @@ -0,0 +1,245 @@ +# Design: Sequence Decoder Generating LoDTensors +In tasks such as machine translation and image to text, +a [sequence decoder](https://github.com/PaddlePaddle/book/blob/develop/08.machine_translation/README.md) is necessary to generate sequences. + +This documentation describes how to implement the sequence decoder as an operator. + +## Beam Search based Decoder +The [beam search algorithm](https://en.wikipedia.org/wiki/Beam_search) is necessary when generating sequences, +it is a heuristic search algorithm that explores the paths by expanding the most promising node in a limited set. + +In the old version of PaddlePaddle, a C++ class `RecurrentGradientMachine` implements the general sequence decoder based on beam search, +due to the complexity, the implementation relays on a lot of special data structures, +quite trivial and hard to be customized by users. + +There are a lot of heuristic tricks in the sequence generation tasks, +so the flexibility of sequence decoder is very important to users. + +During PaddlePaddle's refactoring work, +some new concept is proposed such as [LoDTensor](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/framework/lod_tensor.md) and [TensorArray](https://github.com/PaddlePaddle/Paddle/blob/develop/doc/design/tensor_array.md) that can better support sequence usage, +and they can help to make the implementation of beam search based sequence decoder **more transparent and modular** . + +For example, the RNN sates, candidates IDs and probabilities of beam search can be represented as `LoDTensors`; +the selected candidate's IDs in each time step can be stored in a `TensorArray`, and `Packed` to the sentences translated. + +## Changing LoD's absolute offset to relative offsets +The current `LoDTensor` is designed to store levels of variable-length sequences, +it stores several arrays of integers each represents a level. + +The integers in each level represents the begin and end (not inclusive) offset of a sequence **in the underlying tensor**, +let's call this format the **absolute-offset LoD** for clear. + +The relative-offset LoD can fast retrieve any sequence but fails to represent empty sequences, for example, a two-level LoD is as follows +```python +[[0, 3, 9] + [0, 2, 3, 3, 3, 9]] +``` +The first level tells that there are two sequences: +- the first's offset is `[0, 3)` +- the second's offset is `[3, 9)` + +while on the second level, there are several empty sequences that both begin and end at `3`. +It is impossible to tell how many empty second-level sequences exist in the first-level sequences. + +There are many scenarios that relay on empty sequence representation, +such as machine translation or image to text, one instance has no translations or the empty candidate set for a prefix. + +So let's introduce another format of LoD, +it stores **the offsets of the lower level sequences** and is called **relative-offset** LoD. + +For example, to represent the same sequences of the above data + +```python +[[0, 3, 6] + [0, 2, 3, 3, 3, 9]] +``` + +the first level represents that there are two sequences, +their offsets in the second-level LoD is `[0, 3)` and `[3, 5)`. + +The second level is the same with the relative offset example because the lower level is a tensor. +It is easy to find out the second sequence in the first-level LoD has two empty sequences. + +The following demos are based on relative-offset LoD. + +## Usage in a simple machine translation model +Let's start from a simple machine translation model that is simplified from [machine translation chapter](https://github.com/PaddlePaddle/book/tree/develop/08.machine_translation) to draw a simple blueprint of what a sequence decoder can do and how to use it. + +The model has an encoder that learns the semantic vector from a sequence, +and a decoder which uses the sequence decoder to generate new sentences. + +**Encoder** +```python +import paddle as pd + +dict_size = 8000 +source_dict_size = dict_size +target_dict_size = dict_size +word_vector_dim = 128 +encoder_dim = 128 +decoder_dim = 128 +beam_size = 5 +max_length = 120 + +# encoder +src_word_id = pd.data( + name='source_language_word', + type=pd.data.integer_value_sequence(source_dict_dim)) +src_embedding = pd.embedding(size=source_dict_size, size=word_vector_dim) + +src_word_vec = pd.lookup(src_embedding, src_word_id) + +encoder_out_seq = pd.gru(input=src_word_vec, size=encoder_dim) + +encoder_ctx = pd.last_seq(encoder_out_seq) +# encoder_ctx_proj is the learned semantic vector +encoder_ctx_proj = pd.fc( + encoder_ctx, size=decoder_dim, act=pd.activation.Tanh(), bias=None) +``` + +**Decoder** + +```python +def generate(): + decoder = pd.while_loop() + with decoder.step(): + decoder_mem = decoder.memory(init=encoder_ctx) # mark the memory + generated_ids = decoder.memory() # TODO init to batch_size s + generated_scores = decoder.memory() # TODO init to batch_size 1s or 0s + + target_word = pd.lookup(trg_embedding, gendrated_ids) + # expand encoder_ctx's batch to fit target_word's lod + # for example + # decoder_mem.lod is + # [[0 1 3], + # [0 1 3 6]] + # its tensor content is [a1 a2 a3 a4 a5] + # which means there are 2 sentences to translate + # - the first sentence has 1 translation prefixes, the offsets are [0, 1) + # - the second sentence has 2 translation prefixes, the offsets are [1, 3) and [3, 6) + # the target_word.lod is + # [[0, 1, 6] + # [0, 2, 4, 7, 9 12]] + # which means 2 sentences to translate, each has 1 and 5 prefixes + # the first prefix has 2 candidates + # the following has 2, 3, 2, 3 candidates + # the encoder_ctx_expanded's content will be + # [a1 a1 a2 a2 a3 a3 a3 a4 a4 a5 a5 a5] + encoder_ctx_expanded = pd.lod_expand(encoder_ctx, target_word) + decoder_input = pd.fc( + act=pd.activation.Linear(), + input=[target_word, encoder_ctx], + size=3 * decoder_dim) + gru_out, cur_mem = pd.gru_step( + decoder_input, mem=decoder_mem, size=decoder_dim) + scores = pd.fc( + gru_out, + size=trg_dic_size, + bias=None, + act=pd.activation.Softmax()) + # K is an config + topk_scores, topk_ids = pd.top_k(scores, K) + topk_generated_scores = pd.add_scalar(topk_scores, generated_scores) + + selected_ids, selected_generation_scores = decoder.beam_search( + topk_ids, topk_generated_scores) + + # update the states + decoder_mem.update(cur_mem) # tells how to update state + generated_ids.update(selected_ids) + generated_scores.update(selected_generation_scores) + + decoder.output(selected_ids) + decoder.output(selected_generation_scores) + +translation_ids, translation_scores = decoder() +``` +The `decoder.beam_search` is a operator that given the candidates and the scores of translations including the candidates, +return the result of the beam search algorithm. + +In this way, users can customize anything on the inputs or outputs of beam search, for example, two ways to prune some translation prefixes + +1. meke the correspondind elements in `topk_generated_scores` zero or some small values, beam_search will discard this candidate. +2. remove some specific candidate in `selected_ids` +3. get the final `translation_ids`, remove the translation sequence in it. + +The implementation of sequence decoder can reuse the C++ class [RNNAlgorithm](https://github.com/Superjom/Paddle/blob/68cac3c0f8451fe62a4cdf156747d6dc0ee000b3/paddle/operators/dynamic_recurrent_op.h#L30), +so the python syntax is quite similar to a [RNN](https://github.com/Superjom/Paddle/blob/68cac3c0f8451fe62a4cdf156747d6dc0ee000b3/doc/design/block.md#blocks-with-for-and-rnnop). + +Both of them are two-level `LoDTensors` + +- the first level represents `batch_size` of (source) sentences; +- the second level represents the candidate ID sets for translation prefix. + +for example, 3 source sentences to translate, and has 2, 3, 1 candidates. + +Unlike an RNN, in sequence decoder, the previous state and the current state have different LoD and shape, +a `lod_expand` operator is used to expand the LoD of the previous state to fit the current state. + +For example, the previous state + +* LoD is `[0, 1, 3][0, 2, 5, 6]` +* content of tensor is `a1 a2 b1 b2 b3 c1` + +the current state stored in `encoder_ctx_expanded` + +* LoD is `[0, 2, 7][0 3 5 8 9 11 11]` +* the content is + - a1 a1 a1 (a1 has 3 candidates, so the state should be copied 3 times for each candidates) + - a2 a2 + - b1 b1 b1 + - b2 + - b3 b3 + - None (c1 has 0 candidates, so c1 is dropped) + +Benefit from the relative offset LoD, empty candidate set can be represented naturally. + +the status in each time step can be stored in `TensorArray`, and `Pack`ed to a final LoDTensor, the corresponding syntax is + +```python +decoder.output(selected_ids) +decoder.output(selected_generation_scores) +``` + +the `selected_ids` is the candidate ids for the prefixes, +it will be `Packed` by `TensorArray` to a two-level `LoDTensor`, +the first level represents the source sequences, +the second level represents generated sequences. + +Pack the `selected_scores` will get a `LoDTensor` that stores scores of each candidate of translations. + +Pack the `selected_generation_scores` will get a `LoDTensor`, and each tail is the probability of the translation. + +## LoD and shape changes during decoding +

+ +

+ +According the image above, the only phrase to change LoD is beam search. + +## Beam search design +The beam search algorthm will be implemented as one method of the sequence decoder, it has 3 inputs + +1. `topk_ids`, top K candidate ids for each prefix. +2. `topk_scores`, the corresponding scores for `topk_ids` +3. `generated_scores`, the score of the prefixes. + +All of the are LoDTensors, so that the sequence affilication is clear. +Beam search will keep a beam for each prefix and select a smaller candidate set for each prefix. + +It will return three variables + +1. `selected_ids`, the final candidate beam search function selected for the next step. +2. `selected_scores`, the scores for the candidates. +3. `generated_scores`, the updated scores for each prefixes (with the new candidates appended). + +## Introducing the LoD-based `Pack` and `Unpack` methods in `TensorArray` +The `selected_ids`, `selected_scores` and `generated_scores` are LoDTensors, +and they exist in each time step, +so it is natural to store them in arrays. + +Currently, PaddlePaddle has a module called `TensorArray` which can store an array of tensors, +the results of beam search are better to store in a `TensorArray`. + +The `Pack` and `UnPack` in `TensorArray` are used to package tensors in the array to a `LoDTensor` or split the `LoDTensor` to an array of tensors. +It needs some extensions to support pack or unpack an array of `LoDTensors`.