[API 2.0] adaptive expand op to use shape instead of expand_times (#26206)

* adaptive expand op to 2.0 (align to torch.expand) , test=develop
revert-24895-update_cub
lilong12 5 years ago committed by GitHub
parent cbf8ba1591
commit 241b44db14
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

File diff suppressed because it is too large Load Diff

@ -0,0 +1,32 @@
/* 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/fluid/operators/expand_v2_op.h"
namespace ops = paddle::operators;
namespace plat = paddle::platform;
REGISTER_OP_CUDA_KERNEL(
expand_v2, ops::ExpandV2Kernel<paddle::platform::CUDADeviceContext, float>,
ops::ExpandV2Kernel<paddle::platform::CUDADeviceContext, double>,
ops::ExpandV2Kernel<paddle::platform::CUDADeviceContext, plat::float16>,
ops::ExpandV2Kernel<paddle::platform::CUDADeviceContext, int>,
ops::ExpandV2Kernel<paddle::platform::CUDADeviceContext, int64_t>,
ops::ExpandV2Kernel<paddle::platform::CUDADeviceContext, bool>);
REGISTER_OP_CUDA_KERNEL(
expand_v2_grad,
ops::ExpandV2GradKernel<paddle::platform::CUDADeviceContext, float>,
ops::ExpandV2GradKernel<paddle::platform::CUDADeviceContext, double>,
ops::ExpandV2GradKernel<paddle::platform::CUDADeviceContext, plat::float16>,
ops::ExpandV2GradKernel<paddle::platform::CUDADeviceContext, int>,
ops::ExpandV2GradKernel<paddle::platform::CUDADeviceContext, int64_t>);

File diff suppressed because it is too large Load Diff

@ -0,0 +1,234 @@
# Copyright (c) 2018 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 __future__ import print_function
import unittest
import numpy as np
from op_test import OpTest
import paddle.fluid as fluid
from paddle.fluid import compiler, Program, program_guard
import paddle
# Situation 1: shape is a list(without tensor)
class TestExpandV2OpRank1(OpTest):
def setUp(self):
self.op_type = "expand_v2"
self.init_data()
self.inputs = {'X': np.random.random(self.ori_shape).astype("float64")}
self.attrs = {'shape': self.shape}
output = np.tile(self.inputs['X'], self.expand_times)
self.outputs = {'Out': output}
def init_data(self):
self.ori_shape = [100]
self.shape = [100]
self.expand_times = [1]
def test_check_output(self):
self.check_output()
def test_check_grad(self):
self.check_grad(['X'], 'Out')
class TestExpandV2OpRank2_DimExpanding(TestExpandV2OpRank1):
def init_data(self):
self.ori_shape = [120]
self.shape = [2, 120]
self.expand_times = [2, 1]
class TestExpandV2OpRank2(TestExpandV2OpRank1):
def init_data(self):
self.ori_shape = [1, 140]
self.shape = [12, 140]
self.expand_times = [12, 1]
class TestExpandV2OpRank3_Corner(TestExpandV2OpRank1):
def init_data(self):
self.ori_shape = (2, 10, 5)
self.shape = (2, 10, 5)
self.expand_times = (1, 1, 1)
class TestExpandV2OpRank4(TestExpandV2OpRank1):
def init_data(self):
self.ori_shape = (2, 4, 5, 7)
self.shape = (-1, -1, -1, -1)
self.expand_times = (1, 1, 1, 1)
# Situation 2: shape is a list(with tensor)
class TestExpandV2OpRank1_tensor_attr(OpTest):
def setUp(self):
self.op_type = "expand_v2"
self.init_data()
expand_shapes_tensor = []
for index, ele in enumerate(self.expand_shape):
expand_shapes_tensor.append(("x" + str(index), np.ones(
(1)).astype('int32') * ele))
self.inputs = {
'X': np.random.random(self.ori_shape).astype("float64"),
'expand_shapes_tensor': expand_shapes_tensor,
}
self.attrs = {"shape": self.infer_expand_shape}
output = np.tile(self.inputs['X'], self.expand_times)
self.outputs = {'Out': output}
def init_data(self):
self.ori_shape = [100]
self.expand_times = [1]
self.expand_shape = [100]
self.infer_expand_shape = [-1]
def test_check_output(self):
self.check_output()
def test_check_grad(self):
self.check_grad(['X'], 'Out')
class TestExpandV2OpRank2_Corner_tensor_attr(TestExpandV2OpRank1_tensor_attr):
def init_data(self):
self.ori_shape = [12, 14]
self.expand_times = [1, 1]
self.expand_shape = [12, 14]
self.infer_expand_shape = [12, -1]
# Situation 3: shape is a tensor
class TestExpandV2OpRank1_tensor(OpTest):
def setUp(self):
self.op_type = "expand_v2"
self.init_data()
self.inputs = {
'X': np.random.random(self.ori_shape).astype("float64"),
'Shape': np.array(self.expand_shape).astype("int32"),
}
self.attrs = {}
output = np.tile(self.inputs['X'], self.expand_times)
self.outputs = {'Out': output}
def init_data(self):
self.ori_shape = [100]
self.expand_times = [2, 1]
self.expand_shape = [2, 100]
def test_check_output(self):
self.check_output()
def test_check_grad(self):
self.check_grad(['X'], 'Out')
# Situation 4: input x is Integer
class TestExpandV2OpInteger(OpTest):
def setUp(self):
self.op_type = "expand_v2"
self.inputs = {
'X': np.random.randint(
10, size=(2, 4, 5)).astype("int32")
}
self.attrs = {'shape': [2, 4, 5]}
output = np.tile(self.inputs['X'], (1, 1, 1))
self.outputs = {'Out': output}
def test_check_output(self):
self.check_output()
# Situation 5: input x is Bool
class TestExpandV2OpBoolean(OpTest):
def setUp(self):
self.op_type = "expand_v2"
self.inputs = {'X': np.random.randint(2, size=(2, 4, 5)).astype("bool")}
self.attrs = {'shape': [2, 4, 5]}
output = np.tile(self.inputs['X'], (1, 1, 1))
self.outputs = {'Out': output}
def test_check_output(self):
self.check_output()
# Situation 56: input x is Integer
class TestExpandV2OpInt64_t(OpTest):
def setUp(self):
self.op_type = "expand_v2"
self.inputs = {
'X': np.random.randint(
10, size=(2, 4, 5)).astype("int64")
}
self.attrs = {'shape': [2, 4, 5]}
output = np.tile(self.inputs['X'], (1, 1, 1))
self.outputs = {'Out': output}
def test_check_output(self):
self.check_output()
class TestExpandV2Error(unittest.TestCase):
def test_errors(self):
with program_guard(Program(), Program()):
x1 = fluid.create_lod_tensor(
np.array([[-1]]), [[1]], fluid.CPUPlace())
shape = [2, 2]
self.assertRaises(TypeError, paddle.tensor.expand, x1, shape)
x2 = fluid.layers.data(name='x2', shape=[4], dtype="uint8")
self.assertRaises(TypeError, paddle.tensor.expand, x2, shape)
x3 = fluid.layers.data(name='x3', shape=[4], dtype="bool")
x3.stop_gradient = True
self.assertRaises(ValueError, paddle.tensor.expand, x3, shape)
# Test python API
class TestExpandV2API(unittest.TestCase):
def test_api(self):
input = np.random.random([12, 14]).astype("float32")
x = fluid.layers.data(
name='x', shape=[12, 14], append_batch_size=False, dtype="float32")
positive_2 = fluid.layers.fill_constant([1], "int32", 12)
expand_shape = fluid.layers.data(
name="expand_shape",
shape=[2],
append_batch_size=False,
dtype="int32")
out_1 = paddle.expand(x, shape=[12, 14])
out_2 = paddle.expand(x, shape=[positive_2, 14])
out_3 = paddle.expand(x, shape=expand_shape)
g0 = fluid.backward.calc_gradient(out_2, x)
exe = fluid.Executor(place=fluid.CPUPlace())
res_1, res_2, res_3 = exe.run(fluid.default_main_program(),
feed={
"x": input,
"expand_shape":
np.array([12, 14]).astype("int32")
},
fetch_list=[out_1, out_2, out_3])
assert np.array_equal(res_1, np.tile(input, (1, 1)))
assert np.array_equal(res_2, np.tile(input, (1, 1)))
assert np.array_equal(res_3, np.tile(input, (1, 1)))
if __name__ == "__main__":
unittest.main()

@ -60,8 +60,10 @@ class TestRetainGraph(unittest.TestCase):
interpolatesv = fake_data
elif type == 'mixed':
alpha = paddle.rand((real_data.shape[0], 1))
alpha = paddle.expand(
alpha, [1, np.prod(real_data.shape) // real_data.shape[0]])
alpha = paddle.expand(alpha, [
real_data.shape[0],
np.prod(real_data.shape) // real_data.shape[0]
])
alpha = paddle.reshape(alpha, real_data.shape)
interpolatesv = alpha * real_data + ((1 - alpha) * fake_data)
else:

@ -23,7 +23,6 @@ from ..fluid.layers import utils
import numpy as np
# TODO: define functions to manipulate a tensor
from ..fluid.layers import cast #DEFINE_ALIAS
from ..fluid.layers import expand #DEFINE_ALIAS
from ..fluid.layers import expand_as #DEFINE_ALIAS
from ..fluid.layers import reshape #DEFINE_ALIAS
from ..fluid.layers import scatter #DEFINE_ALIAS
@ -794,7 +793,6 @@ def tile(x, repeat_times, name=None):
"""
:alias_main: paddle.tile
:alias: paddle.tile,paddle.tensor.tile,paddle.tensor.manipulation.tile
Construct a new tensor by repeating ``x`` the number of times given by the parameter ``repeat_times``.
The rank of ``x`` should be less than or equal to 6, and the size of the shape of ``repeat_times`` should
be less than or equal to 6.
@ -807,52 +805,38 @@ def tile(x, repeat_times, name=None):
For example, if the tensor ``x`` is with a shape(4, 3, 2, 2) and ``repeat_times`` is a tuple (3, 2),
``repeat_times`` is first promoted to a tuple (1, 1, 3, 2).
The following gives an using case:
.. code-block:: text
Input(x) is a 3-D tensor of shape (2, 3, 1):
[
[[1], [2], [3]],
[[4], [5], [6]]
]
Attr(repeat_times): [1, 2, 2]
Output(out) is a 3-D tensor of shape (2, 6, 2):
[
[[1, 1], [2, 2], [3, 3], [1, 1], [2, 2], [3, 3]],
[[4, 4], [5, 5], [6, 6], [4, 4], [5, 5], [6, 6]]
]
Args:
x (Tensor): The input tensor, its data type should be bool, float32, float64, int32. The rank of ``x`` should be in [1, 6].
repeat_times (Tensor|tuple|list): The number of repeating times for each dimension of the input ``x``. If repeat_times is a list or tuple, the elements of
it should be integers or Tensors with shape [1]. If repeat_times is Tensor, it should be an 1-D Tensor. The size of its shape should be in [1, 6].
name (str, optional): Name for the operation (optional, default is None). For more information, please refer to :ref:`api_guide_Name` .
Returns:
N-D Tensor. The data type is the same as ``x``. After tiling, each dimension of the output is equal to the corresponding dimension of ``x`` multiplying the corresponding value given by ``repeat_times`` .
Raises:
TypeError: The type of ``repeat_times`` must be list, tuple or Tensor.
ValueError: The elements of ``repeat_times`` cannot be negative.
Examples:
.. code-block:: python
import paddle
import numpy as np
paddle.disable_static()
# example 1:
np_data_1 = np.array([1, 2, 3]).astype('int32')
data_1 = paddle..to_variable(np_data_1)
tiled_1 = paddle.tile(data_1, repeat_times=[2, 1])
# [[1, 2, 3], [1, 2, 3]]
# example 2:
np_repeat_times = np.array([2, 1]).astype("int32")
repeat_times = paddle.to_variable(np_repeat_times)
@ -907,3 +891,95 @@ def tile(x, repeat_times, name=None):
helper.append_op(
type='tile', inputs=inputs, outputs={'Out': out}, attrs=attrs)
return out
def expand(x, shape, name=None):
"""
:alias_main: paddle.expand
:alias: paddle.expand,paddle.tensor.expand,paddle.tensor.manipulation.expand
Expand the input tensor to a given shape.
The rank of ``x`` should be less than or equal to 6, and the number of elements in ``shape`` should also be less than or equal to 6. The size of the dimension to expand must be 1.
Args:
x (Tensor): The input Tensor with rank in [1, 6]. The data type is bool, float32, float64 or int32.
shape (list|tuple|Tensor): The result shape after expanding. The data type is int32. If shape is a list or tuple, all elements of
it should be integers or Tensors with shape (1,). If shape is a Tensor, it should be an 1-D Tensor.
The value -1 in shape, means keeping the corresponding dimension unchanged.
name (str, optional): The default value is None. Normally there is no need for user to set this property. For more information, please refer to :ref:`api_guide_Name` .
Returns:
Tensor: A Tensor with the given shape. The data type is the same as ``x``.
Raises:
TypeError: The type of ``shape`` must be list, tuple or Variable.
ValueError: The elements of ``shape`` must be positive or -1.
Examples:
.. code-block:: python
import numpy as np
import paddle
paddle.disable_static()
# example 1:
np_data_1 = np.array([1, 2, 3]).astype=('int32)
data_1 = paddle.to_variable(np_data_1)
expanded_1 = paddle.expand(data_1, shape=[2, 3])
# [[1, 2, 3], [1, 2, 3]]
# example 2:
np_shape = np.array([2, 3]).astype=('int32)
shape = paddle.to_variable(np_shape)
expanded_2 = paddle.expand(data_1, shape=shape)
# [[1, 2, 3], [1, 2, 3]]
"""
if in_dygraph_mode():
if isinstance(shape, (list, tuple)):
expand_shape = [
item.numpy()[0] if isinstance(item, Variable) else item
for item in shape
]
return core.ops.expand_v2(x, 'shape', expand_shape)
inputs = {"X": [x]}
attrs = {}
check_variable_and_dtype(
x, 'x', ['bool', 'float32', 'float64', 'int32', 'int64'], 'expand')
check_type(shape, 'shape', (list, tuple, Variable), 'expand')
if convert_dtype(x.dtype) == 'bool' and x.stop_gradient == True:
raise ValueError("When the data type of input 'x' for expand is bool, "
"you must set its stop_gradient to be False by "
" some_var.stop_gradient = False, supporting "
"some_var as the input.")
helper = LayerHelper('expand', input=x, **locals())
def get_attr_expand_shape(list_expand_shape):
attrs_expand_shape = []
for idx, shape in enumerate(list_expand_shape):
if isinstance(shape, Variable):
attrs_expand_shape.append(-1)
else:
attrs_expand_shape.append(shape)
assert shape > 0 or shape == -1, (
"Every element in shape must be positive or -1.")
return attrs_expand_shape
if isinstance(shape, Variable):
shape.stop_gradient = True
inputs['Shape'] = shape
elif isinstance(shape, (list, tuple)):
attrs['shape'] = get_attr_expand_shape(shape)
if utils._contain_var(shape):
inputs['expand_shapes_tensor'] = utils._convert_to_tensor_list(
shape)
dtype = helper.input_dtype(input_param_name='x')
out = helper.create_variable_for_type_inference(dtype)
helper.append_op(
type='expand_v2', inputs=inputs, outputs={'Out': out}, attrs=attrs)
return out

Loading…
Cancel
Save