未验证 提交 5041158f 编写于 作者: Y yunyaoXYY 提交者: GitHub

[Zero-Dim] Support 0D Tensor input for topk/broadcast_to/expand/expand_as/broadcast_shape (#50536)

上级 4a0855a5
...@@ -472,6 +472,7 @@ void CumInferMeta(const MetaTensor& x, ...@@ -472,6 +472,7 @@ void CumInferMeta(const MetaTensor& x,
out->set_dims(x_dims); out->set_dims(x_dims);
out->set_dtype(x.dtype()); out->set_dtype(x.dtype());
} }
out->share_lod(x); out->share_lod(x);
} }
...@@ -970,7 +971,7 @@ void ExpandInferMeta(const MetaTensor& x, ...@@ -970,7 +971,7 @@ void ExpandInferMeta(const MetaTensor& x,
MAX_RANK_SUPPORTED)); MAX_RANK_SUPPORTED));
PADDLE_ENFORCE_GE( PADDLE_ENFORCE_GE(
expand_shape.size(), expand_shape.size(),
1, 0,
phi::errors::InvalidArgument("The number of elements (%d) of 'shape' for " phi::errors::InvalidArgument("The number of elements (%d) of 'shape' for "
"must be a positive integer.", "must be a positive integer.",
expand_shape.size())); expand_shape.size()));
...@@ -1005,7 +1006,7 @@ void ExpandInferMeta(const MetaTensor& x, ...@@ -1005,7 +1006,7 @@ void ExpandInferMeta(const MetaTensor& x,
out->set_dims(make_ddim(out_shape)); out->set_dims(make_ddim(out_shape));
out->set_dtype(x.dtype()); out->set_dtype(x.dtype());
if (out_shape[0] == x_dims[0]) { if (out_rank > 0 && out_shape[0] == x_dims[0]) {
out->share_lod(x); out->share_lod(x);
} }
} }
...@@ -4097,14 +4098,23 @@ void TopKInferMeta(const MetaTensor& x, ...@@ -4097,14 +4098,23 @@ void TopKInferMeta(const MetaTensor& x,
MetaConfig config) { MetaConfig config) {
auto input_dims = x.dims(); auto input_dims = x.dims();
const int& dim_size = input_dims.size(); const int& dim_size = input_dims.size();
PADDLE_ENFORCE_EQ( if (dim_size != 0) {
(axis < dim_size) && (axis >= (-1 * dim_size)), PADDLE_ENFORCE_EQ(
true, (axis < dim_size) && (axis >= (-1 * dim_size)),
phi::errors::InvalidArgument( true,
"the axis of topk must be [-%d, %d), but you set axis is %d", phi::errors::InvalidArgument(
dim_size, "the axis of topk must be [-%d, %d), but you set axis is %d",
dim_size, dim_size,
axis)); dim_size,
axis));
} else {
PADDLE_ENFORCE_EQ(
(axis == dim_size) || (axis == -1),
true,
phi::errors::InvalidArgument("the axis of topk must be 0 or -1 when "
"x.dims() = 0, but you set axis is %d",
axis));
}
if (axis < 0) axis += dim_size; if (axis < 0) axis += dim_size;
...@@ -4122,12 +4132,13 @@ void TopKInferMeta(const MetaTensor& x, ...@@ -4122,12 +4132,13 @@ void TopKInferMeta(const MetaTensor& x,
PADDLE_ENFORCE_GE( PADDLE_ENFORCE_GE(
input_dims.size(), input_dims.size(),
1, 0,
phi::errors::InvalidArgument("input of topk must have >= 1d shape")); phi::errors::InvalidArgument("input of topk must have >= 0d shape"));
phi::DDim dims = input_dims; phi::DDim dims = input_dims;
if (input_dims.size() > 0) {
dims[axis] = k; dims[axis] = k;
}
out->set_dims(dims); out->set_dims(dims);
out->share_lod(x); out->share_lod(x);
out->set_dtype(x.dtype()); out->set_dtype(x.dtype());
......
...@@ -66,6 +66,11 @@ void TopkGradKernel(const Context& dev_ctx, ...@@ -66,6 +66,11 @@ void TopkGradKernel(const Context& dev_ctx,
axis = (axis < 0) ? (in_dims.size() + axis) : axis; axis = (axis < 0) ? (in_dims.size() + axis) : axis;
T* x_grad_data = dev_ctx.template Alloc<T>(x_grad); T* x_grad_data = dev_ctx.template Alloc<T>(x_grad);
if (in_dims.size() == 0) {
phi::Copy<Context>(dev_ctx, out_grad, dev_ctx.GetPlace(), false, x_grad);
return;
}
if (axis + 1 == in_dims.size()) { if (axis + 1 == in_dims.size()) {
// allocate the memory for the input_grad // allocate the memory for the input_grad
......
...@@ -140,7 +140,13 @@ void TopkKernel(const Context& dev_ctx, ...@@ -140,7 +140,13 @@ void TopkKernel(const Context& dev_ctx,
const auto* input = &x; const auto* input = &x;
// Get the top k elements of each row of input tensor // Get the top k elements of each row of input tensor
const auto& in_dims = input->dims(); const auto& in_dims = input->dims();
// 0d input x
if (in_dims.size() == 0) {
phi::Copy<Context>(dev_ctx, x, dev_ctx.GetPlace(), false, out);
dev_ctx.template Alloc<int64_t>(indices);
phi::funcs::set_constant(dev_ctx, indices, 0.0);
return;
}
// axis < 0, cacluate the real axis // axis < 0, cacluate the real axis
if (axis < 0) { if (axis < 0) {
axis += in_dims.size(); axis += in_dims.size();
......
...@@ -47,6 +47,11 @@ void TopkGradKernel(const Context& dev_ctx, ...@@ -47,6 +47,11 @@ void TopkGradKernel(const Context& dev_ctx,
const T* out_grad_data = out_grad.data<T>(); const T* out_grad_data = out_grad.data<T>();
const int64_t* indices_data = indices.data<int64_t>(); const int64_t* indices_data = indices.data<int64_t>();
if (in_dims.size() == 0) {
phi::Copy<Context>(dev_ctx, out_grad, dev_ctx.GetPlace(), false, x_grad);
return;
}
int pre, n, post; int pre, n, post;
phi::funcs::GetDims(in_dims, axis, &pre, &n, &post); phi::funcs::GetDims(in_dims, axis, &pre, &n, &post);
......
...@@ -61,6 +61,14 @@ void TopkKernel(const Context& dev_ctx, ...@@ -61,6 +61,14 @@ void TopkKernel(const Context& dev_ctx,
const auto* input = &x; const auto* input = &x;
// get the input dims // get the input dims
const auto& in_dims = input->dims(); const auto& in_dims = input->dims();
// 0d input tensor
if (in_dims.size() == 0) {
phi::Copy<Context>(dev_ctx, x, dev_ctx.GetPlace(), false, out);
dev_ctx.template Alloc<int64_t>(indices);
phi::funcs::set_constant(dev_ctx, indices, 0.0);
return;
}
// calcluate the real axis // calcluate the real axis
if (axis < 0) axis += in_dims.size(); if (axis < 0) axis += in_dims.size();
......
...@@ -49,6 +49,12 @@ void ExpandAsGradKernel(const Context& context, ...@@ -49,6 +49,12 @@ void ExpandAsGradKernel(const Context& context,
const std::vector<int>& target_shape, const std::vector<int>& target_shape,
DenseTensor* in_grad) { DenseTensor* in_grad) {
auto x_dims = x.dims(); auto x_dims = x.dims();
if (in_grad->dims() == out_grad.dims()) {
phi::Copy(context, out_grad, context.GetPlace(), false, in_grad);
return;
}
auto vec_in_dims = phi::vectorize<int>(x_dims); auto vec_in_dims = phi::vectorize<int>(x_dims);
auto diff = target_shape.size() - vec_in_dims.size(); auto diff = target_shape.size() - vec_in_dims.size();
vec_in_dims.insert(vec_in_dims.begin(), diff, 1); vec_in_dims.insert(vec_in_dims.begin(), diff, 1);
...@@ -65,64 +71,56 @@ void ExpandAsGradKernel(const Context& context, ...@@ -65,64 +71,56 @@ void ExpandAsGradKernel(const Context& context,
} }
int dims = reduce_dims_vec.size(); int dims = reduce_dims_vec.size();
bool just_copy = true;
for (size_t i = 0; i < repeat_times.size(); i++) { PADDLE_ENFORCE_GE(
if (repeat_times[i] != 1) { dims,
just_copy = false; 0,
errors::InvalidArgument("The rank of the input 'Out@GRAD' for "
"expand_as_v2_grad op must be greater than or "
"equal to 0, but the value received is %d.",
dims));
PADDLE_ENFORCE_LE(
dims,
MAX_RANK_SUPPORTED,
errors::InvalidArgument("The rank of the input 'Out@GRAD' for "
"expand_as_v2_grad op must be less than or equal "
"to %d, but the value received is %d.",
MAX_RANK_SUPPORTED,
dims));
switch (dims) {
case 0:
ExpandAsBackward<Context, T, 0>(
context, out_grad, reshape_dims_vec, reduce_dims_vec, in_grad);
break; break;
} case 1:
} ExpandAsBackward<Context, T, 1>(
// no need reduce, just copy context, out_grad, reshape_dims_vec, reduce_dims_vec, in_grad);
if (just_copy) { break;
context.template Alloc<T>(in_grad); case 2:
phi::Copy(context, out_grad, context.GetPlace(), false, in_grad); ExpandAsBackward<Context, T, 2>(
} else { context, out_grad, reshape_dims_vec, reduce_dims_vec, in_grad);
PADDLE_ENFORCE_GE( break;
dims, case 3:
1, ExpandAsBackward<Context, T, 3>(
errors::InvalidArgument("The rank of the input 'Out@GRAD' for " context, out_grad, reshape_dims_vec, reduce_dims_vec, in_grad);
"expand_as_v2_grad op must be greater than or " break;
"equal to 1, but the value received is %d.", case 4:
dims)); ExpandAsBackward<Context, T, 4>(
PADDLE_ENFORCE_LE(dims, context, out_grad, reshape_dims_vec, reduce_dims_vec, in_grad);
MAX_RANK_SUPPORTED, break;
errors::InvalidArgument( case 5:
"The rank of the input 'Out@GRAD' for " ExpandAsBackward<Context, T, 5>(
"expand_as_v2_grad op must be less than or equal " context, out_grad, reshape_dims_vec, reduce_dims_vec, in_grad);
"to %d, but the value received is %d.", break;
MAX_RANK_SUPPORTED, case 6:
dims)); ExpandAsBackward<Context, T, 6>(
switch (dims) { context, out_grad, reshape_dims_vec, reduce_dims_vec, in_grad);
case 1: break;
ExpandAsBackward<Context, T, 1>( default:
context, out_grad, reshape_dims_vec, reduce_dims_vec, in_grad); PADDLE_THROW(errors::InvalidArgument(
break; "Only support tensor with rank being between 1 and 6. But "
case 2: "received tensor's rank = %d.",
ExpandAsBackward<Context, T, 2>( dims));
context, out_grad, reshape_dims_vec, reduce_dims_vec, in_grad);
break;
case 3:
ExpandAsBackward<Context, T, 3>(
context, out_grad, reshape_dims_vec, reduce_dims_vec, in_grad);
break;
case 4:
ExpandAsBackward<Context, T, 4>(
context, out_grad, reshape_dims_vec, reduce_dims_vec, in_grad);
break;
case 5:
ExpandAsBackward<Context, T, 5>(
context, out_grad, reshape_dims_vec, reduce_dims_vec, in_grad);
break;
case 6:
ExpandAsBackward<Context, T, 6>(
context, out_grad, reshape_dims_vec, reduce_dims_vec, in_grad);
break;
default:
PADDLE_THROW(errors::InvalidArgument(
"Only support tensor with rank being between 1 and 6. But "
"received tensor's rank = %d.",
dims));
}
} }
} }
......
...@@ -34,6 +34,10 @@ void ExpandAs(const Context& context, ...@@ -34,6 +34,10 @@ void ExpandAs(const Context& context,
auto diff = target_shape.size() - vec_in_dims.size(); auto diff = target_shape.size() - vec_in_dims.size();
vec_in_dims.insert(vec_in_dims.begin(), diff, 1); vec_in_dims.insert(vec_in_dims.begin(), diff, 1);
std::vector<int> repeat_times(vec_in_dims.size()); std::vector<int> repeat_times(vec_in_dims.size());
if (Rank == 0) {
phi::Copy<Context>(context, x, context.GetPlace(), false, out);
return;
}
for (size_t i = 0; i < vec_in_dims.size(); ++i) { for (size_t i = 0; i < vec_in_dims.size(); ++i) {
PADDLE_ENFORCE_NE( PADDLE_ENFORCE_NE(
target_shape[i], target_shape[i],
...@@ -108,7 +112,7 @@ void ExpandAsKernel(const Context& ctx, ...@@ -108,7 +112,7 @@ void ExpandAsKernel(const Context& ctx,
rank)); rank));
PADDLE_ENFORCE_GE( PADDLE_ENFORCE_GE(
rank, rank,
1, 0,
errors::InvalidArgument("The rank (%d) of the input 'x' for " errors::InvalidArgument("The rank (%d) of the input 'x' for "
"expand_as_v2 op must be positive.", "expand_as_v2 op must be positive.",
rank)); rank));
...@@ -133,6 +137,9 @@ void ExpandAsKernel(const Context& ctx, ...@@ -133,6 +137,9 @@ void ExpandAsKernel(const Context& ctx,
} }
switch (target_rank) { switch (target_rank) {
case 0:
ExpandAs<Context, T, 0>(ctx, x, real_target_shape, out);
break;
case 1: case 1:
ExpandAs<Context, T, 1>(ctx, x, real_target_shape, out); ExpandAs<Context, T, 1>(ctx, x, real_target_shape, out);
break; break;
......
...@@ -54,6 +54,11 @@ void ExpandGradKernel(const Context& ctx, ...@@ -54,6 +54,11 @@ void ExpandGradKernel(const Context& ctx,
DenseTensor* in_grad) { DenseTensor* in_grad) {
auto expand_shape = shape.GetData(); auto expand_shape = shape.GetData();
auto x_dims = x.dims(); auto x_dims = x.dims();
if (in_grad->dims() == out_grad.dims()) {
phi::Copy(ctx, out_grad, ctx.GetPlace(), false, in_grad);
return;
}
auto vec_in_dims = phi::vectorize<int>(x_dims); auto vec_in_dims = phi::vectorize<int>(x_dims);
auto diff = expand_shape.size() - vec_in_dims.size(); auto diff = expand_shape.size() - vec_in_dims.size();
vec_in_dims.insert(vec_in_dims.begin(), diff, 1); vec_in_dims.insert(vec_in_dims.begin(), diff, 1);
...@@ -79,63 +84,55 @@ void ExpandGradKernel(const Context& ctx, ...@@ -79,63 +84,55 @@ void ExpandGradKernel(const Context& ctx,
int dims = reduce_dims_vec.size(); int dims = reduce_dims_vec.size();
bool just_copy = true; PADDLE_ENFORCE_GE(
for (size_t i = 0; i < repeat_times.size(); i++) { dims,
if (repeat_times[i] != 1) { 0,
just_copy = false; phi::errors::InvalidArgument("The rank of the input 'Out@GRAD' for "
"expand_v2_grad op must be greater than or "
"equal to 0, but the value received is %d.",
dims));
PADDLE_ENFORCE_LE(dims,
MAX_RANK_SUPPORTED,
phi::errors::InvalidArgument(
"The rank of the input 'Out@GRAD' for "
"expand_v2_grad op must be less than or equal "
"to %d, but the value received is %d.",
MAX_RANK_SUPPORTED,
dims));
switch (dims) {
case 0:
ExpandBackward<Context, T, 1>(
ctx, out_grad, reshape_dims_vec, reduce_dims_vec, in_grad);
break; break;
} case 1:
} ExpandBackward<Context, T, 1>(
// no need reduce, just copy ctx, out_grad, reshape_dims_vec, reduce_dims_vec, in_grad);
if (just_copy) { break;
phi::Copy(ctx, out_grad, ctx.GetPlace(), false, in_grad); case 2:
} else { ExpandBackward<Context, T, 2>(
PADDLE_ENFORCE_GE(dims, ctx, out_grad, reshape_dims_vec, reduce_dims_vec, in_grad);
1, break;
phi::errors::InvalidArgument( case 3:
"The rank of the input 'Out@GRAD' for " ExpandBackward<Context, T, 3>(
"expand_v2_grad op must be greater than or " ctx, out_grad, reshape_dims_vec, reduce_dims_vec, in_grad);
"equal to 1, but the value received is %d.", break;
dims)); case 4:
PADDLE_ENFORCE_LE(dims, ExpandBackward<Context, T, 4>(
MAX_RANK_SUPPORTED, ctx, out_grad, reshape_dims_vec, reduce_dims_vec, in_grad);
phi::errors::InvalidArgument( break;
"The rank of the input 'Out@GRAD' for " case 5:
"expand_v2_grad op must be less than or equal " ExpandBackward<Context, T, 5>(
"to %d, but the value received is %d.", ctx, out_grad, reshape_dims_vec, reduce_dims_vec, in_grad);
MAX_RANK_SUPPORTED, break;
dims)); case 6:
switch (dims) { ExpandBackward<Context, T, 6>(
case 1: ctx, out_grad, reshape_dims_vec, reduce_dims_vec, in_grad);
ExpandBackward<Context, T, 1>( break;
ctx, out_grad, reshape_dims_vec, reduce_dims_vec, in_grad); default:
break; PADDLE_THROW(phi::errors::InvalidArgument(
case 2: "Only support tensor with rank being between 1 and 6. But "
ExpandBackward<Context, T, 2>( "received tensor's rank = %d.",
ctx, out_grad, reshape_dims_vec, reduce_dims_vec, in_grad); dims));
break;
case 3:
ExpandBackward<Context, T, 3>(
ctx, out_grad, reshape_dims_vec, reduce_dims_vec, in_grad);
break;
case 4:
ExpandBackward<Context, T, 4>(
ctx, out_grad, reshape_dims_vec, reduce_dims_vec, in_grad);
break;
case 5:
ExpandBackward<Context, T, 5>(
ctx, out_grad, reshape_dims_vec, reduce_dims_vec, in_grad);
break;
case 6:
ExpandBackward<Context, T, 6>(
ctx, out_grad, reshape_dims_vec, reduce_dims_vec, in_grad);
break;
default:
PADDLE_THROW(phi::errors::InvalidArgument(
"Only support tensor with rank being between 1 and 6. But "
"received tensor's rank = %d.",
dims));
}
} }
} }
......
...@@ -35,6 +35,10 @@ void Expand(const Context& ctx, ...@@ -35,6 +35,10 @@ void Expand(const Context& ctx,
auto diff = expand_shape.size() - vec_in_dims.size(); auto diff = expand_shape.size() - vec_in_dims.size();
vec_in_dims.insert(vec_in_dims.begin(), diff, 1); vec_in_dims.insert(vec_in_dims.begin(), diff, 1);
std::vector<int> repeat_times(vec_in_dims.size()); std::vector<int> repeat_times(vec_in_dims.size());
if (Rank == 0) {
phi::Copy<Context>(ctx, x, ctx.GetPlace(), false, out);
return;
}
for (size_t i = 0; i < vec_in_dims.size(); ++i) { for (size_t i = 0; i < vec_in_dims.size(); ++i) {
PADDLE_ENFORCE_NE( PADDLE_ENFORCE_NE(
expand_shape[i], expand_shape[i],
...@@ -74,7 +78,6 @@ void Expand(const Context& ctx, ...@@ -74,7 +78,6 @@ void Expand(const Context& ctx,
repeat_times[i] = 1; repeat_times[i] = 1;
} }
} }
Eigen::DSizes<Eigen::DenseIndex, Rank> bcast_dims; Eigen::DSizes<Eigen::DenseIndex, Rank> bcast_dims;
for (size_t i = 0; i < repeat_times.size(); ++i) { for (size_t i = 0; i < repeat_times.size(); ++i) {
bcast_dims[i] = repeat_times[i]; bcast_dims[i] = repeat_times[i];
...@@ -112,7 +115,7 @@ void ExpandKernel(const Context& ctx, ...@@ -112,7 +115,7 @@ void ExpandKernel(const Context& ctx,
auto rank = x.dims().size(); auto rank = x.dims().size();
PADDLE_ENFORCE_GE( PADDLE_ENFORCE_GE(
rank, rank,
1, 0,
phi::errors::InvalidArgument( phi::errors::InvalidArgument(
"The rank of the input 'X' for expand_v2 op must be positive, " "The rank of the input 'X' for expand_v2 op must be positive, "
"but the value received is %d.", "but the value received is %d.",
...@@ -145,6 +148,9 @@ void ExpandKernel(const Context& ctx, ...@@ -145,6 +148,9 @@ void ExpandKernel(const Context& ctx,
MAX_RANK_SUPPORTED)); MAX_RANK_SUPPORTED));
rank = std::max(rank, static_cast<int>(shape_size)); rank = std::max(rank, static_cast<int>(shape_size));
switch (rank) { switch (rank) {
case 0:
Expand<Context, T, 0>(ctx, x, shape, out);
break;
case 1: case 1:
Expand<Context, T, 1>(ctx, x, shape, out); Expand<Context, T, 1>(ctx, x, shape, out);
break; break;
......
...@@ -559,6 +559,210 @@ class TestSundryAPI(unittest.TestCase): ...@@ -559,6 +559,210 @@ class TestSundryAPI(unittest.TestCase):
paddle.disable_static() paddle.disable_static()
self.x = paddle.rand([]) self.x = paddle.rand([])
def test_expand(self):
# case1
x = paddle.full([], 1, 'float32')
x.stop_gradient = False
out = paddle.expand(x, shape=[1])
out.retain_grads()
out.backward()
self.assertEqual(out.shape, [1])
np.testing.assert_allclose(out, 1.0)
self.assertEqual(x.grad.shape, [])
np.testing.assert_allclose(x.grad, 1.0)
self.assertEqual(out.grad.shape, [1])
np.testing.assert_allclose(out.grad, 1.0)
# case2
x1 = paddle.full([], 1, 'float32')
x1.stop_gradient = False
out1 = paddle.expand(x1, shape=[])
out1.retain_grads()
out1.backward()
self.assertEqual(out1.shape, [])
np.testing.assert_allclose(out1, 1.0)
self.assertEqual(x1.grad.shape, [])
np.testing.assert_allclose(x1.grad, 1.0)
self.assertEqual(out1.grad.shape, [])
np.testing.assert_allclose(out1.grad, 1.0)
# case3
x2 = paddle.full([], 1, 'float32')
x2.stop_gradient = False
out2 = paddle.expand(x2, shape=[1, 1])
out2.retain_grads()
out2.backward()
self.assertEqual(out2.shape, [1, 1])
np.testing.assert_allclose(out2, 1.0)
self.assertEqual(x2.grad.shape, [])
np.testing.assert_allclose(x2.grad, 1.0)
self.assertEqual(out2.grad.shape, [1, 1])
np.testing.assert_allclose(out2.grad, 1.0)
# case4
x3 = paddle.full([], 1, 'float32')
x3.stop_gradient = False
out3 = paddle.expand(x3, shape=[3, 3])
out3.retain_grads()
out3.backward()
self.assertEqual(out3.shape, [3, 3])
np.testing.assert_allclose(out3, 1.0)
self.assertEqual(x3.grad.shape, [])
np.testing.assert_allclose(x3.grad, 9.0)
self.assertEqual(out3.grad.shape, [3, 3])
np.testing.assert_allclose(out3.grad, 1.0)
def test_expand_as(self):
x = paddle.full([], 1, 'float32')
x.stop_gradient = False
y = paddle.full([], 1, 'float32')
y.stop_gradient = False
out = paddle.expand_as(x, y)
out.backward()
self.assertEqual(x.shape, [])
self.assertEqual(x.item(), 1.0)
self.assertEqual(x.grad.shape, [])
self.assertEqual(x.grad.item(), 1.0)
self.assertEqual(out.shape, [])
self.assertEqual(out.item(), 1.0)
self.assertEqual(out.grad, None)
x1 = paddle.full([], 1, 'float32')
x1.stop_gradient = False
y1 = paddle.full([1], 1, 'float32')
out1 = paddle.expand_as(x1, y1)
out1.backward()
self.assertEqual(x1.shape, [])
self.assertEqual(x1.item(), 1.0)
self.assertEqual(x1.grad.shape, [])
self.assertEqual(x1.grad.item(0), 1.0)
self.assertEqual(out1.shape, [1])
self.assertEqual(out1.item(0), 1.0)
self.assertEqual(out1.grad, None)
x2 = paddle.full([], 1, 'float32')
x2.stop_gradient = False
y2 = paddle.full([3, 3], 1, 'float32')
out2 = paddle.expand_as(x2, y2)
out2.backward()
self.assertEqual(x2.shape, [])
self.assertEqual(x2.item(), 1.0)
self.assertEqual(x2.grad.shape, [])
self.assertEqual(x2.grad.item(0), 9.0)
self.assertEqual(out2.shape, [3, 3])
self.assertEqual(out2.item(0), 1.0)
self.assertEqual(out2.grad, None)
def test_top_k(self):
x = paddle.full([], 1, 'float32')
x.stop_gradient = False
out, indices = paddle.topk(x, k=1, axis=0)
out.retain_grads()
out.backward()
self.assertEqual(indices.shape, [])
self.assertEqual(indices.item(), 0)
self.assertEqual(x.shape, [])
self.assertEqual(x.item(), 1.0)
self.assertEqual(x.grad.shape, [])
self.assertEqual(x.grad.item(0), 1.0)
self.assertEqual(out.shape, [])
self.assertEqual(out.item(), 1.0)
self.assertEqual(out.grad, 1.0)
x1 = paddle.full([], 1, 'float32')
x1.stop_gradient = False
out1, indices1 = paddle.topk(x1, k=1, axis=-1)
out1.retain_grads()
out1.backward()
self.assertEqual(indices1.shape, [])
self.assertEqual(indices1.item(), 0)
self.assertEqual(x1.shape, [])
self.assertEqual(x1.item(), 1.0)
self.assertEqual(x.grad.shape, [])
self.assertEqual(x.grad.item(0), 1.0)
self.assertEqual(out1.shape, [])
self.assertEqual(out1.item(), 1.0)
self.assertEqual(out1.grad, 1.0)
with self.assertRaises(ValueError):
tmp = paddle.topk(x1, k=1, axis=2)
def test_broadcast_to(self):
x = paddle.full([], 1, 'float32')
x.stop_gradient = False
out = paddle.broadcast_to(x, shape=[1])
out.retain_grads()
out.backward()
self.assertEqual(out.shape, [1])
np.testing.assert_allclose(out, 1.0)
self.assertEqual(x.grad.shape, [])
np.testing.assert_allclose(x.grad, 1.0)
self.assertEqual(out.grad.shape, [1])
np.testing.assert_allclose(out.grad, 1.0)
# case2
x1 = paddle.full([], 1, 'float32')
x1.stop_gradient = False
out1 = paddle.broadcast_to(x1, shape=[])
out1.retain_grads()
out1.backward()
self.assertEqual(out1.shape, [])
np.testing.assert_allclose(out1, 1.0)
self.assertEqual(x1.grad.shape, [])
np.testing.assert_allclose(x1.grad, 1.0)
self.assertEqual(out1.grad.shape, [])
np.testing.assert_allclose(out1.grad, 1.0)
# case3
x2 = paddle.full([], 1, 'float32')
x2.stop_gradient = False
out2 = paddle.broadcast_to(x2, shape=[1, 1])
out2.retain_grads()
out2.backward()
self.assertEqual(out2.shape, [1, 1])
np.testing.assert_allclose(out2, 1.0)
self.assertEqual(x2.grad.shape, [])
np.testing.assert_allclose(x2.grad, 1.0)
self.assertEqual(out2.grad.shape, [1, 1])
np.testing.assert_allclose(out2.grad, 1.0)
# case4
x3 = paddle.full([], 1, 'float32')
x3.stop_gradient = False
out3 = paddle.broadcast_to(x3, shape=[3, 3])
out3.retain_grads()
out3.backward()
self.assertEqual(out3.shape, [3, 3])
np.testing.assert_allclose(out3, 1.0)
self.assertEqual(x3.grad.shape, [])
np.testing.assert_allclose(x3.grad, 9.0)
self.assertEqual(out3.grad.shape, [3, 3])
np.testing.assert_allclose(out3.grad, 1.0)
def test_broadcast_shape(self):
x = []
y = [3, 5]
out = paddle.broadcast_shape(x, y)
self.assertEqual(out, [3, 5])
x = [3, 5]
y = []
out = paddle.broadcast_shape(x, y)
self.assertEqual(out, [3, 5])
x = []
y = []
out = paddle.broadcast_shape(x, y)
self.assertEqual(out, [])
def test_argmin(self): def test_argmin(self):
x = paddle.rand([]) x = paddle.rand([])
out1 = paddle.argmin(x, 0) out1 = paddle.argmin(x, 0)
...@@ -1673,6 +1877,200 @@ class TestSundryAPIStatic(unittest.TestCase): ...@@ -1673,6 +1877,200 @@ class TestSundryAPIStatic(unittest.TestCase):
self.exe = paddle.static.Executor() self.exe = paddle.static.Executor()
@prog_scope() @prog_scope()
def test_expand(self):
x = paddle.full([], 1, 'float32')
x.stop_gradient = False
out = paddle.expand(x, shape=[1])
paddle.static.append_backward(out.sum())
prog = paddle.static.default_main_program()
res = self.exe.run(
prog, fetch_list=[x, out, x.grad_name, out.grad_name]
)
self.assertEqual(res[0].shape, ())
self.assertEqual(res[0], 1.0)
self.assertEqual(res[1].shape, (1,))
self.assertEqual(res[1], 1.0)
self.assertEqual(res[2].shape, ())
self.assertEqual(res[2], 1.0)
self.assertEqual(res[3].shape, (1,))
self.assertEqual(res[3], 1.0)
x1 = paddle.full([], 1, 'float32')
x1.stop_gradient = False
out1 = paddle.expand(x1, shape=[])
paddle.static.append_backward(out1.sum())
prog = paddle.static.default_main_program()
res = self.exe.run(
prog, fetch_list=[x1, out1, x1.grad_name, out1.grad_name]
)
self.assertEqual(res[0].shape, ())
self.assertEqual(res[0], 1.0)
self.assertEqual(res[1].shape, ())
self.assertEqual(res[1], 1.0)
self.assertEqual(res[2].shape, ())
self.assertEqual(res[2], 1.0)
self.assertEqual(res[3].shape, ())
self.assertEqual(res[3], 1.0)
x2 = paddle.full([], 1, 'float32')
x2.stop_gradient = False
out2 = paddle.expand(x2, shape=[3, 3])
paddle.static.append_backward(out2.sum())
prog = paddle.static.default_main_program()
res = self.exe.run(
prog, fetch_list=[x2, out2, x2.grad_name, out2.grad_name]
)
self.assertEqual(res[0].shape, ())
self.assertEqual(res[0], 1.0)
self.assertEqual(res[1].shape, (3, 3))
self.assertEqual(res[1].any(), 1.0)
self.assertEqual(res[2].shape, ())
self.assertEqual(res[2], 9)
self.assertEqual(res[3].shape, (3, 3))
self.assertEqual(res[3].any(), 1.0)
@prog_scope()
def test_expand_as(self):
x = paddle.full([], 1, 'float32')
x.stop_gradient = False
y = paddle.full([], 1, 'float32')
y.stop_gradient = False
out = paddle.expand_as(x, y)
paddle.static.append_backward(out.sum())
prog = paddle.static.default_main_program()
res = self.exe.run(
prog, fetch_list=[x, out, x.grad_name, out.grad_name]
)
self.assertEqual(res[0].shape, ())
self.assertEqual(res[0], 1.0)
self.assertEqual(res[1].shape, ())
self.assertEqual(res[1], 1.0)
self.assertEqual(res[2].shape, ())
self.assertEqual(res[2], 1.0)
self.assertEqual(res[3].shape, ())
self.assertEqual(res[3], 1.0)
x1 = paddle.full([], 1, 'float32')
x1.stop_gradient = False
y1 = paddle.full([1], 1, 'float32')
y1.stop_gradient = False
out1 = paddle.expand_as(x1, y1)
paddle.static.append_backward(out1.sum())
prog = paddle.static.default_main_program()
res = self.exe.run(
prog, fetch_list=[x1, out1, x1.grad_name, out1.grad_name]
)
self.assertEqual(res[0].shape, ())
self.assertEqual(res[0], 1.0)
self.assertEqual(res[1].shape, (1,))
self.assertEqual(res[1], 1.0)
self.assertEqual(res[2].shape, ())
self.assertEqual(res[2], 1.0)
self.assertEqual(res[3].shape, (1,))
self.assertEqual(res[3], 1.0)
x2 = paddle.full([], 1, 'float32')
x2.stop_gradient = False
y2 = paddle.full([3, 3], 1, 'float32')
y2.stop_gradient = False
out2 = paddle.expand_as(x2, y2)
paddle.static.append_backward(out2.sum())
prog = paddle.static.default_main_program()
res = self.exe.run(
prog, fetch_list=[x2, out2, x2.grad_name, out2.grad_name]
)
self.assertEqual(res[0].shape, ())
self.assertEqual(res[0], 1.0)
self.assertEqual(res[1].shape, (3, 3))
self.assertEqual(res[1].any(), 1.0)
self.assertEqual(res[2].shape, ())
self.assertEqual(res[2], 9)
self.assertEqual(res[3].shape, (3, 3))
self.assertEqual(res[3].any(), 1.0)
@prog_scope()
def test_top_k(self):
x = paddle.full([], 1, 'float32')
x.stop_gradient = False
out, indices = paddle.topk(x, k=1, axis=0)
paddle.static.append_backward(out.sum())
prog = paddle.static.default_main_program()
res = self.exe.run(
prog, fetch_list=[x, out, indices, x.grad_name, out.grad_name]
)
self.assertEqual(res[0].shape, ())
self.assertEqual(res[0], 1.0)
self.assertEqual(res[1].shape, ())
self.assertEqual(res[1], 1.0)
self.assertEqual(res[2].shape, ())
self.assertEqual(res[2], 0.0)
self.assertEqual(res[3].shape, ())
self.assertEqual(res[3], 1.0)
self.assertEqual(res[4].shape, ())
self.assertEqual(res[4], 1.0)
x1 = paddle.full([], 1, 'float32')
x1.stop_gradient = False
out1, indices1 = paddle.topk(x1, k=1, axis=-1)
paddle.static.append_backward(out1.sum())
prog = paddle.static.default_main_program()
res = self.exe.run(
prog, fetch_list=[x1, out1, indices1, x1.grad_name, out1.grad_name]
)
self.assertEqual(res[0].shape, ())
self.assertEqual(res[0], 1.0)
self.assertEqual(res[1].shape, ())
self.assertEqual(res[1], 1.0)
self.assertEqual(res[2].shape, ())
self.assertEqual(res[2], 0.0)
self.assertEqual(res[3].shape, ())
self.assertEqual(res[3], 1.0)
self.assertEqual(res[4].shape, ())
self.assertEqual(res[4], 1.0)
with self.assertRaises(ValueError):
tmp = paddle.topk(x1, k=1, axis=2)
@prog_scope()
def test_broadcast_to(self):
x = paddle.full([], 1, 'float32')
x.stop_gradient = False
out = paddle.broadcast_to(x, shape=[1])
paddle.static.append_backward(out.sum())
prog = paddle.static.default_main_program()
res = self.exe.run(
prog, fetch_list=[x, out, x.grad_name, out.grad_name]
)
self.assertEqual(res[0].shape, ())
self.assertEqual(res[0], 1.0)
self.assertEqual(res[1].shape, (1,))
self.assertEqual(res[1], 1.0)
self.assertEqual(res[2].shape, ())
self.assertEqual(res[2], 1.0)
self.assertEqual(res[3].shape, (1,))
self.assertEqual(res[3], 1.0)
x1 = paddle.full([], 1, 'float32')
x1.stop_gradient = False
out1 = paddle.broadcast_to(x1, shape=[])
paddle.static.append_backward(out1.sum())
prog = paddle.static.default_main_program()
res = self.exe.run(
prog, fetch_list=[x1, out1, x1.grad_name, out1.grad_name]
)
self.assertEqual(res[0].shape, ())
self.assertEqual(res[0], 1.0)
self.assertEqual(res[1].shape, ())
self.assertEqual(res[1], 1.0)
self.assertEqual(res[2].shape, ())
self.assertEqual(res[2], 1.0)
self.assertEqual(res[3].shape, ())
self.assertEqual(res[3], 1.0)
def test_argmin(self): def test_argmin(self):
x = paddle.rand([]) x = paddle.rand([])
out1 = paddle.argmin(x, 0) out1 = paddle.argmin(x, 0)
......
...@@ -3192,7 +3192,7 @@ def expand_as(x, y, name=None): ...@@ -3192,7 +3192,7 @@ def expand_as(x, y, name=None):
Expand the input tensor ``x`` to the same shape as the input tensor ``y``. Expand the input tensor ``x`` to the same shape as the input tensor ``y``.
Both the number of dimensions of ``x`` and ``y`` must be less than or equal to 6, and the number of dimensions of ``y`` must be greather than or equal to that of ``x``. The dimension to expand must have a value of 1. Both the number of dimensions of ``x`` and ``y`` must be less than or equal to 6, and the number of dimensions of ``y`` must be greather than or equal to that of ``x``. The dimension to expand must have a value of 0.
Args: Args:
x (Tensor): The input tensor, its data type is bool, float32, float64, int32 or int64. x (Tensor): The input tensor, its data type is bool, float32, float64, int32 or int64.
...@@ -3252,13 +3252,13 @@ def broadcast_to(x, shape, name=None): ...@@ -3252,13 +3252,13 @@ def broadcast_to(x, shape, name=None):
Broadcast the input tensor to a given shape. Broadcast the input tensor to a given shape.
Both the number of dimensions of ``x`` and the number of elements in ``shape`` should be less than or equal to 6. The dimension to broadcast to must have a value 1. Both the number of dimensions of ``x`` and the number of elements in ``shape`` should be less than or equal to 6. The dimension to broadcast to must have a value 0.
Args: Args:
x (Tensor): The input tensor, its data type is bool, float32, float64, int32 or int64. x (Tensor): The input tensor, its data type is bool, float32, float64, int32 or int64.
shape (list|tuple|Tensor): The result shape after broadcasting. The data type is int32. If shape is a list or tuple, all its elements shape (list|tuple|Tensor): The result shape after broadcasting. The data type is int32. If shape is a list or tuple, all its elements
should be integers or 1-D Tensors with the data type int32. If shape is a Tensor, it should be an 1-D Tensor with the data type int32. should be integers or 0-D or 1-D Tensors with the data type int32. If shape is a Tensor, it should be an 1-D Tensor with the data type int32.
The value -1 in shape means keeping the corresponding dimension unchanged. The value -1 in shape means keeping the corresponding dimension unchanged.
name (str, optional): Name for the operation (optional, default is None). For more information, please refer to :ref:`api_guide_Name`. name (str, optional): Name for the operation (optional, default is None). For more information, please refer to :ref:`api_guide_Name`.
Returns: Returns:
...@@ -3280,13 +3280,13 @@ def broadcast_to(x, shape, name=None): ...@@ -3280,13 +3280,13 @@ def broadcast_to(x, shape, name=None):
if isinstance(shape, Variable): if isinstance(shape, Variable):
assert len(shape.shape) == 1, 'shape must be an 1-D Tensor.' assert len(shape.shape) == 1, 'shape must be an 1-D Tensor.'
else: else:
type_tuple = (int, np.int32, np.int64)
for elem in shape: for elem in shape:
if isinstance(elem, Variable): if isinstance(elem, Variable):
assert ( assert (
len(elem.shape) == 1 len(elem.shape) == 1
), 'Elements in shape must be 1-D Tensors or integers.' ), 'Elements in shape must be 1-D Tensors or integers.'
else: else:
type_tuple = (int, np.int32, np.int64)
assert isinstance( assert isinstance(
elem, type_tuple elem, type_tuple
), 'Elements in shape must be 1-D Tensors or integers.' ), 'Elements in shape must be 1-D Tensors or integers.'
...@@ -3346,12 +3346,12 @@ def expand(x, shape, name=None): ...@@ -3346,12 +3346,12 @@ def expand(x, shape, name=None):
Expand the input tensor to a given shape. Expand the input tensor to a given shape.
Both the number of dimensions of ``x`` and the number of elements in ``shape`` should be less than or equal to 6. And the number of dimensions of ``x`` should be less than the number of elements in ``shape``. The dimension to expand must have a value 1. Both the number of dimensions of ``x`` and the number of elements in ``shape`` should be less than or equal to 6. And the number of dimensions of ``x`` should be less than the number of elements in ``shape``. The dimension to expand must have a value 0.
Args: Args:
x (Tensor): The input Tensor, its data type is bool, float32, float64, int32 or int64. x (Tensor): The input Tensor, its data type is bool, float32, float64, int32 or int64.
shape (list|tuple|Tensor): The result shape after expanding. The data type is int32. If shape is a list or tuple, all its elements shape (list|tuple|Tensor): The result shape after expanding. The data type is int32. If shape is a list or tuple, all its elements
should be integers or 1-D Tensors with the data type int32. If shape is a Tensor, it should be an 1-D Tensor with the data type int32. should be integers or 0-D or 1-D Tensors with the data type int32. If shape is a Tensor, it should be an 1-D Tensor with the data type int32.
The value -1 in shape means keeping the corresponding dimension unchanged. 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` . 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` .
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册