From b9767aead40edd893052a1d89c56dd9b0b790b61 Mon Sep 17 00:00:00 2001 From: hedaoyuan Date: Mon, 31 Jul 2017 14:44:26 +0800 Subject: [PATCH 1/8] Add SliceConfig. --- proto/ModelConfig.proto | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/proto/ModelConfig.proto b/proto/ModelConfig.proto index 83f72c137bdf5..d7f00fba474cc 100644 --- a/proto/ModelConfig.proto +++ b/proto/ModelConfig.proto @@ -198,6 +198,11 @@ message RowConvConfig { required uint32 context_length = 1; } +message SliceConfig { + required uint32 start = 1; + required uint32 end = 2; +} + message ProjectionConfig { required string type = 1; required string name = 2; @@ -218,6 +223,9 @@ message ProjectionConfig { // For pool optional PoolConfig pool_conf = 12; + + // For slice + repeated SliceConfig slice = 13; } message OperatorConfig { From dc9f31b32b5b4cfd1aa50493d41b13759c2c19fd Mon Sep 17 00:00:00 2001 From: hedaoyuan Date: Mon, 31 Jul 2017 21:22:08 +0800 Subject: [PATCH 2/8] Add SliceProjection and slice_projection. --- proto/ModelConfig.proto | 3 +- python/paddle/trainer/config_parser.py | 29 ++++++++++++++ .../paddle/trainer_config_helpers/layers.py | 40 +++++++++++++++++++ 3 files changed, 71 insertions(+), 1 deletion(-) diff --git a/proto/ModelConfig.proto b/proto/ModelConfig.proto index d7f00fba474cc..3bee5b572ae42 100644 --- a/proto/ModelConfig.proto +++ b/proto/ModelConfig.proto @@ -225,7 +225,8 @@ message ProjectionConfig { optional PoolConfig pool_conf = 12; // For slice - repeated SliceConfig slice = 13; + // Each slice output is the input[start, end) + repeated SliceConfig slices = 13; } message OperatorConfig { diff --git a/python/paddle/trainer/config_parser.py b/python/paddle/trainer/config_parser.py index 5477158ecb864..f71fefffb59d4 100644 --- a/python/paddle/trainer/config_parser.py +++ b/python/paddle/trainer/config_parser.py @@ -565,6 +565,35 @@ def calc_parameter_dims(self, input_size, output_size): return [] +@config_class +class SliceProjection(Projection): + type = 'slice' + + def __init__(self, input_layer_name, slices, **xargs): + super(SliceProjection, self).__init__(input_layer_name, **xargs) + input = g_layer_map[input_layer_name] + if input.type in ["exconv", "cudnn_conv"]: + # the slice operator is for the channel dimension + assert input.num_filters is not None + channels = input.num_filters + image_size = input.size / channels + assert slices[len(slices) - 1][1] <= channels + for i in xrange(len(slices)): + slice = self.proj_conf.slices.add() + slice.start = slices[i][0] * image_size + slice.end = slices[i][1] * image_size + self.size += slice.end - slice.start + else: + config_assert(False, + 'Currently the input should be convolution layer') + + def calc_parameter_size(self, input_size, output_size): + return 0 + + def calc_parameter_dims(self, input_size, output_size): + return [] + + # DotMulProjection performs element-wise multiplication with weight @config_class class DotMulProjection(Projection): diff --git a/python/paddle/trainer_config_helpers/layers.py b/python/paddle/trainer_config_helpers/layers.py index 14f072fc55109..d1c2cecc6c7f7 100755 --- a/python/paddle/trainer_config_helpers/layers.py +++ b/python/paddle/trainer_config_helpers/layers.py @@ -128,6 +128,7 @@ 'prelu_layer', 'gated_unit_layer', 'crop_layer', + 'slice_projection', ] @@ -536,6 +537,45 @@ def identity_projection(input, offset=None, size=None): return proj +def slice_projection(input, slices): + """ + slice_projection can get multiple outputs, and each output is a slice + of the input. + + .. math:: + output[i] = input.slice(slices[i]) + + The example usage is: + + .. code-block:: python + + proj = slice_projection(input=layer, slices=[(0, 10), (20, 30)]) + + Note that slice_projection should not have any parameter. + + :param input: Input Layer. + :type input: LayerOutput + :param slices: An array of slice parameters. + Each slice contains the start and end offsets based + on the input. + :type offset: pair of int + :return: A SliceProjection object + :rtype: SliceProjection + """ + assert len(slices) >= 1 + start = 0 + for i in xrange(len(slices)): + assert len(slices[i]) == 2 + # The start position of the next slice needs to be greater than + # or equal to the end position of the previous slice. + assert slices[i][0] >= start + assert slices[i][1] >= slices[i][0] + start = slices[i][1] + proj = SliceProjection(input_layer_name=input.name, slices=slices) + proj.origin = input + return proj + + @wrap_param_attr_default() def scaling_projection(input, param_attr=None): """ From 0cca0fcf1152b6d2a7c9068934aa2505491ca856 Mon Sep 17 00:00:00 2001 From: hedaoyuan Date: Tue, 1 Aug 2017 11:35:17 +0800 Subject: [PATCH 3/8] Add SliceProjection. --- paddle/gserver/layers/SliceProjection.cpp | 96 +++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 paddle/gserver/layers/SliceProjection.cpp diff --git a/paddle/gserver/layers/SliceProjection.cpp b/paddle/gserver/layers/SliceProjection.cpp new file mode 100644 index 0000000000000..a361d19bde435 --- /dev/null +++ b/paddle/gserver/layers/SliceProjection.cpp @@ -0,0 +1,96 @@ +/* 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 "Projection.h" + +namespace paddle { + +/** + * SliceProjection can slice the input value into multiple parts, + * and then select some of them to merge into a new output. + * + * First, calculate the slices that need to be merged into the output. + * slices = input.slices().for_output() + * + * Second, merge each slice into the output. + * for(auto slice: slices) { + * out.addAtOffset(slice, offset); + * } + * + * Input slices as output: s0, s1, ...: + * ----------------------- + * |///| |//////| | + * |/s0| |//s1//| | + * |///| |//////| | + * ----------------------- + * Output, merge s0, s1, ... into one output: + * ---------------- + * |///|//////| | + * |/s0|//s1//|...| + * |///|//////| | + * ---------------- + * + * The config file api is slice_projection. + */ +class SliceProjection : public Projection { +public: + SliceProjection(const ProjectionConfig& config, + const ParameterPtr& parameter, + bool useGpu); + virtual void forward(); + virtual void backward(const UpdateCallback& callback); + +protected: + std::vector> slices_; +}; + +REGISTER_PROJECTION(slice, SliceProjection); + +/** + * Constructed function. + * @note SliceProjection should not have any parameter. + */ +SliceProjection::SliceProjection(const ProjectionConfig& config, + const ParameterPtr& parameter, + bool useGpu) + : Projection(config, parameter, useGpu) { + CHECK(!parameter) << "'slice' projection should not have any parameter"; + + slices_.reserve(config.slices_size()); + for (const auto& slice : config.slices()) { + slices_.push_back(std::make_pair(slice.start(), slice.end())); + } +} + +void SliceProjection::forward() { + size_t offset = 0; + for (auto& slice : slices_) { + auto slice_out = in_->value->subColMatrix(slice.first, slice.second); + out_->value->addAtOffset(*slice_out, offset); + offset += slice_out->getWidth(); + } +} + +void SliceProjection::backward(const UpdateCallback& callback) { + if (in_->grad) { + size_t offset = 0; + for (auto& slice : slices_) { + auto slice_out = in_->grad->subColMatrix(slice.first, slice.second); + slice_out->addAtOffset(*out_->grad, config_.offset()); + offset += slice_out->getWidth(); + } + } +} + +} // namespace paddle From 4b1bc6815e81b8370ce373b58fb4db1affdec029 Mon Sep 17 00:00:00 2001 From: hedaoyuan Date: Tue, 1 Aug 2017 11:45:49 +0800 Subject: [PATCH 4/8] Fix comments of slice_projection, and add unit test of SliceProjection. --- paddle/gserver/tests/test_LayerGrad.cpp | 20 +++++++++++++++++++ .../paddle/trainer_config_helpers/layers.py | 6 +++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/paddle/gserver/tests/test_LayerGrad.cpp b/paddle/gserver/tests/test_LayerGrad.cpp index 0975c3bc9573c..00ca4982e9ab0 100644 --- a/paddle/gserver/tests/test_LayerGrad.cpp +++ b/paddle/gserver/tests/test_LayerGrad.cpp @@ -152,6 +152,26 @@ TEST(Projection, identity) { } } +TEST(Projection, slice) { + ProjectionConfig conf; + conf.set_type("slice"); + conf.set_input_size(100); + SliceConfig& slice1 = *conf.add_slices(); + slice1.set_start(10); + slice1.set_end(20); + SliceConfig& slice2 = *conf.add_slices(); + slice2.set_start(50); + slice2.set_end(70); + conf.set_output_size(30); + for (auto useGpu : {false, true}) { + testProjectionGrad(conf, + INPUT_DATA, + /* parameterSize */ 0, + /* batchSize */ 100, + useGpu); + } +} + TEST(Projection, scaling) { ProjectionConfig conf; conf.set_type("scaling"); diff --git a/python/paddle/trainer_config_helpers/layers.py b/python/paddle/trainer_config_helpers/layers.py index d1c2cecc6c7f7..2045233522192 100755 --- a/python/paddle/trainer_config_helpers/layers.py +++ b/python/paddle/trainer_config_helpers/layers.py @@ -539,11 +539,11 @@ def identity_projection(input, offset=None, size=None): def slice_projection(input, slices): """ - slice_projection can get multiple outputs, and each output is a slice - of the input. + slice_projection can slice the input value into multiple parts, + and then select some of them to merge into a new output. .. math:: - output[i] = input.slice(slices[i]) + output = [input.slices()] The example usage is: From d456c286093ca6c74f7c6d02b67d3339877f564a Mon Sep 17 00:00:00 2001 From: hedaoyuan Date: Tue, 1 Aug 2017 13:50:05 +0800 Subject: [PATCH 5/8] Fix some bug. --- paddle/gserver/layers/SliceProjection.cpp | 2 +- paddle/gserver/tests/test_LayerGrad.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/paddle/gserver/layers/SliceProjection.cpp b/paddle/gserver/layers/SliceProjection.cpp index a361d19bde435..267dd6154b1b2 100644 --- a/paddle/gserver/layers/SliceProjection.cpp +++ b/paddle/gserver/layers/SliceProjection.cpp @@ -87,7 +87,7 @@ void SliceProjection::backward(const UpdateCallback& callback) { size_t offset = 0; for (auto& slice : slices_) { auto slice_out = in_->grad->subColMatrix(slice.first, slice.second); - slice_out->addAtOffset(*out_->grad, config_.offset()); + slice_out->addAtOffset(*out_->grad, offset); offset += slice_out->getWidth(); } } diff --git a/paddle/gserver/tests/test_LayerGrad.cpp b/paddle/gserver/tests/test_LayerGrad.cpp index 00ca4982e9ab0..8ce8600c67437 100644 --- a/paddle/gserver/tests/test_LayerGrad.cpp +++ b/paddle/gserver/tests/test_LayerGrad.cpp @@ -167,7 +167,7 @@ TEST(Projection, slice) { testProjectionGrad(conf, INPUT_DATA, /* parameterSize */ 0, - /* batchSize */ 100, + /* batchSize */ 10, useGpu); } } From bfdd9a1ced319bce91a0b4fb197cf323a9a6dfe8 Mon Sep 17 00:00:00 2001 From: hedaoyuan Date: Tue, 1 Aug 2017 13:56:05 +0800 Subject: [PATCH 6/8] Add a unit test for concat and slice network in test_NetworkCompare.cpp --- paddle/gserver/tests/concat_slice_a.conf | 41 ++++++++++++++++++++ paddle/gserver/tests/concat_slice_b.conf | 41 ++++++++++++++++++++ paddle/gserver/tests/test_NetworkCompare.cpp | 6 +++ 3 files changed, 88 insertions(+) create mode 100644 paddle/gserver/tests/concat_slice_a.conf create mode 100644 paddle/gserver/tests/concat_slice_b.conf diff --git a/paddle/gserver/tests/concat_slice_a.conf b/paddle/gserver/tests/concat_slice_a.conf new file mode 100644 index 0000000000000..dccf911089e16 --- /dev/null +++ b/paddle/gserver/tests/concat_slice_a.conf @@ -0,0 +1,41 @@ +#edit-mode: -*- python -*- +# 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. + + +from paddle.trainer_config_helpers import * + +settings(batch_size=10) + +data = data_layer(name ="input", size=8*16*16) + +conv1 = img_conv_layer(input=data, filter_size=1, filter_size_y=1, + num_channels=8, + num_filters=16, stride=1, + bias_attr=False, + act=ReluActivation()) +conv2 = img_conv_layer(input=data, filter_size=1, filter_size_y=1, + num_channels=8, + num_filters=16, stride=1, + bias_attr=False, + act=ReluActivation()) + +proj1 = slice_projection(input=conv1, slices=[(0, 4), (4, 12)]) + +proj2 = slice_projection(input=conv2, slices=[(1, 5), (5, 15)]) + +concat = concat_layer(input=[proj1, proj2]) + +outputs(concat) + diff --git a/paddle/gserver/tests/concat_slice_b.conf b/paddle/gserver/tests/concat_slice_b.conf new file mode 100644 index 0000000000000..29686ef281037 --- /dev/null +++ b/paddle/gserver/tests/concat_slice_b.conf @@ -0,0 +1,41 @@ +#edit-mode: -*- python -*- +# 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. + + +from paddle.trainer_config_helpers import * + +settings(batch_size=10) + +data = data_layer(name ="input", size=8*16*16) + +conv1 = img_conv_layer(input=data, filter_size=1, filter_size_y=1, + num_channels=8, + num_filters=16, stride=1, + bias_attr=False, + act=ReluActivation()) +conv2 = img_conv_layer(input=data, filter_size=1, filter_size_y=1, + num_channels=8, + num_filters=16, stride=1, + bias_attr=False, + act=ReluActivation()) + +proj1 = slice_projection(input=conv1, slices=[(0, 12)]) + +proj2 = slice_projection(input=conv2, slices=[(1, 15)]) + +concat = concat_layer(input=[proj1, proj2]) + +outputs(concat) + diff --git a/paddle/gserver/tests/test_NetworkCompare.cpp b/paddle/gserver/tests/test_NetworkCompare.cpp index 40e662b22bac0..f930c72fde3f5 100644 --- a/paddle/gserver/tests/test_NetworkCompare.cpp +++ b/paddle/gserver/tests/test_NetworkCompare.cpp @@ -237,6 +237,12 @@ TEST(Compare, concat_table) { compareNetwork(config_file_a, config_file_b); } +TEST(Compare, concat_slice) { + std::string config_file_a = "./gserver/tests/concat_slice_a.conf"; + std::string config_file_b = "./gserver/tests/concat_slice_b.conf"; + compareNetwork(config_file_a, config_file_b); +} + #ifndef PADDLE_ONLY_CPU TEST(Compare, img_pool) { std::string config_file_a = "./gserver/tests/img_pool_a.conf"; From 46988517f2b352919caaa0cf6879f800d42ed8ae Mon Sep 17 00:00:00 2001 From: hedaoyuan Date: Tue, 1 Aug 2017 14:08:43 +0800 Subject: [PATCH 7/8] Fix a small bug. --- python/paddle/trainer_config_helpers/layers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/paddle/trainer_config_helpers/layers.py b/python/paddle/trainer_config_helpers/layers.py index 2045233522192..965874ddf632a 100755 --- a/python/paddle/trainer_config_helpers/layers.py +++ b/python/paddle/trainer_config_helpers/layers.py @@ -558,7 +558,7 @@ def slice_projection(input, slices): :param slices: An array of slice parameters. Each slice contains the start and end offsets based on the input. - :type offset: pair of int + :type slices: pair of int :return: A SliceProjection object :rtype: SliceProjection """ From 90846f3c9d8db875522442cfadbd10c7f5710b12 Mon Sep 17 00:00:00 2001 From: hedaoyuan Date: Tue, 1 Aug 2017 19:11:21 +0800 Subject: [PATCH 8/8] Add interface description into api documentation. --- doc/api/v2/config/layer.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/api/v2/config/layer.rst b/doc/api/v2/config/layer.rst index daee55b7f9adf..ec7f1446cfb74 100644 --- a/doc/api/v2/config/layer.rst +++ b/doc/api/v2/config/layer.rst @@ -198,6 +198,10 @@ identity_projection .. autoclass:: paddle.v2.layer.identity_projection :noindex: +slice_projection +------------------- +.. autoclass:: paddle.v2.layer.slice_projection + :noindex: table_projection ----------------