diff --git a/doc/api/v2/fluid/layers.rst b/doc/api/v2/fluid/layers.rst index 24bdf08fffd176a799fd12680f4651bb4bd0c9a9..62c154e65dcff1bdfb00109bf1b724c34731652e 100644 --- a/doc/api/v2/fluid/layers.rst +++ b/doc/api/v2/fluid/layers.rst @@ -358,6 +358,12 @@ reduce_min .. autofunction:: paddle.v2.fluid.layers.reduce_min :noindex: + +split +----- +.. autofunction:: paddle.v2.fluid.layers.split + :noindex: + logsigmoid ---------- .. autofunction:: paddle.v2.fluid.layers.logsigmoid diff --git a/doc/api/v2/fluid/nets.rst b/doc/api/v2/fluid/nets.rst index b792efb71f85ae643df655568da69c82414e9d5d..cca0dcdf082c0d809fab1aebba2c0b6c7b8efa2a 100644 --- a/doc/api/v2/fluid/nets.rst +++ b/doc/api/v2/fluid/nets.rst @@ -20,3 +20,8 @@ sequence_conv_pool :noindex: +glu +--- +.. autofunction:: paddle.v2.fluid.nets.glu + :noindex: + diff --git a/paddle/operators/split_op.cc b/paddle/operators/split_op.cc index 4dfae043cb1091c9491d89aec4d1415d4741e013..8d55ae5dd7b0e76acb9f21cb10b79cb7aca18a8d 100644 --- a/paddle/operators/split_op.cc +++ b/paddle/operators/split_op.cc @@ -60,6 +60,12 @@ class SplitOp : public framework::OperatorWithKernel { } } ctx->SetOutputsDim("Out", outs_dims); + if (axis != 0) { + // Only pass LoD when not spliting along the first dim. + for (size_t i = 0; i < outs_number; ++i) { + ctx->ShareLoD("X", "Out", 0, i); + } + } } }; diff --git a/python/paddle/v2/fluid/layers/nn.py b/python/paddle/v2/fluid/layers/nn.py index 99a40ce45a2ff5c89fdfb2f0c170dbc34ee696bc..96a64af37099c68f46ddf309b1995b119c352314 100644 --- a/python/paddle/v2/fluid/layers/nn.py +++ b/python/paddle/v2/fluid/layers/nn.py @@ -36,6 +36,7 @@ __all__ = [ 'sequence_first_step', 'sequence_last_step', 'dropout', + 'split', ] @@ -1525,3 +1526,61 @@ def reduce_min(input, dim=None, keep_dim=False): 'reduce_all': True if dim == None else False }) return out + + +def split(input, num_or_sections, dim=-1): + """ + Splits the tensor into multiple sub-tensors. + + Args: + input (Variable): The input variable which is a Tensor or LoDTensor. + num_or_sections (int|list): If :attr:`num_or_sections` is an integer, + then the integer indicates the number of equal sized sub-tensors + that the tensor will be divided into. If :attr:`num_or_sections` + is a list of integers, the length of list indicates the number of + sub-tensors and the integers indicate the sizes of sub-tensors' + :attr:`dim` dimension orderly. + dim (int): The dimension along which to split. If :math:`dim < 0`, the + dimension to split along is :math:`rank(input) + dim`. + + Returns: + List: The list of segmented tensor variables. + + Examples: + .. code-block:: python + + # x is a Tensor variable with shape [3, 9, 5]: + x0, x1, x2 = fluid.layers.split(x, num_or_sections=3, dim=1) + x0.shape # [3, 3, 5] + x1.shape # [3, 3, 5] + x2.shape # [3, 3, 5] + x0, x1, x2 = fluid.layers.split(x, num_or_sections=[2, 3, 4], dim=1) + x0.shape # [3, 2, 5] + x1.shape # [3, 3, 5] + x2.shape # [3, 4, 5] + """ + helper = LayerHelper('split', **locals()) + input_shape = input.shape + dim = (len(input_shape) + dim) if dim < 0 else dim + if isinstance(num_or_sections, int): + assert num_or_sections > 1, 'num_or_sections must be more than 1.' + num = num_or_sections + else: + assert len(num_or_sections) < input_shape[ + dim], 'len(num_or_sections) must not be more than input.shape[dim].' + num = len(num_or_sections) + outs = [ + helper.create_tmp_variable(dtype=helper.input_dtype()) + for i in range(num) + ] + helper.append_op( + type='split', + inputs={'X': input}, + outputs={'Out': outs}, + attrs={ + 'num': num_or_sections if isinstance(num_or_sections, int) else 0, + 'sections': num_or_sections + if isinstance(num_or_sections, list) else [], + 'axis': dim + }) + return outs diff --git a/python/paddle/v2/fluid/nets.py b/python/paddle/v2/fluid/nets.py index 47b550bf4d851a6c19fa88cc5fff2a7a0afc9bda..d515429216cd4672f1774b0b52e6d9fd6e06db58 100644 --- a/python/paddle/v2/fluid/nets.py +++ b/python/paddle/v2/fluid/nets.py @@ -3,6 +3,7 @@ import layers __all__ = [ "simple_img_conv_pool", "sequence_conv_pool", + "glu", ] @@ -101,3 +102,36 @@ def sequence_conv_pool(input, pool_out = layers.sequence_pool(input=conv_out, pool_type=pool_type) return pool_out + + +def glu(input, dim=-1): + """ + The gated linear unit composed by split and elementwise multiplication. + Specifically, Split the input into two equal sized parts :math:`a` and + :math:`b` along the given dimension and then compute as following: + + .. math:: + + {GLU}(a, b)= a \otimes \sigma(b) + + Refer to `Language Modeling with Gated Convolutional Networks + `_. + + Args: + input (Variable): The input variable which is a Tensor or LoDTensor. + dim (int): The dimension along which to split. If :math:`dim < 0`, the + dimension to split along is :math:`rank(input) + dim`. + + Returns: + Variable: The Tensor variable with half the size of input. + + Examples: + .. code-block:: python + + # x is a Tensor variable with shape [3, 6, 9] + fluid.nets.glu(input=x, dim=1) # shape of output: [3, 3, 9] + """ + + a, b = layers.split(input, num_or_sections=2, dim=dim) + out = layers.elementwise_mul(x=a, y=b) + return out diff --git a/python/paddle/v2/fluid/tests/test_reorder_lod_tensor.py b/python/paddle/v2/fluid/tests/test_reorder_lod_tensor.py index 8b79d448e263d00849877c29158d7898bafe1937..215accd4c66a68ce152690f2fcd90ba0977ecd4b 100644 --- a/python/paddle/v2/fluid/tests/test_reorder_lod_tensor.py +++ b/python/paddle/v2/fluid/tests/test_reorder_lod_tensor.py @@ -6,8 +6,8 @@ import numpy class TestReorderLoDTensor(unittest.TestCase): num_seq = 5 - # [name, dim, lod_level] pair indicating data info of source and target - data_desc = (['input', 9, 0], ['ref', 5, 1]) + # [name, shape, lod_level] pair indicating data info of source and target + data_desc = (['input', [9], 0], ['ref', [5], 1]) @classmethod def setUpClass(cls): @@ -16,10 +16,10 @@ class TestReorderLoDTensor(unittest.TestCase): @classmethod def set_program(cls): dat = fluid.layers.data( - name=cls.data_desc[0][0], shape=[cls.data_desc[0][1]]) + name=cls.data_desc[0][0], shape=cls.data_desc[0][1]) dat.stop_gradient = False rank_dat = fluid.layers.data( - name=cls.data_desc[1][0], shape=[cls.data_desc[1][1]]) + name=cls.data_desc[1][0], shape=cls.data_desc[1][1]) table = fluid.layers.lod_rank_table(rank_dat) new_dat = fluid.layers.reorder_lod_tensor_by_rank( x=dat, rank_table=table) @@ -49,7 +49,7 @@ class TestReorderLoDTensor(unittest.TestCase): self.data = {} for desc in self.data_desc: data_name = desc[0] - data_dim = desc[1] + data_shape = desc[1] data_lod_level = desc[2] data_lod = [] for i in range(data_lod_level): @@ -59,9 +59,9 @@ class TestReorderLoDTensor(unittest.TestCase): size=self.num_seq if i == 0 else lod_level_i[-1]) lod_level_i = [0] + numpy.cumsum(lod_level_i).tolist() data_lod.append(lod_level_i) - data_value = numpy.random.random(size=[ - data_lod[-1][-1] if data_lod else self.num_seq, data_dim - ]).astype('float32') + data_value = numpy.random.random( + size=[data_lod[-1][-1] if data_lod else self.num_seq + ] + data_shape).astype('float32') self.data[data_name] = (data_value, data_lod) def set_inputs(self, place): @@ -163,8 +163,6 @@ class TestReorderLoDTensor(unittest.TestCase): numpy.allclose( numpy.array(actual_grad), expect_grad, atol=0.001)) self.assertEqual(expect_grad_lod, actual_grad.lod()) - global outputs_from_tensor_implicit_lod - outputs_from_tensor_implicit_lod = self.actual_outputs # compare outputs between LodTensors with explicit and implicit lod # use the same data but set the input lod explicitly